From 030adb0ff3979e7158dee4fc7ef40b981d2f72c5 Mon Sep 17 00:00:00 2001 From: karajan1001 Date: Wed, 19 Oct 2022 14:07:41 +0800 Subject: [PATCH] Add mutli rev support for function `describe` fix: #144 --- src/scmrepo/git/__init__.py | 26 ++++++++++++++------- src/scmrepo/git/backend/base.py | 6 ++--- src/scmrepo/git/backend/dulwich/__init__.py | 15 ++++++++---- src/scmrepo/git/backend/gitpython.py | 4 ++-- src/scmrepo/git/backend/pygit2.py | 4 ++-- tests/test_git.py | 17 ++++++++++---- 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/scmrepo/git/__init__.py b/src/scmrepo/git/__init__.py index 749e3cd3..fd892511 100644 --- a/src/scmrepo/git/__init__.py +++ b/src/scmrepo/git/__init__.py @@ -442,15 +442,23 @@ def _reset(self) -> None: def describe( self, - rev: str, + revs: Iterable[str], base: Optional[str] = None, match: Optional[str] = None, exclude: Optional[str] = None, - ) -> Optional[str]: - if ( - base == "refs/heads" - and self.get_rev() == rev - and self.get_ref("HEAD", follow=False).startswith(base) - ): - return self.get_ref("HEAD", follow=False) - return self._describe(rev, base, match, exclude) + ) -> Dict[str, Optional[str]]: + results: Dict[str, Optional[str]] = {} + remained_revs = set() + if base == "refs/heads": + current_rev = self.get_rev() + head_ref = self.get_ref("HEAD", follow=False) + for rev in revs: + if current_rev == rev and head_ref.startswith(base): + results[rev] = self.get_ref("HEAD", follow=False) + else: + remained_revs.add(rev) + else: + remained_revs = set(revs) + if remained_revs: + results.update(self._describe(remained_revs, base, match, exclude)) + return results diff --git a/src/scmrepo/git/backend/base.py b/src/scmrepo/git/backend/base.py index bb06f55d..6e19c1c0 100644 --- a/src/scmrepo/git/backend/base.py +++ b/src/scmrepo/git/backend/base.py @@ -297,12 +297,12 @@ def _stash_drop(self, ref: str, index: int): @abstractmethod def _describe( self, - rev: str, + revs: Iterable[str], base: Optional[str] = None, match: Optional[str] = None, exclude: Optional[str] = None, - ) -> Optional[str]: - """Return the first ref which points to rev. + ) -> Mapping[str, Optional[str]]: + """Return the first ref which points to each revs. Roughly equivalent to `git describe --all --exact-match`. diff --git a/src/scmrepo/git/backend/dulwich/__init__.py b/src/scmrepo/git/backend/dulwich/__init__.py index ae8315b6..2c0910ce 100644 --- a/src/scmrepo/git/backend/dulwich/__init__.py +++ b/src/scmrepo/git/backend/dulwich/__init__.py @@ -724,21 +724,26 @@ def _stash_drop(self, ref: str, index: int): def _describe( self, - rev: str, + revs: Iterable[str], base: Optional[str] = None, match: Optional[str] = None, exclude: Optional[str] = None, - ) -> Optional[str]: + ) -> Mapping[str, Optional[str]]: if not base: base = "refs/tags" + rev_mapping: Dict[str, Optional[str]] = {} + results: Dict[str, Optional[str]] = {} for ref in self.iter_refs(base=base): if (match and not fnmatch.fnmatch(ref, match)) or ( exclude and fnmatch.fnmatch(ref, exclude) ): continue - if self.get_ref(ref, follow=False) == rev: - return ref - return None + revision = self.get_ref(ref, follow=False) + if revision and revision not in rev_mapping: + rev_mapping[revision] = ref + for rev in revs: + results[rev] = rev_mapping.get(rev, None) + return results def diff(self, rev_a: str, rev_b: str, binary=False) -> str: from dulwich.patch import write_tree_diff diff --git a/src/scmrepo/git/backend/gitpython.py b/src/scmrepo/git/backend/gitpython.py index 68b5037a..ed9bc509 100644 --- a/src/scmrepo/git/backend/gitpython.py +++ b/src/scmrepo/git/backend/gitpython.py @@ -572,11 +572,11 @@ def _stash_drop(self, ref: str, index: int): def _describe( self, - rev: str, + revs: Iterable[str], base: Optional[str] = None, match: Optional[str] = None, exclude: Optional[str] = None, - ) -> Optional[str]: + ) -> Mapping[str, Optional[str]]: raise NotImplementedError def diff(self, rev_a: str, rev_b: str, binary=False) -> str: diff --git a/src/scmrepo/git/backend/pygit2.py b/src/scmrepo/git/backend/pygit2.py index 67b7046e..c87acc09 100644 --- a/src/scmrepo/git/backend/pygit2.py +++ b/src/scmrepo/git/backend/pygit2.py @@ -515,11 +515,11 @@ def _stash_drop(self, ref: str, index: int): def _describe( self, - rev: str, + revs: Iterable[str], base: Optional[str] = None, match: Optional[str] = None, exclude: Optional[str] = None, - ) -> Optional[str]: + ) -> Mapping[str, Optional[str]]: raise NotImplementedError def diff(self, rev_a: str, rev_b: str, binary=False) -> str: diff --git a/tests/test_git.py b/tests/test_git.py index 749e0519..070e52f9 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -705,21 +705,28 @@ def test_describe(tmp_dir: TmpDir, scm: Git, git: Git): scm.add_commit("foo", message="bar") rev_bar = scm.get_rev() - assert git.describe(rev_foo, "refs/heads") is None + assert git.describe([rev_foo], "refs/heads") == {rev_foo: None} scm.checkout("branch", create_new=True) - assert git.describe(rev_bar, "refs/heads") == "refs/heads/branch" + assert git.describe([rev_bar], "refs/heads") == { + rev_bar: "refs/heads/branch" + } tmp_dir.gen({"foo": "foobar"}) scm.add_commit("foo", message="foobar") rev_foobar = scm.get_rev() scm.checkout("master") - assert git.describe(rev_bar, "refs/heads") == "refs/heads/master" - assert git.describe(rev_foobar, "refs/heads") == "refs/heads/branch" + assert git.describe([rev_bar, rev_foobar], "refs/heads") == { + rev_bar: "refs/heads/master", + rev_foobar: "refs/heads/branch", + } scm.tag("tag") - assert git.describe(rev_bar) == "refs/tags/tag" + assert git.describe([rev_bar, "nonexist"]) == { + rev_bar: "refs/tags/tag", + "nonexist": None, + } def test_ignore(tmp_dir: TmpDir, scm: Git, git: Git):