217

After doing a "simple" merge (one without conflicts), git show usually only shows something like

commit 0e1329e551a5700614a2a34d8101e92fd9f2cad6 (HEAD, master)
Merge: fc17405 ee2de56
Author: Tilman Vogel <email@email>
Date:   Tue Feb 22 00:27:17 2011 +0100

Merge branch 'testing' into master

This is because, for merges, git show uses the combined diff format which omits files that agree with either of the parent versions.

Is there a way to force git to still show all differences in combined diff mode?

Doing git show -m will show the differences (using pairwise diffs between the new and all parent versions respectively) but I would prefer to have that with the differnces marked by +/- in the respective columns like in combined mode.

Tilman Vogel
  • 9,337
  • 4
  • 33
  • 32
  • 2
    @ Tilman Vogel : please review accepted answer - Looks like there are better answers – Jayan Nov 22 '17 at 00:25
  • 2
    @Jayan While the other answers are more popular because they contain useful hints, they actually don't get closer to my problem as doing just two-way diffs. I was looking for a three-way diff. – Tilman Vogel Nov 23 '17 at 01:03

11 Answers11

285

Look at the commit message:

commit 0e1329e551a5700614a2a34d8101e92fd9f2cad6 (HEAD, master)
Merge: fc17405 ee2de56
Author: Tilman Vogel <email@email>
Date:   Tue Feb 22 00:27:17 2011 +0100

Merge branch 'testing' into master

notice the line:

Merge: fc17405 ee2de56

take those two commit ids and reverse them. so in order get the diff that you want, you would do:

git diff ee2de56...fc17405

to show just the names of the changed files:

git diff --name-only ee2de56..fc17405

and to extract them, you can add this to your gitconfig:

exportfiles = !sh -c 'git diff $0 --name-only | "while read files; do mkdir -p \"$1/$(dirname $files)\"; cp -vf $files $1/$(dirname $files); done"'

then use it by doing:

git exportfiles ee2de56..fc17405 /c/temp/myproject
guettli
  • 25,042
  • 81
  • 346
  • 663
rip747
  • 9,375
  • 8
  • 36
  • 47
  • Thanks for the suggestion but I think it doesn't solve my problem. Due to limited comment markup and formatting, I added my comment to your answer. Sorry for that! Needs to get peer-reviewed until visible. – Tilman Vogel Sep 08 '11 at 08:12
  • 6
    Seems, my edit was declined. In summary: Your diff does not show which additions come from which branch. And you cannot distinguish whether changes were added in the second or removed in the first branch. – Tilman Vogel Sep 08 '11 at 15:07
  • 52
    A better solution is `git diff fc17405...ee2de56` - this will show all of the changes on ee2de56 that are reachable from commits on fc17405, which I believe is what you want. Note the 3 dots instead of two. – Kris Nuttycombe Aug 20 '13 at 16:43
  • 3
    @KrisNuttycombe 3 dots and the order. And your comment is what I was looking for, which I think is more like what the OP wanted. – Izkata Oct 10 '13 at 02:13
  • @KrisNuttycombe This somehow doesn't work with `git log`, which still shows all commits, like the `..` variant. `..` and `...` do the same for `log`, but for `diff` they're different!? How do I get a list of commits that were merged into this branch? – Rudie Nov 11 '15 at 19:42
  • Just for note: I prefer `git diff --stat ee2de56..fc17405` rather than `git diff --name-only ee2de56..fc17405` because --stat show still simple but with much more useful detail :) – kenju Jun 09 '16 at 04:20
  • This is not correct; it won't show you the diff between either of the commits to the merge but only the diff between them. I think that @hesham_EE's solution is correct. – philipp2100 Aug 13 '20 at 13:36
89

A better solution (mentioned by @KrisNuttycombe):

git diff fc17405...ee2de56

for the merge commit:

commit 0e1329e551a5700614a2a34d8101e92fd9f2cad6 (HEAD, master)
Merge: fc17405 ee2de56
Author: Tilman Vogel <email@email>
Date:   Tue Feb 22 00:27:17 2011 +0100

to show all of the changes on ee2de56 that are reachable from commits on fc17405. Note the order of the commit hashes - it's the same as shown in the merge info: Merge: fc17405 ee2de56

Also note the 3 dots ... instead of two!

For a list of changed files, you can use:

git diff fc17405...ee2de56 --name-only
Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
CodeManX
  • 11,159
  • 5
  • 49
  • 70
  • This is exactly what I was after +1. – geedoubleya Jan 17 '17 at 11:15
  • This actually shows the result of a merge conflict, whereas the other answer doesn't. – Pod Feb 19 '20 at 10:36
  • This is not correct. I don't know why it got so many upvotes. This will show the diff between the common ancestor and ee2de56, no matter what happens in the other branch or in the merge commit. (From the manual: `"git diff A...B" is equivalent to "git diff $(git-merge-base A B) B."`) – philipp2100 Aug 13 '20 at 13:26
12

You can create branch with HEAD set to one commit before merge. Then, you can do:

git merge --squash testing

This will merge, but not commit. Then:

git diff
side2k
  • 2,054
  • 3
  • 17
  • 13
6

Seems like answered here: https://public-inbox.org/git/7vd392ezhx.fsf@alter.siamese.dyndns.org/

So in a similar way, running

$ git diff --cc $M $M^1 $M^2 $(git merge-base $M^1 $M^2)

should show a combined patch that explains the state at $M relative to the states recorded in its parents and the merge base.

