在主分支上进行git rebase会引起冲突,但使用“普通”合并到主分支不会引起冲突。

3
当我将我的分支my-featuremaster 进行变基时,会出现冲突:
[my-feature]$ git rebase master
Applying: ...
Using index info to reconstruct a base tree...
M   vars/file.txt
Falling back to patching base and 3-way merge...
Auto-merging vars/file.txt
CONFLICT (content): Merge conflict in vars/file.txt
error: Failed to merge in the changes.

如果我将my-feature直接合并到master分支,那么它就能正常工作:
[master] $ git merge my-feature
Merge made by the 'recursive' strategy.
..

我尝试进行新的克隆以确保与远程同步并处于干净状态。
为什么明明普通合并没有问题,变基却失败了呢?
我已经使用 Git 几年了,之前从未遇到过这种情况,并且一直认为如果合并成功(没有冲突),则变基也会成功,反之亦然。

Rebase 使用三方合并。我想知道这是否有所不同? - evolutionxbox
2
Rebase 是一种 cherry-pick 操作。由于你正在将一个提交从上下文中分离出来,所以使用 rebase / cherry-pick 更容易发生冲突,因此更有可能打断历史记录。请参考 https://stackoverflow.com/a/60848968/341994 获取一个好的例子。 - matt
请注意,您的想法是错误的。冲突并不意味着失败!它只是一个冲突 - Git 不知道您希望如何自动处理,因此要求您帮助它。这是好事,而不是坏事。 - matt
冲突是合并失败的表现 - 在你主动采取行动之前,没有任何东西会进入主分支,如果我可以通过简单地进行合并来避免额外的工作,那么我认为这是好的。这不是说通过变基+解决冲突可以获得更好/更正确的结果。我可能会得到一个更漂亮的历史记录(对某些人来说),但在功能上没有任何区别。 - u123
@matt 我同意你的解释,但我没有觉得OP认为“失败”是一件坏事。我认为这个问题源于“我以为结果应该是相同的,但似乎并不总是这样。” 这个问题旨在确定rebase和merge的过程和结果不同的情况。 - TTT
显示剩余2条评论
2个回答

4

这种情况有多个原因。通常发生在中间进行变更时创建了冲突(当rebase时),但后续版本撤回了该变更。合并操作不必处理冲突的变更,因为它只关心要合并的分支尖端(和最近的公共祖先大约在哪里)。

这段话是一次编辑的结果,我不完全同意,但无奈:-): 另一个原因是,如果你的my-feature分支包含新的合并提交,则如果这些合并提交需要解决冲突,则可能需要再次解决。


那只是许多可能原因之一。 - Mark Adelsberger
可能还有其他原因,比如文件被重命名和频繁更改(因此Git在合并过程中无法识别重命名),但这种情况比我说的要少得多。但好吧,还有其他原因。 - eftshift0
不,不仅如此。有些情况下合并结果甚至与变基结果不同。你可以说它们是边缘情况,但不应该被忽视。 - Mark Adelsberger
嗯,有趣的是,“普通”合并比变基更“强大/健壮”?似乎“如果变基失败,请尝试进行普通合并”可能是一个有效的说法? - u123
@eftshift0 请确保您同意我的更改,我已经将Mark的评论合并到了答案中,关于合并结果有时与rebase不同。我相信这只会发生在您重写的分支中存在合并提交的情况下。 - TTT
@u123:我会反过来说:rebase使用更多的能量,因此可能会造成更多的混乱。:-) (例如,比较使用手动电锯和台式电锯。如果你不小心,哪一个会把你的手指割得干净利落,哪一个会很痛但是你会及时停下来避免需要进行手术重新连接?) - torek

2
“如何才可能出现rebase失败而普通合并不会失败?”
这并不是“失败”,而是出现了“冲突”。二者之间有很大的区别。“冲突”其实是一种成功,但需要您做出判断。这就是所有的区别。
至于rebase过程中为什么会出现冲突,而合并过程中却没有,这是因为分支的相对历史以及它们被应用的方式是不同的。如果它们是相同的,那么rebase过程将是一次合并!
例如,以下是可能的情况:
A -- B -- C -- D (master)
     \
      X -- Y (feature)

如果您将feature合并到master,那么起点是B,因为这是历史分叉的地方。Git会获取整个历史B-C-D和整个历史B-X-Y,并在B上执行它们两个。然后,它将结果的总体表达为一个新提交——合并提交,并使D和Y成为该提交的父级。
如果您将feature变基到master,则起点仍然是B,但原因不同:这是feature中第一个不共通的提交(X)的父级。Git完全忽略历史B-C-D;相反,它会直接对D进行任何操作,以获得从B到X的结果,并将其制作成提交——然后对结果提交进行任何从X到Y的操作,并将其制作成那个提交。然后,它将名称feature移动到指向最后一个新提交。
嗯,"直接对D进行任何操作"可能并不容易。这就是为什么变基可能会导致冲突而真正的合并却不会。
换句话说,这里的变基意味着假装X是从D发展出来的。但是X并没有从D发展出来;它是从B发展出来的。在C和D中可能发生了一些事情,使X很难从D而不是B发展出来。
(换句话说:合并总结历史。变基重写历史。)

我认为你在问题中对“失败”这个词过于看重了。那可能只是问题中不恰当的用词(标题的措辞更好)。不过,我喜欢你最后两段的解释,非常好。 - TTT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接