如何还原已经推送到远程分支的合并提交?


959

git revert <commit_hash>一个人是行不通的。-m必须指定,对此我感到很困惑。

有人经历过吗?


3
看看这个问题的答案:stackoverflow.com/questions/2318777/…–
eugen

2
相关:撤消Git合并?

此处的链接是说明还原合并的提交的最佳示例:christianengvall.se/undo-pushed-merge-git
SK Venkat

这是一个设计gitgit-flow每个人都使用的-ish工作流程不匹配的示例。如果您已develop签出,则当然要还原引入错误的2提交功能分支,而不是恢复多年的共享dev分支。觉得需要选择它很荒谬-m 1
pkamb

2
我之前从未想到过的另一个建议-如果分支的提交列表中的一个很小,则恢复单个提交而不是整个提交分支可能会让您感到更舒服。
Sridhar Sarnobat

Answers:


1152

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

当您在的输出中查看合并提交时git log,您会看到其父行以以下行列出Merge

commit 8f937c683929b08379097828c8a04350b9b8e183
Merge: 8989ee0 7c6b236
Author: 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

126
从两个数字8989ee07c6b236其中一个去。我将如何理解?
奥雅纳(Arup Rakshit)2014年

12
还原后,我认为没有人能够轻松地纠正源分支中的代码并再次合并?kernel.org/pub/software/scm/git/docs/howto/...
IsmailS

10
在四处搜寻以寻求更好的解释时,我发现了这篇文章,我认为这篇文章在详细介绍方面做得很好。阅读后,我发现我真正要寻找的是RESET命令,然后强行按下。也许会帮助别人。atlassian.com/git/tutorials/…–
Funktr0n

46
@ArupRakshit如果您运行git log 8989ee0git log 7c6b236,则应该知道答案。
宝马

4
git log-合并以查看所有合并,git log --no-合并以查看没有合并的历史记录。合并分支将合并的分支历史引入目标,并使其
难以使用

368

这是一个完整的示例,希望对您有所帮助:

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

<commit-hash>您想还原的合并的提交哈希值在哪里,并且如该答案的说明所述,-m 1表明您想还原到合并之前的第一个父级的树。

git revert ...行实质上是提交更改的内容,而第二行是通过将更改推送到远程分支来公开更改的。


21
我相信 git revert命令已经提交了创建的提交对象。为此,您必须输入--no-commit标志
Delfic '18

2
正如@Delfic所提到的,提交已经由第一行管理了(我需要一个:wq来验证它),因此第二行不是必需的。
eka808 '19

1
这令人困惑。只有2行,没有git commit ..有人可以编辑。
杰伊

176

Ben告诉过您如何还原合并提交,但您务必要意识到这样做非常重要

“ ... 声明您将永远不希望合并带来的树更改。因此,以后的合并将仅带来不是先前还原的合并的祖先的提交所引起的树更改。这可能是,也可能不是您想要什么。(git-merge手册页)

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


80
但是,如果确实需要,您可以还原还原以使其恢复原状。
达洛尔

5
谢谢。知道一个非常有用的情况是,撤消合并的用例(例如,由于一个错误),并且在错误被修复后重新合并整个分支是一种常见的情况。
组件15年

3
如果您像我一样,后来又想要合并,则可以还原还原,也可以选择您还原的变更。
UnitasBrooks '17

在我的情况下,我遇到了必须“还原还原”才能获得变更后问题的问题。采摘樱桃可能是更整洁的方式?我下次再试试...
Steven Anderson

79

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

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

    输出:

    提交7cd42475d6f95f5896b6f02e902efab0b70e8038“将分支'wrong-commit'合并到'开发'”中
    提交f9a734f8f44b0b37ccea769b9a2fd774c0f0c012“这是错误的提交”
    commit 3779ab50e72908da92d2cfcd72446d

  3. 将分支重置为上一步中复制的提交哈希
    git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)

  4. 运行,git status以显示属于错误提交的所有更改。
  5. 只需运行git reset --hard即可还原所有这些更改。
  6. 强制将您的本地分支推到远程,并注意到您的提交历史记录很干净,就像它被污染之前一样。
    git push -f origin development

4
如果与此同时20名开发人员撤消了最新的开发合并怎么办?
Ewoks

2
当团队中有20个开发人员使用该分支时,我不会强行推动该分支。:)在这种情况下,明智的做法是执行还原提交。
ssasi

4
当您独自工作或确定没有其他开发人员撤出您搞砸的提交时,这是一个很好的解决方案
Kyle B


30

为了保持日志干净无事(这种方法有一些缺点(由于推-f)):

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

“ commit-hash-before-merge”来自合并后的日志(git日志)。


提示:如果您在公司中这样做,则可能没有权限。
eneski

3
永远做push -f一个共享的回购
巴蒂斯特米勒-马蒂亚斯

17

有时,最有效的回滚方法是退回并替换。

git log

使用第二次提交哈希(完整哈希,要在列出错误之前将其还原回的哈希),然后从那里重新分支。

git checkout -b newbranch <HASH>

然后删除旧分支,将新分支复制到其位置,然后从那里重新启动。

git branch -D oldbranch
git checkout -b oldbranch newbranch

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


