How to "git show" the diffs for a merge commit?

When I have a merge commit and run git show <commit-ish>, it shows only the commit log, not the the diffs:

commit c0f50178901e09a1237f7b9d9173ec5d1c4936c
Merge: ed234b ded051
Author: abc
Date:   Mon Nov 21 15:56:33 2016 -0800


Merge branch 'abc'

I understand the real commit is in merge log, but I want to save typing. Is there a way to show the diff in one?

58692 次浏览

使用下列其中之一:

git show -m c05f017
git show --first-parent c05f017
git diff c05f017^ c05f017

在你的问题中有一个基本的错误: 提交不是差异; 提交是快照。这看起来似乎是没有区别的区别ーー对于某些提交,它是 。但是对于合并提交,它是 没有

git show(或 git log -p)向提交 作为显示一个差异时,它是通过 将提交的快照与其他内容进行比较这样做的。git diff命令执行同样的操作: 将一个提交与另一个提交进行比较。(或者它可以将提交与工作树进行比较,或者将提交与索引的内容进行比较,或者将提交与其他一些组合进行比较。)

对于普通提交,比较什么是显而易见的: 比较 这个提交的快照和 上一页(即父提交)提交的快照。这就是 git show所做的(也包括 git log -p) : 它从父提交运行一个 git diff到这个提交。

但是,合并提交并不只有一个父提交。它们有 的父级。 1首先,这就是它们“合并提交”的原因: 合并提交的定义是至少有两个父级的提交。


1A merge commit can have three or more parents. These are called "octopus merges". They don't do anything special, though, and are mainly for showing off. :-) You can ignore them here.


当有两个父母时,git show应该与哪一个(s)进行比较?

What git log -p chooses to do by default is not to compare at all. You can make it show something by adding various flags (see below).

git show默认选择执行的操作更为复杂。由于有两个父级,git show首先与“第一个父级”进行比较,然后 2与第二个父级进行比较。然后ーー这一部分相当关键ーー它是 将两者结合起来,产生了所谓的“组合差异”。

对于下一节,让我注意一个棘手但非常有用的 Git 语法。如果您有一个类似于 c05f017的提交 ID,那么您可以在其后添加一个插入符号或“ hat”字符 ^来命名父提交。您可以选择添加另一个数字来选择 哪个父级。对于常规(非合并)提交只有一个,所以 c05f017^的父级。对于合并提交,c05f017^c05f017^1都表示 第一个家长,而 c05f017^2表示 the second parent


2 我把这个放在引号中,因为 第一个家长的想法在 Git 中特别重要,我们一会儿就会看到。换句话说,Git 最关心哪个父级是 first,而其余的只是“其余的”。


Combined diffs

组合的 diff 格式在 文件中有描述,但是首先描述了一个键位 给你,以便使它特别模糊: 3

请注意,combined diff只列出从所有父级修改过的文件。

也就是说,假设 M是一个合并提交,不同于 M ^ 1M的是,文件 mainline.txtcommon.txt都被更改了。进一步假设不同的 common.txt1和 M表示文件 sidebranch.txtcommon.txt都发生了更改。合并的差异将显示 common.txt3,跳过 mainline.txtsidebranch.txt,因为这两个文件只是从 common.txt4父文件(每个)修改。(即使这样,Git 也可能只显示 common.txt的一些不同之处。)


我花了很长时间才在文档中找到这个,因为我一直在看另一个部分。


我们各取所需

-m选项ーー 在这里可能代表 合并ーー实际上告诉 Git“拆分”合并。也就是说,不要试图将对每个父代的差异组合成一个大的组合差异,只要显示对 每个人父代的差异,一次一个差异。

有时候这就是你想要的。如果不是您想要的,您可以运行您自己的显式 git diff来区分两个父级中的一个(或者参见下面)。

你应该反对哪一位家长?

通常,正确答案是“第一个父母”。

“第一个父”概念的关键在于,当 Git 进行合并提交时,它总是记录当时所在的分支,作为第一个父分支。另一个分支成为第二个父级。

也就是说,如果你在 develop上合并了 topic:

$ git checkout develop
$ git merge topic

Git 将在当前分支 develop上创建一个新提交ーー一个带有两个父节点的 合并提交。合并提交的 第一父级将是刚才 develop提示的提交。第二父级将是(仍然) topic的提交。

因为你通常关心合并带来了什么,比较第一个父母会给你带来这个。所以通常这是你想要的。因此,git show允许您运行 git show --first-parent。“分割”提交,然后 git show只与第一个父类有所不同。(这与 git show -m略有不同,git show -m将提交分割两次: 第一次分割与第一个父进行比较,第二次分割与第二个父进行比较。)

类似地,您可以在这里运行 git log -p --first-parent--first-parent标志有一个更重要的效果: 日志操作根本不查看边分支的提交的 任何,只查看主(第一父)行上的提交。请注意,如果您的 Git 大于2.31,您仍然需要 -m标志(当使用 git log时,也就是说; git show默认为 --cc,因此不需要 -m,所有这些在 Git 2.31中已经清除了)。

