Have you pushed the commits before the rebase?
– manojldsApr 24 '11 at 18:02

@manojlds: Not sure what you mean. I pushed some time before the rebase, but not immediately before.
– Ben ZottoApr 24 '11 at 18:18

As in did you previously push the commits that you removed in the rebase -i.. From your answer I think not.
– manojldsApr 24 '11 at 18:22

@manojlds: Correct. I only killed commits that were more recent than the most recent push. (Although as I mentioned, I have since pushed, since I thought everything was OK)
– Ben ZottoApr 24 '11 at 18:23

Can you explain what you did in I did a reset of some files to get them out of commit staging part? sorry for the questions :)
– manojldsApr 24 '11 at 18:31

26 Answers
26

First, let’s clarify what HEAD is and what it means when it is detached.

HEAD is the symbolic name for the currently checked out commit. When HEAD is not detached (the “normal”1 situation: you have a branch checked out), HEAD actually points to a branch’s “ref” and the branch points to the commit. HEAD is thus “attached” to a branch. When you make a new commit, the branch that HEAD points to is updated to point to the new commit. HEAD follows automatically since it just points to the branch.

git symbolic-ref HEAD yields refs/heads/master
The branch named “master” is checked out.

git rev-parse refs/heads/master yield 17a02998078923f2d62811326d130de991d1a95a
That commit is the current tip or “head” of the master branch.

git rev-parse HEAD also yields 17a02998078923f2d62811326d130de991d1a95a
This is what it means to be a “symbolic ref”. It points to an object through some other reference.
(Symbolic refs were originally implemented as symbolic links, but later changed to plain files with extra interpretation so that they could be used on platforms that do not have symlinks.)

We have HEAD → refs/heads/master → 17a02998078923f2d62811326d130de991d1a95a

When HEAD is detached, it points directly to a commit—instead of indirectly pointing to one through a branch. You can think of a detached HEAD as being on an unnamed branch.

git symbolic-ref HEAD fails with fatal: ref HEAD is not a symbolic ref

git rev-parse HEAD yields 17a02998078923f2d62811326d130de991d1a95a
Since it is not a symbolic ref, it must point directly to the commit itself.

We have HEAD → 17a02998078923f2d62811326d130de991d1a95a

The important thing to remember with a detached HEAD is that if the commit it points to is otherwise unreferenced (no other ref can reach it), then it will become “dangling” when you checkout some other commit. Eventually, such dangling commits will be pruned through the garbage collection process (by default, they are kept for at least 2 weeks and may be kept longer by being referenced by HEAD’s reflog).

1
It is perfectly fine to do “normal” work with a detached HEAD, you just have to keep track of what you are doing to avoid having to fish dropped history out of the reflog.

The intermediate steps of an interactive rebase are done with a detached HEAD (partially to avoid polluting the active branch’s reflog). If you finish the full rebase operation, it will update your original branch with the cumulative result of the rebase operation and reattach HEAD to the original branch. My guess is that you never fully completed the rebase process; this will leave you with a detached HEAD pointing to the commit that was most recently processed by the rebase operation.

To recover from your situation, you should create a branch that points to the commit currently pointed to by your detached HEAD:

git branch temp
git checkout temp

(these two commands can be abbreviated as git checkout -b temp)

This will reattach your HEAD to the new temp branch.

Next, you should compare the current commit (and its history) with the normal branch on which you expected to be working:

(You will probably want to experiment with the log options: add -p, leave off --pretty=… to see the whole log message, etc.)

If your new temp branch looks good, you may want to update (e.g.) master to point to it:

git branch -f master temp
git checkout master

(these two commands can be abbreviated as git checkout -B master temp)

You can then delete the temporary branch:

git branch -d temp

Finally, you will probably want to push the reestablished history:

git push origin master

You may need to add --force to the end of this command to push if the remote branch can not be “fast-forwarded” to the new commit (i.e. you dropped, or rewrote some existing commit, or otherwise rewrote some bit of history).

