如何“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^1 与 M 表示文件 mainline.txt
和 common.txt
都改变了。进一步假设区分 M^2 和 M 表示文件 sidebranch.txt
和 common.txt
都已更改。合并后的差异将仅显示common.txt
,同时跳过mainline.txt
和sidebranch.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
提供了更好的概览。
但是,它仅显示相对于两个父级(或至少两个父级以进行章鱼合并)更改的文件中的更改。例如。当文件f1
和f2
在第一个父级上更改并且f2
和f3
在第二个父级上更改时,此命令将仅显示来自f2
的更改。 f1
和 f3
中的更改将不会显示。
因此,为了获得完整的概述,在使用 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”合并提交的差异?的主要内容,如果未能解决你的问题,请参考以下文章