git-checkout.txt: improve detached HEAD documentation

The detached HEAD state is a source of much confusion for users
new to git. Here we try to document it better.

Reworked from http://article.gmane.org/gmane.comp.version-control.git/138440

Requested-by: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jay Soffian 2011-02-20 00:21:50 -05:00 committed by Junio C Hamano
parent 5673d695fc
commit be8ef33c1d

View File

@ -206,41 +206,139 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
Detached HEAD
-------------
It is sometimes useful to be able to 'checkout' a commit that is
not at the tip of one of your branches. The most obvious
example is to check out the commit at a tagged official release
point, like this:
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:
------------
$ git checkout v2.6.18
HEAD (refers to branch 'master')
|
v
a---b---c branch 'master' (refers to commit 'c')
^
|
tag 'v2.0' (refers to commit 'b')
------------
Earlier versions of git did not allow this and asked you to
create a temporary branch using the `-b` option, but starting from
version 1.5.0, the above command 'detaches' your HEAD from the
current branch and directly points at the commit named by the tag
(`v2.6.18` in the example above).
You can use all git commands while in this state. You can use
`git reset --hard $othercommit` to further move around, for
example. You can make changes and create a new commit on top of
a detached HEAD. You can even create a merge by using `git
merge $othercommit`.
The state you are in while your HEAD is detached is not recorded
by any branch (which is natural --- you are not on any branch).
What this means is that you can discard your temporary commits
and merges by switching back to an existing branch (e.g. `git
checkout master`), and a later `git prune` or `git gc` would
garbage-collect them. If you did this by mistake, you can ask
the reflog for HEAD where you were, e.g.
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
------------
EXAMPLES
--------