4
关于广播的警告实际上应该更明确地表明这是多么可怕的想法。这将破坏该分支的每个人的版本,并且仅在使用只有您有权访问的远程存储库(github / bitbucket)时才真正有用。
RobbyD

并不比将修改后的配置文件下推到生产环境还差。它不会损坏,只是从较早的提交中重新分支,因此它是将分支指针移至较早版本的一种循环方式。希望它只影响本地存储库
ppostma1'1

5

如果要还原merge提交,这是您必须要做的。

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

在此处输入图片说明

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

短篇故事:

  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登录这些父母,以确定您想走哪条路,这是所有困惑的根源。


5

所有的答案已经涵盖了大部分内容,但是我会加5美分。简而言之,还原合并提交非常简单:

git revert -m 1 <commit-hash>

如果您有权限,则可以将其直接推送到“ master”分支,否则只需将其推送到“ revert”分支并创建提取请求。

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


1

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

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

  1. 结帐大师。
  2. 在主服务器和备份服务器之间创建完整的二进制反向修补程序。 git diff --binary master..master_bk_01012017 > ~/myrevert.patch
  3. 检查你的补丁 git apply --check myrevert.patch
  4. 申请补丁并注销 git am --signoff < myrevert.patch
  5. 如果在修复后需要再次引入此代码,则需要从还原后的母版中分支出来,并检出fix分支 git branch mycodebranch_fix git checkout mycodebranch_fix
  6. 在这里,您需要找到用于还原的SHA键并还原该还原 git revert [SHA]
  7. 现在,您可以使用mycodebranch_fix修复问题,完成后提交并重新合并到master中。

1

正确标记的答案对我有用,但是我不得不花一些时间来确定发生了什么。

比方说,我们得到了分支机构A和B ..您合并一个分支到分支B和推分支B将因此现在的合并是其中的一部分。但要回到上次提交合并。什么做你做?

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

    commit: <commitHash> Merge: <parentHashA> <parentHashB> Author: <author> Date: <date>

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

  4. 就拿<commitHash>你想要的承诺,去你的git的根文件夹和使用git checkout -b <newBranchName> <commitHash>-这将创造一个新的分支从去年开始提交你所选择的合并之前..瞧,准备好了!


0

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

因为我只是想修改一些修改过的文件,但不是全部的变化PR带来的,我不得不amendmerge commitgit commit --am

脚步:

  1. 转到要更改/还原某些修改文件的分支
  2. 根据修改后的文件进行所需的更改
  3. 运行git add *git add <file>
  4. 运行git commit --am并验证
  5. git push -f

为什么有趣:

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

0

我从此链接中找到了有关如何还原合并的很好的解释,并复制并粘贴了下面的解释,以防万一下面的链接不起作用。

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

我有一个总公司。我们从一些开发人员正在从事的工作中得到了一个分支。他们声称已经准备好了。我们将其合并到master分支中。它破坏了某些内容,因此我们还原了合并。他们对代码进行更改。他们说到可以了,我们又合并了。在检查时,我们发现在还原之前进行的代码更改不在master分支中,但是在更改之后的代码更改在master分支中。并寻求帮助以从这种情况中恢复过来。

“合并还原”之后的历史记录如下所示:

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

其中A和B的关系不太好,M是使这些过早的更改进入主线的合并,x是与支行所做的和主线已经进行的更改无关的更改,W是“还原合并M”(W看起来M倒置吗?)。IOW,“ diff W ^ .. W”类似于“ diff -RM ^ .. M”。

可以通过以下方式进行合并的“还原”:

$ git revert -m 1 M 分支分支的开发人员修复了错误之后,历史记录可能如下所示:

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

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

如果合并更新的边分支(在其顶端带有D),则对A或B所做的任何更改都不会出现在结果中,因为它们已由W恢复。这就是Alan看到的。

Linus解释了这种情况:

恢复常规提交只会有效地撤消该提交所做的操作,并且非常简单。但是,还原合并提交也会撤消提交更改的数据,但是对合并所产生的历史记录的影响绝对没有任何作用。因此,合并仍将存在,并且仍将其视为将两个分支合并在一起,并且将来的合并将把合并视为最后一个共享状态-还原引入的合并的还原将完全不影响此状态。因此,“还原”撤消了数据更改,但实际上不是从某种意义上说是“撤消”,它不会撤消提交对存储库历史记录的影响。因此,如果您将“还原”视为“撤消”,那么您将总是会错过这部分还原。是的,它撤消数据,但是不,它不会撤消历史记录。在这种情况下,您需要首先还原以前的还原,这将使历史记录如下所示:

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

其中Y是W的还原。可以通过以下方式完成“还原的还原”:

$ git revert 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所做的冲突,但这只是正常的合并冲突。


-2

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

一旦你已经完成了艰难的复位部分,然后你可以强制推到远程分支,即git push -f <remote-name> <remote-branch-name>,在<remote-name>通常命名origin。从那时起,您可以根据需要重新合并。


4
除非您是唯一使用回购协议的人,并且您确切地知道自己在做什么,否则任何涉及强制推送的事情都是一个坏主意。使用git revert进行还原,然后再通过git revert进行还原(如果需要再次将其还原),则是一种更为安全的选择。
oyvind '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.