Skip to content

doc: checkout: rewrite detached HEAD state explanation - #2250

Open
jvns wants to merge 1 commit into
gitgitgadget:masterfrom
jvns:detached-head
Open

jvns wants to merge 1 commit into
gitgitgadget:masterfrom
jvns:detached-head

Conversation

@jvns

@jvns jvns commented Oct 6, 2026 •

Copy link
Copy Markdown

This is a pretty quick rewrite where we didn't get any user feedback. If we really want to make sure it's clear to users as possible, we should IMO do a second stage later where we ask users which parts are unclear.

Changes in v2:

  • Make the meson.build and Makefile changes (thanks to Kristoffer)
  • Rename to gitdata-model (thanks to Kristoffer)
  • Mention commits will be deleted after 90 days to make it seem less scary (thanks to Kristoffer)
  • Add an "easily" qualifier to "it lets you look at an old version of your code", to avoid implying that this is the only way (thanks to Junio)
  • Mention that Git will warn you when switching branches if you're leaving commits behind (thanks to Junio)
  • Remove "unintentionally" (thanks to Junio)
  • Mention git help datamodel (thanks to Junio)

Changes in v3:

I went through the style comments from v1.

content changes:

  • Change "90 days" to "30 days" (thanks to Junio)
  • Say that HEAD is where Git stores your "current branch or commit" to make the first sentence read better (thanks to Junio)
  • Mention git switch --detach a little earlier (thanks to Kristoffer)
  • Replace "the only problem" with "it's important to know that" (thanks to Junio)

formatting changes etc:

  • Replace help detachedhead with help detached-head (thanks to Junio)
  • Fix spacing in command-list.txt (thanks to Junio)
  • Get Marie's signoff (thanks to Junio)

non-changes: Left these phrases alone because I don't think the proposed changes would make it more clear.

  • "3 main ways you can end up in detached HEAD state"
  • "to look at an old version of your code"
  • "avoid accidentally ending up"

Changes in v4:

  • Fix commit message in example: "fix bug", not "3" (thanks to Junio)
  • "commit for that tag" -> "tagged commit" (thanks to Junio)
  • Move "git checkout COMMIT_ID or git switch --detach COMMIT_ID" down to the "3 main ways" list to reduce duplication (thanks to Junio)
  • Add some examples and be clearer about how detached HEAD state lets you check out an old version of the project. (thanks to Junio)

non-changes:

  • It's probably possible to explain HEAD better. That seems like a big project to me since HEAD in Git is a very tricky topic. I think it would be better to address in a separate patch series to have more time to think it through. Or if we think it's not possible to merge this without explaining HEAD better then I guess it shouldn't be merged.

cc: "Kristoffer Haugsbakk" kristofferhaugsbakk@fastmail.com

@jvns
jvns force-pushed the detached-head branch 6 times, most recently from 88e36e4 to efe0f9d Compare October 7, 2026 14:36
@jvns

jvns commented Oct 7, 2026

Copy link
Copy Markdown
Author

/submit

@gitgitgadget

gitgitgadget Bot commented Oct 7, 2026

Copy link
Copy Markdown

Submitted as pull.2250.git.1791384721919.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-2250/jvns/detached-head-v1

To fetch this version to local tag pr-2250/jvns/detached-head-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-2250/jvns/detached-head-v1

@gitgitgadget

gitgitgadget Bot commented Oct 7, 2026

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Julia Evans via GitGitGadget" <gitgitgadget@gmail.com> writes:

