Skip to content

Commit 6ef056f

Browse files
Fix diff parsing to support mnemonicPrefix configuration
Fixes #2013 When diff.mnemonicPrefix=true is set in git config, git uses different prefixes for diff paths instead of the standard a/ and b/: - c/ for commit - w/ for worktree - i/ for index - o/ for object - h/ for HEAD Previously, the diff regex and decode_path() function only accepted a/ and b/ prefixes, causing create_patch=True diffs to fail parsing. Changes: - Update re_header regex to accept [abciwoh]/ prefixes - Update decode_path() assertion to accept all valid mnemonic prefixes - Add test case for mnemonicPrefix-style diffs
1 parent eecc28d commit 6ef056f

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

git/diff.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,10 @@ def decode_path(path: bytes, has_ab_prefix: bool = True) -> Optional[bytes]:
113113
path = _octal_byte_re.sub(_octal_repl, path)
114114

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

119122
return path
@@ -367,10 +370,12 @@ class Diff:
367370
"""
368371

369372
# Precompiled regex.
373+
# Note: The path prefixes support both default (a/b) and mnemonicPrefix mode
374+
# which can use prefixes like c/ (commit), w/ (worktree), i/ (index), o/ (object)
370375
re_header = re.compile(
371376
rb"""
372377
^diff[ ]--git
373-
[ ](?P<a_path_fallback>"?[ab]/.+?"?)[ ](?P<b_path_fallback>"?[ab]/.+?"?)\n
378+
[ ](?P<a_path_fallback>"?[abciwoh]/.+?"?)[ ](?P<b_path_fallback>"?[abciwoh]/.+?"?)\n
374379
(?:^old[ ]mode[ ](?P<old_mode>\d+)\n
375380
^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
376381
(?:^similarity[ ]index[ ]\d+%\n

test/test_diff.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,38 @@ def test_diff_unsafe_paths(self):
281281
self.assertEqual(res[13].a_path, 'a/"with-quotes"')
282282
self.assertEqual(res[13].b_path, 'b/"with even more quotes"')
283283

284+
def test_diff_mnemonic_prefix(self):
285+
"""Test that diff parsing works with mnemonicPrefix enabled.
286+
287+
When diff.mnemonicPrefix=true is set in git config, git uses different
288+
prefixes for diff paths:
289+
- c/ for commit
290+
- w/ for worktree
291+
- i/ for index
292+
- o/ for object
293+
294+
This addresses issue #2013 where the regex only matched [ab]/ prefixes.
295+
"""
296+
# Create a diff with mnemonicPrefix-style c/ and w/ prefixes
297+
# Using valid 40-char hex SHAs
298+
diff_mnemonic = b"""diff --git c/.vscode/launch.json w/.vscode/launch.json
299+
index 1234567890abcdef1234567890abcdef12345678..abcdef1234567890abcdef1234567890abcdef12 100644
300+
--- c/.vscode/launch.json
301+
+++ w/.vscode/launch.json
302+
@@ -1,3 +1,3 @@
303+
-old content
304+
+new content
305+
"""
306+
diff_proc = StringProcessAdapter(diff_mnemonic)
307+
diffs = Diff._index_from_patch_format(self.rorepo, diff_proc)
308+
309+
# Should parse successfully (previously would fail or return empty)
310+
self.assertEqual(len(diffs), 1)
311+
diff = diffs[0]
312+
# The path should be extracted correctly (without the c/ or w/ prefix)
313+
self.assertEqual(diff.a_path, ".vscode/launch.json")
314+
self.assertEqual(diff.b_path, ".vscode/launch.json")
315+
284316
def test_diff_patch_format(self):
285317
# Test all of the 'old' format diffs for completeness - it should at least be
286318
# able to deal with it.

0 commit comments

Comments
 (0)