ChatGPT解决这个技术问题 Extra ChatGPT

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

git

单独使用 git revert <commit_hash> 是行不通的。 -m 必须指定,我对此感到很困惑。

以前有人经历过吗?

看看这个问题的答案:stackoverflow.com/questions/2318777/…
相关:Undo a Git merge?
此处的链接是说明还原合并提交的最佳示例:christianengvall.se/undo-pushed-merge-git
这是 git 的设计与每个人都使用的 git-flow 式工作流程不匹配的示例。如果您已 develop 签出,当然您希望恢复引入错误的 2-commit 功能分支,而不是多年的共享开发分支。需要用 -m 1 来选择它感觉很荒谬。
只是我以前从未想过的另一个建议 - 如果某个分支的提交列表很小,那么您可能会觉得恢复单个提交而不是整个提交分支会更舒服。

J
Jess

-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

从两个数字8989ee07c6b236,去哪一个。我怎么理解?
还原后,我认为无法轻松更正源分支中的代码并再次合并? kernel.org/pub/software/scm/git/docs/howto/…
在谷歌搜索更好的解释时,我发现了这篇文章,我认为它在详细了解细节方面做得很好。我在阅读后发现我真正要找的是 RESET 命令,然后是强制推送。也许它会帮助别人。 atlassian.com/git/tutorials/…
@ArupRakshit 如果您运行 git log 8989ee0git log 7c6b236,您应该知道答案。
git log --merges 查看所有合并和 git log --no-merges 查看没有合并的历史记录。合并一个分支会将合并的分支历史记录到目标中,并且使用普通的 git log 很难辨认
W
Will

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

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 如上面评论中所述, revert 命令包含提交。
我已经合并了有冲突的分支......这个命令帮助我恢复它
b
bruno

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

“...声明您永远不希望合并带来的树更改。因此,以后的合并只会带来不是先前恢复合并的祖先的提交引入的树更改。这可能是也可能不是你想要什么。” (git-merge 手册页)。

从手册页链接的 article/mailing list message 详细说明了所涉及的机制和注意事项。只要确保您了解,如果您恢复合并提交,您不能稍后再次合并分支并期望相同的更改回来。


但是,如果确实需要,您可以还原还原以使它们恢复原状。
谢谢。知道撤消合并的用例非常有用 - 例如由于错误 - 然后在修复错误后重新合并整个分支,这是一种常见的情况。
如果您像我一样,后来确实想要合并,您可以还原还原,或者选择您还原的更改。
在我的情况下,我不得不“恢复还原”来获得我的更改回复问题。樱桃采摘可能是一种更整洁的方式?下次我会试试...
这非常重要,应该作为警告添加到其他答案中。 IMO,您并没有真正扭转合并本身,而是扭转了合并所做的更改。
s
ssasi

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

注意:此解决方案仅适用于您自己的分支,不适用于共享分支。

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

应该显示如下内容:

commit 7cd42475d6f95f5896b6f02e902efab0b70e8038 "Merge branch 'wrong-commit' into 'your_branch_name'" commit f9a734f8f44b0b37ccea769b9a2fd774c0f0c012 "this is a wrong commit" commit 3779ab50e72908da92d2cfcd72256d7a09f446ba "this is the correct commit"

将分支重置为在上一步中复制的提交哈希 git reset (即 3779ab50e72908da92d2cfcd72256d7a09f446ba)运行 git status 以显示作为错误提交的一部分的所有更改。只需运行 git reset --hard 即可恢复所有这些更改。强制将您的本地分支推送到远程并注意您的提交历史记录是干净的,就像它被污染之前一样。 git push -f origin your_branch_name


如果同时有 20 名开发人员撤消了最新的开发合并怎么办?
当团队中有 20 名开发人员使用该分支时,我不会强制推送该开发分支。 :) 在这种情况下,明智的做法是进行还原提交。
当您自己工作或者您确定没有其他开发人员拉动您搞砸的提交时,这是一个非常好的解决方案
正是我在错误地在本地拉分支后所需要的。谢谢 !!
默认情况下,此答案显示在顶部,但这是一个危险的答案。许多人只会按照步骤进行操作,而不会阅读底部的说明。我试图将注释放在顶部,但看起来编辑队列已满。
N
Neeraj Kumar
git revert -m 1 <merge-commit>

这个答案缺少很多细节。也许这就是它好的原因。
@GustavoStraube 是讽刺还是您实际上的意思是答案很好?它实际上对我有用。
为什么是“1”?根据谷歌搜索:“我们使用合并提交的 SHA1 哈希指定合并。 -m 后跟 1 表示我们要保留合并的父端(我们要合并到的分支)。”来源:mijingo.com/blog/reverting-a-git-merge
这就是答案。简单,实际有效。
该答案可以继续依赖其他解释您何时想要使用 1 以及何时不想使用的答案。
n
nvd

