Setting up diff and merge tools for Git on Windows

UPDATE!!! I’m now using an easier way of configuring diff and merge tools. If you don’t mind using KDiff3 then you might want to check that out first. If you want flexibility in which tools you use and how they’re used, then read on. – DT, 26 Mar 2010

I finally got tired of reading diff outputs from git on the command line, and decided to hook up a visual diff and merge tool. After piecing together hints from various posts around ye olde intraweb I’ve now got somethings that works. I am using Git on MSys to get Git working on Windows via a PowerShell command line, but a similar setup might work via Cygwin or via the cmd.exe command line.

Setting up a diff tool

Update 2009-05-20: This approach stopped working for me when I upgraded to Windows 7 and Git 1.6.3. I had to switch to using git difftool instead.

The easiest way I found to do this was to create a shell script wrapper for your diff tool of choice. To make things easy on me I put the script, git-diff-wrapper.sh in C:\Program Files\Git\cmd, which is in my Path environment variable. The file contents looks like this:

#!/bin/sh

# diff is called by git with 7 parameters:
#  path old-file old-hex old-mode new-file new-hex new-mode
"C:/Program Files/SourceGear/DiffMerge/DiffMerge.exe" "$2" "$5" | cat

#"C:/Program Files/WinMerge/WinMergeU.exe" "$2" "$5" | cat
#"C:/Program Files/KDiff3/kdiff3.exe" "$2" "$5" | cat

The basic format of this is stolen directly from this post on the Msysgit site. Provided your diff tool can be called from the command line, you’ll be able to set it up to work with Git using this template. In this case I’ve used it to setup a call to Sourcefear’s DiffMerge, a nice free (as in beer) diff and merge tool.

You’ll also see I’ve commented out calls to WinMerge and KDiff3, which are both free as in beer and speech. I ended up settling on DiffMerge because it is purdy (unlike KDiff3 – sorry), and also does 3-way merges (unlike WinMerge, which was my previous diff tool of choice).

The next step is telling Git about your wrapper. In your home directory (C:\Users\(username) or the corresponding Documents and Settings equivalent) Git will normally have created a .gitconfig file. If not then go ahead and create one. You’ll need to have the following section in there:

[diff]
 external = C:/Program Files/git/cmd/git-diff-wrapper.sh

We can now use git diff and it will fire up our diff tool of choice.

Setting up a merge tool

I had more trouble getting a merge tool working, largely because putting references to paths like C:/Program Files/... in .gitconfig seems to bork when used here. Again I ended up using the wrapper approach, creating git-merge-diffmerge-wrapper.sh at C:\Program Files\Git\cmd to call DiffMerge. This file ended up looking like this:

#!/bin/sh

# Passing the following parameters to mergetool:
#  local base remote merge_result

"C:/Program Files/SourceGear/DiffMerge/DiffMerge.exe" "$1" "$2" "$3" --result="$4" --title1="Mine" --title2="Merging to: $4" --title3="Theirs"

I unashamedly stole this from Alexander Groß’s post on getting DiffMerge running on Cygwin-based Git (with slight modifications, any faults are mine :)). We can then wire this up in .gitconfig again:

[merge]
    tool = diffmerge

[mergetool "diffmerge"]
 cmd = git-merge-diffmerge-wrapper.sh "$PWD/$LOCAL" "$PWD/$BASE" "$PWD/$REMOTE" "$PWD/$MERGED" 
    trustExitCode = false
 keepBackup = false
 
[mergetool "kdiff3"]
    path = C:/Program Files/KDiff3/kdiff3.exe
    keepBackup = false
    trustExitCode = false

Now remember that C:\Program Files\Git\cmd is on my PATH, so I can set the command to call git-merge-diffmerge-wrapper.sh without fully qualifying the path. The details on the arguments are available from the git-mergetool(1) man page. I’ve also left in a KDiff3 version that seems to work too (Git has some built in support for KDiff3 I think, which is why you can get away with only specifying the path).

Time for a test drive

Let’s make sure everything’s in working order. I’ll open up a PowerShell window and let’s see how we go (you’ll have to excuse me for using old DOS commands instead of fancy PowerShell ones, I’ve only just switched to PS after running into various issues with the cmd.exe command line).

PS> mkdir HelloWorld
PS> cd HelloWorld
PS> git init
  Initialized empty Git repository in C:/Development/Git/HelloWorld/.git/
PS> echo Hello World! > hello.txt
PS> git add hello.txt
PS> git commit -m "Initial commit"
  [master (root-commit)]: created 2a2cf34: "Initial commit"
   1 files changed, 0 insertions(+), 0 deletions(-)
   create mode 100644 hello.txt
PS> git checkout -b helloGit
  Switched to a new branch "helloGit"
PS> echo Hello Git! > hello.txt
PS> git add hello.txt
PS> git commit -m "Update from helloGit branch"
  [helloGit]: created e71437b: "Update from helloGit branch"
   1 files changed, 0 insertions(+), 0 deletions(-)
PS> git checkout master
  Switched to branch "master"
PS> echo Hello World! Hello indeed! > hello.txt
PS> git add hello.txt
PS> git commit -m "Update from master"
  [master]: created 5dee19d: "Update from master"
   1 files changed, 0 insertions(+), 0 deletions(-)
PS> git merge helloGit
  warning: Cannot merge binary files: HEAD:hello.txt vs. helloGit:hello.txt

  Auto-merging hello.txt
  CONFLICT (content): Merge conflict in hello.txt
  Automatic merge failed; fix conflicts and then commit the result.
Because I’ve used echo to stream data into hello.txt the files have ended up in binary mode. Let’s ignore that as it is just to get an easy demo going. The important thing is we now have a merge conflict to resolve.

If we now type in git mergetool Git ask’s us if we’d like to run our merge tool. We can hit enter and up pops DiffMerge. If you wanted KDiff3, we could have called git mergetool -t kdiff3, or any other merge tool in our .gitconfig (by default the tool specified in the merge.tool config option is used).

PS> git mergetool
Merging the files: hello.txt

Normal merge conflict for 'hello.txt':
  {local}: modified
  {remote}: modified
Hit return to start merge resolution tool (diffmerge):

Resolve the merge conflict however you like and then try this:

PS> echo Wow, it worked! > hello.txt
PS> git diff

Hooray, we have our diff tool working! We can also run commands like git diff HEAD~ HEAD and git diff helloGit master and get the output via our diff tool.

Conclusion

This setup earns the coveted Works on My Machine certification, but even if that’s the only place it works then hopefully it still gives you enough information to configure your Windows diff and merge tools for use with Git.

If you hunt down the relevant command line options you should be able to get pretty much any tool working: Beyond Compare, p4merge, TortoiseMerge etc.

Hope this helps.

Comments