This module covers one of the most important and misunderstood topics in git – the detached head in git. In this article we discuss in detail about what is git detached head mode, how it works and how to fix git commits made accidentally in detached mode.
What are HEAD and branch in git?
Before we jump into what is detached mode, we need to learn what is a HEAD and what is branch a little more in detail.
Every commit in git is represented by an SHA-1 value. This is difficult to interact with in real life. So we have named references called a branch. The branch is a pointer that points to the latest commit in the chain of commits. When we add the commits on that remote branch, the branch pointer is updated to the new commit.
HEAD is a symbolic pointer in git. Broadly speaking, it refers to the current commit (the commit which represents the current state of the directory).
HEAD as we say is a symbolic pointer, which means it points to a reference, which is generally the branch pointer. The state is in which the HEAD points to a branch pointer is called attached mode. The HEAD can be moved from one commit to another using the git checkout command. For example:
# Points the HEAD pointer to reference/branch master
git checkout master
Note: Git still uses master as default current branch, so we stick to using master as default.
What is a git detached head state?
Detached head is a condition where the HEAD no longer points to the latest commit in the branch. Let us understand it using a simple diagram
In this figure, we see that HEAD is pointing to a commit that is not the latest commit on that branch. As we discussed earlier, HEAD is a symbolic reference. In normal cases, it would point to the reference master, which in turn points to the latest commit on the branch master. But this is not the case here, where the HEAD is not in alignment with the branch. This condition is called the detached-head condition.
NOTE: HEAD is no longer a symbolic reference, it contains the SHA-1 value of the commit it is pointing to.
How do you end up in detached mode?
Any checkout of a commit that is not the name of one of your branches will get you a detached HEAD. An SHA-1 which represents the tip of a branch still gives a detached HEAD. Only a checkout of a local branch name avoids being in detached mode. Here are some examples using git checkout that shows if our checkout results in detached mode or not.
# Go to the first commit in branch dev
# Does not end up in the detached mode
git checkout dev
# Go to the previous commit
# Swtiches to detached mode
git checkout HEAD^
# Move 3 commit before the master reference
# Swtiches to detached mode
git checkout master~3
Detached mode is not a bug, it is a feature. It lets you visit old commits by restoring the directory in that state. Git rebase is one of the main examples where the detached mode is used.
How to reattach the head?
Under a hypothetical situation, let us consider, you have made some commits in detached mode, making the commit graph look like figure 3.
You have accidentally made some commits from the detached head resulting in this kind of commit graph. There might two things that you want to do:
- Disregard the commits made in the detached mode and move on
- Commit these changes made in detached mode to a branch.
Let us look at how to do this:
1. Disregard the commits made in detached mode
If you want to ignore the commits made in detached mode, you can just check out the branch you are working on. You can do this by,
# Just checkout the branch where you want to place your head
git checkout master
The git directory will look like nothing ever happened. These commits will still be present in the graph, but it will have no evident effect on this branch. This method is also useful if you didn’t make a commit and just wish to switch back to another branch. If you made accidental changes, you can stash it (optional) and just check out the desired branch.
2. Commit the changes made in detached mode to another branch
If you don’t want to disregard your efforts, you can attach these detached commits to your preferred branch. This can be done by converting these detached HEADs to a branch. This can be done using
# Convert the detached commits to a branch
git branch temp
These commits will no longer be detached, and the most recent commit will be referenced by the branch name, which will be referred to by the HEAD. The commit-graph will look like this
- Now checkout the branch where you wish to commit and merge the new branch. You can also create a new branch or a repository and commit your changes there, or just use an existing branch.
# Merge the commits to master
git checkout master
git merge temp
Note: Merging branches based on old commits can create merge conflicts in some cases.
Conclusion
This brings us to the end of this article on the detached head. Hope you were able to understand and fix a detached head state in git. I would also like to mention this open-source tool that I used to visualize the git commands, which you can check out if you are interested.