Why do I have a detached HEAD after checking out the most recent commit?

限于喜欢 提交于 2019-12-06 14:54:53

why would I be in a detached HEAD state?

Because you've checked out a commit instead of a branch. Checkout any commit — and you're in a detached HEAD state.

Isn't HEAD now pointing to the "top" of the repository?

git doesn't really knows if it's the top. You have to explain that to git by checking out a branch:

git checkout master

Now git knows it's the head of a known branch. The end of detached HEAD problem.

To expand on phd's answer a bit: in Git, HEAD, spelled in all uppercase like this,1 is a very special name. HEAD can either be attached (to a branch name), or detached. In both cases, Git will be able to tell you which commit you're using:

git rev-parse HEAD

will print some hash ID. But only when HEAD is attached to a branch name can Git tell you which branch name you're using:

git rev-parse --symbolic-full-name HEAD
git symbolic-ref HEAD

Both will give you the name of the current branch (prefixed with refs/heads/) if you're on a branch. If you're in detached HEAD mode, the former will just print HEAD and the latter will produce an error:

$ 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

Many forms of git checkout will detach HEAD. A few forms will attach it. Using git checkout branch-name attaches it, while—as shown above—you can add --detach to make sure it becomes or stays detached.

Using a raw hash ID such as 7c20df84bd always results in a detached HEAD, even if there are one or more branch names that identify this particular commit.

Note that you can have as many branch names as you like that all identify the same commit:

$ 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

If I explicitly check out 7c20df84bd21ec0215358381844274fa10515017, which of the four names—m1, m2, m3, or master—would you like Git to use? But it uses none of them: if you want it to use a name, you must supply a name yourself:

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

after which we can delete the extra names so that commit 7c20df84bd21ec0215358381844274fa10515017 is only on, and at the tip of, master, rather than being on and at the tip of four branches all at the same time.

$ 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).

Remember, HEAD has two functions. It finds the current branch (name), or fails to do so if HEAD is detached; and it finds the current commit.2 The answer you get from Git depends on the question you ask: did you want to know the branch name, or did you want to know the current commit hash ID?


1You can, on some systems, sometimes spell it out in lowercase, head, and get the same effect. However, this starts to fail mysteriously in added work-trees. It's best to stick with the all-caps HEAD, or if that's too annoying to type, the single character @ has the same special meaning.

2This too can fail, but only in a special state. You are in this state in a new, totally-empty repository, in which your current branch name is master, but branch master itself does not yet exist. This is because a branch name must contain the hash ID of some existing, valid commit object. In a new, totally-empty repository, there are no commits at all. So no branch names are allowed to exist. Nonetheless, HEAD is attached to the name master.

When you're in this state—some parts of Git call this an orphan branch, as in git checkout --orphan, and others call it an unborn branch, as in what git status will say—the next commit you make causes the branch name to come into existence. The name is already somewhere—specifically, stored in HEAD—but the commit creates the name as a valid branch name, after first creating a valid commit whose hash ID the name can hold.

HEAD is whatever commit you currently have checked out. There may or may not be a branch (like master maybe) that points to HEAD or not. When you did git checkout 2bcfd11, you updated your HEAD, but remained detached - that is, you didn't indicate to git that you want to have some symbolic name associated with that. If you have a branch that points to 2bcfd11, you can git checkout that branch and be fine. If you don't, git branch will let you create a branch at 2bcfd11 with whatever name you want.

With Git 2.23 (released yesterday, August 2019), do a git restore

git restore -s <SHA1> -- .

You won't have a detached HEAD then (you remain on your current branch, master for instance, but with a different content).

Once you are done, you can, well, restore the proper working tree with:

git restore -s master -- .
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!