By admin , 28 April 2026

Introduction

Git keeps several special "auxiliary" refs in .git/ to record state during multi-step operations. Knowing them turns scary recoveries into one-liners.

ORIG_HEAD

ORIG_HEAD is set whenever a "dangerous" operation moves HEAD by a lot: merge, rebase, reset, am. It captures the previous tip so you can undo:

git merge feature
# decide it was a mistake
git reset --hard ORIG_HEAD

Same trick after a bad rebase or reset:

git rebase main
git reset --hard ORIG_HEAD     # back to pre-rebase state

MERGE_HEAD

During a merge, MERGE_HEAD records the tip of the branch being merged in. It exists from the start of git merge until the merge commit is created or the merge is aborted:

git merge feature
cat .git/MERGE_HEAD
git merge --abort                # removes MERGE_HEAD

If you finished resolving conflicts and want to write the merge commit:

git commit                       # uses MERGE_HEAD to record the second parent

MERGE_MSG and AUTO_MERGE

Companion files include .git/MERGE_MSG (the prepared commit message) and, since Git 2.40, AUTO_MERGE (a tree containing Git's best-effort auto-resolution). The latter is useful for diffing against your manual resolution.

CHERRY_PICK_HEAD and REVERT_HEAD

During a cherry-pick or revert, Git records the source commit:

git cherry-pick a1b2c3d
cat .git/CHERRY_PICK_HEAD
git cherry-pick --abort
git cherry-pick --continue        # after resolving conflicts

The same pattern applies to git revert with REVERT_HEAD.

BISECT_HEAD and REBASE_HEAD

When bisecting or rebasing, Git also writes BISECT_HEAD or REBASE_HEAD to track in-progress state. Tools like git status use these to print accurate "you are in the middle of X" messages.

FETCH_HEAD

After a fetch, FETCH_HEAD lists what was fetched, one ref per line. git pull reads it to know what to merge:

git fetch origin main
cat .git/FETCH_HEAD
git merge FETCH_HEAD

Putting it together

git status                         # references all of these as needed
ls .git/*HEAD                      # list current auxiliary refs

Detecting in-progress operations

git status reads these auxiliary refs to tell you what is in progress. Scripts can do the same by testing for the existence of the files:

if test -f "$(git rev-parse --git-dir)/MERGE_HEAD"; then
    echo "merge in progress"
fi
if test -f "$(git rev-parse --git-dir)/CHERRY_PICK_HEAD"; then
    echo "cherry-pick in progress"
fi
if test -d "$(git rev-parse --git-dir)/rebase-merge"; then
    echo "interactive rebase in progress"
fi

This is exactly what shell prompt integrations like __git_ps1 do to render (main|MERGING).

Common mistakes

Trying to delete MERGE_HEAD manually to escape a merge; use git merge --abort. Forgetting ORIG_HEAD exists and recreating commits by hand after a bad reset. Confusing FETCH_HEAD (last fetch, transient) with remote-tracking refs like origin/main (persistent). And finally, scripting around these refs assuming they exist; MERGE_HEAD only exists during a merge, so always test for the file before reading it.