By admin , 28 April 2026

Introduction

git fetch is the network operation that downloads new objects and updates remote-tracking refs. It does not change your branches or working tree, which makes it the safest "what's new?" command.

The sequence

  1. Resolve the remote URL.
  2. Open a connection (HTTPS, SSH, or git://).
  3. Negotiate capabilities (protocol version, multi-ack, partial clone filters, etc.).
  4. Server advertises refs (or, in protocol v2, only those matching requested patterns).
  5. Client reports what it already has.
  6. Server sends a pack file containing the missing objects.
  7. Client writes the pack to .git/objects/pack/ and indexes it.
  8. Client updates remote-tracking refs (refs/remotes/origin/*).
  9. Optionally prunes deleted refs.

Commands

git fetch
git fetch origin
git fetch --all
git fetch --prune
git fetch origin main:my-main         # fetch into a local ref

The colon syntax main:my-main creates or updates a local ref directly; use it carefully.

Inspecting what was fetched

git fetch origin
cat .git/FETCH_HEAD
git log HEAD..origin/main --oneline

FETCH_HEAD lists every ref updated by the most recent fetch, in the order requested.

Refspecs

Each remote has one or more fetch refspecs. The default for origin:

+refs/heads/*:refs/remotes/origin/*

You can add more, for instance to fetch pull-request refs from GitHub:

git config --add remote.origin.fetch '+refs/pull/*/head:refs/remotes/origin/pr/*'
git fetch
git switch -c review/123 origin/pr/123

Pruning

Without prune, branches deleted on the server linger as stale origin/* refs:

git fetch --prune
git config --global fetch.prune true

Tags

By default, fetch downloads tags pointing at fetched commits. Force or disable:

git fetch --tags
git fetch --no-tags

Trace and debug

GIT_TRACE=1 GIT_TRACE_PACKET=1 git fetch
GIT_CURL_VERBOSE=1 git fetch        # HTTPS

These reveal exact requests, useful for debugging proxies or auth.

Negotiation and haves

The fetch handshake is a dance of "wants" and "haves". The client lists what it wants (the tip SHAs of remote branches), and offers up haves (recent local commits) so the server can compute a minimal pack. Modern Git uses the skipping negotiation algorithm, which converges in O(log n) rounds even on long histories. You can tune it:

git config fetch.negotiationAlgorithm skipping
git config fetch.negotiationAlgorithm consecutive
GIT_TRACE_PACKET=1 git fetch 2>&1 | grep -E 'have|want' | head

Common mistakes

Believing git fetch changes your branches; it never does. Use git pull or git merge origin/main to integrate. Skipping --prune in long-lived clones and accumulating dozens of stale origin/* refs. Misconfiguring a refspec by hand and overwriting local branches; double-check with git config -e. Finally, fetching from a partial clone without realizing some objects will require a follow-up network call when accessed; partial clones trade up-front speed for occasional latency later.