I Committed in Detached HEAD — Now What?

Made commits while in detached HEAD state and they seem lost. Attach them to a branch before Git garbage-collects them.

You ran git checkout v2.3.1 to investigate an old tag, made a few experimental commits, then ran git checkout main — and Git printed “Warning: you are leaving N commits behind that are not connected to any branch.” Your new commits are not on main, not on any branch, and git log does not show them. They are “dangling commits”: live objects in the object database with no branch reference pointing to them. Git’s garbage collector will delete them after roughly 30 days unless you attach them to a branch. The fix takes under a minute, but you need to act before git gc runs.

Common causes

Ordered by hit rate, highest first.

1. Checking out a tag puts you in detached HEAD

git checkout v2.3.1 or git checkout abc1234 both detach HEAD. There is no active branch, so any commits you make are orphaned the moment you switch elsewhere.

How to spot it: Before switching away, run git status — the first line reads HEAD detached at v2.3.1 rather than On branch feature.

2. git bisect left you in detached HEAD after the session

git bisect start / git bisect good / git bisect bad operates in detached HEAD mode. If you committed a workaround to verify a fix during bisect and forgot to run git bisect reset, those commits are dangling.

How to spot it: git bisect log still shows an active bisect session, or cat .git/BISECT_LOG exists.

3. Checking out a remote tracking branch directly

git checkout origin/feature checks out the remote tracking ref, not a local branch, causing detached HEAD. New commits are not pushed anywhere and have no local branch reference.

How to spot it: git branch shows * (HEAD detached at origin/feature) instead of a branch name.

4. CI / CD pipeline checked out a specific SHA

Automated pipelines (GitHub Actions, Jenkins) often clone with git checkout <sha> for reproducibility. A developer who made local edits inside such a pipeline’s workspace and committed locally is now in detached HEAD.

How to spot it: The workspace’s .git/HEAD contains a raw SHA rather than ref: refs/heads/<branch>.

5. git stash pop after a checkout moved to an unexpected state

Some older Git workflows stash, checkout a SHA, pop the stash, make edits, commit — all in detached HEAD without realizing the stash pop did not re-attach HEAD to a branch.

How to spot it: git reflog | head -5 shows stash@{0}: pop immediately before a series of commits with no branch-switch entry between them.

Shortest path to fix

Step 1: Find the SHA of your detached-HEAD commits

If you have not switched away yet, git log --oneline shows them at the top. Note the topmost SHA.

If you already switched away:

git reflog --date=relative | head -20

Find the line that says commit: <your message> under the detached HEAD session. The SHA in the left column is your most recent detached commit.

Step 2: Create a branch pointing to those commits immediately

# If you are still in detached HEAD
git branch rescue/detached-work HEAD

# If you have already switched to another branch
git branch rescue/detached-work <SHA-from-reflog>

Step 3: Verify the branch has your work

git log --oneline rescue/detached-work | head -10
git diff main..rescue/detached-work -- src/

Confirm your changes are present.

Step 4: Choose how to integrate the commits

Option A — merge into your target branch:

git checkout main
git merge rescue/detached-work --no-ff -m "merge: restore detached-HEAD work"

Option B — cherry-pick individual commits:

git checkout main
git cherry-pick rescue/detached-work~2..rescue/detached-work

Option C — rebase onto the target:

git checkout rescue/detached-work
git rebase main
git checkout main
git merge rescue/detached-work --ff-only

Step 5: Push and clean up

git push origin main
git branch -d rescue/detached-work

Prevention

  • Use git switch instead of git checkout for branch operations — git switch warns you more clearly when an operation would detach HEAD.
  • To inspect an old tag without detaching permanently, use a temporary branch: git switch --detach v2.3.1 is fine for looking, but git switch -c investigate/v2.3.1 v2.3.1 gives you a proper branch if you plan to commit.
  • Always run git status before committing to confirm you are on a named branch.
  • After git bisect, always run git bisect reset to return to your original branch before making any commits.
  • Set your shell prompt to show the current Git branch or detached-HEAD state (oh-my-zsh and bash-git-prompt both do this by default).
  • In CI pipelines, add a post-checkout step that creates a local branch: git switch -c ci/run-$BUILD_ID so any commits inside the job are attached to a named ref.
  • Configure core.warnAmbiguousRefs = true (the default) so Git warns you when a checkout is ambiguous between a branch and a tag.

FAQ

Q: How long do I have before the garbage collector deletes my dangling commits? A: By default, unreachable commits expire from the reflog after 30 days (gc.reflogExpireUnreachable = 30). After expiry, git gc can prune them. You typically have at least two weeks, but act sooner.

Q: I ran git gc already and the reflog entry is gone. Is all work lost? A: Not necessarily. Run git fsck --lost-found. Git writes all dangling commit, tree, and blob objects to .git/lost-found/commit/. Inspect each SHA with git show <sha> to find your work.

Q: Can I push a detached HEAD directly to a remote branch? A: Yes, with an explicit refspec: git push origin HEAD:refs/heads/new-branch-name. This creates new-branch-name on the remote pointing to your current detached HEAD SHA.

Q: Is detached HEAD ever intentional? A: Yes. git bisect, read-only exploration of historical states, and some CI reproducibility workflows intentionally use detached HEAD. The problem is only when you commit new work without creating a branch first.

Tags: #git #version-control #Troubleshooting