git fetch downloads new commits from the remote and updates your remote-tracking branches, but it leaves your working files and your current branch exactly as they are. git pull does that same fetch and then immediately merges the new commits into the branch you have checked out. So a pull is really two commands in one: fetch, then merge. The practical difference is that fetch lets you look before you leap, while pull changes your working branch right away.
The one-line answer
If you only remember one thing: git fetch is read-only against your work, git pull modifies it.
# Download remote changes, touch nothing in your working tree
git fetch origin
# Download remote changes AND merge them into the current branch
git pull origin mainAfter a git fetch, your files are unchanged. The new commits sit in a remote-tracking branch named origin/main (or whatever the remote and branch are called), and you can inspect them before deciding what to do. After a git pull, those commits are already in your branch and your files may have changed.
What git fetch actually does
A fetch contacts the remote (over whatever connection you configured, which is its own decision between the difference between SSH and HTTPS remotes), downloads any commits, branches, and tags you do not have yet, and stores them under refs/remotes/. Your local branch pointer does not move. Your working directory is untouched: fetch only writes to the remote-tracking refs, not to your index or working tree, which is worth understanding alongside how the remote-tracking index and staging area work.
git fetch originThe useful mental model: after a fetch, your local main and the remote's version of main are two separate pointers. The remote's version is now visible to you as origin/main. You can compare them:
# See what commits the remote has that you don't
git log main..origin/main --oneline
# See the actual file changes coming in
git diff main origin/mainNothing in that workflow can clobber your uncommitted work, because fetch never merges. This is exactly why I reach for fetch when I am not sure what landed upstream. If you want to understand how origin/main relates to your local main, the Git branching basics walk through branch pointers in detail.
What git pull actually does
git pull is a convenience wrapper. By default it runs a fetch and then a merge:
git pull origin main
# is roughly equivalent to:
git fetch origin
git merge origin/mainIf the remote only added commits on top of where you were (a fast-forward), the merge is silent and your branch pointer just moves forward. If your local branch and the remote branch have both moved on independently, the pull creates a merge commit, or it stops with a conflict you have to resolve. When that happens, the merge conflict resolution guide covers the markers and how to finish the merge.
You can also tell pull to rebase instead of merge, which replays your local commits on top of the fetched ones rather than tying them together with a merge commit:
git pull --rebase origin mainThat is the same fetch, followed by git rebase origin/main instead of git merge. Which one you want is its own decision; I compare them in git merge vs git rebase.
pull = fetch + merge (or rebase)
Here is the whole relationship in one table.
| Step | git fetch | git pull (default) | git pull --rebase |
|---|---|---|---|
| Downloads remote commits | Yes | Yes | Yes |
Updates origin/main | Yes | Yes | Yes |
| Moves your local branch | No | Yes | Yes |
| Changes working files | No | Yes | Yes |
| Can create a merge commit | No | Yes | No (replays instead) |
| Can stop on a conflict | No | Yes | Yes |
| Safe with uncommitted work | Yes | Risky | Risky |
The bottom two rows are the reason this distinction matters in day-to-day work.
Why fetch then review is safer than a blind pull
A blind git pull is fine when you are alone on a branch and your working tree is clean. It gets risky when either of those is not true.
If you have uncommitted changes and the incoming commits touch the same files, the merge step of a pull can refuse to run, leaving you staring at error: Your local changes would be overwritten by merge. I cover that exact case and the ways out of it in the "local changes would be overwritten" fix. The same divergence bites on the push side too, where it shows up as git pull failing with a non-fast-forward push error. Fetching first sidesteps the surprise entirely: you fetch, you see what is coming, and you decide whether to stash, commit, or wait. And if a merge or rebase ever moves your branch somewhere you did not expect, you can recover them with git reflog, which logs every position the branch tip has occupied.
The fetch-first habit looks like this:
# 1. Pull down the remote state without touching anything
git fetch origin
# 2. See what changed and where you stand
git status
git log --oneline main..origin/main
# 3. Now merge (or rebase) on your own terms
git merge origin/mainIf step 2 shows incoming changes you are not ready for, you can stash your work first, then merge, then pop the stash. If you decide you want none of your local edits anyway, you can discard local changes before merging. The point is that fetch buys you a decision point that pull skips.
Divergent branches and the reconcile prompt
Git 2.27 (mid-2020) started warning when you ran git pull on a diverged branch without telling it how to reconcile, but it still completed the pull with the default merge:
warning: Pulling without specifying how to reconcile divergent branches is
discouraged.Git 2.34 (late 2021) turned that into a hard error: now git pull aborts and refuses to guess if your branch and the remote have diverged and you have not set a reconcile strategy:
fatal: Need to specify how to reconcile divergent branches.This is Git asking whether you want a merge or a rebase. You set the behaviour once and the message goes away:
# Default to merge on pull
git config --global pull.rebase false
# Or default to rebase on pull
git config --global pull.rebase true
# Or only fast-forward, and error out if a real merge would be needed
git config --global pull.ff onlyIf you hit that message, the divergent branches fix walks through choosing the right setting for your workflow.
When to use which
A quick rule of thumb I follow:
- Use
git fetchwhen you want to know what is on the remote without committing to integrating it yet: before starting work in the morning, before a tricky merge, or when reviewing what a teammate pushed. - Use
git pullwhen you are on a clean branch, you already know roughly what is upstream, and you just want it integrated now. On a personal feature branch with nothing staged, a pull is perfectly fine and saves keystrokes. - Use
git pull --rebaseon a shared branch where you want to keep history linear and avoid a clutter of merge commits.
If you are brand new to all of this, start with the Git for beginners hub, which sequences these commands into a learnable order rather than dropping them on you all at once. Working on a project someone else set up is covered in starting on an existing Git project, and if you do not yet have a remote at all, here is how to set up a project's remote in the first place.
Updating the remote-tracking refs without merging anything
One more practical note: git fetch is also how you prune branches that were deleted on the remote, so your origin/* list stays honest.
# Fetch and drop remote-tracking branches that no longer exist upstream
git fetch --prune originThat keeps git branch -r from listing branches that were merged and deleted weeks ago. A plain git pull does not prune by default, which is another small reason the explicit fetch is worth keeping in your toolbox.
Sources
Authoritative references this article was fact-checked against.
- git-fetch: official Git documentationgit-scm.com
- git-pull: official Git documentationgit-scm.com
- Pro Git: Remote Branchesgit-scm.com





