Git:用一个分支修复回购及其在不同分支上的远程

Git: Fix repo with a branch and its remote on different branches

我不知道这是怎么发生的,但我有一个 git 存储库处于以下状态:

A--B--C--D  master
    \
     E--F   origin/master

提交 EF 此时已过时;他们在 B 上所做的更改无关紧要,如有必要,可以将其删除。

我只保留 origin 远程作为我的代码的备份,没有其他人克隆它,所以我可以使用我需要的任何暴力破解。

清理它的最干净的方法是什么?

I only keep the origin remote as a backup for my code, and nobody else has cloned it, so I can use whatever brute force I need.

在这种情况下,git push --force origin master 就足够了。 非常确定 你可以在你的存储库中使用 origin/master,并且 origin 上的(单独的)Git 存储库中的 master ], no-longer-able 找到提交 F.

(您自己的存储库会在 reflog 名称 [=235 下记住提交 <code>EF 一段时间=]@{number},因此只要您保持自己的存储库完好无损,如果它们毕竟没有过时,您仍然可以取回它们。然而,在某些时候它们真的会被删除。server 可能根本没有 reflogs 并且它的提交副本 EF 可能会相对快速地消失—例如,以秒、小时或几天为单位。)

这是如何工作的以及为什么工作

每个 Git 存储库都有 自己的 分支名称,semi-private 到特定的 Git 存储库。在您调用 origin 的机器上有一个存储库,它有一个分支名称 master.

你的remote-tracking名字,1origin/master,是你的Git'这是记住某些 other Git 的 branch 名称的方法。在这种情况下,您的 origin/master 是您的 Git 对 originmaster 的记忆。

当你的 Git 通过 git fetchgit push 呼叫他们的 Git 时,两个 Git 开始对话。 fetch 和 push 略有不同,但是你可以使用 git ls-remote 来观察 git fetch 本身的观察者。只需 运行 git ls-remote origin(或保留 origin,因为默认值通常是 origin):您的 Git 会溢出 other Git 的分支、标签和它列出的其他名称。这是 git fetch 的第一部分:您的 Git 打电话给他们的 Git 并获得此列表。

当您使用 git fetch 时,您的 Git 使用此列表询问他们的 Git 任何提交和他们拥有的其他内部 Git objects ,你没有,你的 Git 想要。例如,如果您还没有提交 F,而您的 origin/master 记住了提交 BE,并且他们将提交 F 作为他们的master,你的 Git 会说:我想提交 F please 他们会将其添加到提交列表中发送。然后他们会提供提交 E——一个 Git 必须 提供每个提交的 parent——你的 Git 要么说 不,谢谢,我已经有了 是的 (在这种情况下,他们会提供 BE' s parent,等等)。

然后,他们的获取过程会捆绑将这些提交提交给您的 Git 所需的任何内容——这是您将看到 counting objectscompressing objects 的地方——并将其发送过来。您的 Git 扩展了这些,以便您拥有一组适当的提交及其数据,然后根据存储在 their 中的哈希 ID 创建或更新您的 origin/master master:现在您肯定已经提交了 F,您的 Git 可以让您的 origin/master 指向它。

因此,如果您 运行:

git fetch origin

在任何时候——这是命令的 "get me everything" 形式——你的 Git 将调用他们的 Git 并获得初步的 "everything" 列表。两个 Git 会从那里弄清楚你的 Git 需要什么,然后把它交给你的 Git。然后,您的 Git 将根据 all 分支名称创建或更新 all 您的 remote-tracking 名称。

当你使用 git push 时,它与 Git 接近 fetch 的对立面——这对是 pushfetch 而不是比 pushpull 由于历史错误2——发生类似的过程,但有两个关键区别:

  • 您的 Git 发送,而不是接收。 (您的 Git 仍然选择要发送的内容,并且 "must offer parent if sending commit" 仍然有效,依此类推,但是对于 git fetch,他们的 Git 已发送而您的 Git 已收到.)

  • 最后,您的 Git 不是 Git 更新某种 "remote-tracking name",而是询问(礼貌地)或命令(强制地)他们的 Git 到 设置 它的一些 分支 名称。3

如果您要求或命令他们设置他们的 master,并且他们服从,您的 Git 现在知道他们的 master 已设置为您提供的哈希 ID,因此您的Git 现在更新您的 origin/master remote-tracking 姓名。

当您使用 ask-type git push origin master 时,您的 Git 会发送您拥有而他们没有的提交——例如 CD——然后礼貌地问他们:拜托,如果没问题,请将你的 master 设置为指向提交 D? 他们会说 不!如果我这样做,我会忘记提交 F! 这个错误作为 Git 行话 rejectednon-fast-forward,但这只是意味着他们拒绝了,因为那样会丢失提交 F,因此也会丢失 E

但这正是您希望他们做的。所以哟只需要向他们发送一个强有力的命令:设置你的 master! 他们仍然可以拒绝——例如,如果你没有权限——但你当然有权限所以在这种情况下,他们会服从,并失去提交 F.

git push有两种强制模式:git push --force是古版本,git push --force-with-lease是more-modern(因为Git1.9左右)变体。它们之间的区别是 --force 只是说 set,但是 --force-with-lease 说: I think your master is _____.如果是这样,请将其设置为 _____ 并告诉我您做到了;如果不是,告诉我我错了,毕竟不要设置你的 master 第一个空白填入你的 Git 的 origin/master 值:你的Git 对他们 Git 的 master 的记忆。这样一来,您就可以确保让他们扔掉的正是您认为他们会扔掉的东西。

如果您是其他 Git 中唯一的 用户,则无需 --force-with-lease 添加的注意事项:您的想法是正确的, 正确的,根据定义。但如果您担心自己可能会健忘(例如,如果您使用两台不同的笔记本电脑并且不记得哪一台是最新的),您可以使用更高级的 check-first-then-force --force-with-lease。使用 --force-with-lease 永远不会 错误 ,除了旧的 --force 是历史默认值之外,--force-with-lease 确实应该是默认值。


1Git 调用这些 remote-tracking 分支名称 但几年前我决定 branch 在这里是无用的混乱,会造成一些额外的混乱,所以我现在只称它们为 remote-tracking names.

2通常,在获取提交之后,您希望整合那些提交。这就是 git pull 所做的:它 运行s git fetchget 提交,然后 运行s 第二个 Git 命令到整合 那些提交。在早期,这似乎是正确的自然粒度,所以 git pull 是与 git push 相反的 user-facing 命令。

不过,事实证明,将获取操作与集成操作分开通常很重要,例如,在两者之间插入某种 inspect 操作,以便选择正确的积分形式。 Mercurial 做对了,Git 做错了:在 Mercurial 中,hg pull 的意思是 Git 对 git fetch 的意思,你必须添加一个单独的操作,或者一个额外的标志, hg pull 使其也执行集成步骤。

3您可以选择您要求他们设置的名称,这样您就可以让您的 Git 请求或命令设置标签名称,或其他类型的名字,但一般来说,这里通常的情况是 "branch name".