1243

In Git, how could I compare the same file between two different commits (not contiguous) on the same branch (master for example)?

I'm searching for a compare feature like the one in Visual SourceSafe (VSS) or Team Foundation Server (TFS).
Is it possible in Git?

Arsen Khachaturyan
  • 6,472
  • 4
  • 32
  • 36
systempuntoout
  • 65,753
  • 43
  • 162
  • 239

11 Answers11

1596

From the git-diff manpage:

git diff [--options] <commit> <commit> [--] [<path>...]

For instance, to see the difference for a file "main.c" between now and two commits back, here are three equivalent commands:

$ git diff HEAD^^ HEAD main.c
$ git diff HEAD^^..HEAD -- main.c
$ git diff HEAD~2 HEAD -- main.c
mikek3332002
  • 3,460
  • 4
  • 34
  • 44
mipadi
  • 359,228
  • 81
  • 502
  • 469
  • 47
    The `..` isn't really necessary, though it'll work with it (except in fairly old versions, maybe). You can also use `git log` or `gitk` to find SHA1s to use, should the two commits be very far apart. `gitk` also has a "diff selected -> this" and "diff this -> selected" in its context menu. – Cascabel Jul 26 '10 at 19:19
  • 20
    Will this work even if the file name was modified between the 2 commits? – reubenjohn Feb 14 '14 at 16:14
  • 28
    So what is the purpose of the "--" – user64141 Aug 06 '14 at 19:08
  • 35
    @user64141 The `--` is useful e.g. when you have a file named `-p`. Good to use in scripts, only in rare cases needed in practice. – Palec Dec 30 '14 at 10:44
  • 14
    Note: you need to use paths relative to the root of the repo. Paths relative to the current working directory will not work. – Kevin Wheeler Jun 30 '15 at 02:57
  • 4
    `git diff master..HEAD -- $(find your_search_folder/ -name '*.c')` for diffing all `.c` files inside a directory. – Andrei-Niculae Petre Dec 20 '15 at 07:43
  • 5
    The `HEAD^^^` syntax means "compare to 3 repo commits" ago, not 3 "file commits" ago. A way round this is to specify a date e.g. `git diff -w @{2016-01-01} filename` The `-w` means ignore whitespace changes. – Andrew Murphy Jun 18 '16 at 12:31
  • Assuming we're working with a single branch: suppose you've made a commit and then modified `file.c`. How would you compare the current state of the file to the state of the same file in the previous commit? (I ask because the arguments to the command expect ` ` and not ` `) – Minh Tran Dec 01 '17 at 17:24
  • 1
    To @KevinWheeler - I'm actually seeing the opposite in both ubuntu and windows git. The path must be relative to the current directory, not the depot root. Do you still see the same behavior? – aggieNick02 Jan 11 '18 at 15:45
  • 3
    Does not work for me. Always shows me zero changes, but the file has changes. –  Mar 02 '18 at 18:17
  • 2
    If there is a folder structure change between the commits, then you need to use git diff commit_hash_1:folder/file.ext commit_hash_2:new_folder/file.ext – Jerin Dec 06 '18 at 23:51
  • 1
    The man page linked doesn't mention `HEAD~2` or even `~`. Also, why does `diff HEAD~2 HEAD~1` or `diff HEAD~3 HEAD~2` etc. return nothing?? – Marc Apr 03 '19 at 14:34
  • Paths are relative: the command `git diff A B c` from folder `/git-clone/a/b` will give same result as `git diff A B a/b/c` from folder `/git-clone` – Oliver Jan 25 '21 at 17:19
321

You can also compare two different files in two different revisions, like this:

git diff <revision_1>:<file_1> <revision_2>:<file_2>

Jakub Narębski
  • 268,805
  • 58
  • 209
  • 228
  • 27
    note that it looks like if `` and `` are in the current directory, not on the top level git managed directory, one has to prepend `./` on Unix: `:./filename_1` – Andre Holzner Aug 12 '13 at 12:44
  • 7
    : can be ommitted, so you can diff with a file which was not committed yet. – Yaroslav Nikitenko Dec 06 '15 at 18:39
  • 2
    Note that on Windows one has to use '/' for file paths, not '\'. – np8 Oct 23 '18 at 07:10
  • Is there a way to perform this diff such that one of the two files is local? I.e. when your difftool is opened, the file isn't a copy in a temp directory. In the same vein as the difference between `git diff HEAD..HEAD~1` and `git diff HEAD~1` – Gregory Kuhn Mar 11 '21 at 16:50
94

If you have configured the "difftool" you can use

git difftool revision_1:file_1 revision_2:file_2

Example: Comparing a file from its last commit to its previous commit on the same branch: Assuming that if you are in your project root folder

$git difftool HEAD:src/main/java/com.xyz.test/MyApp.java HEAD^:src/main/java/com.xyz.test/MyApp.java

You should have the following entries in your ~/.gitconfig or in project/.git/config file. Install the p4merge [This is my preferred diff and merge tool]

[merge]
    tool = p4merge
    keepBackup = false
[diff]
    tool = p4merge
    keepBackup = false
[difftool "p4merge"]
    path = C:/Program Files (x86)/Perforce/p4merge.exe
