26

I have a repository where a number of files have been renamed from .html to .php and further edited in a number of commits since my last pull. Using git diff to shows all the html contents removed and all the php content added. Is there a neat way to have git diff detect the renames (something like git log --follow does), or directly compare different filenames across different commits (something like the solution in Git: How to diff two different files in different branches? , but for commits)?

Community
  • 1
  • 1
Steve Kroon
  • 373
  • 3
  • 6
  • Did you use `git mv` when changing the files originally? – dave Mar 27 '17 at 08:55
  • 9
    git diff commit1:file1_path commit2:file2_path – ElpieKay Mar 27 '17 at 09:29
  • This does the trick, thanks, please convert to an answer so I can accept. (It is at the individual file level, unfortunately, but I can do what needs to be done with it for now.) – Steve Kroon Mar 27 '17 at 10:04
  • @dave: not actually sure - someone else moved the files. – Steve Kroon Mar 27 '17 at 10:04
  • 1
    @dave: that does not actually matter. Git normally *detects* renames (rather than recording them) by doing tree comparisons. That, however, requires that you diff two complete trees, not two specific files within the tree. – torek Mar 27 '17 at 15:19

2 Answers2

32

You can always compare 2 files in 2 different commits:

git diff commit1:file1_path commit2:file2_path
Eugene Kaurov
  • 2,356
  • 28
  • 39
  • 1
    Answers with just code get flagged as 'Low Quality Posts', try explaining your answer a little to avoid it being deleted. – piman314 Mar 08 '18 at 19:42
  • @ElpieKay provided this answer as a comment on my question a year back - tagging him here to provide it as an answer. If I don't get a response from him in a week, Eugene, ping me here and I'll accept your answer. – Steve Kroon Mar 27 '18 at 23:05
  • @SteveKroon It would be already 3 weeks tomorrow since the time of your comment. I appreciate ElpieKay input, he really added good commented, but looks like he is not available. If you like, feel free to accept my answer/ – Eugene Kaurov Apr 16 '18 at 18:17
5

As said previously, the format needed is:

$ git diff commit1:file1_path commit2:file2_path

But there are a couple gotchas:

XXX$ git diff file1_path commit2:file2_path

This will not work, even if file1_path is in HEAD; if you want to specify a commit for one file you must specify it for both. This will attempt to interpret "commit2:file2_path" as a file path with the first directory it steps into being, e.g., HEAD^:foo/. This will rarely exist so it generally gives an informative error message. (Note: @philh says that git diff commit1:file1_path file2_path works fine, but I haven't confirmed that myself.)

file1_path format: The paths given will be treated as relative to the repository's root. Absolute paths are not permitted, and relative paths must be explicit. For a concrete example; say your directory structure looks like the below, and your working directory is ~/repo/proj:

~/repo
  .git
  proj
    tests
      foo
        bar.sh
    utils
      baz
        quux.sh

If you are trying to compare HEAD^:(...)/bar.sh to HEAD:(...)/quux.sh, the only permissible paths for bar.sh are:

  • proj/tests/foo/bar.sh
  • ./tests/foo/bar.sh

The explicit relative path with ./ works, but the more common implicit path tests/foo/bar.sh will not work. That version will sometimes fail silently - I think this is based on the format used in the other arguments but I'm not sure. Absolute paths will also not work; ~/repo/proj/tests/foo/bar.sh and /home/username/repo/proj/tests/foo/bar.sh will fail. AFAICT that will never fail silently.

Jacob Kopczynski
  • 392
  • 3
  • 13
  • 2
    "if you want to specify a commit for one file you must specify it for both" Not entirely - it seems that `git diff path1 foo:path2` doesn't work, but `git diff foo:path2 path1` does. – philh Aug 03 '20 at 11:11
  • Huh, I thought I tested that and it failed. – Jacob Kopczynski Aug 06 '20 at 22:34