通过拉取请求撤消合并?

Posted

技术标签:

【中文标题】通过拉取请求撤消合并?【英文标题】:Undo a merge by pull request? 【发布时间】:2011-09-22 19:40:40 【问题描述】:

有人接受了他们不应该接受的拉取请求。现在我们合并了一堆损坏的代码。如何撤消拉取请求?我只是要在合并之前恢复对提交的更改,但我注意到它合并成一堆提交。所以现在有这个人在合并前几天的所有这些提交。您如何撤消此操作?

【问题讨论】:

尽管接受了答案的建议,但如果与其他人共享存储库,请不要强制推送到存储库。您冒着破坏其他人所做工作的风险,GitHub 可能会继续显示该拉取请求已被合并。此问题的另一个答案解释了撤消拉取请求的更安全方法。 注意:至少现在(2014 年 6 月),GitHub 在他们的 Pull Request Web GUI 中提出了“Revert”按钮。见my answer below revert 按钮的问题在于它创建了一个新的提交作为 pull request 的逆向,这意味着如果你想最终合并这些更改,使用“Revert”按钮可以做到这一点困难得多。 【参考方案1】:

这个问题有一个better answer,尽管我可以逐步分解。

您需要像这样获取和签出最新的上游更改,例如:

git fetch upstream
git checkout upstream/master -b revert/john/foo_and_bar

查看提交日志,您应该会发现类似以下内容:

commit b76a5f1f5d3b323679e466a1a1d5f93c8828b269
Merge: 9271e6e a507888
Author: Tim Tom <tim@tom.com>
Date:   Mon Apr 29 06:12:38 2013 -0700

    Merge pull request #123 from john/foo_and_bar

    Add foo and bar

commit a507888e9fcc9e08b658c0b25414d1aeb1eef45e
Author: John Doe <john@doe.com>
Date:   Mon Apr 29 12:13:29 2013 +0000

    Add bar

commit 470ee0f407198057d5cb1d6427bb8371eab6157e
Author: John Doe <john@doe.com>
Date:   Mon Apr 29 10:29:10 2013 +0000

    Add foo

现在您想要恢复整个拉取请求,并能够在以后恢复。 为此,您需要获取 merge commit 的 ID。

在上面的例子中,merge commit 是上面写着“Merged pull request #123...”

这样做可以恢复两个更改(“Add bar”“Add foo”),您最终将在一次提交中恢复整个拉取请求您可以稍后取消还原并保持更改历史记录干净:

git revert -m 1 b76a5f1f5d3b323679e466a1a1d5f93c8828b269

【讨论】:

这应该是正确的答案。在 git-revert 手册页中有参考这里的 git 邮件列表中的更多详细信息 kernel.org/pub/software/scm/git/docs/howto/… 为什么是 git checkout upstream/master -b revert/john/foo_and_bar ?它到底是做什么的? @Magne - 您正在创建一个新分支来执行还原,然后您可以选择合并还原的位置。基本上只是让您更好地控制如何处理分支。就我而言,我从这个“固定”分支提交了一个新的拉取请求,其中包含返回到我们的开发分支的恢复,这意味着我的新拉取请求也可以在必要时恢复。这是为一个版本完成的,我们决定拉出重新安排的功能的初稿。没有糟糕的代码,只是不会出现在这个版本中。 我在这件事上学到了深刻的教训。我们将一项功能推送到我们的开发分支,但意识到存在数据问题并直接在开发时恢复。后来当事情解决后,我们想在推送之前将新的开发功能添加回这个分支以测试它们与这个功能的兼容性,所以我将开发合并回功能分支。这也应用了还原提交,它取消了对功能分支所做的每一个更改。 > 我以后如何“取消还原”它?【参考方案2】:

查看您的提交图(使用 gitk 或类似程序)。您将看到来自拉取请求的提交,您将看到自己的提交和合并提交(如果它不是快进合并)。你只需要在合并之前找到你自己的最后一个提交,并将分支重置为这个提交。

(如果你有分支的 reflog,应该更容易在合并前找到提交。)


(在 cmets 中获得更多信息后编辑:)

