Git Pull vs Fetch: What Each Command Really Does
Originally published on DevToolHub.
[IMAGE: articles/images/2026-11-05-git-pull-vs-fetch-diagram.png | alt: "Flow from remote to origin/main to the local branch, with fetch and pull arrows"]
git fetch downloads new commits and leaves your branch and your files alone. git pull runs a fetch and then merges or rebases the result into your current branch. The git pull vs fetch choice comes down to one question: do you want to look at incoming changes first, or take them right now?
I ran every command below on Git 2.56.0. A bare repository played the remote, and a second clone played a teammate who keeps pushing to main.
What Is the Difference Between Git Pull vs Fetch?
git fetch updates your remote-tracking branches, such as origin/main, and records what it got in .git/FETCH_HEAD. It never touches your local branches or your working tree. git pull runs git fetch first, then integrates the fetched branch into your current branch. So fetch is safe to run at any time, and pull can change your files.
git fetch | git pull | |
|---|---|---|
Updates origin/main | Yes | Yes |
| Moves your local branch | No | Yes, by merge or rebase |
| Changes files in your working tree | No | Yes |
| Can stop on a conflict | No | Yes |
| Safe with uncommitted changes | Yes | Only if they don't overlap |
The git fetch docs describe the first half. The git pull docs describe the second.
What Does Git Fetch Change in Your Repository?
Fetch changes your remote-tracking refs and nothing else. In my test the teammate pushed one commit. Before the fetch, git status saw nothing:
git status -sb ## main...origin/main Then I fetched:
git fetch From /tmp/gpf/remote 144625a..22c2c9f main -> origin/main Now Git knows the remote moved ahead:
## main...origin/main [behind 1] My app.txt still said v1. The new commit sat on origin/main, where I could read it first:
git log --oneline main..origin/main git diff main origin/main --stat 22c2c9f teammate edits app app.txt | 1 + 1 file changed, 1 insertion(+) .git/FETCH_HEAD held one line, the commit hash plus branch 'main' of /tmp/gpf/remote. Scripts use that file, and you rarely need to open it.
What Does Git Pull Do Beyond a Fetch?
Pull does the fetch, then moves your branch. From the same starting point, git pull printed this:
Updating 144625a..22c2c9f Fast-forward app.txt | 1 + 1 file changed, 1 insertion(+) That is the same result as running git fetch and then git merge origin/main. I checked: the two-step version printed the identical Fast-forward output. Pull knows which branch to merge because of the upstream settings Git stored when I first pushed with -u:
branch.main.remote origin branch.main.merge refs/heads/main A fast-forward is the easy case. The trouble starts when both sides have new commits.
Git Pull vs Fetch When Your Branches Have Diverged
When your branch and origin/main both have commits the other lacks, a bare git pull stops with an error until you say how to combine them. I added mine.txt locally while the teammate pushed another commit. After a fetch, status showed the split:
## main...origin/main [ahead 1, behind 1] * 8e9826c me adds mine.txt | * 0ed7d96 teammate adds line 2 |/ * 22c2c9f teammate edits app With no pull.* config set, git pull refused:
hint: You have divergent branches and need to specify how to reconcile them. hint: git config pull.rebase false # merge hint: git config pull.rebase true # rebase hint: git config pull.ff only # fast-forward only fatal: Need to specify how to reconcile divergent branches. The command exited with code 128 and changed nothing. You have three ways forward, and each leaves different history:
| Command | What it does | History after |
|---|---|---|
git pull --ff-only | Refuses unless a fast-forward is possible | Unchanged. It failed with Not possible to fast-forward, aborting. |
git pull --rebase | Replays your commits on top of origin/main | Linear. My commit became 1b5ae05 |
git pull --no-rebase | Merges origin/main into your branch | A merge commit, Merge branch 'main' of ... |
Rebase gave me a straight line of four commits. The merge version added a fifth commit with two parents. Neither is wrong. Rebase keeps git log readable, and merge never rewrites a commit you already made. If you want the longer comparison, the branching and merging strategies guide covers when each fits, and the rebase, stash and cherry-pick guide covers the rebase details.
Did the pull go the wrong way? Git records where your branch was before an integration in ORIG_HEAD. After my --rebase pull, I ran this to go back:
⚠️ Note: git reset --hard throws away every uncommitted change to tracked files in the whole repository. Commit or stash first.
git reset --hard ORIG_HEAD My branch returned to 8e9826c, the commit from before the rebase. The reset, revert and restore guide explains the three reset modes.
Why Does Git Pull Fail With Uncommitted Changes?
Pull fails when your uncommitted edits touch a file the incoming commits also change. Git aborts before it changes anything. I edited app.txt without committing while the teammate pushed a new line to the same file:
error: Your local changes to the following files would be overwritten by merge: app.txt Please commit your changes or stash them before you merge. Aborting A fetch in the same state worked without complaint. It printed 0ed7d96..b67314f main -> origin/main, and git status still showed my M app.txt edit untouched. That is the practical reason to fetch first: you see the incoming change, then decide whether to commit, stash, or wait.
How Do You Remove Stale Remote Branches With Fetch?
A plain git fetch adds new branches but never deletes old remote-tracking refs. Run git fetch --prune to remove the ones whose branch is gone from the remote. The teammate pushed feature/login, then deleted it from the remote. My next plain fetch printed nothing, and git branch -r still listed it:
origin/HEAD -> origin/main origin/feature/login origin/main With --prune, the stale ref went away:
git fetch --prune - [deleted] (none) -> origin/feature/login This only removes remote-tracking refs, never your local branches. Per the docs, tags are left alone unless you add --prune-tags. To make it the default for every fetch and pull, run git config --global fetch.prune true.
Git Pull vs Fetch: Which Should You Use?
Use git fetch when you want to see what changed before you integrate it, and git pull when your branch is simply behind and you trust the incoming commits. If you pull on a shared, busy branch, expect the divergence prompt sooner or later.
- Fetch first when you have uncommitted work, when the branch is shared and busy, or when a script only needs to know whether you're behind.
- Pull when you are a plain fast-forward behind. My first pull above was exactly that.
- Use a two-step habit when you want control:
git fetch, thengit status -sb, thengit merge --ff-only origin/mainorgit rebase origin/main.
Settle the divergent-branch prompt once instead of per command. For linear history, set git config --global pull.rebase true. To refuse anything except a fast-forward, set git config --global pull.ff only. Those two keys come from Git's own hint text above. I tested the matching command-line flags, not the config keys themselves.
Nothing in the Git 3.0 breaking changes list touches pull or fetch, so nothing planned for Git 3.0 changes how these two commands behave. For the bigger picture of how these commands fit together, start with the Git fundamentals guide.
Frequently Asked Questions
Q: Is git pull the same as git fetch plus git merge?
A: Yes, by default. git pull runs git fetch, then merges the upstream branch into your current branch. In my test, git fetch followed by git merge origin/main printed the same Fast-forward output as a single pull. With --rebase it rebases instead of merging.
Q: Does git fetch change my working directory?
A: No. It updates remote-tracking branches like origin/main and .git/FETCH_HEAD only. My edited app.txt was untouched after a fetch, and git status still showed it as modified.
Q: Why does git pull say "Need to specify how to reconcile divergent branches"?
A: Your branch and the remote both have new commits, and no pull.rebase or pull.ff setting tells Git what to do. Pass --rebase, --no-rebase or --ff-only, or set pull.rebase or pull.ff once.
Q: Should I use git fetch or git pull?
A: Fetch when you want to inspect incoming commits first or have uncommitted work. Pull when you're just behind and want to catch up. Many people fetch, check git status -sb, then merge or rebase by hand.
Q: How do I delete remote-tracking branches that no longer exist?
A: Run git fetch --prune. It removes stale origin/* refs and leaves local branches alone. Set git config --global fetch.prune true to do it on every fetch.
Quick Summary:
-
git fetchupdatesorigin/mainand.git/FETCH_HEAD, and never touches your branch or files -
git pullis a fetch plus a merge or rebase, so it can change files and stop on conflicts - A bare pull on diverged branches fails with exit 128 until you pass
--rebase,--no-rebaseor--ff-only - Pull aborts if uncommitted edits overlap incoming changes, while fetch works fine
- Plain fetch keeps stale
origin/*refs, and--pruneremoves them -
git reset --hard ORIG_HEADbrought my branch back to its pre-rebase commit
Next time the git pull vs fetch question comes up, run git fetch and git status -sb first. If the status says behind, pull is a safe fast-forward. If it says ahead and behind, pick a strategy before you pull.
原文:https://www.a8q9.cn/en/article/git-pull-vs-fetch-what-each-command-really-does-07ca10.html