> diff --git a/Documentation/detached-head.adoc b/Documentation/detached-head.adoc
> new file mode 100644
> index 0000000000..1e00712124
> --- /dev/null
> +++ b/Documentation/detached-head.adoc
> @@ -0,0 +1,43 @@
> +`HEAD` is where Git stores your current branch. `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".

HEAD is branch which can either be a branch or something else?

    HEAD records the state from which your current changes started.
    This can be a branch (the "current branch"), or it may not be
    associated with any concrete branch (in which case HEAD is
    "detached").

> +It can sometimes be useful for `HEAD` to be a commit ID.
> +For example, it lets you look at an old version of your code
> +(with `git checkout COMMIT_ID`).

To "look at" an older state is more versatile than simply running
'git show COMMIT_ID:path', as it allows you to do anything you can
do on a branch, such as building and testing.  Thus,

 	A detached HEAD is useful when you want to tentatively visit
	an old state with 'git checkout --detach v0.1', without
	having to create a dedicated branch for it.

> +The only problem is that if you create new commits while in detached
> +HEAD state, those commits won't be on a branch.

I would suggest rephrasing "The only problem" to "One caveat is" or
something.  When sightseeing and experimenting, the ability to
create throw-away commits without the need to clean them up later,
or to invent a unique name for a temporary branch, is not a problem.
It is an advantage.

> 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.

Correct.

Perhaps it is better to clarify this by adding "after you leave
the detached HEAD state" after "to find later".  While you are in
the detached HEAD state, you can run 'git log' to find the commits
you created there, as you can do everything you normally can on a
branch, or use 'git reflog @{now}' for that matter.

> +There are 3 main ways you can end up in detached HEAD state
> +unintentionally:
> +
> +1. `git checkout COMMIT_ID`, where `COMMIT_ID` is a commit ID
> +2. `git checkout v1.3`, where v1.3 is a tag name

These I wouldn't call "unintentionally".  They are documented and
supported ways to do so, and will remain to be so.  "unknowingly"
may be a more fair way to call these gotchas, though.

> +3. `git checkout origin/main`, where `origin/main` is
> +   a remote-tracking branch

This can happen when the user forgets to say "-t" ('git checkout -t
origin/main' dwims to 'git checkout -t -b main origin/main'), so it
is closer to 'unintentionally' than the above two.

> +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 commit for that tag.

Correct.

> +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.

Correct, but the motivation to 'avoid accidentally ending up' may
want to be spelled out.  Most often checking out a tag is to go
sightseeing, where you do not want to 'avoid' detached HEAD.

> +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.

Very good.

> +If you create commits in detached HEAD state that aren't on a branch,
> +you can find them later using linkgit:git-reflog[1].

This, as you already said, is "much harder to find later" option.
There should be a better recovery option described here before
resorting to "git reflog HEAD" after you switched back.  E.g.,

    If you have created commits in detached HEAD state and then
    switched away from the state, all is not lost.  "git checkout"
    would have give you a warning message, like this:

	Warning: you are leaving 2 commits behind, not connected to
	any of your branches

	c816689 typofix the previous
	8e1bff5 decsribe detached HEAD state better

    You can create branches to keep them, e.g., "git branch saved c816689",
    by using the commit object names left there.

> diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
> index 2aefea0228..4e9e94e24d 100644
> --- a/Documentation/git-checkout.adoc
> +++ b/Documentation/git-checkout.adoc
> @@ -376,135 +376,8 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
>  [[DETACHED_HEAD]]
>  DETACHED HEAD
>  -------------
> +include::detached-head.adoc[]

Losing the diagrams is a bit concerning.  However, if the intention
is to have readers learn how HEAD refers to a branch pointing at a
commit with history structured as a DAG elsewhere in a more basic
concepts guide, I suspect that it would actually work better.  This
could even be reduced to a simple "see also" pointer to that other
guide.

If an existing user only uses 'switch' and never reads 'git help
checkout', they are already not seeing these diagrams, or perhaps
they learned the concepts elsewhere.  They still manage to
understand Git well enough to make use of it, so perhaps this
approach is OK.  I dunno.

> diff --git a/Documentation/gitdetachedhead.adoc b/Documentation/gitdetachedhead.adoc
> new file mode 100644
> index 0000000000..0aeecd159c
> --- /dev/null
> +++ b/Documentation/gitdetachedhead.adoc
> @@ -0,0 +1,14 @@
> +gitdetachedhead(7)
> +===============
> +
> +NAME
> +----
> +gitdetachedhead - How detached HEAD state works
> +
> +DESCRIPTION
> +-----------
> +include::detached-head.adoc[]

This may be good as a first step, but we may want to have more here
than what we show in "git help checkout" later.  If we miss the
pictures we lost from "git help checkout", it can be moved here.

Thanks.

> +
> +GIT
> +---
> +Part of the linkgit:git[1] suite
> diff --git a/advice.c b/advice.c
> index 401d047391..43f86c2eaf 100644
> --- a/advice.c
> +++ b/advice.c
> @@ -291,6 +291,7 @@ void detach_advice(const char *new_name)
>  	"\n"
>  	"  git switch -\n"
>  	"\n"
> +	"Run `git help detachedhead` to learn more.\n"
>  	"Turn off this advice by setting config variable advice.detachedHead to false\n\n");
>  
>  	fprintf(stderr, fmt, new_name);
> diff --git a/command-list.txt b/command-list.txt
> index 63ae2a67c9..313689335f 100644
> --- a/command-list.txt
> +++ b/command-list.txt
> @@ -218,6 +218,7 @@ gitcore-tutorial                        guide
>  gitcredentials                          guide
>  gitcvs-migration                        guide
>  gitdatamodel                            guide
> +gitdetachedhead                         guide
>  gitdiffcore                             guide
>  giteveryday                             guide
>  gitfaq                                  guide
>
> base-commit: 5a7d1e8045ce66c908f62598e26cbb8df7b39a90

@gitgitgadget

gitgitgadget Bot commented Oct 7, 2026

Copy link
Copy Markdown

"Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email):

> Re: [PATCH] doc: checkout: rewrite detached HEAD state explanation

This change adds a standalone guide as well. The subject “just” make it
sound like you are rewriting the text where it is.

On Wed, Oct 7, 2026, at 16:52, Julia Evans via GitGitGadget wrote:
> From: Julia Evans <julia@jvns.ca>
>
> 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 7968bef06b (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 detachedhead`").
> 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: Julia Evans <julia@jvns.ca>
> ---
>     doc: checkout: rewrite detached HEAD state explanation
>
>     Often when rewriting these explanations I go through a process where I
>     ask users' feedback on the old explanation. Here I didn't do that
>     basically because that process takes a long time and I'm working on a
>     bunch of other time consuming docs projects so I did a quick rewrite
>     based on my previous experience explaining detached HEAD state to folks.
>
>     I thought this could be a nice quick docs win on a topic which many
>     users find quite confusing. If the changes here are too controversial I
>     can drop it for now.
>
>     Also if folks object to making a separate git help detachedhead guide
>     I'm happy to drop that too. Personally I'm excited about the idea of
>     being able to reference the guides in our advice (which is one of our
>     best tools for getting users info about how to use Git!), but it's not
>     possible to let users jump to a subsection, so this is sort of a hack
>     around that.
>
> Published-As:
> https://github.com/gitgitgadget/git/releases/tag/pr-2250%2Fjvns%2Fdetached-head-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git
> pr-2250/jvns/detached-head-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/2250
>
>  Documentation/detached-head.adoc   |  43 ++++++++++
>  Documentation/git-checkout.adoc    | 129 +----------------------------
>  Documentation/gitdetachedhead.adoc |  14 ++++

Why in one word instead of hyphenating `detached-head`?
(gitdetached-head(7))

>  advice.c                           |   1 +
>  command-list.txt                   |   1 +

This is missing meson/Makefile changes in order to make
gitdetachedhead(7).

>  5 files changed, 60 insertions(+), 128 deletions(-)
>  create mode 100644 Documentation/detached-head.adoc
>  create mode 100644 Documentation/gitdetachedhead.adoc
>
> diff --git a/Documentation/detached-head.adoc
> b/Documentation/detached-head.adoc
> new file mode 100644
> index 0000000000..1e00712124
> --- /dev/null
> +++ b/Documentation/detached-head.adoc
> @@ -0,0 +1,43 @@
> +`HEAD` is where Git stores your current branch. `HEAD` can either be:

I see that this is similar to gitdatamodel(7). Except it adds “, if
there is a current branch”:

    HEAD is where Git stores your current branch, if there is a current
    branch. HEAD can either be: [...]

But a potential problem with that is:

A: What is HEAD?
B: It stores the current branch, if there is a current branch.
A: Oh so it is *empty* if there is no current branch?
B: No. Then it stores a commit.

This is of course revealed later here. But I wonder if starting with
current-branch can set someone up for a double take.

One could go in the other direction: you are almost always on a
commit. For most people the exception will be the unborn branch state
after `git init`. That commit is stored in the thing called `HEAD`. And
if you are on branch it also points to that branch. (It doesn’t point at
two things at the same time but instead at the branch, which is in turn
points at the commit for the branch, but conceptually it’s the same
difference).

> +
> +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.
> +For example, it lets you look at an old version of your code
> +(with `git checkout COMMIT_ID`).

Maybe go straight to `git switch --detached COMMIT_ID` here.

It is very useful to describe detached `HEAD` as something that you can
fall into by accident. It is very much perenially needed. And this
paragraph explains a legitimate, intentional use.

But a dedicated guide is also an opportunity to not only acknowledge hey
there is a use here but to explain actively why it is
convenient. Because one thing is “detached `HEAD`” as a conceptual,
scary blind alley where my work might get garbage collected. But only
thinking in terms of branches and tags can lead to a dead end of only
thinking of Git as:

1. The state of our collective work right now (“main”)
2. Our important waypoints (tags)

So before you lead them out of the maw of the garbage incinerator, you
can help level up their use of Git by explaining why checking out
commits freely is useful with an example of why they would do that.

1. Finding buggy commits manually (this would probably end with a
   mention of git-bisect(1))
2. Testing that each commit on a branch (forked from `main`) passes the
   test suite
3. Browsing an interesting commit manually instead of pointing `git
   grep` at the commit (or something)
4. Just being on `origin/main` instead of having a `main`. I never
   update “main”. So why would I go to the trouble of keeping it up to
   date? I can just “be on” `origin/main` (detached on whatever that
   points to).

   See also StackOverflow questions about how to update hundreds of
   local branches with git-pull(1) or something. Why? They do not have
   hundreds of local branches that they are authoring. They just want
   those branches by other people to be updated. But that is not
   necessary for anything. You just need the remote-tracking
   branches. Including if you want to check them out (on detached
   `HEAD`).

This conceptual dead end (of no “detached `HEAD`) could manifest itself
like this:

• “I must always be on a branch”
• “Therefore I can only check out old commits by first creating a
  branch”
• “Every interesting commit must have a tag for posteriety so we can
  name it” (you just need the hash, even abbreviated)
• “We must tag this commit for the build so we can name the build” (you
  can just use the hash or use part-of-the-hash with git-describe(1))

> +
> +The only problem is 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.

Maybe this is where you should mention git-reflog(1) (and git-log(1)
maybe (see later)).

Maybe this about garbage collection could be tempered with a mention of
the however-many default days for reflog expiry. It’s such a long time
that if any of my commits get garbage collected... I probably didn’t
need them.

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

I would say “typical” instead of “main”. As in these things are the
typical gateways to this state (unintentionally).

> +
> +1. `git checkout 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 commit for that tag.

Nice.

> +
> +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 create commits in detached HEAD state that aren't on a branch,
> +you can find them later using linkgit:git-reflog[1].

Okay, but it’s more immediate and obvious to use `git log`. Say you
created three commits by accident while not on a branch. Then they will
be immediately um logged.

git-reflog(1) is excellent too of course, mostly because you are likely
to find all of them if you did a lot of weird checkouts in one session.

> diff --git a/Documentation/git-checkout.adoc
> b/Documentation/git-checkout.adoc
> index 2aefea0228..4e9e94e24d 100644
> --- a/Documentation/git-checkout.adoc
> +++ b/Documentation/git-checkout.adoc
> @@ -376,135 +376,8 @@ For more details, see the 'pathspec' entry in
> [snip]

I didn’t read the rest here.

@gitgitgadget

gitgitgadget Bot commented Oct 7, 2026

Copy link
Copy Markdown

User "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com> has been added to the cc: list.

@jvns

jvns commented Oct 13, 2026

Copy link
Copy Markdown
Author

/submit

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

Submitted as pull.2250.v2.git.1791896486100.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-2250/jvns/detached-head-v2

To fetch this version to local tag pr-2250/jvns/detached-head-v2:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-2250/jvns/detached-head-v2

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

"Julia Evans" wrote on the Git mailing list (how to reply to this email):

To make this process easier, I'm going to first deal with factual issues
(like any mistakes or ideas for more information content to include).
I'll mark comments that are suggestions for how to make the wording
more clear with "style comment" to potentially address later.

On Wed, Oct 7, 2026, at 3:02 PM, Kristoffer Haugsbakk wrote:
>> Re: [PATCH] doc: checkout: rewrite detached HEAD state explanation
>
> This change adds a standalone guide as well. The subject “just” make it
> sound like you are rewriting the text where it is.

Yes. I ran out of space while writing the subject line. 
Happy to make changes to how the commit is structured if that would
be better.

>>  Documentation/detached-head.adoc   |  43 ++++++++++
>>  Documentation/git-checkout.adoc    | 129 +----------------------------
>>  Documentation/gitdetachedhead.adoc |  14 ++++
>
> Why in one word instead of hyphenating `detached-head`?
> (gitdetached-head(7))

Currently the guides are all one word with no hyphens.
Happy to use a different style if folks think we shouldn't use
the current naming convention for the guides 

>>  advice.c                           |   1 +
>>  command-list.txt                   |   1 +
>
> This is missing meson/Makefile changes in order to make
> gitdetachedhead(7).

Thanks, will fix.

>>  5 files changed, 60 insertions(+), 128 deletions(-)
>>  create mode 100644 Documentation/detached-head.adoc
>>  create mode 100644 Documentation/gitdetachedhead.adoc
>>
>> diff --git a/Documentation/detached-head.adoc
>> b/Documentation/detached-head.adoc
>> new file mode 100644
>> index 0000000000..1e00712124
>> --- /dev/null
>> +++ b/Documentation/detached-head.adoc
>> @@ -0,0 +1,43 @@
>> +`HEAD` is where Git stores your current branch. `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.
>> +For example, it lets you look at an old version of your code
>> +(with `git checkout COMMIT_ID`).
>
> Maybe go straight to `git switch --detached COMMIT_ID` here.

