Part IV: Chapter 4.4

Detached HEAD

What it means, why it's not scary, how to escape.

Normally .git/HEADcontains a branch name — a symbolic reference. A "detached HEAD" means HEAD contains a raw commit hash instead. Git is no longer tracking a branch; it is pointing directly at a commit node in the graph.

The Detached HEAD Playground

Step through the scenario: detach HEAD, make a commit, then leave without saving it — and watch it float away. Then try the rescue.

9f8aInitial commit
a1b2Add homepage
mainHEAD
b2c3Fix nav spacing

Why It Happens

You end up in detached HEAD any time you point HEAD directly at a commit hash rather than a branch name:

  • git checkout <hash> — checkout a specific commit
  • git checkout v1.2.0 — checkout a tag (tags point at commits)
  • git checkout HEAD~3 — navigate to a commit relative to HEAD
  • Certain rebase and bisect operations

It is a perfectly valid state for reading or exploring old code. The risk only appears if you commit while in this state.

The Actual Danger

If you make commits while detached and then switch back to a branch, those commits become unreachable. No branch pointer leads to them. They will not appear in git log. Git's garbage collector will eventually delete them.

The commits are not deleted immediately — they linger in .git/objects/ for about 30 days. You can recover them with git reflog, which keeps a local journal of every position HEAD has ever been at.

This is why Git prints a warning when you enter detached HEAD state — it is telling you to create a branch before you commit anything.

Three Ways to Escape

Depending on whether you made commits you want to keep, your escape route differs.

Terminal
$# You're in detached HEAD. No new commits — just go back:
$git switch main
Switched to branch 'main'
$
$# You made commits and want to KEEP them — create a branch first:
$git switch -c my-experiment
Switched to a new branch 'my-experiment'
# Now those commits are safely anchored.
$
$# You left without saving and need to recover — use reflog:
$git reflog
c4d5e6f HEAD@{0}: checkout: moving from a1b2c3d to main
a1b2c3d HEAD@{1}: commit: Ghost Work
9f8a357 HEAD@{2}: checkout: moving from main to 9f8a357
$git switch -c recovered a1b2c3d
Switched to a new branch 'recovered'