What does detached HEAD mean in Git, and how do you keep commits made in that state?
answer
- HEAD stops naming and starts holding
- commits still work, that is the trap
- nothing in refs/ reaches the tip
- give the tip a name before you leave
basics
~10 sDetached HEAD means HEAD holds a commit hash directly instead of naming a branch. Commits still work, but nothing points at them, so create a branch at that commit before switching away.
solid answer
~40 sNormally HEAD is a symbolic ref containing `ref: refs/heads/<branch>`. When you check out a commit id, a tag, or a remote-tracking ref, Git writes the raw hash into HEAD instead — that is **detached HEAD**. Everything still works: you can build, test, and commit, and each commit's parent is the previous one, with HEAD itself moving forward. What is missing is a branch ref recording the tip, so as soon as you switch away, nothing in `refs/` reaches those commits and they become unreferenced, eligible for eventual pruning. The fix is trivial and should be done before leaving: `git switch -c <name>` while still detached, or `git branch <name> <hash>` afterwards using the hash Git printed when you left.
code
console · 7 lines$ git switch --detach HEAD
HEAD is now at d2b5d53 add f
$ cat .git/HEAD
d2b5d5322e03b5733ebfa5debcd995cfc63f718f
$ git symbolic-ref HEAD
fatal: ref HEAD is not a symbolic ref
$ git switch -c experimentgo deeper
Recognise the warning message, know HEAD is holding a commit id rather than a branch name, and remember git switch -c <name> before moving away if you committed anything.
Explain the mechanics: with no branch to update, the new commit hash is written into HEAD itself, so the chain has no ref at its tip once you leave.
Treat it as routine rather than alarming — detaching is how tag checkouts and bisect work — and be able to recover a lost tip from the reflog or the id Git printed on the way out.
Frame the general principle for the team: reachability from refs is what makes work durable, so tooling and automation that operate on commits should always leave a named ref behind rather than relying on transient state.
## Attached versus detached HEAD has two possible contents. **Attached**: the text `ref: refs/heads/main`, meaning "the current branch is main". **Detached**: a bare object id. Both are valid states; detached is not an error, and Git tells you plainly (`You are in 'detached HEAD' state`) because the consequences are easy to miss. You land there by checking out anything that is not a local branch: a commit id, a tag name, a remote-tracking ref, or explicitly with `git switch --detach`. Several operations also detach internally — a rebase replays commits with HEAD detached before finally moving the branch, which is why an interrupted rebase can leave you detached. ## What still works, and what does not Committing works normally: Git makes the commit with the current HEAD commit as parent and writes the new hash *into HEAD itself*, since there is no branch to update. So a chain of commits builds up perfectly well. Nothing is lost while you stay there. The hazard is leaving. Refs are what make commits reachable, and a detached chain has no ref at its tip. Switch back to a branch and those commits are referenced by nothing in `refs/` — they survive for a while because HEAD's reflog still records where you were, but they are candidates for pruning once that expires, and they will not appear in ordinary history listings. ## Keeping the work Before switching away: `git switch -c experiment` creates a branch at the current commit and attaches HEAD to it. Nothing is copied; the commits already exist, you are only giving the tip a name. After switching away: Git prints the abandoned commit id in its warning. `git branch experiment <hash>` recovers it. If you did not keep the hash, `git reflog` lists where HEAD has been, including the detached positions. ## When detaching is the right tool Detached HEAD is genuinely useful, not just a trap. Checking out a tag to build a release, inspecting an old commit's working tree, or bisecting all put you in a detached state deliberately, because none of those positions should be named by a branch. The discipline is simply: if you *create* commits while detached, name them before you leave. ## Related transient refs Drastic HEAD moves record the previous position in `ORIG_HEAD`, a plain ref at the top of the repository written by commands such as merge, rebase, reset and am. `git reset --hard ORIG_HEAD` therefore undoes the position change from the most recent such command — but it stores only the single last value, so the reflog remains the reliable history when several operations have run.
- Which everyday actions put you into detached HEAD without asking?Checking out anything that is not a local branch: a commit id, a tag, or a remote-tracking ref. Bisect runs detached by design, and rebase detaches internally while replaying, so an aborted or stopped rebase can leave you there.
- What is ORIG_HEAD, and which commands set it?A ref at the top level of the repository holding where HEAD was before a drastic move — written by `git merge`, `git rebase`, `git reset` and `git am`, so also by a pull that merges. `git reset --hard ORIG_HEAD` reverts that move. It keeps only the single most recent value, so the reflog is the fuller record.
- Are commits made in a detached HEAD deleted the moment you switch branches?No. They remain in the object database and are still reachable through HEAD's reflog, so recovery is straightforward for as long as that entry survives. They are merely unreferenced by any branch or tag, which makes them invisible to normal history views and eventual candidates for pruning.
saying these in an interview costs you the question
- Says commits made in detached HEAD are impossible or rejected
- Thinks detached HEAD deletes the work instantly on switching
- Believes you must cherry-pick to rescue detached commits
- Calls detached HEAD a corrupted repository state