skip to content

In Git, what is ORIG_HEAD, and why can it be stale after two operations in a row?

level: seniorimportance: should knowfreq 36%

answer

  1. A ref Git writes for you, not one you set
  2. Only drastic HEAD moves update it
  3. It has room for exactly one value
  4. The second operation costs you the first
  5. The journal that does keep history lives elsewhere

basics

~20 s

ORIG_HEAD is a single ref Git overwrites with the previous HEAD position whenever a command moves HEAD drastically — reset, merge, rebase, am. Because it holds only one value, a second such command overwrites it, so it points one step back, not to the original.

solid answer

~50 s

`ORIG_HEAD` is a plain ref in the repository that Git rewrites whenever a command moves HEAD in a drastic way: `git reset`, `git merge`, `git rebase` and `git am` all record the previous tip there (and `git pull` inherits it through the merge or rebase it runs). That makes `git reset --hard ORIG_HEAD` the quick undo for a bad merge or reset, and `git diff ORIG_HEAD` a fast way to see what an operation brought in. The catch is that it stores exactly one value. Reset twice, or reset and then merge, and `ORIG_HEAD` names the position before the *second* command — the original tip is no longer there. It is also not journalled the way branches are, so the durable record is the reflog: `HEAD@{n}` still lists every position in order, and that is where you go once ORIG_HEAD has been clobbered.

code

bash · 6 lines
bash
git pull                 # ORIG_HEAD = tip before the pull
git diff ORIG_HEAD       # what the pull changed
git log ORIG_HEAD..HEAD  # commits it brought in

git merge feature        # ORIG_HEAD overwritten by the merge
git reset --hard ORIG_HEAD   # undo the merge, not the pull

go deeper

for a junior

Recall that ORIG_HEAD holds where HEAD was before a big operation such as a merge or reset, which makes reset --hard ORIG_HEAD a quick undo.

for a middle

Name the commands that write it — reset, merge, rebase, am, and pull through the merge or rebase it runs — and explain that it stores one commit id with no history.

for a senior

Show the diagnostic habit: verify ORIG_HEAD before acting on it, fall through to the reflog when a second operation has overwritten it, and create a real backup branch before risky sequences.

for a principal

Own the reliability argument: a single-slot convenience ref is not a recovery strategy for a team, so scripted or automated history operations should record explicit refs rather than depend on ORIG_HEAD.

## What it is `ORIG_HEAD` is an ordinary ref stored in the repository's Git directory alongside `HEAD`, holding a single commit id. Git writes it automatically: commands that move HEAD in a drastic way record where HEAD was **before** the operation. The documented writers are `git reset`, `git merge`, `git rebase` and `git am`. `git pull` gets one for free, because it finishes with a merge or a rebase. Because it is a ref, it can be used anywhere a commit can: - `git reset --hard ORIG_HEAD` — undo a merge or a reset that went the wrong way. - `git diff ORIG_HEAD` — see everything the last operation changed in the tree. - `git log ORIG_HEAD..HEAD` — list the commits the operation brought in. ## Why it exists Before reflogs were universal, `ORIG_HEAD` was the one guaranteed handle on "where I was a moment ago". It survives because it is convenient: after `git pull` drops twenty commits on you, `git diff ORIG_HEAD` is shorter to type than working out the right `HEAD@{n}`, and after a merge you regret, `git reset --hard ORIG_HEAD` is the canonical undo. ## The staleness trap `ORIG_HEAD` is a **single slot**. Every qualifying command overwrites it, and there is no history of previous values. Suppose you run `git reset --hard HEAD~5`, realise the target was wrong, and run `git reset --hard HEAD~2` from there. After the first command `ORIG_HEAD` was the original tip; after the second it is the commit you were on *between* the two resets. `git reset --hard ORIG_HEAD` now takes you to that intermediate position, not home. The same happens with reset-then-merge, or a `git pull` immediately after a rebase. Unlike branch heads and HEAD, `ORIG_HEAD` is not journalled — the default reflog configuration covers HEAD, branch heads, remote-tracking refs and notes, not this ref. There is no `ORIG_HEAD@{1}` to fall back to. ## What to use instead once it is stale The reflog. Every movement of HEAD is appended to its journal with an action label, so `git reflog` shows the sequence — `reset: moving to HEAD~5`, `reset: moving to HEAD~2` — and `HEAD@{2}` names the position two moves ago. Resetting a branch to the right reflog entry restores it exactly. That is the reason the practised answer to "how do I undo this" is *ORIG_HEAD if I am acting immediately, reflog if I am not*. ## Practical habits 1. **Use it immediately or not at all.** If more than one HEAD-moving command has run since the mistake, go straight to `git reflog` instead of trusting the ref. 2. **Verify before acting.** `git log --oneline -1 ORIG_HEAD` costs nothing and tells you whether it points where you assume. 3. **Pin the value yourself for risky work.** Before a long rebase or a scripted sequence, `git branch backup/pre-rebase` creates a real ref that nothing else will overwrite. That is strictly better than relying on a slot every subsequent command competes for. 4. **Remember what it does not cover.** `ORIG_HEAD` names a commit, so it restores committed history only. Working-tree edits destroyed by `git reset --hard` or files deleted by `git clean` were never objects; no ref brings them back. ## The one-line summary ORIG_HEAD is a convenience ref holding the pre-operation HEAD for reset, merge, rebase and am; it is overwritten by the next such operation, and the reflog — not ORIG_HEAD — is the durable record.

  • If ORIG_HEAD is already stale, how do you find the position you actually wanted?
    Read the reflog. `git reflog` lists every HEAD movement newest-first with an action label such as `reset: moving to HEAD~5`, so you can identify the entry from before the first mistake and reset the branch to that `HEAD@{n}`. Unlike ORIG_HEAD it keeps a full ordered history, subject to its expiry window.
  • How would you protect a risky rebase or scripted history operation instead of relying on ORIG_HEAD?
    Create a real ref first: `git branch backup/pre-rebase` or `git tag pre-rebase`. Nothing overwrites it, it survives any number of subsequent operations, and it is visible to `git log` and `git branch`. Delete it once the result is verified. ORIG_HEAD is a convenience for immediate undo, not a checkpoint mechanism.

saying these in an interview costs you the question

  • Believing ORIG_HEAD keeps a stack of previous positions
  • Expecting every command, including commit, to update it
  • Thinking ORIG_HEAD restores uncommitted working-tree changes
  • Assuming ORIG_HEAD has its own reflog to fall back on

context