Git:合并冲突的合并提交的变基

8

在完成对一个主题分支的工作后,我按以下方式将主题分支合并到主干:

          o---*---o---o topic
         /             \
o---o---o---*---o---o---+ master

带有 '*' 标记的提交修改了相同的代码,因此合并导致了合并冲突,并在合并提交中得到解决(标记为 '+')。

当我合并和解决冲突时,我的同事推送了新的提交到主分支(标记为 'n'),导致以下的历史记录:

          o---*---o---o topic
         /             \
o---o---o---*---o---o---+ master
                    \
                     n---n origin/master

现在,推送我的本地主分支会导致错误,因为它不是快进推送,所以我有两个选择:
  1. 放弃我的工作,将主分支重置为 origin/master 并重新合并。我想避免这样做,因为我必须再次解决冲突。
  2. 将主分支变基到 origin/master 并推送。这里出现了我的问题:即使使用 -p 开关进行变基,也无法顺利进行,合并提交显示相同的冲突,即使新的提交('n')没有改变由主题分支触及的任何内容。因此,在变基过程中我必须再次解决冲突,并且与选项1相同的结果。
我想要实现的是在不必再次解决冲突的情况下对合并提交 '+' 进行变基:
          o---*---o---o-------- topic
         /                     \
o---o---o---*---o---o---n---n---+ master & origin/master

编辑:

启用了rerere开关,但似乎没有任何帮助。除了将config.rerere设置为true之外,我还需要做些什么来解决我的问题吗?

编辑2:

将合并提交('+')到origin/master也可以解决问题(如评论和答案中提出的),但会导致一种丑陋的历史记录,我想通过仅有一个合并提交来避免这种情况。