Style comment.

> It is very useful to describe detached `HEAD` as something that you can
> fall into by accident. It is very much perenially needed. And this
> paragraph explains a legitimate, intentional use.
>
> But a dedicated guide is also an opportunity to not only acknowledge hey
> there is a use here but to explain actively why it is
> convenient. Because one thing is “detached `HEAD`” as a conceptual,
> scary blind alley where my work might get garbage collected. But only
> thinking in terms of branches and tags can lead to a dead end of only
> thinking of Git as:
>
> 1. The state of our collective work right now (“main”)
> 2. Our important waypoints (tags)
>
> So before you lead them out of the maw of the garbage incinerator, you
> can help level up their use of Git by explaining why checking out
> commits freely is useful with an example of why they would do that.
>
> 1. Finding buggy commits manually (this would probably end with a
>    mention of git-bisect(1))
> 2. Testing that each commit on a branch (forked from `main`) passes the
>    test suite
> 3. Browsing an interesting commit manually instead of pointing `git
>    grep` at the commit (or something)
> 4. Just being on `origin/main` instead of having a `main`. I never
>    update “main”. So why would I go to the trouble of keeping it up to
>    date? I can just “be on” `origin/main` (detached on whatever that
>    points to).

This is a nice list of reasons being in detached head state, thanks.
Not sure if it will be helpful to include or not though.

>    See also StackOverflow questions about how to update hundreds of
>    local branches with git-pull(1) or something. Why? They do not have
>    hundreds of local branches that they are authoring. They just want
>    those branches by other people to be updated. But that is not
>    necessary for anything. You just need the remote-tracking
>    branches. Including if you want to check them out (on detached
>    `HEAD`).
>
> This conceptual dead end (of no “detached `HEAD`) could manifest itself
> like this:
>
> • “I must always be on a branch”
> • “Therefore I can only check out old commits by first creating a
>   branch”
> • “Every interesting commit must have a tag for posteriety so we can
>   name it” (you just need the hash, even abbreviated)
> • “We must tag this commit for the build so we can name the build” (you
>   can just use the hash or use part-of-the-hash with git-describe(1))
>
>> +The only problem is 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.
>
> Maybe this is where you should mention git-reflog(1) (and git-log(1)
> maybe (see later)).
>
> Maybe this about garbage collection could be tempered with a mention of
> the however-many default days for reflog expiry. It’s such a long time
> that if any of my commits get garbage collected... I probably didn’t
> need them.

Good idea, will mention it's 90 days by default.

>> +
>> +There are 3 main ways you can end up in detached HEAD state
>> +unintentionally:
>
> I would say “typical” instead of “main”. As in these things are the
> typical gateways to this state (unintentionally).

Style comment

>> +
>> +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 create commits in detached HEAD state that aren't on a branch,
>> +you can find them later using linkgit:git-reflog[1].
>
> Okay, but it’s more immediate and obvious to use `git log`. Say you
> created three commits by accident while not on a branch. Then they will
> be immediately um logged.
>
> git-reflog(1) is excellent too of course, mostly because you are likely
> to find all of them if you did a lot of weird checkouts in one session.

Going to deal with this another way

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

"Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email):

On Tue, Oct 13, 2026, at 13:48, Julia Evans wrote:
> On Wed, Oct 7, 2026, at 3:02 PM, Kristoffer Haugsbakk wrote:
>>>[snip]
>>>  Documentation/detached-head.adoc   |  43 ++++++++++
>>>  Documentation/git-checkout.adoc    | 129 +----------------------------
>>>  Documentation/gitdetachedhead.adoc |  14 ++++
>>
>> Why in one word instead of hyphenating `detached-head`?
>> (gitdetached-head(7))
>
> Currently the guides are all one word with no hyphens.
> Happy to use a different style if folks think we shouldn't use
> the current naming convention for the guides

I’m confused. It doesn’t look like that to me?

    $ git help --guides
    The Git concept guides are:
       core-tutorial    A Git core tutorial for developers
       credentials      Providing usernames and passwords to Git
       cvs-migration    Git for CVS users
       diffcore         Tweaking diff output
       everyday         A useful minimum set of commands for Everyday Git
       faq              Frequently asked questions about using Git
       glossary         A Git Glossary
       namespaces       Git namespaces
       remote-helpers   Helper programs to interact with remote repositories
       submodules       Mounting one repository inside another
       tutorial         A tutorial introduction to Git
       tutorial-2       A tutorial introduction to Git: part two
       workflows        An overview of recommended workflows with Git

    'git help -a' and 'git help -g' list available subcommands and some
    concept guides. See 'git help <command>' or 'git help <concept>'
    to read about a specific subcommand or concept.
    See 'git help git' for an overview of the system.

What is definitely the case is that there is no hyphen between `git` and
the first word, maybe in order to distinguish section 1 commands) and
section 5 and 7 manpages.

>
>[snip]

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

"Julia Evans" wrote on the Git mailing list (how to reply to this email):

On Tue, Oct 13, 2026, at 8:20 AM, Kristoffer Haugsbakk wrote:
> On Tue, Oct 13, 2026, at 13:48, Julia Evans wrote:
>> On Wed, Oct 7, 2026, at 3:02 PM, Kristoffer Haugsbakk wrote:
>>>>[snip]
>>>>  Documentation/detached-head.adoc   |  43 ++++++++++
>>>>  Documentation/git-checkout.adoc    | 129 +----------------------------
>>>>  Documentation/gitdetachedhead.adoc |  14 ++++
>>>
>>> Why in one word instead of hyphenating `detached-head`?
>>> (gitdetached-head(7))
>>
>> Currently the guides are all one word with no hyphens.
>> Happy to use a different style if folks think we shouldn't use
>> the current naming convention for the guides
>
> I’m confused. It doesn’t look like that to me?
>
>     $ git help --guides
>     The Git concept guides are:
>        core-tutorial    A Git core tutorial for developers
>        credentials      Providing usernames and passwords to Git
>        cvs-migration    Git for CVS users
>        diffcore         Tweaking diff output
>        everyday         A useful minimum set of commands for Everyday Git
>        faq              Frequently asked questions about using Git
>        glossary         A Git Glossary
>        namespaces       Git namespaces
>        remote-helpers   Helper programs to interact with remote repositories
>        submodules       Mounting one repository inside another
>        tutorial         A tutorial introduction to Git
>        tutorial-2       A tutorial introduction to Git: part two
>        workflows        An overview of recommended workflows with Git
>
>     'git help -a' and 'git help -g' list available subcommands and some
>     concept guides. See 'git help <command>' or 'git help <concept>'
>     to read about a specific subcommand or concept.
>     See 'git help git' for an overview of the system.
>
> What is definitely the case is that there is no hyphen between `git` and
> the first word, maybe in order to distinguish section 1 commands) and
> section 5 and 7 manpages.

