如何“git show”合并提交的差异?

Posted

技术标签:

【中文标题】如何“git show”合并提交的差异?【英文标题】:How to "git show" the diffs for a merge commit? 【发布时间】:2017-04-20 13:40:50 【问题描述】:

当我有一个合并提交并运行git show <commit-ish> 时,它只显示提交日志,而不是差异:

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

    Merge branch 'abc'

我知道真正的提交在合并日志中,但我想节省输入。有没有办法在一个中显示差异?

【问题讨论】:

How to "git show" a merge commit with combined diff output even when every changed file agrees with one of the parents?的可能重复 【参考方案1】:

使用以下方法之一:

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

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

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

对于普通的提交,比较什么很明显:比较 this 提交的快照与 previous(即父级)提交的快照。这就是git show 所做的事情(还有git log -p):它从父提交运行git diff,到此提交。

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


1一个合并提交可以有三个或更多的父级。这些被称为“章鱼合并”。不过,他们并没有做任何特别的事情,主要是为了炫耀。 :-) 你可以在这里忽略它们。


当有两个父母时,git show 应该与哪一个比较?

git log -p 默认选择做的是根本不比较。您可以通过添加各种标志使其显示某些内容(见下文)。

git show 默认选择做什么比较复杂。由于有两个父母,git show 首先与“第一个父母”进行比较,2 然后与第二个父母进行比较。然后——这部分非常关键——它结合了两个差异,产生了所谓的“组合差异”。

在下一节中,让我注意一个棘手但非常有用的 Git 语法。如果你有一个像c05f017 这样的提交ID,你可以在之后添加一个插入符号或“帽子”字符^,来命名一个父提交。您可以选择添加另一个数字来选择 which 父级。对于常规(非合并)提交,只有一个,所以 c05f017^the 父级。对于合并提交,c05f017^c05f017^1 都表示第一个父节点,而c05f017^2 表示第二个父节点


2我把它放在引号里是因为 first parent 的想法在 Git 中特别重要,我们稍后会看到。换句话说,Git 最关心的是哪个父级首先,而其余的只是“其余的”。


组合差异

组合的diff格式在the documentation中描述,但是在here中先描述了一个关键位,使其特别晦涩:3

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

也就是说,假设 M 是一个合并提交,并且区分 M^1M 表示文件 mainline.txtcommon.txt都改变了。进一步假设区分 M^2M 表示文件 sidebranch.txtcommon.txt 都已更改。合并后的差异将仅显示common.txt,同时跳过mainline.txtsidebranch.txt,因为这两个文件仅从一个 父级(每个)修改。 (即使这样,Git 也可能只显示common.txt 的一些差异。)


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


拆分差异

-m 选项——m 在这里可能代表 merge——告诉 Git 实际上“拆分”合并。也就是说,与其尝试将每个父级的差异组合成一个大的组合差异,只需显示与 每个 父级的差异,一次一个差异。

这有时是你想要的。如果不是您想要的,您可以运行自己的显式 git diff 来区分两个父母之一(或见下文)。

您应该与哪个父母进行比较?

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

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

也就是说,如果您在 develop 上并合并 topic

$ git checkout develop
$ git merge topic

Git 将在您当前的分支develop 上进行一个新的提交——一个合并提交,有两个父节点。合并提交的 first 父级将是刚才develop 的提示的提交。 second 父级将是(仍然)是topic 提示的提交。

由于您通常关心合并带来的内容,因此与第一个父项进行比较会给您带来好处。所以通常这就是你想要的。因此,git show 允许您运行git show --first-parent。这会“拆分”提交,然后 git show 仅与第一个父级不同。 (这与git show -m 有点不同,它将提交拆分两次:第一次拆分与第一个父项进行比较,第二次拆分与第二个父项进行比较。)

同样,你可以运行git log -p --first-parent 这里,--first-parent 标志有一个更重要的作用:日志操作根本不查看侧分支的提交,只查看那些在主(第一父)行上。请注意,如果您的 Git 早于 2.31,您仍然需要 -m 标志(使用 git log 时,即;git show 默认为 --cc,因此不需要 -m,并且所有这在 Git 2.31 中已被清理)。

【讨论】:

谢谢,帮助找到了在解决的冲突中丢失的方法。我认为git show -m c05f017 是最有用的。使用git show -m -p --stat c05f017 将添加已更改文件的列表。如果你跳过-p,你只会得到文件列表。 谢谢。 git show HEAD^2 很有帮助,解释后很容易记住。很遗憾git show --first-parent 做了同样的事情...... @remcycles:在您发表评论大约 2 年后,有人提议为 Git 2.29(我认为)解决此问题。【参考方案2】:

作为mentioned here,这些解决方案涉及显示组合差异,例如:

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

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

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

log,diff-tree:添加--combined-all-paths选项

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

例如,使用原始格式可能会看到:

::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 在父母中的任何一方。

相比之下,对于非合并提交,原始格式确实提供了原始文件名(以及启动时的重命名分数)。 为了也为合并提交提供原始文件名,添加一个--combined-all-paths 选项(必须与-c--cc 一起使用,并且可能只在重命名或复制检测活动时有用)以便我们可以打印选项卡- 分隔文件名时 涉及重命名。

这会将上面的输出转换为:

::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”标头。 例如,而不是拥有

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

我们会看到

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

【讨论】:

【参考方案3】:

这是一个简单的命令:

git show HEAD -m

也许它更容易记住,因为它反映了其他常用命令。

它显示了由于合并而对合并分支所做的所有更改。

【讨论】:

【参考方案4】:

git show -c c0f501 将在合并期间向其父母双方显示来自提交c0f501 的combined diff,如git diff 打印的那样。

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

但是,它仅显示相对于两个父级(或至少两个父级以进行章鱼合并)更改的文件中的更改。例如。当文件f1f2 在第一个父级上更改并且f2f3 在第二个父级上更改时,此命令将仅显示来自f2 的更改。 f1f3 中的更改将不会显示。 因此,为了获得完整的概述,在使用 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”合并提交的差异?的主要内容,如果未能解决你的问题,请参考以下文章

idea解决代码冲突

如何提交两个分支的“git diff”?

在GitHub上实际显示的合并提交的差异是什么?

获取合并提交的git diff输出结果

Git:如何将 KDiff3 配置为合并工具和差异工具?

Git撤消先前的合并,这会阻止差异