合并提交的 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
git show of a merge commit
提问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 c05f017
or git show --first-parent c05f017
, or perhaps git diff c05f017^ c05f017
.
TL;DR:使用git show -m c05f017
或git 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 diff
command 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 show
does (and git log -p
too): it runs a git diff
from the parent commit, to this commit.
对于普通提交,比较什么是显而易见的:将这次提交的快照与之前(即父)提交的快照进行比较。所以这就是git show
(git 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 show
compare against?
当有两个父母时,应该git show
比较哪一个?
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 log -p
选择默认做的不是在所有的比较。您可以通过添加各种标志使其显示某些内容(见下文)。
What git show
chooses to do by default is more complicated. Since there are two parents, git show
first 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^1
both mean the first parent, while c05f017^2
means 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.txt
and common.txt
were both changed. Suppose further that diffing M^2and Msays that file sidebranch.txt
and common.txt
were both changed. The combined diff will show only common.txt
, skipping both mainline.txt
and sidebranch.txt
because those two files were only modified from oneparent (each). (Even then Git may show only some of the diffs for common.txt
.)
也就是说,假设M是一个合并提交,并且比较M^1与M表示文件mainline.txt
并且common.txt
都被更改了。进一步假设 diffing M^2和M表示该文件sidebranch.txt
并且common.txt
都被更改了。组合的差异将只common.txt
显示,跳过这两个文件mainline.txt
,sidebranch.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 -m
option—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 diff
to 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 develop
and 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 develop
just 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 show
allows you to run git show --first-parent
. That "splits" the commit and then git show
only 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 -m
to see the change as a patch, because by default git log
just 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-parent
flag 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-paths
optionThe 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.sh
was in the first parent, and doesn't let us know what either of the original names ofphooey.c
were 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-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 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