Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ MAN7_TXT += gitcore-tutorial.adoc
MAN7_TXT += gitcredentials.adoc
MAN7_TXT += gitcvs-migration.adoc
MAN7_TXT += gitdatamodel.adoc
MAN7_TXT += gitdetached-head.adoc
MAN7_TXT += gitdiffcore.adoc
MAN7_TXT += giteveryday.adoc
MAN7_TXT += gitfaq.adoc
Expand Down
60 changes: 60 additions & 0 deletions Documentation/detached-head.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
`HEAD` is where Git stores your current branch or commit. `HEAD` can either be:

1. A branch, which is your current branch.
2. A commit ID, when you don't have a current branch.
This is called "detached HEAD state".
It can sometimes be useful for `HEAD` to be a commit ID, because
it lets you easily check out an old version of your project. For
example, a data scientist could use this to run an old version of a
data pipeline or a programmer could use this to figure out when a bug
was introduced.

But it's important to know that if you create new commits while in
detached HEAD state, those commits won't be on a branch. This makes
those new commits much harder to find later. Also, Git considers commits
that aren't on any branch (or a tag or other reference) to be garbage.
Git will eventually permanently delete those "garbage" commits during
garbage collection. Those commits will be deleted after 30 days by
default, see linkgit:git-gc[1] for more.

There are 3 main ways you can end up in detached HEAD state:

1. `git checkout COMMIT_ID` or `git switch --detach COMMIT_ID`,
where `COMMIT_ID` is a commit ID.
2. `git checkout v1.3`, where v1.3 is a tag name
3. `git checkout origin/main`, where `origin/main` is
a remote-tracking branch
Checking out a tag puts you in detached HEAD state because `HEAD` can
only be a branch or a commit, not a tag or any other reference.
So `git checkout TAG` will set HEAD to the tagged commit.

The easiest way to avoid accidentally ending up in detached HEAD state
is to use linkgit:git-switch[1] instead of linkgit:git-checkout[1] to
switch branches. `git switch` won't let you detach unless you explicitly
pass the `--detach` argument.

To get back onto a branch, you can:

1. Switch to the branch you want to be on, with `git switch BRANCHNAME`.
2. Create a new branch at the current commit, with `git switch -c BRANCHNAME`.
You might want to do this if you've created new commits, so that you can
find the commit later and so that it won't be garbage collected.
If you switch branches after making commits in detached HEAD state, Git
will warn you that you might be about to "lose" those commits and print
out the commit ID. You can also always find those commits later using
linkgit:git-reflog[1].

Here's what that warning looks like:

-------------------------
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
48c09f6 fix bug
-------------------------

For a more in-depth explanation of how `HEAD` works in Git,
see linkgit:gitdatamodel[7] (`git help datamodel`).
129 changes: 1 addition & 128 deletions Documentation/git-checkout.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -376,135 +376,8 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
[[DETACHED_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:
------------
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 # or "git switch -c 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
------------
include::detached-head.adoc[]
[[ARGUMENT_DISAMBIGUATION]]
ARGUMENT DISAMBIGUATION
Expand Down
14 changes: 14 additions & 0 deletions Documentation/gitdetached-head.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
gitdetached-head(7)
===================

NAME
----
gitdetached-head - How detached HEAD state works

DESCRIPTION
-----------
include::detached-head.adoc[]

GIT
---
Part of the linkgit:git[1] suite
1 change: 1 addition & 0 deletions Documentation/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ manpages = {
'gitcredentials.adoc' : 7,
'gitcvs-migration.adoc' : 7,
'gitdatamodel.adoc' : 7,
'gitdetached-head.adoc' : 7,
'gitdiffcore.adoc' : 7,
'giteveryday.adoc' : 7,
'gitfaq.adoc' : 7,
Expand Down
1 change: 1 addition & 0 deletions advice.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ void detach_advice(const char *new_name)
"\n"
" git switch -\n"
"\n"
"Run `git help detached-head` to learn more.\n"
"Turn off this advice by setting config variable advice.detachedHead to false\n\n");

fprintf(stderr, fmt, new_name);
Expand Down
1 change: 1 addition & 0 deletions command-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ gitcore-tutorial guide
gitcredentials guide
gitcvs-migration guide
gitdatamodel guide
gitdetached-head guide
gitdiffcore guide
giteveryday guide
gitfaq guide
Expand Down
Loading