16

Git makes it very easy to compare differences between commits, using for instance the git commands diff and difftool. Also in TortoiseGit you just select two commits to compare them.

But is there a way to compare changesets? In other words: to see the differences between the diffs of one set of commits and the diffs of another set of commits.

This would be very handy to compare (sets of) commits which are cherry-picked or have been rebased.

Yue Lin Ho
  • 2,945
  • 26
  • 36
Paul Pladijs
  • 18,628
  • 5
  • 28
  • 31
  • 1
    This is a little snarky, but one answer is to try to never need to do this. If you do have two similar but not identical changesets, they should hopefully start from a reasonable common ancestor, so you can just diff them. And if you cherry-pick/rebase, hopefully you won't actually modify the changeset in the process, so there's nothing to diff; failing that, if you later modify the transplanted version, you should be able to just diff between the original transplant and the new one. – Cascabel Feb 18 '11 at 06:55
  • 1
    It's snarky indeed. I don't need to do it often. However branches get rebased frequently (interactive + non-interactive). So, as an integrator of a very very smal team, I would like to know if the rebased branches still contain the same changeset or to see what is changed in the changeset. – Paul Pladijs Feb 18 '11 at 08:47
  • The other thing, of course, is that if you're looking for a yes or no, you could simply generate the combined diff of the two changesets and diff those. Might be good enough. – Cascabel Feb 18 '11 at 13:57
  • 1
    I just found http://stackoverflow.com/questions/2069177/diffing-diffs-with-diff which lead to other ways of doing something close to what you want. – Tyler Apr 08 '11 at 23:34

6 Answers6

15

Perhaps diff <(git show rev1) <(git show rev2) will do what you want?

Jeff Bradberry
  • 1,597
  • 1
  • 12
  • 11
  • It's very good for comparing the changesets of two single commits but not for a set of them. The code above works on linux but not on git bash of msysgit. However I could make a script to compare the two changesets in a gui difftool. – Paul Pladijs Feb 18 '11 at 13:04
  • 1
    Being able to do stuff like this is why I like cygwin git over msysgit. – Tyler Apr 08 '11 at 23:27
6

I think that in general to get what you want, you'll have to do some sort of merge/rebase operation in order to create something to compare with.

When you really think about it, the idea of a diff between changesets is fuzzy. I'm assuming here that you're in a situation like this:

[other history]   [ "changeset 1" ]
o - o - o - o - o ( - o - o - o - o)
 \
  (o - o - o - o - o)
  [ "changeset 2" ]

So what does it mean to compare those two? Maybe in your case, the diffs in the other history are completely disjoint from those of the two changesets, but in general, the contents of changeset 1 may depend on that other history! This means that there's no good general way for git to perform an operation like this; to do it properly, it'd have to essentially say "what would the difference between the two end commits be if I rebased?" In other words, I believe that the only reasonable definition of the diff between the changesets is the diff between the resulting end commits if they're rebased to have a common ancestor. And of course, if that's what you want, then you'll have to perform an operation in the work tree - there's no other way to muck around with diffs like this. The obvious thing to do would be to rebase, and compare the new endpoints (branches):

[other history]   [ "changeset 1" ]
o - o - o - o - o ( - o - o - o - o)
                 \
                  (o - o - o - o - o)
                  [ "changeset 2'" ]

Rebases aren't always the most fun, though, and I can think of one little way to work around this. The resulting work tree of the rebase, assuming you resolve conflicts appropriately, ought to be the same as the result of a merge:

[other history]   [ "changeset 1" ]
o - o - o - o - o ( - o - o - o - o)
 \               \
  \               ------
   \                    \
   (o - o - o - o - o) - X
    [ "changeset 2" ]

So you could perform that temporary merge, and compare the resulting commit to the end commit of the other changeset. That'll be a lot faster than doing the rebase. (Either way, you'll of course use a throwaway branch, not the real one for changeset 2.)

Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • The last diagram is a very good idea. As long there are no conflicts to resolve – Paul Pladijs Feb 18 '11 at 08:49
  • The second diagram would be, for my team, like doing the same job as one of the other developers did. – Paul Pladijs Feb 18 '11 at 12:47
  • 1
    @Paul: Part of the idea here is that if there are conflicts to resolve, they're inherently part of constructing a diff between the two changesets. Perhaps if you think of it like this: what you want is the diff between the two end commits, with all of the diffs resulting from "other history" removed. Disentangling those diffs from the diffs of changeset 1 is equivalent to resolving the conflicts. – Cascabel Feb 18 '11 at 13:55
2

Here's what worked for me to compare two changesets:

git diff [base_sha_a]..[final_sha_a] > ./a.diff
git diff [base_sha_b]..[final_sha_b] > ./b.diff
diff ./a.diff ./b.diff

If the result of the diff command is empty, the changesets are the same. Otherwise you'll see the difference between the two diffs.

Andrew Childs
  • 2,895
  • 1
  • 19
  • 17
1

Expanding on jeff-bradberry's answer:

To compare the changesets introduced by two single commits:

diff <(git show -U0 <sha-A>) <(git show -U0 <sha-B>)

To compare the change sets introduced by two sequences of commits:

diff <(git show -U0 <sha-A>...<sha-B>) <(git show -U0 <sha-C>...<sha-D>)

Note: -U0 is to avoid comparing "context" lines (i.e. lines that have changed around your edits, but not directly by them).

marcotama
  • 1,991
  • 2
  • 19
  • 24
0

Starting 2.19 we have git radge-diff. It is exactly for that.

Denis Rozhnev
  • 2,547
  • 1
  • 14
  • 15
  • Note: `git range-diff` will output a list of commits. It does not show the differences in the content. I may not be what you are looking for. The OP is not clear about that. – user5534993 Jul 14 '23 at 10:06
-2
git diff end_rev_1...end_rev_2

Taken from: http://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html

A similar notation r1...r2 is called symmetric difference of r1 and r2 and is defined as r1 r2 --not $(git merge-base --all r1 r2). It is the set of commits that are reachable from either one of r1 or r2 but not from both.

And from git diff help:

git diff [--options] <commit>...<commit> [--] [<path>...]
   This form is to view the changes on the branch containing and up to the second <commit>, starting at a
   common ancestor of both <commit>. "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B".
   You can omit any one of <commit>, which has the same effect as using HEAD instead.

Does that work for you?

Chris Cherry
  • 28,118
  • 6
  • 68
  • 71
  • I don't think this is what the OP wants. Look at what it's equivalent to - it doesn't actually have anything to do with the changes on `A`; it just uses it as a way to find the starting point for the diff. – Cascabel Feb 18 '11 at 06:52
  • It's indeed not what I need. This answers how to compare the differences of the project contents between two commits (or revisions). I would like to know how one (or more) commit(s) change the contents (=changeset) and compare that with the changeset of one (or more) other commit(s). – Paul Pladijs Feb 18 '11 at 08:23