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 opendiffFor 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 opendiffand 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 = falseRunning
git mergetool -t diffmergestarts 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
March 5th, 2010 at 4:53 am
http://homogeneouslopes.blogspot.com/2010/03/video-fatima-lopes-flees-capturer.html
games free best best download.
March 6th, 2010 at 5:29 am
http://panthersinfiniti.blogspot.com/2010/03/scholarship-infiniti-video-technologic.html
video funny best games free.