如何恢复已经推送到远程分支的合并提交?
Posted
技术标签:
【中文标题】如何恢复已经推送到远程分支的合并提交?【英文标题】:How to revert a merge commit that's already pushed to remote branch? 【发布时间】:2011-10-29 07:32:36 【问题描述】:git revert <commit_hash>
单独是行不通的。 -m
必须指定,我对此感到很困惑。
以前有人经历过吗?
【问题讨论】:
看看这个问题的答案:***.com/questions/2318777/… 相关:Undo a Git merge?. 此处的链接是说明还原合并提交的最佳示例:christianengvall.se/undo-pushed-merge-git 这是git
的设计与每个人都使用的git-flow
-ish 工作流程不匹配的示例。如果您已签出 develop
,当然您希望恢复引入错误的 2-commit 功能分支,而不是多年的共享开发分支。需要用-m 1
来选择它感觉很荒谬。
我以前从未想过的另一个建议 - 如果某个分支的提交列表很小,您可能会更愿意恢复单个提交而不是整个提交分支。
【参考方案1】:
-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
【讨论】:
来自两个号码8989ee0
,7c6b236
,选择哪一个。我怎么理解?
还原后,我认为无法轻松更正源分支中的代码并再次合并? kernel.org/pub/software/scm/git/docs/howto/…
在谷歌上四处寻找更好的解释时,我发现了这篇文章,我认为它在详细了解细节方面做得很好。我在阅读后发现我真正要找的是 RESET 命令,然后是强制推送。也许它会帮助别人。 atlassian.com/git/tutorials/…
@ArupRakshit 如果你运行git log 8989ee0
和git log 7c6b236
,你应该知道答案。
git log --merges 查看所有合并和 git log --no-merges 查看没有合并的历史记录。合并一个分支会将合并的分支历史记录到目标中,并且使用普通的 git log 很难识别【参考方案2】:
Ben 已经告诉过你如何恢复合并提交,但它非常重要你意识到这样做
"...声明您永远不会希望合并带来的树更改。因此,以后的合并只会带来提交引入的树更改,这些提交不是先前恢复的合并的祖先。这可能是也可能不是你想要的。" (git-merge man page)。
从手册页链接的article/mailing list message 详细说明了所涉及的机制和注意事项。只要确保您了解,如果您恢复合并提交,您就不能稍后再次合并分支并期望相同的更改回来。
【讨论】:
但如果确实需要,您可以恢复恢复以将其取回。 谢谢。知道撤消合并的用例非常有用 - 例如由于错误 - 然后在修复错误后重新合并整个分支,这是一种常见的情况。 如果你和我一样,后来确实想要合并,你可以恢复恢复,或者挑选你恢复的更改。 在我的情况下,我遇到了必须“还原还原”才能获得更改回复的问题。樱桃采摘可能是一种更整洁的方式?下次我试试…… 这非常重要,应该作为警告添加到其他答案中。 IMO,您并没有真正逆转合并本身,而是在逆转合并所做的更改。【参考方案3】:这是一个完整的示例,希望对某人有所帮助:
git revert -m 1 <commit-hash>
git push -u origin master
其中<commit-hash>
是您想要还原的合并的提交哈希,正如this answer 的解释中所述,-m 1
表示您想要还原到第一个父级的树在合并之前。
git revert ...
行实质上是提交您的更改,而第二行通过将更改推送到远程分支来公开您的更改。
【讨论】:
我相信git revert
命令已经提交了创建的提交对象。为避免这种情况发生,您必须输入 --no-commit
标志
正如@Delfic 提到的,提交已经由第一行管理(我需要一个 :wq 来验证它)所以第二行不是必需的。
这令人困惑。只有 2 行,没有 git commit .. 有人可以编辑。
@JayRandom 如上面的 cmets 中所述,revert
命令包含一个提交。
我已经合并了有冲突的分支......这个命令帮助我恢复它【参考方案4】:
有时最有效的回滚方式是后退和替换。
git log
使用第二次提交散列(完整散列,在列出的错误之前要恢复的散列),然后从那里重新分支。
git checkout -b newbranch <HASH>
然后删除旧分支,将新分支复制到它的位置并从那里重新启动。
git branch -D oldbranch
git checkout -b oldbranch newbranch
如果已广播,则从所有存储库中删除旧分支,将重做分支推送到最中心,然后将其拉回全部。
【讨论】:
关于广播的警告应该更明确地说明这是多么可怕的想法。这将破坏该分支的每个人的版本,并且只有在您使用只有您有权访问的远程存储库(github/bitbucket)时才真正有用。 还不如将修改后的配置文件向下推送到生产环境。它不会损坏,它只是对早期提交的重新分支,因此它是一种将分支指针移动到早期版本的迂回方法。希望它只影响本地存储库 这是最好的方法,恕我直言,它不会与撤消合并提交分支的尝试混淆【参考方案5】:您可以按照这些步骤恢复不正确的提交或将远程分支重置为正确的 HEAD/状态。
-
将远程分支签出到本地仓库。
git checkout your_branch_name
从 git log 复制提交哈希(即错误提交之前的提交 ID)
git log -n5
应该显示如下内容:
commit 7cd42475d6f95f5896b6f02e902efab0b70e8038 "合并分支 '错误提交' 到 'your_branch_name'" 提交 f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 “这是一个错误的提交” 提交 3779ab50e72908da92d2cfcd72256d7a09f446ba “这是正确的 提交”
-
将分支重置为上一步复制的提交哈希
git reset <commit-hash> (i.e. 3779ab50e72908da92d2cfcd72256d7a09f446ba)
运行git status
以显示属于错误提交的所有更改。
只需运行 git reset --hard
即可恢复所有这些更改。
强制将您的本地分支推送到远程,并注意您的提交历史记录是干净的,就像它被污染之前一样。git push -f origin your_branch_name
注意:此解决方案仅适用于您自己的分支,而不适用于共享分支。
【讨论】:
如果同时有 20 位开发人员撤消了最新的开发合并怎么办? 当团队中有 20 名开发人员使用该分支时,我不会强制推送该分支。 :) 在这种情况下,明智的做法是进行还原提交。 这是一个非常好的解决方案,当您自己工作或者您确定没有其他开发人员拉动您搞砸的提交时 误将分支拉到本地后,正是我所需要的。谢谢!!【参考方案6】:我发现在两个已知端点之间创建一个反向补丁并应用该补丁会起作用。这假定您已经从主分支创建了快照(标签),甚至是主分支的备份,例如 master_bk_01012017。
假设你合并到 master 的代码分支是 mycodebranch。
-
结帐大师。
在主服务器和备份服务器之间创建一个完整的二进制反向补丁。
git diff --binary master..master_bk_01012017 > ~/myrevert.patch
检查您的补丁
git apply --check myrevert.patch
应用带有签核的补丁
git am --signoff < myrevert.patch
如果您需要在修复后再次引入此代码,您将需要从还原的 master 分支并签出修复分支
git branch mycodebranch_fix
git checkout mycodebranch_fix
这里你需要找到revert的SHA key,revert the revert
git revert [SHA]
现在您可以使用 mycodebranch_fix 修复问题,提交并在完成后重新合并到 master 中。
【讨论】:
【参考方案7】:正如 Ryan 提到的,git revert
可能会使合并变得困难,因此git revert
可能不是您想要的。我发现在这里使用git reset --hard <commit-hash-prior-to-merge>
命令更有用。
完成硬重置部分后,您可以强制推送到远程分支,即git push -f <remote-name> <remote-branch-name>
,其中<remote-name>
通常被命名为origin
。从那时起,您可以根据需要重新合并。
【讨论】:
任何涉及强制推送的事情都是一个坏主意,除非您是唯一使用 repo 的人,并且您确切地知道自己在做什么。使用 git revert 还原,然后可能使用 git revert 还原还原(如果您需要再次恢复)是一种更安全的选择。【参考方案8】:git revert -m 1 <merge-commit>
【讨论】:
这个答案缺少很多细节。也许这就是它好的原因。 @GustavoStraube 是讽刺还是您实际上的意思是答案很好?它实际上对我有用。 为什么是“1”?根据谷歌搜索:“我们使用合并提交的 SHA1 哈希指定合并。 -m 后跟 1 表示我们要保留合并的父端(我们要合并到的分支)。”来源:mijingo.com/blog/reverting-a-git-merge 这就是答案。简单,实际有效。 此答案可以继续依赖其他答案,这些答案解释了您何时想要使用1
以及何时不想使用。【参考方案9】:
在没有发生任何事情的情况下保持日志干净(这种方法有一些缺点(由于 push -f)):
git checkout <branch>
git reset --hard <commit-hash-before-merge>
git push -f origin HEAD:<remote-branch>
'commit-hash-before-merge'来自合并后的日志(git log)。
【讨论】:
提示:如果您是在公司这样做,您可能没有权限。 永远不要在共享仓库中使用push -f
在执行此操作之前,请创建一个备份分支,以防万一事情不按预期进行。
我认为通常通过查看git log
来确定commit-hash-before-merge 并不简单,因为提交会随着时间的推移交织在一起——我想知道这是否总是MERGECOMMIT^
(这似乎表示是git-tower.com/learn/git/faq/undo-git-merge/…)?【参考方案10】:
正确标记的答案对我有用,但我不得不花一些时间来确定发生了什么。所以我决定为像我这样的案例添加一个简单直接的步骤的答案..
假设我们有分支 A 和 B.. 你将分支 A 合并到分支 B 并将分支 B 推送到它自己,所以现在合并是它的一部分.. 但是你想回到最后一次提交 之前 合并.. 你是做什么的?
-
转到你的 git 根文件夹(通常是项目文件夹)并使用
git log
您将看到最近提交的历史记录 - 提交具有提交/作者/日期属性,而合并也具有合并属性 - 因此您会看到它们:
commit: <commitHash>
Merge: <parentHashA> <parentHashB>
Author: <author>
Date: <date>
使用git log <parentHashA>
和git log <parentHashB>
- 你会看到这些父分支的提交历史 - 列表中的第一个提交是最新的
<commitHash>
,转到您的 git 根文件夹并使用git checkout -b <newBranchName> <commitHash>
- 这将从您在合并之前选择的最后一个提交开始创建一个新分支。瞧,准备好了!
【讨论】:
【参考方案11】:关于 git revert -m 的 git doc 提供了一个链接,准确地解释了这一点: https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt
【讨论】:
【参考方案12】:所有答案已经涵盖了大部分内容,但我会加上我的 5 美分。 简而言之,恢复合并提交非常简单:
git revert -m 1 <commit-hash>
如果您有权限,您可以将其直接推送到“master”分支,否则只需将其推送到您的“revert”分支并创建拉取请求。
您可以在此处找到有关此主题的更多有用信息:https://itcodehub.blogspot.com/2019/06/how-to-revert-merge-in-git.html
【讨论】:
【参考方案13】:如果您想恢复 merge
提交,您必须执行以下操作。
-
首先,检查
git log
以找到您的合并提交的ID。您还会发现与合并关联的多个父 ID(见下图)。
记下黄色显示的合并提交 ID。
父 ID 是写在下一行的 Merge: parent1 parent2
。现在...
短篇小说:
-
切换到进行合并的分支。然后只需执行
git revert <merge commit id> -m 1
,它将打开一个vi
控制台以输入提交消息。写入,保存,退出,完成!
长篇大论:
切换到进行合并的分支。就我而言,它是 test
分支,我正在尝试从中删除 feature/analytics-v3
分支。
git revert
是恢复任何提交的命令。但是在恢复 merge
提交时有一个讨厌的技巧。您需要输入-m
标志,否则它将失败。从这里开始,您需要决定是否要恢复您的分支并使其看起来与 parent1
或 parent2
上的完全相同,通过:
git revert <merge commit id> -m 1
(恢复为parent2
)
git revert <merge commit id> -m 2
(恢复为parent1
)
您可以对这些父母进行 git log 以确定您想走哪条路,这就是所有混乱的根源。
【讨论】:
【参考方案14】:我在已合并到 GitHub 存储库的主分支的 PR 上也遇到了这个问题。
由于我只是想修改一些修改过的文件而不是 PR 带来的全部更改,我不得不 amend
merge commit
和 git commit --am
。
步骤:
-
转到您要更改/还原一些已修改文件的分支
根据修改过的文件做你想做的修改
运行
git add *
或git add <file>
运行git commit --am
并验证
运行git push -f
为什么有趣:
它保持 PR 的作者提交不变 它不会破坏 git 树 您将被标记为提交者(合并提交作者将保持不变) Git 就像你解决了冲突一样,它会删除/更改修改文件中的代码,就像你手动告诉 GitHub 不要按原样合并它一样【讨论】:
【参考方案15】:我从link 找到了关于如何恢复合并的很好的解释,我复制粘贴了下面的解释,以防万一下面的链接不起作用。 p>
如何恢复错误的合并 Alan(alan@clueserver.org) 说:
我有一个主分支。我们有一个分支 开发人员正在做的工作。他们声称它已经准备好了。我们合并它 进入主分支。它破坏了一些东西,所以我们恢复合并。 他们对代码进行更改。他们达到了他们所说的程度 没关系,我们再次合并。 检查时,我们发现在还原之前所做的代码更改是 不在主分支中,但之后的代码更改在主分支中 分支。 并请求帮助从这种情况中恢复。
“恢复合并”之后的历史记录如下所示:
---o---o---o---M---x---x---W
/
---A---B
其中 A 和 B 处于不太好的分支开发中,M 是将这些过早的更改带入主线的合并,x 是与侧分支所做的和已经在主线上所做的无关的更改,以及 W是“合并 M 的还原”(W 看起来不是 M 倒置吗?)。 IOW,“diff W^..W”类似于“diff -R M^..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 解释情况:
恢复常规提交只会有效地撤消该提交 做到了,而且相当简单。但也恢复合并提交 撤消提交更改的 data,但它确实如此 合并对 history 的影响没有任何影响。 所以merge还是会存在的,还是会被看成join的 两个分支在一起,未来的合并将看到合并为 最后一个共享状态 - 以及恢复合并的恢复带来 in 根本不会影响这一点。 因此,“还原”会撤消数据更改,但它不是 “撤消”是指它不会撤消提交对 存储库历史记录。 因此,如果您将“还原”视为“撤消”,那么您将始终 错过这部分回复。是的,它会撤消数据,但不,它不会 撤消历史。 在这种情况下,您可能希望先还原上一次还原,这会使历史看起来像这样:
---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
并且再次合并侧分支不会因为之前的revert和revert的revert而产生冲突。
---o---o---o---M---x---x-------x-------*
/ /
---A---B-------------------C---D
当然,在 C 和 D 中所做的更改仍然可能与任何 x 所做的冲突,但这只是正常的合并冲突。
【讨论】:
【参考方案16】:这是一个非常古老的线程,但我认为我缺少另一个方便的解决方案:
我从不还原合并。我只是从一切正常的修订版中创建另一个分支,然后从中间添加的旧分支中挑选需要挑选的所有内容。
所以,如果 GIT 历史是这样的:
d c b 一个 ...我从 a 创建一个新分支,挑选 c 和 d,然后从 b 清除新分支。我可以决定再次在我的新分支中合并“b”。旧分支已弃用,如果不再需要“b”或仍在另一个(功能/修补程序)分支中,则旧分支将被删除。
现在唯一的问题是计算机科学中最困难的事情之一:您如何命名新分支? ;)
好的,如果你失败了,尤其是。在 devel 中,您如上所述创建 newdevel,删除旧的 devel 并将 newdevel 重命名为 devel。 任务完成。您现在可以在需要时再次合并更改。就像以前从未合并过一样......
【讨论】:
"我可以决定再次进行合并。":如果您有精心挑选的提交,那就更难做到,因为它们会被复制。变基会更容易(变基会检测到类似的提交并跳过它们:***.com/a/2628915/6309,***.com/a/43084547/6309) 嗯,是的。樱桃采摘存在这个问题,但在这种情况下,其他提交位于旧的“死”分支中,我永远不会再次合并,并且在一段时间后也会删除旧分支。 “我可以决定再次进行合并。”与“b”的合并有关。 好吧,我没有正确解释那句话。赞成。【参考方案17】:-m1 是当前正在修复的分支的最后一个父级,-m 2 是合并到此分支的原始父级。
如果命令行令人困惑,Tortoise Git 也可以在这里提供帮助。
【讨论】:
【参考方案18】:如果您希望恢复刚才推送的更改,这是一个非常简单的答案:
commit 446sjb1uznnmaownlaybiosqwbs278q87
Merge: 123jshc 90asaf
git revert -m 2 446sjb1uznnmaownlaybiosqwbs278q87 //does the work
【讨论】:
【参考方案19】:当您在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
建立一个备份分支
git checkout -b mybackup-brach
git reset --hard 8989ee0
git push origin -u mybackup-branch
所以现在你有了合并前的更改,如果一切正常,签出到上一个分支并使用备份分支重置
git reset --hard origin/mybakcup-branhc
【讨论】:
以上是关于如何恢复已经推送到远程分支的合并提交?的主要内容,如果未能解决你的问题,请参考以下文章
TortoiseGit- 创建本地新分支,提交推送到远程,本地新分支合并到工作分支,提交到远程工作分支等。