max630
  • 8,762
  • 3
  • 30
  • 55
  • do you know if any tool can be configured to display such a diff in side-by-side manner, potentially in few columns (like in IntelliJ merge conflict resolution window)? Your answer is precisely i was looking for – Max Jun 24 '20 at 05:47
  • @Max No I'm afraid I don't. Googling "n-way visual diff" does provide some links, so I'd tried those. – max630 Jun 24 '20 at 06:24
  • This doesn't show you files only in one commit, at least if it's the second. I think that @hesham_EE's solution is correct. – philipp2100 Aug 13 '20 at 13:39
6

If you are sitting at the merge commit then this shows the diffs:

git diff HEAD~1..HEAD

If you're not at the merge commit then just replace HEAD with the merge commit. This method seems like the simplest and most intuitive.

Bruce Dawson
  • 3,284
  • 29
  • 38
4

I think you just need 'git show -c $ref'. Trying this on the git repository on a8e4a59 shows a combined diff (plus/minus chars in one of 2 columns). As the git-show manual mentions, it pretty much delegates to 'git diff-tree' so those options look useful.

patthoyts
  • 32,320
  • 3
  • 62
  • 93
  • 4
    No, for a "simple" merge, `git show -c $ref` shows the same output as I have quoted, i.e. no differences. `-c` selects a combined diff mode very similar to the default mode for merge commits which is '--cc', see `git help show` and `git help diff-tree`. Both completely omit files that agree with either of the parent versions of that file. – Tilman Vogel Feb 22 '11 at 08:37
  • `a8e4a59` indeed does not fall into the category of merge commits, I mean. This merge commit indeed contains one file that differs from both its parent versions. `Documentation/git-fast-import.txt` has some things added from one parent and some from the other. This results in non-empty output from `git diff-tree --cc`. However, only the changes in this "conflicting" case are shown. All the "clean" merge results, look at `git show -m a8e4a59`, are not shown at all. – Tilman Vogel Feb 22 '11 at 08:44
  • 1
    @TilmanVogel: Thanks for pointing out that "uninteresting" file merges are left out of the `git show -c` output. (`man git-diff-tree` does say "Furthermore, it lists only files which were modified from all parents." but I for one certainly hadn't spotted that.) – Paul Whittaker Jul 30 '14 at 18:27
4

If your merge commit is commit 0e1329e5, as above, you can get the diff that was contained in this merge by:

git diff 0e1329e5^..0e1329e5

I hope this helps!

hesham_EE
  • 1,125
  • 13
  • 24
3

in your case you just need to

git diff HEAD^ HEAD^2

or just hash for you commit:

git diff 0e1329e55^ 0e1329e55^2
gurugray
  • 67
  • 2
  • 4
    No, this just does a plain two-way diff between the two parents. What I was asking for was a mode that simultaneously shows the diff between the `git merge-base HEAD^ HEAD^2` and `HEAD^` and `HEAD^2` in the same style as is done for files that were merged with conflicts. – Tilman Vogel Jun 06 '13 at 16:22
3

You can use the diff-tree command with the -c flag. This command shows you what files have changed in the merge commit.

git diff-tree -c {merged_commit_sha}

I got the -c flag's description from Git-Scm:

This flag changes the way a merge commit is displayed (which means it is useful only when the command is given one , or --stdin). It shows the differences from each of the parents to the merge result simultaneously instead of showing pairwise diff between a parent and the result one at a time (which is what the -m option does). Furthermore, it lists only files which were modified from all parents.

Ehsan Mirsaeedi
  • 6,924
  • 1
  • 41
  • 46
  • 3
    Looks like a good article on this topic: https://haacked.com/archive/2014/02/21/reviewing-merge-commits/ and maybe this too: https://longair.net/blog/2009/04/16/git-fetch-and-merge/ – Devin Rhode Jan 23 '18 at 01:27
1

I built a general-purpose approach to doing various operations on a merge's commits.

Step One: Add an alias to git by editing ~/.gitconfig:

[alias]
  range = "!. ~/.githelpers && run_on_merge_range"

Step Two: In ~/.githelpers, define a bash function:

run_on_merge_range() {
  cmd=$1; shift
  commit=$1; shift
  range=$(git show $commit | grep Merge: | awk '{print $2 "..." $3}')
  echo "git $cmd $range $@"
  if [ -z $range ]; then
    echo "No merge detected"
    exit 1
  fi
  git $cmd $range $@
}

Step Three: Profit!

git range log <merge SHA> --oneline
git range diff <merge SHA> --reverse -p
git range diff <merge SHA> --name-only

There is probably a LOT of room for improvement here, I just whipped this together to get past an annoying situation. Feel free to mock my bash syntax and/or logic.

Nerdmaster
  • 4,287
  • 1
  • 22
  • 16
  • Note that you may want to change "..." to ".." in the "awk" bit, depending on what you need and what command you're running: http://stackoverflow.com/questions/462974/what-are-the-differences-between-double-dot-and-triple-dot-in-git-com – Nerdmaster Feb 07 '17 at 19:35
-3

No, there is no way to do this with git show. But it would certainly be nice sometimes, and it would probably be relatively easy to implement in the git source code (after all, you just have to tell it to not trim out what it thinks is extraneous output), so the patch to do so would probably be accepted by the git maintainers.

Be careful what you wish for, though; merging a branch with a one-line change that was forked three months ago will still have a huge diff versus the mainline, and so such a full diff would be almost completely unhelpful. That's why git doesn't show it.

apenwarr
  • 10,838
  • 6
  • 47
  • 58