All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jay Soffian <jaysoffian@gmail.com>
To: git@vger.kernel.org
Cc: Jay Soffian <jaysoffian@gmail.com>,
	Nicolas Pitre <nico@fluxnic.net>,
	Junio C Hamano <gitster@pobox.com>
Subject: [PATCH] git-checkout.txt: improve detached HEAD documentation
Date: Sun, 20 Feb 2011 00:21:50 -0500	[thread overview]
Message-ID: <1298179310-46207-1-git-send-email-jaysoffian@gmail.com> (raw)
In-Reply-To: <7v7hcy9vrk.fsf@alter.siamese.dyndns.org>

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>
---
Incorporated Junio's feedback. Though this is a bit clearer, I think it's
starting to make the man page quite long due to the taller ascii
art (adding the tag plus the stem to the arrows adds 4 lines to each
picture).

I don't have any more time to allocate to this patch. I think it's a vast
improvement as is, and you should feel free to make any corrections/edits you
see fit so that it may be applied.

 Documentation/git-checkout.txt |  145 +++++++++++++++++++++++++++++++++------
 1 files changed, 122 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 880763d..8a252dc 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -206,40 +206,139 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 Detached HEAD
 -------------
+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:
 
-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 (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':
 
 ------------
-$ git checkout v2.6.18
+$ 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')
 ------------
 
-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).
+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 three ways this may be done):
 
-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`.
+------------
+$ git checkout v2.0  # or
+$ git checkout b     # 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')
+------------
 
-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.
+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:
 
 ------------
-$ git log -g -2 HEAD
+$ 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
 --------
-- 
1.7.4.1.52.g8e1b

  parent reply	other threads:[~2011-02-20  5:22 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-02-17 19:46 [PATCH] git-checkout.txt: improve detached HEAD documentation Jay Soffian
2011-02-17 19:48 ` Jay Soffian
2011-02-17 20:47 ` Nicolas Pitre
2011-02-17 21:49 ` Junio C Hamano
2011-02-18  0:14   ` Nicolas Pitre
2011-02-18  0:25     ` Junio C Hamano
2011-02-20  5:21   ` Jay Soffian [this message]
2011-02-22  5:56     ` Junio C Hamano
2011-02-22 15:19       ` Jay Soffian

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1298179310-46207-1-git-send-email-jaysoffian@gmail.com \
    --to=jaysoffian@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=nico@fluxnic.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.