在将新的主分支合并到原始主分支之前,您也可以先将 origin/master 回退合并。 - ScayTrase
@ScayTrase 这是可能的,但会导致一种丑陋的历史记录;我更喜欢只有一个合并提交的清晰解决方案。 - rhabarbersaft
不幸的是,这个问题没有真正的解决方法:无论你选择哪个选项,你很可能都需要重新解决冲突。如果你预计将来会再次遇到同样的情况,你应该考虑激活rerere(https://dev59.com/pV8e5IYBdhLWcg3whqk4#25671230)。 - jub0bs
@Jubobs 忘了提到 config.rerere 已启用,但也没有帮助。我会将此添加到问题中。 - rhabarbersaft
@rhabarbersaft 你能详细说明一下为什么 rerere 没有起作用吗?你尝试过使用变基(选项2)并且激活了 rerere,但出现了相同的合并冲突吗? - jub0bs
@Jubobs 当合并(和解决冲突)时,我看到了rerere的“已记录解决方案...”消息,所以似乎可以工作。但是当变基时,相同的冲突出现,并且rerere不起作用(在调用git rerere remaining时显示所有有冲突的文件)。 - rhabarbersaft
4个回答

1
第一种方法是最好的。撤销您的合并,然后重新执行合并以将同事的更改合并到合并提交中。但您不需要丢弃这些更改。
由于您知道同事的更改没有影响到您解决冲突的文件。您可以从当前的主分支状态创建一个新分支(我们称之为conflict-fix)。重置您的分支并重新执行合并。
与其使用git mergetool或其他编辑器。您可以使用git checkout conflict-fix -- <file names>将文件从另一个分支带入主分支。git add文件并提交以完成合并。然后您可以删除conflict-fix分支。
这很容易做到,并将产生您正在寻找的单个合并提交,同时允许您推送您的更改。如果新的提交影响到您解决冲突的文件,则必须重新执行它们。

我对 git rerere 不是完全熟悉,但这应该可以解决问题。根据您的评论,您不需要重新进行变基操作。您仍然可以撤销合并提交,git fetch 更新并重新执行合并。您只需调用 git rerere 命令,它将为您解决文件中的冲突。您的树形结构看起来像这样:

          o---*---o---A topic
         /             \
o---o---o---*---o---o---+ master
                 \
                  n---n origin/master

你会做以下事情:

你将会执行以下操作:

git reset --hard A
git checkout master
git pull
git merge topic
git rerere
//Fix other conflicts
git push

你应该最终得到:

并且你应该以此结束:

          o---*---o---o-------- topic
         /                     \
o---o---o---*---o---o---n---n---+ master & origin/master

不需要重新定位任何东西。

http://git-scm.com/docs/git-rerere


刚试了一下,运行正常。但是:如果origin/master在同一文件中有更改,导致冲突(在“*”中进行修改),但在其他行上(不会导致额外的合并冲突),如果我只是检出冲突修复版本的冲突文件,则这些更改将被还原。因此,我仍在寻找一个解决方案,使Git自动重新应用我在第一次合并中做出的冲突解决方案(我认为rerere可以做到这一点)。 - rhabarbersaft
很不幸,在这种情况下rerere无法工作,我再次尝试了一遍以确保。即使在第一次合并时它会显示“已记录分辨率...”,但按照建议再次合并时却什么也没做。 - rhabarbersaft

0

rerere是避免这种麻烦的方法,但如果您在第一次合并之前没有启用它,则无济于事。您可以知道它是否已启用,因为它会显示有关“记录预映像”的消息。

最近我自己遇到了这个问题,因为我有一台新的开发机器,并忘记在一个丑陋的合并之前启用rerere。对于这种情况,没有一个伟大的git解决方案,但以下是我能想到的最简单的恢复此情况的方法。

  1. 确保您的工作目录正好是合并结果(git reset HEAD --hard),并将源代码树从合并提交中复制到某个安全位置
  2. 将本地分支重置回任何已合并的提交之前(git reset --hard master~或git reset HEAD~n,其中n是您需要回退多远的数字)
  3. git pull
  4. git merge topic
  5. 将源文件复制回您的工作树以覆盖现有文件。
  6. git mergetool(处理任何已删除与修改的冲突)
  7. 提交并推送

由于我们正在使用gerrit,我还确保Change-Id与我的先前合并提交相同,以便我可以验证没有出现任何问题。


rerere有所帮助,但不幸的是并不能完全解决问题,因为它只记录了冲突的解决方案,但可能还需要进行其他更改才能成功构建(例如,一些纯粹添加的代码可能会向已更改的方法传递错误的参数)。 - Miral

0
就我所读/理解的关于rerere的知识,如果您使用rerere-train.sh脚本记录已经提交的决策,它应该能够帮助您解决这个确切的情况。
所以下面的步骤应该可以解决问题:
  1. 运行脚本,使用参数<merge commit>^..<current branch> →现在rerere知道您对此合并的冲突决策
  2. 确保你的配置文件中有rerere.enabled=true
  3. 执行选项1(使用更新的主分支重新进行合并,详见问题)→现在rerere应该能够重用先前记录的决策
  4. 可选择取消设置rerere.enabled(可能有一些原因不能保持启用状态,请参阅启用git rerere存在什么缺点?)

如果您不想使用脚本,您也可以手动进行培训,参见https://dev59.com/nW855IYBdhLWcg3wxHYq#4155237


更新:

请记住,rerere 仅记录有冲突的块(其中包含 <<<<>>>> 的块)的冲突/解决方案。它将不会记录因为一个分支中的删除和另一个分支中的修改而导致的冲突的解决方法。此外,它也不会记录您在冲突块之外进行的任何其他更改。

你还有另一种选择,虽然并不是很流行,特别是如果 origin/master 已经发布给了一组未知的人:在本地 master 上重新设置 origin/master 并进行强制推送。

我过去选择过这个选项多次,不一定是从相同的情况而来,而是与需要重写 origin/master 历史记录的其他情况有关。如果您有一个小团队,这只是一个沟通/协调的问题。


0
如果您将主题变基到主分支之上,然后重置主分支到先前的提交并从远程拉取,那会怎样呢:
          o---*---o---o 
         /             \
o---o---o---*---o---o---+ topic
                    \
                     n---n master & origin/master

然后你将主题合并到主分支,结果是这样的(新的合并提交标记为'#'):

          o---*---o----o 
         /              \
o---o---o---*---o---o----+ topic
                    \     \
                     n--n--# master

这会引起冲突吗?对你有用吗?


我想要避免这个“丑陋”的历史,只有一个合并提交。 - rhabarbersaft

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