Recently, while working in a git repository, I wanted to view the code at an old commit (68cce45
), so I did
git checkout 68cce45
After viewing the changes, I wanted to return to the current version of the repository and keep working. Since 2bcfd11
was the most recent commit, I did
git checkout 2bcfd11
I then made some changes and did
git add *
and then
git status
which gave me the warning: HEAD detached at 2bcfd11
.
I'm confused. I can understand why I would be in a "detached HEAD state" if the last commit I checked-out was several versions ago. But since the last commit I checked-out was the most current version of the repository, then why would I be in a detached HEAD state? Isn't HEAD now pointing to the "top" of the repository?
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 -- .
来源:https://stackoverflow.com/questions/57541141/why-do-i-have-a-detached-head-after-checking-out-the-most-recent-commit