skip to content

In Git, what is FETCH_HEAD and when would you use it directly?

level: middleimportance: should knowfreq 42%

answer

  1. A file, not a branch
  2. Written by every fetch, overwritten by the next
  3. Some lines are marked not-for-merge
  4. Needed when no remote-tracking ref exists
  5. pull merges this after fetching

basics

~20 s

FETCH_HEAD is a file in the .git directory that records the refs downloaded by the most recent git fetch, one line per ref, with the branch fetched for merging marked first. It lets you merge or inspect a fetch that has no remote-tracking ref of its own.

solid answer

~40 s

Every `git fetch` writes `.git/FETCH_HEAD`, listing the object IDs it just fetched together with the remote and ref each came from; entries not intended for merging are marked `not-for-merge`. It exists because a fetch does not always have a remote-tracking ref to land in — `git fetch <url> <branch>` or fetching an arbitrary ref stores its result only in `FETCH_HEAD`. Internally `git pull` is defined in terms of it: fetch, then merge or rebase `FETCH_HEAD`. In day-to-day work you use it when you fetch from a one-off URL or a ref you have no refspec for, and then run `git log FETCH_HEAD` or `git merge FETCH_HEAD`. It is overwritten by the next fetch unless you pass `--append`.

code

bash · 4 lines
bash
git fetch https://example.invalid/repo.git topic
git log --oneline HEAD..FETCH_HEAD
git diff HEAD...FETCH_HEAD
git branch review FETCH_HEAD

go deeper

for a junior

Recall that a fetch writes .git/FETCH_HEAD naming what it just downloaded, and that you can pass FETCH_HEAD to commands like git log or git merge as if it were a revision.

for a middle

Explain why it exists: fetches from a bare URL or an unconfigured ref have no remote-tracking ref to land in, and pull's merge half is defined against it. Mention that it is overwritten each fetch.

for a senior

Show it as a debugging tool — reading the file tells you exactly which object an integration was handed — and know to promote anything worth keeping into a real branch or tag.

for a principal

Frame it when defining review or automation workflows: fetching a contributor's URL into FETCH_HEAD avoids permanent remotes and stray branches, but scripts must create refs for anything that must outlive the next fetch.

## What the file is `FETCH_HEAD` is not a branch and not a normal ref — it is a plain text file at `.git/FETCH_HEAD` that `git fetch` rewrites on every run. Each line records one ref that the fetch brought down: the object ID, an optional `not-for-merge` marker, and a human-readable description of where it came from, for example the branch name and the remote URL. The first line without the `not-for-merge` marker is the one Git treats as "the thing you fetched in order to integrate", and the name `FETCH_HEAD` resolves to that object ID anywhere Git accepts a revision. ## Why it exists at all Remote-tracking refs only exist for refs covered by a configured refspec — by default `+refs/heads/*:refs/remotes/origin/*` for a named remote. Plenty of fetches fall outside that. You can fetch straight from a URL with no remote configured; you can fetch a single ref that has no local counterpart. In those cases Git still has to tell you where the downloaded work is, and `FETCH_HEAD` is the answer. It is the universal landing spot for "whatever the last fetch produced". This also explains the classic definition of `git pull` as fetch plus merge: the second half is conceptually `git merge FETCH_HEAD`. Understanding that removes most of the mystery from pull's behaviour — when a merging pull creates a merge commit, the second parent is the commit that `FETCH_HEAD` named. ## Using it After a fetch, `FETCH_HEAD` behaves like any revision. `git log FETCH_HEAD` shows the fetched history. `git log HEAD..FETCH_HEAD` shows what arrived that you do not have. `git diff HEAD...FETCH_HEAD` shows the net change. `git merge FETCH_HEAD` integrates it, and `git cherry-pick FETCH_HEAD` takes just the tip commit. A typical use is reviewing a contribution from a repository you do not want to add as a permanent remote: fetch its URL and branch, inspect `FETCH_HEAD`, then merge or discard it — no remote, no branch, nothing to clean up afterwards. ## Lifetime and gotchas The file is overwritten by the next fetch, so treat it as a scratch pad, not storage. `git fetch --append` adds to it instead of replacing it, which is how a multi-remote fetch accumulates entries. `git fetch --dry-run` never writes it. If you fetched something you want to keep, create a real ref: `git branch review FETCH_HEAD` or `git tag`. Otherwise the object is unreferenced once `FETCH_HEAD` is overwritten and becomes eligible for garbage collection. A second subtlety is that a normal `git fetch origin` writes both remote-tracking refs **and** `FETCH_HEAD`. People often assume the two are alternatives; they are not. Remote-tracking refs are the durable, per-branch record you compare against every day; `FETCH_HEAD` is the transient record of the last fetch operation itself. ## Where it shows up in real work Beyond ad-hoc review, `FETCH_HEAD` is what makes `git pull <url> <branch>` work at all — there is no remote-tracking ref in that form, so pull merges `FETCH_HEAD`. Scripts sometimes fetch a ref and act on `FETCH_HEAD` rather than inventing a temporary branch name. And when you are debugging a confusing pull, reading `.git/FETCH_HEAD` tells you precisely which object the integration step was handed, which is often faster than reasoning about refspecs. ## Interview framing The question is really a probe for whether you understand fetch as an operation with its own output, distinct from the remote-tracking refs it usually also updates. A candidate who can say "pull is fetch then merge FETCH_HEAD" and can name a case where no remote-tracking ref exists has demonstrated exactly that.

  • What does the not-for-merge marker on a FETCH_HEAD line mean?
    It marks refs that were downloaded as part of the fetch but are not the branch you asked to integrate — for example every other branch a wildcard refspec brought down. Git skips those when something resolves `FETCH_HEAD` for merging, so `git merge FETCH_HEAD` picks the intended branch rather than an arbitrary one.
  • How do you keep something you fetched into FETCH_HEAD from being lost?
    Give it a real ref before the next fetch overwrites the file: `git branch review FETCH_HEAD` or `git tag`. Until a ref points at it, the fetched commit is unreachable once `FETCH_HEAD` is replaced and becomes a candidate for garbage collection. `git fetch --append` also preserves earlier entries within the file itself.

saying these in an interview costs you the question

  • Calls FETCH_HEAD a branch that Git checks out
  • Thinks it accumulates history across fetches
  • Confuses it with HEAD or ORIG_HEAD
  • Believes only remote-tracking refs record a fetch
  • Assumes a fetched commit survives without a ref

context