Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 899
Trevor Avatar asked Aug 18 '19 02:08

Trevor


2 Answers

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.

like image 160
phd Avatar answered Nov 15 '22 10:11

phd


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.

like image 33
torek Avatar answered Nov 15 '22 10:11

torek