如何恢复已经推送到远程的合并提交?

单靠git revert <commit_hash>是不行的。显然,必须指定-m

1397455 次浏览

git revert -m中,-m选项指定父编号。这是必要的,因为合并提交有多个父级,Git不会自动知道哪个父级是主线,哪个父级是您要取消合并的分支。

当您在git log的输出中查看合并提交时,您将看到它的父级列在以Merge开头的行中:

commit 8f937c683929b08379097828c8a04350b9b8e183Merge: 8989ee0 7c6b236Author: Ben James <ben@example.com>Date:   Wed Aug 17 22:49:41 2011 +0100
Merge branch 'gh-pages'
Conflicts:README

在这种情况下,git revert 8f937c6 -m 1会让你得到8989ee0中的树,git revert -m 2会恢复7c6b236中的树。

为了更好地理解父ID,您可以运行:

git log 8989ee0

git log 7c6b236

Ben已经告诉你如何恢复合并提交,但它是非常重要,你意识到这样做

"…声明您永远不希望合并带来的树更改。因此,以后的合并只会带来由提交引入的树更改,这些提交不是先前恢复的合并的祖先。这可能是您想要的,也可能不是您想要的。"git合并手册页.

从手册页链接的文章/邮件列表消息详细说明了所涉及的机制和注意事项。只要确保您明白,如果您恢复合并提交,您就不能稍后再次合并分支并期望相同的更改回来。

下面是一个完整的例子:

git revert -m 1 <commit-hash>git push -u origin master

git revert ...提交您的更改。

  • -m 1表示您希望在合并之前恢复到第一个父级的树,如这个答案所述。

  • <commit-hash>是您要恢复的合并的提交哈希。

git push ...将您的更改推送到远程分支。

有时最有效的回滚方法是后退和替换。

git log

使用第二个提交哈希(完整哈希,您要恢复到的哈希,在列出的错误之前),然后从那里重新分支。

git checkout -b newbranch <HASH>

然后删除旧分支,复制新分支并从那里重新启动。

git branch -D oldbranchgit checkout -b oldbranch newbranch

如果它已被广播,则从所有存储库中删除旧分支,将重做分支推到最中央,然后将其拉回所有。

您可以按照以下步骤恢复不正确的提交或将远程分支重置回正确的HEAD/状态。

注意:仅为您自己的分支应用此解决方案,而不是共享的分支分支。

  1. 将远程分支签出到本地存储库。
    git checkout your_branch_name
  2. 从git日志复制提交哈希(即错误提交之前的提交ID)git log -n5

应该显示这样的东西:

提交7cd42475d6f95f5896b6f02e902efab0b70e8038"合并分支

your_branch_namef9a734f8f44b0b37ccea769b9a2fd774c0f0c012"这是一个错误的提交"提交3779ab50e72908da92d2cfcd72256d7a09f446ba"这是正确的提交

  1. 将分支重置为上一步复制的提交哈希
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)
  2. 运行git status以显示错误提交的所有更改。
  3. 只需运行git reset --hard即可恢复所有这些更改。
  4. 强制将您的本地分支推送到远程,并注意到您的提交历史记录与被污染之前一样干净。
    git push -f origin your_branch_name

我发现在两个已知端点之间创建一个反向补丁并应用该补丁会起作用。这假设您已经从主分支创建了快照(标签),甚至是主分支的备份,比如master_bk_01012017。

假设您合并到master中的代码分支是mycode分支。

  1. 结帐大师。
  2. 在master和备份之间创建一个完整的二进制反向补丁。git diff --binary master..master_bk_01012017 > ~/myrevert.patch
  3. 检查你的补丁git apply --check myrevert.patch
  4. 使用签名应用补丁git am --signoff < myrevert.patch
  5. 如果您需要在修复后再次引入此代码,您将需要分支恢复的master并检查修复分支git branch mycodebranch_fixgit checkout mycodebranch_fix
  6. 在这里您需要找到恢复的SHA键并恢复恢复git revert [SHA]
  7. 现在,您可以使用mycodebranch_fix来修复问题,提交并在完成后重新合并到master中。

正如Ryan提到的,git revert可能会使合并变得困难,所以git revert可能不是你想要的。我发现在这里使用git reset --hard <commit-hash-prior-to-merge>命令更有用。