Thanks, I'll change the naming, I didn't realize that. For some reason I felt
sure it was the other way. Maybe we should rename `gitmergeconflicts`
to `gitmerge-conflicts` in the other patch series too.

My best guess is that I made that mistake because I (maybe idiosyncratically)
always use `man <page>`  to access the Git man pages, and typing
`man gitdetached-head` feels awkward to me. 
But of course we're recommending that folks use `git help detached-head`,
and `git help detached-head` is much nicer than `git help detachedhead`.

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

"Julia Evans" wrote on the Git mailing list (how to reply to this email):

>> What is definitely the case is that there is no hyphen between `git` and
>> the first word, maybe in order to distinguish section 1 commands) and
>> section 5 and 7 manpages.
>
> Thanks, I'll change the naming, I didn't realize that. For some reason I felt
> sure it was the other way. Maybe we should rename `gitmergeconflicts`
> to `gitmerge-conflicts` in the other patch series too.
>
> My best guess is that I made that mistake because I (maybe idiosyncratically)
> always use `man <page>`  to access the Git man pages, and typing
> `man gitdetached-head` feels awkward to me. 
> But of course we're recommending that folks use `git help detached-head`,
> and `git help detached-head` is much nicer than `git help detachedhead`.

Reflecting on this a little more, I think there's a bit of a disconnect between
how we recommend users use the Git help system (via `git help config`)
and how we refer to other man pages in the man pages, which is like this:

> (see git-fetch(1), git-config(1) and gitmodules(5))

If a user sees a reference like `git-config(1)` or `gitmodules(5)`,
the most obvious way to look it up is to type `man git-config`, or
`man gitmodules` and not to deduce that you can run
`git help config` or `git help modules`: Especially because sometimes
you need to remove a `git-` from the beginning and sometimes just
`git`. I'm don't think the `git-help` man page even explains how those
prefixes work.

So at some point if we want to really encourage people to use
`git help` instead of `man` we might want to revisit how we present
the internal references in the man pages.

Obviously not something to solve right now, and I don't know
what a good solution would be.

@gitgitgadget

gitgitgadget Bot commented Oct 13, 2026

Copy link
Copy Markdown

"Julia Evans" wrote on the Git mailing list (how to reply to this email):

Thanks for the review. I'm going to mark comments that I feel are about style issues
(like "could this be explained in a more clear way?") as just "style comment" to deal with
later.

In general if we really want to make sure that this is as clear as possible, I
think it would be most effective to do a second revision later (after merging
this) where we get feedback from users and find out what folks are confused by.

On Wed, Oct 7, 2026, at 12:43 PM, Junio C Hamano wrote:
> "Julia Evans via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> diff --git a/Documentation/detached-head.adoc b/Documentation/detached-head.adoc
>> new file mode 100644
>> index 0000000000..1e00712124
>> --- /dev/null
>> +++ b/Documentation/detached-head.adoc
>> @@ -0,0 +1,43 @@
>> +`HEAD` is where Git stores your current branch. `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".
>
> HEAD is branch which can either be a branch or something else?

Style comment. Probably this is the one I'd prioritize the highest.

>     HEAD records the state from which your current changes started.
>     This can be a branch (the "current branch"), or it may not be
>     associated with any concrete branch (in which case HEAD is
>     "detached")
>> +It can sometimes be useful for `HEAD` to be a commit ID.
>> +For example, it lets you look at an old version of your code
>> +(with `git checkout COMMIT_ID`).
>
> To "look at" an older state is more versatile than simply running
> 'git show COMMIT_ID:path', as it allows you to do anything you can
> do on a branch, such as building and testing.  Thus,
>
>  	A detached HEAD is useful when you want to tentatively visit
> 	an old state with 'git checkout --detach v0.1', without
> 	having to create a dedicated branch for it.

Style comment.

>> +The only problem is that if you create new commits while in detached
>> +HEAD state, those commits won't be on a branch.
>
> I would suggest rephrasing "The only problem" to "One caveat is" or
> something.  When sightseeing and experimenting, the ability to
> create throw-away commits without the need to clean them up later,
> or to invent a unique name for a temporary branch, is not a problem.
> It is an advantage.

Style comment.

>> 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.
>
> Correct.
>
> Perhaps it is better to clarify this by adding "after you leave
> the detached HEAD state" after "to find later".  While you are in
> the detached HEAD state, you can run 'git log' to find the commits
> you created there, as you can do everything you normally can on a
> branch, or use 'git reflog @{now}' for that matter.
>
>> +There are 3 main ways you can end up in detached HEAD state
>> +unintentionally:
>> +
>> +1. `git checkout COMMIT_ID`, where `COMMIT_ID` is a commit ID
>> +2. `git checkout v1.3`, where v1.3 is a tag name
>
> These I wouldn't call "unintentionally".  They are documented and
> supported ways to do so, and will remain to be so.  "unknowingly"
> may be a more fair way to call these gotchas, though.

Thanks, removed "unintentionally".

>
>> +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.
>
> Correct, but the motivation to 'avoid accidentally ending up' may
> want to be spelled out.  Most often checking out a tag is to go
> sightseeing, where you do not want to 'avoid' detached HEAD.

Style comment.

>> +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.
>
> Very good.
>
>> +If you create commits in detached HEAD state that aren't on a branch,
>> +you can find them later using linkgit:git-reflog[1].
>
> This, as you already said, is "much harder to find later" option.
> There should be a better recovery option described here before
> resorting to "git reflog HEAD" after you switched back.  E.g.,
>
>     If you have created commits in detached HEAD state and then
>     switched away from the state, all is not lost.  "git checkout"
>     would have give you a warning message, like this:
>
> 	Warning: you are leaving 2 commits behind, not connected to
> 	any of your branches
>
> 	c816689 typofix the previous
> 	8e1bff5 decsribe detached HEAD state better
>
>     You can create branches to keep them, e.g., "git branch saved c816689",
>     by using the commit object names left there.

Thanks, added a mention of this warning. It's a really nice example of the ways
Git helps avoid issues.

>> diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
>> index 2aefea0228..4e9e94e24d 100644
>> --- a/Documentation/git-checkout.adoc
>> +++ b/Documentation/git-checkout.adoc
>> @@ -376,135 +376,8 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
>>  [[DETACHED_HEAD]]
>>  DETACHED HEAD
>>  -------------
>> +include::detached-head.adoc[]
>
> Losing the diagrams is a bit concerning.  However, if the intention
> is to have readers learn how HEAD refers to a branch pointing at a
> commit with history structured as a DAG elsewhere in a more basic
> concepts guide, I suspect that it would actually work better.  This
> could even be reduced to a simple "see also" pointer to that other
> guide.
>
> If an existing user only uses 'switch' and never reads 'git help
> checkout', they are already not seeing these diagrams, or perhaps
> they learned the concepts elsewhere.  They still manage to
> understand Git well enough to make use of it, so perhaps this
> approach is OK.  I dunno.

Thanks, good idea to give people a pointer to more info about HEAD.
I'll try adding a pointer to `git help datamodel`.

I'm not against adding diagrams, but I think if we want to add diagrams that
should be a separate project where we actually get feedback from users.
Similarly for explaining `HEAD` in more detail.

@gitgitgadget

gitgitgadget Bot commented Oct 14, 2026

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Julia Evans via GitGitGadget" <gitgitgadget@gmail.com> writes:

> 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 detachedhead`").

