撤消对远程分支的最新推送

时间:2016-08-04 18:15:48

标签: git github

我意外地提交并将我的代码更改推送到了错误的分支。

以下是我为撤消不良修改所做的工作

  1. git log:找出我需要回到的地方
  2. git reset --hard 3cd4e57dcbb2a5bae350086c11d64c2f01ad4546
  3. git push -f origin 3cd4e57dcbb2a5bae350086c11d64c2f01ad4546:develop
  4. 但是我收到了错误

      

    ! [远程拒绝] 3cd4e57dcbb2a5bae350086c11d64c2f01ad4546 - >发展(受保护的分支挂钩拒绝)

    如何在遥控器上撤消?我猜git reset --hard只是本地而不是远程。

2 个答案:

答案 0 :(得分:4)

远程分支似乎受到保护。 为了推动力量, 您可以暂时取消保护分支,再次推送和保护。 您可以在GitHub上的存储库页面的设置/分支选项卡上执行此操作。 请注意,这(并且通常强制推送)不是对公共存储库的推荐操作。

可替换地, 您可以通过还原来撤消提交, 在坏的之后会生成一个新的提交, 然后简单地推。

答案 1 :(得分:2)

我发表了一条评论,但这个评论太长了,所以我也会把它作为一个答案。我不清楚你在本地和任何其他(可推送的)存储库中做了什么,此时。

分布式存储库

首先,请记住,每次以分布式方式使用Git时 - 在您自己的计算机上,以及使用"远程"像origin一样 - 涉及两个(或更多)单独的Git存储库。你在本地做的事情确实是纯粹本地的:你在你的存储库中执行它们,现在拥有它们,但没有其他存储库有这些提交。

要将提交从您的存储库发送到另一个存储库,您可以推送它们:

git push origin refspec

或与运营其他存储库的人交谈 - 让他们说你的朋友Bob - 并说服运行git fetch 你的< / em> repository。

如果遥控器是像GitHub这样的大型服务器,那么你实际上没有朋友Bob运行它,服务器人员通常会设置一些东西以便你有某种界面(例如网络浏览器)方法),您可以通过它来进行更改。例如GitHub允许你假装是Bob&#34;并在GitHub服务器上按照名称保护和取消保护分支。

提交图

接下来,请记住,Git中的分支名称只被视为提交图中提交的可移动指针。提交图更加永久和可靠:一旦提交存在,它就以精确的形式存在&#34;永远&#34;。但是,可以添加,删除和移动分支名称。所以它是提交,它们具有&#34;真实的名称&#34;那些大丑陋的哈希ID,如3cd4e57dcbb2a5bae350086c11d64c2f01ad4546 - 这是主要的问题 - 但你几乎总是通过分支名称之类的东西访问

(分支机构名称也保护提交,并且可以访问它们,但目前这不是您需要担心的事情。)

绘制提交图

如果您绘制(至少部分)提交图,这些事情总是更有意义。您可能有一个分支名称,如develop,&#34;指向&#34; (具有ID)像3cd4e57dcbb2a5bae350086c11d64c2f01ad4546这样的提交。然而,该特定提交本身指向另一个较早的提交(包含ID)。每个提交都指向其父提交,这意味着我们可以使用箭头绘制提交的链接。我实际上没有箭头所以我在这里使用近似值:

... <- o <- o <- o   <-- develop

这里,每次提交只是指向其父级的轮次o。分支名称develop指向分支的最近 tip 提交。该提交指向其父级,它指向另一个父级,依此类推。

所有中间箭头往往只是一种分心(再加上它们向前倾斜)所以我更喜欢把它们画成:

...--o--o--o   <-- develop

如果我们develop&#34;分支&#34;从另一个分支如master开始,该图清楚地说明了它是如何工作的:

...--o--o--o--o   <-- master
      \
       o--o--o    <-- develop

git reset移动分支标签

使用git reset时,可以移动分支标签。这对图形本身没有影响,仅在标签指向上。我们假设您在分支develop上,并使用git reset来删除&#34;来自develop的最后一次提交。图片现在改为:

...--o--o--o--o   <-- master
      \
       o--o--o
          ^
          |
       develop

看看图表是如何完全不变的,只有标签移动了? develop 的旧提示 - 提交本身,就是 - 仍然存在。只是它不再有一个指向它的分支名称。要命名该特定提交,您现在必须使用其哈希ID,您可能已经或可能没有在任何地方保存。

(嗯,它在这个图中没有这样的名字 。但我们是否在所有中绘制了分支名称?如果没有,是否有一些其他< / em>指向它的人类可读的名称?再次,让我们暂时不担心。)

git reset之前,如果您要求您的Git向您显示您致电develop的提交,它会向您显示旧提示:最右侧o提交。在git reset之后,如果您要求您的Git向您显示您调用develop的提交,则会在同一行显示中间o提交。所以你的 Git现在认为develop意味着提交和所有早期的提交。

git push要求其他 Git更改名称点

的位置

