Skip to content
masterclass Buddy

Git · Command

git fetch

git fetch downloads commits, trees, blobs and refs (branches and tags) from one or more other repositories and updates your remote-tracking branches. It does not merge anything into your own branches or change your working tree.

Last updated

How it works

Fetch first contacts the remote and works out which commits you already have. It then downloads only the objects you are missing. Next, it uses a refspec (+src:dst) to decide which remote refs to fetch and where to store them locally. If you give no refspec on the command line, Git uses the remote.<name>.fetch config values, which normally map refs/heads/* to refs/remotes/<name>/*. Updates that are not fast-forwards are refused unless the refspec starts with + or you pass --force. Fetch also writes the refs it got to FETCH_HEAD, which git pull and scripts can read.

Syntax

git fetch [<options>] [<repository> [<refspec>…]]
git fetch [<options>] <group>
git fetch --multiple [<options>] [(<repository>|<group>)…]
git fetch --all [<options>]

Parameters

NameTypeDescription
<repository>URL or remote nameThe repository to fetch from. This can be a URL or the name of a configured remote.
<group>stringA name defined by remotes.<group> in your config that stands for a list of repositories.
<refspec>[+]<src>:<dst>Says which remote refs to fetch and which local refs to update. A leading + allows non-fast-forward updates, a ^ prefix makes it a negative (exclusion) refspec, and * acts as a pattern.
--prune / -pflagBefore fetching, deletes remote-tracking refs whose branches no longer exist on the remote.
--tags / --no-tagsflag--tags fetches every tag. --no-tags turns off automatic tag following.
--depth=<depth> / --unshallowint / flag--depth limits or changes how much history a shallow repository has. --unshallow turns a shallow repository into a complete one.
--all / --multipleflag--all fetches from every remote. --multiple lets you name several repositories or groups.
--dry-runflagShows what would happen without changing anything.

Returns

For each ref, fetch prints a status line of the form '<flag> <summary> <from> -> <to> [<reason>]' to stderr. The flags are: space = fast-forward, + = forced update, - = pruned, t = tag update, * = new ref, ! = rejected, = = up to date. With --porcelain it prints a machine-readable format to stdout instead.

Key points

  • If you don't name a remote, fetch uses the current branch's upstream remote when one is set, and origin otherwise.
  • By default, fetch also downloads tags that point into the history it is fetching. Use --tags to get all tags, or --no-tags to get none automatically.
  • When you list refspecs on the command line, only those refs are fetched. The configured remote.<name>.fetch values then just act as a map that decides which remote-tracking ref gets updated.
  • --atomic updates all local refs in one transaction, so you never end up with only some refs updated after a failure.
  • --unshallow downloads the missing history so a shallow clone becomes a complete repository.
  • Fetching from several remotes or submodules happens one at a time unless you use -j/--jobs or the fetch.parallel setting.
  • If a remote has several url entries, fetch only uses the first one. Push, by contrast, goes to all of them.

Common mistakes

  • --prune-tags also deletes tags you created locally that don't exist on the remote, so it is much more destructive than --prune.
  • If you combine --prune with a refspec like refs/tags/*:refs/tags/*, Git can delete tags you got from other remotes.
  • Since Git 2.20, an existing local tag that changed on the remote is not updated unless you use + in the refspec or --force.
  • --recurse-submodules can't fetch a submodule that upstream just added. You have to clone it first, for example with git submodule update.

Example: Safely inspect upstream changes before integrating

#!/usr/bin/env bash
set -euo pipefail

# Update all remote-tracking branches for origin, dropping deleted ones
git fetch --prune origin

# See what arrived on main that you don't have yet
git log --oneline main..origin/main

# Fetch one branch into a local branch of your choice
git fetch origin feature/login:review-login

# Peek at a branch from an unconfigured repo via FETCH_HEAD
git fetch https://git.kernel.org/pub/scm/git/git.git maint
git log -3 --oneline FETCH_HEAD

# Integrate only when ready
git merge --ff-only origin/main

The script updates and prunes origin's remote-tracking branches, shows the new upstream commits, fetches one branch under a local name, peeks at another repository through FETCH_HEAD, and fast-forwards main only at the end.

When to use it

Review before merging

Fetch and compare main with origin/main (or run git log HEAD..origin/main) so you can see incoming commits before you merge or rebase them.

Cleaning up deleted branches

Run git fetch --prune, or set fetch.prune=true, so remote-tracking branches deleted on the server stop showing up in git branch -a.

Deepening a CI shallow clone

A CI job cloned with --depth=1 and now needs more history for git describe or a merge-base. Run git fetch --deepen=N or git fetch --unshallow.

Keeping several remotes in sync

Use git fetch --all -j 4 to update origin, upstream and the remotes of forks in parallel.

Interview questions

QWhat is the difference between git fetch and git pull?

git fetch only downloads objects and updates refs: remote-tracking branches and FETCH_HEAD. Your local branches and working tree stay as they were. git pull runs a fetch and then merges or rebases the fetched branch into your current branch, using the information fetch wrote. That makes fetch the safe choice when you only want to look at upstream changes.

Follow-up: Why won't fetch update the branch you have checked out, even with a matching refspec? By default fetch refuses to update the ref for your current branch, because that would leave your working tree out of step with it. The --update-head-ok flag turns this check off, but it is meant only for git pull's internal use.

QExplain the refspec +refs/heads/*:refs/remotes/origin/*.

The left side picks the source refs on the remote, here every branch. The right side is where each one is stored locally, with * replaced by the matched name. The leading + allows non-fast-forward updates, so the remote-tracking branches still update after someone force-pushes or rebases. This is the default remote.origin.fetch value that git clone sets up.

Follow-up: How do you leave out some branches that a pattern matches? Add a negative refspec with a ^ prefix, for example ^refs/heads/tmp/*. A ref is fetched only if it matches at least one positive refspec and no negative one.

QWhat are stale remote-tracking branches and how do you deal with them?

They are refs/remotes/<name>/* entries whose branches have been deleted on the server. Git keeps them because it holds on to data until you explicitly remove it. Over time they clutter commands like git branch -a and can slow down busy repositories. You can remove them with git fetch --prune <name>, or with git remote prune <name> if you don't want to fetch, or set fetch.prune / remote.<name>.prune to prune on every fetch.

Follow-up: Does --prune delete local tags? Usually not. Tags fetched through auto-following or --tags are never pruned. They are pruned only when an explicit tag refspec is in effect, which is what --prune-tags adds.

  • git pull
  • git push
  • git remote
  • git clone
  • git config
  • git submodule
  • git maintenance
  • git gc

Source: Git - git-fetch Documentation. Checked 2026-11-02.