[mergetool]
    keepBackup = false
[difftool]
    keepBackup = false
[mergetool "p4merge"]
    path = C:/Program Files (x86)/Perforce/p4merge.exe
    cmd = p4merge.exe \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"
Anver Sadhat
  • 2,696
  • 22
  • 24
56

Check $ git log, copy the SHA-1 ID of the two different commits, and run the git diff command with those IDs. for example:

$ git diff (sha-id-one) (sha-id-two)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Vibhuti
  • 1,342
  • 14
  • 17
  • 23
    If you want the diff for a specific file, add the path to it at the end of the command. – hBrent Jul 13 '15 at 22:52
  • Also do a "git pull" to download the full tree info if the two commits are across diffrerent branches. Otherwise you will get a "fatal: bad object" error. – user238607 Nov 30 '18 at 21:00
  • 5
    `git diff (sha-id-one) (sha-id-two) -- filename.ext` without the filename it will list diffs of *all* files in those two commits. – SherylHohman Jan 18 '19 at 00:32
40

If you want to see all changes to the file between the two commits on a commit-by-commit basis, you can also do

git log -u $start_commit..$end_commit -- path/to/file

cxreg
  • 9,788
  • 1
  • 16
  • 10
21

Here is a Perl script that prints out Git diff commands for a given file as found in a Git log command.

E.g.

git log pom.xml | perl gldiff.pl 3 pom.xml

Yields:

git diff 5cc287:pom.xml e8e420:pom.xml
git diff 3aa914:pom.xml 7476e1:pom.xml
git diff 422bfd:pom.xml f92ad8:pom.xml

which could then be cut and pasted in a shell window session or piped to /bin/sh.

Notes:

  1. the number (3 in this case) specifies how many lines to print
  2. the file (pom.xml in this case) must agree in both places (you could wrap it in a shell function to provide the same file in both places) or put it in a binary directory as a shell script

Code:

# gldiff.pl
use strict;

my $max  = shift;
my $file = shift;

die "not a number" unless $max =~ m/\d+/;
die "not a file"   unless -f $file;

my $count;
my @lines;

while (<>) {
    chomp;
    next unless s/^commit\s+(.*)//;
    my $commit = $1;
    push @lines, sprintf "%s:%s", substr($commit,0,6),$file;
    if (@lines == 2) {
        printf "git diff %s %s\n", @lines;
        @lines = ();
    }
    last if ++$count >= $max *2;
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
user2520657
  • 321
  • 2
  • 2
14

If you have several files or directories and want to compare non continuous commits, you could do this:

Make a temporary branch ("revision" in this example)

git checkout -b revision

Rewind to the first commit target

git reset --hard <commit_target>

Cherry picking on those commit interested

git cherry-pick <commit_interested> ...

Apply diff

git diff <commit-target>^

When you done

git branch -D revision
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
dvdvck
  • 397
  • 3
  • 7
  • 2
    Thanks for this solution. It worked well for my use case. The only thing i would update is that when your done you can't delete the branch until you switch off of it. – Steven Dix Jul 09 '13 at 15:00
14

If you want to make a diff with more than one file, with the method specified by @mipadi:

E.g. diff between HEAD and your master, to find all .coffee files:

git diff master..HEAD -- `find your_search_folder/ -name '*.coffee'`

This will recursively search your your_search_folder/ for all .coffee files and make a diff between them and their master versions.

9

Just another way to use Git's awesomeness...

git difftool HEAD HEAD@{N} /PATH/FILE.ext
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Edward J Beckett
  • 4,701
  • 1
  • 38
  • 39
  • I defined an alias from this answer that works with bash: ```difftool-file = "!git difftool HEAD@{\"$2\"} HEAD \"$1\" #"``` – blueogive May 14 '17 at 14:56
2

If you want a simple visual comparison on Windows such as you can get in Visual SourceSafe or Team Foundation Server (TFS), try this:

  • right-click on the file in File Explorer
  • select 'Git History'

Note: After upgrading to Windows 10 I have lost the Git context menu options. However, you can achieve the same thing using 'gitk' or 'gitk filename' in a command window.

Once you call 'Git History', the Git GUI tool will start, with a history of the file in the top left pane. Select one of the versions you would like to compare. Then right-click on the second version and choose either

Diff this -> selected

or

Diff selected -> this

Colour-coded differences will appear in the lower left-hand pane.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Resource
  • 444
  • 3
  • 14
  • Note to downvoters: this is a simple solution, easy to implement and addresses the OP's issue. It works on Windows which the OP is clearly using (see references to TFS and VSS in the question). – Resource Jan 04 '19 at 12:03
2

All the other responses are more complete, so upvote them. This one is just to remember that you can avoid knowing the id of the recent commit. Usually, I set my self in the branch that I want to compare and run diff tools knowing the old commit uid (You can use other notations):

git checkout master
git difftool 6f8bba my/file/relative/path.py

Also, check this other response here to set the tool you want git open to compare the file: Configuring diff tool with .gitconfig And to learn more about difftool, go to the difftool doc

Raúl Martín
  • 3,616
  • 2
  • 16
  • 37