Last modified: June 11, 2024
This article is written in: 🇺🇸
Understanding HEAD
HEAD
is a special pointer in Git, which refers to the currently checked-out snapshot of your project. This could be a particular commit, a branch, or a tag. It serves as a kind of "you are here" marker, indicating what part of the project history you're currently looking at or working with. When you make a commit, HEAD
updates to point to the latest commit.
HEAD
can exist in two forms: attached and detached.
- An attached
HEAD
is the most common form, and it means thatHEAD
points to the latest commit of the current branch. When you make a new commit, theHEAD
(along with the branch pointer) moves to that new commit. - A detached
HEAD
happens whenHEAD
points directly to a commit instead of a branch. This typically happens when you check out a specific commit rather than a branch. In this state, your changes don't belong to any branch, and you'll need to create a new branch if you want to save your changes.
The Concept of a Detached HEAD
A "detached HEAD" occurs when HEAD
points to a specific commit, and not the latest commit of a branch. This could happen when you checkout a specific commit or tag, or when you switch to a remote branch. In a detached HEAD state, any new commits you create won't belong to any branch. To preserve your work, you'd need to create a new branch or merge these changes into an existing branch.
For instance, consider a repository with the following commit history:
A <-- B <-- C <-- D <-- E (master, HEAD)
Here, both the master branch and the HEAD pointer refer to the most recent commit (E). So, the HEAD is not detached.
However, if we switch to commit C, the HEAD pointer detaches and points to C, but the master branch remains at E:
A <-- B <-- C (HEAD) <-- D <-- E (master)
In this state, any new commits won't be part of the master branch. Let's say we make a new commit (F), this commit will be unassociated with any branch:
A <-- B <-- C (HEAD) <-- F
\
D <-- E (master)
To keep commit F in the repository's history, we need to create a new branch from HEAD or merge F into an existing branch.
Detaching HEAD: Switching to a Specific Commit
In Git, you can switch to a specific commit and enter a detached HEAD state by using the git switch
command followed by the hash of the commit you want to check out. This hash is a unique identifier for each commit. For example:
git switch b4d373k8990g2b5de30a37bn843b2f51fks2b40
Alternatively, you can use the git checkout command to achieve the same result. The checkout command is older and has more options, but switch was introduced to make certain actions more intuitive:
git checkout b4d373k8990g2b5de30a37bn843b2f51fks2b40
Both commands detach your HEAD pointer and place it on the specified commit.
Creating a Branch from a Detached HEAD
While working in a detached HEAD state, you might make some changes and commit them. These changes are not associated with any branch. To prevent these changes from being lost when you switch branches, you can create a new branch at the current commit.
Use the git branch
command followed by your chosen branch name. For example, to create a new branch called new_branch
:
git branch new_branch
This creates a new branch named new_branch from the current commit. To switch to this newly created branch and exit the detached HEAD state, use the git switch
or git checkout
command:
git switch new_branch
Now, new_branch contains all the commits you made while HEAD was detached, ensuring your changes are preserved.
Preventing a Detached HEAD State
To prevent ending up in a detached HEAD state, it's recommended to avoid explicitly checking out specific commits or tags. Instead, you should primarily interact with different versions of your codebase using branches.
For instance, if you wish to work with an older version of your code, instead of directly checking out that specific commit, create a new branch from that commit and switch to the new branch. This approach allows you to work on the older version of your code without disturbing the main codebase.
Here's an example of how to create a new branch named old_version
from a specific commit and switch to it:
git branch old_version b4d373k8990g2b5de30a37bn843b2f51fks2b40
git switch old_version
Switching Back to a Branch from a Detached HEAD
If you find yourself in a detached HEAD state and wish to switch back to a branch (let's say the master branch), use the git switch
or git checkout
command followed by the branch name:
git switch master
With this command, you will switch back to the master branch. Any new commits you make will now be added to the history of the master branch.
Merging Changes from a Detached HEAD into a Branch
If you've made some changes in a detached HEAD state and want to merge these changes into a branch (e.g., master), you can do so using the git merge
command followed by the commit hash or tag name:
git checkout master
git merge b4d373k8990g2b5de30a37bn843b2f51fks2b40
This command creates a new commit on the master branch, combining the changes from the detached HEAD with those in the master branch. If there are conflicts between the two sets of changes, Git will prompt you to resolve them before the merge can be completed.