Integrating Git with a Visual Merge Tool
February 22nd, 2009One of the first real points of frustration a developer encounters with Git is the initial unresolved merge conflict. And it’s only a matter of when and not if it’ll happen.
Merge Conflicts in Git
The root cause of the conflicts is unavoidable in any kind of parallel development: Updates that are made independently may modify the same or overlapping regions of the codebase. The longer the development remains independent, the greater the probability this will take place. For a tool that is basically based on branching and parallelism, Git is essentially inviting this “trouble.” (Rest assured that the benefits are still well worth it.)
As an example, in the course of Insoshi development, we’ve seen merge conflicts arise from
- Merging new edge features into contributions based on an earlier commit
- Applying fixes made against our production code to edge
And I’m sure Insoshi community developers encountered a number of conflicts when applying the new tabbed and AJAX-ified layout against code they’d developed against the original layout.
Here’s a re-creation of a recent merge conflict in the Insoshi repository:
$ git checkout -b edge_merge_example 757bf03d90be14fd393d457ec20455700a5fc751 Switched to a new branch "edge_merge_example" $ git branch exception_fix 07097a3b5a797b4c3d0e3ba53a0ad98a8861db79 $ git merge exception_fix Auto-merged app/controllers/people_controller.rb CONFLICT (content): Merge conflict in app/controllers/people_controller.rb Auto-merged app/models/person.rb CONFLICT (content): Merge conflict in app/models/person.rb Removed app/views/connections/show.html.erb Auto-merged spec/models/person_spec.rb Automatic merge failed; fix conflicts and then commit the result. |
What Git presents in the event of a conflict that requires manual resolution isn’t any different than what you would see in CVS or SVN:
app/models/person.rb
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | not deactivated? end end # Return the common connections with the given person. <<<<<<< HEAD:app/models/person.rb def common_contacts_with(person, options = {}) # I tried to do this in SQL for efficiency, but failed miserably. # Horrifyingly, MySQL lacks support for the INTERSECT keyword. (contacts & person.contacts).paginate(options) ======= def common_contacts_with(contact, options = {}) # I tried to do this in SQL for efficiency, but failed miserably. # Horrifyingly, MySQL lacks support for the INTERSECT keyword. (contacts & contact.contacts).paginate(options) >>>>>>> exception_fix:app/models/person.rb end protected ## Callbacks |
The contents of conflicting lines from the current version and version to be merged are placed inline with the code separated by “<<<<<<<”, “>>>>>>>” and “=======” markers. It’s ugly and depending on the extent of the conflicts, can be extremely confusing and time consuming to resolve. Thankfully, the conflicts in this example appear to be easily manageable. (And without delving back to far into the commit history, the conflict lines in the persons model probably were copied/cherry-picked from one branch to another and then an additional correction was made to one branch).
So for all of the things that Git does right, why doesn’t it offer a better way? The short answer is that it can’t by itself. It needs help.
Visual Merge Tools
A manual 3-way merge is the way to resolve these conflict. You’re using 3 versions of a file as the starting points: the version on the branch you’re on, the version on the branch you’re merging in, and their common ancestor. The common ancestor is used as the base with updates from either of the other two are applied on top of as direct selections or edited combinations at each conflict point.
Visual merge tools provide the interface for processing the 3-way merge and reduce the time and effort required. You’re presented with the three versions and can easily choose code from any and all of them to resolve conflicts.
If you’ve been using a VCS tool for a while, you’re probably already using a merge tool, either the one that came with it or one that you’ve picked up along the way out of necessity. If you haven’t used one before, now’s a good time as any to start.
Merge tools that I know about include
- Open Source
- Free from a commercial vendor
- Commercial
- Araxis Merge (Windows | Mac OS X)
- Beyond Compare
- Guiffy SureMerge
This is by no means a complete list. Be aware that some merge tools only offer a 2-way merge or require an upgraded/pro version to perform 3-way merges.
git mergetool
The git mergetool command allows for the integration of those tools into the merge process. Run after merge conflicts have been identified, it loops through the files that need to be resolved and provides the specified tool with the version information necessary to invoke the 3-way merge.
git mergetool already includes support for a number open source and freely available merge tools: kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff.
Support for additional tools including DiffMerge and Araxis Merge can be added via custom configuration settings provided a command-line call exists:
git config --global mergetool.[tool].cmd [command-line call] git config --global mergetool.[tool].trustExitCode [true|false] |
The “–global” flag is used so the setting will apply across all of your Git repositories.
The command line needs to accept the following file variables passed in as parameters:
- $LOCAL – Current branch version
- $REMOTE – Version to be merged
- $BASE – Common ancestor
- $MERGED – File where results will be written
git mergetool will create the versions as temporary files and set the variables appropriately before the tool command-line is executed.
If the tool returns a proper exit code after a successful or unsuccessful merge, then the trustExitCode setting can be set to true. Otherwise set it as false so you will be prompted as to whether the merge conflicts for a file were resolved.
Performing a Merge with Conflicts
The sequence of commands for a merge using mergetool would be
git merge git mergetool -t [tool] git add . git commit |
You can specify a default tool via the merge.tool setting
git config --global merge.tool [tool] |
This will allow you to just simply call
git mergetool |
Using a Supported Merge Tool
My main machine is a MacBook Pro and since I have Apple Developer Tools installed, I have opendiff/FileMerge installed. It’s one of the supported merge tools so I can immediately start using it:
git mergetool -t opendiff |
For the Insoshi conflict example you’ll see
$ git mergetool -t opendiff Merging the files: app/controllers/people_controller.rb app/models/person.rb Normal merge conflict for 'app/controllers/people_controller.rb': {local}: modified {remote}: modified Hit return to start merge resolution tool (opendiff): Normal merge conflict for 'app/models/person.rb': {local}: modified {remote}: modified Hit return to start merge resolution tool (opendiff): |
The FileMerge window for app/models/person.rb looks like

