为什么在签出最近的提交后我有一个分离的 HEAD?
Why do I have a detached HEAD after checking out the most recent commit?
最近,在 git 存储库中工作时,我想查看旧提交 (68cce45
) 的代码,所以我做了
git checkout 68cce45
查看更改后,我想 return 到存储库的当前版本并继续工作。因为 2bcfd11
是最近的提交,所以我做了
git checkout 2bcfd11
然后我做了一些修改并做了
git add *
然后
git status
这给了我警告:HEAD detached at 2bcfd11
。
我很困惑。如果我签出的最后一次提交是在几个版本之前,我可以理解为什么我会在 "detached HEAD state" 中。但是由于我检出的最后一次提交是存储库的最新版本,那么为什么我会处于分离的 HEAD 状态? HEAD 现在不是指向版本库的 "top" 吗?
why would I be in a detached HEAD state?
因为您检出的是提交而不是分支。签出任何提交 - 您处于分离的 HEAD 状态。
Isn't HEAD now pointing to the "top" of the repository?
git
真不知道是不是顶了。您必须通过查看分支向 git
解释这一点:
git checkout master
现在 git
知道它是已知分支的负责人。分离式 HEAD 问题结束。
HEAD
是您当前签出的任何提交。可能有也可能没有指向 HEAD
或不指向 HEAD
的分支(可能像 master
)。当您执行 git checkout 2bcfd11
时,您更新了您的 HEAD
,但保持分离 - 也就是说,您没有向 git 表明您想要与之关联的一些符号名称。如果你有一个指向 2bcfd11
的分支,你可以 git checkout
该分支并且没问题。如果您不这样做,git branch
将让您在 2bcfd11
处创建一个分支,名称任意。
稍微扩展一下:在Git、HEAD
中全部大写,1是一个非常特殊名称。 HEAD
可以 附加 (到分支名称),或 分离 。在这两种情况下,Git 将能够告诉您您正在使用哪个 commit:
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 <em>branch-name</em>
附加它,而如上所示,您可以添加 --detach
以确保它变得或保持分离。
使用原始哈希 ID,例如 7c20df84bd
总是会导致分离 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
,您会喜欢 m1
、m2
、m3
或 master
这四个名字中的哪一个Git要用吗?但是它使用了其中的 none 个:如果你想让它使用一个名字,你必须自己提供一个名字:
$ 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
有两个函数。它找到当前的 branch (name),或者如果 HEAD
被分离则找不到;它会找到当前的 commit.2 你从 Git 得到的答案取决于你问的问题:你想知道吗分支名称,或者您想知道当前提交哈希 ID?
1在某些系统上,您有时可以将其拼写为小写 head
,并获得相同的效果。然而,这在添加的工作树中开始神秘地失败。最好坚持使用全部大写 HEAD
,或者如果打字太麻烦,单个字符 @
具有相同的特殊含义。
2这个也有可能会失败,只是在特殊情况下。您在一个新的、完全空的存储库中处于这种状态,其中您当前的分支 name 是 master
,但分支 master
本身尚不存在。这是因为分支名称 必须 包含一些现有的有效提交对象的哈希 ID。在一个新的、完全空的存储库中,根本没有提交。因此不允许存在任何分支名称。尽管如此,HEAD
附加到名称 master
.
当你处于这种状态时——Git 的某些部分将其称为 孤儿分支 ,如 git checkout --orphan
,而其他部分将其称为unborn branch,正如 git status
所说的那样——您所做的 next 提交导致分支名称出现。该名称已经存在于某个地方——具体来说,存储在 HEAD
中——但是提交将名称创建为有效的 branch 名称,首先创建一个有效的提交,其哈希 ID 名称可以坚持。
使用 Git 2.23(昨天发布,2019 年 8 月),执行
git restore -s <SHA1> -- .
那么您将不会有分离的 HEAD(例如,您保留在当前分支上,master
,但内容不同)。
完成后,您可以使用以下方法恢复正确的工作树:
git restore -s master -- .
最近,在 git 存储库中工作时,我想查看旧提交 (68cce45
) 的代码,所以我做了
git checkout 68cce45
查看更改后,我想 return 到存储库的当前版本并继续工作。因为 2bcfd11
是最近的提交,所以我做了
git checkout 2bcfd11
然后我做了一些修改并做了
git add *
然后
git status
这给了我警告:HEAD detached at 2bcfd11
。
我很困惑。如果我签出的最后一次提交是在几个版本之前,我可以理解为什么我会在 "detached HEAD state" 中。但是由于我检出的最后一次提交是存储库的最新版本,那么为什么我会处于分离的 HEAD 状态? HEAD 现在不是指向版本库的 "top" 吗?
why would I be in a detached HEAD state?
因为您检出的是提交而不是分支。签出任何提交 - 您处于分离的 HEAD 状态。
Isn't HEAD now pointing to the "top" of the repository?
git
真不知道是不是顶了。您必须通过查看分支向 git
解释这一点:
git checkout master
现在 git
知道它是已知分支的负责人。分离式 HEAD 问题结束。
HEAD
是您当前签出的任何提交。可能有也可能没有指向 HEAD
或不指向 HEAD
的分支(可能像 master
)。当您执行 git checkout 2bcfd11
时,您更新了您的 HEAD
,但保持分离 - 也就是说,您没有向 git 表明您想要与之关联的一些符号名称。如果你有一个指向 2bcfd11
的分支,你可以 git checkout
该分支并且没问题。如果您不这样做,git branch
将让您在 2bcfd11
处创建一个分支,名称任意。
稍微扩展一下HEAD
中全部大写,1是一个非常特殊名称。 HEAD
可以 附加 (到分支名称),或 分离 。在这两种情况下,Git 将能够告诉您您正在使用哪个 commit:
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 <em>branch-name</em>
附加它,而如上所示,您可以添加 --detach
以确保它变得或保持分离。
使用原始哈希 ID,例如 7c20df84bd
总是会导致分离 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
,您会喜欢 m1
、m2
、m3
或 master
这四个名字中的哪一个Git要用吗?但是它使用了其中的 none 个:如果你想让它使用一个名字,你必须自己提供一个名字:
$ 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
有两个函数。它找到当前的 branch (name),或者如果 HEAD
被分离则找不到;它会找到当前的 commit.2 你从 Git 得到的答案取决于你问的问题:你想知道吗分支名称,或者您想知道当前提交哈希 ID?
1在某些系统上,您有时可以将其拼写为小写 head
,并获得相同的效果。然而,这在添加的工作树中开始神秘地失败。最好坚持使用全部大写 HEAD
,或者如果打字太麻烦,单个字符 @
具有相同的特殊含义。
2这个也有可能会失败,只是在特殊情况下。您在一个新的、完全空的存储库中处于这种状态,其中您当前的分支 name 是 master
,但分支 master
本身尚不存在。这是因为分支名称 必须 包含一些现有的有效提交对象的哈希 ID。在一个新的、完全空的存储库中,根本没有提交。因此不允许存在任何分支名称。尽管如此,HEAD
附加到名称 master
.
当你处于这种状态时——Git 的某些部分将其称为 孤儿分支 ,如 git checkout --orphan
,而其他部分将其称为unborn branch,正如 git status
所说的那样——您所做的 next 提交导致分支名称出现。该名称已经存在于某个地方——具体来说,存储在 HEAD
中——但是提交将名称创建为有效的 branch 名称,首先创建一个有效的提交,其哈希 ID 名称可以坚持。
使用 Git 2.23(昨天发布,2019 年 8 月),执行
git restore -s <SHA1> -- .
那么您将不会有分离的 HEAD(例如,您保留在当前分支上,master
,但内容不同)。
完成后,您可以使用以下方法恢复正确的工作树:
git restore -s master -- .