Issue
Summary
find_diffs removes standard Git a/ and b/ prefixes from the first file in a fenced unified diff, but retains the b/ prefix for the second and subsequent files.
For a two-file diff targeting file1.txt and file2.txt, the parser returns file1.txt and b/file2.txt.
Steps to reproduce
-
Check out Aider main at commit 5dc9490bb35f9729ef2c95d00a19ccd30c26339c.
-
Add the following test inside TestUnifiedDiffCoder in tests/basic/test_udiff.py:
def test_find_multi_diffs_with_git_prefixes(self):
content = """```diff
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1 @@
-old one
+new one
--- a/file2.txt
+++ b/file2.txt
@@ -1 +1 @@
-old two
+new two
```
"""
edits = find_diffs(content)
self.assertEqual(
[edit[0] for edit in edits],
["file1.txt", "file2.txt"],
)
- Run:
python -m pytest tests/basic/test_udiff.py -q
- Observe that the newly added assertion fails while the four existing tests pass.
Expected behavior
find_diffs should normalize the standard Git prefixes for every file-header pair and return:
["file1.txt", "file2.txt"]
The parser already removes a/ and b/ from the initial header pair. A fenced multi-file diff contains the same standard header structure for each subsequent file, so those paths should be normalized consistently.
Actual behavior
find_diffs returns:
["file1.txt", "b/file2.txt"]
The focused test reports:
AssertionError: Lists differ: ['file1.txt', 'b/file2.txt'] != ['file1.txt', 'file2.txt']
First differing element 1:
'b/file2.txt'
'file2.txt'
1 failed, 4 passed
The second edit is therefore associated with b/file2.txt rather than the intended repository-relative path file2.txt.
Root cause
process_fenced_block normalizes the initial header pair:
a_fname = block[0][4:].strip()
b_fname = block[1][4:].strip()
if (a_fname.startswith("a/") or a_fname == "/dev/null") and b_fname.startswith("b/"):
fname = b_fname[2:]
When a later header pair is encountered, however, the parser assigns the raw +++ path directly:
if line.startswith("+++ ") and hunk[-2].startswith("--- "):
...
fname = line[4:].strip()
The later branch does not apply the normalization used for the initial pair.
UnifiedDiffCoder.get_edits consumes the filenames returned by find_diffs, and apply_edits subsequently resolves each filename as a repository-relative path. The retained prefix therefore propagates beyond the parser.
Possible fix direction
Apply the same paired-header normalization to every ---/+++ pair encountered by process_fenced_block, rather than only the initial pair.
Regression coverage should include a fenced multi-file diff using standard a/ and b/ prefixes and verify the normalized filename of every returned edit.
Version and model info
Aider: 0.86.3.dev, main@5dc9490bb35f9729ef2c95d00a19ccd30c26339c
Python: 3.12.13
Operating system: macOS 15.7.3
Installation: source checkout; focused pytest reproduction
Edit format: udiff
Model: not applicable; reproduced by directly invoking deterministic unified-diff parsing code
Issue
Summary
find_diffsremoves standard Gita/andb/prefixes from the first file in a fenced unified diff, but retains theb/prefix for the second and subsequent files.For a two-file diff targeting
file1.txtandfile2.txt, the parser returnsfile1.txtandb/file2.txt.Steps to reproduce
Check out Aider
mainat commit5dc9490bb35f9729ef2c95d00a19ccd30c26339c.Add the following test inside
TestUnifiedDiffCoderintests/basic/test_udiff.py:Expected behavior
find_diffsshould normalize the standard Git prefixes for every file-header pair and return:The parser already removes
a/andb/from the initial header pair. A fenced multi-file diff contains the same standard header structure for each subsequent file, so those paths should be normalized consistently.Actual behavior
find_diffsreturns:The focused test reports:
The second edit is therefore associated with
b/file2.txtrather than the intended repository-relative pathfile2.txt.Root cause
process_fenced_blocknormalizes the initial header pair:When a later header pair is encountered, however, the parser assigns the raw
+++path directly:The later branch does not apply the normalization used for the initial pair.
UnifiedDiffCoder.get_editsconsumes the filenames returned byfind_diffs, andapply_editssubsequently resolves each filename as a repository-relative path. The retained prefix therefore propagates beyond the parser.Possible fix direction
Apply the same paired-header normalization to every
---/+++pair encountered byprocess_fenced_block, rather than only the initial pair.Regression coverage should include a fenced multi-file diff using standard
a/andb/prefixes and verify the normalized filename of every returned edit.Version and model info
Aider:
0.86.3.dev,main@5dc9490bb35f9729ef2c95d00a19ccd30c26339cPython:
3.12.13Operating system: macOS
15.7.3Installation: source checkout; focused pytest reproduction
Edit format:
udiffModel: not applicable; reproduced by directly invoking deterministic unified-diff parsing code