Use vimdiff to resolve git/subversion/mercurial merge conflicts effectively

  |   Source

If you are already a vim master and you are impatient, please jump to the Quick start section at the end of this article.

In this post, I use git as an example of version control software. But you can use any other version control softwares instead.

The reasons to use vimdiff to do the merge?

  • vimdiff is free (vim)
  • vimdiff works on any OS
  • vimdiff works on non-GUI environment
  • Efficient, all the operations are finished by keyboard
  • light weight

Set up vimdiff

The vimdiff as a merge tool will display several buffers to show YOURS/THEIRS/ORIGINAL code.

First, add following code into your ~/.vimrc,


set laststatus=2 "show the status line

set statusline=%-10.3n  "buffer number

The purpose of above two lines is to display buffer number at the status line of vim. It's OPTIONAL. You don't need see the buffer number is you are familiar with the all the buffer's position. The left top is buffer number 2. The middle top is buffer number 3. The right top is buffer number 4.

Second, if you know the buffer number, you can use hot key like ",2" (press comma first, then press two key as quickly as possible) to pull change from buffer number 2. Add below code into your ~/.vimrc to set up hot keys:


map <silent> <leader>2 :diffget 2<CR> :diffupdate<CR>

map <silent> <leader>3 :diffget 3<CR> :diffupdate<CR>

map <silent> <leader>4 :diffget 4<CR> :diffupdate<CR>

Set up git

To use vimdiff as default merge tool:


git config --global merge.tool vimdiff

git config --global mergetool.prompt false

Create a git project which has conflicting merges

I already set up a "hello world" project at https://github.com/redguardtoo/test-git-mergetool for your practice.

It has three branches "master", "bob", and "chen".


git clone git://github.com/redguardtoo/test-git-mergetool.git

cd test-git-mergetool

git checkout -b bob origin/bob # create local mirror of bob branch

git checkout -b chen origin/chen # create local mirror of chen branch

Bob and Chen has edited same files. So please merge branch "bob" into "master" at first. Then merge from "chen". The merge conflicts will be created.


git branch # double check that we got three local branches: master, bob, chen

git checkout master # set master branch as main branch

git merge bob #this is ok, because bob is the first one to merge changes

git merge chen # now some conflicts created because Bob has already edited and merged same files

Resolve merge conflict

Now start merge tool:


git mergetool

Git will invoke vimdiff with the following window layout. There are four buffers in this layout: http://blog.binchen.org/wp-content/uploads/2013/06/wpid-git-merge-tool-nq8.png

Here is the explanation of each buffer:

Buffer Explanation Buffer Number
THEIRS (LOCAL) contents of the file on the current branch 2
BASE common base for the merge 3
YOURS (REMOTE) contents of the file to be merged. 4
MERGED The file containing the conflict markers. You need edit and commit this file. 1

Some people name THEIRS and YOURS buffer to LOCAL and REMOTE buffer. That's totally fine because names are just names. The point is that the top middle buffer is the BASE one which contains the original code before Bob and Chen committing any code. And the bottom buffer is the mess which contains resolved/unresolved conflicts where you actual editing work happens.

You could press hot key ",2" (comma + two), Then you pick the content from THEIRS buffer (the top left buffer). It means you will use the Bob's code and discard Chen's code in MERGED buffer.

You could press hot key ",3" (comma + three), Then you pick the content from BASE buffer (the top middle buffer). It means you will discard either Bob's code or Chen's code in MERGED buffer.

You could press hot key ",4" (comma + four), Then you pick the content from YOURS buffer (the top right buffer). It means you will use Chen's code and discard Bob code in MERGED buffer.

Or you can edit the content directly in MERGED buffer. Anyway, git only care about the file binding to MERGED buffer. Any other buffer will be ignored by git.

You can use hot key "[c language=" and "][/c]c" to navigate to previous/next conflict (including the conflict resolved by git automatically) in current file which is binding to MERGED buffer.

After finishing editing of the conflicting file in MERGED buffer, you can use hot key ":xa" to exit vimdiff. Git will open next conflicting file with vimdiff automatically.

When you have resolved all the conflicts, follow the hint of git to commit your changes.

Tips

  • A vim plugin called fugitive.vim (https://github.com/tpope/vim-fugitive) can do this too. Actually it can do much more git stuff than merge. I cannot write this article without reading its code.
  • You can use Emacs to do the similar job (http://stackoverflow.com/questions/1817370/using-ediff-as-git-mergetool). For me, Emacs start up time is too much for this task. Some people use emacsclient which has other overhead which I don't like.
  • If you prefer merge tool with GUI, you can use command line git mergetool -t gvimdiff instead.
  • The ":diffget" command is valid if and only if there are MARKED conflicts in merged buffer. If there are NOT any marked string like ">>>>" or "<<<<<" around current change (you can jump to previous/next change by press "[c language=" or "][/c]c"), it means the git have automatically resolved potential conflict for you. Review this kind of change is still wise because git is not as smart as human.
  • If you prefer navigating between the unresolved conflicts only, you can install Tim Pope's vim-unimpaired and use hot key "[n" and "]n" to do the navigation.
  • I map "[n" and "]n" to more handy hot keys:

map ]] ]n

map [[ [n

Quick start

You can use command line like "git mergetool -t vimdiff" to start vimdiff from git.

So the minimum set up is adding three lines of code into your ~/.vimrc:


map <silent> <leader>2 :diffget 2<CR> :diffupdate<CR>

map <silent> <leader>3 :diffget 3<CR> :diffupdate<CR>

map <silent> <leader>4 :diffget 4<CR> :diffupdate<CR>

Then you can press hot key ",2" ",3" ",4" in vimdiff to pull change from top three buffer. The bottom buffer is for editing the code with markers which is actually your only work space.

":help vimdiff" for other hot keys.

Comments powered by Disqus