为什么在检出最新的提交后,我的HEAD变成了Detached?

7

最近,我在一个Git仓库中工作时,想要查看旧提交(68cce45)的代码,因此我执行了以下操作:

git checkout 68cce45

浏览了一下更改后,我想返回到当前版本的仓库并继续工作。由于2bcfd11是最近一次提交,所以我执行了以下操作:

git checkout 2bcfd11

我随后做了些更改并执行了。
git add *

然后

git status

这给了我一个警告:HEAD分离于2bcfd11

我感到困惑。如果我检出的最后一个提交是几个版本之前的,我可以理解为什么我会处于"分离的HEAD状态"。但是,由于我检出的最后一个提交是存储库中最新的版本,那么我为什么会处于分离的HEAD状态呢?此时,难道HEAD不是指向存储库的"顶部"吗?


不要忘记在Git 2.23中使用新的git restore命令。请参见下面的我的答案:不会出现分离的HEAD! - VonC
4个回答

9

我为什么会处于游离状态(detached HEAD)?

因为你检出了一个提交而不是一个分支。检出任何提交——你就会处于游离状态。

现在 HEAD 指向仓库的“顶部”了吗?

git 不知道它是否是顶部。你需要通过检出一个分支来告诉 git

git checkout master

现在,git 知道它是已知分支的最新提交。这解决了 "detached HEAD" 问题。

值得注意的是,分离的 HEAD 状态并不一定是问题。如果您想要尝试特定提交,检出整个分支并没有帮助,这时通常会检出一个提交。然而,开发人员可能仍然会对分离的 HEAD 消息感到困惑。 - Brandon Essler
@BrandonEssler 无论如何你都不能检出整个分支。分支是指向提交的指针,因此 git checkout master 并不会检出整个分支 master,它只会检出由 master 指向的提交。 - phd
当然,您是正确的。但那只是语义学问题。我表达得不够清楚,但观点仍然成立。如果您正在检查特定提交,则希望使用该提交;而不是分支指向的提交。 - Brandon Essler

2
稍微补充一下 phd's answer:在Git中,HEAD是一个非常特殊的名称,全大写拼写如此1。它可以被附加(到分支名称)或者分离。无论哪种情况,Git都能告诉你正在使用哪个提交
git rev-parse HEAD

将会打印出一些哈希 ID。但只有当 HEAD 附着在分支名上时,Git 才能告诉你正在使用哪个分支名。
git rev-parse --symbolic-full-name HEAD
git symbolic-ref HEAD

两个命令都会给你当前分支的名称(前缀为refs/heads/),如果你在一个分支上的话。如果你处于分离 HEAD 模式下,第一个命令只会打印出HEAD,而第二个命令则会产生错误。
$ git checkout --detach master
HEAD is now at 7c20df84bd Git 2.23-rc1
Your branch is up to date with 'origin/master'.
$ git rev-parse --symbolic-full-name HEAD
HEAD
$ git symbolic-ref HEAD
fatal: ref HEAD is not a symbolic ref

许多形式的git checkout都会分离HEAD。其中一些形式会附加它。使用git checkout branch-name将其附加,而如上所示,您可以添加--detach以确保它变成或保持分离状态。

使用诸如7c20df84bd之类的原始哈希ID始终导致分离的HEAD,即使有一个或多个分支名称标识此特定提交。

请注意,您可以拥有尽可能多的分支名称,这些名称全部标识相同的提交:

$ for i in m1 m2 m3; do git branch $i master; done
$ git checkout m1
Switched to branch 'm1'
$ git rev-parse HEAD
7c20df84bd21ec0215358381844274fa10515017
$ git checkout m2
Switched to branch 'm2'
$ git rev-parse HEAD
7c20df84bd21ec0215358381844274fa10515017

如果我明确地检出 7c20df84bd21ec0215358381844274fa10515017,那么你想让 Git 使用以下四个名称中的哪一个——m1m2m3master?但它不使用它们中的任何一个:如果你想要它使用一个名称,你必须自己提供一个名称。
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

