Overview
A detached HEAD means your working directory points to a commit, not a branch. Commits you make here are valid but unreferenced by any branch. To keep or move them, create a branch or cherry-pick onto the right branch. If you’ve already moved away, recover with the reflog.
Common DevOps scenario: CI systems often check out a specific commit (detached HEAD). If build scripts commit changes, you must explicitly save and move those commits.
Quickstart (TL;DR)
- Still detached and want to keep the commit(s):
- Save them: git switch -c rescue/detached
- Go to target branch: git switch main
- Apply changes: git cherry-pick rescue/detached
- Clean up: git branch -d rescue/detached
- Already left the detached state:
- Find the commit: git reflog, note the SHA
- Save it: git branch rescue/<name> <sha>
- Move to target branch and cherry-pick: git switch main && git cherry-pick rescue/<name>
- Want to discard: simply switch back to a branch and don’t reference the commit. It will be garbage-collected later.
Minimal Working Example
This recreates the problem and shows the fix.
# Setup
mkdir demo && cd demo
git init -q
git commit --allow-empty -m "init"
git commit --allow-empty -m "second"
# Detach HEAD at the previous commit
git switch --detach HEAD~1 # or: git checkout HEAD~1
# Accidental commit on detached HEAD
echo oops > file.txt
git add file.txt
git commit -m "oops on detached head"
# Save the detached commit by creating a branch at it
git switch -c rescue/oops
# Move it onto main (or your target branch)
git switch main
git cherry-pick rescue/oops
# Verify and clean up
git log --oneline -n 3
rm -f file.txt # only if you want to clean working tree artifacts
git branch -d rescue/oops
Step-by-Step Scenarios
1) You are still on a detached HEAD and want to keep the commit(s)
- Create a branch pointing at the current commit:
- git switch -c rescue/detached
- Legacy: git checkout -b rescue/detached
- Switch to the intended branch:
- git switch main
- Bring the changes over:
- Single commit: git cherry-pick rescue/detached
- Multiple commits on rescue: cherry-pick a range, e.g. git cherry-pick rescue/detached~2..rescue/detached
- Delete the rescue branch if no longer needed:
- git branch -d rescue/detached
2) You left the detached commit behind (lost track)
- Inspect the reflog to find the commit SHA:
- git reflog
- Look for entries like HEAD@{...}: commit: oops on detached head
- Restore a reference to it:
- git branch rescue/found <sha>
- Move onto your target branch and apply:
- git switch main
- git cherry-pick rescue/found
If reflog is missing or expired, see FAQ for recovery options.
3) You want to discard the detached commit
- Do nothing special: switch back to a branch and continue your work:
- git switch main
- As long as no reference (branch or tag) points to the detached commit, it will be pruned by GC later.
4) You want your branch to point exactly to the detached commit
Use only if the branch is unpublished or you coordinate a history rewrite.
git switch my-branch
git reset --hard <detached-sha> # rewrites history!
# If pushed, you must force-push
# git push --force-with-lease
Prefer cherry-pick for shared branches; use reset only for private or coordinated workflows.
Pitfalls
- Losing the commit to GC: Detached commits without references are prunable after a grace period. Create a branch ASAP if you care about them.
- Rewriting public history: git reset --hard on a published branch requires a force-push and will disrupt collaborators. Prefer cherry-pick or revert.
- Conflicts during cherry-pick: Resolve conflicts, then git add and git cherry-pick --continue. If it’s too complex, consider creating a branch at the detached commit and merging with a regular merge (may create a merge commit).
- Tag checkouts are detached: Committing on a tag is still detached. Always create a branch before committing changes based on a tag.
- CI environments: Some checkouts use detached HEAD by default (e.g., by commit SHA). Ensure automation creates a branch before committing, or pushes using explicit refspecs (e.g., HEAD:refs/heads/ci-output).
- Shallow or bare clones: Reflog may be limited or disabled; plan rescue steps accordingly.
Performance Notes
- Creating a branch and switching branches are O(1) metadata operations; effectively instant.
- Cherry-pick scales with the size of changes. For many commits, cherry-pick ranges are faster than one-by-one picks.
- Avoid running git gc before rescuing; it could prune unreachable objects sooner in some setups.
- In large repos, prefer minimal working trees and narrow cherry-picks to reduce index operations.
FAQ
How do I know I’m detached?
- git status shows: HEAD detached at <sha or tag>.
Can I push from a detached HEAD?
- Yes, by naming the destination ref explicitly, e.g., git push origin HEAD:refs/heads/tmp. But first create a local branch for clarity.
I made multiple commits while detached. What’s the best way to move them?
- Create a rescue branch at the tip, then cherry-pick a range onto the target branch, e.g., git cherry-pick rescue~N..rescue.
Reflog entry is gone. Any other recovery?
- Try: git fsck --no-reflogs --lost-found, or search all refs: git log --all --grep 'your message'. If found, create a branch at the SHA.
Why did I end up detached in CI?
- CI often checks out a specific commit SHA. Create a branch before committing, or configure checkout to use a branch ref.