One of the regular error messages you’re likely to encounter with Git is the warning that “you are in ‘detached HEAD’ state.” You may have stumbled into this accidentally, but don’t worry—it’s a normal problem, and can easily be fixed once you understand what it means.
What Is The Git HEAD?
“HEAD” is simply an alias for your current working commit, much like your current directory on a command line. Whatever state your Git repository is in, HEAD always points to something, and new commits will be appended in front of the HEAD.
Usually, HEAD doesn’t directly reference a single commit. It instead points at a branch, and indirectly references the latest commit in that branch. Whenever you “checkout” a new branch, Git switches the HEAD over to that branch. This allows you to make new commits that will be appended to the end of that branch.
However, you can also check out individual commits. What if you wanted to examine the repository at a specific point in time? That’s one of the major upsides of Git, but it presents a slight problem with how Git structures things. Since new commits won’t update a branch, any commits you make after moving the HEAD away will become detached from all the branch references, hence, “detached HEAD.”
This can actually be fairly useful. Say you moved the HEAD back a few commits, and then made some experimental updates. You’re basically making a new branch, which could get merged back into master later on.
You’ll need to officially integrate it into the Git repo if you do decide to keep the changes, but as a tool for doing back-in-time modifications, having a detached HEAD isn’t as scary as it sounds.
If You Got Here By Accident
It’s very easy to end up here on accident, and be confused about the error message Git is complaining about despite having done nothing wrong. Luckily, it’s incredibly easy to fix:
If you just checked out a commit, and haven’t touched the repository otherwise (no new commits were made), you can simply move the HEAD back to where it belongs with git checkout:
This is basically like running cd on Linux, except Git still associates cached commits and changes with the old HEAD, so you don’t want to checkout if you have commits or changes that would be left hanging.
If you did have some changes, and want to just throw them away, you can hard reset back to master:
If you want to save your commits though, you’ll need to officially merge them back into Git’s timeline.
If You Want To Save Your Changes
The first thing you’ll want to do if you want to keep the changes you made while in a detached HEAD state is to make a new branch. This is because Git’s garbage collector will clean up detached commits automatically, so you don’t ever want to lose track of them.
Then you can checkout this branch to move the HEAD so it is no longer detached, and instead pointed at this official new branch:
Once the changes are recorded, you have one of two options. This new branch is basically an ordinary feature branch, so you can either git merge or git rebase.
Merging is straightforward; checkout master, and merge the detached branch:
This works well if you’re integrating into a branch that needs approval, as is the case with pull requests and code reviews. You may need to go through your team’s approval process rather than running these commands yourself.
If you do have access, like if you’re merging back into a feature branch you’re working on, a better method is to cherry pick the detached commits. Cherry picking basically copies a commit, and pastes it onto another branch. It’s usually used for pulling out specific commits from a feature branch before the whole thing is ready, but in this case, cherry-picking can reconcile the detached branch without a merge.
In this example, the commit up above is detached. Rather than merging it, it’s cherry-picked and moved onto the feature branch. This moves the HEAD and feature branch to point to the latest commit without a merge. Then, the original detached commit can be discarded.
First, you’ll need to make the detached branch, and then checkout the feature branch to move the HEAD there:
Then run Git log to get a list of commits:
Then you can cherry-pick a commit by its ID:
Then, once you’ve confirmed that the cherry-picked commits are applied properly, you can delete the detached branch, since the commits were copied and it is no longer used.
Rebasing Instead of Merging
You could also do a rebase. Rebases are different from merges in that they rewrite the branch history, lifting up the detached commits and moving them to the front of the branch.
However, the problem with this is that you’re still left with two branches, and you will still need to merge feature and detached-branch together, leaving you with a merge commit either way. But, it does leave the repository with a more linear Git history, which is preferable to running git merge if you have the authority to do so.