Okay, lets look at the graph:

我认为最后一次(最右边的)提交是您的错误按拉取请求合并,它合并了此处看到的蓝线。 您的最后一个 good 提交将是之前在黑线上的那个,这里用红色标记:

重置到这个提交,你应该没问题。

这意味着,在您的本地工作副本中执行此操作(确保您没有更多未提交的内容,例如通过 git stash):

git checkout master
git reset --hard 7a62674ba3df0853c63539175197a16122a739ef
gitk 

现在确认你确实在我在那里标记的提交上,你将看不到它的祖先中的任何被拉取的东西。

git push -f origin master

(如果您的 github 远程命名为 origin - 否则更改名称)。

现在一切都应该在 github 上看起来正确了。提交仍将在您的存储库中,但任何分支都无法访问,因此不应在那里造成任何伤害。 (当然,它们仍然在 RogerPaladin 的存储库中。)

(可能有一种特定于 Github 的 Web 方式来做同样的事情,但我对 Github 及其拉取请求管理系统不太熟悉。)

注意,如果其他人可能已经通过错误的提交拉取了您的 master,那么他们就会遇到与您当前相同的问题,并且无法真正做出贡献。在重置为新的主版本之前。

如果很可能发生这种情况,或者您只是想避免任何问题,请使用git revert 命令而不是git reset,通过新提交恢复更改,而不是设置回旧提交。 (有些人认为您永远不应该对已发布的分支进行重置。)请参阅此问题的其他答案以了解如何执行此操作。

未来:

如果您只想要 RogerPaladin 分支的一些提交,请考虑使用 cherry-pick 而不是 merge。或者与 RogerPaladin 沟通以将它们移动到单独的分支并发送新的拉取请求。

【讨论】:

但是我们在合并和他的第一次提交之间有大量的提交。所以就像我们有合并提交,我们自己的提交,然后是他的另一个提交。似乎它合并到了他非常古老的提交中。 是的,它将合并到作为合并提交的祖先的所有内容中(并且还不是您的提交的祖先)。这不应该妨碍您重置 - 如果您之后没有重新设置基准,提交将不会自行重新排序。 是的,现在看起来是正确的(除了奇怪的 merge into its own - 但这可能是网络图软件中的一个小故障)。 :-) 我很高兴能帮上忙。 我认为如果有人拉取了错误的提交并且他们后来发送了一个包含相同错误提交的拉取请求,因此他们偷偷溜进了仓库,这可能会有问题。创建一个逆转错误提交的新提交不是更安全吗?这样,如果错误的提交已被拉到其他分支/分叉,它们将被另一个拉到另一个分支/分叉有效地删除?我并不是说这是事实,这是我认为可能是真的,与 OP 处于同一位置并且花了一个小时左右考虑我的选择。 @Will 我会建议不接受这个答案。这对于尚未推送的代码很好(在这种情况下,this 是一个更相关的 SO 问题,但对于公开共享的代码,执行重写历史的 git 命令(在这种情况下,reset --hard并且强制推送是非常糟糕的做法)。下面@errordeveloper 的回答显示了一种无需任何历史重写或强制推送的方法。【参考方案3】:

如果拉是他做的最后一件事

git reset --hard HEAD~1

【讨论】:

要小心遵循这个说明,它实际上让我后退了两步,而不是一步。 @szeitlin 怎么可能让你后退两步,而不是一步?我知道这条评论是 4 年前留下的,但我很好奇是否有人知道它是如何发生的。这对我来说非常重要。谢谢。 我现在不记得了,但我猜如果我在尝试重置时进行了新的提交,可能会发生这种情况?如果你担心的话,我会用一个虚拟的 repo 来测试它。 这个对我来说效果很好。它删除了最近合并的拉取请求。在 git reset --hard HEAD~1 之后,我使用 git push origin -f 来更新远程存储库。但要小心,在这样做之前要小心。【参考方案4】:

从 2014 年 6 月 24 日开始,您可以尝试通过以下方式轻松取消 PR(请参阅“Reverting a pull request”):

Introducing the Revert Button

您可以通过单击 Revert 轻松地在 GitHub 上恢复拉取请求:

系统将提示您使用已恢复的更改创建新的拉取请求:

虽然该还原是否使用-m,但仍有待测试(也用于还原合并)

但是Adil H Raza 加入了 cmets(2019 年 12 月):

这是预期的行为,它创建了一个新分支,您可以从这个新分支创建一个 PR 到您的 master。 这样,将来您可以在需要时取消还原,这是最安全的选择,而不是直接更改您的 master


警告:Korayem 指出in the comments:

还原后,假设您在 Git 分支上做了一些进一步的更改,并从同一源/目标分支创建了一个新 PR。您会发现 PR 只显示新的更改,但没有显示任何内容在还原之前在那里

Korayem 向我们推荐“Github: Changes ignored after revert (git cherry-pick, git rebase)”了解更多信息。

【讨论】:

我试过了,它创建了一个新分支而不是撤消 master 上的 PR? 奇怪。您能否提出一个新问题来说明这种行为? 这是预期的行为,它创建了一个新分支,您可以从这个新分支创建一个 PR 到您的 master。这样,将来您可以在需要时取消还原,这是最安全的选择,而不是直接更改您的主人。 @AdilH.Raza 谢谢。我已将您的评论包含在答案中以提高知名度。 @VonC 手指交叉,这将使全球的开发人员免于过多的痛苦和浪费时间【参考方案5】:

要撤消包含您不想删除的提交的 github 拉取请求,您必须运行:

git reset --hard --merge &lt;commit hash&gt;

提交哈希是合并拉取请求之前的提交。这将从拉取请求中删除所有提交,而不会影响历史记录中的任何提交。

找到此字段的一个好方法是转到现已关闭的拉取请求并找到此字段:

Pull Request Image

运行git reset 后,运行:

git push origin --force &lt;branch name&gt;

这应该在拉取请求之前将分支恢复回来,而不会影响分支中的任何提交,这些提交会在拉取请求的提交之间的提交历史记录中出现。

编辑:

如果您要单击拉取请求上的还原按钮,则会在分支上创建一个额外的提交。它不会取消提交或取消合并。这意味着如果您要点击恢复按钮,您将无法打开新的拉取请求来重新添加所有这些代码。

【讨论】:

解决问题的好方法,无需任何疯狂的还原或挑选 谢谢!这就是我计划做的事情,但那将需要大约 200 次提交才能挑选出来。【参考方案6】:

我一直在使用这个地方,谢谢。

我正在寻找如何撤消拉取请求并来到这里。

我正要git reset --hard 到“很久以前”,然后 快进到执行拉取请求之前的位置。

除了看这里,我还问我的同事他会做什么, 他有一个典型的好答案:使用示例输出 上面的第一个答案:

git reset --hard 9271e6e

与 Git 中的大多数事情一样,如果您以某种不容易的方式进行操作, 你可能做错了。

【讨论】:

【参考方案7】:

如果您发出以下命令,您将获得活动列表,包括提交、合并。

git reflog

您的最后一次提交可能应该在'HEAD@0'。您可以使用您的提交消息检查相同的内容。 要转到该点,请使用命令

git reset --hard 'HEAD@0'

您的合并将被还原。如果您还有新文件,请放弃合并中的这些更改。

【讨论】:

【参考方案8】:

如果您想撤消拉取请求,我们可以按照这些 步骤-

所有拉取请求的活动列表 -

git reflog

运行上述命令后,将出现此输出。

然后运行reset命令-

git reset --hard 6954dff92

其中 6954dff92 合并 id,并且您的代码根据合并 id 更新。

谢谢,希望对你有帮助,请点赞和支持。

【讨论】:

以上是关于通过拉取请求撤消合并?的主要内容,如果未能解决你的问题,请参考以下文章

如何撤消合并请求及其提交?

GitHub:重新打开合并的拉取请求

使用 github api 从拉取请求编号获取拉取请求合并提交 sha

如何避免使用 API 延迟 github 拉取请求合并

如何使用“合并前需要拉取请求审查”来保护分支

重新应用通过 Github 合并和撤消的更改