origin/HEAD 是如何设置的?

205
我设置了一个分支来跟踪origin中的一个引用。使用git checkout <branchname>切换到该分支,git status将显示该分支与origin相比有多少提交落后或领先。但我很惊讶origin/HEAD仍然指向origin/master而不是origin/<branchname>
因此我的问题是,在什么情况下会移动origin/HEAD?
编辑:
感谢关于如何移动origin/HEAD的答案,但我对"自然"移动它的方式感兴趣,而非我明确告诉它要这样做的方式。
例如,当我切换分支时,git会使HEAD指向我正在检出的分支,因此我很惊讶origin/HEAD没有以同样的方式移动。

请注意,此问题涉及远程位置上的本地符号引用,例如 refs/origin/HEAD。这不涉及如何设置存储库自身的符号引用 HEAD - clacke
7个回答

245
请注意,您的问题有些误解。origin/HEAD 表示远程默认分支,即您称为 origin 的远程存储库中的 HEAD。当您在仓库中切换分支时,并不会影响到它。远程分支也是如此;您可能在仓库中拥有 master 和 origin/master,其中 origin/master 表示远程存储库中 master 分支的本地副本。
只有当您或其他人实际在远程存储库中更改 origin 的 HEAD 时,它才会发生变化,这基本上永远不会发生——您希望公共 repo 的默认分支保持恒定,保持在稳定分支(可能是主分支)。origin/HEAD 是表示远程存储库中 HEAD 的本地引用。(其完整名称为 refs/remotes/origin/HEAD。)
我认为以上回答了你实际想知道的内容,但是为了回答你明确提出的问题...当你克隆一个仓库时,origin/HEAD会自动设置,就这样。奇怪的是,像git remote update这样的命令并不会设置它 - 我相信唯一的改变方法是手动更改它。(通过改变,我指的是指向一个不同的分支;显然,如果那个分支发生变化,它所指向的提交也会发生变化,这可能会在fetch/pull/remote update时发生。)

编辑: 下面讨论的问题已在 Git 1.8.4.3 中得到修正;请参见此更新


不过,有一个小小的警告。HEAD是一个符号引用,指向一个分支而不是直接指向提交,但是git远程传输协议只报告引用的提交。因此,Git知道HEAD和所有其他引用指向的提交的SHA1值;然后它必须通过找到指向相同提交的分支来推断HEAD的值。这意味着,如果两个分支恰好指向那里,就会存在歧义。(我相信它会优先选择主分支,然后回退到按字母顺序排列的第一个分支。)你可以在git remote show origin的输出中看到这个问题:

$ git remote show origin
* remote origin
  Fetch URL: ...
  Push  URL: ...
  HEAD branch (remote HEAD is ambiguous, may be one of the following):
    foo
    master

奇怪的是,尽管以这种方式打印的 HEAD 概念将随远程发生变化而改变(例如,如果删除了 foo),但它实际上不会更新 refs/remotes/origin/HEAD。这可能会导致非常奇怪的情况。假设在上面的示例中,origin/HEAD 实际上指向 foo,并且然后删除了 origin 的 foo 分支。我们可以这样做:

$ git remote show origin
...
HEAD branch: master
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/foo
$ git remote update --prune origin
Fetching origin
 x [deleted]         (none)     -> origin/foo
   (refs/remotes/origin/HEAD has become dangling)

因此,即使远程展示知道HEAD是主分支,它也不会更新任何内容。过时的foo分支被正确修剪,HEAD变成悬空状态(指向不存在的分支),而且仍然没有更新为指向主分支。如果您想解决这个问题,请使用git remote set-head origin -a,它会自动确定origin的HEAD如上所述,然后实际设置origin/HEAD以指向适当的远程分支。

@jefromi 很棒的答案!只是一个备注:你写道HEAD 是一个指向分支而不是直接指向提交的符号引用[...], 但是考虑到完整性,值得提及“分离 HEAD 状态”。 - jub0bs
2
@Jubobs 谢谢!如果我的答案需要更新,请随意编辑它 - 这肯定会节省人们的时间,让他们阅读一份简要的关于事情实际运作方式的摘要,而不必整理两年前的真相和现在的真相。 - Cascabel
2
已经读了至少5次,但仍然一点也不理解。 - krb686
30
git remote set-head origin -a 对我有用。 - Shujito
3
顺便说一句,由于某种原因,许多代码库正在更改远程代码库中的“HEAD”(通常更改为“main”)。据称原因是有些人认为“master”这个词很冒犯。(顺便说一句,如果给出该解释的人自己似乎感到被冒犯了,而不是代表其他人感到被冒犯,我会更信服。但无论如何,这是他们的代码库。) - BCS
显示剩余2条评论

117

这是您在本地存储库中的所有者设置。请像这样更改它:

git remote set-head origin some_branch

当你将分支推送到远程仓库后,origin/HEAD 将指向你的分支而不是 master 分支。这只会影响你自己的仓库,不会影响其他人的仓库。除非在远程仓库上进行了特殊配置,默认情况下 origin/HEAD 指向 master 分支。

git-remote 的手册 提供了关于此的详细信息。

补充说明:若没有特别设置,origin/HEAD 不会“移动”,除非像重命名主分支这种情况,但我认为这并不算“自然”移动。因此,从本质上讲,它是不会自动移动的。


1
这里的编辑重点并不完全正确。如果您从不在主分支上的本地副本克隆,则它也可能会更改。 - mphair
我不认为克隆是“移动”,但我猜我们在这个问题上可能有不同意见 :) - eis

40

