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

xtupzzrd  于 2023-04-04  发布在  Git
关注(0)|答案(5)|浏览(141)

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

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

    Merge branch 'abc'

我知道真实的的提交是在合并日志中,但我想保存输入。有办法在一个中显示差异吗?

mwg9r5ms

mwg9r5ms1#

请使用以下选项之一:

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

你的问题中有一个根本性的错误:提交不是差异;提交是快照。这看起来似乎是一个没有区别的区别-对于一些提交,它是 *。但是对于合并提交,它不是 *。
git show(或git log -p)将一个提交 * 显示为 * 一个diff时,它是通过 * 将提交的快照与其他内容进行比较 * 来实现的。git diff命令做同样的事情:它将一个提交与另一个提交进行比较。(或者它可以将一个提交与工作树进行比较,或者与索引的内容进行比较,或者与其他一些组合进行比较。)
对于普通的提交,比较什么是显而易见的:比较 this commit的快照和 previous(即父)commit的快照。这就是git show所做的(还有git log -p):它运行从父提交到该提交的git diff
然而,合并提交并不是只有一个父提交,而是有 * 两个 * 父提交。1这就是为什么合并提交首先是“合并提交”:合并提交的定义是具有至少两个父提交。
1一个合并提交可以有三个或更多的父提交。这些被称为“章鱼合并”。它们没有做任何特别的事情,但是,主要是为了炫耀。:-)你可以在这里忽略它们。

当有两个父节点时,git show应该与哪一个进行比较?

git log -p在默认情况下根本不进行比较,您可以通过添加各种标志(见下文)使其显示某些内容。
git show在默认情况下选择做的事情更复杂。由于有两个父节点,git show首先与“第一个父节点”进行比较,2然后与第二个父节点进行比较。然后-这部分非常关键-它组合两个差异,产生所谓的“组合差异”。
在下一节中,让我注意一个棘手但非常有用的Git语法。如果你有一个像c05f017这样的提交ID,你可以在后面添加一个插入符号或“帽子”字符^,来命名一个父提交。你可以选择添加另一个数字来选择 which parent。对于常规的(non-merge)commits只有一个,所以c05f017^the parent。对于merge commits,c05f017^c05f017^1都表示 the first parent,而c05f017^2表示 the second parent
2我把这句话放在引号里是因为 first parent 这个概念在Git中特别重要,我们一会儿就会看到。换句话说,Git最关心的是哪个父节点是 first,而其余的只是“其余的”。

组合差异

文档中描述了组合的diff格式,但这里首先描述了一个关键位,以便使其特别模糊:3
请注意,combined diff 仅列出从所有父代修改的文件。
也就是说,假设 M 是一个合并提交,并且diff M^1 vs M 表示文件mainline.txtcommon.txt都被更改。进一步假设diff M^2M 表示文件sidebranch.txtcommon.txt都被更改。组合的diff将显示 *only common.txt *,跳过mainline.txtsidebranch.txt,因为这两个文件只修改了 * 一个 * 父文件(每个)。(即使这样,Git也可能只显示common.txt的一些差异。)
3我花了很长时间才在文档中找到这一点,因为我一直在看其他部分。

区分

-m选项-m 在这里可能代表 merge-告诉Git实际上“拆分”合并。也就是说,不要试图将每个父节点的差异组合成一个大的组合差异,而只是显示 each 父节点的差异,一次一个差异。
有时候这是你想要的,但如果不是你想要的,你可以运行你自己的git diff来比较两个父类中的一个(或者见下文)。

需要与哪个父节点进行diff比较?

通常,正确答案是“第一个父母”。
“第一个父分支”的关键在于,当Git进行合并提交时,它总是把你当时所在的分支记录为第一个父分支,而另一个分支则成为第二个父分支。
也就是说,如果你在develop上合并topic

$ git checkout develop
$ git merge topic

