Skip to content

Report conflicted binary files and modify/delete conflicts as unmerged in Patch() - #192

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/123-unmerged-patch-shapes
Oct 7, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/123-unmerged-patch-shapes

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #123

What changed

GitPatchParser used to mark a file as conflicted only when a @@@ line followed its header. Two kinds of unmerged output that git produces have no such line:

  • Conflicted binary file (UU). The output is diff --cc bin.dat / index … / Binary files differ. Patch() returned it as Kind = Modified, IsConflicted = false.
  • Modify/delete conflict (DU/UD). git prints only * Unmerged path d.txt. The top-level loop skipped that line, so the path was missing from Patch() entirely.

The fix:

  • Every diff --cc / diff --combined header now counts as conflicted, with Kind = Unmerged and no hunks, whether or not a @@@ line follows. The kind is set after the header lines are read, so a mode line can't turn it back into Added or Deleted.
  • Each * Unmerged path <path> line becomes a conflicted GitFilePatch with no hunks. A file's header and its combined body both stop at such a line, so it can't be swallowed by the file before it.

Tests

  • GitPatchParserTests.FlagsAConflictedBinaryFileAndAModifyDeleteConflict reads a new fixture, patch-unmerged-binary-and-deleted.txt, captured with git 2.43.0 from a real merge that leaves both kinds of conflict.
  • GitPatchParserTests.FlagsALongFormCombinedHeaderAsConflicted covers a diff --combined header.
  • GitPatchRoundTripTests.PatchAndDiffAgreeOnUnmergedPathsAfterABinaryAndAModifyDeleteConflictAsync is an integration test. It runs a real merge and checks that Patch() and Diff() report the same unmerged paths.

I checked that the tests catch the bug: with the parser change reverted, all three fail, and with it they pass. The full suite passes locally (749 tests, 0 failed).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JgoVAth3nGZUgbKrSgVCYX


Generated by Claude Code

…d in Patch()

GitPatchParser marked a file conflicted only when a @@@ line followed its
header. A conflicted binary file stops at "Binary files differ", so it came
back as an ordinary binary modification, and a modify/delete conflict is
printed only as "* Unmerged path <path>", which the parser skipped. Every
diff --cc / diff --combined header is now conflicted, and each
"* Unmerged path" line becomes a conflicted file with no hunks, so Patch()
and Diff() agree on the set of unmerged paths.

Fixes #123

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JgoVAth3nGZUgbKrSgVCYX
@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 9b31dc9 into main Oct 7, 2026
23 of 24 checks passed
@matt-edmondson
matt-edmondson deleted the fix/123-unmerged-patch-shapes branch October 7, 2026 05:37
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() reports a conflicted binary file as an ordinary modification and omits modify/delete conflicts entirely, disagreeing with Diff()

2 participants