Setting Up Git to Use Your Diff Viewer or Editor of Choice

Git offers two ways of viewing differences between commits, or between commits and your working tree: diff and difftool.
The first of these, by default, dumps the results to the standard output.
This mode of presentation is great for quick summaries of small sets of changes, but is a little cumbersome if there are a large number of changes between the two commits being compared and/or you want to closely examine the changes, browsing back-and-forth between different files/lines, search for specific text, fold away or hide non-changed lines etc.
In these cases, you would like to use an external or third-party diff program/viewer to review the differences, and
Git offers two ways to allow for this.

The Less-Than-Ideal Approach

You can set a configuration variable to send the results to a third-party diff program by adding the following line to your “~/.gitconfig“:

[diff]
    external = 

where “” is a script that invokes your diff program/viewer of choice.
The reason you need to use a wrapper script rather than the external program directly is because Git calls the program specified by passing it seven arguments in the following order: “path“, “old-file“, “old-hex“, “old-mode“, “new-file“, “new-hex“, “new-mode“.
So, depending on your diff program, you would need to filter out unneeded/unused arguments, or add switches/flags as appropriate.
For example, if you want to use Vim, the wrapper script may look something like:

#! /bin/sh
vimdiff "$2" "$5"

This approach is less than ideal, however, at least for me, because once you have configured your Git this way, then all invocations of “git diff” will launch the external applications.
And the fact is that there are many times (the majority, in my case) where this is simply overkill and the short summary in standard output does just fine.
You can, of course, still get the native Git standard output diff dump even with the external diff program configured as above by passing in an appropriate flag, but, trust me, this is a bit of a pain.

The Ideal Approach

Git, fortunately, offers a second approach: difftool.
This is essentially a wrapper around diff, taking the same arguments and options, but instead calls the external diff program/viewer by default.
This approach thus allows you to retain “git diff” for standard output reviews of changes, while unleashing the power of a more sophisticated diff program/viewer for more extended/flexible/complex reviews by invoking “git difftool“.

Git offers a range of difftools pre-configured “out-of-the-box” (kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, diffuse, opendiff, p4merge and araxis), and also allows you to specify your own.
To use one of the pre-configured difftools (for example, “vimdiff”), you add the following lines to your “~/.gitconfig“:

[diff]
    tool = vimdiff

Specifying your own difftool, on the other hand, takes a little bit more work, but is still pretty simple … IF you know how to do it.

I did not. And, unfortunately, the documentation did not help me very much.
It took quite a bit of Googling and experimentation before I figured it out.

You basically need to add the following lines to your “~/.gitconfig“:

[diff]
    tool = default-difftool

[difftool "default-difftool"]
    cmd = default-difftool.sh $LOCAL $REMOTE

You can, of course, replace “default-difftool” with anything you care to name your preferred difftool, and “default-difftool.sh” with whatever you end up calling your wrapper script.

My difftool of choice is Vim, and, while “vimdiff” is a pre-configured option, I did not want to use it because I wanted the flexiblity to invoke MacVim when I am using my laptop but console Vim when I am working remotely on a Linux box (my Git configuration, and for that matter, my entire work environment from the shell to Vim to what-have-you, all 37MB, is shared across multiple machines … and all managed/synced using Git, of course).
So my wrapper script looks like the following:

#! /bin/bash

if [[ -f /Applications/MacVim.app/Contents/MacOS/Vim ]]
then
    # bypass mvim for speed
    VIMPATH='/Applications/MacVim.app/Contents/MacOS/Vim -g -dO -f'
elif [[ -f /usr/local/bin/mvim ]]
then
    # fall back to mvim
    VIMPATH='mvim -d -f'
else
    # fall back to original vim
    VIMPATH='vimdiff'
fi

$VIMPATH $@

And that’s all there is to it!

One Last Tweak

I find it very annoying to have to hit “” before moving on to the next file. The following lines added to your “~/.gitconfig” put a stop to that:

[difftool]
    prompt = false

Leave a Reply

Your email address will not be published. Required fields are marked *