从我的阅读中,他们两个都有助于我们获得线性历史。
根据我的实验,变基一直都在起作用。但是merge --ff-only仅在可以快速转发的情况下有效。
我还注意到,git merge创建了一个合并提交,但是如果我们仅使用--ff,它会给出一个线性历史记录,基本上等于git rebasing。所以--ff-only杀死了git merge的目的,对吗?
那么它们之间的实际区别是什么?
Answers:
请注意,git rebase
它的工作不同于git merge
(带有或不带有--ff-only
)。要做的rebase
是获取现有提交并复制它们。举例来说,假设您在进行branch1
两次提交,A
并且B
:
...-o--o--A--B <-- HEAD=branch1
\
o--C <-- branch2
然后您决定宁愿启用这两个提交branch2
。您可以:
A
(A
与父项的区别)B
(B
与相比A
)branch2
A
并提交,从复制您的提交消息A
;我们称这个提交A'
B
并提交,从复制您的提交消息B
;让我们称之为B'
。有一个git命令可以为您完成diff-then-copy-commit git cherry-pick
。所以:
git checkout branch2 # switch HEAD to branch2 (commit C)
git cherry-pick branch1^ # this copies A to A'
git cherry-pick branch1 # and this copies B to B'
现在您有了这个:
...-o--o--A--B <-- branch1
\
o--C--A'-B' <-- HEAD=branch2
现在,您可以使用切换回branch1
原来的A
和B
,并将其删除git reset
(我将--hard
在这里使用它,因为它也可以清理工作树,因此更加方便):
git checkout branch1
git reset --hard HEAD~2
这消除了原有的A
和B
,1所以现在您有:
...-o--o <-- HEAD=branch1
\
o--C--A'-B' <-- branch2
现在,您只需要重新签出branch2
即可继续在那里工作。
这是做什么的git rebase
:它“移动”提交(尽管不能通过实际上移动它们,因为它不能这样做:在git中,一个提交永远都不能更改,因此即使仅更改父ID,也需要将其复制到新的并且稍有不同的地方承诺)。
换句话说,虽然git cherry-pick
是一次提交的自动差异化和重做,但它git rebase
是一种重做多次提交的自动化过程,最后还可以移动标签以“忘记”或隐藏原始文档。
上面的示例说明了将提交从一个本地分支移动branch1
到另一个本地分支的过程branch2
,但是git使用完全相同的过程来移动提交,前提是您拥有一个远程跟踪分支,当您进行远程处理时,该分支会获取一些新提交git fetch
(包括的fetch
第一步)git pull
)。您可能会先处理feature
上游的分支,origin/feature
然后自己进行几次提交:
...-o <-- origin/feature
\
A--B <-- HEAD=feature
但你决定你应该看到发生了什么事上游,让你跑git fetch
,2和,啊哈,有人上游写了一个承诺C
:
...-o--C <-- origin/feature
\
A--B <-- HEAD=feature
在这一点上,你可以简单地衍合feature
的A
,并B
到C
,赠送:
...-o--C <-- origin/feature
\
A'-B' <-- HEAD=feature
这些是原件A
和B
的副本,副本完成后将其丢弃(但请参阅脚注1)。
有时,没有什么可以改变的,即您自己没有做过的工作。也就是说,之前的图形fetch
看起来像这样:
...-o <-- origin/feature
`-- HEAD=feature
但是,如果您随后git fetch
又提交了,则在前进的同时C
,您的 feature
分支仍指向旧的提交origin/feature
:
...-o--C <-- origin/feature
`---- <-- HEAD=feature
这就是git merge --ff-only
来自于:如果你问合并当前分支feature
同origin/feature
,Git把它可能只是滑动箭头前进,因为它是,这样feature
直接点提交C
。不需要实际的合并。
如果你有自己的提交A
和B
,虽然,你问合并那些C
,混帐会做一个真正的合并,做一个新的合并提交M
:
...-o--C <-- origin/feature
\ `-_
A--B--M <-- feature
在这里,--ff-only
将停止并给您一个错误。底垫中,而另一方面,可以复制A
和B
以A'
和B'
,然后躲起来原来的A
和B
。
因此,总而言之(可以,为时已晚:-)),他们只是在做不同的事情。有时结果是相同的,有时则不同。如果它的确定以复制A
和B
,你可以使用git rebase
; 但是如果有很好的理由不复制它们,则可以根据需要使用git merge
,也许将其与--ff-only
合并或失败。
1 Git实际上将原件保留了一段时间(在这种情况下通常为一个月),但将它们隐藏了起来。找到它们的最简单方法是使用git的“ reflogs”,它保留每个分支指向的位置以及在HEAD
更新分支和/或的每次更改之前所指向的位置的历史记录HEAD
。
最终reflog历史记录条目到期,这时这些提交才有资格进行垃圾回收。
2或者再次使用git pull
,它是一个便捷脚本,其开始于运行git fetch
。提取完成后,便捷脚本将运行git merge
或git rebase
,具体取决于您如何配置和运行它。
git cherry-pick
用来解释的方式git rebase
。看起来很棒!
--no-ff
此处合并。
是,有一点不同。git merge --ff-only
如果无法快进,将中止操作,并接受一个提交(通常是一个分支)进行合并。只有无法快进(即永远不会使用--ff-only
),它才会创建一个合并提交。
git rebase
重写当前分支上的历史记录,或可用于将现有分支重新建立到现有分支上。在那种情况下,它不会创建合并提交,因为它是基于基础而不是合并。
git merge
也永远不会重写历史记录(带有或不带有--ff-only
)。--ff-only
防止除快速合并之外的任何合并(没有合并重写历史记录)。除快速合并外,其他合并通常只是简单地按顺序应用变更集(这是一种简化,但足以配合使用),并记录已合并变更集的元数据;不涉及历史重写。
是的,--ff-only
在平原git merge
将失败的地方总是失败,在平原git merge
将成功的地方可能失败。这就是要点-如果您试图保持线性历史记录,并且合并无法以这种方式完成,则您希望它失败。
向命令中添加失败案例的选项并不是没有用的。这是一种验证前提条件的方法,因此,如果系统的当前状态不是您期望的状态,则不会使问题变得更糟。