Isn't it 'git help detached-head' in this edition?

> Co-authored-by: Marie Claire LeBlanc Flanagan <hello@marieflanagan.com>
> Signed-off-by: Julia Evans <julia@jvns.ca>

Can the other author also sign it off?

> +Git will eventually permanently delete those "garbage" commits during
> +garbage collection. Those commits will be deleted after 90 days by
> +default, see linkgit:git-gc[1] for more.

Does this say 90 days because that is the gc.reflogExpire retention?

If you create a commit, change your mind, rewind the tip of the
branch (e.g., with "git commit --amend"), and then keep working, the
original commit before amending would be unreachable from the tip of
that branch.  Its reflog entry would then expire after
gc.reflogExpireUnreachable, which defaults to 30 days.  If your
commit were created on a detached HEAD, I am not sure offhand how
the thresholds for gc.reflogExpire and gc.reflogExpireUnreachable
come into play for HEAD, as HEAD by default flips back and forth
regularly across branch tips that are not reachable from each other.
When "gc" kicks in, the commit HEAD currently points to is unlikely
to reach the abandoned commit.

It might be safer to either say "after 30 days by default" or
generalize it to "after a few weeks by default" to avoid being
overly prescriptive.

> diff --git a/advice.c b/advice.c
> index 401d047391..43f86c2eaf 100644
> --- a/advice.c
> +++ b/advice.c
> @@ -291,6 +291,7 @@ void detach_advice(const char *new_name)
>  	"\n"
>  	"  git switch -\n"
>  	"\n"
> +	"Run `git help detachedhead` to learn more.\n"

"git help deatched-head" here, too.

>  	fprintf(stderr, fmt, new_name);
> diff --git a/command-list.txt b/command-list.txt
> index 63ae2a67c9..922a1dbd84 100644
> --- a/command-list.txt
> +++ b/command-list.txt
> @@ -218,6 +218,7 @@ gitcore-tutorial                        guide
>  gitcredentials                          guide
>  gitcvs-migration                        guide
>  gitdatamodel                            guide
> +gitdetached-head                         guide
>  gitdiffcore                             guide
>  giteveryday                             guide
>  gitfaq                                  guide

This seems to be manually aligned with SP, so compensate the
addition of "-" between detached and head by removing one SP before
guide on the same line.

Thanks.

@gitgitgadget

gitgitgadget Bot commented Oct 14, 2026

Copy link
Copy Markdown

"Julia Evans" wrote on the Git mailing list (how to reply to this email):

On Wed, Oct 14, 2026, at 2:52 PM, Junio C Hamano wrote:
> "Julia Evans via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> 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 detachedhead`").
>
> Isn't it 'git help detached-head' in this edition?

Will fix.

>> Co-authored-by: Marie Claire LeBlanc Flanagan <hello@marieflanagan.com>
>> Signed-off-by: Julia Evans <julia@jvns.ca>
>
> Can the other author also sign it off?

Will do.

>> +Git will eventually permanently delete those "garbage" commits during
>> +garbage collection. Those commits will be deleted after 90 days by
>> +default, see linkgit:git-gc[1] for more.
>
> Does this say 90 days because that is the gc.reflogExpire retention?

Yes, I wasn't sure how it works exactly.

> If you create a commit, change your mind, rewind the tip of the
> branch (e.g., with "git commit --amend"), and then keep working, the
> original commit before amending would be unreachable from the tip of
> that branch.  Its reflog entry would then expire after
> gc.reflogExpireUnreachable, which defaults to 30 days.  If your
> commit were created on a detached HEAD, I am not sure offhand how
> the thresholds for gc.reflogExpire and gc.reflogExpireUnreachable
> come into play for HEAD, as HEAD by default flips back and forth
> regularly across branch tips that are not reachable from each other.
> When "gc" kicks in, the commit HEAD currently points to is unlikely
> to reach the abandoned commit.
>
> It might be safer to either say "after 30 days by default" or
> generalize it to "after a few weeks by default" to avoid being
> overly prescriptive.

