diff --git a/git/repo/base.py b/git/repo/base.py index 0de87d01c..3965ba1a1 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -1293,6 +1293,8 @@ def blame_incremental( If you combine all line number ranges outputted by this command, you should get a continuous range spanning all line numbers in the file. """ + from git.diff import _unquote_path + if not allow_unsafe_options: Git.check_unsafe_options( options=Git._option_candidates([rev], kwargs), @@ -1375,6 +1377,10 @@ def blame_incremental( orig_filename = value break + # Git C-quotes a filename containing control, quote, backslash or, + # with core.quotePath, non-ASCII bytes, the way it does in diff output. + if orig_filename.startswith(b'"') and orig_filename.endswith(b'"'): + orig_filename = _unquote_path(orig_filename[1:-1]) yield BlameEntry( commits[hexsha], range(lineno, lineno + num_lines), diff --git a/test/test_repo.py b/test/test_repo.py index 7ecf2a920..57d674770 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -792,6 +792,17 @@ def test_blame_content_with_carriage_returns(self): self.assertEqual(blames[0][0].author.name, "GitPython Tests") self.assertEqual(blames[0][1], ["first", forged]) + def test_blame_incremental_unquotes_filenames(self): + # Git C-quotes the filename line of its incremental output for a path with + # non-ASCII, control, quote or backslash bytes. The entry must name the path. + for name in ("caf\u00e9.txt", '"quoted".txt', "tab\tname.txt", "back\\slash.txt"): + with self.subTest(name=name), fixture_repo("repo.sh", "blame-quoted-filename", name) as repo: + quoted = repo.git.blame("HEAD", "-p", "--incremental", "--", "plain.txt", stdout_as_string=False) + assert b'\nfilename "' in quoted + entries = list(repo.blame_incremental("HEAD", "plain.txt")) + self.assertEqual([entry.orig_path for entry in entries], [name]) + self.assertEqual([entry.linenos for entry in entries], [range(1, 3)]) + def test_untracked_files(self): for command in ("index", "git"): with fixture_repo("repo.sh", "untracked", write=True) as repo: diff --git a/tests/fixtures/repo.sh b/tests/fixtures/repo.sh index c6d636c33..85955c6d5 100644 --- a/tests/fixtures/repo.sh +++ b/tests/fixtures/repo.sh @@ -123,6 +123,14 @@ blame-carriage-returns) git -C repo add file.txt git -C repo commit -qm init --author='GitPython Tests ' ;; +blame-quoted-filename) + git init -q repo + printf 'first\nsecond\n' > "repo/$2" + git -C repo add -- "$2" + git -C repo commit -qm init --author='GitPython Tests ' + git -C repo mv -- "$2" plain.txt + git -C repo commit -qm rename --author='GitPython Tests ' + ;; relocated-git-dir) git init -q repo mv repo/.git repo/.real