|
12 | 12 | import shlex |
13 | 13 | import stat |
14 | 14 | import sys |
15 | | -import urllib.parse |
16 | 15 | import uuid |
17 | 16 | from io import BytesIO |
18 | 17 | from pathlib import Path |
|
57 | 56 |
|
58 | 57 | from .util import ( |
59 | 58 | SubmoduleConfigParser, |
| 59 | + _is_absolute_path, |
| 60 | + _url_is_local_not_ssh, |
60 | 61 | find_first_remote_branch, |
| 62 | + is_relative_url, |
61 | 63 | mkhead, |
| 64 | + resolve_relative_url, |
62 | 65 | sm_name, |
63 | 66 | sm_section, |
64 | 67 | ) |
|
68 | 71 | if TYPE_CHECKING: |
69 | 72 | from git.index import IndexFile |
70 | 73 | from git.objects.commit import Commit |
71 | | - from git.refs import Head, RemoteReference |
| 74 | + from git.refs import Head |
72 | 75 | from git.repo import Repo |
73 | 76 |
|
74 | 77 | # ----------------------------------------------------------------------------- |
@@ -428,13 +431,8 @@ def _clone_repo( |
428 | 431 | if not osp.isdir(module_abspath_dir): |
429 | 432 | os.makedirs(module_abspath_dir) |
430 | 433 |
|
431 | | - if url.startswith("../"): |
432 | | - remote_name = cast("RemoteReference", repo.active_branch.tracking_branch()).remote_name |
433 | | - repo_remote_url = repo.remote(remote_name).url |
434 | | - url = os.path.join(repo_remote_url, url) |
435 | | - |
436 | 434 | clone = git.Repo.clone_from( |
437 | | - url, |
| 435 | + cls._resolve_url(repo, url), |
438 | 436 | module_checkout_path, |
439 | 437 | allow_unsafe_options=allow_unsafe_options, |
440 | 438 | allow_unsafe_protocols=allow_unsafe_protocols, |
@@ -746,8 +744,9 @@ def add( |
746 | 744 | ) |
747 | 745 | # END verify url |
748 | 746 |
|
749 | | - ## See #525 for ensuring git URLs in config-files are valid under Windows. |
750 | | - url = Git.polish_url(url, expand_vars=False) |
| 747 | + # Relative submodule URLs must keep their prefix to resolve against the parent remote. |
| 748 | + if not is_relative_url(url): |
| 749 | + url = Git.polish_url(url, expand_vars=False) |
751 | 750 |
|
752 | 751 | # It's important to add the URL to the parent config, to let `git submodule` know. |
753 | 752 | # Otherwise there is a '-' character in front of the submodule listing: |
@@ -982,13 +981,9 @@ def fetch_remotes(module_repo: "Repo") -> None: |
982 | 981 | if not dry_run: |
983 | 982 | if no_fetch: |
984 | 983 | raise ValueError("Missing module at %r but fetching is disabled" % self.path) from None |
985 | | - if self.url.startswith("."): |
986 | | - url = urllib.parse.urljoin(self.repo.remotes.origin.url + "/", self.url) |
987 | | - else: |
988 | | - url = self.url |
989 | 984 | mrepo = self._clone_repo( |
990 | 985 | self.repo, |
991 | | - url, |
| 986 | + self.url, |
992 | 987 | self.path, |
993 | 988 | self.name, |
994 | 989 | n=True, |
@@ -1738,6 +1733,31 @@ def url(self) -> str: |
1738 | 1733 | """:return: The url to the repository our submodule's repository refers to""" |
1739 | 1734 | return self._url |
1740 | 1735 |
|
| 1736 | + @staticmethod |
| 1737 | + def _resolve_url(repo: "Repo", url: str) -> str: |
| 1738 | + """Resolve relative URLs against the tracking remote, then ``origin``, |
| 1739 | + falling back to the superproject's directory the way git does.""" |
| 1740 | + if not is_relative_url(url): |
| 1741 | + return url |
| 1742 | + remote_name = "origin" |
| 1743 | + if not repo.head.is_detached: |
| 1744 | + with repo.active_branch.config_reader() as reader: |
| 1745 | + remote_name = reader.get("remote", fallback="origin") |
| 1746 | + try: |
| 1747 | + remote_url = repo.remote(remote_name).url |
| 1748 | + except ValueError: |
| 1749 | + remote_url = os.fspath(repo.working_dir) |
| 1750 | + if remote_url == ".": |
| 1751 | + remote_url = os.fspath(repo.working_dir) |
| 1752 | + resolved = resolve_relative_url(remote_url, url) |
| 1753 | + if resolved and _url_is_local_not_ssh(resolved) and not _is_absolute_path(resolved): |
| 1754 | + resolved = os.path.join(repo.working_dir, resolved) |
| 1755 | + return resolved |
| 1756 | + |
| 1757 | + def _resolved_url(self) -> str: |
| 1758 | + """:return: :attr:`url` resolved against the superproject's default remote""" |
| 1759 | + return self._resolve_url(self.repo, self.url) |
| 1760 | + |
1741 | 1761 | @property |
1742 | 1762 | def parent_commit(self) -> "Commit": |
1743 | 1763 | """ |
|
0 commit comments