How do I view 'git diff' output with my preferred diff tool/ viewer?

483,762

Solution 1

Since Git1.6.3, you can use the git difftool script: see my answer below.


May be this article will help you. Here are the best parts:

There are two different ways to specify an external diff tool.

The first is the method you used, by setting the GIT_EXTERNAL_DIFF variable. However, the variable is supposed to point to the full path of the executable. Moreover, the executable specified by GIT_EXTERNAL_DIFF will be called with a fixed set of 7 arguments:

path old-file old-hex old-mode new-file new-hex new-mode

As most diff tools will require a different order (and only some) of the arguments, you will most likely have to specify a wrapper script instead, which in turn calls the real diff tool.

The second method, which I prefer, is to configure the external diff tool via "git config". Here is what I did:

1) Create a wrapper script "git-diff-wrapper.sh" which contains something like

-->8-(snip)--
#!/bin/sh

# diff is called by git with 7 parameters:
# path old-file old-hex old-mode new-file new-hex new-mode

"<path_to_diff_executable>" "$2" "$5" | cat
--8<-(snap)--

As you can see, only the second ("old-file") and fifth ("new-file") arguments will be passed to the diff tool.

2) Type

$ git config --global diff.external <path_to_wrapper_script>

at the command prompt, replacing with the path to "git-diff-wrapper.sh", so your ~/.gitconfig contains

-->8-(snip)--
[diff]
    external = <path_to_wrapper_script>
--8<-(snap)--

Be sure to use the correct syntax to specify the paths to the wrapper script and diff tool, i.e. use forward slashed instead of backslashes. In my case, I have

[diff]
    external = \"c:/Documents and Settings/sschuber/git-diff-wrapper.sh\"

in .gitconfig and

"d:/Program Files/Beyond Compare 3/BCompare.exe" "$2" "$5" | cat

in the wrapper script. Mind the trailing "cat"!

(I suppose the '| cat' is needed only for some programs which may not return a proper or consistent return status. You might want to try without the trailing cat if your diff tool has explicit return status)

(Diomidis Spinellis adds in the comments:

The cat command is required, because diff(1), by default exits with an error code if the files differ.
Git expects the external diff program to exit with an error code only if an actual error occurred, e.g. if it run out of memory.
By piping the output of git to cat the non-zero error code is masked.
More efficiently, the program could just run exit with and argument of 0.)


That (the article quoted above) is the theory for external tool defined through config file (not through environment variable).
In practice (still for config file definition of external tool), you can refer to:

Solution 2

To complete my previous "diff.external" config answer above:

As mentioned by Jakub, Git1.6.3 introduced git difftool, originally proposed in September 2008:

USAGE='[--tool=tool] [--commit=ref] [--start=ref --end=ref] [--no-prompt] [file to merge]'
(See --extcmd in the last part of this answer)

$LOCAL contains the contents of the file from the starting revision and $REMOTE contains the contents of the file in the ending revision.
$BASE contains the contents of the file in the wor

It's basically git-mergetool modified to operate on the git index/worktree.

The usual use case for this script is when you have either staged or unstaged changes and you'd like to see the changes in a side-by-side diff viewer (e.g. xxdiff, tkdiff, etc).

git difftool [<filename>*]

Another use case is when you'd like to see the same information but are comparing arbitrary commits (this is the part where the revarg parsing could be better)

git difftool --start=HEAD^ --end=HEAD [-- <filename>*]

The last use case is when you'd like to compare your current worktree to something other than HEAD (e.g. a tag)

git difftool --commit=v1.0.0 [-- <filename>*]

Note: since Git 2.5, git config diff.tool winmerge is enough!
See "git mergetool winmerge"

And since Git 1.7.11, you have the option --dir-diff, in order to to spawn external diff tools that can compare two directory hierarchies at a time after populating two temporary directories, instead of running an instance of the external tool once per a file pair.


Before Git 2.5:

Practical case for configuring difftool with your custom diff tool:

C:\myGitRepo>git config --global diff.tool winmerge
C:\myGitRepo>git config --global difftool.winmerge.cmd "winmerge.sh \"$LOCAL\" \"$REMOTE\""
C:\myGitRepo>git config --global difftool.prompt false

With winmerge.sh stored in a directory part of your PATH:

#!/bin/sh
echo Launching WinMergeU.exe: $1 $2
"C:/Program Files/WinMerge/WinMergeU.exe" -u -e "$1" "$2" -dl "Local" -dr "Remote"

If you have another tool (kdiff3, P4Diff, ...), create another shell script, and the appropriate difftool.myDiffTool.cmd config directive.
Then you can easily switch tools with the diff.tool config.

You have also this blog entry by Dave to add other details.
(Or this question for the winmergeu options)

The interest with this setting is the winmerge.shscript: you can customize it to take into account special cases.

See for instance David Marble's answer below for an example which deals with:

  • new files in either origin or destination
  • removed files in either origin or destination

As Kem Mason mentions in his answer, you can also avoid any wrapper by using the --extcmd option:

--extcmd=<command>

Specify a custom command for viewing diffs. git-difftool ignores the configured defaults and runs $command $LOCAL $REMOTE when this option is specified.

For instance, this is how gitk is able to run/use any diff tool.

Solution 3

Try this solution:

$ meld my_project_using_git

Meld understands Git and provides navigating around the recent changes.

Solution 4

With new git difftool, its as simple as adding this to your .gitconfig file:

[diff]
    tool = any-name
[difftool "any-name"]
    cmd = "\"C:/path/to/my/ext/diff.exe\" \"$LOCAL\" \"$REMOTE\""

Optionally, also add:

[difftool]
    prompt = false

Also check out diffall, a simple script I wrote to extend the annoying (IMO) default diff behaviour of opening each in serial.

Global .gitconfig on Windows is in %USERPROFILE%\.gitconfig

Solution 5

Since Git version 1.6.3 there is "git difftool" which you can configure to use your favorite graphical diff tool.

Currently supported (at the time of writing this answer) out-of-the-box are KDiff3, Kompare, tkdiff, Meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, Diffuse and opendiff; if the tool you want to use isn't on this list, you can always use 'difftool.<tool>.cmd' configuration option.

"git difftool" accepts the same options as "git diff".

Share:
483,762
user3891
Author by

user3891

Updated on July 24, 2022