If you were in the middle of a rebase operation you should probably clean it up. You can check whether a rebase was in process by looking for the directory .git/rebase-merge/. You can manually clean up the in-progress rebase by just deleting that directory (e.g. if you no longer remember the purpose and context of the active rebase operation). Usually you would use git rebase --abort, but that does some extra resetting that you probably want to avoid (it moves HEAD back to the original branch and resets it back to the original commit, which will undo some of the work we did above).

Interesting from man git-symbolic-ref: "In the past, .git/HEAD was a symbolic link pointing at refs/heads/master. When we wanted to switch to another branch, we did ln -sf refs/heads/newbranch .git/HEAD, and when we wanted to find out which branch we are on, we did readlink .git/HEAD. But symbolic links are not entirely portable, so they are now deprecated and symbolic refs (as described above) are used by default."
– Dmitry MinkovskyAug 2 '13 at 2:43

I agree with @AntonioSesto: for most projects (even fairly large ones) you don't need the mind-boggling complexity that is Git. My brain rebels at grappling with something that is so clearly over-engineered. I don't need it, and I don't want it.
– Jasper SprengersSep 17 '15 at 11:24

28

This is a good answer, but I think there is no need for the temp branch (although I usually use one my self). git branch -f master HEAD && git checkout master is enough -- assuming your goal is to keep your current head but to designate it as master. Other goals also make sense, and call for other recipes.
– Adrian RatnapalaNov 6 '15 at 10:28

26

Lol at the comment gurning about length. Whereas the rest of us simply scan through till we reach the line that says "To recover from your situation [...]", and go from there - while making a mental note that there's a useful well-explained backstory that we can read on a rainy day. The option to read more does not hurt you, but it does stand to benefit others.
– underscore_dMar 10 '16 at 22:53

Thanks for the brevity. Not everyone has time for the "First, let’s clarify what HEAD is..." answer.
– Brian RiskFeb 28 '16 at 12:58

46

This is a dangerous response. People arriving at this answer have different states and "just do this to fix it" responses don't answer questions. This one can easily destroy work.
– ArchonicMar 10 '16 at 18:51

12

!"git checkout master" will cause all changes to be lost if the detached head is not part of master !!
– TonyMar 28 '16 at 20:53

3

@Blauhirn You probably had the commit checked out, not the branch. The branch still points to the same commit, but you're in a different 'mode'.
– Daniel AlexiucJun 13 '16 at 1:12

1

git reset should come with a warning "If you have no idea what you're doing, stop it". Just recovered from an hour of terror thinking I'd lost the last week of work. Thanks!
– Opus1217Jun 13 '16 at 1:26

I thought: Ah-ha! If HEAD is the symbolic name for the currenlty checkout commit, I can reconcile it against master by rebasing it against master:

git rebase HEAD master

This command:

checks out master

identifies the parent commits of HEAD back to the point HEAD diverged from master

plays those commits on top of master

The end result is that all commits that were in HEAD but not master are then also in master. master remains checked out.

Regarding the remote:

a couple of the commits I'd killed in the rebase got pushed, and the new ones committed locally aren't there.

The remote history can no longer be fast-forwarded using your local history. You'll need to force-push (git push -f) to overwrite the remote history. If you have any collaborators, it usually makes sense to coordinate this with them so everyone is on the same page.

After you push master to remote origin, your remote tracking branch origin/master will be updated to point to the same commit as master.

You could have come to this state by doing a git checkout somecommit etc. and it would have warned you with the following:

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 performing another checkout.

If you want to create a new branch to
retain commits you create, you may do
so (now or later) by using -b with the
checkout command again. Example:

git checkout -b new_branch_name

Now, to get them onto master:

Do a git reflog or even just git log and note your commits. Now git checkout master and git merge the commits.

git merge HEAD@{1}

Edit:

To add, use git rebase -i not only for deleting / killing commits that you don't need, but also for editing them. Just mention "edit" in the commit list and you will be able to amend your commit and then issue a git rebase --continue to go ahead. This would have ensured that you never came in to a detached HEAD.

if you have just master branch and wanna back to "develop" or a feature just do this :

git checkout origin/develop

Note: checking out origin/develop.

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 performing another checkout...

