GitThe HEAD Pointer

The HEAD Pointer

HEAD is the single most important ref in Git. It answers the question "where am I right now?" — which branch you are on, or which commit you are looking at. Almost every Git operation reads HEAD to know what to base new work on, and many operations update HEAD when they complete. Understanding HEAD makes git checkout, git reset, git rebase, and detached HEAD state all make intuitive sense.

Normal HEAD: A Symbolic Ref

In normal operation, HEAD is a symbolic ref — it does not contain a commit hash directly. Instead, it contains the name of the current branch. When you commit, Git updates the branch ref, and HEAD follows automatically because it points to the branch.

Inspect HEAD content

Bash
# HEAD is a file in .git/
cat .git/HEAD
# ref: refs/heads/main

# Git resolves it: HEAD → refs/heads/main → SHA hash
git symbolic-ref HEAD
# refs/heads/main

# Get the actual commit hash HEAD resolves to
git rev-parse HEAD
# a3f1c2d83e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b

Normal HEAD diagram

Text
.git/HEAD
  │
  └── ref: refs/heads/main
                │
                └── refs/heads/main (file)
                      │
                      └── a3f1c2d... (commit hash)

HEAD → main → a3f1c2d
Switching Branches Updates HEAD

What git checkout/switch does to HEAD

Bash
# On main branch
cat .git/HEAD
# ref: refs/heads/main

# Switch to feature branch
git switch feature/auth

cat .git/HEAD
# ref: refs/heads/feature/auth

# HEAD now follows feature/auth
git rev-parse HEAD
# 9e2b0f1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a
Making a Commit Advances HEAD via the Branch

How committing moves HEAD

Bash
# Before commit
cat .git/refs/heads/main
# a3f1c2d...   (old commit)

# Make a commit
echo "change" >> file.txt
git add file.txt
git commit -m "New change"

# After commit: branch ref was updated
cat .git/refs/heads/main
# b5e3f4c...   (new commit, a3f1c2d is its parent)

# HEAD still points to main — it moved along with main
git rev-parse HEAD
# b5e3f4c...
Detached HEAD State

Detached HEAD means HEAD contains a commit hash directly instead of a branch name. This happens when you check out a commit by hash, a tag, or a remote-tracking branch directly. You are "looking at" a commit that no branch points to.

Entering detached HEAD state

Bash
# Checkout by commit hash → detached HEAD
git checkout a3f1c2d
# HEAD is now at a3f1c2d Add authentication module

# Checkout a tag → also detached HEAD
git checkout v1.0.0
# HEAD is now at a3f1c2d (tag: v1.0.0)

# Checkout a remote branch directly → detached HEAD
git checkout origin/main
# HEAD is now at a3f1c2d...

Confirm you are in detached HEAD state

Bash
cat .git/HEAD
# a3f1c2d83e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b  ← raw hash, not a ref name

git symbolic-ref HEAD
# fatal: ref HEAD is not a symbolic ref   ← confirms detached state

git status
# HEAD detached at a3f1c2d

Detached HEAD diagram

Text
Normal HEAD:                     Detached HEAD:
.git/HEAD                        .git/HEAD
  │                                │
  └── ref: refs/heads/main         └── a3f1c2d...  (direct hash)
              │
              └── a3f1c2d...

HEAD → main → a3f1c2d            HEAD → a3f1c2d (no branch involved)
Warning
In detached HEAD state, new commits are NOT on any branch. If you commit in detached HEAD and then switch to a different branch, those commits become unreachable. Git will garbage collect them after ~30 days. Always create a branch before making commits in detached HEAD.
Recovering from Detached HEAD

Options for leaving detached HEAD state

Bash
# Option 1: go back to where you came from (discard any commits made)
git switch -

# Option 2: create a new branch at the current detached position
git switch -c my-experiment
# Now your detached commits are safe on a new branch

# Option 3: attach to an existing branch (if you haven't committed anything)
git switch main

# Option 4: if you committed in detached state and then moved away,
# find the commit in reflog and create a branch:
git reflog | grep "HEAD@{" | head -10
# HEAD@{1}: commit: My experimental commit
git branch rescue-branch HEAD@{1}
ORIG_HEAD
Before any operation that significantly moves HEAD (merge, rebase, reset, cherry-pick), Git saves the current HEAD position into `ORIG_HEAD`. This gives you a one-step undo.

Using ORIG_HEAD to undo operations

Bash
# After a merge you regret:
git merge feature/big-refactor
# ... something went wrong

git reset --hard ORIG_HEAD   # undo the merge

# After a rebase:
git rebase main
# ... looks wrong

git reset --hard ORIG_HEAD   # undo the rebase

# After an accidental reset:
git reset --hard HEAD~5      # oops

git reset --hard ORIG_HEAD   # undo the reset
Other Special HEAD-like Refs

Ref

Set By

Contains

Purpose

HEAD

git switch/checkout/commit

Current branch or commit

Where you are now

ORIG_HEAD

merge, rebase, reset, am

Previous HEAD value

One-step undo

MERGE_HEAD

git merge (during conflict)

The other parent being merged

Complete or abort the merge

CHERRY_PICK_HEAD

git cherry-pick (during conflict)

Commit being cherry-picked

Complete or abort cherry-pick

REBASE_HEAD

git rebase (during conflict)

Commit being rebased

Complete or abort rebase

FETCH_HEAD

git fetch

Last fetched branch info

Used by git merge after fetch

Reading and Writing HEAD Programmatically

git symbolic-ref: read and write HEAD

Bash
# Read the current branch name from HEAD
git symbolic-ref HEAD
# refs/heads/main

# Short form (just the branch name)
git symbolic-ref --short HEAD
# main

# Programmatically switch branch by writing HEAD
git symbolic-ref HEAD refs/heads/develop
# (equivalent to git switch develop, but no working-tree update)

# Detach HEAD to a specific commit
git checkout --detach HEAD~3
HEAD in Git Commands

Common HEAD relative references

Bash
# HEAD  = current commit
# HEAD~ = parent of HEAD (same as HEAD~1)
# HEAD~3 = 3 commits back
# HEAD^  = first parent of HEAD (for merge commits)
# HEAD^2 = second parent of HEAD (the merged-in branch)

git show HEAD         # show current commit
git diff HEAD~1       # diff current vs one commit ago
git log HEAD~5..HEAD  # last 5 commits
git reset HEAD~1      # undo last commit (keep changes staged)
git revert HEAD       # create new commit that undoes HEAD
Tip
Run `cat .git/HEAD` whenever you are confused about your repository state. The raw content tells you immediately whether you are on a branch (starts with `ref:`) or in detached HEAD state (starts with a hex hash). This single file explains "where you are".