Home > Mobile >  How can I make git diff return an error if it can't find the file?
How can I make git diff return an error if it can't find the file?

Time:06-27

I often use git diff to compare my local file with the last check-in, as follows:

git diff -- HEAD [name_of_file]

Annoyingly, if I happen to mistype the file name, git simply returns empty, indicating no change, instead of telling me it can't find the file, forcing me to scrutinize the file name for errors. Is there a way to make it tell me the file doesn't exist?

CodePudding user response:

The short version is "you can't" so you'll need some auxiliary code (e.g., a small wrapper script).

Note that when running:

git diff HEAD -- file

there are in fact two files involved: HEAD:file (the copy in the current commit), and file (the copy in the working tree). One of these two can be missing, representing "files is new" or "file is deleted" depending on which one is absent and which one is present.

To find out whether HEAD:file exists, use:

git rev-parse HEAD:file

e.g.:

$ git rev-parse HEAD:Makefile
56b3d3c5396e5983497c6c26e1a6f4dd3750a0d3
$ git rev-parse HEAD:Makefoil
fatal: path 'Makefoil' does not exist in 'HEAD'

If the above error message is sufficient, you can just use:

git rev-parse HEAD:$path || exit

in a shell script at this point: the fatal error goes to stderr, and the nonzero (failure) status from git rev-parse causes the shell to go on to evaluate the || clause, which exits with the status already in $? (whatever nonzero exit status git rev-parse produced). If the rev-parse succeeds, you get the hash ID printed to stdout (so you might want to redirect that to /dev/null) and the success status prevents evaluating the exit command.

Add --quiet --verify to suppress error messages:

$ git rev-parse --quiet --verify HEAD:Makefoil
$ git rev-parse --quiet --verify HEAD:Makefile
56b3d3c5396e5983497c6c26e1a6f4dd3750a0d3

The exit status from the failed rev-parse is still nonzero, so if git rev-parse --quiet --verify HEAD:$path >/dev/null; then <file exists case>; else <file does not exist case>; fi works here, for instance.

Remember to quote the expansion of $path if it might contain shell metacharacters.

To test whether a file exists locally, use the test program (alternately spelled [: when run as [ it demands a closing ] at the end) with the -e (file exists) option:

$ if test -e Makefile; then echo exists; fi
exists
$ if [ -e Makefoil ]; then echo exists; fi

If you'd like to compare the working tree version of some file against the index version of that file, use git diff -- $path and convert the rev-parse test to using :$path rather than HEAD:$path. To compare HEAD vs index, use git diff --cached -- $path (and you can rev-parse both HEAD:$path and :$path).

In all cases, remember that git diff is a porcelain command, so it obeys the user's configuration selections (e.g., when and whether to use a pager, whether to display colorized output, and so on). Use git diff-files or git diff-index to get plumbing commands that do not obey user configurations and are therefore predictable and machine-usable.

CodePudding user response:

You can use git diff HEAD <path>. But there are downsides:

  1. if you have the branch with the same name as the filename, then the error will occur (but you will get different error message compare to when the file is not found).
  2. you can actually misspell the filename to the branch name and still get some diff (the diff between commits)

You can fix it, by running:

git diff HEAD <path> || git diff HEAD -- <path>

About fixing listed problems. 1. problem is solved by showing the error. 2. problem you should be able to see the difference between the diff HEAD <commit> and diff HEAD <path>. And of course your problem is solved, you will only see the error.

If you are fine with such solution, then you can simplify it, by adding the alias to the config:

git config --global alias.diff2 '!git diff HEAD $1 || git diff HEAD -- $1'

And then just run git diff2 <path>


Instead of running the above solution you can change execution to:

git diff HEAD <path> ; git diff HEAD -- <path>

But, please make note, that this introduce the duplication in case there are no errors in first command. That being said, you can improve this solution by making more complex .sh script to take care of said duplication, and of the 2. problem case (as you would see only one output in such case)

  •  Tags:  
  • git
  • Related