Because FileMerge is now handling the merge for this file, it’s processing all the differences and not just the conflict area. You can see this on the bottom status:
- status: 12 differences (12 left, 1 right, 1 conflict)
If you haven’t used FileMerge before, you’ll need to do a couple of things that aren’t immediately obvious:
- Drag up to reveal the “Ancestor” pane at the bottom of the window
- Click on the conflict section numbers located between the LOCAL and REMOTE panes in order to select an action
If you complete the merge and save it, mergetool will accept the result.
If you abort and quit out of the merge without saving, you will be prompted if the save was successful.
app/models/person.rb seems unchanged. Was the merge successful? [y/n] n merge of app/models/person.rb failed |
If you’re in the middle of resolving conflicts in a number of files, this will exit out from the process. You just need to re-run the mergetool command:
git mergetool -t opendiff |
and it’ll pick up with the last unmerged file.
Using a Configured Merge Tool
DiffMerge is available for OS X. The binary install is simple (the usual copy to the Application folder) and it comes with a shell script so it can be invoked from the command-line. The minimal command to start a 3-way merge is
diffmerge --merge --result=$MERGED $LOCAL $BASE $REMOTE |
The order of the files dictates which pane (left, middle or right) it be displayed in. The middle pane is used as the editor for the merge and will need to start off with the common BASE version. You can explicitly set the title of each pane via -t1=, -t2=, -t3= flags if you like (see the DiffMerge documentation [PDF]).
I’ve tested the return code for successful and aborted merge scenarios and found that it returns 0 if the DiffMerge application ran successfully, not if the actual merge was successful. Git will rely on a comparison between the temporary $MERGED file to determine if any changes were saved. It will prompt you in the event the file is unchanged.
The configuration settings will be
git config --global mergetool.diffmerge.cmd \ "diffmerge --merge --result=\$MERGED \$LOCAL \$BASE \$REMOTE" git config --global mergetool.diffmerge.trustExitCode false |
You need to escape the $’s in the variables otherwise the shell will try the do the substitution before command gets placed in the configuration.
The .gitconfig entries will look like
[mergetool "diffmerge"]
cmd = diffmerge --merge --result=$MERGED $LOCAL $BASE $REMOTE
trustExitCode = false |
Running
git mergetool -t diffmerge |
starts DiffMerge showing the current version in the left pane, the merge result in the middle pane and the remote version in the right pane:

This may be easier for you to use to visualize the merge process than opendiff/FileMerge.
You should see the same basic output from execution of git mergetool with DiffMerge as was seen from opendiff/FileMerge:
$ git mergetool -t diffmerge Merging the files: app/controllers/people_controller.rb app/models/person.rb Normal merge conflict for 'app/controllers/people_controller.rb': {local}: modified {remote}: modified Hit return to start merge resolution tool (diffmerge): Normal merge conflict for 'app/models/person.rb': {local}: modified {remote}: modified Hit return to start merge resolution tool (diffmerge): |
If you’re using Git on Windows, definitely check out these posts on configuring DiffMerge and Git:
There’s always a few extra things to be aware of on the Windows platform, especially when it comes to passing in command-line arguments in mixed UNIX shell/Windows CMD context.
A few final notes:
- git mergetool saves the merge-conflict version of the file with a “.orig” suffix. Make sure to delete it before adding and committing the merge or add *.orig to your .gitignore
- A custom merge driver can be configured and set in order to override the built-in behavior of the git merge command. This could be useful in order to deal with additional filetypes via an external diff/merge tool or if you wanted to invoke a visual merge tool for all merges.
- IDE integrations for handling merges will depend both on the level of integration that’s possible with Git and the merge tool.
March 26th, 2009 at 2:41 pm
One shining Internet for you, sir/madam! Many thanks for this well-written piece.
Oh, and please set an explicit tab order on this Leave a comment form. The Archives search box is inbetween “name” and “mail” on Webkit.
April 1st, 2009 at 12:05 am
Thanks!
And I did fix the tab order. The theme I’m using did set explicit values but didn’t account for having both search and comment fields (aka they both started with tabindex=”1″).
May 1st, 2009 at 10:05 pm
Thanks for this post. I’m new to git and having a tough time without my Eclipse Subclipse SVN crutch
May 10th, 2009 at 6:00 am
Very nice article!!!
maybe you knew this, but you can also tell git not create the .orig file(s);
git config –global merge.keepBackup false
May 27th, 2009 at 6:26 am
Amazing blog post indeed! I just noticed the power of mergetool…
Anyway, I am still missing a shining tool to manage merges where say a bunch of similar files have changed names, because git considers these as additions/deletions?
Also, I’m trying to find out how to interact with the :1:, :2: and :3: stages more than showing the files, but that’s maybe out of scope here
June 12th, 2009 at 6:15 am
[...] Integrating Git with a Visual Merge Tool Tags: branch, git, merge [...]
September 18th, 2009 at 12:26 pm
It appears that in git 1.6.3-rc1 and later, the setting to tell git to not leave the .orig files hanging around is “mergetool.keepBackup”, and no longer “merge.keepBackup”.
see git.git commit 70af4e9 and the man page for git-config.
November 30th, 2009 at 7:18 am
I setup Diffmerge on Mac, and aside from the obvious rename of “diffmerge.sh” to “diffmerge”, when I try to merge a conflict (I’m talking about my first attemp using diffmerge with git – on ubuntu/meld it was straightforward), diffmerge quits saying it can’t find the BASE version file – in fact, I can see only the BACKUP, LOCAL and REMOTE in the filesystem during the merge.
???
December 21st, 2009 at 5:26 am
Hello, I have a question, how do I setup the opendiff (FileMerge) as the default and global merge.tool ? Didin’t quite got that from reading the article.
Thanks
May 7th, 2010 at 1:54 pm
I finally now can understand mergetool. Well written article. thanks a lot!!
July 13th, 2010 at 8:43 am
Another thing I found useful: in git-gui, right click a conflicted file, and pick ‘Run merge tool’ context menu.
This way you can pick the conflicts you want to go through in one pass, as well as see the diff in advance; rather than relying on the order git-mergetool’s loop dictates.
July 29th, 2010 at 4:17 pm
[...] Integrating Git with a Visual Merge Tool [...]
October 26th, 2010 at 11:10 am
Thanks for the article, it really helped me out.
November 25th, 2010 at 8:23 am
Thanks a lot, it was very helpful!!
March 13th, 2011 at 6:20 am
Thanks, very helpful!
March 30th, 2011 at 6:23 am
git…
Git Resources Books…
April 16th, 2011 at 5:12 am
Exxxxxcelent…. Thanks a lot!
July 27th, 2011 at 9:12 am
That is particularly imformative. I appreciate you all the content.
November 24th, 2011 at 10:50 am
Excellent peace of infor . Thanks.
December 31st, 2011 at 11:39 am
BlackScale RT marcustroy All this work gets put into something just for some hater to leave a comment on a post to say it Sucks.
January 12th, 2012 at 11:29 am
[...] Here is a more complete tutorial on merging in DiffMerge. Share this:FacebookTwitterLinkedInDiggRedditStumbleUponEmailPrintLike this:LikeBe the first to like this post. [...]
January 15th, 2012 at 4:49 pm
[...] vorher ein Mergetool eingestellt haben. Das ganze ist eigentlich auch recht einfach und ist hier detailiert beschrieben. Im Grunde muss man nur ein Merge Programm sich aussuchen (ich hab z.B. KDiff3 gewählt. Sieht aber [...]
February 19th, 2012 at 4:27 am
[...] you start make sure you already created a Mergetool. It’s not that difficult and you will find a detailed instruction here. In fact all you have to do is to choose a Merge Program (I’ve chosen KDiff3 but it’s ugly). [...]
April 27th, 2012 at 4:37 am
If you run into DiffMerge starting with a “DiffMerge Error file xxxx not found” it could be due to no common base. The solution is to setup your diffmerge cmd a bit different.
Use
git config –global mergetool.diffmerge.cmd ‘diffmerge –merge –result=”$MERGED” “$LOCAL” “$(if test -f “$BASE”; then echo “$BASE”; else echo “$LOCAL”; fi)” “$REMOTE”‘ instead.
Credit to Brian Campbell on Stack Overflow http://stackoverflow.com/questions/4439268/problem-with-git-diffmerge-on-os-x
May 7th, 2012 at 1:18 am
Git…
Allgemein IP: 81.93.112.45 Login: erster Buchstabe…
August 15th, 2012 at 6:47 am
[...] configure a different diff tool, read this article. Right about now, I am starting to miss my Eclipse diff view. Oh, how many times has it come to my [...]
September 17th, 2012 at 5:32 am
How we use git…
Guiding principle: sharing code early and often is…
November 26th, 2012 at 1:08 am
[...] 비교하고 병합하려니 토할 것 같아서 미친 듯이 웹을 뒤졌다. 그 와중에 Integrating Git with a Visual Merge Tool라는 글 덕분에 삽질을 덜었다. [...]
February 26th, 2013 at 7:13 am
Responsive Homepage Doc Stories…
As a user, I want to see my local station when…
May 17th, 2013 at 1:32 pm
I use gitk to see the “tree” of the project.
I would like to have Beyond Compare 3 (I have it on my machine and works very well for “git difftool”) to see the difference between the files I selected. Have spent very much time but failed. I hate the default tool gitk use. It does not give the whole picture about the change.
Any one can help?
Thanks
May 17th, 2013 at 1:33 pm
BTW, this article is very well written, very understandable.