恢复失败的合并后重新合并

Remerge after reverting failed merge

我在存储库中有两个分支:featuremaster。我已将 master 合并到 feature 并将结果推送到远程 feature branch:

git merge master

因为它包含了对外接口的必要改动。比我发现合并冲突被错误地解决了,我已经恢复了这个合并:

git revert -n -m 1 78e7ebfa8237

所以我回到 未合并的功能。但是根据历史合并已经发生并且功能分支已经包含必要的更改。我可以再次尝试合并分支(更仔细地检查冲突并且在检查构建之前不提交)吗?

如果您执行 git 还原,它将撤消合并,因此我认为该功能不包含必要的更改。我将只与一些额外的 git 日志进入合并前的状态。所以你可以自由地合并它。 但是,如果您不想要额外的 git 日志,您可以使用 git 重置。

  1. 检查您想要的先前状态:git log

  2. git reset {commit-id-founded}

  3. 解决您的冲突

  4. 再次合并

如果您没有发布错误的合并及其恢复,您可以删除它们并发布正确的合并。

如果您已经发布(推送或以其他方式给出)错误的合并,您最好的选择可能是通过从之前开始创建一个新分支来计算出正确的合并糟糕的合并。例如,假设提交图片段如下所示:

...--i--j--m--w   <-- feature
          /
...---k--l        <-- master

其中merge commit m是出错的那个,wm颠倒1)是回退m。 (注意:如果您有更复杂的历史记录,您可能应该使用不同的策略;请参阅脚注中的 link。)

在这里,想法是直接检查提交 j

git checkout <sha1-of-j>

您现在处于 "detached HEAD" 模式。此时你可以运行一个新的git merge:

git merge master

这将(根据您提到的合并冲突)因合并冲突而停止,因为它重复了使您无法合并的步骤-m。 (如果它不会自行停止,请将 --no-commit 添加到合并命令中。)

这次正确解决冲突 :-) 并根据需要 addcommit。这会创建一个新的合并,我将其称为 M,我将像这样绘制新图形:

...--i--j------m--w   <-- feature
         \    /
          M  /        <-- HEAD
          | /
         / /
         |/
...---k--l            <-- master

这个新提交 M 不(还)在任何分支上,事实上,你并不真的需要它在任何分支上:你想要的是 此时你获得了

现在我们将创建一个新的(但临时的)分支以记住提交 M:

的 SHA-1
git checkout -b temp

(我们本可以更早地完成此操作;如果您愿意,可以在 "check out commit j" 步骤中执行此操作;但我想到了其他一些未经测试的方法,我将在下面概述)。现在让我们回到 feature 并使用 M 的树而不是 mw 的树进行新提交。有几种方法可以做到这一点,但我将说明这一点,因为它非常简单:

git checkout feature
git rm -r .  # assumes you're in the top level of the work dir
git checkout temp -- .

其中第一个 checkout feature,只是让我们回到分支 feature。第二个清空索引("next commit")——仅当 M 缺少 mw 中的某些文件时才需要此步骤——然后第三个将整个树从提交 M 提取到索引和工作树中。

现在我们准备好提交结果了:

git commit -m "replace everything with corrected merge"

图表现在看起来像这样:

...--i--j------m--w--n   <-- HEAD=feature
         \    /
          M  /           <-- temp
          | /
         / /
         |/
...---k--l               <-- master

提交 n 下的文件与提交 M 下的文件相同。我们根本不再需要提交 M 和分支 temp,所以我们可以简单地删除它们 (git branch -D temp),给出:

...--i--j--m--w--n   <-- HEAD=feature
          /
...---k--l           <-- master

如果您习惯于使用较低级别的 git 命令,有一种更简单(?)的方法可以将树从 M 复制到我们将在 [=36] 上进行的新提交=].特别是我们只需要创建一个新的提交,其父节点是 w 并且其树是 M 的树。我们可以在 M 和匿名 HEAD 上一步完成,使用 git commit-tree:

id=$(git commit-tree -p feature -m "message" $(git rev-parse HEAD^{tree}))

假设这行得通(我还没有测试过这种特殊形式,您可能必须使用 git rev-parse 将名称 feature 转换为原始 SHA-1),然后我们可以使用 git update-ref 使 refs/heads/feature 包含 id $id:

git update-ref -m "add corrected merge" refs/heads/feature $id

之后可以安全地 git checkout feature 返回(更新的)分支。

这是 git,还有更多方法可以做到这一点,例如,在匿名分支上,您可以这样做:

git symbolic-ref HEAD refs/heads/feature
git commit -m "replace everything with corrected merge"

这可能比git commit-tree方法简单(commit-tree方法正是我首先想到的,因为最近写了一个复杂的shell脚本,使用commit-tree 一个花哨的回购阴影的东西)。它的工作方式是 symbolic-ref 让你回到分支 feature 但根本不触及索引(也不是工作树),所以 it/they 仍然匹配提交树 M。然后我们使用当前索引以普通方式进行新提交;由于没有任何东西指向提交 M,垃圾收集器最终将删除该提交(但不是树本身,它现在安全地保存在分支 feature 上)。


1mw 的东西是 stolen directly from Linus Torvalds and Junio Hamano.