Skip to content

Commit

Permalink
feat(blame): Support custom rev_opts for blame
Browse files Browse the repository at this point in the history
The `git blame` CLI offers a repeated `-C` option that can be used to detect
lines that move within/between files. While a slower operation, it yields more
accurate authorship reports.
https://git-scm.com/docs/git-blame#Documentation/git-blame.txt--Cltnumgt

While GitPython does enable passing custom kwargs to the command line `git`
invocation, the fact that kwargs is a dictionary (i.e. no duplicate keys) means
that there was no way to request the `-C` option in `git blame` more than once.

This commit adds an optional `rev_opts` parameter to the `blame` method which
accepts a list of strings to propagate to the CLI invocation of `git blame`. By
using a `List[str]` for `rev_opts`, users of GitPython can pass now the `-C`
option multiple times to get more detailed authorship reports from `git blame`.
  • Loading branch information
thehale committed Aug 31, 2022
1 parent 73bde1f commit 18a79d8
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 3 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -49,4 +49,5 @@ Contributors are:
-Julien Mauroy <pro.julien.mauroy _at_ gmail.com>
-Patrick Gerard
-Luke Twist <itsluketwist@gmail.com>
-Joseph Hale <me _at_ jhale.dev>
Portions derived from other open source works and are clearly marked.
11 changes: 8 additions & 3 deletions git/repo/base.py
Expand Up @@ -950,7 +950,12 @@ def blame_incremental(self, rev: str | HEAD, file: str, **kwargs: Any) -> Iterat
)

def blame(
self, rev: Union[str, HEAD], file: str, incremental: bool = False, **kwargs: Any
self,
rev: Union[str, HEAD],
file: str,
incremental: bool = False,
rev_opts: Optional[List[str]] = None,
**kwargs: Any
) -> List[List[Commit | List[str | bytes] | None]] | Iterator[BlameEntry] | None:
"""The blame information for the given file at the given revision.
Expand All @@ -962,8 +967,8 @@ def blame(
of appearance."""
if incremental:
return self.blame_incremental(rev, file, **kwargs)

data: bytes = self.git.blame(rev, "--", file, p=True, stdout_as_string=False, **kwargs)
rev_opts = rev_opts or []
data: bytes = self.git.blame(rev, *rev_opts, "--", file, p=True, stdout_as_string=False, **kwargs)
commits: Dict[str, Commit] = {}
blames: List[List[Commit | List[str | bytes] | None]] = []

Expand Down
7 changes: 7 additions & 0 deletions test/test_repo.py
Expand Up @@ -553,6 +553,13 @@ def test_blame_complex_revision(self, git):
self.assertEqual(len(res), 1)
self.assertEqual(len(res[0][1]), 83, "Unexpected amount of parsed blame lines")

@mock.patch.object(Git, "_call_process")
def test_blame_accepts_rev_opts(self, git):
res = self.rorepo.blame("HEAD", "README.md", rev_opts=["-M", "-C", "-C"])
expected_args = ['blame', 'HEAD', '-M', '-C', '-C', '--', 'README.md']
boilerplate_kwargs = {"p" : True, "stdout_as_string": False}
git.assert_called_once_with(*expected_args, **boilerplate_kwargs)

@skipIf(
HIDE_WINDOWS_KNOWN_ERRORS and Git.is_cygwin(),
"""FIXME: File "C:\\projects\\gitpython\\git\\cmd.py", line 671, in execute
Expand Down

0 comments on commit 18a79d8

Please sign in to comment.