29

I find myself recurrently in a situation, where I want to see the diff of all changes, that a branch introduced since branching off. The naive

$ git diff ..branch

doesn't work well, because changes in master are taken into account as well. What I'm looking for is basically a nicer way to run

$ git diff $(git merge-base master branch)..branch

or expressed in graphics:

---A---B---C---D---E  <== master
        \
         F---G---H    <== branch

How do I elegantly find the diff between B and H?

Edit: As noted in my answer below, master...branch is a solution to my problem. However, I still don't know, why that's the case, given the quoted man page snippet.

Why does diff master...branch only show the differences from the merge base, while man rev-parse says, it should include master's commits, too?

Why does diff master..branch show the diff between the current state of master and branch, while man rev-parse says, it should ignore the master-only commits?

Boldewyn
  • 81,211
  • 44
  • 156
  • 212
  • Thanks for your answer. In a comment on it, @ELLIOTTCABLE points to the full story at [What are the differences between double-dot ".." and triple-dot "..." in Git commit ranges?](https://stackoverflow.com/questions/462974/what-are-the-differences-between-double-dot-and-triple-dot-in-git-com/463027#463027). As a comment there notes: *It's pretty ridiculous how the meaning of .. and ... is exactly swapped for log and diff: log A..B is changes from merge base to B which is what diff A...B does*. – nealmcb Aug 04 '20 at 22:53

4 Answers4

32

After extensively looking at git help rev-parse and experimenting around, I found this piece of information:

   <rev1>..<rev2>
       Include commits that are reachable from <rev2> but exclude those
       that are reachable from <rev1>. When either <rev1> or <rev2> is
       omitted, it defaults to HEAD.

   <rev1>...<rev2>
       Include commits that are reachable from either <rev1> or <rev2>
       but exclude those that are reachable from both. When either <rev1>
       or <rev2> is omitted, it defaults to HEAD.

Somehow I was always under the impression, that master..branch is what I need and forgot about master...branch (with three dots).

But experimenting with it showed, that the three-dot notation is exactly what I'm looking for:

$ git diff master...branch

shows only the differences of branch relative to where it took off of master.

Boldewyn
  • 81,211
  • 44
  • 156
  • 212
  • 1
    _Nota bene_: I have no idea why, though. Even after reading the man page for the umpteenth time, I still think, that `master..branch` should've do what I want. – Boldewyn Feb 08 '18 at 09:07
  • 7
    This is definitely the answer; and as this is the first Google result (at least in my search-bubble!), you might want to mark your own answer as ‘accepted.’ Oh, and as for the explanation as to *why* this is the case — basically backwards compatibility. The `rev-parse` documentation actually doesn't apply here; it's documented directly in `git help diff` instead. (See https://stackoverflow.com/a/463027/31897) – ELLIOTTCABLE May 21 '18 at 08:16
  • 1
    Thank you, @ELLIOTTCABLE! Good to know, where that difference comes from. – Boldewyn May 29 '18 at 19:26
  • Incorrect (I think). This works for commands like log, but diff is different. See git help diff: ``` git diff [] .. [--] [...] This is synonymous to the earlier form (without the "..") for viewing the changes between two arbitrary . git diff [] ... [--] [...] This form is to view the changes on the branch containing and up to the second , starting at a common ancestor of both . "git diff A...B" is equivalent to "git diff $(git merge-base A B) B". ``` – blais Nov 17 '20 at 06:18
  • I might be mistaken, but isn’t that basically what ELLIOTTCABLE said two comments above? (Or put another way, I do not see, how my answer to _my own_ question might be incorrect with regard to “does this address the problem?”, inaccuracies in the explanation notwithstanding.) – Boldewyn Nov 17 '20 at 08:27
3

What I'm looking for is basically a nicer way to run

$ git diff $(git merge-base master branch)..branch

How about creating an alias?

git config alias.diffbr '!f() { git diff $(git merge-base master $1)..$1; }; f'

Then you can simply do git diffbr branch.

If you want the alias to be available for all repositories on your machine add the --global argument to git config.

Community
  • 1
  • 1
ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
  • Sorry, I somehow missed your answer. Yes, an alias is what I currently consider doing. Do you by chance know about a revision notation, that would allow addressing things easier? (Sometimes I do weird stuff with `diff`, where an alias might get in the way.) – Boldewyn Feb 08 '18 at 09:02
  • @Boldewyn, I'm not clear on what you need in a revision notation. But it looks like you've found another, much simpler solution of your own. I'll assume you're all set unless I hear otherwise. – ChrisGPT was on strike Feb 08 '18 at 12:51
  • Yes, indeed (although I'm still puzzled by that man page). Thanks for the answer! – Boldewyn Feb 08 '18 at 14:54
3

If you are going to check the current branch against where you branch off, just add .... So the command would look like

git diff master...

It's the same as git diff master...HEAD. I am running git version 2.17.1. This might behave differently in an older version.

Shiplu Mokaddim
  • 56,364
  • 17
  • 141
  • 187
2

I know this is not the answer you're looking for here, but it might help someone else. Using a graphical tool like gitk, you can:

  • Open gitk
  • Click on H, right click and select "Mark this commit"
  • Click on B, right click and select "Diff this -> marked commit"

Now you see all updated files to the right, and all the modified lines (per file) to the left.

Martin G
  • 17,357
  • 9
  • 82
  • 98