首页 > 解决方案 > 错误合并后如何恢复到远程的旧提交?

问题描述

我使用 BitBucket 作为我的远程仓库。我的一位培训团队成员错误地将另一个分支推入master. 现在,这就是我的仓库的样子。

在此处输入图像描述

好吧,他把事情搞得一团糟。我想将此恢复为4f9ff具有“添加了新模型类”消息的提交。在我的电脑中,最后一次提交master也是4f9ff

仅供参考,这就是我的本地仓库的样子。

在此处输入图像描述

如何将远程仓库恢复为提交4f9ff

我试过git push --force origin 4f9ff27:master; 了,它在远程什么也没做。

标签: gitbitbucketgit-mergegit-push

解决方案


您可以使用git push --force您尝试过的命令,但您需要稍有不同:

git push --force origin 4f9ff27:master

(您尝试过的)必须改为阅读:

git push --force origin 4f9ff27:refs/heads/master

但是您可以更简单地运行:

git push --force origin master

在你这样做之前,请继续阅读。请注意,Bitbucket 服务器可能仍然拒绝执行此操作,这也取决于您授予自己的权限。

首先,Git 真的是关于提交。这些哈希 ID,无论是缩写(如4f9ff27)还是完整拼写(完整拼写为 40 个字符),都是提交的“真实名称”。这些“真实姓名”在每个Git 存储库中都有效,只要 Git 存储库确实有 commit

这些大而丑陋的哈希 ID 的问题在于它们对人类毫无用处。我们不会去记住今天4f9ff27最新的master提交,然后明天记住fadbabe最新的提交,然后在星期二记住现在dadcafe或其他什么。我们只想说master并获得最新的提交master,不管是什么

这就是分支名称的含义和对我们的作用。每个克隆中的名称 master(每个都有自己的名称)是 上的“最新提交” 。有你的克隆,在你的笔记本电脑上,有你的,你的团队成员的克隆,在他的笔记本电脑上,有他的——他几乎可以肯定使用的是他的最新版本——Bitbucket 上有一个。mastermaster master4af43...

Bitbucket 上的 Git 存储库在任何方面都没有什么特别之处,除了一个:你和这个人,也许还有更多的人,认为它很特别。但实际上,您自己的每个 Git 存储库都与 Bitbucket 上的一样好,甚至可能更好,因为其中包含您的提交。Bitbucket 之所以特别,只是因为您是这样想的

所以,当你这样想的时候要小心。请记住,您的提交是的,您的分支名称是您的,您自己的 Git 存储库是它自己的东西。

当您运行git pushgit fetch和/或时,您所做git pull的是连接您的 Git 软件,将您的 Git 存储库与另一个 Git-software-and-repository 对一起使用。将您的 Git(您的软件和存储库作为一对)连接到他们的 Git 后,您现在可以:

  • 给他们你所做的任何新的承诺,但他们没有;和
  • 要求他们设置一些分支名称。

这是一个git push操作。

或者,您可以:

  • 从他们那里获得他们拥有的任何你没有的新提交。

这是一个git fetch操作。这不会设置您的任何分支名称,因为git fetch 永远不会触及您的任何分支

不更新任何分支名称的坏处......好吧,名称是我们找到提交的方式,因为我们(人类)不擅长随机看起来像4f9ff27. 因此,当从他们git fetch那里获得提交时,您的 Git 将复制他们的分支名称,但会更改它们。您将获得远程跟踪名称。如果他们有,并且您调用他们的 Git ,您的 Git 将创建或更新您自己的。masteroriginorigin/master

换句话说,这里有点不对称。 当您使用 时,您会git fetch它们那里获得新的提交,然后您的 Git 会创建或更新远程跟踪名称。这不会以任何方式干扰任何分支名称。(您从 Bitbucket 获得的新提交可能是因为有人使用了,所以它们可能是由几个不同的人提交的。)git push

但是,当您使用时,git push将新提交发送给他们,然后您要求他们(Bitbucket)创建或更新他们的分支名称之一。

分支名称以refs/heads/

通常,你会这样做:

git push origin master

或者:

git push origin feature/xyz

管他呢。此git push请求使用您身边的分支名称 <code>master 或—来查找要发送的提交。然后你的 Git 将这些提交发送到 Bitbucket。(在这个阶段,他们有机会直接拒绝提交。我不使用 Bitbucket,但值得注意的是,如果任何一个提交中的任何一个文件超过 100 MB,GitHub 将在此时拒绝传入的提交。大多数情况下不过,这还没有真正影响任何事情。)然后——一旦提交完成——你的 Git 会说:现在,如果可以,请将你的名字 _______(填写名称)设置为 ______(填写哈希 ID)。你的 Git 在这里发送的名称是feature/xyz您用于选择要发送的提交的相同分支名称

