Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions git/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ def decode_path(path: bytes, has_ab_prefix: bool = True) -> Optional[bytes]:
path = _octal_byte_re.sub(_octal_repl, path)

if has_ab_prefix:
assert path.startswith(b"a/") or path.startswith(b"b/")
# Support standard (a/b) and mnemonicPrefix (c/w/i/o/h) prefixes
# See git-config diff.mnemonicPrefix documentation
valid_prefixes = (b"a/", b"b/", b"c/", b"w/", b"i/", b"o/", b"h/")
assert any(path.startswith(p) for p in valid_prefixes), f"Unexpected path prefix: {path[:10]!r}"
path = path[2:]

return path
Expand Down Expand Up @@ -367,10 +370,12 @@ class Diff:
"""

# Precompiled regex.
# Note: The path prefixes support both default (a/b) and mnemonicPrefix mode
# which can use prefixes like c/ (commit), w/ (worktree), i/ (index), o/ (object)
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is missing the h/ prefix for HEAD. According to the PR description and the Git documentation, h/ is also a valid mnemonicPrefix. The comment should list all supported prefixes: c/ (commit), w/ (worktree), i/ (index), o/ (object), and h/ (HEAD).

Suggested change
# which can use prefixes like c/ (commit), w/ (worktree), i/ (index), o/ (object)
# which can use prefixes like c/ (commit), w/ (worktree), i/ (index), o/ (object), and h/ (HEAD)

Copilot uses AI. Check for mistakes.
re_header = re.compile(
rb"""
^diff[ ]--git
[ ](?P<a_path_fallback>"?[ab]/.+?"?)[ ](?P<b_path_fallback>"?[ab]/.+?"?)\n
[ ](?P<a_path_fallback>"?[abciwoh]/.+?"?)[ ](?P<b_path_fallback>"?[abciwoh]/.+?"?)\n
(?:^old[ ]mode[ ](?P<old_mode>\d+)\n
^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
(?:^similarity[ ]index[ ]\d+%\n
Expand Down
32 changes: 32 additions & 0 deletions test/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,38 @@ def test_diff_unsafe_paths(self):
self.assertEqual(res[13].a_path, 'a/"with-quotes"')
self.assertEqual(res[13].b_path, 'b/"with even more quotes"')

def test_diff_mnemonic_prefix(self):
"""Test that diff parsing works with mnemonicPrefix enabled.
When diff.mnemonicPrefix=true is set in git config, git uses different
prefixes for diff paths:
- c/ for commit
- w/ for worktree
- i/ for index
- o/ for object
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring is missing the h/ prefix for HEAD. According to the PR description and Git documentation, h/ is also a valid mnemonicPrefix. The comment should list all supported prefixes: c/ (commit), w/ (worktree), i/ (index), o/ (object), and h/ (HEAD).

Suggested change
- o/ for object
- o/ for object
- h/ for HEAD

Copilot uses AI. Check for mistakes.
This addresses issue #2013 where the regex only matched [ab]/ prefixes.
"""
# Create a diff with mnemonicPrefix-style c/ and w/ prefixes
# Using valid 40-char hex SHAs
diff_mnemonic = b"""diff --git c/.vscode/launch.json w/.vscode/launch.json
index 1234567890abcdef1234567890abcdef12345678..abcdef1234567890abcdef1234567890abcdef12 100644
--- c/.vscode/launch.json
+++ w/.vscode/launch.json
@@ -1,3 +1,3 @@
-old content
+new content
"""
diff_proc = StringProcessAdapter(diff_mnemonic)
diffs = Diff._index_from_patch_format(self.rorepo, diff_proc)

# Should parse successfully (previously would fail or return empty)
self.assertEqual(len(diffs), 1)
diff = diffs[0]
# The path should be extracted correctly (without the c/ or w/ prefix)
self.assertEqual(diff.a_path, ".vscode/launch.json")
self.assertEqual(diff.b_path, ".vscode/launch.json")
Comment on lines +284 to +314
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only covers c/ and w/ prefixes but doesn't test the other valid mnemonicPrefix options (i/, o/, h/). Consider adding test cases for these prefixes as well to ensure the regex pattern [abciwoh] and the decode_path() function work correctly with all supported prefixes. This could be done either by adding more test data in this test or creating a parameterized test.

Copilot uses AI. Check for mistakes.

def test_diff_patch_format(self):
# Test all of the 'old' format diffs for completeness - it should at least be
# able to deal with it.
Expand Down
Loading