Skip to content

Avoid conflicts and duplicate lines when moving a patch to an earlier commit - #6167

Open
stefanhaller wants to merge 8 commits into
masterfrom
improve-moving-hunk-to-earlier-commit
Open

stefanhaller wants to merge 8 commits into
masterfrom
improve-moving-hunk-to-earlier-commit

Conversation

@stefanhaller

Copy link
Copy Markdown
Collaborator

When moving a custom patch to an earlier commit, or into a new commit before the original one, lazygit applies the patch to the earlier commit and then replayed the original commit unchanged. It relied on git noticing during the replay that the patch's changes are already there. This doesn't always work:

  • If the patch contains only some of several adjacent added lines, replaying the original commit stops with a conflict.
  • If git can place the patch's added lines in two positions because they repeat the line after them, the replay may put them in the other position than the patch did. Git then keeps both copies, and the file ends up containing the patch's lines twice. There's no conflict, so this goes unnoticed.

Fix this by making the rebase pick a copy of the source commit instead. The copy has the same tree, author and message, but its parent is the original parent with the patch applied. Picking it only replays the source commit's changes minus the patch, so git no longer has to recognize the patch as already applied.

stefanhaller and others added 8 commits October 30, 2026 17:46
When moving a custom patch to an earlier commit, or into a new commit
before the original one, lazygit applies the patch to the earlier
commit and then replays the original commit unchanged. It relies on
git noticing during the replay that the patch's changes are already
there. This doesn't always work:

- If the patch contains only some of several adjacent added lines,
  replaying the original commit stops with a conflict.
- If git can place the patch's added lines in two positions because
  they repeat the line after them, the replay may put them in the
  other position than the patch did. Git then keeps both copies, and
  the file ends up containing the patch's lines twice. There's no
  conflict, so this goes unnoticed.

When moving into a new commit before the original one, the conflict of
the first case doesn't show the usual "Conflicts!" popup; lazygit
focuses the commits panel instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If moving a custom patch into a new commit before the original one
results in a conflict, lazygit shows the "Conflicts!" popup, but then
immediately focuses the commits view. This closes the popup, and the
user is left in a rebase that is stopped at a conflict without being
told about it.

The test uses a pre-commit hook that changes a file of the new commit;
this makes picking the original commit conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If the patch comes from a merge commit, moving it to a later commit
leaves the user in a rebase that is stuck before its first todo.
Moving it to an earlier commit, or into a new commit before the merge,
finishes, but with surprising results. The rebase doesn't replay a
merge commit; it recreates it from its parents. If the patch contains
changes from the merged branch, the commits on that branch that made
them become empty. And a new commit "before" the merge ends up at the
tip of the merged branch.

Moving a patch to a later merge commit gets stuck in the same way.
Moving a patch to a merge commit isn't useful in either direction, so
disable both.

The other patch commands amend the merge commit itself and work fine,
so they stay enabled. They are useful for removing changes that were
added to a merge commit by accident.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The next commit uses this to replay a modified copy of a commit in
place of the original one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… commit

When moving a custom patch to an earlier commit, we apply the patch to
the earlier commit and then let the rebase pick the commit that the
patch came from, unchanged. This relies on git noticing during the pick
that the patch's changes are already there. If the patch contains only
some of several adjacent added lines, git doesn't notice, and the pick
stops with a conflict. If git can place the patch's added lines in more
than one position, it may place them differently for the pick than for
the patch. It then adds them a second time, without any conflict.

Make the rebase pick a copy of the source commit instead. The copy has
the same tree, author and message, but its parent is the original
parent with the patch applied. Picking it only replays the source
commit's changes minus the patch, so git no longer has to recognize
the patch as already applied. Create the copy with plumbing commands
and a temporary index; this doesn't touch the working tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mmit before

Moving a custom patch into a new commit before the original one has the
same problem as moving it to an earlier commit. After committing the
patch, the rebase picks the original commit unchanged and relies on git
noticing that the patch is already there.

Make the rebase pick a copy of the original commit whose parent already
contains the patch, as we do when moving to an earlier commit. Here,
the copy's parent normally has the same tree as the new commit, so
picking the copy doesn't conflict. Only a commit hook that changes the
new commit's files could make it conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The handlers for moving the patch into a new commit before or after the
original one differ only in the git command they call. The next commit
fixes a bug that both have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If moving a custom patch into a new commit stops at a conflict,
CheckMergeOrRebase shows the "Conflicts!" popup. The handler then
focused the commits view anyway, and this closed the popup.

Only focus the commits view if the operation finished.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stefanhaller stefanhaller added the bug Something isn't working label Oct 30, 2026

This branch has not been deployed

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant