您不能将 --no-ff 与 --squash 组合在一起的原因是 squash 合并不是合并。
壁球合并不是合并!
我知道这听起来很奇怪和错误。而且,更糟糕的是,当您在运行git merge --squash 的过程中时,您正在合并。
结果不是“合并”。
这怎么可能?合并的行为怎么会导致不是合并的结果?
在某种程度上,这是一个术语问题。 Git 使用 merge 作为动词,表示合并一些代码更改的动作,作为形容词,表示特定类型的提交:merge commit。包括我在内的人们将这个形容词形式缩短为“a merge”,将其变成名词。
你从 git merge --squash 得到的 commit 不是 merge commit
合并提交,在 Git 中,是一个包含至少两个父提交的提交。
这就是合并提交的定义。它清晰而简单。这意味着在未来,当人们——也许是你自己,也许是其他人——正在查看这个存储库中随着时间推移发生的事情的历史,并且他们找到了提交时,他们将能够看到:“啊哈,这个提交是一次合并,是通过合并这两个父提交来实现的。”
命令git merge --squash 不会进行这些提交之一。相反,它会运行常规的合并代码,然后停止并强制您进行自己的提交,即使合并成功也是如此。当您进行该提交时,它是一个普通的、非合并的、单父提交。
因此,git merge --squash 可以合并(代码),但它不会产生合并。
同时,--no-ff 标志表示“进行合并”
如果您运行常规的非壁球类型git merge,Git 有两个选项,其中一个仅有时可用。 Git 可以进行真正的合并,或者——如果真正的合并是微不足道的——它可以完全跳过合并并在当前分支名称上执行“快进”。
快进也不是合并
这是 Git 奇怪的术语阻碍的另一个地方。将此称为“快进合并”会适得其反:我们不仅没有得到合并commit,甚至没有得到任何合并action。动词动作合并永远不会发生,名词形容词“合并”也不会产生。相反,我们只是在提交图上获得一个标签(分支名称)。1
在任何情况下,有时 Git 可以 做一个简单的合并,只是快进一个标签,但你真的希望 Git 做一个 真正的 合并.特别是,如果您将开发或功能线合并到发布或主分支中,您通常希望保持沿袭(提交历史)分开。快进操作会加入历史记录,而不是将它们分开。这就是为什么git merge 有--no-ff:强制它进行真正的合并,即使Git 可以 做一个假的并且只是快进分支。
将--no-ff(或者,就此而言,--ff-only)与--squash结合起来是没有意义的,因为--squash的意思是“调用合并机制,但不提交,也不记录合并操作未来的承诺。” “squash 合并”——您在运行 git merge --squash 后所做的提交——不是合并提交。
简而言之,壁球合并不是合并。
1这种进步是你做git push时想要的,有时也是你在成功git fetch之后想要的。如果你有一些上游分支,比如origin/master,并且其他人已经向上游添加了提交,你可以git fetch这些提交。然后你通常想在上面git rebase你自己的作品,但如果你还没有自己的新作品,你可以简单地快进你的master。
执行此操作的命令是git merge,这很烦人,因为如果您做有自己的工作,您可能应该使用git rebase。幸运的是,无论如何你都可以运行git rebase;当您没有工作时,这对您的分支名称的影响与快进合并相同。
或者,您可以运行git merge --ff-only,它会尝试快进,如果无法实现,则会失败。这有点长,所以我自己使用别名git mff,其中m 代表“合并”或“移动”,ff 代表“快进”。我有第二个别名lin,用于log/look-at what is incoming。制作这两个别名非常简单:
git config --global alias.lin "log ..@{u}"
git config --global alias.mff "merge --ff-only"
现在我可以运行git fetch,然后运行git lin 来看看有什么进来,然后git mff 如果我只需要快进到那个,或者git show 特别有趣的部分,创建一个新分支和/或git rebase(如有必要),等等。