I can easily find out what changed for a file since the last commit with git diff HEAD^ -- <filename>
but is there an equivalent shorthand to view a diff for a particular file since it was last committed, regardless of how many commits have happened since? Or to go back N commits of that particular file?
Context: I found an error in a file and I want to track down when it snuck in. It's easy enough to get a log report for a particular file with git log -<n> <filename>
to show only the commits that included changes to that file. So clearly I can just copy and paste the SHAs from that log
report, but what I really want is to be able to do something like git diff ^ -- <filename>
or git diff ~2 -- <filename>
.
git checkout recovers old versions of files.
To pull up a list of your commits and their associated hashes, you can run the git log command. To checkout a previous commit, you will use the Git checkout command followed by the commit hash you retrieved from your Git log.
The diff can be done with git diff (followed by the filename or nothing if you want to see the diff of all modified files). But if you already did something like git add * , you have to undo with git restore --staged .
$ git log -p <filename>
will show you the log message plus a diff for each commit that touched the named file.
To show only differences to the previous version, ask for just one step in log history:
$ git log -1 -p <filename>
You can make use of git log
formatting to get the hashes of previous commits to a file. For example,
git log --pretty=format:'%h' -1 --skip=1 <filename>
will get you the 2nd to last commit to touch a specific file. In fact, if you don't specify a filename, this will get you the 2nd to last commit on the entire repository. To get older hashes, you might set up a git alias that calls a shell function, like this:
[alias]
prior-hash = "!ph() { git log --pretty=format:'%h' -1 --skip=$1 $2; }; ph"
To use it, you'd type something like git prior-hash n <filename>
, where n is the (n+1)th most recent version of the file. So 1 would be the 2nd to last commit to the file, 2 would be the 3rd to last, etc, and 0 would be the most recent commit to touch that file. And again, the filename is optional, if you want to examine the repo as a whole.
I'm sure you could figure out how to build a diff command from there:
[alias]
prior-hash = "!ph() { git log --pretty=format:'%h' -1 --skip=$1 $2; }; ph"
diff-prev-easy = "!dp() { git diff $(git prior-hash $1 $2).. $2; }; dp"
which would be used similar to the prior-hash alias, git diff-prev-easy n <filename>
. This would compare the (n+1)th last revision to the most recent revision of the file. If you wanted to instead compare the (n+1)th last revision to the nth last revision, it's a simple change:
[alias]
prior-hash = "!ph() { git log --pretty=format:'%h' -1 --skip=$1 $2; }; ph"
diff-prev = "!dp() { git diff $(git prior-hash $1 $2)..$(git prior-hash $(($1 - 1)) $2) $2; }; dp"
which, again, is used the same way: git diff-prev n <filename>
One potential problem to watch out for, though, is that git log
lists commits in chronological order, which may not be what you want. Consider this history:
1 - 2 - - - 4 - 5 master
\ /
3 - - develop
Our git diff-prev 1
command would produce the diff between commit 4 and 5, as expected. But git diff-prev 2
would show the diff between commit 3 and 4, which is likely undesirable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With