32

With git I can set a custom diff tool that is used for certain file extensions by the following in .git/config

[diff "csv_diff"]
    command = Tools/csv_diff

and this in .gitattributes (in the root of the repository)

*.csv diff=csv_diff

This works when using git diff, but it doesn't work with git show. My question is, how do I use a custom tool with git show?

Khaelex
  • 742
  • 5
  • 15
  • "restrict git show to only use that custom diff tool on certain file extensions" - What exactly does that behavior look like? What should happen when you `show` a diff that affects multiple files, some with extensions in that category and some not? – Chris Martin Jan 21 '16 at 21:01
  • The same thing `git diff` does. The files with the specific extension use the custom tool, files with other extensions use the default tool. – Khaelex Jan 21 '16 at 21:04
  • 2
    Seems like you're going to have to do something like write a script that wraps `git diff x^..x` and alias `show` to it. – Chris Martin Jan 21 '16 at 21:06
  • Oh, I suppose you also need to consider what it should do for merge commits. – Chris Martin Jan 21 '16 at 21:07
  • I believe Git cannot invoke other tools for `git show` since that’s the command that is used for generating patches :/ – poke Jan 21 '16 at 21:07
  • 5
    Did you try `--ext-diff` ? – Lucas Trzesniewski Jan 21 '16 at 21:09
  • @LucasTrzesniewski You might want to post that as an answer :) – poke Jan 21 '16 at 21:11
  • 2
    @LucasTrzesniewski that worked. You should make that an answer. – Khaelex Jan 21 '16 at 21:13
  • @poke done :) since it was in the docs, I figured OP has already tested that one and something didn't work. – Lucas Trzesniewski Jan 21 '16 at 21:14
  • 2
    @LucasTrzesniewski It’s not the first time some Git feature is hidden somewhere within the docs ;) Well spotted! – poke Jan 21 '16 at 21:15

4 Answers4

31

Looks like you're looking for the --ext-diff option.

Here's what git show docs say about it:

--ext-diff

Allow an external diff helper to be executed. If you set an external diff driver with gitattributes, you need to use this option with git-log and friends.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • 1
    How to use `--ext-diff`? Just like `git show --ext-diff=meld git_hash_id`?Could you please show me a simple example? – John Jan 10 '22 at 03:14
  • I found neither `git show --ext-diff=meld git_hash_id` nor `git config --global diff.tool meld; git show --ext-diff git_hash_id` works. I can't figure out where goes on.Could you please shed some light on this matter? – John Jan 10 '22 at 03:16
  • Someone found a way to make this the default? – Ulysse BN Apr 19 '22 at 18:10
14

Like @LucasTrzesniewski said, you can use --ext-diff from the command line to set a diff for the current session.

You can also use the .gitattributes to set the git-diff perfile.

A git diff implementation exists of 2 parts:

  • A definition in $GIT_DIR/config or $HOME/.gitconfig
  • A bound between a file and a definition in gitattributes

Git has choosing for this design to separate the executable code from the source code, this makes it impossible for a git clone or other git command to run any harmful code.

Writing a definition

To write a definition, we start with a header consisting of [diff "namehere"], then followed by a line-break.

The next line consists of the command definition, this line is as follows: command = commandlinehere. This command is then called with 7 arguments if it is ran, these are documented for the GIT_EXTERNAL_DIFF enviroment vriable in the docs.

GIT_EXTERNAL_DIFF When the environment variable GIT_EXTERNAL_DIFF is set, the program named by it is called, instead of the diff invocation described above. For a path that is added, removed, or modified, GIT_EXTERNAL_DIFF is called with 7 parameters:

path old-file old-hex old-mode new-file new-hex new-mode where:

<old|new>-file are files GIT_EXTERNAL_DIFF can use to read the contents of <old|new>,

<old|new>-hex are the 40-hexdigit SHA-1 hashes,

<old|new>-mode are the octal representation of the file modes.