What has worked for me is not 'git checkout origin/develop' but 'git checkout develop'. Using 'origin/develop' has always resulted in no changes, thereby remaining in "HEAD detached at origin/develop". Skipping the 'origin' part fixed everything.
– DrStrangeporkApr 23 '14 at 18:58

If you want to push your current detached HEAD (check git log before), try:

git push origin HEAD:master

to send your detached HEAD into master branch at origin. If your push gets rejected, try git pull origin master first to get the changes from origin. If you don't care about the changes from origin and it's rejected, because you did some intentional rebase and you want to replace origin/master with your currently detached branch - then you may force it (-f). In case you lost some access to previous commits, you can always run git reflog to see the history from all branches.

To get back on a master branch, while keeping the changes, try the following commands:

When I do this I get This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/post-checkout.
– user2568374Mar 12 '18 at 18:07

The third one recovers the HEAD that becomes attached to branch master.

Problems might arise at the first command if the push gets rejected. But this would no longer be a problem of detached head, but is about the fact that the detached HEAD is not aware of some remote changes.

didn't work, I got: This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting .git/hooks/pre-push. AND You are not currently on a branch. Please specify which branch you want to merge with.
– user2568374Mar 12 '18 at 16:59

I just ran into this issue today and am pretty sure I solved it by doing:

git branch temp
git checkout master
git merge temp

I was on my work computer when I figured out how to do this, and now I'm running into the same problem on my personal computer. So will have to wait till Monday when I'm back at the work computer to see exactly how I did it.

Thanks! My head had gotten detached. I could catch it up to master but they just happened to be pointing at the same commit rather than head pointing to master which pointed to the commit. Good tip =D
– RagingRooseveltMar 22 '18 at 19:51

All you have to do is 'git checkout [branch-name]' where [branch-name] is the name of the original branch from which you got into a detached head state. The (detached from asdfasdf) will disappear.

So for example, in branch 'dev' you checkout the commit asdfasd14314 ->

'git checkout asdfasd14314'

you are now in a detached head state

'git branch' will list something like ->

* (detached from asdfasdf)
dev
prod
stage

but to get out of the detached head state and back to dev ->

'git checkout dev'

and then 'git branch' will list ->

* dev
prod
stage

but that is of course if you do not intend on keeping any changes from the detached head state but I find myself doing this a lot not intending to make any changes but just to look at a previous commit

I had this problem today, where I had updated a submodule, but wasn't on any branch. I had already committed, so stashing, checkout, unstashing wouldn't work. I ended up cherry-picking the detached head's commit. So immediately after I committed (when the push failed), I did:

git checkout master
git cherry-pick 99fe23ab

My thinking went: I'm on a detached head, but I want to be on master. Assuming my detached state is not very different from master, if I could apply my commit to master, I'd be set. This is exactly what cherry-pick does.

When I personally find myself in a situation when it turns out that I made some changes while I am not in master (i.e. HEAD is detached right above the master and there are no commits in between) stashing might help:

git stash # HEAD has same content as master, but we are still not in master
git checkout master # switch to master, okay because no changes and master
git stash apply # apply changes we had between HEAD and master in the first place

As you can see above in case of sequence of commits, your branch points to your latest commit. So in that case if you checkout to commit 123be6a76168aca712aea16076e971c23835f8ca then you would be in detached head state since HEAD of your branch points to 100644a76168aca712aea16076e971c23835f8ca and technically you are checked out at HEAD of no branch. Hence, you are in the detached HEAD state.

Theoretical Explanation

In this Blog it's clearly stating
a Git repository is a tree-of-commits, with each commit pointing to its ancestor with each commit pointer is updated and these pointers to each branch are stored in the .git/refs sub-directories. Tags are stored in .git/refs/tags and branches are stored in .git/refs/heads. If you look at any of the files, you'll find each tag corresponds to a single file, with a 40-character commit hash and as explained above by @Chris Johnsen and @Yaroslav Nikitenko, you can check out these references.

I had the same problem. I stash my changes with
git stash and hard reset the branch in local to a previous commit(I thought it caused that) then did a git pull and I am not getting that head detached now. Dont forget git stash apply to have your changes again.

Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).