Why did my Git repo enter a detached HEAD state?

Viewed 310657
14 Answers

Detached HEAD means that what's currently checked out is not a local branch.

Some scenarios that will result in a Detached HEAD state:

  • If you checkout a remote branch, say origin/master. This is a read-only branch. Thus, when creating a commit from origin/master it will be free-floating, i.e. not connected to any branch.

  • If you checkout a specific tag or commit. When doing a new commit from here, it will again be free-floating, i.e. not connected to any branch. Note that when a branch is checked out, new commits always gets automatically placed at the tip.

    When you want to go back and checkout a specific commit or tag to start working from there, you could create a new branch originating from that commit and switch to it by git checkout -b new_branch_name. This will prevent the Detached HEAD state as you now have a branch checked out and not a commit.

It can happen if you have a tag named same as a branch.

Example: if "release/0.1" is tag name, then

git checkout release/0.1

produces detached HEAD at "release/0.1". If you expect release/0.1 to be a branch name, then you get confused.

If git was to rename detached HEAD I would have it named as a HEAD that isn’t identified by a branch and will soon be forgotten.

We as people can easily remember branch names. We do git checkout new-button-feature / git checkout main. main and new-button-feature are easy to remember. And we can just do git branch and get a list of all branches. But to do the same with just commits you'd have to do git reflog which is very tedious. Because you have thousands of commits but only very few branches.

A detached commit’s identifier is just its SHA. So suppose you checked out a commit (not a branch) i.e. you did git checkout d747dd10e450871928a56c9cb7c6577cf61fdf31 you'll get:

Note: checking out 'd747dd10e450871928a56c9cb7c6577cf61fdf31'.

You are in 'detached HEAD' state.

...

Then if you made some changes and made a commit, you're still NOT on a branch.

Do you think you'd remember the commit SHA? You won't!

git doesn't want this to happen. Hence it's informing your HEAD is not associated to a branch so you're more inclined to checkout a new branch. As a result below that message it also says:

If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example:

git checkout -b


To go a bit deeper a branch is built in a way that it's smart. It will update its HEAD as you make commits. Tags on the other hand are not meant to be like that. If you checkout a tag, then you're again on a detached HEAD. The main reason is that if you make a new commit from that tag then given that that commit is not referenced by anything (not any branch or tag) then still its considered a detached HEAD.

Attached HEADs can only happen when you're on a branch.

For more see here

HEAD is a pointer, and it points — directly or indirectly — to a particular commit:

Attached HEAD means that it is attached to some branch (i.e. it points to a branch).

Detached HEAD means that it is not attached to any branch, i.e. it points directly to some commit.

To look at from another angle, if you're on a branch and do cat .git/HEAD you'd get:

ref: refs/heads/Your-current-branch-name

Then if you do cat refs/heads/Your-current-branch-name then you'd also see the SHA of the commit that your branch is pointing/referencing to.

However if you were on a detached HEAD you and cat .git/HEAD you'd just get the SHA of the commit and nothing more:

639ce5dd952a645b7c3fcbe89e88e3dd081a9912

By nothing more I mean the head isn't pointing to any branch. It's just directly pointing to a commit.


As a result of all this, anytime you checkout a commit (without using the branch name to checkout), even if that commit was the latest commit of your main branch, you're still in a detached HEAD because your HEAD is not pointing to any of your local branches. Hence even checking out a tag will put you in a detached HEAD. To add onto that, even checking out a remote branch that you have fetched into your computer would result in a detached head ie git checkout origin main would also end up as a detached head...

Summary

All of the following will cause detached head:

  • checkout any commit
  • checkout any tag
  • checkout any remote branch

You're only on an attached head, if you've checked out a local branch


Special thanks to Josh Caswell & Saagar Jha in helping me figure this out.

When you checkout to a commit git checkout <commit-hash> or to a remote branch your HEAD will get detached and try to create a new commit on it.

Commits that are not reachable by any branch or tag will be garbage collected and removed from the repository after 30 days.

Another way to solve this is by creating a new branch for the newly created commit and checkout to it. git checkout -b <branch-name> <commit-hash>

This article illustrates how you can get to detached HEAD state.

Detached HEAD

HEAD is a pointer to the currently checked out branch or commit, it answers the question: Where am I right now in the repository? HEAD can be in either of two states, attached (default) or detached, depending on if you've checked out a local branch or not.

OP: How did I end up with a detached HEAD?

Operations than leave HEAD in detached state

Ending up in detached HEAD state can be due to various reasons, below are 5 common cases (perhaps did you do any of the following):

  • Checking out a particular commit using its hash, i.e.
    $ git checkout 14ko3
    
  • Explicitly checking out a remote branch, i.e.
    $ git checkout origin/master
    
  • Switching to a branch using the detached flag (Git 2.23), i.e.
    $ git switch master --detached
    
  • Checking out a tag, i.e.
    $ git checkout v1.0.1
    
  • Performing an interactive rebase (or a regular rebase containing conflicting changes), i.e.
    $ git rebase master feature-1 --interactive
    

In detached state experimental changes can be made without impacting any existing branch. See infographic below illustrating the difference between committing in attached vs detached state.

Comparison between detached and attached HEAD state

A common misconception is that the message You are in 'detached HEAD' state is of erroneous tone, when in fact it just describes how HEAD is referencing the current snapshot.

Moving from detached to attached state

To move from detached to attached state, you can either create a new branch from where you're at, or switch back to an existing branch.

Note: any commits created in detached state will eventually (post garbage collection) be discarded if you switch to another existing branch, without first persisting your changes in a new branch.

Source: Above excerpt is taken from this full length post on the subject: What is HEAD in Git?

Following VonC's comment, here is the short version of how I resolved this same 'detached HEAD' issue.

  1. Created a branch in my remote; origin/feature/dev
  2. In my local run git fetch, so now my local will be aware of this new remote branch
  3. Now run git switch feature/dev, and we are done!

For my case it happens this way:

  • Create a new branch (feb_debugging).
  • Run git fetch
  • I see new branch (feb_debugging) pulled
  • Now, I use git checkout origin/feb_debugging

Here it gets me to HEAD is now at....

For fixing I just need another checkout

  • git checkout feb_debugging
  • Now git says that I am at feb_debugging branch.

For others who may be looking into a way of getting the branch name but are getting HEAD instead, here is what I came up with:

const { execSync } = require('child_process');

const getBranchName = () => {
  let branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
  if (branch === 'HEAD') branch = execSync(`git branch -a --contains HEAD | sed -n 2p | awk '{ printf $1 }'`).toString().trim();
  return branch;
}
Related