git diff, with external diff program, just once
Asked Answered
M

4

5

When doing a git diff, sometimes I want to use a different diff tool (like, for example, cmp, or a homebrew word-diff program I have in my own bin directory).

Many diff-like programs have an option along the lines of

-D <diffprog>

to tell them to use that other diff or diff-like program instead.

I am aware of git-difftool and this question, but that's not quite what I was looking for. I'd very much rather not have to configure something in advance, and the nonstandard command I want to use today might not be the nonstandard command I want to use tomorrow. Ideally I'd like to be able to just do it, on a one-time basis, from the command line.

(Is it possible to configure difftool to be able to use multiple nonstandard tools not on the official list?)

If it's too much trouble to get git diff to use my choice of diff tool, I could also use something like

git cat file | mydiff - file

I know there's no such command as "git cat". But the idea here is to manually compare the repo version of the file with the local one, given a git command to simply emit the repo (or, in other circumstances, the index) version of the file. Is there any such command?

Finally, if there isn't the hypothetical git diff -D diffprog option I was asking about, and if there isn't anything like the "git cat" command I was hypothesizing, am I wrong to be wishing for them?

Munshi answered 16/6, 2021 at 21:29 Comment(8)
are you looking for git difftool ?Ventilate
Ok, I had skipped the line "I don't want git-difftool", but reading the remainder of your question, I do not understand why ?Ventilate
@Ventilate I'll update the question. For my part, I didn't properly understand what difftool does. But it looks like it doesn't want to let you use any external difflike program, it seems to be restricted to an approved list?Munshi
ok, that's not the case. The known list opens each tool with the appropriate options, but you can also add your own command line invocation in the configuration.Ventilate
@Ventilate Can I add several at once? (I'm also curious why I should have to configure anything in advance at all, but I guess that can be a question for another day.)Munshi
The git cat you're looking for is spelled git show, or git cat-file -p.Cashbook
@SteveSummit : you can check the individual files in github.com/git/git/tree/master/mergetools to view how each tool is called with specific options. Is the diff program you intend to use a known program ? or a custom script ?Ventilate
@Cashbook Thanks for that tip! Neither of those worked for me at all the first time I tried, but if after reading about them I still can't get them working, I'll ask a separate question.Munshi
C
5

I think you might be looking for something like

git show somecommit:path/to/file | mydiff - path/to/file

or

mydiff  <(git show somecommit:path/to/file) \
        <(git show someothercommit:path/to/file)

but a little manpage reading says difftool does accept path limiters, and also has the -x option, so I don't see any problem there. For the case in your comment I'd use

git difftool -yx mydiff @ -- file
Cashbook answered 17/6, 2021 at 2:37 Comment(2)
Yes, or specifically, git show HEAD:./file. This appears to be about the best I can do.Munshi
That -yx was the key for me to feed a file list through jd thanks! -- for C in $(git diff --name-only --relative master -- "${DIRECTORY}"); do echo "${C}"; git difftool -yx jd @ master -- "${C}"; echo; doneFrow
V
3

The integrated way in git to open a diff in an external viewer is git difftool.

As far as cli options go, git difftool takes the same arguments as git diff, and opens the resulting diff in the external viewer.
One extra useful option is -d | --dir-diff, which will create two checkouts of your repo in a temporary directory and open the external viewer in "directory comparison mode" :

git difftool -d <something> <something else>

As far as the choice for external tool goes :

  • you can configure a default difftool : git config diff.tool kdiff3
  • you can select any tool you want on a one of basis using -t | --tool :
git difftool -t meld <something> <something else>
  • you can also define custom commands by creating difftool.<tool>.cmd entries in your git configuration

quoting the docs of the -t|--tool section :

You can explicitly provide a full path to the tool by setting the configuration variable difftool.<tool>.path. For example, you can configure the absolute path to kdiff3 by setting difftool.kdiff3.path. Otherwise, git difftool assumes the tool is available in PATH.

Instead of running one of the known diff tools, git difftool can be customized to run an alternative program by specifying the command line to invoke in a configuration variable difftool.<tool>.cmd.

When git difftool is invoked with this tool (either through the -t or --tool option or the diff.tool configuration variable) the configured command line will be invoked with the following variables available: $LOCAL is set to the name of the temporary file containing the contents of the diff pre-image and $REMOTE is set to the name of the temporary file containing the contents of the diff post-image. $MERGED is the name of the file which is being compared. $BASE is provided for compatibility with custom merge tool commands and has the same value as $MERGED.

Ventilate answered 16/6, 2021 at 21:53 Comment(7)
I may have been misled by the error messages. I tried git difftool -t ndiff, and it said "Unknown merge tool ndiff", followed by "fatal: external diff died". So I thought it had refused to run my external program. But perhaps it was just that the external tool exited with nonzero status, indicating a difference.Munshi
ok, you may need to define git config diff.ndiff.path "path/to/ndiff", or git config diff.ndiff.cmd 'ndiff "$LOCAL" "$REMOTE"'Ventilate
ndiff is available from your PATH, right ?Ventilate
ndiff is available from your PATH, right? Of course!Munshi
Thanks for your help. The question you've answered is not the one I asked (I wanted to use an ad-hoc diff tool on the command line without having to configure it in advance, and what you've helped me to do is to configure a different diff tool in advance), but since it looks like the command-line option I was looking for doesn't exist, this has been very helpful, and I appreciate it.Munshi
@SteveSummit: if your need is narrower, jthill's answer is a very straightforward way to get "the content of that file in that commit" and insert it in a shell commandVentilate
In the end, what I actually needed to configure was difftool.ndiff.cmd. And having to go to this much work to explicitly configure something completely defeats the purpose of a one-off from the command line, but I'm not blaming you for this, I believe it's a deficiency in git. Thanks again for your help.Munshi
S
3

