Git rebase --preserve-merges失败

时间:2014-09-04 16:24:28

标签: git git-rebase

我有一个(大)提交树,其中包含几个我想要重新绑定到另一个提交的合并提交。执行正常的rebase会导致git要求我解决合并冲突。我不想审查每个合并,因为这将是很多工作。在找到--preserve-merges选项(详细解释为here)后,我认为我找到了完成此任务的完美工具。但是,我似乎无法让它正常工作。我创建了一个演示问题的玩具示例。

从空文件夹开始,我们首先创建一个带有合并的分支和另一个我们将在其上重新分支的分支。

A---B--
\      \
 ---C---D
 \
  ---E

master 指的是 B branch 指的是 D goodbye-branch 指的是 E

git init
echo Hello > Hello.txt
git add Hello.txt
git commit -m "Create Hello.txt (commit A)"
git tag start

echo World! >> Hello.txt
git commit -am "Change to Hello World (commit B)"

git checkout start
git checkout -b branch
echo Dave >> Hello.txt
git commit -am "Change to Hello Dave (commit C)"

git merge master
echo Hello World, Dave! > Hello.txt
git add Hello.txt
git commit -m "Merge branch master into branch (commit D)"

git checkout start
git checkout -b goodbye-branch
echo Goodbye > Goodbye.txt
git add Goodbye.txt
git commit -m "Add Goodbye.txt (commit E)"

到目前为止,一切都很顺利。合并冲突,但我们解决了。现在我们尝试将 branch 重新绑定到 E ,最终得到以下提交树:

A---E----B'
     \    \
      C'---D'

git checkout branch
git rebase -p goodbye-branch

这结束时出现以下错误:

Auto-merging Hello.txt
CONFLICT (content): Merge conflict in Hello.txt
Automatic merge failed; fix conflicts and then commit the result.
Error redoing merge f567809e2cc91244cc7fdac210e1771dc75e4d86

该文件包含以下内容:

Hello
<<<<<<< HEAD
Dave
=======
World!
>>>>>>> 0437403c97f33f229e41ec9584ce891a50052e48

我做错了什么?我希望git能够使用commit D 来解决它在重新定位时遇到的合并冲突。

我正在使用Git 1.9.4.msysgit.1,这是目前最新的版本。

1 个答案:

答案 0 :(得分:26)

TL; DR

--preserve-merges标志只是告诉git-rebase尝试重新创建合并提交而不是忽略它们。它不会让git rebase能够记住合并冲突是如何解决的,即它记录冲突解决方案以备将来使用。您想要使用的是rerere

在您的玩具示例中,rebase期间产生的冲突与您在前一次合并期间解决的冲突完全相同。如果您在合并之前激活了rerere ,那么您将不必在rebase期间再次解决该冲突。

如果您预计要合并,然后重新绑定分支,则应激活rerere,以便将来只需要解决一次给定的合并冲突,而不是多次。

详细说明

让我们打破你的玩具示例。

git init
echo Hello > Hello.txt
git add Hello.txt
git commit -m "Create Hello.txt (commit A)"
git tag start

echo World! >> Hello.txt
git commit -am "Change to Hello World (commit B)"

git checkout start
git checkout -b branch
echo Dave >> Hello.txt
git commit -am "Change to Hello Dave (commit C)"

到目前为止,这么好。在您的第一个git merge命令之前,您的仓库看起来像这样:

enter image description here

在提交A中,Hello.text包含

Hello

在提交B中,Hello.text包含

Hello
World!

在提交C中,Hello.text包含

Hello
Dave

现在,当您尝试通过运行

master合并到branch
git merge master

Git报告合并冲突,因为它无法自行确定合并后Hello.txt的内容是否应该

Hello
World!
Dave

Hello
Dave
World!

或其他......

您可以通过使用Hello.txt覆盖Hello World, Dave!的内容,暂存更改以及完成合并提交来解决该冲突。

echo "Hello World, Dave!" > Hello.txt
git add Hello.txt
git commit -m "Merge branch master into branch (commit D)"

您的回购现在看起来像这样:

enter image description here

然后你运行

git checkout start
git checkout -b goodbye-branch
echo Goodbye > Goodbye.txt
git add Goodbye.txt
git commit -m "Add Goodbye.txt (commit E)"

在那个阶段,您的仓库看起来如下:

enter image description here

现在你运行

git checkout branch
git rebase -p goodbye-branch

但遇到冲突。在解释为什么会出现这种冲突之前,让我们看看如果git-rebase操作成功(即无冲突),您的回购将会是什么样子:

enter image description here

现在让我们看看为什么你在Hello.txt遇到与第一次合并时相同的冲突; Goodbye.txt在任何方面都没有问题。实际上可以通过一系列更基本的操作(checkout s和cherry-pick s)来分解rebase;更多关于http://think-like-a-git.net/sections/rebase-from-the-ground-up.html的信息。 长话短说......在git rebase操作过程中,您的仓库将如下所示:

enter image description here

情况与您第一次合并之前的情况非常相似: 在提交B&#39;中,Hello.text包含

Hello
World!

在提交C&#39;中,Hello.text包含

Hello
Dave

然后Git尝试创建合并B&#39;和C&#39;,但出现合并冲突的原因与您遇到的第一次合并冲突完全相同:Git无法确定Dave行应该在World!之前还是之后线。因此,rebase操作会停止运行,Git会要求您在完成rebase之前解决该合并冲突。

您可以采取的措施:使用rerere

Git&#39; s rerere是你的朋友,在这里。

  

该名称代表&#34;重复使用记录的分辨率&#34;顾名思义,它允许你让Git记住你是如何解决一个大块冲突的,以便下次看到同样的冲突时,Git会自动为你解决它。

     

[...]如果你想要一个你合并的分支并修复了一堆冲突,然后决定改变它 - 你可能不会再次做同样的冲突。

如果已启用rerere

git config --global rerere.enabled true
合并之前

,然后Git会记录您在创建提交D时如何解决合并冲突,并且在后续rebase期间遇到相同的冲突时会应用相同的解决方案。冲突仍然会中断rebase操作,但它会自动解决。你所要做的只是git rebase --continue

但是,看起来rerere在合并之前尚未激活,这意味着Git必须没有记录您第一次如何解决冲突。在此阶段,您可以立即激活rerere并再次手动解决所有相同的冲突,或使用rerere-train.sh脚本(另请参阅此blog post)使用现有历史记录预先播种rerere缓存。