这里值得注意的是git merge
and git merge --squash
密切相关,但是git merge --squash
不创建合并.
这里的措辞非常重要,特别是“merge”前面的冠词“a”:“a merge”是名词,而“to merge”是动词。两个命令都执行合并操作。区别在于结果如何保存.
还值得以提交图的形式快速提醒一下合并的外观。每轮o
这里的节点代表一次提交,较早的提交位于左侧。分支名称是指向一个特定提交(tip分支的提交)。你可以从这个开始,例如:
...--o--*--o--o <-- main
\
o--o--o <-- feature
然后您决定合并一个特定的feature
分支回到main
分支,所以你运行:
$ git checkout main && git merge feature
这会执行合并(动词)并进行合并(名词),结果如下所示:
...--o--*--o--o---o <-- main
\ /
o--o--o <-- feature
Git 添加了一项新提交main
,这个新的提交是合并提交:它指向前一个提示main
,并且还返回(在这种情况下,也向下)到仍然当前的尖端feature
。 (名字feature
继续指向与之前相同的提交。)
What git merge --squash
所做的就是修改最后一步。它没有进行合并提交,而是完全抑制提交(没有明显的原因)并强制您运行git commit
。当你do run git commit
,这使得ordinary提交,而不是merge提交,结果如下所示:
...--o--*--o--o---o <-- main
\
o--o--o <-- feature
这里有两个关键项目:
The contents新提交的数量是相同的。两个新提交都是由索引制成 (the index是 Git 的术语,意思是“你所做的下一次提交会发生什么”)。该索引是通过合并动词过程设置的。这first父级是“主线”提交,来自我们进行合并时所在的分支。这second父级是另一个提交,我们刚刚合并的那个。
The 父级链接新提交的数量有所不同。真正的合并有both先前的提交作为其父级,但“挤压合并”仅one上一个提交作为其父级——“主线”提交。这意味着它不——它can不 - 记住合并了哪个提交。
在有冲突(但真实)的合并的情况下,git merge
无法自行进行新的提交,因此它会停止并迫使您解决冲突。解决完这些冲突后,您必须手动运行git commit
,就像您必须始终(没有明显原因)所做的那样git merge --squash
及其假合并。您还可以请求任何真正的合并停止并让您检查结果,使用git merge --no-commit
.
这导致了简单的方法将真正的合并变成假(挤压)合并,只要真正的合并尚未提交:
For git commit
要知道进行合并,它依赖于冲突(或--no-commit
)合并。该文件名为.git/MERGE_HEAD
。 (它还留下了一个名为.git/MERGE_MSG
尽管这个额外的文件是无害的。)
因此,您可以简单地remove .git/MERGE_HEAD
并运行git commit
。 (一旦您使用其消息编写了提交,您可能还想删除 MERGE_MSG 文件。在那之前您可以使用它as消息,或作为其起点。)git commit
步骤将不再知道进行合并提交,而是进行普通提交 - 瞧,您已经进行了挤压合并。
如果您已经进行了真正的合并提交,这个过程可能会更困难。特别是,如果您有发表合并,你现在必须让所有获得此合并的人,将其收回。否则真正的合并将会返回(他们很可能会再次合并),或者给他们带来问题,甚至两者兼而有之。如果你能做到这一点,或者如果它有not发布后,您只需“将合并推到一边”,然后放入一个假的合并提交。
让我们重新绘制我们所拥有的,以腾出一些空间。这是与之前相同的图表,只是分散到更多行上:
...--o--*----o----o
\ \
\ o <-- main
\ /
o--o--o <-- feature
如果我们可以搬家怎么办main
回到顶行,然后进行新的提交?好吧,我们会得到下面的图:
...--o--*----o----o--o <-- main
\ \
\ o [abandoned]
\ /
o--o--o <-- feature
请注意,所有箭头 - 包括提交用于记录历史的内部提交箭头(此处未显示,因为它们很难在文本绘图中生成) - 指向leftward,所以在任何最上面的提交中,都不知道它们下面的任何提交:这些都是“在他们的右边”。只有底线提交,加上我们要放弃的提交,才知道有关顶线提交的任何信息。
此外,如果我们要完全放弃中线提交,那么我们就停止绘制它及其两个合并箭头:
...--o--*----o----o--o <-- main
\
\
\
o--o--o <-- feature
看看这个:我们刚刚为那些假挤压“合并”之一绘制了我们想要的提交图。这正是我们想要的,只要我们得到正确的内容在我们的新承诺中。
但是——这是一种练习;在继续之前看看你是否知道答案——新提交的内容从哪里来?答案就在上面的第一个要点中,以粗体显示。 (如果忘记了,请返回检查。)
现在你知道了git commit
使用索引来进行新的提交,让我们考虑一下我们现在拥有的真正的合并提交。它有内容。 (所有提交都有内容。)它们来自哪里?它们来自索引!如果我们能以某种方式获取这些内容回到指数,我们是黄金。事实上,我们可以:我们所要做的就是查看承诺,与git checkout
, or 已经将其作为当前提交.
由于我们刚刚进行了新的合并提交,因此我们已经将合并提交作为当前提交。索引是干净的——如果我们运行git status
,它说没有什么可提交的。所以现在我们使用git reset --soft
to reset分支指针main
后退一步,得到我们的中间图。这--soft
参数告诉 Git:“移动分支指针,但是don't更改索引和工作树。”因此我们仍然拥有原始合并中的索引(和工作树)。现在我们只需运行git commit
要进行普通提交,请提供一些适当的提交消息,然后我们就完成了:我们有一个挤压合并。最初的合并现已被放弃,最终(默认情况下 30 天后的某个时间)Git 会注意到它不再使用并将其删除。
(要后退一步,您可以使用HEAD~1
or HEAD^
;两者的意思是一样的。因此命令序列就是git reset --soft HEAD^ && git commit
,假设当前提交是您希望用假合并替换的真实合并。)
可以使用上面较长的方法even if您进行了多次合并提交。不过,您必须决定是想要多次假合并,还是一次大假合并。例如,假设你有这个:
...--o--o---o--o--o <-- develop
\ \ / /
\ o--o / <-- feature1
\ /
o----o <-- feature2
你希望你的最终图片看起来像:
...--o--o---o--o--o <-- develop
\ \
\ o--o <-- feature1
\
o----o <-- feature2
最后一个在哪里two致力于develop
是两个假(南瓜)合并,还是您只想要一个大假南瓜:
...--o--o---o--o <-- develop
\ \
\ o--o <-- feature1
\
o----o <-- feature2
最后一次提交是最终结果吗?如果你想twofake-squashes,您将需要保留两个原始合并提交足够长的时间,以便将它们放入索引并从中进行两个普通提交。如果你只想对最终合并进行一次大的假挤压,那么只需要两个 Git 命令:
$ git reset --soft HEAD~2 && git commit
因为我们将保留second合并索引,向后移动two步骤,然后进行新的提交,将其推到一边both merges.
再次强调,重要的是,没有一个real合并已发布,或者,如果它们已发布,您说服所有其他已选择它们的人停止使用它们。