Using Vim to resolve merge conflicts

16 November 2023

Resolving diffs is a fundamental part of every software engineers job. Over the last couple of years I've experimented with several diff resolution tools and whittled it down to this simple workflow in Vim that satisfies my needs.

If you have linting configured, I recommend telling git to put the base, local, remote files in your computer's temp directory rather than the current working directory to prevent it from linting all the files together. When working with Go files for example, the linter will complain about duplicate function definitions unless.

[mergetool]
writeToTemp = true

I resolve merge conflicts in git using git-mergetool(1). So I've created an alias for the merge tool so I can type git mt to start resolving the conflicts and I've changed the tool command to enlarge the window containing the merged changes to better see the context.

[alias]
mt = mergetool -t vimdiff

[mergetool "vimdiff"]
cmd = nvim -d $MERGED $LOCAL $BASE $REMOTE -c '/<<<<<' -c 'wincmd J'

This does two things. First, it searches for <<<<< so Vim will automatically jump to the first chunk, and secondly, it moves the "merged" window to the bottom which allows me to seem more of whats going on.

+--------+-------+------+--------+          +-------+------+--------+
|        |       |      |        |          |       |      |        |
|        |       |      |        |          | LOCAL | BASE | REMOTE |
|        |       |      |        |          |       |      |        |
| merged | local | base | remote |  ----->  +-------+------+--------+
|        |       |      |        |          |                       |
|        |       |      |        |          |        MERGED         |
|        |       |      |        |          |                       |
+--------+-------+------+--------+          +-------+------+--------+

Accepting changes

The following keymaps accept the upper or lower change of the diff the cursor is currently in. It accepts the change by deleting the other change and the >>>, === and <<< metadata

nnoremap <space>k :call search('^<<', 'b')<CR>dd:call search('^==')<CR>dV:call search('^>>')<CR>
nnoremap <space>j :call search('^<<', 'b')<CR>dV:call search('^==')<CR>:call search('^>>')<CR>dd