Command Palette

Search for a command to run...

Hectal
PHASE 2Intermediate ~11 min· topic 5 of 5

Topic 2.5

Detached HEAD, and checkout vs switch vs restore

In one line

Normally HEAD points at a branch, and new commits move that branch. In 'detached HEAD' state, HEAD points straight at a commit, which happens when you check out a tag, an old commit, or during rebase, bisect, and CI. It's safe for looking around, but new commits belong to no branch and become hard to find once you leave. Create a branch (git switch -c name) to keep them. Since Git 2.23, git switch (branches) and git restore (files) split up the old do-everything git checkout.

0/5 · 0%

Think of it like this

Reading an old edition of a book in the library. You can read anything, and even scribble notes on loose sheets. But if you walk away without clipping the sheets into your own notebook (a branch), the cleaner throws them out.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

HEAD
Git's pointer to what you currently have checked out, usually a branch.
Detached HEAD
HEAD pointing directly at a commit instead of a branch.
Orphaned commit
A commit that no branch or tag points to, findable only through the reflog until cleanup.
git switch
Change branches, or create one with -c (Git 2.23+).
git restore
Restore files in the working directory or staging area (Git 2.23+).
git checkout
The older command that can do both, depending on its arguments.

Step by step

01Looking at an old release

A customer says delivery fees were different in v1.6.0. Priya checks out the tag to look, which detaches HEAD. Git explains what that means.

terminal
$ git checkout v1.6.0
git status | head -1
── expected output ──
Note: switching to 'v1.6.0'.
 
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.
 
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
 
git switch -c <new-branch-name>
 
HEAD is now at 7f0c1b9 Show dish photos at full size
HEAD detached at v1.6.0
Looking at an old releasediagram
Rendering diagram…

02Committing while detached

She tries a quick experiment and commits it. The commit exists, but only HEAD points to it, and no branch does.

terminal
$ git commit -am "Try old fee formula"
git log --oneline --graph --all -3
── expected output ──
[detached HEAD a1b2c3d] Try old fee formula
1 file changed, 2 insertions(+), 1 deletion(-)
* a1b2c3d (HEAD) Try old fee formula
| * 0e9d8c7 (origin/main, main) Merge branch 'feature/coupons'
|/
* 7f0c1b9 (tag: v1.6.0) Show dish photos at full size

03Leaving, and the warning

When she switches back to main, Git warns that a commit is being left behind and shows its hash. If she wants it, one command saves it on a branch. If it was just an experiment, she can ignore it, and it will be cleaned up eventually.

terminal
$ git switch main
── expected output ──
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
 
a1b2c3d Try old fee formula
 
If you want to keep it by creating a new branch, this may be a good time
to do so with:
 
git branch <new-branch-name> a1b2c3d
 
Switched to branch 'main'

04checkout vs switch vs restore

git checkout decides what to do from its arguments: a branch name switches branches, but a file name silently overwrites your file with the staged version. The newer commands make the intent explicit.

old command → modern commandwhole filetext
git checkout feature/coupons        →  git switch feature/coupons
git checkout -b feature/coupons     →  git switch -c feature/coupons
git checkout v1.6.0                 →  git switch --detach v1.6.0
git checkout -- src/cart.js         →  git restore src/cart.js
git checkout main -- src/cart.js    →  git restore --source=main src/cart.js
git reset HEAD src/cart.js          →  git restore --staged src/cart.js

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

A day of work on no branch

After a bisect (Topic 6.2) Arjun forgets git bisect reset, works all afternoon, commits three times, then runs git switch main, ignoring the warning.

terminal
$ git log --oneline --all | grep -c 'rider rating'
── what you'll see ──
0
His three commits don't appear in any branch's log.

Myth vs fact

Myth

Detached HEAD means something is broken.

Fact

It's a normal state for inspecting old code, and Git uses it during rebase and bisect. It only matters if you commit there and forget to create a branch.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    CI systems usually check out a specific commit in detached HEAD state, so scripts that run git branch --show-current get an empty string there. Use the CI's variables (for example GITHUB_REF_NAME) or git rev-parse HEAD instead.

Remember this

  1. 1

    Attached (normal): HEAD → main → commit. Committing moves main.

  2. 2

    Detached: HEAD → commit. Committing creates commits no branch points to.

  3. 3

    You get there by git switch --detach v1.6.0, git checkout <hash> or a tag, and also automatically during rebase, bisect, and in most CI checkouts.

  4. 4

    git status says HEAD detached at …, and many shell prompts show a hash instead of a branch name.

  5. 5

    Keep work made there: git switch -c new-branch before leaving. Already left? Git prints the hash in a warning, and the reflog has it (Topic 5.3).

  6. 6

    git checkout does two unrelated jobs: switch branches and overwrite files. That's why git checkout file (which discards changes) was a common accident. Use git switch for branches and git restore for files.

Explain it without notes

01

What changes when HEAD is detached?

02

Why were git switch and git restore added?

Practice

01

Check out an old commit, make a commit there, and save it on a branch.

Trade-offs

  • ↔

    Detached HEAD is quick for inspecting any point in history without creating branches, but committing there is risky. A throwaway branch costs nothing and is safer for experiments.

Done when you can

  • I can recognise detached HEAD.

  • I save detached commits with git switch -c.

  • I use switch for branches and restore for files.