diff --git a/dvc/scm/git/backend/pygit2.py b/dvc/scm/git/backend/pygit2.py index fe6b97d96..aeee30579 100644 --- a/dvc/scm/git/backend/pygit2.py +++ b/dvc/scm/git/backend/pygit2.py @@ -94,6 +94,15 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method return RefdbFsBackend(self.repo) + def _resolve_refish(self, refish: str): + from pygit2 import GIT_OBJ_COMMIT, Tag + + commit, ref = self.repo.resolve_refish(refish) + if isinstance(commit, Tag): + ref = commit + commit = commit.peel(GIT_OBJ_COMMIT) + return commit, ref + @property def default_signature(self): try: @@ -153,7 +162,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method if branch == "-": branch = "@{-1}" try: - commit, ref = self.repo.resolve_refish(branch) + commit, ref = self._resolve_refish(branch) except (KeyError, GitError): raise RevError(f"unknown Git revision '{branch}'") self.repo.checkout_tree(commit, strategy=checkout_strategy) @@ -213,7 +222,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method from pygit2 import GitError try: - commit, _ref = self.repo.resolve_refish(rev) + commit, _ref = self._resolve_refish(rev) return str(commit.id) except (KeyError, GitError): pass @@ -233,7 +242,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method from pygit2 import GitError try: - commit, _ref = self.repo.resolve_refish(rev) + commit, _ref = self._resolve_refish(rev) except (KeyError, GitError): raise SCMError(f"Invalid commit '{rev}'") return GitCommit( @@ -307,12 +316,12 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method from pygit2 import GitError def _contains(repo, ref, search_commit): - commit, _ref = self.repo.resolve_refish(ref) + commit, _ref = self._resolve_refish(ref) base = repo.merge_base(search_commit.id, commit.id) return base == search_commit.id try: - search_commit, _ref = self.repo.resolve_refish(rev) + search_commit, _ref = self._resolve_refish(rev) except (KeyError, GitError): raise SCMError(f"Invalid rev '{rev}'") @@ -397,7 +406,7 @@ class Pygit2Backend(BaseGitBackend): # pylint:disable=abstract-method # libgit2 stash apply only accepts refs/stash items by index. If rev is # not in refs/stash, we will push it onto the stash, and then pop it - commit, _ref = self.repo.resolve_refish(rev) + commit, _ref = self._resolve_refish(rev) stash = self.repo.references.get(Stash.DEFAULT_STASH) if stash: for i, entry in enumerate(stash.log()): diff --git a/tests/unit/scm/test_git.py b/tests/unit/scm/test_git.py index 3303b21b6..1c3b09daa 100644 --- a/tests/unit/scm/test_git.py +++ b/tests/unit/scm/test_git.py @@ -553,3 +553,30 @@ def test_add(tmp_dir, scm, git): assert set(staged["modify"]) == {"dir/baz"} assert len(unstaged) == 1 assert len(untracked) == 1 + + +@pytest.mark.parametrize("use_sha", [True, False]) +def test_pygit_resolve_refish(tmp_dir, scm, git, use_sha): + import pygit2 + + if git.test_backend != "pygit2": + pytest.skip() + + tmp_dir.scm_gen("foo", "foo", commit="foo") + head = scm.get_rev() + tag = "my_tag" + scm.gitpython.git.tag("-a", tag, "-m", "create annotated tag") + + if use_sha: + # refish will be annotated tag SHA (not commit SHA) + ref = git.pygit2.repo.references.get(f"refs/tags/{tag}") + refish = str(ref.target) + else: + refish = tag + + assert refish != head + commit, ref = git.pygit2._resolve_refish(refish) + assert isinstance(commit, pygit2.Commit) + assert str(commit.id) == head + if not use_sha: + assert ref.name == f"refs/tags/{tag}"