合并之间的 git rebase 导致完全不相关的文件中的冲突

ano*_*nol 4 git git-rebase

我有一个大型 Git 存储库,几个月前在其中引入了一个错误,我想bisect通过首先在存储库的过去引入提交,然后重放合并(执行rebase新提交),如图所示在下图中。

我知道由于合并,Git 似乎没有像预期的那样工作,但我想更好地了解为什么会发生这种情况(以及是否有参数可以帮助它更好地工作)。

  • 修改的文件patch永远不会被任何提交再次修改master

  • 我尝试rebase使用和不使用--preserve-merges,并且都在离提交不远的某个合并中失败patch

  • 冲突中显示的差异没有意义,例如文件中有一些空格,另一个文件中有一些不相关的行......我可以尝试从中提取任何有用的信息,或者它只是垃圾,我不应该花任何时间尝试明白吗?

  • 是否有不同的参数/合并策略可以帮助推进合并?

注意:最后,我的组合git apply; git bisect确实有效,但我仍然对有关 Git 行为的替代解决方案或解释感兴趣。

我的 git-rebase 问题总结

tor*_*rek 7

基本问题描述起来很简单,但解决起来却很难。

你在master. 那些提交图很好,但很难输入:-) 所以让我们在这里使用一个非常简化的 ASCII 格式。(这只是对问题的重述,经过简化并带有不同的标签。)

                D
              /   \
A - B ----- C       F - G   <-- master
              \   /
                E
Run Code Online (Sandbox Code Playgroud)

在这里,在提交时引入了一个错误B,因此出现在每个子提交中(C通过G)。放在一个修复,我们要创建提交的新序列(我会用小写字母而不是C'D'等等)与修复x安装为一个新的提交:

                d
              /   \
A - B - x - c       f - g   <-- [anonymous; will become master]
              \   /
                e
Run Code Online (Sandbox Code Playgroud)

为了做到这一点,git(git rebase -p在这种情况下——注意典型的变基会删除合并提交!)必须基于现有提交进行新的提交。对于像 new commitc这样的非合并,这并不太棘手:rebase 获取Cvs的差异,B然后将该补丁应用于附加到 commit 的源树x。也就是说, rebasegit cherry-pick master~3在新的匿名分支上执行。目前没问题!现在 rebase 需要创建 commit d,这也不是问题,它只需要 do git cherry-pick master~2,它确实做了,给出:

                d
              /
A - B - x - c
Run Code Online (Sandbox Code Playgroud)

制作e有点棘手,因为它的父提交应该是c. 原则上,rebase 会d挂起,倒带到c,然后git cherry-pick master~1^2复制E。(我认为 rebase 脚本实际上会执行自己的git commit-tree操作,因此它不必回滚匿名分支,但是效果是一样的,它只是在这里涉及一些内部毛毛。)这给了我们:

                d
              /
A - B - x - c
              \
                e
Run Code Online (Sandbox Code Playgroud)

这就是我们遇到问题的地方:rebase 如何创建合并提交f?它无法比拟的树提交F针对于D:这只是一个合并的一半。它无法FE任何一个进行比较:那只是合并的另一半。所以它不能使用,git cherry-pick因为这需要选择原始合并的一侧或另一侧。

rebase 使用的解决方案是运行git merge(或者更确切地说是运行的内部位git merge)。这样,由x它引入的任何更改都会继续通过新的de得到正确处理。

(这确实引入了一个完全不同的问题:如果F是一个“邪恶的合并”,即改变任何父级中未更改的事物的合并,则用于合并的复制树f将与用于 的树不匹配F,因为 git 永远不会看到“邪恶合并”的变化。需要的是一个变体,cherry-pick它可以将合并提交的树与其所有父项进行比较——作为各种组合的差异,除了比 git 的标准组合差异更完整——并将该差异应用于所有输入树. 但是没有这样的八足樱桃选择 [ git calamari-pick?],所以它不可用。)


无论如何,这种变基使用合并,在这里您会看到与原始合并期间可能发生的合并冲突相同的合并冲突。如果你只是用同样的方式再次解决它——git rerere例如使用——那将允许你继续。不幸的是,没有办法追溯打开git rerere(即使我认为编写这样的东西很容易)。