Besides the raw git show or git cat-file -p trick mentioned in jthill's answer, you can use git difftool as described in LeGEC's answer. To avoid stumbling over configurations or requiring special one-time configuration files, consider adding -c options to the front end git command:

git -c diff.tool=whatever ...

This pattern works in general: if you want to pretend that the config file(s) add up to having some particular x set to y, git config -c x=y does the trick. Multiple -c options are gathered together and used as-if-configured.

(I believe this adds on to configurations and relies on the "last config overrides earlier ones" stuff, so for multivalued settings like remote.remote.fetch, if you need to avoid picking up user configurations, you currently have to use tricks like modifying $HOME, which has ... drawbacks. The upcoming Git release has ways to skip both system and user configurations, for special purposes including Git's own self-tests.)

Shadchan answered 17/6, 2021 at 15:19 Comment(2)
This worked, modulo changing it to -c difftool.ndiff.cmd=whatever. It's still too cumbersome for an actual one-off, but it's good to know. Thank you. (And if your first name is Chris, hi there, haven't talked to you in a while! :-) )Munshi
That's me... I think the last time I saw you, I lived in Calif, before I moved to Utah, then back to CA, now in WA state. That was quite a while ago...Shadchan
S
0

Caveat Emptor: This solution is specific to people using git-1.8.3.1 (the version of git available from base/updates yum repositories on RHEL7/Centos 7/OtherClone 7, where git doesn't support --ignore-white-space (which was introduced in git-1.8.4).

For me, the following works to create the output as if git supports --ignore-blank-lines using GNU diff:

git diff --name-only |xargs -rn1 bash -c 'git diff $0 |head -n4; git show HEAD:$0 | diff -U3 -b - $0|tail -n+3'

Explaination:

  • git diff --name-only gives me the list of changed lines
  • | xargs -rn1 ... takes the list of files and runs ... for each input, passing the input as the last argument to ...
  • bash -c 'git diff $0 |head -n4; git show HEAD:$0 | diff -U3 -b - $0|tail -n+3' takes the argument '$0' (which contains a path) and runs 2 subcommands on it's first parameter $0:
    • runs git diff $0 on the path ($0), to get the header, discarding the actual diff
    • get the checked in version of the file with git show HEAD:$0 piped through | diff -U3 -b -B - $0 to generate the diff and then piped through | tail -n+3 to discard the header generated by GNU diff

The combination of git diff header along with the body generated by GNU diff gives me the output as I desired.

Hopefully with the explanation, this solution can be dis-assembled and used as required in many use cases

Saltine answered 14/1, 2022 at 9:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.