Git rebase --preserve-merges 失败

标签 git git-rebase

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

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

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)"

到此为止,一切正常。存在 merge 冲突,但我们解决了它。现在我们尝试将分支 rebase 到 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 来解决它在 rebase 时遇到的 merge 冲突。

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

最佳答案

TL; DR--preserve-merges标志只是告诉 git-rebase尝试重新创建 merge 提交而不是忽略它们。它不给 git rebase能够记住 merge 冲突是如何解决的,即它不记录冲突解决方案以备将来使用。你要使用的是 rerere .
在您的玩具示例中, rebase 期间出现的冲突与您在之前的 merge 期间解决的冲突完全相同。如果您已激活 rerere在 merge 之前,您不必在 rebase 期间再次解决该冲突。
如果你预计你会 merge ,然后重新建立一个分支,你应该激活 rerere这样,将来,您只需解决给定的 merge 冲突一次,而不是多次。
详细说明
让我们分解您的玩具示例。

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 之前命令,你的 repo 看起来像这样:
enter image description here
在提交 A 中,Hello.text包含
Hello
在提交 B 中,Hello.text包含
Hello
World!
在提交 C 中,Hello.text包含
Hello
Dave
现在,当您尝试 merge 时 master进入 branch通过运行
git merge master
Git 报告 merge 冲突,因为它无法自行确定 Hello.txt 的内容是否存在。 merge 后应该是
Hello
World!
Dave
或者
Hello
Dave
World!
或者是其他东西...
您可以通过覆盖 Hello.txt 的内容来解决该冲突。与 Hello World, Dave! ,暂存您的更改,并完成 merge 提交。
echo "Hello World, Dave!" > Hello.txt
git add Hello.txt
git commit -m "Merge branch master into branch (commit D)"
你的 repo 现在看起来像这样:
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)"
在那个阶段,您的 repo 如下所示:
enter image description here
现在你跑
git checkout branch
git rebase -p goodbye-branch
但经历冲突。在解释为什么会出现这种冲突之前,让我们先看看如果出现 git-rebase,你的 repo 会是什么样子。操作成功(即无冲突):
enter image description here
现在让我们看看为什么您会在 Hello.txt 中遇到同样的冲突。就像你第一次 merge 一样; Goodbye.txt在这里没有任何问题。 rebase 实际上可以分解为一系列更基本的操作(checkout s 和 cherry-pick s);更多详情请访问 http://think-like-a-git.net/sections/rebase-from-the-ground-up.html .
长话短说......在你的中间 git rebase操作,您的 repo 将如下所示:
enter image description here
这种情况与您第一次 merge 之前的情况非常相似:
在提交 B' 中,Hello.text包含
Hello
World!
在提交 C' 中,Hello.text包含
Hello
Dave
然后 Git 尝试创建 merge B' 和 C',但是由于与您遇到的第一次 merge 冲突完全相同的原因,出现了 merge 冲突:Git 无法确定 Dave 是否存在。行应该在 World! 之前或之后线。因此,rebase 操作会停止,并且 Git 会要求您在完成 rebase 之前解决该 merge 冲突。
你能做些什么:使用 rerereGit的 rerere 是你的 friend ,在这里。

The name stands for "reuse recorded resolution" and as the name implies, it allows you to ask Git to remember how you've resolved a hunk conflict so that the next time it sees the same conflict, Git can automatically resolve it for you.

[...] if you want to take a branch that you merged and fixed a bunch of conflicts and then decide to rebase it instead - you likely won't have to do all the same conflicts again.


rerere已启用,
git config --global rerere.enabled true
在 merge 之前,Git 会记录你在创建提交 D 时如何解决 merge 冲突,并且在随后的 rebase 中遇到相同的冲突时会应用相同的解决方案。冲突仍会中断 rebase 操作,但它会自动解决。您所要做的就是 git rebase --continue .
但是,它看起来像 rerere在 merge 之前尚未激活,这意味着 Git 必须没有记录您第一次如何解决冲突。在此阶段,您可以激活 rerere现在再次手动解决所有相同的冲突,或使用 rerere-train.sh 脚本(另见此 blog post )使用现有历史记录预置 rerere缓存。

关于Git rebase --preserve-merges 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25670519/

相关文章:

git - 无法在 github 上共享来自 intellij 的项目

带有差异的 Git 日志用于某个文件

git - 如何在没有 TreeView 的情况下查看 Azure Repos 中的分支?

github - 为 fork 的 github 项目的 fork 的 github 项目准备拉取请求

git-rebase - `Rebase and merge` 合并策略在 Bitbucket Server 上的作用是什么?

git - 为什么我的 --no-ff 提交在 git pull --rebase origin 之后折叠到 master 分支中?

git merge 和 rebase : Do they only affect the currently checked out branch?

git - Jenkins 和更新 git 子模块

git - 如何真正删除 git/SourceTree 上的标签

git - 编辑 git merge/rebase 中的所有冲突文件(特殊字符安全)