Skip to content

Resolve merge conflicts in a three-pane merge window - #12

Merged
leafOfTree merged 16 commits into
masterfrom
dev-merge
Oct 27, 2026
Merged

leafOfTree merged 16 commits into
masterfrom
dev-merge

Conversation

@leafOfTree

Copy link
Copy Markdown
Owner

Summary

You can now resolve merge conflicts in Vim, so you no longer have to go back to a terminal. The window is modeled on IntelliJ IDEA's Merge dialog.

  • Entry point: when a pull or a branch merge (<C-o> in :ProjectGitBranch) stops on conflicts, the local changes open on the Unmerged changelist. Before, you only got git's output. Opening a conflicting file (Enter by default) shows yours | result | theirs in a new tab.
  • Merge window (autoload/project/git_merge.vim):
    • Conflicts line up across the three panes and are colored by state: unresolved, taken from a side, or edited by hand.
    • All panes are read-only. Each key acts on the conflict under the cursor, and any choice can be changed or undone.
    • Keys: < yours, > theirs, = both, e edit by hand, u undo, ]c / [c move, L / R take one side for every conflict.
    • q writes the file and runs git add. It is refused while conflicts are unresolved. Q discards your choices.
    • A help bar at the bottom always shows the keys, and the status line shows the conflict under the cursor (2/5 3 unresolved). Both use the keys from git_merge_mappings.
    • Pane titles show the branch and commit each side comes from. In a rebase, "yours" is the commit being rebased.
  • After the last file, it offers to commit the merge, or to continue the rebase, cherry-pick or revert. A in the local changes aborts it.
  • Conflict states: all unmerged states (AA, DU, UD, ...) are listed as conflicts, not only UU. A file deleted on one side asks whether to keep it or delete it.
  • Branch list: it runs git fetch --all --prune in the background when it opens, and refreshes only when a branch moved. git_branch_fetch turns this off.
  • CI: Basic-Test installs Neovim from its release. The neovim PPA has no build for Ubuntu 26.04, which ubuntu-latest now uses, so the job was failing before any test ran.
  • Demo: test/demo_merge.sh [merge|rebase|delete] creates a repository stopped on conflicts so you can try the window by hand.

Testing

  • New test/cases/git_merge_test.vim cases cover:
    • parsing the conflicts, and the merge state;
    • taking a side, undo and both;
    • edit by hand, including closing the edit window with :wq, :x and :q!;
    • accept and discard, and finishing the merge;
    • abort;
    • configured keys and the conflict number.
  • New case in test/cases/git_branch_test.vim: a branch pushed from another clone shows up after the background fetch.
  • Vim 9.1 runs locally: 140 passed. CI on dev-merge is green.

https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd


Generated by Claude Code

A pull or branch merge that stops on conflicts now opens the local
changes on the Unmerged changelist instead of only printing git's
output. Enter on a conflicting file opens yours | result | theirs in a
new tab, like IntelliJ IDEA's Merge dialog: conflicts line up and are
colored by state, every choice applies to the conflict under the cursor
and can be changed or undone, and the file is written and added only
once all are resolved. After the last file it offers to commit the
merge or continue the rebase, cherry-pick or revert; A aborts it.

All unmerged statuses (AA, DU, UD, ...) are listed as conflicts now, not
only UU, and a file deleted on one side asks to keep or delete it.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
It makes a repository stopped on conflicts in a merge, a rebase or with
a deleted file, then prints how to open it and the keys to use.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
The help colored the first word of each label too, since labels follow
the keys after spaces. Keys are highlighted by position now. The result
pane's status line cut its start when narrow; the file name is cut first.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
Saving an edited conflict wiped its buffer during the write, so the q of
:wq or :x then closed the result pane below it. The edit window now
closes once the write is done, and any way of closing it, :q! too, goes
back to the result.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
Like the commit message, where closing commits, q accepts the result.
It is refused while conflicts are unresolved. Q discards the choices.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
New remote branches only showed up after a git fetch in a terminal. The
list now runs git fetch --all --prune as a job when it opens and shows
the branches again once it is done. git_branch_fetch turns it off.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
w next to q read as two ways to do different things. q accepts, like
closing the commit message commits; Q discards.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
The status line and messages said q and w even when git_merge_mappings
set other keys.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
ubuntu-latest moved to 26.04, which the neovim PPA does not build for,
so adding it failed the job before any test ran.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
Each fetch redrew the list even when nothing changed, flashing it and
the cursor, plus a redraw that cleared the input line. Now the refs are
compared first, and a failed reload keeps the list as it was instead of
leaving it empty.

Claude-Session: https://claude.ai/code/session_01BwofrMkZ4QpGsCYYSgncrd
@leafOfTree
leafOfTree merged commit 5220e98 into master Oct 27, 2026
16 checks passed
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