Will change to "30 days", I think "30 days" makes it a tiny bit easier
for someone to figure out where that number might come from.

>> diff --git a/advice.c b/advice.c
>> index 401d047391..43f86c2eaf 100644
>> --- a/advice.c
>> +++ b/advice.c
>> @@ -291,6 +291,7 @@ void detach_advice(const char *new_name)
>>  	"\n"
>>  	"  git switch -\n"
>>  	"\n"
>> +	"Run `git help detachedhead` to learn more.\n"
>
> "git help deatched-head" here, too.

Will do.

>>  	fprintf(stderr, fmt, new_name);
>> diff --git a/command-list.txt b/command-list.txt
>> index 63ae2a67c9..922a1dbd84 100644
>> --- a/command-list.txt
>> +++ b/command-list.txt
>> @@ -218,6 +218,7 @@ gitcore-tutorial                        guide
>>  gitcredentials                          guide
>>  gitcvs-migration                        guide
>>  gitdatamodel                            guide
>> +gitdetached-head                         guide
>>  gitdiffcore                             guide
>>  giteveryday                             guide
>>  gitfaq                                  guide
>
> This seems to be manually aligned with SP, so compensate the
> addition of "-" between detached and head by removing one SP before
> guide on the same line.

Will do.

@jvns
jvns force-pushed the detached-head branch 2 times, most recently from 5a8e2c0 to 0ab8814 Compare October 15, 2026 20:55
@jvns

jvns commented Oct 15, 2026

Copy link
Copy Markdown
Author

/submit

@gitgitgadget

gitgitgadget Bot commented Oct 15, 2026

Copy link
Copy Markdown

Submitted as pull.2250.v3.git.1792097882327.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-2250/jvns/detached-head-v3

To fetch this version to local tag pr-2250/jvns/detached-head-v3:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-2250/jvns/detached-head-v3

@gitgitgadget

gitgitgadget Bot commented Oct 15, 2026

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Julia Evans via GitGitGadget" <gitgitgadget@gmail.com> writes:

>     non-changes: Left these phrases alone because I don't think the proposed
>     changes would make it more clear.

Those who contributed their time to review this document did so
believing they were suggesting improvements.  Simply dismissing
their feedback as "style" in 'v2' without explaining what you
disliked about their suggestions is unhelpful.  It is hard to know
what to make of a dismissal that lacks any supporting reasoning,
other than "I don't think".

> diff --git a/Documentation/detached-head.adoc b/Documentation/detached-head.adoc
> new file mode 100644
> index 0000000000..d8e59e10c6
> --- /dev/null
> +++ b/Documentation/detached-head.adoc
> @@ -0,0 +1,57 @@
> +`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".

Differences between v1 and v3 did not make much improvement in this
area, though.  The phrase "branch or commit" highlights the
distinction, but it fails to clarify what is common to both, which
is equally important.

Perhaps restarting from something along this line

    HEAD records the state from which your current changes started.
    This can be a branch (the "current branch"), or it be a commit,
    (in which case HEAD is "detached").

and polishing further may take us to a better end state.  An
important distinction that is not captured in the above (or your
version) that we may want to cover is that when you are on a branch,
your changes will grow that branch, and when you are not on a
branch, your changes will not affect any branch and stay temporary.

> +It can sometimes be useful for `HEAD` to be a commit ID.
> +For example, it lets you easily look at an old version of your code
> +(with `git checkout COMMIT_ID` or `git switch --detach COMMIT_ID`).

We should use a verb stronger than "look at" to emphasize the
primary advantage of having the entire state visible in the working
tree.  If a user merely wants to read or inspect a single historical
file, they can do so easily without moving their HEAD by running
'git show COMMIT_ID:path/to/file'.  Checking out a commit allows
something much more powerful.  It restores your entire working
directory to that exact state.

    In the detached HEAD state, you can safely build, run, and
    experiment with any historical version of your codebase, without
    having to worry about clobbering your branches, and you do not
    have to clutter your repository with temporary branches.

I said "safely build, run" because you had "old version of your
CODE" in the paragraph, but if you can give examples outside the
programming area, that would be even better.  E.g.,

- Writers may able to rebuild and locally preview a complete
  historical version of a book or documentation site to see how
  cross-references and formatting rendered at that time.

- Game Designers may open an entire historical scene in an editor
  (like Unity or Blender) where assets, textures, and coordinates
  must all exist in a precise, interconnected state to load
  correctly.

- Data Scientists may execute and reproduce a specific historical
  data pipeline or model run using the exact combination of scripts
  and datasets that existed on that day.


> +There are 3 main ways you can end up in detached HEAD state:
> +
> +1. `git checkout 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 commit for that tag.

A more standard phrase for "the commit for that tag" is "the tagged
commit".

Because we will recommend "switch" to be a way to require to be more
explicit when users mean to go into the detached HEAD state soon
below, I think we would want to move the "checkout <COMMIT>" and
"switch --detach <COMMIT>" description to here from the earlier
"sometimes useful" paragraph, to complement the above list of ways
to detach your HEAD.

> +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 3
> +-------------------------

Your readers can guess that the 7 digit hex may probably be the
commit object name you are about to lose, but you should use a bit
more realistic descriptive title, not "3", to avoid confusion.  I
know many users do not write a good log message, but even a single
word "bugfix" is better than "3" ;-)

@gitgitgadget

gitgitgadget Bot commented Oct 16, 2026

Copy link
Copy Markdown

"Julia Evans" wrote on the Git mailing list (how to reply to this email):

On Thu, Oct 15, 2026, at 6:39 PM, Junio C Hamano wrote:
> "Julia Evans via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>>     non-changes: Left these phrases alone because I don't think the proposed
>>     changes would make it more clear.
>
> Those who contributed their time to review this document did so
> believing they were suggesting improvements.  Simply dismissing
> their feedback as "style" in 'v2' without explaining what you
> disliked about their suggestions is unhelpful.  It is hard to know
> what to make of a dismissal that lacks any supporting reasoning,
> other than "I don't think".

