【问题标题】:Git rebase --preserve-merges failsGit rebase --preserve-merges 失败
【发布时间】:2014-10-29 12:00:55
【问题描述】:

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

从一个空文件夹开始,我们首先创建一个包含合并的分支和另一个我们将基于它的分支。

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

其中masterBbranchDgoodbye-branch em> 指的是 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,这是目前最新的版本。

【问题讨论】:

  • 你帮助了很多 Jubobs,我赞成你的回答。但严格来说,这个问题仍然没有答案。这个问题的解决方案是 rerere-teach.sh 脚本的 windows 版本。

标签: git git-rebase


【解决方案1】:

TL;博士

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

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

如果您预计您将合并,然后变基一个分支,您应该激活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 命令之前,您的 repo 如下所示:

在提交 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 World, Dave! 覆盖Hello.txt 的内容、暂存更改并完成合并提交来解决该冲突。

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

在那个阶段,您的 repo 如下所示:

现在你运行

git checkout branch
git rebase -p goodbye-branch

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

现在让我们看看为什么在Hello.txt 中遇到与第一次合并时相同的冲突; Goodbye.txt 在这里没有任何问题。 rebase 实际上可以分解为一系列更基本的操作(checkouts 和 cherry-picks);更多信息请访问http://think-like-a-git.net/sections/rebase-from-the-ground-up.html。 长话短说...在您的git rebase 操作中间,您的回购将如下所示:

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

Hello
World!

在提交 C' 中,Hello.text 包含

Hello
Dave

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

你可以做些什么:使用rerere

Git 的 rerere 是你的朋友,在这里。

这个名字代表“重用记录的解决方案”,顾名思义,它允许你让 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 缓存.

【讨论】:

  • 我了解这是进行合并时的情况。我不明白为什么在变基时会再次发生这种情况。毕竟,git 已经知道如何解决冲突,因为合并提交也正在重新定位...假设这只是重新定位的工作方式,有没有其他方法可以避免在移动时重新解决所有冲突我的承诺?
  • 根据您问题中提供的信息,我只能告诉您,冲突是在合并期间出现的(不是变基),并且 Git 不允许您在您之前继续进行解决它。也就是说,在rebasecherry-pickrevert 操作等期间可能会出现冲突。也许您应该编辑您的问题以添加一个玩具示例,其中冲突仅由rebase 引起,我会尽我所能解释那次冲突的原因。
  • rerere 是我知道的唯一解决方案,但我同意@DieterDP 的观点,即使没有激活 rerere,git 也能够在 rebase 期间解决冲突,因为当 git 出现冲突时是摘樱桃 D 产生 D'。如果您检查提交 D,它实际上包含冲突解决方案,并且对冲突应用解决方案不会“重新冲突”,即,由于 rebase 导致冲突甚至没有细微差别,它可以应用相同的解决方案。
  • 我认为@AndréSassi 的意思是 git 可以,如果它更聪明,“向前看”看看在它当前是樱桃的那个之后有一个提交 -选择,因此与该提交关联的树定义(在某种意义上)正确的冲突解决方案。不过,Git 并不那么聪明。
  • 这是一个非常清晰和有趣的解释,带有图形和所有内容。就这一点而言,它可能是一篇很好的博文
猜你喜欢
  • 2014-02-17
  • 2018-03-13
  • 2021-09-13
  • 1970-01-01
  • 2015-05-12
  • 2016-04-14
  • 1970-01-01
  • 2010-11-05
  • 1970-01-01
相关资源
最近更新 更多