【问题标题】:How is it possible that I can checkout a non-existant branch in Git?我怎么可能在 Git 中签出一个不存在的分支?
【发布时间】:2021-12-31 14:45:21
【问题描述】:

关于 Git,我有很多不知道的地方。我希望了解这是如何发生的:

  • 2 个月前我有一个活动分支:feature/branch-1
  • 分支已合并到我的默认分支:develop
  • 已在远程存储库(位桶)中删除
  • 已从本地文件系统中删除:git branch -d feature/branch-1
  • git branch我没看到分支
  • 今天我惊讶地发现我可以做到:git checkout feature/branch-1
  • git branch我可以看到分支了

git 是否找出合并点(合并分支时)并检查该提交?

【问题讨论】:

  • 你确定所有遥控器中的分支都被删除了吗?请与git branch -vav联系。
  • 你还没有完成`git fetch -p`所以远程跟踪分支仍然存在于你的本地仓库中,这就是检查出来的。 (或者更准确地说,本地分支是基于跟踪分支创建的。)
  • 我以前从未使用过 prune。谢谢!

标签: git git-branch


【解决方案1】:

git checkoutgit switch(在这种情况下做同样的事情)都内置了一个特殊功能。他们处理请求切换到类似于分支名称的字符串的方式是这样的:

  1. 检查给定的字符串,例如foofeature/branch-15a73c,是否已经存在作为分支名称。如果是这样,那就是要签出的分支的名称。

  2. 检查给定的字符串,例如5a73c,是否可以转换为有效的提交哈希ID。如果是这样,那就是 commit 作为一个分离的 HEAD 检出。 (这里git checkout 会这样做,而git switch 会因致命错误而死,除非你使用--detach,在这种情况下它很好并且也会这样做。)

  3. 假设我们已经走到这一步,如果--guess 有效(见下文),请使用猜测代码。在旧版本的 Git 中,这称为“DWIM 模式”,其中 DWIM 代表按我的意思行事。 (DWIM 的悠久历史可以追溯到 1960 年代的 Lisp,甚至在我使用计算机之前:直到 1970 年代我才开始使用硬件和软件。)

--guess 选项首先在 commit ccb111b342f472d12baddbfa5b5281 中正式(并正确记录),首先在 Git 2.23.0 中发布,但由于它默认为 on 并且在此之前一直存在,所以除非您明确将其关闭,否则它需要至少 2.23 的 Git 版本。所以它几乎总是开启。

它的工作方式是扫描您自己的存储库中的每个远程跟踪名称。这些名称在git fetch 时间创建和更新,包括git pull 操作运行的大部分git fetch 操作。默认情况下,它们不会删除,除非您明确运行 git fetch --prunegit remote prune,或者在特殊情况下,您专门使用 git push 从您当前所在的远程删除分支有一个对应的远程跟踪名称。

您的远程跟踪名称是类似的名称,例如 origin/fooorigin/feature/branch-1。您不太可能拥有origin/5a73c,因为没有人会将其用作分支名称:您的远程跟踪名称是您的 Git 与其他人的分支名称的副本,而其他人会很疯狂1将其用作分支名称。但它可能会偶然发生,偶尔会出现完全由有效十六进制数字组成的四个或更多字母单词2:分支名称,如 deedeffacefaded 可能会在这里引发奇怪.

无论如何,假设我们首先进入第 3 步(--guess 代码),Git 会扫描您的远程跟踪名称。您输入的内容,例如:

git checkout feature/branch-1

当你没有feature/branch-1 分支时,所以第1 步失败; feature/branch-1 无法转换为有效的哈希 ID,因为它包含非十六进制字符,例如 t 和正斜杠;所以我们到了第 3 步。Git 现在扫描你所有的 origin/* 名称:是其中之一 origin/feature/branch-1

在这种情况下:是的,一个是。此时,Git 还将扫描所有 其他 个远程跟踪名称,例如 upstream/*,以找到 所有 个候选对象。然后所有此类候选人的列表进入最后一组测试:

  • 列表是空的吗?如果是,则猜测失败。

  • 列表是否正好是一个元素?如果是这样,这就是您希望 Git 猜测的远程跟踪名称。

  • 否则(列表中有多个条目),由于比赛之间的竞争,猜测失败,除非您使用 Git 2.19 中引入的功能),checkout.defaultRemote。此功能可让您选择“赢得”此类比赛的特定遥控器。

在这种情况下,您只得到了一个匹配项:origin/feature/branch-1。这使--guess 能够猜测,而不是:

git checkout feature/branch-1

你的意思是:

git checkout -b feature/branch-1 --track origin/feature/branch-1

这就是git checkout 所做的。 (虽然git switch-c 拼写,但git switch 在这里的行为方式相同,使用相同的控制旋钮:--guess 在命令行上,checkout.defaultRemote 用于处理模棱两可的多个匹配项。)

这里的一个潜在教训是,在您的个人 Git 配置中经常运行 git fetch -pgit remote prune 甚至将 fetch.prune 设置为 true 可能是明智之举。否则,您可能会有很多陈旧的远程跟踪名称,并且由于人是人,您为您的新功能发明的名称可能会与某人为他们的发明的旧名称发生冲突新功能。或者,代替那一课,也许要采取的方法是禁用猜测(也许使用 new-in-Git-2.30 config.guess 设置)。请注意,如果您想使用远程跟踪名称origin/foo 来创建本地分支foo,您可以输入:

git switch -t origin/foo

(隐含 -c foo 部分)。当然,这也适用于旧的 git checkout


1他们的疯狂可能有一种方法,或者他们的方法可能只是一种疯狂。 ?

2Git 允许原始哈希 ID 的最短缩写是四个字符。因此,分支名称 abc 尽管由十六进制数字组成,但绝不是提交哈希 ID。但abcd 有时是提交哈希 ID。

【讨论】:

  • 感谢您抽出宝贵时间提供有关正在发生的事情的详细分步说明。作为跟进,我执行了git remote show origin,确实我看到了refs/remotes/origin/feature/branch-1 stale (use 'git remote prune' to remove)!我只是在学习修剪...以及您提到的其他一些我需要阅读的内容...
猜你喜欢
  • 2012-09-17
  • 1970-01-01
  • 2011-01-03
  • 2019-06-26
  • 1970-01-01
  • 1970-01-01
  • 2011-08-28
  • 2016-03-30
  • 2015-02-06
相关资源
最近更新 更多