合并提交的 git 显示

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40986518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 12:28:49  来源:igfitidea点击:

git show of a merge commit

gitmergeshow

提问by Sohaib Farooqui

when I have a merge commit and run git show #commit, it shows only commit log, not the the diff to the real change, like

当我有一个合并提交并运行 git show #commit 时,它只显示提交日志,而不是实际更改的差异,例如

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?

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

回答by torek

TL;DR: use git show -m c05f017or git show --first-parent c05f017, or perhaps git diff c05f017^ c05f017.

TL;DR:使用git show -m c05f017git show --first-parent c05f017,或者也许git diff c05f017^ c05f017



There's a fundamental error in your question: commits are not diffs; commits are snapshots. This might seem like a distinction without a difference—and for some commits, it is. But for merge commits, it's not.

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

When git show(or git log -p) shows a commit asa diff, it's doing so by comparing the commit's snapshot to something else. The git diffcommand does the same thing: it compares one commit to another commit. (Or it can compare a commit to the work-tree, or to the contents of the index, or a few other combinations as well.)

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

For ordinary commits, it's trivially obvious what to compare: compare thiscommit's snapshot to the previous(i.e., parent) commit's snapshot. So that is what git showdoes (and git log -ptoo): it runs a git difffrom the parent commit, to this commit.

对于普通提交,比较什么是显而易见的:将这次提交的快照与之前(即父)提交的快照进行比较。所以这就是git showgit log -p也是):它git diff从父提交运行一个,到这个提交。

Merge commits don't have just one parent commit, though. They have twoparents.1This is what makes them "merge commits" in the first place: the definition of a merge commit is a commit with at least two parents.

但是,合并提交不仅仅只有一个父提交。他们有两个父母。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.

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



When there are two parents, which one(s) should git showcompare against?

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

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

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

What git showchooses to do by default is more complicated. Since there are two parents, git showfirst compares against the "first parent",2then compares against the second parent. Then—this part is quite crucial—it combines the two diffs, producing a so-called "combined diff".

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

For the next section, let me note a tricky, but very useful, bit of Git syntax. If you have a commit ID like c05f017, you can add a caret or "hat" character ^after that, to name a parent commit. You can optionally add another number to select whichparent. For regular (non-merge) commits there's only one, so c05f017^is theparent. For merge commits, c05f017^and c05f017^1both mean the first parent, while c05f017^2means the second parent.

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



2I put this in quotes because the first parentidea is especially important in Git, as we will see in a moment. In other words, Git cares most about which parent is first, while the rest are just "the rest".

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



Combined diffs

组合差异

The combined diff format is described in the documentation, but a key bit is first described here, so as to make it especially obscure:3

文档中描述了组合的差异格式,但这里首先描述一个关键位,以使其特别模糊:3

Note that combined difflists only files which were modified from all parents.

请注意,组合差异仅列出从所有父项修改的文件。

That is, suppose Mis a merge commit, and diffing M^1vs Msays file mainline.txtand common.txtwere both changed. Suppose further that diffing M^2and Msays that file sidebranch.txtand common.txtwere both changed. The combined diff will show only common.txt, skipping both mainline.txtand sidebranch.txtbecause those two files were only modified from oneparent (each). (Even then Git may show only some of the diffs for common.txt.)

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



3It took me a long time to find this in the documentation, as I kept looking at the other section.

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



Splitting the diffs

拆分差异

The -moption—mprobably stands for mergehere—tells Git to, in effect, "split" the merge. That is, instead of trying to combine the diffs against each parent into one big combined diff, just show the diff against eachparent, one diff at a time.

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

This is sometimes what you want. When it's not what you want, you can run your own explicit git diffto just diff against one of the two parents (or see below).

这有时正是您想要的。当它不是你想要的时,你可以运行你自己的显式git diff来只针对两个父母之一(或见下文)进行差异化。

Which parent should you diff against?

你应该反对哪个家长?

Usually, the correct answer is "the first parent".

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

The key to the "first parent" notion is that when Git makes a merge commit, it always records the branch you're on at the time, as the first parent. The other branch becomes the second parent.

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

That is, if you're on developand you merge topic:

也就是说,如果您打开develop并合并topic

$ git checkout develop
$ git merge topic

Git will make a new commit—a merge commit, with two parents—on your current branch, develop. The firstparent of the merge commit will be the commit that was the tip of developjust a moment ago. The secondparent will be the commit that is (still) the tip of topic.

Git会作出新的承诺,一个合并提交,两个父母,你的当前分支,develop。合并提交的第一个父项将是develop刚才的提示的提交。所述第二亲本将是提交是(仍然)的尖端topic

Since you're usually concerned with what the merge brought in, comparing against the first parent will give you that. So usually that's what you want. For this reason, git showallows you to run git show --first-parent. That "splits" the commit and then git showonly diffs against the first parent. (This is a bit different than git show -m, which splits the commit twice: the first split compares against the first parent, and the second split compares against the second parent.)

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

Similarly, you can run git log -p --first-parent... but you must still add -mto see the change as a patch, because by default git logjust skips showing the diffs entirely for a merge. (Internally, the skipping-because-merge operation overrides the way the split makes it act like not-a-merge.) Here, the --first-parentflag has an even more important effect: the log operation does not look at anyof the side branch's commits at all, only those on the main (first-parent) line.

类似地,您可以运行git log -p --first-parent... 但您仍然必须添加-m以将更改视为补丁,因为默认情况下git log只是跳过完全显示差异以进行合并。(在内部,skiping-because-merge 操作覆盖了拆分的方式,使其表现得像 not-a-merge。)在这里,--first-parent标志具有更重要的作用:日志操作不查看任何侧分支的提交根本上,只有那些在主(第一父)线上的。

回答by VonC

As mentioned here, those solution involve showing a combined diff, like:

如此处所述,这些解决方案涉及显示组合差异,例如:

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

But: the output from "diff --cc" did not show the original paths when the merge involved renames.
A new option in Git 2.22 (Q1 2019) adds the paths in the original trees to the output.

但是: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: add --combined-all-pathsoption

The combined diff format for merges will only list one filename, even if rename or copy detection is active.

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

This doesn't let us know what the original name of bar.shwas in the first parent, and doesn't let us know what either of the original names of phooey.cwere in either of the parents.

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-pathsoption (which must be used with either -cor --cc, and is likely only useful with rename or copy detection active) so that we can print tab-separated filenames when renames are involved.

This transforms the above output to:

::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

Further, in patch format, this changes the from/to headers so that instead of just having one "from" header, we get one for each parent.
For example, instead of having

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

we would see

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

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