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.
- Behind the scenes,
HEAD
is a simple reference in the.git/HEAD
file. If you runcat .git/HEAD
, you might see a line likeref: refs/heads/master
. That meansHEAD
is pointing to the branch namedmaster
. - When you switch or “check out” another branch,
HEAD
changes to point to that branch. Understanding how this pointer moves makes it easier to navigate, branch off, and merge code.
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.
- When
HEAD
is detached, any new commits you make won’t be on a recognized branch. That means they’re easy to lose if you moveHEAD
elsewhere. - If you do create commits in a detached state but want to keep them, you need to attach them to a branch (either by creating a new branch at that commit or merging them into an existing branch).
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.