Thursday, November 17, 2016

What is a detached HEAD in Git, and how to deal with it

Checkout providing a branch name:

$ git checkout development

Checkout providing a SHA1 hash of a specific commit:

$ git checkout 56a4e5c08
Note: checking out '56a4e5c08'.

You are in 'detached HEAD' state...

A "detached HEAD" describes this exact state - when a specific commit is checked out instead of a branch.

"Detached HEAD" are common

Submodules are checked out at specific commits instead of branches
Rebase creates a temporary detached HEAD

Where a detached HEAD should not show up


When going back in time to try out an older version of your project, for example in the context of a bug, when you want to see how things worked in an older revision.

This is a perfectly valid and common use case. However, you don't have to maneuver yourself into a detached HEAD state to deal with it. Instead, remember how simple and cheap the whole concept of branching is in Git: you can simply create a (temporary) branch and delete it once you're done.

$ git checkout -b test-branch 56a4e5c08
...do your thing...
$ git checkout master
$ git branch -d test-branch

How to re-attach a detached HEAD

To recover from the situation where you have a detached HEAD, 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:

$git log --graph --decorate --pretty=oneline --abbrev-commit master $origin/master temp
$git diff master temp
$git diff origin/master temp

(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

References

[1] See an excellent explanation on the concept of detached HEAD
[2] Stackoverflow account on how to deal with a detached HEAD