为什么 'remotes/origin/HEAD' 有时会映射到 'origin/master' 而有时不会?

3

在我的一些存储库中,我看到...

> git branch --all
* master
  remotes/origin/master

我看到一些人对IT技术了解很多,而另一些人则不是这样:

> git branch --all
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

前者对我来说是正确的,后者让我感到不安。这是怎么回事?
我之前将另一个代码库映射到了一个额外的 "common" 分支,设置可能引起了问题。现在已经删除了这个额外的分支,但是后一个代码库中的奇怪设置仍然存在。两个代码库中都没有额外的远程库,只有标准的 "origin"。
在我的理解中,在第一个代码库中,"remotes/origin/master" 是 "remotes/origin/master -> origin/master" 的缩写。只有当本地分支和远程分支在某些方面上不同,才需要明确说明映射关系?因此,似乎有这个奇怪的 "remotes/origin/HEAD -> origin/master" 映射(如果这是一个称呼的话),它似乎正在起作用?
2个回答

6

Mark Adelsberger的答案讲述了如何获取它们(git clone会生成它们)。但是我们更加通俗易懂地来看待分支名称,以及Git所谓的引用符号引用

你已经知道分支名称相对简单:它们只是像masterdevelop这样的字符串。而且,由于git branch --all输出,你也知道你拥有Git有时称为远程跟踪分支名称,例如origin/master。我更喜欢将其称为远程跟踪名称,以避免过多使用"branch"这个词。

你可能不知道的是,这两者都只是Git如何将所有内容汇集在一起的专业形式。像 master 这样的常规分支名称实际上是一个称为 refs/heads/master引用。像 origin/master 这样的远程跟踪名称是一个称为 refs/remotes/origin/master 的引用。标签也只是以 refs/tags/ 开头的引用。
如果你运行git branch -r,这将只列出远程跟踪名称,你会看到origin/master而不是remotes/origin/master。这是因为Git在剥离refs/remotes/的部分时并不总是一致的。(我不知道)Git在一般情况下喜欢缩小这些东西以使其更易管理。由于所有引用始终都以refs/开头--这是Git如何定义它们--通常你可以省略refs/部分,通常还可以省略refs/heads/refs/remotes/refs/tags/
一个引用为你做的事情——其中非常重要的一部分——是保存那些你在例如git log输出中看到的巨大丑陋的Git哈希ID之一。Git内部需要这些哈希ID,但它们对于普通人来说相当难以使用,因此Git给我们提供了可以用来记住这些ID的名称。
但是有一种特殊情况允许任何引用,尽管最好将其保留给使用单词HEAD的引用。任何引用都可以是符号性引用。引用不包含一个巨大丑陋的哈希ID,而是包含另一个引用的名称。然后,Git会将该名称转换为存储在其他引用中的哈希ID。
因此,如果refs/remotes/origin/HEAD包含名称refs/remotes/origin/master,则您可以在任何可以使用origin/master的地方使用origin/HEAD。这并没有太大帮助 - 至少我发现使用全部大写的HEAD更难输入 - 但是Git添加了另一个特殊情况:如果您仅使用origin名称,在Git“需要”哈希ID的位置,Git将注意到origin对应于refs/remotes/origin/,然后看到refs/remotes/origin/HEAD存在。Git将读取它并查看refs/remotes/origin/HEAD是指向refs/remotes/origin/master的符号引用。Git将读取那个并查看哈希ID - 现在Git将知道您想要的哈希ID。
因此,如果origin/HEAD(或refs/remotes/origin/HEAD)“指向”origin/master,那么你可以在一些地方写origin,而不是写出origin/master。你可以使用git remote命令来操纵这些特殊的远程跟踪HEAD名称,具体来说就是使用它的set-head子命令。我自己从未真正关注过这些内容——当我想要的是origin/master时,我只需键入该内容,并忽略与这些远程跟踪名称相关的符号HEAD。但如果你喜欢它们,它们就在那里供你使用。

1我在这里说“通常”是因为有一个特殊情况:如果您不小心将名称foo作为分支名称(refs/heads/foo)和标签名称(refs/tags/foo),那么仅仅说foo就会成为一个问题。一种解决方法是拼写出heads/footags/foo,甚至是refs/heads/foo。关于这个问题以及更多细节,请参考git修订文档


感谢您详细的回答,非常感激。虽然我还不能完全理解发生了什么,但我对此有了稍微更好的理解,至少似乎没有出现任何问题! - undefined
1
是的,这很正常;在我(有偏见的)看法中也是无用的,可以忽略不计。(在早期的Git 1.6左右版本中,我曾经取消设置它们,但实际上并没有真正的必要。) - undefined
我正在重新阅读你的回答,以充分理解它。origin\HEAD -> origin\master 这个是一种符号链接吗?对我来说,这似乎毫无意义。 - undefined
如果你熟悉符号链接的话:是的,实际上,符号引用的最初实现是通过符号链接完成的。为了适应Windows系统,它被更改为“带有特殊文本内容的普通文件”。 - undefined
2
同样的 git remote 命令中包含了所有的繁琐操作:git remote set-head origin --delete 的意思是 "删除 origin 的符号化 HEAD"。如我之前提到的,我曾经这样做过(在需要手动干预的时候,git remote set-head 还不存在),但实际上并没有什么意义。 - undefined
显示剩余2条评论

2
该行代码remotes/origin/HEAD -> origin/master告诉您origin分支检出的情况,这也是新克隆中默认检出的分支。
(可能应该说,当本地的remotes/origin/HEAD引用最后一次更新时,origin分支检出的情况。在我的测试中,只有在初始创建克隆时才会更新此引用。可能有我没想到的强制更新的方法。)
如果您有一个repo,其中没有看到这一行,则可能意味着在克隆时未检出任何分支 - 例如,如果您克隆了一个空存储库。

git remote命令可以让你设置remotes/<name>/HEAD。虽然在Git中并没有太多地方使用它(主要用途是在解析修订名称时,Git会通过注意到remotes/<name>/HEAD的存在并将其解析为链接目标来解析哈希ID),但它仍然是一个重要的功能。 - undefined
谢谢你的回答。我还是有点困惑,但看起来我没有做错什么,也没有出现任何问题,所以没关系。 - undefined
我收回之前的话。现在一切都有点明白了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接