为了保持日志干净,因为没有发生任何事情(这种方法有一些缺点(由于 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/… )?
K
K-Gun

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

git revert -m 1 <commit-hash>

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

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


p
ppostma1

有时最有效的回滚方法是退后一步并替换。

git log

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

git checkout -b newbranch <HASH>

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

git branch -D oldbranch
git checkout -b oldbranch newbranch

如果已广播,则从所有存储库中删除旧分支,将重做的分支推送到最中心,然后将其拉回全部。


关于广播的警告应该更明确地说明这是一个多么可怕的想法。这将破坏该分支的每个人的版本,并且仅在您使用只有您有权访问的远程存储库(github/bitbucket)时才真正有用。
不如将修改后的配置文件向下推送到生产环境那么糟糕。它不会损坏,它只是对早期提交的重新分支,因此它是一种将分支指针移动到早期版本的迂回方式。希望它只影响本地存储库
这是最好的方法恕我直言,它不会与撤消合并提交分支的尝试混淆
s
saran3h

如果您想恢复 merge 提交,您必须执行以下操作。

首先,检查 git 日志以找到您的合并提交的 id。您还会发现与合并关联的多个父 ID(见下图)。

https://i.stack.imgur.com/lZScB.png

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

短篇故事:

切换到进行合并的分支。然后只需执行 git revert -m 1 这将打开一个 vi 控制台以输入提交消息。写入,保存,退出,完成!

很长的故事:

切换到进行合并的分支。就我而言,它是测试分支,我正在尝试从中删除 feature/analytics-v3 分支。 git revert 是恢复任何提交的命令。但是在恢复合并提交时有一个讨厌的技巧。您需要输入 -m 标志,否则它将失败。从这里开始,您需要通过以下方式决定是否要恢复分支并使其看起来与 parent1 或 parent2 上的完全相同:

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

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

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


我不理解父母。我想恢复两个合并,其中一个是最后一次提交,其中一个是最后一次提交的第三个,在两者之间还有其他人的另一个合并。
t
torek

我从这个 link 中找到了关于How To Revert The Merge 的很好的解释,我复制并粘贴了下面的解释,如果下面的链接不起作用,它会很有帮助。

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

我有一个主分支。我们有一个分支,一些开发人员正在研究它。他们声称它已经准备好了。我们将它合并到主分支中。它破坏了一些东西,所以我们恢复合并。他们对代码进行更改。他们达到了他们说没问题的地步,然后我们再次合并。经过检查,我们发现在还原之前所做的代码更改不在 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

并且再次合并侧分支不会因为之前的revert和revert的revert而产生冲突。

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

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


A
AnilS

当您在 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

m
miwoe

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

我从不恢复合并。我只是从一切正常的修订版中创建另一个分支,然后从中间添加的旧分支中挑选需要挑选的所有内容。

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

d

C

b <<< 合并

一个

...

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

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

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


“我可以决定再次进行合并。”:如果您有精心挑选的提交,那就更难做到,因为它们会被重复。变基会更容易(变基会检测到类似的提交并跳过它们:stackoverflow.com/a/2628915/6309stackoverflow.com/a/43084547/6309
嗯,是的。樱桃采摘存在这个问题,但在这种情况下,其他提交位于旧的“死”分支中,我永远不会再次合并,并且在一段时间后也会删除旧分支。 “我可以决定再次进行合并。”与“b”的合并有关。
Б
Божидар Йовчев

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

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

转到您的 git 根文件夹(通常是项目文件夹)并使用 git log 您将看到最近提交的历史记录-提交具有提交/作者/日期属性,而合并也具有合并属性-因此您会看到它们如下所示:提交: 合并: 作者: 日期: 使用 git log 和 git log - 你会看到那些父分支的提交历史 - 第一次提交列表中是最新的 获取您想要的提交的 ,转到您的 git 根文件夹并使用 git checkout -b - 这将从您的最后一次提交开始创建一个新分支'在合并之前选择了.. 瞧,准备好了!


a
arqam

如果您希望恢复刚才推送的更改,这是一个非常简单的答案:

commit 446sjb1uznnmaownlaybiosqwbs278q87
Merge: 123jshc 90asaf


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

a
alexplain

我发现在两个已知端点之间创建一个反向补丁并应用该补丁会起作用。这假定您已经从主分支创建了快照(标签),甚至是主分支的备份,例如 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 在这里您需要找到恢复的 SHA 密钥并恢复恢复 git revert [SHA] 现在您可以使用你的 mycodebranch_fix 来解决问题,一旦完成,提交并重新合并到 master 中。


J
Junyong

关于 git revert -m 的 git doc 提供了一个链接来准确解释这一点:https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt


V
Vinay

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

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


M
Maxime Lafarie

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

因为我只想修改一些修改过的文件,而不是 PR 带来的全部更改,所以我不得不用 git commit --amamend merge commit

脚步:

转到您要更改/还原一些修改过的文件的分支根据修改过的文件执行您想要的更改运行 git add * 或 git add 运行 git commit --am 并验证运行 git push -f

为什么有趣:

它保持 PR 的作者提交不变

它不会破坏 git 树

您将被标记为提交者(合并提交作者将保持不变)

Git 就像你解决了冲突一样,它会删除/更改修改文件中的代码,就像你手动告诉 GitHub 不要按原样合并它一样


H
Harry Wang

正如 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 还原还原(如果您需要再次恢复)是一种更安全的选择。