之后,我们可以删除多余的名称,以便提交7c20df84bd21ec0215358381844274fa10515017仅位于master的顶端,并且不再同时位于四个分支的顶端。
$ for i in m1 m2 m3; do git branch -d $i; done
Deleted branch m1 (was 7c20df84bd).
Deleted branch m2 (was 7c20df84bd).
Deleted branch m3 (was 7c20df84bd).

记住,HEAD 有两个功能。它可以找到当前的 分支(名称),如果 HEAD 被分离,则无法找到;它还可以找到当前的 提交2 从 Git 得到的答案取决于你提出的问题:你想知道 分支名称 还是想知道 当前提交哈希值


在某些系统上,有时可以使用小写字母拼写head,并获得相同的效果。然而,在添加工作树中,这种方法开始神秘地失败了。最好坚持使用全大写的HEAD,或者如果打字太麻烦,单个字符@具有相同的特殊含义。
这也可能失败,但只有在特殊状态下才会出现。您在一个新的、完全空的仓库中处于这种状态,在这种状态下,您当前的分支名称是master,但分支master本身尚不存在。这是因为分支名称必须包含某个现有的有效提交对象的哈希ID。在一个新的、完全空的仓库中,根本没有任何提交。因此,不允许存在任何分支名称。尽管如此,HEAD仍然附加到名称master上。
当你处于这种状态时,Git 的一些部分称之为“孤立分支”(orphan branch),如在git checkout --orphan中使用的那样,而其他部分则称之为“未出生分支”(unborn branch),如git status所显示的那样。你接下来所做的下一个提交将导致分支名称的出现。该名称已经存在于某个地方,具体来说,存储在HEAD中,但是提交会将该名称创建为有效的分支名称,首先创建一个有效的提交,其哈希 ID 可以由该名称持有。

感谢解释。关于多个分支能够识别相同的提交的部分有助于澄清事情。在这种情况下,使用 git checkout <哈希值> 命令将不清楚正在检出哪个分支。 - Trevor

0

HEAD 是你当前检出的提交。可能有一个指向 HEAD 的分支(比如 master),也可能没有。当你执行 git checkout 2bcfd11 命令时,你更新了你的 HEAD,但是保持了分离状态 - 也就是说,你没有告诉 git 你想要将某个符号名称与其关联。如果你有一个指向 2bcfd11 的分支,你可以切换到该分支并正常工作。如果没有,git branch 命令将允许你创建一个以任何名称命名的分支在 2bcfd11 处。


0

使用 Git 2.23(于2019年8月昨天发布),执行 {{link1:git restore}} 命令

git restore -s <SHA1> -- .

这样你就不会有一个分离的 HEAD(你仍然在当前分支上,例如 master,但内容不同)。

完成后,您可以使用以下命令恢复正确的工作树:

git restore -s master -- .

感谢您的建议。为了确认我是否理解正确,这是我在我的情况下如何使用 git restore(引用我最初在问题中发布的 SHA1 值)吗?要查看旧提交,我应该执行 git restore -s 68cce45 -- . 而不是 git checkout 68cce45?然后要返回到最新的提交,我应该执行 git restore -s master -- . 而不是 git checkout 2bcfd11 - Trevor
@wxyz 是的,这样你就不需要 切换 分支,在恢复 master 分支内容后仍然在主分支上。 - VonC
啊,好的。另外,命令末尾的 -- . 是什么意思? - Trevor
@wxyz表示您要恢复的内容的路径:当前路径。在本地存储库的根目录下执行此操作,即可恢复所有工作树的提交。 - VonC
你能完全省略 -- . 吗?这样做会将整个工作树的提交恢复吗? - Trevor
@wxyz 除非您使用补丁选项进行还原(https://git-scm.com/docs/git-restore#Documentation/git-restore.txt--p),否则必须提供路径。要恢复整个工作树(从任何子文件夹),您可以使用 -- :/(请参阅示例:https://git-scm.com/docs/git-restore#_examples)。 - VonC

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