How to deal with a detached head in git?

How To Reattach A Detached Head In Git

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 detached mode, how it works and how to fix 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 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 branch, so we stick to using master as default.

What is a detached head in git?

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

Git Attached
Fig 1: The commit-graph representing the attached mode. The green circle represents the current commit.
Git Detached
Fig 2: Figure representing a detached head state. The HEAD pointer is not pointing to the branch reference. Instead, it contains the SHA-1 value of the commit( here d82553d…) it points to.

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.

Detached Commit
Fig 3: Accidental commits made during the detached head state.

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:

  1. disregard the commits made in the detached mode and move on
  2. Commit these changes made in detached mode to a branch.

Let us looks at how 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
Detached Master
Detached 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 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

Branch Detach
Fig 4: The detached head is converted to branch named temp
  • Now checkout the branch where you wish to commit and merge the new branch.
# Merge the commits to master
git checkout master
git merge temp
Commit Detach
Fig 5: The temp branch containing the commits from detached head are now merged into master.

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 resolve the problems in your detached HEAD. I would also like to mention this open-source tool that I used to visualize the git commands.