19

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>.

Clayton
  • 1,967
  • 4
  • 18
  • 26

3 Answers3

43
$ 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>
Jan Vlcinsky
  • 42,725
  • 12
  • 101
  • 98
Matt McHenry
  • 20,009
  • 8
  • 65
  • 64
  • 1
    I find it odd that this seems to be the closest thing to what OP is looking for, but it's quite useful nevertheless. Thanks. – Kyle Strand Mar 14 '14 at 17:42
  • 1
    @MattMcHenry Really nice. I edited your answer and added syntax for showing just change in last commit, as asked in OP. – Jan Vlcinsky Jun 28 '14 at 08:37
4

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.

Alex W
  • 308
  • 1
  • 7
1

git blame should get you to your destination pretty fast.

Mike Monkiewicz
  • 4,481
  • 1
  • 22
  • 19