Comments

  • user3891
    user3891 almost 2 years

    When I type git diff, I want to view the output with my visual diff tool of choice (SourceGear "diffmerge" on Windows). How do I configure git to do this?

    • Debajit
      Debajit almost 15 years
      You can use "git difftool" instead of "git diff" in all newer versions of git. That will open up a visual diff program.
    • VonC
      VonC almost 15 years
      Regarding the new script difftool, I have added an answer below: stackoverflow.com/questions/255202/…
    • Alex Grande
      Alex Grande over 12 years
      gitx - gitx.frim.nl. Nuff said.
    • Dmitry Bespalov
      Dmitry Bespalov over 10 years
    • potench
      potench about 9 years
      git difftool -tool=opendiff just replace opendiff with whatever.
    • Michael S.
      Michael S. about 8 years
      Go to your repository in Git Bash. Type git config diff.tool winmerge. Verify it worked by typing git difftool. Get rid of the prompt typing git config --global difftool.prompt false. I recommend p4merge instead of winmerge.
    • Trevor Boyd Smith
      Trevor Boyd Smith almost 6 years
      (1.) to list supported external diff tools do git difftool --tool-help (2.) to open using a specific diff tool like vimdiff just do git difftool --tool=vimdiff. This is a summary of the solution written by @JakubNarębski
  • user3891
    user3891 over 15 years
    ah, I'd set the external diff program but didn't know about the 7 arguments, thanks.
  • VonC
    VonC almost 15 years
    @Ryan: that is great :) Did you use the "diff.external" setting detailed in this answer or the "git difftool" of my second answer below?
  • koustubh
    koustubh over 14 years
    Also, any idea of how to get the -s option (open several diffs in one winmerge window) to work in this scheme?
  • vfilby
    vfilby over 14 years
    Fantastic! Thank you, tried both DiffMerge and opendiff; both work quite well.
  • Mario
    Mario about 14 years
    Great script, it saved me the trouble of looking DiffMerge commands. You might want to modify the install line to use quotes - I had trouble on my machine: "%1"=="--install".
  • VonC
    VonC about 14 years
    @Frank: I confirm, the $PATH or %PATH% is respected when it comes to git config directives (what is your setup? Git Version? OS?). And if you want to signal a post to Carlos, your comment needs to begin with '@': @Carlos (see meta.stackexchange.com/questions/1093/…)
  • VonC
    VonC about 14 years
    @Carlos: Frank (see comment above) wanted you to look at stackoverflow.com/questions/1220309/… (for opening several diffs in one winmerge window)
  • Frank Schwieterman
    Frank Schwieterman about 14 years
    the path issue I had was resolved, stackoverflow.com/questions/2664283/…
  • VonC
    VonC over 13 years
    Excellent addition to the winmerge.sh script. +1. I have updated my answer to link to yours.
  • Tom
    Tom over 13 years
    That's great, much easier to type meld . than git difftool and have to view changed files sequentially. And it's much closer to Mercurial's hg vdiff plugin.
  • Tom
    Tom over 13 years
    Also, meld . works on Mercurial and Subversion projects too, in case anyone is curious.
  • VonC
    VonC over 13 years
    Good point: nobody has mentioned yet that --extcmd option. +1. I have included it in my answer.
  • misiu_mp
    misiu_mp almost 13 years
    Hmm, when I do this a list of modified files shows. When I click on one of them or do 'compare' it opens alone in a separate tab. How do I get a diff?
  • db42
    db42 almost 13 years
    @misiu_mp, I just tried it, on clicking the modified files, it shows the diff (old file as well as modified file).
  • ΩmegaMan
    ΩmegaMan over 12 years
    Araxis Merge is also configured. araxis.com/merge/scm_integration.html
  • emanaton
    emanaton over 12 years
    example usage: git difftool -t kdiff3 HEAD
  • Jez
    Jez over 12 years
    old-file old-hex old-mode new-file new-hex new-mode that's only 6 arguments, not 7...
  • VonC
    VonC over 12 years
    @Jez: yes that article omit the first argument, which is the path. You will find it though a few line below.
  • MarkSwears
    MarkSwears about 12 years
    At what version was this added? It doesn't appear to work with meld version 1.1.5.
  • idbrii
    idbrii almost 12 years
    For some reason, using "bc3" is no longer working for me, but if I use "beyond" instead, it's fine.
  • VonC
    VonC almost 12 years
    Note: git difftool now support directory diffs: stackoverflow.com/questions/2113889/…
  • Christoffer Lette
    Christoffer Lette over 11 years
    +1 (+10 if I could.) This is really the most simple solution. I struggled for hours with the accepted answer, and finally gave up (some of my problems were probably related to running Git through PowerShell...) However, I used git config --global diff.external winmerge.cmd, instead of setting the GIT_EXTERNAL_DIFF environment variable, and it works equally well.
  • blong
    blong over 11 years
    Do you also have kdiff3 defined as your mergetool? Mind sharing that part of your gitconfig if so?
  • miner49r
    miner49r almost 11 years
    Use the -y option to avoid annoying prompts. Unfortunately, git will wait until FileMerge quits before offering the next changed file. Use the -d option to do a directory compare in one shot.
  • C.J.
    C.J. over 10 years
    So git sends 7 arguments to a diff program? The more I learn about Git, the more I feel it is was made for one person: The original programmer. This dvcs seems more like a shiny toy that doesn't do much.
  • C.J.
    C.J. over 10 years
    what is $local and what is $remote?
  • jhewlett
    jhewlett over 10 years
    +1 for a method that doesn't require a wrapper script
  • jhewlett
    jhewlett over 10 years
    @CJohnson: Path to the original file and path to the edited file, respectively.
  • Michael
    Michael over 10 years
    The --start= and --end= options are not recognised in v1.7.11
  • Steve Chambers
    Steve Chambers over 10 years
    Thanks. This didn't quite work for me but it did work when I removed path and changed cmd to "\"C:/Program Files (x86)/KDiff3/kdiff3.exe\" \"$LOCAL\" \"$REMOTE\""
  • Tobias Kienzler
    Tobias Kienzler over 9 years
    in case the blog-link dies, here's the linked SO answer: stackoverflow.com/a/1291578/321973
  • BartoszKP
    BartoszKP over 9 years
    winmerge.bat in the second snippet should be winmerge.sh
  • legends2k
    legends2k over 9 years
    @VonC What does the option -ub do? I don't see it as one of the command line options of WinMerge.
  • spec3
    spec3 about 8 years
    Hi, I tried to follow this procedure for TextWrangler. I am able to open the files, find the differences, but when I click on the list of differences it doesn't show where they are in the text...Any idea?
  • Peter Nimmo
    Peter Nimmo over 7 years
    @user3891 is there an alternate link to the article you referenced, that one is no longer reachable on the web
  • VonC
    VonC over 7 years
    @PeterNimmo I have found an alternative link: osdir.com/ml/version-control.msysgit/2008-06/msg00200.html
  • Vadim Kotov
    Vadim Kotov over 7 years
    Only one drawback: sometimes meld can scan diffs much longer than through using difftool. Looks like it still scans gitignored files (lots of build generated files in my case). It took several seconds on my machine to create a list of modified files, but with difftool it happened instantly
  • yucer
    yucer about 7 years
    The option -t seems to require the complete path. Maybe using git difftool --extcmd=opendiff is better.
  • yucer
    yucer about 7 years
    Can you make this for many tools ? And associate them by file extension ?
  • Granger
    Granger over 6 years
    @CJohnson: $LOCAL is always the latest commit. However $REMOTE isn't consistent. When looking at changes in the local sandbox, it's the current/edited file, But when diffing historical commits, it's the parent commit. IOW for normal diffs $LOCAL = old and $REMOTE = new. For historical, $LOCAL = newer and $REMOTE = older.
  • Ben
    Ben about 6 years
    @VonC The alternative link in your comment is also no longer accessible
  • VonC
    VonC about 6 years
    @Ben Thank you. I have edited the answer and restored the link to web.archive.org/web/20170508180316/http://git.net:80/ml/…
  • Steve Chambers
    Steve Chambers about 6 years
    Also worth mentioning the highly useful --dir-diff option in order to compare all the files at once and avoid prompts.
  • VonC
    VonC about 6 years
    @SteveChambers Yes, I mentioned it in stackoverflow.com/a/10879804/6309. I will edit this answer.
  • Diomidis Spinellis
    Diomidis Spinellis about 5 years
    @JesseRusak The cat command is required, because diff(1), by default exits with an error code if the files differ. Git expects the external diff program to exit with an error code only if an actual error occurred, e.g. if it run out of memory. By piping the output of git to cat the non-zero error code is masked. More efficiently, the program could just run exit with and argument of 0.
  • VonC
    VonC about 5 years
    @DiomidisSpinellis Thank you. I have included your comment in the answer for more visibility.
  • Kevin Meboe
    Kevin Meboe about 5 years
    Note: step 2 (modifying diff.external) will cause "git diff" to launch your difftool. This can be undesirable, and can cause problems with user interfaces (like SourceTree), causing them to aggressively launch your tool at odd times. It's probably better to modify diff.tool instead (e.g.: git config --global diff.tool p4merge)
  • VonC
    VonC about 5 years
    @KevinMeboe I agree: that is what my second answer below (stackoverflow.com/a/949242/6309) proposes.
  • Jesus H
    Jesus H almost 5 years
    A couple of issues with this solution: 1. Doesn't work for new files. WinMerge asks me to enter a second file to compare against. 2. Window to compare next file only opens up after you close the current WinMerge window, no easy way to see all files at the same time.
  • Zameer Fouzan
    Zameer Fouzan almost 5 years
    this is the simplest and fastest way.
  • Calvin Taylor
    Calvin Taylor over 3 years
    I love meld but it has been behaving very poorly on MacOs, so I've switched to sublime merge.
  • Ashark
    Ashark over 3 years
    Is there the same feature for kompare? I liked this solution very much (especially, that it allows you to select interested file from a list).
  • webasdf
    webasdf about 3 years
    Awesome tip! Worked great for me!
  • Peter Mortensen
    Peter Mortensen almost 3 years
    "port"? On BSD?
  • Peter Mortensen
    Peter Mortensen almost 3 years
    Can you add references to 'port' and 'diffuse'? And presumed platforms/operating system(s)?
  • Peter Mortensen
    Peter Mortensen almost 3 years
    Can you add a reference to "kaleidoscope"?
  • Eric Duminil
    Eric Duminil about 2 years
    That's excellent if you want to see current changes. I found git difftool -y -t meld --dir-diff other_branch to be useful too, to see the difference with any branch.