Last modified: November 04, 2023

This article is written in: 🇺🇸

Understanding HEAD

HEAD is Git’s pointer to the snapshot of your project you’re currently working on. Think of it as the “current location” in your repository’s history. Typically, HEAD points to the tip of a branch (like master or main). When you commit, HEAD moves to your newly created commit, keeping track of exactly where you are.

ASCII ART EXAMPLE:

       (branch: master)
                |
  C0 <-- C1 <-- C2 (HEAD)

C2 is the latest commit on the master branch, and HEAD currently references it.

The Concept of a Detached HEAD

A detached HEAD happens if HEAD points directly to a commit instead of pointing to the tip of a branch. This usually happens when you check out a specific commit by its hash or a tag rather than a branch name.

A <-- B <-- C <-- D <-- E (master, HEAD)

Initially, HEAD is pointing to E (the tip of master).
If you check out commit C:

A <-- B <-- C (HEAD) <-- D <-- E (master)

Now HEAD is detached, pointing directly to commit C.

What Happens When You Commit on a Detached HEAD

If you make a new commit (let’s call it F) while your HEAD is detached at commit C, your commit history forks:

A <-- B <-- C (HEAD) <-- F
             \
              D <-- E (master)

Commit F is not on master. If you leave F floating like this and switch back to master, there’s no direct link to F unless you explicitly merge or branch it. If you don’t attach commit F to a branch somehow, it may be lost later when Git does garbage collection.

Detaching HEAD: Switching to a Specific Commit

You can enter a detached HEAD state by telling Git to check out a specific commit hash or tag. Suppose you want to check out the commit with hash b4d373k8990g2b5de30a37bn843b2f51fks2b40:

git switch b4d373k8990g2b5de30a37bn843b2f51fks2b40

Or equivalently:

git checkout b4d373k8990g2b5de30a37bn843b2f51fks2b40

HEAD now points to that exact commit instead of a branch tip. You might see something like:

Note: switching to 'b4d373k8990g2b5de30a37bn843b2f51fks2b40'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

This is Git letting you know you’ve detached HEAD. Any commits you make now will be off in their own world unless you later attach them to a branch.

Creating a Branch from a Detached HEAD

Sometimes you realize you’ve made valuable commits while detached and want to keep them. To do this safely, create a new branch that starts at your current commit:

git branch new_branch

Then switch to it:

git switch new_branch

Now your new_branch includes all commits made while HEAD was detached. You’re no longer detached since HEAD is pointing to a branch tip again. The commit history is preserved and attached to new_branch.

Before:
A <-- B <-- C (HEAD) <-- F

After creating and switching to new_branch:
A <-- B <-- C <-- F (new_branch, HEAD)

F is no longer “floating;” it’s anchored to the new_branch branch.

Preventing a Detached HEAD State

It’s generally cleaner to avoid a detached HEAD unless you really need to peek at an old commit. The safer approach is to create a branch at that commit if you plan to make changes.

Example Scenario: You want to check out an older commit to experiment with some debugging or older version behavior.

I. Create a branch named old_version at that commit:

git branch old_version b4d373k8990g2b5de30a37bn843b2f51fks2b40

II. Switch to old_version:

git switch old_version

This way, you’re always on a branch, so no commits float in limbo. If you do make new commits, they’ll stay on old_version.

Switching Back to a Branch from a Detached HEAD

To return to a standard branch (like master) after poking around in a detached commit, just switch back:

git switch master

or

git checkout master

You’ll rejoin the commit history where master was last pointing. Any commits made in a detached state and not saved to a branch will remain isolated.

Merging Changes from a Detached HEAD into a Branch

Let’s say you made some great changes while detached and now want them in master. You can merge those commits into master by referencing the commit’s hash:

I. Switch to master (the branch you want to bring changes into):

git switch master

(Or git checkout master.)

II. Merge the detached commit:

git merge b4d373k8990g2b5de30a37bn843b2f51fks2b40

If Git detects conflicts, you’ll see files with markers you need to resolve. After resolving them, run:

git add <file_with_conflicts>
git commit

Now your detached changes are merged into master with a new merge commit that ties everything together.

(master)                (HEAD detached)
   |                          |
   D <-- E                   C <-- F
    \                      /
     ------ Merge Commit --

Resulting in a single continuous history where the changes in F are incorporated into master.

Table of Contents

    Understanding HEAD
    1. The Concept of a Detached HEAD
    2. What Happens When You Commit on a Detached HEAD
    3. Detaching HEAD: Switching to a Specific Commit
    4. Creating a Branch from a Detached HEAD
    5. Preventing a Detached HEAD State
    6. Switching Back to a Branch from a Detached HEAD
    7. Merging Changes from a Detached HEAD into a Branch