最近合并了一个包含3行修订的分支。第二天,提交了一个分支请求请求,其中包含3行修复程序的2行。它是原始合并分支后面的多个提交。那棵树看起来像...
A1--A2--A3-------B3--C4
\ \ / /
\ A3--B3 /
\ /
A2------C3
因此,B3将包含3行,而C3将包含2行。当我去检查C4时,即使技术上没有,它也会显示提交的差异。实际上,在进行交互式基准时,该基准将停止并说这是一个空提交。
我知道git可以让您合并空的提交,但我感到困惑的是:
答案 0 :(得分:0)
合并会影响提交图,因此根据定义,必须始终允许合并。一些请求合并的命令行命令可以作为非合并 fast-forward 操作完成,但是GitHub clicky按钮在这些情况下会强制进行真正的合并。
您尚未回答我的评论问题,因此我在这里至少要含糊不清或冗长。另外,我现在意识到您的图表中有两次A2
,A3
和B3
,这意味着您的图表是错误的,我们应该有例如:
A1--A2--A3-------B5--C4
\ \ / /
\ B3--B4 /
\ /
C2------C3
或者也许:
A1--A2--A3-------B4--C4
\ \ / /
\ ----B3 /
\ /
--------C3
最后,在我开始之前,将这些东西标记为An
,Bn
和Cn
的序列从Git的角度来看并没有什么用处,因为它是无关紧要的在查看和合并提交时,哪个分支包含提交。重要的是提交者本身及其父母/子女关系-分支名称仅在此处用于人类。 :-)因此,我将其重新绘制为:
...--A--B--C------H--I <-- branchname
\ \ / /
\ D--E /
\ /
F----G
其中H
和I
是合并提交。 (要 make H
,必须有人跑:
git checkout branchname
当branchname
指向C
时,然后:
git merge --no-ff <identifier-selecting-E>
或类似的东西。 --no-ff
强制此操作为真正的合并,而不是快进的非合并“合并”。请注意,GitHub“合并”按钮可以执行此操作,即使用--no-ff
。)
当我去检查[在我的图表中,最终的合并提交现在称为
I
–编辑。]时,即使技术上没有,它也显示了提交的差异...
这是使事情变得有趣的地方。如您现在已经知道的那样,每个提交都是其所有文件的完整快照-提交不包含与先前提交的区别,它们仅包含文件的快照。
当我们有一个普通的非合并提交,例如E
时,该提交具有一个父提交,因此Git可以通过提取快照轻松显示提交E
其父提交D
的快照,然后为E
本身提取快照,并比较两者。对于在这两个快照中不同的每个文件,Git尝试产生一个最小的编辑序列,该序列会将D
更改为E
。
如果您运行git commit
,并且您的索引(Git生成新提交的区域)与当前提交匹配,则Git通常只会告诉您“没有任何提交”,并且拒绝进行提交。添加--allow-empty
会告诉Git:创建新快照,即使它与当前快照相同。不是快照本身为空,而是上一次提交的更改为空。因此,没有特别的理由来打扰新快照。
合并提交不同。在某种程度上,它们是,就像普通的提交一样:它们拥有快照。区别在于它们有两个或更多的 1 父母,每个父母还都拥有快照。这意味着当涉及至少三个快照时,很难显示两个快照之间的差异。
某些命令,例如git log
,默认为不打扰。也就是说,他们看到他们正在显示的提交是合并提交,他们只是不费心地提取任何父级和计算差异。
git show
命令在默认情况下会做一些额外的事情:它提取父母的快照和子合并提交的所有快照,然后将子文件中的每个文件与 all 该文件的父母版本。然后,它会抛出与 any 父变量完全匹配的任何文件,并且仅对于与双方父变量不同的某些文件,显示Git称为组合差异。组合的diff输出格式记录在several Git manual pages including that for git diff
中。
在这些命令中添加-m
标志使Git进行 split 合并为多个虚拟提交,每个父提交一个。也就是说,除了显示I
与H
和 G
的组合差异之外,Git还将显示H
-vs-的一个差异I
,然后是G
-vs-I
的第二个差异。这些差异包含更多信息:例如,如果文件README.txt
在H
-vs-I
中是相同的,而foo.txt
是不同的,则您会看到{{1 }}。如果foo.txt
在{{1}-vs-README.txt
中与G
相同,则您会看到I
。但是,如果您得到了一个组合差异,那么Git就会同时抛出 foo.txt
(README.txt
的匹配项README.txt
的)和 { {1}}(与H
匹配的I
),所以您根本看不到任何一个文件。
现在,即使所有在和输入中都完全匹配的文件都提交了,即foo.txt
和G
中的快照完全匹配了,Git仍会进行合并提交,因为合并提交I
本身(其两个父哈希ID指向H
和G
)记录了是合并。这将为以后的合并设置新的合并基础。 Git使用图为以后的I
操作计算合并基础,因此我们需要连接这两个节点的图节点。在您的示例中不是这种情况,但是值得注意。
1 Git称两个以上的父母合并为章鱼合并。但是,它们不会做任何更明显的成对合并所无法实现的事情。其他一些VCS仅支持成对合并。
实际上,当进行交互式重新设置时,重新设置将停止并说[合并是]空提交。
Rebase通过复制提交来工作。这需要将每个提交变成一个变更集,将提交与其父级进行比较。根据定义,合并至少有两个父对象,因此不能以这种方式复制。 2 Rebase解决此难题的方法是完全删除 合并提交。但是:
...因此[
H
将包含3行,而[G
]将包含2行。
让我们绘制第二个分支名称,指向git merge
,用于重新定级,而完全不进行合并提交E
:
G
我们可以先运行G
,然后运行I
(有或没有...--A--B--C------H <-- branchname
\ \ /
\ D--E
\
F----G <-- feature
)。 Git将从git checkout feature
开始并向后工作,从git rebase branchname
开始并向后工作,以枚举--interactive
无法到达的提交。这两个“后退工作”步骤在提交feature
处同时出现,因此{em> not 不会被复制branchname
,更早的提交也不会被复制。这样就可以按顺序复制提交G
和H
。
B
的副本可能会起作用,给出:
B
(请注意,rebase与分离的F
一起使用,直接指向某些提交)。
现在,Git准备复制G
中的更改,这将更改已更改的三行中的两行,而剩下一行。 Git认为这是徒劳的(已经更改了两行),并且根据上下文声明了合并冲突(因此您必须清理所有内容并确定哪个是正确的最终版本),或者只是确定什么都没有改变。如果Git确实因合并冲突而停止,则将其修复,并使用提交F
中三行更改的版本。您运行...--A--B--C------H <-- branchname
\ \ / \
\ D--E F' <-- HEAD
\
F----G <-- feature
使其继续运行,此时,Git说没有什么要提交的。您现在可以选择:使用HEAD
提交,以便保留提交消息和单独的快照(与其父快照匹配),或完全使用G
删除提交。
假设您要进行后者,Git通过将名称(F'
)指向其复制的最后一个提交,并设置git rebase --continue
来记住--allow-empty
过去的位置来完成重新配置点:
git rebase --skip
一切都完成了。
2 feature
有一个附加标志ORIG_HEAD
或feature
,声称保留合并。这是不正确的:实际上是重新执行合并。也就是说,它将复制足够的提交以设置合并条件,然后运行...--A--B--C------H <-- branchname
\ \ / \
\ D--E F' <-- feature (HEAD)
\
F----G <-- ORIG_HEAD
进行新的合并。如果您以前解决了冲突,则可能必须再次进行。即使您以前不必解决冲突,也可能必须在这一点上解决。如果您先使用git rebase -i
进行合并,然后再编辑内容,然后依次编辑--preserve
或-p
以产生所谓的 evil merge ,则Git不知道您执行了此操作,并进行了新的非邪恶合并,从而降低了所有的偷偷摸摸的感觉。如果尽管有邪恶合并这个术语,偷偷摸摸实际上是好的事情,那么您将失去它。