Skip to content

chore(tools): Fix hunk +Y offset when staging a subset of hunks#592

Merged
JeanMertz merged 1 commit intomainfrom
prr159
Apr 30, 2026
Merged

chore(tools): Fix hunk +Y offset when staging a subset of hunks#592
JeanMertz merged 1 commit intomainfrom
prr159

Conversation

@JeanMertz
Copy link
Copy Markdown
Collaborator

When git diff-files --unified=0 emits hunks for a file, each hunk's +Y value reflects the cumulative line shift of every preceding unstaged hunk in that file. Staging only a subset of those hunks with git apply --cached --unidiff-zero uses +Y to position each change, so carrying over the full-diff offset causes the patch to land at the wrong line whenever unselected hunks contributed to the shift.

Two fixes are applied:

For git_stage_patch, a rewrite_hunk_y helper is introduced in hunk.rs that recomputes each selected hunk's +Y from the net line change of the preceding selected hunks only. A cumulative_offset accumulates (additions - removals) as hunks are assembled, and each header is rewritten before the patch is handed to git apply.

For git_stage_patch_lines, the HunkHeader struct previously preserved new_start from the original full-diff header and then applied it as an offset to the sub-hunk. That offset was wrong for the same reason. The field is removed and build_sub_hunk now derives new_start directly from the sub-hunk's own content: for replacements and pure removals it equals old_start; for pure insertions it equals old_start + 1.

Regression tests are added for both tools covering the scenario where an addition hunk is staged while an unrelated removal hunk higher in the file remains unstaged.

When `git diff-files --unified=0` emits hunks for a file, each hunk's
`+Y` value reflects the cumulative line shift of every preceding
unstaged hunk in that file. Staging only a subset of those hunks with
`git apply --cached --unidiff-zero` uses `+Y` to position each change,
so carrying over the full-diff offset causes the patch to land at the
wrong line whenever unselected hunks contributed to the shift.

Two fixes are applied:

For `git_stage_patch`, a `rewrite_hunk_y` helper is introduced in
`hunk.rs` that recomputes each selected hunk's `+Y` from the net line
change of the preceding *selected* hunks only. A `cumulative_offset`
accumulates `(additions - removals)` as hunks are assembled, and each
header is rewritten before the patch is handed to `git apply`.

For `git_stage_patch_lines`, the `HunkHeader` struct previously
preserved `new_start` from the original full-diff header and then
applied it as an offset to the sub-hunk. That offset was wrong for the
same reason. The field is removed and `build_sub_hunk` now derives
`new_start` directly from the sub-hunk's own content: for replacements
and pure removals it equals `old_start`; for pure insertions it equals
`old_start + 1`.

Regression tests are added for both tools covering the scenario where an
addition hunk is staged while an unrelated removal hunk higher in the
file remains unstaged.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
@JeanMertz JeanMertz merged commit 3b3e154 into main Apr 30, 2026
13 checks passed
@JeanMertz JeanMertz deleted the prr159 branch April 30, 2026 16:50
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.

1 participant