在 Git 内部,分支名称以refs/heads/. 这意味着你master是真的refs/heads/master远程跟踪名称,例如origin/master开头refs/remotes/,所以origin/master真的是refs/remotes/origin/master. 标签名称存在于 中refs/tags/,例如refs/tags/v2.1. 大多数情况下,您不必考虑这一点,因为您只需在自己身边使用分支或标签名称 for git push,而根本不使用任何东西 for git fetch。对于 fetch 情况,Git在两边使用相同的名称。

但是,当您使用冒号语法4f9ff27:<name>时,您是在告诉您的 Git:使用左侧的内容来查找要发送/推送的提交,但要求他们在右侧设置名称。当你这样做时,你必须拼出全名。所以这就是4f9ff27:master不够的原因。

当他们收到这个表格的礼貌请求时,如果可以,请设置一个分支名称,他们会检查它是否可以。如果以下情况在普通 Git 中是可以的:

  • 这是一个新分支,或者
  • 它只会导致新提交在该分支上变得可查找。

当您需要使用git push --forcegit push --force-with-lease类似时,这是因为您的非强制推送未通过第二次测试:您正在告诉另一个 Git 设置分支名称,以使某些提交无法找到他们的存储库中使用您告诉他们设置的分支名称。

在你这样做之前,如果这些提交是有价值的,那么确保有人仍然可以找到它们是个好主意。谁能找到它们 并不重要,但需要有人能够做到这一点。

如果所有这些提交肯定可以通过其他分支名称找到,那么您就可以开始了,并且可以继续使用git push --force master. 如果没有,您应该确保其他人保留他的提交,和/或在 Bitbucket 存储库中设置一些其他分支名称,以便 Bitbucket Git 将在其他分支名称下记住这些提交。例如,您可以运行:

git fetch origin
git push origin origin/master:refs/heads/bob

使(Bitbucket)refs/heads/bob上的名称origin记住他们master在您git fetch运行时拥有的提交哈希 ID。1 现在你可以git push --force-with-lease origin master2


1您在此处git fetch运行时的短语是经过深思熟虑的:如果有人积极推送到 Bitbucket,您git fetch将获得git fetch运行时的提交。有可能几秒甚至几毫秒之后,有人添加了十个新的提交。你还没有它们!有很多方法可以解决这个问题,但在较小的商店中,有时有人会大喊(和/或在 Slack 上发帖)“不要推动掌握,我们正在修复它”,然后几分钟后“都修好了,你可以再推”。

2--force-with-lease选项是--force. Plain--force对 Bitbucket Git 说:立即设置!refs/heads/master4f9ff27--force-with-lease选项说: 我认为你refs/heads/master是 ______(通过 origin/master 填空);如果是这样,请将其设置为4f9ff27现在! 这处理了我在脚注 1 中提到的那场比赛:如果有人了,你有机会再试一次。


一个简短的关于git pull

很多人喜欢用 来git pull作为 的反义词git push,但它不是(相反,即)。这git fetch是一个接近对立面的人。

什么git pull是:

  1. 运行git fetch;然后
  2. 运行第二个 Git 命令。

第二个命令通常是,git merge尽管您可以让它运行git rebase,即使默认情况下也是如此。第二个命令的要点很简单:git fetch永远不会影响您的任何分支,但通常,您运行的原因git fetch是为了影响您的某些分支。这需要额外的命令。

将 fetch 和一个附加命令拉在一起。如果您想要或需要多个命令(我经常这样做)或不需要fetch其他命令,那么首先运行,然后运行命令(复数)或不执行任何操作更有意义。在这种情况下,您希望您的 Git 更新您的 master. 你希望你的 Git 让你master一个人呆着。所以你想要git fetch,不是git pull

受保护的分支

Git,在它的基础上,没有分支保护。托管 GitHub、GitLab 和 Bitbucket 等网站增加了受保护分支的想法。

保护的分支限制谁可以使用分支名称做什么。确切的细节取决于托管服务。查看 Bitbucket 为该名称提供的保护master。您可能能够阻止除您自己之外的任何人直接向它推送。您可能可以阻止包括您自己在内的任何人直接推送到它,即使使用--force,所以要小心这些设置,但如果您是管理员,即使您不小心已经删除了自己的权限,您也应该能够授予自己权限.

结论

您尝试做的强制推动大部分是正确的。确保其他人的提交不会完全丢失——例如,将它们保存在您自己的笔记本电脑上,git fetch并使用您自己的分支名称,和/或在适当的情况下在 Bitbucket 上创建另一个分支。如果您确定其他提交通过其他分支名称获得(即,其他人没有进行合并然后删除),则可以跳过此步骤。

然后,一旦你确定它是安全的,git push --force。如果您已禁止 Bitbucket 允许任何强制推送,即使是您自己,请允许自己强制推送至少足够长的时间以完成此操作。


推荐阅读