Git将在当前分支develop上创建一个新的提交-一个 merge commit,有两个父提交。合并提交的 * 第一个 * 父提交将是刚才develop的提交。* 第二个 * 父提交将是(仍然)topic的提交。
由于你通常关心合并带来的是什么,与第一个父节点进行比较会给你带来什么。所以通常这就是你想要的。出于这个原因,git show允许你运行git show --first-parent。这会“分割”提交,然后git show只与第一个父节点进行比较。(这与git show -m有点不同,它会分割提交两次:第一分割与第一父比较,第二分割与第二父比较)。

类似地,你可以运行git log -p --first-parent在这里,--first-parent标志有一个更重要的作用:log操作根本不查看分支的提交,只查看主行(第一父行)上的提交。注意,如果你的Git版本早于2.31,你仍然需要-m标志(当使用git log时,即;git show默认为--cc,因此不需要-m,所有这些都在Git 2.31中进行了清理。

1tuwyuhd

1tuwyuhd2#

下面是一个简单的命令:
git show HEAD -m
考虑到它反映了其他常用的命令,也许它稍微更容易记住。
它显示合并后对合并分支所做的所有更改。

9ceoxa92

9ceoxa923#

参见git show --diff-merges,其中--diff-merges是由Git 2.31 (Q1 2021)引入的。
作为mentioned here,这些解决方案涉及显示组合的差异,如:

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

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

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

logdiff-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在任何一个父节点中的原始名称。
相比之下,对于非合并提交,原始格式确实提供原始文件名(以及 Boot 的重命名得分)。
为了提供合并提交的原始文件名,添加一个--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”头,而不是只有一个“from”头。
例如,与其拥有

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

我们会看到

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

pn9klfpd4#

git show -c c0f501将显示提交c0f501到其双亲的组合差异,如git diff在合并期间打印的。
这给出了比git show -m更好的概述。
但是,它只显示相对于双亲更改的文件中的更改(或至少两个用于octopus合并的父节点)。例如,当文件f1f2在第一个父节点上被改变,而f2f3在第二个父节点上被改变时,这个命令只会显示f2中的更改。f1f3中的更改 * 不会 * 显示。因此,为了获得完整的概述,在使用git show -c之前或之后使用git show -m仍然是有意义的。
以下是一个octopus合并的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
gab6jxml

gab6jxml5#

现代git有精心设计的diff选项:

--diff-merges=(off|none|on|first-parent|1|separate|m|combined|c|dense-combined|cc|remerge|r), --no-diff-merges
     Specify diff format to be used for merge commits.
     Default is dense-combined unless --first-parent is in use, in which case first-parent is the default.

     --diff-merges=(off|none), --no-diff-merges
         Disable output of diffs for merge commits. Useful to override implied value.

     --diff-merges=on, --diff-merges=m, -m
         This option makes diff output for merge commits to be shown in the default format.
         -m will produce the output only if -p is given as well. The default format
         could be changed using log.diffMerges configuration parameter, which default value is separate.

     --diff-merges=first-parent, --diff-merges=1
         This option makes merge commits show the full diff with respect to the first parent only.

     --diff-merges=separate
         This makes merge commits show the full diff with respect to each of the parents.
         Separate log entry and diff is generated for each parent.

     --diff-merges=remerge, --diff-merges=r, --remerge-diff
         With this option, two-parent merge commits are remerged to create a temporary
         tree object -- potentially containing files with conflict markers and such.
         A diff is then shown between that temporary tree and the actual merge commit.

         The output emitted when this option is used is subject to change,
         and so is its interaction with other options (unless explicitly documented).

     --diff-merges=combined, --diff-merges=c, -c
         With this option, diff output for a merge commit 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.
         Furthermore, it lists only files which were modified from all parents.  -c implies -p.

     --diff-merges=dense-combined, --diff-merges=cc, --cc
         With this option the output produced by --diff-merges=combined is further
         compressed by omitting uninteresting hunks whose contents in the parents have
         only two variants and the merge result picks one of them without modification.
         --cc implies -p.

相关问题