My job to is write clear technical explanations and a big part of what I do
is to try to reconcile the point of view of experts (who understand how the
software works) and users (who know which parts are confusing).
I think we've talked about this "curse of knowledge" issue before.

I'm always happy to have a detailed discussion when it comes to accuracy,
and I'm extremely grateful for the time everyone puts in when it comes
to pointing out accuracy issues, and when figuring out when it's appropriate
to occasionally tell a "white lie" :)

The reason I was a little brief is that these in depth discussions about how
to explain a concept as clearly as possible to users are not effective if
there are no users involved in the process. It takes a lot of time without
making substantial improvements.

There are some more ideas on this site about having a "user-centric"
mindset, like using learner personas. Happy to talk about that if you think
it would be helpful. Sometimes tone can get lost here so let me know if it
would be helpful to have a video call!
https://www.writethedocs.org/blog/newsletter-june-2025/#help-contributors-think-like-users

>> diff --git a/Documentation/detached-head.adoc b/Documentation/detached-head.adoc
>> new file mode 100644
>> index 0000000000..d8e59e10c6
>> --- /dev/null
>> +++ b/Documentation/detached-head.adoc
>> @@ -0,0 +1,57 @@
>> +`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".
>
> Differences between v1 and v3 did not make much improvement in this
> area, though.  The phrase "branch or commit" highlights the
> distinction, but it fails to clarify what is common to both, which
> is equally important.
>
> Perhaps restarting from something along this line
>
>     HEAD records the state from which your current changes started.
>     This can be a branch (the "current branch"), or it be a commit,
>     (in which case HEAD is "detached").
>
> and polishing further may take us to a better end state.  An
> important distinction that is not captured in the above (or your
> version) that we may want to cover is that when you are on a branch,
> your changes will grow that branch, and when you are not on a
> branch, your changes will not affect any branch and stay temporary.

This is a case where if we really want to make sure it's clear, we need
to go get feedback from users. There's likely a better way to explain it
but I don't think that this conversation will result in an improvement.
Really doing a good job of explaining HEAD is a pretty big project.

It's not clear to me if you think the current wording is unusable
or if you just think it could be better.

>> +It can sometimes be useful for `HEAD` to be a commit ID.
>> +For example, it lets you easily look at an old version of your code
>> +(with `git checkout COMMIT_ID` or `git switch --detach COMMIT_ID`).
>
> We should use a verb stronger than "look at" to emphasize the
> primary advantage of having the entire state visible in the working
> tree.  If a user merely wants to read or inspect a single historical
> file, they can do so easily without moving their HEAD by running
> 'git show COMMIT_ID:path/to/file'.  Checking out a commit allows
> something much more powerful.  It restores your entire working
> directory to that exact state.

Changed this to "check out an old version of your project".

>     In the detached HEAD state, you can safely build, run, and
>     experiment with any historical version of your codebase, without
>     having to worry about clobbering your branches, and you do not
>     have to clutter your repository with temporary branches.
>
> I said "safely build, run" because you had "old version of your
> CODE" in the paragraph, but if you can give examples outside the
> programming area, that would be even better.  E.g.,
>
> - Writers may able to rebuild and locally preview a complete
>   historical version of a book or documentation site to see how
>   cross-references and formatting rendered at that time.
>
> - Game Designers may open an entire historical scene in an editor
>   (like Unity or Blender) where assets, textures, and coordinates
>   must all exist in a precise, interconnected state to load
>   correctly.
>
> - Data Scientists may execute and reproduce a specific historical
>   data pipeline or model run using the exact combination of scripts
>   and datasets that existed on that day.

I like these examples, added a shorter version of a couple of
them. Also replaced "code" with "project" to make it more general.

>
>> +There are 3 main ways you can end up in detached HEAD state:
>> +
>> +1. `git checkout 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 commit for that tag.
>
> A more standard phrase for "the commit for that tag" is "the tagged
> commit".

Will do.

> Because we will recommend "switch" to be a way to require to be more
> explicit when users mean to go into the detached HEAD state soon
> below, I think we would want to move the "checkout <COMMIT>" and
> "switch --detach <COMMIT>" description to here from the earlier
> "sometimes useful" paragraph, to complement the above list of ways
> to detach your HEAD.

Agreed there's some duplication here we can fix, will do.

>> +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 3
>> +-------------------------
>
> Your readers can guess that the 7 digit hex may probably be the
> commit object name you are about to lose, but you should use a bit
> more realistic descriptive title, not "3", to avoid confusion.  I
> know many users do not write a good log message, but even a single
> word "bugfix" is better than "3" ;-)

Oops, I meant to fix that before, thanks.

@gitgitgadget

gitgitgadget Bot commented Oct 16, 2026

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Julia Evans" <julia@jvns.ca> writes:

> This is a case where if we really want to make sure it's clear, we need
> to go get feedback from users. There's likely a better way to explain it
> but I don't think that this conversation will result in an improvement.
> Really doing a good job of explaining HEAD is a pretty big project.
>
> It's not clear to me if you think the current wording is unusable
> or if you just think it could be better.

Then let me make it clear.

I do not know if your version is unusable or good enough, but if you
are going to take one version to guinea-pig readers to test and
polish starting from that version, I think what I gave you would
give us a better starting point.

Otherwise I would not be suggesting replacements (this applies to
all other points in the message you are responding to).

Thanks.

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>
@gitgitgadget

gitgitgadget Bot commented Oct 16, 2026

Copy link
Copy Markdown

There are issues in commit b846b00:
fixup! doc: checkout: rewrite detached HEAD state explanation

  • Commit checks stopped - the message is too short
  • Rebase needed to squash commit
  • Commit not signed off

@jvns

jvns commented Oct 16, 2026

Copy link
Copy Markdown
Author

/submit

@gitgitgadget

gitgitgadget Bot commented Oct 16, 2026

Copy link
Copy Markdown

Submitted as pull.2250.v4.git.1792177409868.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-2250/jvns/detached-head-v4

To fetch this version to local tag pr-2250/jvns/detached-head-v4:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-2250/jvns/detached-head-v4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant