Detached HEAD in Git: what it means and how to escape
A git detached HEAD means you are on a commit, not a branch. Commits made here are unreachable the moment you leave, and the reflog gets them back.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
This is not an error. It means HEAD is pointing at a commit instead of at a branch, which happens whenever you check out a commit hash, a tag, or a remote-tracking branch directly.
Nothing is broken. But commits you make here are attached to nothing, and switching away leaves them unreachable. Everything below was measured on git 2.50.0.
What HEAD normally is
HEAD is a file, and normally it contains the name of a branch:
cat .git/HEAD
ref: refs/heads/main
That indirection is the entire mechanism. You are on main, main points at a commit, and committing moves main forward automatically.
Check out a hash and HEAD holds the hash directly instead of a branch name. There is no branch in the chain, so there is nothing for a new commit to move. That is what "detached" means: HEAD has been detached from a branch and attached straight to a commit.
Confirming it
git status
HEAD detached at ce77ee1
nothing to commit, working tree clean
A quicker check, useful in scripts and prompts:
git branch --show-current
Measured, that prints an empty line when detached. On a branch it prints the branch name. An empty result is the signal.
How you got here
git checkout <hash>, to look at an old version.git checkout v1.2.0, because a tag names a commit, not a branch.git checkout origin/main, because remote-tracking branches are checked out detached by design.- A rebase in progress, which moves through commits one at a time and is temporarily detached. This is normal and ends when the rebase does.
- A CI job, most of which check out a commit rather than a branch.
The tag case catches people most often, because v1.2.0 looks like a branch name.
Leaving when you have made no commits
Just go back:
git switch - # the branch you were on before
git switch main # or name one
Nothing is lost, because nothing was created. This is the ordinary case: you looked at an old commit, you are done looking.
The case that loses work
Commits made in detached HEAD belong to nothing. Measured: create a commit while detached, note its hash, switch back to main, and then check whether main can reach it.
made commit bf12deb
switching to main...
is it reachable from main? NO, it is unreachable
The commit exists in the repository. No branch points at it, nothing in your history leads to it, and git log will never show it. It will sit there until git garbage-collects unreachable objects, typically after about thirty days, and then it is genuinely gone.
Git does warn you on the way out, with a message naming the commit and suggesting a branch. It scrolls past easily.
Getting it back with the reflog
The reflog records every position HEAD has held, including ones nothing points at any more:
git reflog
50fc8d6 HEAD@{0}: checkout: moving from bf12deb... to main
bf12deb HEAD@{1}: commit: work made while detached
ce77ee1 HEAD@{2}: checkout: moving from main to ce77ee1
The middle line is the orphaned commit, with its message and its hash. Give it a branch and it is reachable again:
git branch rescued bf12deb
rescued: bf12deb work made while detached
Recovered. The reflog is the closest thing git has to a universal undo, and it covers far more than this: a reset --hard you regret, a branch you deleted, a rebase that went wrong. The one thing it cannot recover is work that was never committed, because git never had a copy.
Note that the reflog is per-clone and local. It is not pushed and not shared, so it only helps on the machine where the mistake happened.
The right way to work from a detached HEAD
If you check out an old commit and decide you want to build on it, create a branch before you commit rather than after:
git switch -c experiment
That takes the commit you are sitting on and starts a branch there, attaching HEAD to it. Everything afterwards behaves normally, and the recovery dance above never becomes necessary.
If you have already made the commits, the same command still works, because a branch created now points at your current commit and everything behind it:
git switch -c experiment # while still detached
The order that costs you is committing, then switching away, then remembering.
Using it on purpose
Three everyday operations put you here deliberately, and knowing that turns the state from an accident into a tool.
Looking at an old version. Check out a commit, run the code, look around, switch back. Nothing is created, nothing is lost, and it is much cleaner than creating a throwaway branch every time you want to read old code.
git checkout v1.2.0
# poke around
git switch -
Bisecting. git bisect finds the commit that introduced a bug by checking out commits in a binary search, each one detached:
git bisect start
git bisect bad # the current commit is broken
git bisect good v1.2.0 # this one worked
# git checks out a commit between them; test it, then:
git bisect good # or: git bisect bad
Git narrows the range each time and eventually names the commit that broke things. git bisect reset returns you to where you started. This is the single most useful thing detached HEAD enables, and it turns "when did this break" from an afternoon into a few minutes.
Building a release from a tag. CI systems check out a tag by hash, which is detached by definition, because a build should be pinned to an exact commit rather than to whatever a branch points at today.
More about the reflog
The reflog deserves more attention than it gets, because it is the answer to most of the questions that begin "I think I just lost".
git reflog # HEAD's history
git reflog show main # one branch's history
Every entry records where a ref pointed and what moved it: commit, checkout, reset, rebase, merge. So it covers far more than detached commits:
git reset --hard HEAD@{1} # undo a reset --hard
git branch rescued HEAD@{5} # recover a deleted branch's tip
git reset --hard ORIG_HEAD # undo a merge or rebase
HEAD@{n} means "where HEAD was n moves ago", and it is a valid reference anywhere a hash would be.
Three limits worth knowing. The reflog is local, so it does not help on a different machine. It is per-clone, so a fresh clone has an empty one. And entries expire, by default after 90 days for reachable commits and 30 for unreachable ones, after which garbage collection can remove the objects.
Within those limits it is close to a complete undo history, and it is the first thing to reach for before concluding that anything is gone.
Why git allows this at all
It would be easy to see detached HEAD as a design flaw. It is closer to the opposite: it falls out of HEAD being a pointer that can point at either a branch or a commit, and that flexibility is what makes several ordinary operations possible.
Checking out a tag to build a release, bisecting to find where a bug appeared, and rebasing all rely on being able to sit on a commit without a branch. git bisect in particular parks you on a series of detached commits and expects you to be comfortable there.
So it is worth being at home in this state rather than avoiding it. The only rule to remember is: if you commit, make a branch.
Telling your prompt to warn you
Most of the risk here comes from not noticing. A shell prompt that shows the current branch removes the problem, because a detached HEAD shows a hash instead of a name and the difference is obvious every time you look.
Most git prompt integrations do this by default. If you are configuring one yourself, git branch --show-current printing nothing is the condition to check, since it is empty exactly when HEAD is detached.
The same applies to scripts. Anything that assumes it is on a branch should verify it rather than discover the problem later:
if [ -z "$(git branch --show-current)" ]; then
echo "detached HEAD, refusing to continue"
exit 1
fi
Three common mistakes
Panicking and running git reset --hard. Detached HEAD is not a broken state and nothing needs resetting. If you have made commits, --hard here is how you turn a recoverable situation into a genuinely lost one.
Reading the warning as an error. The message is long, appears in yellow, and says nothing is wrong. People close the terminal on it. The only sentence that matters is the one about commits being discardable, and it is telling you the truth.
Making a branch after switching away rather than before. Both work, because the reflog remembers, but one of them requires finding a hash in a log and the other is a single command while you are already standing on the commit. Create the branch before you leave.
Quick reference
git status # "HEAD detached at ..."
git branch --show-current # empty when detached
git switch - # back to the previous branch
git switch -c <name> # keep the work, on a new branch
git reflog # every position HEAD has held
git branch <name> <hash> # rescue an unreachable commit
Want to detach, commit, and rescue it somewhere safe? Work through Git basics in a real repository in the browser, or read undoing your last commit for the other place the reflog saves you.
More from the blog
How to resolve merge conflicts in Git
A Git merge conflict happens when two branches change the same lines. How to read the markers, resolve each file, and the check that stops a bad commit.
Read moreGit vs GitHub: they are not the same thing
Git vs GitHub: Git is the version control tool on your computer, GitHub is a website that hosts Git repositories. What each one does and how they fit.
Read moreReady to write some code?
Put this into practice - start your first free lesson. No setup, no credit card.