2017-09-26 151 views
0

我在这种情况下撤消推git的合并

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

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

我需要创建一个与[dev]中相同更改的新分支,并将其稍后合并到master中,此时我无法因为这些提交已被合并。 也都在[dev]的变化来自于从不同的分支合并不同。

重要的限制: 我的git服务器不允许我强行推到主分支,所以我不能重置主分支。

有没有办法将[dev]的更改置于[master]之后,合并不好[M]

编辑:允许我力推在未掌握分支机构。

回答

0

合并后,当您签入您的分支,给下面提到的评论为你腻子,在那里你最后一次正确的提交ID放入#commit_id地方。 警告 - 一旦您回到以前的代码中,您将无法检索到最后的合并代码。

git revert #commit id 
+0

这将撤销'M'中的更改,但它不会帮助我将'A,B,C'中的更改放入'master'中,因为历史记录表明它们已被合并。 –

+0

再次执行#commit-id是你最后一次提交的id对应的A,B,C分支commit-id的。 –

0

一只受了伤合并提交始终难以解决,因为你不能(完全)恢复合并重新开始。 (您可以恢复它所带来的更改,但重新合并不会重新引入这些更改)。通常,最简单的解决方案是重写历史并重新开始,但我知道这是不可能的。

下一个最好的事情是建立一个修正提交,带来了状态,以你想要的方式。如果我正确理解你的问题,问题是合并不是“完全”完成的(因为合并阶段的编辑),并且你实际上希望完全合并成为主。

在这种情况下,我会:

# 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 
+0

谢谢!我不想将[dev]合并到[master]中,但是我认为这是一个很好的折衷方案。 –

0

你可以恢复合并,比恢复合并的复归承诺,比去年重新提交,并手动选择哪些补丁适用。

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

比你将不得不从合并所有未分级的变化承诺可提供GUI或编辑:

git add -i 

有了这个,你可以通过交互应用或重置补丁。或者只需手动编辑文件。

1

所以这听起来像问题是

1)合并应该没有发生

2)合并在那意味着M不超过D

应用所有 dev变化的方式编辑

3)最终你将要合并dev(包括A通过C)为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,引入相对DC合并同样的变化,但没有真正合并的“第二父母”关系 - 这意味着git将“忘记”这些更改与您已合并到master中的更改相同。

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

在此图中,在W比赛DCBA比赛什么M会一直,如果它没有被错误地编辑TREE(内容)。您可能已在dev的原始分支点取代CBA而不是在D(由checkout命令控制)。鉴于历史将会以任何方式“断”,唯一的区别在于是否应该现在或以后解决现有的冲突。

如果您对dev的“新历史”应该是什么样子有更具体的要求,请告诉我们,我可以建议一个可能更接近满足它们的程序。

在任何情况下,因为这在改写历史dev,当没有人对dev unpushed变化是最好的做法(否则他们将不得不被复位至新dev分支)。即便如此,每个人都必须强制更新本地的dev分支。请参阅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后裔;所以不需要强制推送。如果这看起来令人不安,可以为压扁合并创建一个临时分支,然后在rebase之后做一个简单的ff合并以提前dev,但结果是一样的。

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

注意CBA不可达,并最终将垃圾收集;我在这里展示没有特别的原因。新的devCBA' - 重写CBA

同样,如果你对历史应该如何看WCBA'之间让我们知道,我们可以尝试制定出一个程序的具体要求 - 但是,请记住,历史原貌仍然可见M之前反正。