HEAD normally refers to a named branch (e.g. master). Meanwhile,
each branch refers to a specific commit. Let's look at a repo
with three commits, one of them tagged, and with branch master
checked out:
HEAD (refers to branch 'master')
|
v
a---b---c branch 'master' (refers to commit 'c')
^
|
tag 'v2.0' (refers to commit 'b')
When a commit is created in this state, the branch is updated to
refer to the new commit. Specifically, git commit creates a new
commit d, whose parent is commit c, and then updates branch
master to refer to new commit d. HEAD still refers to branch
master and so indirectly now refers to commit d:
$ edit; git add; git commit
HEAD (refers to branch 'master')
|
v
a---b---c---d branch 'master' (refers to commit 'd')
^
|
tag 'v2.0' (refers to commit 'b')
It is sometimes useful to be able to checkout a commit that is
not at the tip of any named branch, or even to create a new
commit that is not referenced by a named branch. Let's look at
what happens when we checkout commit b (here we show two ways
this may be done):
$ git checkout v2.0 # or
$ git checkout master^^
HEAD (refers to commit 'b')
|
v
a---b---c---d branch 'master' (refers to commit 'd')
^
|
tag 'v2.0' (refers to commit 'b')
Notice that regardless of which checkout command we use, HEAD now
refers directly to commit b. This is known as being in detached
HEAD state. It means simply that HEAD refers to a specific
commit, as opposed to referring to a named branch. Let's see what
happens when we create a commit:
$ edit; git add; git commit
HEAD (refers to commit 'e')
|
v
e
/
a---b---c---d branch 'master' (refers to commit 'd')
^
|
tag 'v2.0' (refers to commit 'b')
There is now a new commit e, but it is referenced only by HEAD.
We can of course add yet another commit in this state:
$ edit; git add; git commit
HEAD (refers to commit 'f')
|
v
e---f
/
a---b---c---d branch 'master' (refers to commit 'd')
^
|
tag 'v2.0' (refers to commit 'b')
In fact, we can perform all the normal Git operations. But, let's
look at what happens when we then checkout master:
$ git checkout master
HEAD (refers to branch 'master')
e---f |
/ v
a---b---c---d branch 'master' (refers to commit 'd')
^
|
tag 'v2.0' (refers to commit 'b')
It is important to realize that at this point nothing refers to
commit f. Eventually commit f (and by extension commit e) will be
deleted by the routine Git garbage collection process, unless we
create a reference before that happens. If we have not yet moved
away from commit f, any of these will create a reference to it:
$ git checkout -b foo (1)
$ git branch foo (2)
$ git tag foo (3)
1. creates a new branch foo, which refers to commit f, and then
updates HEAD to refer to branch foo. In other words, we'll no
longer be in detached HEAD state after this command.
2. similarly creates a new branch foo, which refers to commit f,
but leaves HEAD detached.
3. creates a new tag foo, which refers to commit f, leaving HEAD
detached.
If we have moved away from commit f, then we must first recover
its object name (typically by using git reflog), and then we can
create a reference to it. For example, to see the last two
commits to which HEAD referred, we can use either of these
commands:
$ git reflog -2 HEAD # or
$ git log -g -2 HEAD