Skip to content

Commit 88210ad

Browse files
jvnsMarie Claire LeBlanc Flanagan
andcommitted
doc: checkout: rewrite detached HEAD state explanation
The current explanation has the following issues: - Says detached HEAD state is useful but doesn't explain why - Takes many paragraphs before explaining what detached HEAD state is - It's common for users to accidentally end up in detached HEAD state, but it doesn't explain why that might happen - One of the UI improvements in `git switch` is to make it harder to detach accidentally, but that isn't advertised. See 7968bef (switch: only allow explicit detached HEAD, 2019-03-29) - Too many confusing diagrams Write a new explanation addressing these issues, and put it in a standalone guide so that we can easily reference it from advice ("see `git help detached-head`"). The result is a shorter guide that covers more material. The framing that "Git considers commits that aren't on a branch/other reference to be garbage" is taken from Steve Klabnik's tutorial https://steveklabnik.github.io/jujutsu-tutorial/branching-merging-and-conflicts/anonymous-branches.html. It's funny and it's consistent with the way Git uses the term "garbage collection". Co-authored-by: Marie Claire LeBlanc Flanagan <hello@marieflanagan.com> Signed-off-by: Marie Claire LeBlanc Flanagan <hello@marieflanagan.com> Signed-off-by: Julia Evans <julia@jvns.ca>
1 parent 5a7d1e8 commit 88210ad

7 files changed

Lines changed: 79 additions & 128 deletions

File tree

‎Documentation/Makefile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ MAN7_TXT += gitcore-tutorial.adoc
5454
MAN7_TXT += gitcredentials.adoc
5555
MAN7_TXT += gitcvs-migration.adoc
5656
MAN7_TXT += gitdatamodel.adoc
57+
MAN7_TXT += gitdetached-head.adoc
5758
MAN7_TXT += gitdiffcore.adoc
5859
MAN7_TXT += giteveryday.adoc
5960
MAN7_TXT += gitfaq.adoc

‎Documentation/detached-head.adoc‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
`HEAD` is where Git stores your current branch or commit. `HEAD` can either be:
2+
3+
1. A branch, which is your current branch.
4+
2. A commit ID, when you don't have a current branch.
5+
This is called "detached HEAD state".
6+
7+
It can sometimes be useful for `HEAD` to be a commit ID, because
8+
it lets you easily check out an old version of your project. For
9+
example, a data scientist could use this to run an old version of a
10+
data pipeline or a programmer could use this to figure out when a bug
11+
was introduced.
12+
13+
But it's important to know that if you create new commits while in
14+
detached HEAD state, those commits won't be on a branch. This makes
15+
those new commits much harder to find later. Also, Git considers commits
16+
that aren't on any branch (or a tag or other reference) to be garbage.
17+
Git will eventually permanently delete those "garbage" commits during
18+
garbage collection. Those commits will be deleted after 30 days by
19+
default, see linkgit:git-gc[1] for more.
20+
21+
There are 3 main ways you can end up in detached HEAD state:
22+
23+
1. `git checkout COMMIT_ID` or `git switch --detach COMMIT_ID`,
24+
where `COMMIT_ID` is a commit ID.
25+
2. `git checkout v1.3`, where v1.3 is a tag name
26+
3. `git checkout origin/main`, where `origin/main` is
27+
a remote-tracking branch
28+
29+
Checking out a tag puts you in detached HEAD state because `HEAD` can
30+
only be a branch or a commit, not a tag or any other reference.
31+
So `git checkout TAG` will set HEAD to the tagged commit.
32+
33+
The easiest way to avoid accidentally ending up in detached HEAD state
34+
is to use linkgit:git-switch[1] instead of linkgit:git-checkout[1] to
35+
switch branches. `git switch` won't let you detach unless you explicitly
36+
pass the `--detach` argument.
37+
38+
To get back onto a branch, you can:
39+
40+
1. Switch to the branch you want to be on, with `git switch BRANCHNAME`.
41+
2. Create a new branch at the current commit, with `git switch -c BRANCHNAME`.
42+
You might want to do this if you've created new commits, so that you can
43+
find the commit later and so that it won't be garbage collected.
44+
45+
If you switch branches after making commits in detached HEAD state, Git
46+
will warn you that you might be about to "lose" those commits and print
47+
out the commit ID. You can also always find those commits later using
48+
linkgit:git-reflog[1].
49+
50+
Here's what that warning looks like:
51+
52+
-------------------------
53+
Warning: you are leaving 1 commit behind, not connected to
54+
any of your branches:
55+
56+
48c09f6 fix bug
57+
-------------------------
58+
59+
For a more in-depth explanation of how `HEAD` works in Git,
60+
see linkgit:gitdatamodel[7] (`git help datamodel`).

‎Documentation/git-checkout.adoc‎