正如您在问题中所建议的,执行git reset只会更改 Git关于哪个提交develop名称的想法。名称develop指向其他提交的任何其他存储库(例如上一个提示 - 仍然名称指向旧提示。

使用git push,您可以询问任何其他可以连接的Git,以更改他们对任何分支名称应指向的位置的想法。其他Git有权说&#34; no&#34;,并且有两个&#34;级别&#34;您可以在执行git push时使用:一种礼貌的请求,&#34;请执行此操作&#34;,并且更有力&#34;执行此操作!&#34;你得到--force。另一个Git仍然可以说&#34; no&#34;一个或两个这样的操作。

执行错误提交时会发生什么

你在问题​​中说你:

  

提交并将我的代码更改推送到错误的分支。

我不清楚你是否犯了错误的分支,然后按名称推送了这个分支,或者你是否致力于正确的分支,然后推错了名字或者什么。它还不清楚你的推送是否首先成功,因为你给出的唯一示例输出有一个错误:

git push -f origin 3cd4e57dcbb2a5bae350086c11d64c2f01ad4546:develop
! [remote rejected] 3cd4e57dcbb2a5bae350086c11d64c2f01ad4546 -> develop
(protected branch hook declined)

这是一种强制推动,他们仍然说'不是'#34;。这里的错误消息使用短语&#34; protected branch&#34;,这是一个GitHub功能,但它也是一个GitLab功能,可以来自几乎任何地方。所以我不确定这指的是哪个保护功能。我可以肯定的是,他们说&#34;没有&#34;,而不是为什么他们说&#34;不&#34;。而且,特别是,我无法判断是否有任何之前的推送成功。

让我们仔细看看在任何一种情况下不良提交会发生什么。让我们从这张图开始:

...--o--o--o--o   <-- master
      \
       o--o       <-- develop

如果你在分支develop上并且对工作树和git add以及git commit进行了一些更改,Git会使用新快照进行新的提交。让我们说这不是一个好的改变,让我们称之为B为Bad:

...--o--o--o--o   <-- master
      \
       o--o--B    <-- develop

如果没有将其推送到任何地方,那么根据定义,没有其他人有错误的提交。这使您可以轻松地git reset离开它:

...--o--o--o--o   <-- master
      \
       o--o--B
          ^
          |
           \--------- develop

现在我们可以简单地忘记B,假装它从未存在过,把它推到一边并开始忽略它:

...--o--o--o--o   <-- master
      \
       o--o       <-- develop
           \
            B     [abandoned]

这样放弃的提交最终会过期并且确实会被删除(通常,30天之后的某个时间已经过去 - 被放弃的提交不再受分支名称的保护,但通常仍然受到的保护reflog 条目,这些reflog条目最终会在一段可配置的时间后到期,默认为30天。)

但是,您还有另一种方法可以摆脱B。只需反过来做同样的改变。如果您向文件blah.html添加了一行,请删除该行。如果您更改了main.py中的字词,请将其更改回来。无论你做了什么,都做相反的事情。然后,进行新的提交。我们将其称为U进行撤消:

...--o--o--o--o     <-- master
      \
       o--o--B--U   <-- develop

与提交U相关联的代码 - 工作树 - 将在B之前匹配提交代码,因为B做错了,然后U撤消它。

将提交U称为还原 1 并且Git中有一个命令可以执行此操作而无需您执行任何额外的工作:{{1 }}。你给revert命令指定一个错误提交的名称 - 例如它的哈希ID,或者当git revert是分支develop上的提示提交时,只提供名称B - 并且Git指出在错误提交中发生了什么变化,并进行反向提交。

1 好吧,Git称之为&#34;还原&#34;。其他人称之为&#34;退出&#34;:在Mercurial,&#34;退出&#34;提交是develop

重置和恢复之间的区别

执行hg backout删除错误提交,执行reset添加新提交之间的区别撤消坏的,正是这样:恢复添加内容。它将错误的提交留在原地并简单地添加一个新的提交来支持错误提交的效果。

Git是围绕添加新提交而构建的

Git的所有内容都围绕着这个&#34;添加新东西&#34;的整个想法。在旧提交之上添加新提交。每个人和每件事都准备好应对这一点。 Git的一些是为了处理删除提交而构建的,但Git的所有都是为了处理添加它们而构建的。

如果没有在其他任何地方推送错误的提交,也不允许其他任何人从你那里获取它,那么显然没有其他人拥有它。这意味着很容易从您自己的存储库中删除提交,或者至少将其放弃。你的Git会处理这个问题。但是如果你已经推送了提交,或者让它被提取,那么其他人就拥有了它。如果拿走它,你必须得到他们来把它带走。如果另一个人拥有它,他可能会把它交给其他人,到现在可能有数千份副本。

在这种情况下,对其他人来说,还原更容易。他们都准备好了新的提交,如果你添加一个新的恢复提交,他们会以通常的方式提取它:没有大惊小怪,没有麻烦,每个人都做他们都在做的事情。因此,为此目的,恢复通常会更好。当然,它留下了糟糕的提交 - 但也许这甚至是好的事情。它可以作为一个教训:&#34;不要尝试这个,它是错的&#34;。 : - )

如果受保护的分支仅允许新提交

如果受保护分支上的保护允许添加 new 提交,但不允许&#34;删除&#34; (这可能更好地称为&#34;放弃&#34;)旧提交,恢复和推送将起作用。

如果保护是别的,也许什么都不会起作用。

如果可以关闭保护(暂时或其他方式),则可能允许 强制推送放弃提交,并还原。

请记住,如果你强行推动,那么你正在与其他可能正在推动的人竞争。当你强制推送时,你会告诉其他一些Git 更改某些分支点的提交ID。当你这样做时,你可能假设它现在指向你的错误提交。但是如果其他人在你的错误提交revert之后完成推送,那么使用你的提交{{1} }?你的推力将放弃他们的新提交。确保没问题 - 如果没有,找到一些方法确保它不会发生。 2

2 这是B的用武之地,但我不打算在此解决这个问题。