Git dropped you into "detached HEAD" and the commits you just made seem to have vanished. Here's what happened and how to get your work back.
I still remember the first time this bit me. I'd checked out an old commit to confirm a bug existed before a certain release, fixed it right there because I was already looking at the code, committed, and pushed nothing because I got pulled into a meeting. Came back an hour later, ran git switch main, and my two commits were gone. Not on any branch. Not in the log. Gone.
They weren't actually gone, but I didn't know that yet. Here's what was going on.
HEAD is Git's pointer for "where you are right now." In normal life it points at a branch, like main, and the branch points at a commit. When you make a new commit, the branch moves forward and drags HEAD along with it. That indirection is the whole trick: your branch name follows your work automatically.
Detached HEAD is when that middle step disappears. HEAD points straight at a commit instead of at a branch. You're standing on a specific snapshot with no branch label attached to your position.
You land here a few ways:
git checkout <sha> or git switch --detach <sha> — jumping to a specific commitgit checkout v1.4.0 — checking out a tag (tags aren't branches)git bisect — the whole mechanism walks you across commits detachedWhen it happens, Git tells you plainly:
Note: switching to 'a1b2c3d'.
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.
That text is worth reading once, slowly. It's not an error. It's Git warning you about the tradeoff you just accepted.
Nothing is broken in detached HEAD. You can read files, run the build, check out a suspicious old commit and reproduce a bug. All of that is fine, and half the time it's exactly why you went there.
The catch is committing. If you make commits in detached HEAD, they exist, but no branch points at them. The only thing pointing at your new work is HEAD itself. The moment you switch to a branch, HEAD moves, and those commits have nothing holding them in place. Git's garbage collection eventually sweeps up unreferenced commits, so "eventually" they really do disappear.
That's the trap I fell into. My two commits were real, they just had no name. Switching branches let go of the only handle I had on them.
If you only looked around and didn't commit anything, there's nothing to save. Return to your branch:
git switch main
Or the older spelling, still fine:
git checkout main
Done. No drama. This is the common case and it's why the warning exists but rarely causes real pain.
This is the case that matters. You're in detached HEAD, you committed real work, and you want it to survive. Do NOT switch branches yet. First, give your work a name:
git switch -c fix/old-release-bug
That creates a new branch right where you're standing, and every commit you made in detached state is now on it, safe. HEAD is reattached to a real branch. From here you push, open a PR, merge, whatever you normally do.
The opinionated version of this: create the branch before you commit, not after. The instant you decide you're going to write code in a detached checkout, run git switch -c something first. It costs you nothing, and it turns the whole "lost commits" problem into a non-event. I've never once regretted making a throwaway branch. I've regretted the other choice plenty.
This is where people panic, and it's the most recoverable situation of all. Your commits aren't in any branch's history, but Git wrote down every place HEAD has ever been. That log is the reflog.
git reflog
You'll get something like:
a1b2c3d HEAD@{0}: checkout: moving from a1b2c3d to main
9f8e7d6 HEAD@{1}: commit: fix null check in parser
3c2b1a0 HEAD@{2}: commit: reproduce the crash
a1b2c3d HEAD@{3}: checkout: moving from main to a1b2c3d
There they are. 9f8e7d6 is the tip of the work I thought I'd lost. Grab it with a branch:
git branch recovered 9f8e7d6
Now switch to it, confirm the log looks right, and carry on. The reflog keeps entries for 90 days by default, so unless you waited a season or ran an aggressive git gc, your commits are sitting there waiting.
None of this means detached HEAD is a mistake to avoid. It's genuinely useful. Inspecting an old state to answer "did this bug exist at the v2.0 tag" is cleaner detached than creating a branch you'll throw away. git bisect depends on it. And CI systems check out a specific commit SHA on purpose — a build should be reproducible against an exact snapshot, not against whatever main happens to be right now. If you see detached HEAD in a CI log, that's correct behavior, not a bug in your pipeline.
The rule is simple. Detached HEAD for looking: perfect. Detached HEAD for committing: only after you've made a branch to catch the work.
Treat the warning as a checklist, not noise. If you're only inspecting, ignore it and git switch back when you're done. If there's any chance you'll commit, run git switch -c name the second you land, before you touch a file. And if you're reading this because your commits already vanished, breathe, run git reflog, and branch off the SHA — the work is almost certainly still there. For the wider set of these, our Git troubleshooting guide collects the ones that catch people most.
Get the latest tutorials, guides, and insights on AI, DevOps, Cloud, and Infrastructure delivered directly to your inbox.
Both run pipelines as CRDs inside your cluster, but they were built for different jobs. Here's how Tekton and Argo Workflows actually differ in practice.
A practitioner's guide to how LLM API pricing works, how to estimate a workload's monthly bill, and the levers that actually cut it.
Explore more articles in this category
The IaC landscape fractured after the Terraform license change. This is the map to what each tool is actually best at, and how to choose without regret.
Terragrunt keeps large Terraform setups DRY and orchestrated, but small teams often pay its learning curve for little return.
A practical comparison of Kubernetes-native continuous reconciliation against the classic CLI-driven, state-file IaC model for platform teams.
Evergreen posts worth revisiting.