Lines changed: 1 addition & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -376,135 +376,8 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
376376
[[DETACHED_HEAD]]
377377
DETACHED HEAD
378378
-------------
379-
`HEAD` normally refers to a named branch (e.g. `master`). Meanwhile, each
380-
branch refers to a specific commit. Let's look at a repo with three
381-
commits, one of them tagged, and with branch `master` checked out:
382379
383-
------------
384-
HEAD (refers to branch 'master')
385-
|
386-
v
387-
a---b---c branch 'master' (refers to commit 'c')
388-
^
389-
|
390-
tag 'v2.0' (refers to commit 'b')
391-
------------
392-
393-
When a commit is created in this state, the branch is updated to refer to
394-
the new commit. Specifically, `git commit` creates a new commit `d`, whose
395-
parent is commit `c`, and then updates branch `master` to refer to new
396-
commit `d`. `HEAD` still refers to branch `master` and so indirectly now refers
397-
to commit `d`:
398-
399-
------------
400-
$ edit; git add; git commit
401-
402-
HEAD (refers to branch 'master')
403-
|
404-
v
405-
a---b---c---d branch 'master' (refers to commit 'd')
406-
^
407-
|
408-
tag 'v2.0' (refers to commit 'b')
409-
------------
410-
411-
It is sometimes useful to be able to checkout a commit that is not at
412-
the tip of any named branch, or even to create a new commit that is not
413-
referenced by a named branch. Let's look at what happens when we
414-
checkout commit `b` (here we show two ways this may be done):
415-
416-
------------
417-
$ git checkout v2.0 # or
418-
$ git checkout master^^
419-
420-
HEAD (refers to commit 'b')
421-
|
422-
v
423-
a---b---c---d branch 'master' (refers to commit 'd')
424-
^
425-
|
426-
tag 'v2.0' (refers to commit 'b')
427-
------------
428-
429-
Notice that regardless of which checkout command we use, `HEAD` now refers
430-
directly to commit `b`. This is known as being in detached `HEAD` state.
431-
It means simply that `HEAD` refers to a specific commit, as opposed to
432-
referring to a named branch. Let's see what happens when we create a commit:
433-
434-
------------
435-
$ edit; git add; git commit
436-
437-
HEAD (refers to commit 'e')
438-
|
439-
v
440-
e
441-
/
442-
a---b---c---d branch 'master' (refers to commit 'd')
443-
^
444-
|
445-
tag 'v2.0' (refers to commit 'b')
446-
------------
447-
448-
There is now a new commit `e`, but it is referenced only by `HEAD`. We can
449-
of course add yet another commit in this state:
450-
451-
------------
452-
$ edit; git add; git commit
453-
454-
HEAD (refers to commit 'f')
455-
|
456-
v
457-
e---f
458-
/
459-
a---b---c---d branch 'master' (refers to commit 'd')
460-
^
461-
|
462-
tag 'v2.0' (refers to commit 'b')
463-
------------
464-
465-
In fact, we can perform all the normal Git operations. But, let's look
466-
at what happens when we then checkout `master`:
467-
468-
------------
469-
$ git checkout master
470-
471-
HEAD (refers to branch 'master')
472-
e---f |
473-
/ v
474-
a---b---c---d branch 'master' (refers to commit 'd')
475-
^
476-
|
477-
tag 'v2.0' (refers to commit 'b')
478-
------------
479-
480-
It is important to realize that at this point nothing refers to commit
481-
`f`. Eventually commit `f` (and by extension commit `e`) will be deleted
482-
by the routine Git garbage collection process, unless we create a reference
483-
before that happens. If we have not yet moved away from commit `f`,
484-
any of these will create a reference to it:
485-
486-
------------
487-
$ git checkout -b foo # or "git switch -c foo" <1>
488-
$ git branch foo <2>
489-
$ git tag foo <3>
490-
------------
491-
<1> creates a new branch `foo`, which refers to commit `f`, and then
492-
updates `HEAD` to refer to branch `foo`. In other words, we'll no longer
493-
be in detached `HEAD` state after this command.
494-
<2> similarly creates a new branch `foo`, which refers to commit `f`,
495-
but leaves `HEAD` detached.
496-
<3> creates a new tag `foo`, which refers to commit `f`,
497-
leaving `HEAD` detached.
498-
499-
If we have moved away from commit `f`, then we must first recover its object
500-
name (typically by using git reflog), and then we can create a reference to
501-
it. For example, to see the last two commits to which `HEAD` referred, we
502-
can use either of these commands:
503-
504-
------------
505-
$ git reflog -2 HEAD # or
506-
$ git log -g -2 HEAD
507-
------------
380+
include::detached-head.adoc[]
508381
509382
[[ARGUMENT_DISAMBIGUATION]]
510383
ARGUMENT DISAMBIGUATION
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
gitdetached-head(7)
2+
===================
3+
4+
NAME
5+
----
6+
gitdetached-head - How detached HEAD state works
7+
8+
DESCRIPTION
9+
-----------
10+
include::detached-head.adoc[]
11+
12+
GIT
13+
---
14+
Part of the linkgit:git[1] suite

‎Documentation/meson.build‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ manpages = {
197197
'gitcredentials.adoc' : 7,
198198
'gitcvs-migration.adoc' : 7,
199199
'gitdatamodel.adoc' : 7,
200+
'gitdetached-head.adoc' : 7,
200201
'gitdiffcore.adoc' : 7,
201202
'giteveryday.adoc' : 7,
202203
'gitfaq.adoc' : 7,

‎advice.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ void detach_advice(const char *new_name)
291291
"\n"
292292
" git switch -\n"
293293
"\n"
294+
"Run `git help detached-head` to learn more.\n"
294295
"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
295296

296297
fprintf(stderr, fmt, new_name);

‎command-list.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ gitcore-tutorial guide
218218
gitcredentials guide
219219
gitcvs-migration guide
220220
gitdatamodel guide
221+
gitdetached-head guide
221222
gitdiffcore guide
222223
giteveryday guide
223224
gitfaq guide

0 commit comments

Comments
 (0)