Skip to content

Commit 75e2cb9

Browse files
authored
Merge pull request #2315 from gitpython-developers/2314-supersede
fix(repo): return actual historical paths from incremental blame
2 parents 4debc51 + bb378dd commit 75e2cb9

3 files changed

Lines changed: 55 additions & 0 deletions

File tree

‎git/repo/base.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,8 @@ def blame_incremental(
12931293
If you combine all line number ranges outputted by this command, you should get
12941294
a continuous range spanning all line numbers in the file.
12951295
"""
1296+
from git.diff import _unquote_path
1297+
12961298
if not allow_unsafe_options:
12971299
Git.check_unsafe_options(
12981300
options=Git._option_candidates([rev], kwargs),
@@ -1375,6 +1377,9 @@ def blame_incremental(
13751377
orig_filename = value
13761378
break
13771379

1380+
if orig_filename.startswith(b'"') and orig_filename.endswith(b'"'):
1381+
orig_filename = _unquote_path(orig_filename[1:-1])
1382+
13781383
yield BlameEntry(
13791384
commits[hexsha],
13801385
range(lineno, lineno + num_lines),

‎test/test_repo.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
Submodule,
3737
Tree,
3838
)
39+
from git.compat import safe_decode
3940
from git.exc import BadObject, UnsafeOptionError, UnsafeProtocolError
4041
from git.repo.fun import find_worktree_git_dir
4142
from git.util import bin_to_hex, cwd, rmfile, rmtree
@@ -754,6 +755,35 @@ def test_blame_incremental(self, git):
754755
),
755756
)
756757

758+
def test_blame_incremental_preserves_historical_paths(self):
759+
self.maxDiff = None
760+
paths = (
761+
b"plain.txt",
762+
b" spaced name.txt ",
763+
"café.txt".encode(),
764+
b"byte-\xff.txt",
765+
b'"literal-\\303\\251.txt"',
766+
b'quote"name.txt',
767+
b"back\\slash.txt",
768+
b"control\a\b\t\n\v\f\r.txt",
769+
)
770+
for quote_path in ("true", "false"):
771+
for i, path in enumerate(paths):
772+
with self.subTest(quote_path=quote_path, path=path), fixture_repo(
773+
"blame-paths.sh", quote_path, subdir=str(i)
774+
) as repo:
775+
entries = list(repo.blame_incremental("HEAD", "current.txt"))
776+
original = repo.commit("HEAD~2")
777+
self.assertEqual(
778+
sorted(entries, key=lambda e: e.linenos.start),
779+
[
780+
(original, range(1, 2), safe_decode(path), range(1, 2)),
781+
(repo.head.commit, range(2, 3), "current.txt", range(2, 3)),
782+
(original, range(3, 4), safe_decode(path), range(3, 4)),
783+
],
784+
)
785+
self.assertEqual(list(repo.blame("HEAD", "current.txt", incremental=True)), entries)
786+
757787
@mock.patch.object(Git, "_call_process")
758788
def test_blame_complex_revision(self, git):
759789
git.return_value = fixture("blame_complex_revision")

‎tests/fixtures/blame-paths.sh‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env bash
2+
set -eu
3+
# C-quoted tree input preserves control bytes through Windows fixture creation.
4+
paths=('plain.txt' ' spaced name.txt ' '"caf\303\251.txt"' '"byte-\377.txt"' '"\"literal-\\303\\251.txt\""' '"quote\"name.txt"' '"back\\slash.txt"' '"control\a\b\t\n\v\f\r.txt"')
5+
for i in "${!paths[@]}"; do
6+
git init -q --initial-branch=main "$i"
7+
(
8+
cd "$i"
9+
git config core.quotePath "$1"
10+
blob=$(printf 'first\nmiddle\nlast\n' | git hash-object -w --stdin)
11+
tree=$(printf '100644 blob %s\t%s\n' "$blob" "${paths[$i]}" | git mktree)
12+
original=$(git commit-tree "$tree" -m original)
13+
tree=$(printf '100644 blob %s\tcurrent.txt\0' "$blob" | git mktree -z)
14+
renamed=$(git commit-tree "$tree" -p "$original" -m rename)
15+
blob=$(printf 'first\nchanged\nlast\n' | git hash-object -w --stdin)
16+
tree=$(printf '100644 blob %s\tcurrent.txt\0' "$blob" | git mktree -z)
17+
edited=$(git commit-tree "$tree" -p "$renamed" -m edit)
18+
git update-ref refs/heads/main "$edited"
19+
)
20+
done

0 commit comments

Comments
 (0)