我在这种情况下
\ /
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]
?
答案 0 :(得分:1)
所以听起来问题是
1)合并不应该发生
2)合并的编辑方式意味着M
不会应用dev
以上的所有D
更改
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
为根的新提交,引入相对于D
的相同更改作为C
的合并,但没有真实的“第二父”关系合并 - 这意味着git将“忘记”这些更改与您已合并到master
的更改相同。
\ /
C
| |
B F
| |
A D
\ / \
\ / \
M CBA [Dev]
|
W [Master]
在此图表中,TREE
的{{1}}(内容)与W
匹配,而D
与CBA
匹配的内容相匹配编辑错误。您可能已将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'
。
同样,如果您对CBA
和W
之间的历史记录有特定的要求,请告诉我们,我们可以尝试制定一个程序 - 但请再次记住,原始历史记录是无论如何,在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
通过这种方式,您可以交互式地应用或重置补丁。或者只是手动编辑文件。