【问题标题】:Undo a merge that has been pushed撤消已推送的合并
【发布时间】:2012-04-27 21:56:43
【问题描述】:

好的,我弄得有点乱。显然,在我家里的机器上,开发分支没有更新。我做了一个提交并推送。结果是实际的 origin/develop 分支已合并到我的本地开发分支中 - 由于某种原因被视为不同的分支!

一方面,我真的不明白这是怎么发生的,另一方面,我可以撤消吗?

为了说明这一点,网络现在看起来像这样:

 local-develop ---------------------------------- C*--- M -
origin/develop --- C --- C --- C --- C --- C --------- / 

我真正想要的是 C* 将致力于起源/开发,而不是合并分支。

正如我所说,这已经被推送了。有没有办法删除更改并按照我想要的方式提交?

例如我这样做:

git reset --hard HEAD~1

我不确定这是否会撤消合并并且我有两个不同的开发,然后合并删除等等...?

【问题讨论】:

    标签: git merge reset revert


    【解决方案1】:

    合并不会发生在 push 上,它们发生在 git merge 上(嗯,pull 但实际上只是 fetch + merge,因此,合并发生在合并上)。

    您似乎更有可能做了这样的事情:

    <get copy from origin/develop>
    <wait around for remote's origin/develop to acquire new commits>
    git checkout develop      # get onto (local) develop branch
    <edit>
    git commit -m message     # create some new commit(s)
    git push origin develop   # attempt to push -- but this fails!
    git pull
    

    这是创建合并提交的最后一步(上面的M),因为pull 表示fetch(获取现在在origin/develop 中的所有新提交),然后是merge(取您的本地develop 并将您的提交与刚刚获取的新提交合并)。

    如果您还没有git pushed 这个 new 结果,那么远程 repo 没有 either 您的本地提交,即您标记的那些C*M。在这种情况下,你的状态很好! (您可以通过再次运行 git fetch 来检查以确保您的本地仓库的 origin/develop 与远程仓库中的匹配,然后执行 git log origin/develop 以查看其中的内容。)

    记住这里有两个完全独立的 git 存储库可能会有所帮助:你的,你的东西;和你在这里打电话给origin 的那个。 (我们称那台机器为X。)如果你登录到X,它有自己独立的提交历史和分支名称等等。在那里,您可以将目录更改为 repo,运行 git log -1 develop,然后查看该分支顶端的内容。

    现在,如果您从 X 注销并回到您自己的计算机上,则可以运行 git log -1 origin/develop。如果这与您在X 上看到的相同,那么get fetch 没有什么可更新的,因为git fetch 所做的实际上(但更有效)登录到X 并查看@ 中的内容987654346@那里。 X 拥有的任何你在 origin/develop 中没有的东西,fetch 带来并添加到 origin/develop。现在您与X 同步。 X 没有你的东西,但你有他们的。

    如果您随后采取额外的步骤来执行merge(包括pull 所暗示的步骤),git 将在必要时进行合并提交......但这一切都在 你的 repo,在分支的顶端(在这种情况下仍然是develop)。除非并且直到您将此合并提交推送到 X(或 X 上的某个人从您那里提取您的提交,但我们现在先忽略它 :-)),X 不会拥有它。

    无论如何,只要远程(X 此处)没有您的合并提交,您就是黄金。因为 他们 没有它,所以其他人也没有。你可以在你的develop 分支中创建一个rebase,将你的提交(C*)放在origin/develop 之上。这将摆脱合并提交 (M),然后您可以将简单的快进推送到 origin/develop

    如果X 确实 有你的合并提交——即,如果你在pulled 之后推动并获得了合并——那么你(在某种程度上)被卡住了,因为可能是其他人可以访问X,现在正在使用您的合并提交。可以回滚 X 上的 repo,类似于您在自己的 repo 中使用 git resetgit rebase 等执行此操作的方式,但这通常不是一个好主意。


    现在,假设您已经实际上推送到了另一个 repo(在机器 X 上),但是您绝对确定没有其他人看到您的更改,并且您肯定会重置它们,而不是还原它们(还原等于“坦白”并将搞砸记录抛在脑后,这可以让其他人轻松从中恢复,但也让他们看到您的错误:-))。1

    这是诀窍:你首先需要让机器 X 的 repo 说“分支的尖端 devel 是提交 C7”,其中 C7 在你之前的图表中,只是重新编号以便我可以命名每个提交都不同:

    --------------------------- C*--- M
    --- C4 --- C5 --- C6 --- C7 ---- /
    

    那么,你怎么能这样做呢?好吧,一种方法是登录 X,2cd 进入 repo(即使它是 --bare),并在那里使用 git update-ref。假设 C7 的 SHA1 实际上是 50db850(如“git log”所示)。然后你可以这样做:

    localhost$ ssh X
    X$ cd /path/to/repo.git
    X$ git update-ref refs/heads/develop 50db850
    

    但是如果你不能登录X,或者只是不想登录,你可以用git push -f做同样的事情。3(这还有其他好处:在特别是,一旦 push -f 成功完成,您的 git repo 将知道 origin/develop 已被回绕。)只需创建一个指向正确提交的本地分支提示:4

    localhost$ git branch resetter 50db850
    localhost$ git log resetter         # make sure it looks right
    ...
    localhost$ git push -f origin resetter:develop
    Total 0 (delta 0), reused 0 (delta 0)
    To ssh://[redacted]
     + 21011c9...50db850 resetter -> develop (forced update)
    localhost$ git branch -d resetter   # we don't need it anymore
    

    完成此操作后,机器 X 将恢复到您想要的状态,并且您可以继续进行,就好像您从未推送过您不喜欢的合并一样。

    请注意,当您执行 push -f 时,如果其他人在 M 之上进行了新提交,那么这些将变得不可见(从技术上讲,它们仍然在那里,以及您的合并提交,但它们在 lost+foundlost+found 意义上“丢失”了,几个月后它们真的会永远消失)。5

    再次非常重要:这种“共享存储库的回滚”对于共享存储库的其他用户来说是一个很大的痛苦,所以在你这样做之前一定要确定它是好的。


    1这种微不足道的合并甚至不需要还原。将合并留在那里没有根本性的问题。但是,如果您正在处理更严重的错误合并,那么除了“oops”的记录之外,恢复合并还有另一个缺点:它使稍后“重做”更改变得更加困难,因为稍后的“合并故意”将看到较早的合并并认为:好的,我不需要重新合并 那些 更改。然后,您必须改为“还原还原”。

    我认为正确的经验教训是:在你推动之前先看看 (git log),确保你要推动的是你打算推动的。

    2或者,更简单:git ls-remote。这使用获取协议代码来查看遥控器有什么。但它隐藏了我要在这里讨论的主题,即:遥控器和你的一样是一个 repo!

    3即将发布的 git 版本 2 具有新的“安全功能”,可用于 git push -f。但他们还没有出来,所以这对你没有帮助。稍后我会重新编辑它,以记录它们。在此之前,真的要小心:此时,您正在与任何试图将新内容推送到共享存储库的人竞争。

    4您甚至可以通过原始 SHA-1 ID 执行此操作。不过,分支名称更容易连续多次正确键入。

    5保留和过期是通过git的“reflogs”。共享服务器可能没有记录所有 ref 更新;如果没有,只有已经拥有新提交的私有仓库才会保留它们。最短的默认到期时间是 30 天,即大约 1 个月,因为提交不再可以从分支提示到达。但请注意,在强制将“丢失的工作”从共享存储库中推出后,让每个人都在他们的存储库中搜索“丢失的”提交是一种无趣且疯狂的争夺。

    【讨论】:

    • 哇,非常感谢这个广泛的答案。不幸的是,我已将其推送到 X。由于我确信没有其他人进行了新的提取,我想知道是否可以撤消这些更改。有一件事我真的不清楚:如果我后退两步进行重置,我是留下一个分支还是两个分支?
    • 好的,好吧,在推送之后,如果你真的确定 :-) ... 你想要做的是得到X 对分支@987654393 尖端的想法@ 倒带一点。让我编辑答案...
    • 哦,还有:如果你 git reset HEAD~2“后退两步”,这实际上等于告诉 git:“设置我现在所在的分支的尖端”——大概这会成为您的develop—“通过查找 HEAD~2 获得的提交 ID”。如果您想查看提交的内容,请先git show HEAD~2。如果您在上图中提交MHEAD~2M~2,它支持MC*。可能不是你想要的。
    • 好的,超级。今天吸取了大教训!太感谢了。 reset HEAD~2 会做与我想要的完全相反的事情:-),所以上面的帮助非常有用
    • @torek 你的冗长与你的慷慨相匹配!谢谢
    【解决方案2】:

    这里是 Linus Torvals 的回复 这里是 Linus Torvals 的回复http://opensource.apple.com/source/Git/Git-26/src/git-htmldocs/howto/revert-a-faulty-merge.txt

    【讨论】:

      猜你喜欢
      • 2011-08-22
      • 2011-01-24
      • 2016-06-02
      • 2020-07-08
      相关资源
      最近更新 更多