根据我的阅读,它们都有助于我们获得线性历史。
根据我的实验,rebase一直在运作。但是merge -ff-only仅适用于可以快速转发的场景。
我也注意到,git merge创建了一个合并提交,但如果我们使用--ff-only,它会给出一个基本上等于git rebasing的线性历史记录。所以--ff-only杀了git merge的目的,对吧?
那么它们之间的实际区别是什么?
答案 0 :(得分:76)
请注意,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-and-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
现在你有了这个:
...-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
的作用:它&#34;移动&#34;提交(虽然不是通过实际移动它们,因为它不能:在git中,提交永远不会被更改,所以即使只更改父ID也需要将其复制到新的略有不同的提交中。)
换句话说,虽然git cherry-pick
是一个提交的自动差异和重做,但git rebase
是重做多个的自动过程>提交,最后,将标签移动到&#34;忘记&#34;或隐藏原件。
上面说明了将提交从一个本地分支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
合并,那么git会进行真正的合并,从而进行新的合并提交{{ 1}}:
M
在这里,...-o--C <-- origin/feature
\ `-_
A--B--M <-- feature
会停止并给您一个错误。另一方面,Rebase可以将--ff-only
和A
复制到B
和A'
,然后隐藏原始B'
和A
。
所以,简而言之(好吧,为时已晚:-)),他们只是做了不同的事情。有时结果是一样的,有时候不是。如果可以复制B
和A
,则可以使用B
;但是如果不有一些很好的理由来复制它们,您可以使用git rebase
(可能与git merge
)合并或合并。
1 Git实际上保留原件一段时间 - 通常是一个月 - 在这种情况下 - 但它隐藏了它们。找到它们的最简单方法是使用git&#34; reflogs&#34;,它保存每个分支所指向的历史记录,以及--ff-only
指向的每个更新分支之前的位置和/或HEAD
。
最终,reflog历史记录条目到期,此时这些提交符合garbage collection的条件。
2 或者,您可以再次使用HEAD
,这是一个以运行git pull
开头的便捷脚本。完成提取后,便捷脚本将运行git fetch
或git merge
,具体取决于您配置和运行它的方式。
答案 1 :(得分:5)
是的,有区别。 git merge --ff-only
如果不能快进就会中止,并且提交(通常是一个分支)进行合并。如果它不能快进,它将只创建一个合并提交(即永远不会这样做{ {1}})。
--ff-only
重写当前分支上的历史记录,或者可用于将现有分支重新绑定到现有分支上。在那种情况下,它不会创建合并提交,因为它的变基,而不是合并。
答案 2 :(得分:3)
是的,--ff-only
将始终在普通git merge
失败的地方失败,并且可能在普通git merge
成功的地方失败。这就是重点 - 如果您尝试保留线性历史记录,并且合并无法以这种方式完成,那么您希望它失败。
将失败案例添加到命令的选项并不是无用的;它是验证前提条件的一种方式,因此,如果系统的当前状态不是您所期望的,那么您就不会使问题变得更糟。