The file parameters can point at the user’s working file (e.g. new-file in "git-diff-files"), /dev/null (e.g. old-file when a new file is added), or a temporary file (e.g. old-file in the index). GIT_EXTERNAL_DIFF should not worry about unlinking the temporary file, it is removed when GIT_EXTERNAL_DIFF exits.

For a path that is unmerged, GIT_EXTERNAL_DIFF is called with 1 parameter, <path>.

For each path GIT_EXTERNAL_DIFF is called, two environment variables, GIT_DIFF_PATH_COUNTER and GIT_DIFF_PATH_TOTAL are set.

The total example looks like this:

[diff "jcdiff"]
command = j-c-diff

Writing the gitattributes

We need to modify our gitattributes to use our custom driver. The gitattributes file consist of a syntax similar to filename value [value2 [value3 [value4 [...]]]].

Examples:

*           diff=jcdiff

We to use our custom git diff for every file.

*.java      diff=javadiff
*.python    diff=pythondiff

Use javadiff for java files, pythondiff for python files.

*           diff=globaldiff
*.java      diff=javadiff

Use javadiff for java files, globaldiff for remaining files.

Configuring git to automatically set --ext-diff

You can add an alias using git config alias.showobject 'show --ext-diff' to define a new command called git showobject that automatically uses our filter.

Ferrybig
  • 18,194
  • 6
  • 57
  • 79
  • This is what I had originally (before I asked the question). It works perfectly when using `git diff`, but with `git show`, the `--ext-diff` argument must be given for `git` to use the tool specified in the `config` and `.gitattributes` file. – Khaelex Jan 27 '16 at 00:34
  • 1
    There does not seem to be a way to force `--ext-diff` within git, right? The only possible workarounds I found are to use a git alias (as in the answer above, i.e., setting up another command name) or using a shell alias. – stefanct Jan 29 '20 at 16:44
8

As an alternate answer for others in search of using a diff tool like meld with git show

git difftool --tool=meld HEAD~..HEAD

Most of the time I like to see my small diffs on the command line because it's quick, but every once in a while I do want to boot up a diff tool to inspect things a little deeper - especially with the work of others. But this excludes the ability to use .gitattributes because this would then be the default behavior.

In order to show a particular commit or branch, then I've created this alias in my ~/.gitconfig

[alias]
    showm = "!f(){ if [ -z $1 ]; then c='HEAD'; else c=$1; fi; git difftool --tool=meld -y $c~..$c; }; f"

This will launch meld to show the diffs introduced by that commit:

git showm <branch or hash>

Without an argument, it will use HEAD as a default:

git showm

As a bonus, you can follow that up with a bash alias to get tab completion for branch names by adding this to the ~/.bashrc

alias showm='git showm'
__git_complete showm _git_show
Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
  • 2
    For merge commits, `git diff HEAD~..HEAD` is not equivalent to `git show`. I'm using the latter specifically to review forward integrations (verifying that merge conflicts were properly resolved). `git diff` is not an alternative there. Not even close. – Ruud Helderman Jul 17 '20 at 14:06
2

Its very simple, you just have the wrong command-key in your git config.

It should be like this:

[diff "csv_diff"]
    textconv = Tools/csv_diff

This works in git show and all other places where a diff is displayed (even in gitk).

I tried this with git version 2.24.1.windows.2 and this configfiles:

gitconfig

[diff "zip"]
    textconv = unzip -c -a
[diff "tgz"]
    binary = true
    textconv = tar -xvzO -f
[diff "gz"]
    binary = true
    textconv = gunzip -c

gitattributes

*.zip diff=zip
*.tgz diff=tgz
*.tar.gz diff=tgz
*.sql.gz diff=gz

If your command don`t likes to have the filename as last parameter you can do somethime like this:

[diff "sqlite3"]
    binary = true
    textconv = "f() { sqlite3 $1 --readonly .dump; }; f"

Where the $1 stands for the place where the filename is inserted. Maybe you must enclose $1 into single quotes to support filenames with spaces.

bgusach
  • 14,527
  • 14
  • 51
  • 68
Radon8472
  • 4,285
  • 1
  • 33
  • 41