Skip to content

Read quoted and " b/"-containing paths in Patch() headers - #129

Merged
matt-edmondson merged 4 commits into
mainfrom
fix/patch-header-paths
Sep 28, 2026
Merged

matt-edmondson merged 4 commits into
mainfrom
fix/patch-header-paths

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #120

What changed

GitPatchParser.ReadPathFromFileStart found the new-side path by taking whatever followed the last " b/" in the header. That broke in two cases:

  • A path containing b/. Plan b/notes.txt came back as notes.txt.
  • A C-quoted path. Git quotes a name containing ", \ or a control character even with core.quotepath=false. That header has no b/, so Patch() threw GitParseException for the whole repository.

The parser now reads the header this way:

  • No rename: both sides are the same path, so it splits a/P b/P at the midpoint and checks that the halves match. Git's own git_header_name does the same.
  • Quoted operands: it decodes C escapes before building the path: \", \\, \t, \n and the others, plus octal \NNN bytes read as UTF-8. The rename from / rename to lines are decoded the same way, and so is the diff --cc path.
  • Rename: it falls back to the old " b/" search. That is only a placeholder, because the rename to line replaces it, as before.

Tests

  • There are new fixtures captured from real git: patch-b-slash-in-path.txt, patch-quoted-path.txt and patch-quoted-rename.txt.
  • New parser tests cover Plan b/notes.txt, say "hi".txt, a quoted rename, an octal UTF-8 escape, and a quoted file followed by an ordinary one in the same patch. The last one checks that one quoted name no longer breaks the rest.
  • The fixtures avoid tab characters in names. The Windows CI leg's RelativeFilePath validation rejects control characters, so tab-named fixtures would fail there.

With the parser change reverted, all 5 new tests fail. With it, the full suite passes: 713 tests, 0 failed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VKxYoUXQqgRKJR6ynbvnms


Generated by Claude Code

The diff --git header was split at the last " b/", so a path such as
"Plan b/notes.txt" came back as "notes.txt", and a header whose paths
git had C-quoted threw GitParseException for the whole patch.

Headers without a rename are now split at the midpoint, as git's own
git_header_name does, and quoted operands and rename from/to lines are
decoded, octal escapes included.

Fixes #120

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VKxYoUXQqgRKJR6ynbvnms
RelativeFilePath normalizes separators, so on Windows the parsed
"Plan b/notes.txt" reads back as "Plan b\notes.txt". Compare against
the same value built as a RelativeFilePath, as the builder tests do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VKxYoUXQqgRKJR6ynbvnms
UnquotePath is now internal so its escape table can be tested directly;
most escapes decode to control characters that RelativeFilePath refuses
on Windows, so they cannot go through a parsed header. Adds header cases
for one side quoted and the other not, and for malformed quoting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VKxYoUXQqgRKJR6ynbvnms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VKxYoUXQqgRKJR6ynbvnms
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Patch() misreports paths containing " b/" and throws for the whole repo when any file name is C-quoted by git

2 participants