完成硬重置部分后,您可以强制推送到远程分支,即git push -f <remote-name> <remote-branch-name>,其中<remote-name>通常命名为origin。从那时起,如果您愿意,您可以重新合并。

git revert -m 1 <merge-commit>

为了保持日志干净,因为什么都没发生(这种方法有一些缺点(由于推送-f)):

git checkout <branch>git reset --hard <commit-hash-before-merge>git push -f origin HEAD:<remote-branch>

“提交-哈希-合并前”来自合并后的日志(git log)。

正确标记的答案对我有用,但我不得不花一些时间来确定发生了什么…所以我决定为像我这样的情况添加一个简单直接的步骤的答案…

假设我们有分支A和分支B…你将分支A合并到分支B并将分支B推送到自己,所以现在合并是它的一部分…但是你想回到最后一次提交之前合并…你会怎么做?

  1. 转到您的git根文件夹(通常是项目文件夹)并使用git log
  2. 您将看到最近提交的历史记录-提交具有提交/作者/日期属性,而合并也具有合并属性-因此您会看到它们如下所示:

    提交://合并:<家长HashA><家长HashB>作者:<作者>日期:

  3. 使用git log <parentHashA>git log <parentHashB>-您将看到这些父分支的提交历史-列表中的第一个提交是最新的提交

  4. 取你想要的提交的<commitHash>,转到你的git根文件夹并使用git checkout -b <newBranchName> <commitHash>-这将从你在合并前选择的最后一次提交开始创建一个新分支。

git doc about git恢复-m提供一个链接确切地解释这个:https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt

所有的答案已经涵盖了大部分的事情,但我会加上我的5美分。简而言之,恢复合并提交非常简单:

git revert -m 1 <commit-hash>

如果您有权限,您可以将其直接推送到“主”分支,否则只需将其推送到您的“恢复”分支并创建拉取请求。

您可能会在此处找到有关此主题的更多有用信息:https://itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.html

如果你想恢复merge提交,这是你必须做的。

  1. 首先,检查git log以找到合并提交的id。您还会找到与合并关联的多个父id(见下图)。

在此处输入图片描述

记下黄色显示的合并提交ID。父ID是下一行中写为Merge: parent1 parent2的ID。现在…

短篇故事:

  1. 切换到进行合并的分支。然后执行git revert <merge commit id> -m 1,这将打开vi控制台以输入提交消息。写、保存、退出、完成!

长话短说:

  1. 切换到进行合并的分支。在我的情况下,它是test分支,我正在尝试从中删除feature/analytics-v3分支。

  2. git revert是恢复任何提交的命令。但是在恢复merge提交时有一个令人讨厌的技巧。您需要输入-m标志,否则它将失败。从这里开始,您需要决定是否要恢复您的分支并使其看起来与parent1parent2完全相同:

git revert <merge commit id> -m 1(恢复到parent2

git revert <merge commit id> -m 2(恢复到parent1

你可以git log这些父母来弄清楚你想走哪条路,这就是所有混乱的根源。

我在已合并到GitHub存储库的master分支的PR上也遇到了这个问题。

由于我只想修改一些修改后的文件,而不是PR带来的全部更改,因此我必须用git commit --amamendmerge commit

步骤:

  1. 转到要更改/恢复某些修改文件的分支
  2. 根据修改后的文件做你想要的修改
  3. 运行git add *git add <file>
  4. 运行git commit --am并验证
  5. 运行git push -f

为什么它很有趣:

  • 保持PR作者的承诺不变
  • 它不会破坏那棵树
  • 您将被标记为提交者(合并提交作者将保持不变)
  • Git就像你解决了冲突一样,它会删除/更改修改文件中的代码,就像你手动告诉GitHub不要按原样合并它一样

我从这个链接中找到了如何恢复合并的好解释,我复制粘贴了下面的解释,如果下面的链接不起作用,这将是有帮助的。

如何恢复错误的合并阿兰(alan@clueserver.org)说:

我有一个主分支。我们有一个分支,一些开发人员正在做工作。他们声称它已经准备好了。我们合并它进入主分支。它破坏了一些东西,所以我们恢复合并。他们对代码进行了修改他们把它带到了他们说没事,我们再合并。检查时,我们发现还原之前所做的代码更改是不在master分支中,但在master分支中的代码更改分支。并寻求帮助从这种情况下恢复。

紧随“合并的恢复”之后的历史将如下所示:

---o---o---o---M---x---x---W/---A---B

其中A和B处于不太好的侧面开发,M是将这些过早的更改带入主线的合并,x是与侧分支在主线上所做的和已经做的更改无关的更改,W是“合并M的恢复”(W看起来不是颠倒了吗?)。IOW,“diff W^… W”类似于“diff-R M^… M”。

这种合并的“恢复”可以通过以下方式进行:

git1 M侧分支的开发者修复错误后,历史可能是这样的:

---o---o---o---M---x---x---W---x/---A---B-------------------C---D

其中C和D将修复A和B中损坏的内容,并且您可能已经在W之后的主线上进行了一些其他更改。

如果您合并更新的侧分支(在其尖端有D),结果中不会有A或B中的任何更改,因为它们已被W恢复。

Linus解释了这种情况:

恢复常规提交只会有效地撤消该提交的内容做了,并且相当简单。但是恢复合并提交也撤消提交更改的数据,但它绝对与合并对历史的影响无关。所以合并仍然存在,它仍然会被视为加入这两个分支在一起,未来的合并将看到合并为最后一个共享状态-以及恢复合并带来的恢复这一点都不会影响。因此,“恢复”会撤消数据更改,但它非常没有和“撤消”是指它不会撤消提交的效果存储库历史。所以如果你认为“恢复”是“撤消”,那么你将永远-遗漏了恢复的这一部分-是的,它撤消了数据,但不,它没有撤销历史。在这种情况下,您可能希望首先恢复上一个恢复,这将使历史如下所示:

---o---o---o---M---x---x---W---x---Y/---A---B-------------------C---D

其中Y是W的回复。这样的“回复的回复”可以用:

$git恢复W这段历史(忽略W和W… Y改变的东西之间可能的冲突)相当于历史中根本没有W或Y:

---o---o---o---M---x---x-------x----/---A---B-------------------C---D

并且再次合并侧分支不会因先前恢复和恢复而产生冲突。

---o---o---o---M---x---x-------x-------*/                       /---A---B-------------------C---D

当然,C和D中所做的更改仍然可能与任何x所做的冲突,但这只是正常的合并冲突。

这是一个非常古老的线程,但我缺少另一个在我看来方便的解决方案:

我从来没有恢复过合并。我只是从修订版中创建了另一个分支,在那里一切都好,然后从中间添加的旧分支中挑选需要挑选的所有东西。

所以,如果GIT历史是这样的:

  • d
  • c
  • b<<<合并
  • 一个

我从a创建了一个新分支,樱桃选择c和d,然后新分支从b中清除。我可以决定再次在我的新分支中合并“b”。旧分支被弃用,如果“b”不再需要或仍在另一个(功能/热修复补丁)分支中,则将被删除。

现在唯一的问题是计算机科学中最难的事情之一:你如何命名这个新分支?

好的,如果你失败了,特别是在devel中,你创建了上面提到的newdevel,删除旧的devel并将newdevel重命名为devel。任务完成。您现在可以在需要时再次合并更改。就像以前从未合并过一样……

-m1是正在修复的当前分支的最后一个父级,-m 2是合并到其中的分支的原始父级。

如果命令行令人困惑,Tortoise Git也可以在这里提供帮助。

如果您想恢复刚才推送的更改,请提供一个非常简单的答案:

commit 446sjb1uznnmaownlaybiosqwbs278q87Merge: 123jshc 90asaf

git revert -m 2 446sjb1uznnmaownlaybiosqwbs278q87 //does the work

当您在git log的输出中查看合并提交时,您将看到它的父级列在以Merge开头的行中:

commit 8f937c683929b08379097828c8a04350b9b8e183Merge: 8989ee0 7c6b236Author: Ben James <ben@example.com>Date:   Wed Aug 17 22:49:41 2011 +0100
Merge branch 'gh-pages'
Conflicts:README

在这种情况下,git revert 8f937c6 -m 1会让你得到8989ee0中的树,git revert -m 2会恢复7c6b236中的树。

为了更好地理解父ID,您可以运行:

git log 8989ee0

git log 7c6b236

找个备用分支

git checkout -b mybackup-brach
git reset --hard 8989ee0git push origin -u mybackup-branch

所以现在你有合并前的变化,如果一切正常,签出到前一个分支,并用备份分支重置

git reset --hard origin/mybakcup-branhc