撤消推送git merge

时间:2017-09-26 10:35:15

标签: git bitbucket

我在这种情况下

        \ /
         C
         |     |
         B     F
         |     |
    [dev]A     D
          \   /
           \ /
            M [Master]

Master在提交D并且发生了这个错误:提交A的分支被错误地合并到master并被推送。此外,提交M不包含A,B,C,...中的所有更改,因为在提交之前已经手动修改了。所有这些提交都已被推送。

我需要使用与[dev]中相同的更改创建一个新分支,然后将其合并到master中,此刻我无法进行,因为这些提交已经合并。 此外,[dev]中的所有更改都来自不同分支的各种合并。

重要限制: 我的git服务器不允许我强制推入master分支,所以我无法重置master分支。

在合并错误[dev]之后,有没有办法将[master]中的更改加入[M]

编辑:我被允许强行推进不是主人的分支。

4 个答案:

答案 0 :(得分:1)

所以听起来问题是

1)合并不应该发生

2)合并的编辑方式意味着M不会应用dev以上的所有D更改

3)最后,您需要将dev(包括AC)合并到master

4)master

上不允许强制推送

我们留下了不太好的选择。

要解决(1)和(2),您只需要恢复M。由于(4)您无法重写master的历史记录,因此将master返回到匹配D的状态的所有方法都等同于git revert。这解决了(1)并使(2)没有实际意义。

但现在(3)是一个问题。大多数解决方案涉及强制推动dev;虽然您无法强制推送master,但您没有指明是否允许重写dev历史记录。 (如果没有,无论如何都要跟我说话,因为我会在这个基础上为这个案子建立一个解决方案......)

如果重写dev历史记录没问题,那么问题就在于您在替换分支中想要的历史记录的详细程度(记住原始历史记录在提交之前仍然会出现,无论您是否需要)你还原M)。

我能想到的最简单的选择(但是在新分支上具有最少历史保真度的选项)看起来像

git checkout D
git branch -f dev
git merge --squash C
git commit -m "Replacement dev branch"
git push -f

请注意,如果 希望手动编辑“squash merge”的结果,则可以在merge命令之后但commit命令之前执行此操作;但同样,我的理解是,之前完成的编辑是问题的一部分......

因此,这会创建一个以D为根的新提交,引入相对于D的相同更改作为C的合并,但没有真实的“第二父”关系合并 - 这意味着git将“忘记”这些更改与您已合并到master的更改相同。

     \ /
      C
      |     |
      B     F
      |     |
      A     D
       \   / \
        \ /   \
         M    CBA [Dev]
         |
         W [Master]

在此图表中,TREE的{​​{1}}(内容)与W匹配,而DCBA匹配的内容相匹配编辑错误。您可能已将M置于CBA的原始分支点,而不是dev(由D命令控制);唯一不同的是,现在或以后是否应该解决现有的冲突,因为历史将以某种方式“关闭”。

如果您对checkout的“新历史记录”看起来有更具体的要求,请告诉我们,我可以建议一个可能更接近满足它们的程序。

无论如何,因为这会在dev上重写历史记录,所以最好在没有人在dev上发布任何更改时进行更改(否则他们必须重新定位到新的dev科)。即便如此,每个人都必须强制更新其本地dev分支。请参阅dev文档中的“从上游rebase中恢复”,因为这基本上就是必须发生的事情。

这让我想到了另一种可能性:如果你不能(或者不想)重写git rebase历史记录,那么需要额外的步骤来确保dev始终在一种“正常”的方式(远程看到)。

dev

请注意,虽然我们移动git checkout D git branch -f dev git merge --squash C git commit -m "Replacement dev branch" git rebase master git push 分支,但我们稍后dev将其移回rebase的后代;所以不需要强制推动。如果这看起来令人不安,你可以为壁球合并创建一个临时分支,然后在origin/dev之后进行简单的ff合并以推进dev,但结果是相同的。

rebase

请注意 \ / C | | B F | | A D \ / \ \ / \ M CBA | W [Master] / / CBA' [Dev] 无法访问,最终会被垃圾回收;我没有特别的原因在这里展示。新CBA位于dev - 重写CBA'

同样,如果您对CBAW之间的历史记录有特定的要求,请告诉我们,我们可以尝试制定一个程序 - 但请再次记住,原始历史记录是无论如何,在CBA'之前仍然可见。

答案 1 :(得分:0)

合并后,当你签到你的分支并将下面提到的评论提供给你的putty时,你将最后一个正确的提交id放入#commit_id的地方。 警告 - 一旦您返回上一代码,就无法检索上一次合并代码。

git revert #commit id

答案 2 :(得分:0)

错误的合并提交总是难以修复,因为您无法(完全)还原合并以重新开始。 (您可以还原它带来的更改,但重新合并不会重新引入这些更改)。通常,最简单的解决方案是重写历史并重新开始,但我知道这是不可能的。

接下来最好的事情是创建一个修复提交,使状态达到您想要的状态。如果我正确理解你的问题,问题是合并没有“完全”完成(因为合并阶段的编辑),你实际上希望完全合并是在master中。

在那种情况下,我会:

# Undo the merge, locally
git checkout master
git reset --hard D  # the commitID from your graph, just before the merge

# Re-do the merge correctly, locally
git merge dev
N=$(git rev-parse HEAD); echo $N   # print as reference, call this commit N

# restore to upstream state
git reset --hard origin/master

现在您需要在主M'之上创建一个新提交,这会将M带到所需的状态N

我对这部分问题有一个解决方案,但我并不为此感到特别自豪。可能有更好的方法来做到这一点,但在这里:

# Figure out the tree-ID of the wanted state:
TREE_ID=$( git cat-file -p $N | grep '^tree' | sed 's/tree //' ); echo $TREE_ID

# Generate a commit with that tree ID. I.e. all files will be exactly as
# they are in commit N: everything you did in M to mangle the merge will be undone
COMMIT_ID=$( echo "Fixup merge" | git commit-tree $TREE_ID -p HEAD ); echo $COMMIT_ID
#                   ^^^^ this will be the commit message; change if needed

# pull the generated commit in to the master branch
git reset --hard $COMMIT_ID

答案 3 :(得分:0)

您可以还原合并,而不是还原合并的还原提交,而不是重置上次提交,并手动选择要应用的修补程序。

git revert -m 1 <commit id> 
git revert HEAD
git reset HEAD~1 

您可以使用GUI或使用以下命令编辑所有来自合并提交的未分级更改:

git add -i 

通过这种方式,您可以交互式地应用或重置补丁。或者只是手动编辑文件。