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
- Resolve the remote URL.
- Open a connection (HTTPS, SSH, or git://).
- Negotiate capabilities (protocol version, multi-ack, partial clone filters, etc.).
- Server advertises refs (or, in protocol v2, only those matching requested patterns).
- Client reports what it already has.
- Server sends a pack file containing the missing objects.
- Client writes the pack to
.git/objects/pack/and indexes it. - Client updates remote-tracking refs (
refs/remotes/origin/*). - 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.