Skip to content

Write a partial rename patch as a plain change to the new path - #193

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/126-partial-rename-patch
Oct 7, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/126-partial-rename-patch

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #126

What changed

GitFilePatch.PatchFor(hunks) always wrote the file's full header. For a renamed file, that header includes similarity index, rename from and rename to. So when a caller picked one hunk of a staged rename and unstaged it with Apply(file.PatchFor([hunk])).ToIndex().Reversed(), git also undid the rename: b dropped out of the index and the other hunk's change ended up on a.

Now, when PatchFor is given only some of a renamed or copied file's hunks, it writes them under a plain modification header on the new path:

  • the diff --git, --- and +++ lines all name the new path
  • the similarity, rename and copy lines are dropped
  • the index and mode lines are kept
  • C-quoted paths stay quoted

When every hunk is selected, the full rename header is kept as before. That's the option the issue's acceptance criteria describe.

Tests

  • GitPatchTests.PatchForSomeHunksOfARenameWritesAPlainChangeToTheNewPath and PatchForSomeHunksOfAQuotedRenameKeepsTheQuoting check the exact header text produced.
  • GitPatchTests.PatchForEveryHunkOfARenameKeepsTheRenameHeader is a guard for the all-hunks case. It passes with or without the fix.
  • GitPatchRoundTripTests.UnstagingOneHunkOfAStagedRenameKeepsTheRenameAsync runs the issue's reproduction against real git. After the reverse-apply, the index still holds b as a rename of a with only the line-28 change, and the line-2 change is unstaged.

With the fix reverted, the three new behaviour tests fail; with it, they pass. The full suite passes locally (750 tests, 0 failed).

🤖 Generated with Claude Code

https://claude.ai/code/session_01JgoVAth3nGZUgbKrSgVCYX


Generated by Claude Code

claude added 2 commits October 7, 2026 03:33
GitFilePatch.PatchFor always wrote the file's full header, so for a renamed
file it carried the rename from / rename to lines under whatever subset of
hunks the caller picked. Reverse-applying one hunk of a staged rename to the
index then undid the rename as well: b left the index and the other hunk
moved onto a. A subset of a renamed or copied file's hunks is now written
under a plain modification header on the new path, and the full header is
kept only when every hunk is selected.

Fixes #126

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

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.

Reverse-applying one hunk of a staged rename (PatchFor + Apply().ToIndex().Reversed()) undoes the whole rename

2 participants