Skip to content

Commit

Permalink
Use finally to clean temporary remote
Browse files Browse the repository at this point in the history
  • Loading branch information
karajan1001 committed Jan 31, 2023
1 parent 5a505bb commit cd64c82
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 53 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ install_requires=
pathspec>=0.9.0
asyncssh>=2.7.1,<3
funcy>=1.14
shortuuid>=0.5.0

[options.extras_require]
tests =
Expand Down
4 changes: 3 additions & 1 deletion src/scmrepo/git/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def __getitem__(self, key: str) -> BaseGitBackend:
"""Lazily initialize backends and cache it afterwards"""
initialized = self.initialized.get(key)
if not initialized:
if key not in self.backends and key in self.DEFAULT:
raise NotImplementedError
backend = self.backends[key]
initialized = backend(*self.args, **self.kwargs)
self.initialized[key] = initialized
Expand Down Expand Up @@ -278,7 +280,7 @@ def _backend_func(self, name, *args, **kwargs):
self._last_backend = key
self.backends.move_to_end(key, last=False)
return result
except (NotImplementedError, KeyError):
except NotImplementedError:
pass
raise NoGitBackendError(name)

Expand Down
111 changes: 59 additions & 52 deletions src/scmrepo/git/backend/pygit2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
TYPE_CHECKING,
Callable,
Dict,
Generator,
Iterable,
List,
Mapping,
Expand All @@ -17,6 +18,7 @@
)

from funcy import cached_property, reraise
from shortuuid import uuid

from scmrepo.exceptions import CloneError, MergeConflictError, RevError, SCMError
from scmrepo.utils import relpath
Expand Down Expand Up @@ -448,6 +450,19 @@ def _merge_remote_branch(
logger.debug("Unexpected merge result: %s", pygit2.GIT_MERGE_ANALYSIS_NORMAL)
raise SCMError("Unknown merge analysis result")

@contextmanager
def get_remote(self, url: str) -> Generator["Remote", None, None]:
try:
yield self.repo.remotes[url]
except ValueError:
try:
remote_name = uuid()
yield self.repo.remotes.create(remote_name, url)
finally:
self.repo.remotes.delete(remote_name)
except KeyError:
raise SCMError(f"'{url}' is not a valid Git remote or URL")

def fetch_refspecs(
self,
url: str,
Expand All @@ -462,60 +477,52 @@ def fetch_refspecs(
if isinstance(refspecs, str):
refspecs = [refspecs]

try:
remote: "Remote" = self.repo.remotes[url]
except ValueError:
remote_name = "fetch"
self.repo.remotes.set_url(remote_name, url)
remote = self.repo.remotes[remote_name]
except KeyError:
raise SCMError(f"'{url}' is not a valid Git remote or URL")
with self.get_remote(url) as remote:
if os.name == "nt" and remote.url.startswith("ssh://"):
raise NotImplementedError

if os.name == "nt" and remote.url.startswith("ssh://"):
raise NotImplementedError
if os.name == "nt" and remote.url.startswith("file://"):
url = remote.url[len("file://") :]
self.repo.remotes.set_url(remote.name, url)
remote = self.repo.remotes[remote.name]

if os.name == "nt" and remote.url.startswith("file://"):
url = remote.url[len("file://") :]
self.repo.remotes.set_url(remote.name, url)
remote = self.repo.remotes[remote.name]

fetch_refspecs: List[str] = []
for refspec in refspecs:
if ":" in refspec:
lh, rh = refspec.split(":")
else:
lh = rh = refspec
if not rh.startswith("refs/"):
rh = f"refs/heads/{rh}"
if not lh.startswith("refs/"):
lh = f"refs/heads/{lh}"
rh = rh[len("refs/") :]
refspec = f"+{lh}:refs/remotes/{remote.name}/{rh}"
fetch_refspecs.append(refspec)

logger.debug("fetch_refspecs: %s", fetch_refspecs)
with reraise(
GitError,
SCMError(f"Git failed to fetch ref from '{url}'"),
):
remote.fetch(refspecs=fetch_refspecs)

result: Dict[str, "SyncStatus"] = {}
for refspec in fetch_refspecs:
_, rh = refspec.split(":")
if not rh.endswith("*"):
refname = rh.split("/", 3)[-1]
refname = f"refs/{refname}"
result[refname] = self._merge_remote_branch(
rh, refname, force, on_diverged
)
continue
rh = rh.rstrip("*").rstrip("/") + "/"
for branch in self.iter_refs(base=rh):
refname = f"refs/{branch[len(rh):]}"
result[refname] = self._merge_remote_branch(
branch, refname, force, on_diverged
)
fetch_refspecs: List[str] = []
for refspec in refspecs:
if ":" in refspec:
lh, rh = refspec.split(":")
else:
lh = rh = refspec
if not rh.startswith("refs/"):
rh = f"refs/heads/{rh}"
if not lh.startswith("refs/"):
lh = f"refs/heads/{lh}"
rh = rh[len("refs/") :]
refspec = f"+{lh}:refs/remotes/{remote.name}/{rh}"
fetch_refspecs.append(refspec)

logger.debug("fetch_refspecs: %s", fetch_refspecs)
with reraise(
GitError,
SCMError(f"Git failed to fetch ref from '{url}'"),
):
remote.fetch(refspecs=fetch_refspecs)

result: Dict[str, "SyncStatus"] = {}
for refspec in fetch_refspecs:
_, rh = refspec.split(":")
if not rh.endswith("*"):
refname = rh.split("/", 3)[-1]
refname = f"refs/{refname}"
result[refname] = self._merge_remote_branch(
rh, refname, force, on_diverged
)
continue
rh = rh.rstrip("*").rstrip("/") + "/"
for branch in self.iter_refs(base=rh):
refname = f"refs/{branch[len(rh):]}"
result[refname] = self._merge_remote_branch(
branch, refname, force, on_diverged
)
return result

def _stash_iter(self, ref: str):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ def test_fetch_refspecs(
mocker.patch.object(Remote, "fetch", side_effect=GitError)
git.fetch_refspecs(remote, "refs/foo/bar:refs/foo/bar")

assert len(scm.pygit2.repo.remotes) == 1


@pytest.mark.skip_git_backend("pygit2", "gitpython")
@pytest.mark.parametrize("use_url", [True, False])
Expand Down

0 comments on commit cd64c82

Please sign in to comment.