作为 这里提到,这些解决方案包括显示组合差异,如:

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

但是: “ diff --cc”的输出在 Merge 涉及重命名
Git 2.22(2019年第一季度)中的一个新选项将原始树中的路径添加到输出中。

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

logdiff-tree: 添加 --combined-all-paths选项

合并的 diff 格式将只列出一个文件名,即使 重命名或复制检测处于活动状态。

For example, with raw format one might see:

::100644 100644 100644 fabadb8 cc95eb0 4866510 MM describe.c
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM   bar.sh
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR   phooey.c

这并不能让我们知道 bar.sh的原始名称在 第一个父母,不让我们知道他们的名字 phooey.c基因在父母任何一方身上。

In contrast, for non-merge commits, raw format does provide original filenames (and a rename score to boot).
In order to also provide original filenames for merge commits, add a --combined-all-paths option (which must be used with either -c or --cc, and is likely only useful with rename or copy detection active) so that we can print tab-separated filenames when 涉及到重新命名。

这将上述输出转换为:

::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c  desc.c  desc.c
::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM   foo.sh  bar.sh  bar.sh
::100644 100644 100644 e07d6c5 9042e82 ee91881 RR   fooey.c fuey.c  phooey.c

此外,在补丁格式中,这会更改 from/to 头,这样就不再只有一个“ from”头,而是为每个父节点获得一个。
For example, instead of having

--- a/phooey.c
+++ b/phooey.c

我们会看到的

--- a/fooey.c
--- a/fuey.c
+++ b/phooey.c

git show -c c0f501将显示一个 组合差速器,从提交 c0f501到它的双亲,由 git diff在合并期间打印。

这比 git show -m提供了更好的概述。

然而,它只显示相对于父母双方(或至少两个父母的八达通合并)更改的文件的变化。例如,当文件 f1f2在第一个父节点上被更改,而 f2f3在第二个父节点上被更改时,这个命令只显示来自 f2的更改。f1f3中的变化将显示为 not。 因此,为了获得一个完整的概述,在使用 git show -c之前或之后使用 git show -m仍然是有意义的。

一个例子输出 git show -c的章鱼合并与变化在两个文件,其中只有一个是变化为多于一个家长:

commit 3a9f99582921495f7c25e682d4af36d3407983f9 (HEAD -> master)
Merge: 33fb507 91c772b edf2d9c
Author: Foo Bar <foobar@example.net>
Date:   Mon Mar 22 15:56:37 2021 +0100


Merge branches 'b1' and 'b2'


diff --combined b
index 4658c0c,a305e3c,2788b76..9c7beb1
--- a/b
+++ b/b
@@@@ -1,5 -1,5 -1,5 +1,7 @@@@
1
2
+ +a
3
++b
4
++ c

相比之下,git show -m提供了以下冗长但完整的输出:

git show -m 3a9f99582921495f7c25e682d4af36d3407983f9
commit 3a9f99582921495f7c25e682d4af36d3407983f9 (from 33fb5076fbbcc2d82aa0b877c959b8e4cc4f7b74)
Merge: 33fb507 91c772b edf2d9c
Author: Foo Bar <foobar@example.net>
Date:   Mon Mar 22 15:56:37 2021 +0100


Merge branches 'b1' and 'b2'


diff --git a/a b/a
index 94ebaf9..775aea6 100644
--- a/a
+++ b/a
@@ -1,4 +1,5 @@
1
+a
2
3
4
diff --git a/b b/b
index 4658c0c..9c7beb1 100644
--- a/b
+++ b/b
@@ -1,5 +1,7 @@
1
2
+a
3
b
4
+c


commit 3a9f99582921495f7c25e682d4af36d3407983f9 (from 91c772b2c57ff9b4791b57712c26aefbd0c7e730)
Merge: 33fb507 91c772b edf2d9c
Author: Foo Bar <foobar@example.net>
Date:   Mon Mar 22 15:56:37 2021 +0100


Merge branches 'b1' and 'b2'


diff --git a/b b/b
index a305e3c..9c7beb1 100644
--- a/b
+++ b/b
@@ -2,4 +2,6 @@
2
a
3
+b
4
+c


commit 3a9f99582921495f7c25e682d4af36d3407983f9 (from edf2d9c9a255a709875988278a4eda6f7072196e)
Merge: 33fb507 91c772b edf2d9c
Author: Foo Bar <foobar@example.net>
Date:   Mon Mar 22 15:56:37 2021 +0100


Merge branches 'b1' and 'b2'


diff --git a/a b/a
index 94ebaf9..775aea6 100644
--- a/a
+++ b/a
@@ -1,4 +1,5 @@
1
+a
2
3
4
diff --git a/b b/b
index 2788b76..9c7beb1 100644
--- a/b
+++ b/b
@@ -1,5 +1,7 @@
1
2
+a
3
+b
4
c

下面是一个简单的命令:

git show HEAD -m

Maybe it's slightly more memorable, given that it mirrors other commonly used commands.

它显示合并后对合并分支所做的所有更改。