“organically”是指什么能够移动origin/HEAD?

  • git clone会将其设置为HEAD在origin的位置
    • 它作为git clone克隆后要检出的默认分支

origin/HEAD代表什么?

  • 对于裸仓库(通常是“服务器上”的仓库),它作为默认分支的标记,因为git clone以这种方式使用它
  • 对于非裸仓库(本地或远程),它反映了仓库的当前检出状态

是什么设置了origin/HEAD?

  • git clone获取并设置它
  • 如果git fetch像其他引用一样更新它的话,那就很合理,但它没有
  • git remote set-head origin -a获取并设置它
    • 用于更新本地关于远程“默认分支”的知识,很有用

琐事

  • origin/HEAD也可以设置为任何其他值,而无需联系远程:git remote set-head origin <branch>
    • 我看不出其他用例,除了测试
  • 不幸的是,没有任何工具可以设置远程的HEAD
  • 较旧的git版本不知道远程的HEAD指向哪个分支,只知道最终有哪个提交哈希:因此它随便选择一个分支名称指向相同的哈希
  • 查看@MichaWiedenmann的评论:origin/HEAD在您命名远程时用作快捷方式到分支

我已经失去了对 origin/HEAD 的引用,你的解决方案帮了我很大忙。谢谢! - java_dude
我不同意 git fetch 更新它,因为它允许配置一个(本地)快捷方式。引用文档:“对于远程仓库来说没有默认分支是不需要的,但允许使用远程名称代替特定的分支名称。”如果远程更改会更新本地配置的快捷方式,那将是奇怪的。 - Micha Wiedenmann
1
@MichaWiedenmann 为什么这会是一个本地配置的快捷方式?对于本地配置的快捷方式,“origin/HEAD”是一个糟糕的名称。而且,“git clone”使用远程名称作为“本地配置分支”的默认名称,也与之相矛盾。在非裸库中,甚至使用远程的当前“HEAD”也没有意义。 - Robert Siemer

13

免责声明:这是对Cascabel的答案进行更新的,我写这篇文章是为了帮助那些好奇的人节省时间。

我在Git 2.0.1中尝试复制(Cascabel在他的答案中提到的)remote HEAD is ambiguous错误信息,但徒劳无功。因此,我进行了一些挖掘(通过克隆https://github.com/git/git并搜索日志)。以前情况是这样的:

Determining HEAD is ambiguous since it is done by comparing SHA1s.

In the case of multiple matches we return refs/heads/master if it
matches, else we return the first match we encounter. builtin-remote
needs all matches returned to it, so add a flag for it to request such.

(提交记录 4229f1fa325870d6b24fe2a4c7d2ed5f14c6f771,日期为2009年2月27日,使用git log --reverse --grep="HEAD is ambiguous"命令找到)

然而,涉及的歧义已经解决:

One long-standing flaw in the pack transfer protocol used by "git
clone" was that there was no way to tell the other end which branch
"HEAD" points at, and the receiving end needed to guess.  A new
capability has been defined in the pack protocol to convey this
information so that cloning from a repository with more than one
branches pointing at the same commit where the HEAD is at now
reliably sets the initial branch in the resulting repository.

(提交记录 9196a2f8bd46d36a285bdfa03b4540ed3f01f671,时间为2013年11月8日,使用指令 git log --grep="ambiguous" --grep="HEAD" --all-match 找到)

编辑(感谢torek):

$ git name-rev --name-only 9196a2f8bd46d36a285bdfa03b4540ed3f01f671
tags/v1.8.4.3~3

这意味着,如果您使用的是 Git v1.8.4.3 或更高版本,则不应遇到任何模糊的远程HEAD问题。


1
根据git源代码中的标签,此修复程序适用于git版本1.8.4.3及更高版本。 - torek

10
记住有两个独立的 git 仓库。一个是本地仓库,存储着你的代码,另一个则在远程某处运行。
当你切换分支时,HEAD 指向当前所在的分支,这都是发生在你的本地 git 仓库中。而不是在远程仓库中,远程仓库可能由另一位开发者拥有,或者位于你的办公室的服务器上,或者在 GitHub 上或文件系统的其他目录中等等。
你的计算机(本地仓库)没有更改远程 git 仓库上的 HEAD 指针的必要。例如,该远程仓库可能由不同的开发人员拥有。
还有一件事,你的计算机所称的 origin/XXX 是你的计算机在上一次拉取时对远程仓库状态的理解。
那么什么会“自然地”更新 origin/HEAD 呢? 远程 git 仓库上的活动将会导致它更新,而不是你的本地仓库。
人们已经提到:

git symbolic-ref HEAD refs/head/my_other_branch

通常,这是在团队使用的共享中央 git 仓库上执行的命令。它将在远程计算机上执行,你会看到这是远程 git 仓库上的活动。

1
抱歉,如果我有点重复。我只是想指出git是一个分布式版本控制系统,因此这两个仓库是独立的。 - Pablo Maurin
'git symbolic-ref HEAD refs/head/my_other_branch' 这正是我需要的!这样我就可以将我的远程主分支重命名为main,然后使用“git push origin -d master”删除该引用。 - 00prometheus

2

从git CLI运行以下命令:

原始答案: "最初的回答"

# move to the wanted commit
git reset --hard <commit-hash> 

# update remote
git push --force origin <branch-name> 

这会将分支推送到所需的提交,但实际上并不会更改远程 HEAD。 - Charles Duffy

1
它由git clonegit remote add -m master $url设置。除此之外,您需要手动设置它。我还发送了一些补丁以便通过git fetch更新它(取决于某些配置)。在我看来,那将是最自然的更新方式。不幸的是,它们尚未合并。

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