Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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")
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