git fetch vs pull comes down to one question: do you want to see what changed before it touches your branch, or do you want Git to merge it in immediately? git fetch downloads new commits, branches, and tags from a remote and updates your remote-tracking branches — origin/main, origin/develop — without touching your working tree or your local main at all. git pull does that same fetch, then immediately merges (or rebases) the result into whatever branch you have checked out. Confusing the two is how "surprise merge commit" and "why did my file just change" tickets get filed. This guide covers what each command actually touches, the FETCH_HEAD file almost nobody explains well, the pull.ff=only default that prevents most of the damage, and the exact review workflow — fetch, log, diff, then decide — for reading a change before you merge it. If you haven't cloned the repo yet, start with cloning a remote Git repository first; everything below assumes an origin already exists.

Quick Answer: git fetch vs pull

git fetch downloads; git pull downloads and merges. That's the whole difference between git fetch and git pull in one sentence — everything else in this guide is detail. Run git fetch when you want to look before you leap: it updates your remote-tracking branches and writes FETCH_HEAD, but your checked-out branch and working tree are untouched. Run git pull when you're confident the incoming commits should be merged into your current branch right now, with no review step in between.

# See what changed, without touching your branch
git fetch origin

# Download AND merge into your current branch, immediately
git pull origin main

Which of the two is the right call for a given moment depends less on the command and more on whether your team has already agreed on a merge strategy — check which branch you're on and what it tracks before running either one blind. The sections below work through every real shape of the question: what each command touches mechanically, the FETCH_HEAD model nobody draws out, the concrete review workflow, and the config settings — pull.rebase, pull.ff — that turn git pull from a gamble into a predictable operation.

One clarification before going further: a GitHub pull request has nothing to do with git pull. It's a review workflow GitHub layered on top of Git, and it doesn't run either command on your machine — searches for github pull vs fetch almost always mean the two Git commands, not the PR feature. Phrasing aside, the underlying question is always the same one. Whether you searched for the git difference between fetch and pull, are comparing git fetch vs git pull for a team convention, or just want the git fetch meaning spelled out because you've only ever run pull, the answer is that single distinction: does the merge happen automatically, or do you get to look first?

What Does git fetch Do?

In plain English, the git fetch meaning is "go and get whatever is new on the remote, but don't change anything I'm working on." What does git fetch do, mechanically? It contacts the remote named in the command, downloads any commits, branches, and tags you don't have yet, and updates your remote-tracking branches — origin/main, origin/feature-x — to match what's currently on the remote. Nothing about your working tree, your index, or your currently checked-out branch changes. If you're sitting on main and a teammate pushed three commits to origin/main, after git fetch your local main is still exactly where it was; only origin/main moved. The full flag set and exact behavior is documented in Git's official git fetch reference.

git fetch Moves Only the Middle Box git fetch × origin remote repository new commits, branches, tags origin/main remote-tracking branch updated by fetch main your checked-out branch untouched by fetch git fetch updates origin/main only — your working tree and checked-out branch never move
git fetch downloads from origin and updates the origin/main remote-tracking branch. Your local main and working tree stay exactly where they were.

What does git fetch origin do specifically? git fetch origin fetches every branch and tag tracked from the remote named origin — the default remote name Git assigns when you clone. Naming a branch narrows that scope: git fetch origin main updates only origin/main, skipping every other remote-tracking branch, which is noticeably faster on a large repo with dozens of branches. Narrowing it permanently instead of per-call is a clone-time decision: cloning a single branch writes a scoped fetch refspec into .git/config, so every later fetch stays narrow without you naming the branch each time.

# Fetch everything from origin — all branches, all tags
git fetch origin

# Fetch just main
git fetch origin main

# Fetch from every configured remote at once
git fetch --all

origin/main is a remote-tracking branch, not a branch you commit to directly — it's Git's local bookmark for where main sat on origin the last time you fetched. You can check it out (Git detaches HEAD when you do) or diff against it, but there's no such thing as pushing to it; you push to main on origin, and your next fetch moves the bookmark to match. The mechanics of remote-tracking branches are covered in more depth in the Git Branching chapter on remote branches, and it's worth confirming locally too — checking out a remote branch only works once at least one fetch or clone has created the bookmark you're checking out.

What Does git pull Do?

git pull is fetch plus one more step: internally it runs the equivalent of git fetch, then git merge FETCH_HEAD — or, depending on configuration, rebasing onto the fetched commits — into your current branch. That second step is the entire difference between git fetch and git pull; pull never skips it. Run git pull origin main while checked out on a feature branch and Git fetches origin's main, then tries to merge origin/main into your feature branch — not into main itself, which is a common source of confusion for anyone who assumed pull always updates the branch named in the command.

git pull origin main
# is roughly equivalent to running these two commands back to back:
git fetch origin main
git merge FETCH_HEAD

Run git pull with no arguments and Git resolves the remote and branch from your current branch's tracking configuration — set automatically by git clone and by git push -u — rather than reading them off the command line. The complete option set, including the merge and rebase variants, is in Git's official git pull reference. When the two branches haven't diverged, that merge step fast-forwards silently and nothing about it feels risky; the divergent case, covered a few sections down, is where git pull's convenience starts costing something — a surprise merge commit that's already been pushed comes back out only by undoing the merge commit with revert -m 1.

git pull Is Fetch Plus One More Step git pull = git fetch + git merge FETCH_HEAD (or git rebase, if configured) always runs — never skipped Same download either way — the difference is entirely in that second, automatic step
git pull runs an internal git fetch, then immediately merges FETCH_HEAD — or rebases, if pull.rebase is configured — into your current branch.

Key Differences Between git fetch and git pull

Laid out side by side, git fetch vs pull is really a question of how many steps run and how much of that is automatic versus something you control.

Aspect git fetch git pull
What it downloads New commits, branches, tags from the remote Same download — via an internal git fetch
Updates working tree? No Yes — merges or rebases into the checked-out branch
Updates current branch? No, only remote-tracking branches move Yes, the checked-out branch moves
Can create a merge commit No Yes, when history diverged and pull.rebase isn't true
Can rewrite local commits No Yes, when pull.rebase=true
Risk of surprise conflicts None — nothing merges Real — conflicts can appear mid-command
Safe to run repeatedly Always Only if you're fine with an auto-merge or rebase each time
Undo if you don't like the result Nothing to undo — no local state changed git merge --abort, or a reset back to ORIG_HEAD

That last row is the one worth internalizing: a plain git fetch never leaves anything to undo, because it never touches your branch. A git pull that goes wrong might need aborting a merge that's already in progress or, if you already committed the result, resetting your branch back to where it was before the pull ran. Both are real, safe fixes — but they're fixes you only need after pull, never after a bare fetch.

FETCH_HEAD: The Piece Nobody Explains

Every git fetch writes a file: .git/FETCH_HEAD. It's a plain-text record of every ref the fetch just downloaded, each line pairing a commit SHA with where it came from. git pull's merge step reads this file directly — a bare git pull runs git merge FETCH_HEAD, merging whichever fetched ref matches your current branch's configured upstream. Most days you never look at FETCH_HEAD on purpose, but the moment git pull does something you didn't expect, it's the first place with the actual answer of what got fetched and from where.

$ git fetch origin
$ cat .git/FETCH_HEAD
a1b2c3d4e5f6...        branch 'main' of https://github.com/you/repo
f6e5d4c3b2a1... not-for-merge branch 'develop' of https://github.com/you/repo
What git fetch Writes, What git pull Reads git fetch writes updates .git/FETCH_HEAD transient — overwritten every fetch a1b2c3d branch 'main' f6e5d4c not-for-merge 'develop' pull reads this, skips not-for-merge lines origin/main durable — persists between fetches always reflects the last fetch of main separate from FETCH_HEAD entirely read by pull git pull git merge FETCH_HEAD or git rebase, against it git fetch always updates both boxes — git pull's merge step only ever reads FETCH_HEAD
git fetch writes the transient FETCH_HEAD file and updates the durable origin/main pointer at the same time. git pull's merge step reads only FETCH_HEAD.

Refs that were fetched but don't match your branch's merge configuration get marked not-for-merge in that file, exactly like develop in the example above — git pull downloads them for reference but ignores them when deciding what to merge. The distinction between FETCH_HEAD and a remote-tracking branch like origin/main is the part most explanations skip: origin/main is a durable pointer that persists between fetches and always reflects the last time main was fetched. FETCH_HEAD is transient — overwritten by every single git fetch, regardless of branch, recording only what the most recent fetch pulled down. That makes it the tool for fetching something you don't want a permanent remote-tracking branch for — a pull request ref, for instance: git fetch origin pull/123/head followed by git merge FETCH_HEAD merges that ref without ever creating a local branch for it.

The Review Workflow: Fetch, Log, Diff, Then Decide

How do I review changes before merging? Skip git pull entirely and run three commands in sequence instead of one — that's the whole trick, and it's the workflow this guide is really arguing for.

# 1. Download without touching anything
git fetch origin

# 2. See which commits are new, oldest first
git log --oneline main..origin/main

# 3. See the actual content changes
git diff main origin/main

# 4. Now decide, deliberately
git merge origin/main
# or
git rebase origin/main

The double-dot range in step 2, main..origin/main, means "commits reachable from origin/main but not from main" — exactly the set that would land on your branch if you merged right now. git diff main origin/main in step 3 shows the actual content behind that commit list: every line added, changed, or removed, computed once between the two tips rather than commit by commit.

Raw git diff output is readable for a three-line change and a wall of text for a rewritten module. Pasting it into a dedicated side-by-side diff viewer turns the same information into color-coded, word-level highlighted changes instead of a page of plus and minus signs. That's the actual use case for a tool like Diff Checker here: it doesn't run git commands or talk to your remote — it just makes the output of git diff main origin/main, or the fetched version of a single file pasted next to your local copy, something you can actually read before you commit to a merge or a rebase.

Fetch, Log, Diff, Then Decide 1 git fetch origin download without touching anything 2 git log --oneline main..origin/main see which commits are new 3 git diff main origin/main see the actual content changes 4 git merge origin/main deliberate, after review 4 git rebase origin/main deliberate, after review All three read-only commands run before step 4 — the fork only happens after you've seen the diff
Fetch, log, diff — three read-only steps — then decide deliberately between merge and rebase, instead of letting a bare git pull choose for you.

If you have uncommitted local edits, git fetch is still safe on its own — it never touches the working tree — but the merge or rebase step after it isn't. Stashing those changes first keeps the review honest instead of mixing your own in-progress edits into the diff you're trying to read; if those edits were throwaway anyway, discarding local changes outright clears the way just as well.

git pull --ff-only: The Modern Safe Default

Why use git fetch instead of git pull? Because a bare git pull merges or rebases without asking first, and if your local branch and origin/main have diverged, that merge can land a commit you never actually reviewed. --ff-only closes that gap without giving up the convenience of pull entirely.

# Pull, but refuse unless it's a clean fast-forward
git pull --ff-only

# Make that the permanent default for this repo
git config pull.ff only

# Or globally, for every repo on this machine
git config --global pull.ff only

With pull.ff=only set, git pull succeeds silently exactly when your branch hasn't diverged — no local commits sitting ahead of the last fetch — and fails loudly the moment it has, instead of picking a reconciliation strategy for you. "Fails loudly" means an actual error and a non-zero exit code, not a merge commit you discover three days later scrolling through git log. That failure is the point: it hands you the same signal git fetch always gives, forcing the review-then-decide workflow from the section above instead of letting you skip straight to a merge you didn't mean to make.

Treat pull.ff=only the way you'd treat a seatbelt, not a restriction — it doesn't stop you from merging or rebasing, it stops Git from choosing for you on the one day you weren't paying attention. That's the single highest-leverage config change most of the confusion behind git fetch or git pull questions is actually trying to solve.

Configuring git pull: pull.rebase and pull.ff

How do I configure git pull behavior? Three settings cover every case — pull.rebase, pull.ff, and the interaction between them — and one of them is the reason git pull started printing a warning in the first place.

Since Git 2.27, released June 2020, running git pull on a branch that has diverged from its upstream — both sides have new commits since the last common point — prints: "Pulling without specifying how to reconcile divergent branches is discouraged." Git still defaults to a merge commit in that case, exactly as it always did; the warning exists because that silent default was the actual complaint. Teams that wanted a rebase were getting merge commits without ever deciding to, and teams that wanted a merge had no record of having consciously chosen it. The fix isn't a new default behavior — it's making the existing choice explicit before Git acts on it.

Setting How to Set It What git pull Does Best For
Merge (classic default) git config pull.rebase false Creates a merge commit on divergence; fast-forwards otherwise Shared branches, preserving exact history
Rebase git config pull.rebase true Replays local commits on top of the fetched upstream instead of merging Personal feature branches, before they're pushed
Fast-forward only git config pull.ff only Refuses to pull at all unless a fast-forward is possible Anyone who wants an explicit stop instead of an automatic decision
Not set, Git before 2.27 (no config) Silently merges on divergence, no warning printed Legacy behavior, not recommended today
Not set, Git 2.27 or newer (no config) Warns about divergence on every affected pull, still merges Transitional — pick one of the three rows above instead

Set any of these per-repo with a plain git config, or add --global to apply the default across every repo on the machine. There's no wrong answer among the first three rows — the only genuinely bad state is the unset default, which is the one worth eliminating on any machine you use regularly.

Same Divergence, Three Different pull.* Outcomes Have branches diverged? No Yes No divergence any pull.* setting fast-forward, one line pull.rebase=false merge commit fork, then rejoin pull.rebase=true rebase, linear commits replayed, new SHAs pull.ff=only refuse and stop no merge commit made Same divergence, three pull.* configs, three different resulting git log shapes
Divergence is the same in all three cases — the resulting git log shape depends entirely on which pull.* configuration is set.

Fetching Into a Local Branch: git fetch origin main:main

git fetch accepts a refspec — source:destination — that lets you fetch directly into a local branch ref without checking it out first. git fetch origin main:main updates your local refs/heads/main to match origin/main's tip, bypassing the usual remote-tracking indirection and the merge step pull would otherwise run.

# Works from a completely different branch
$ git status
On branch feature/login

# Update local main from origin, without ever checking main out
$ git fetch origin main:main

$ git branch -vv
  main            a1b2c3d [origin/main] ...
* feature/login   9f8e7d6 [origin/feature/login] ...

Git refuses that refspec form if the target branch is the one currently checked out — updating the ref under your own feet mid-edit would leave your working tree out of sync with what the branch now points to. This refusal applies regardless of whether it would be a fast-forward. Stay on a different branch and the same refspec updates main safely in the background, which is the actual use case: keeping a local main current for comparison, or for branching off a fresh checkout later, without ever switching away from the branch you're actually working on.

Rebase or Merge: Choosing a pull Strategy

Merge (pull.rebase=false) preserves the true chronological history, including concurrent work from other people — git log --graph shows the actual branching and rejoining, and reverting an entire merge is a single command if the change turns out to be wrong. The cost is a merge commit every time your branch and its upstream diverged, which adds up to a noisier log on a busy repo.

Rebase (pull.rebase=true) produces a linear history that reads cleanly with git log --oneline and bisects easily, at the cost of rewriting your local commits' SHAs every time it runs. Anything already pushed and then rebased needs a force push to update the remote, and a branch other people have already built work on top of must never be rebased out from under them — that's the one hard rule this strategy comes with.

In practice: personal feature branches nobody else has branched from are safe with pull.rebase=true, kept tidy right up until you push the branch to share it. Shared branches — main, release branches, anything protected — are safer with pull.rebase=false or pull.ff=only, never silently rewritten underneath the team. If a long-lived feature branch needs to catch up with main without rebasing its own history, merging master into the branch is the conventional way to do that periodically, and a pile of rough commits accumulated along the way can still be squashed into one right before it's reviewed — so choosing merge over rebase for safety doesn't have to mean living with messy history forever.

Same Commits, Two Different Log Shapes pull.rebase=false merge — true chronology preserved A B L M A, B from origin — L is your local commit M is a new merge commit; A, B, L keep their SHAs pull.rebase=true rebase — linear, easy to bisect A B L' A, B unchanged — L is replayed after them L becomes L' — same content, new SHA Same underlying edits, same starting point — the log shape is a config choice, not a fact about the code
Merge (left) keeps the true branching history with a merge commit M. Rebase (right) replays the same edits onto a single line, rewriting the local commit's SHA from L to L'.

GitHub Pull vs Fetch: Desktop, the Web UI, and Pull Requests

Hosting a repository on GitHub changes nothing about what these commands do — Git is Git, and origin is just an HTTPS or SSH URL. What changes is the wrapper you click instead of typing. That's why github pull vs fetch is its own question: plenty of people meet the two operations as buttons long before they meet them as commands, and a button hides which one it's about to run.

GitHub Desktop puts both behind a single toolbar button. It reads Fetch origin and runs a plain git fetch; the moment that fetch turns up commits your branch doesn't have, the same button relabels itself Pull origin with a count beside it, and clicking it now runs git pull. Desktop also fetches periodically on its own, which is why the label sometimes changes while you're doing something else. Framed that way, the Git difference between fetch and pull is easier to see than it is on the command line: one label downloads, the other downloads and then rewrites the files in front of you. It's also why so many people use git pull and fetch interchangeably for years — the button never made them choose.

On github.com itself there is no fetch and no pull, because both are things your machine does to its own clone. The web UI only ever shows you the state of the remote. Sync fork on a fork, and Update branch on an open pull request, do their merging on GitHub's servers, inside the remote copy — your local clone learns about the result on your next fetch and not a second sooner. Editors split the same way: VS Code exposes Git: Fetch and Git: Pull as separate commands, and its Sync button is a pull followed by a push.

As for the feature itself, a pull request borrows the word from an older workflow — asking a maintainer to pull from your repository — but nothing about opening or reviewing one touches your working tree. Getting a PR's commits onto your machine to read them is a fetch, not a pull: git fetch origin pull/123/head followed by a look at FETCH_HEAD, the same trick from the section above, and no local branch created for something you only wanted to read.

git fetch vs git pull: When to Use Which

  • Starting your day, want to see what's new firstgit fetch, then git log --oneline main..origin/main and git diff main origin/main.
  • Automated build or CI scriptgit fetch, not pull, unless the merge strategy is pinned exactly and reviewed like any other script.
  • Confident, no local changes, branch tracks a clean remotegit pull --ff-only, or the equivalent config setting.
  • Personal branch catching up with main, no merge commit wantedgit pull --rebase, or git fetch followed by git rebase origin/main.
  • Reviewing a teammate's push before touching your own work — always fetch, review with diff, decide after.

git fetch or git pull isn't really a choice between two competing commands — fetch is strictly safer, pull is strictly more convenient, and pull only ever calls fetch internally to begin with. Default to fetch when you're not sure what changed; default to a pinned pull strategy — --ff-only on shared branches, --rebase on your own — once you are.

Frequently Asked Questions

What does git fetch do?

git fetch contacts a remote, downloads any commits, branches, and tags you don't already have, and updates your remote-tracking branches — origin/main, origin/develop — to match. It never touches your working tree, your index, or whichever branch you currently have checked out; only the origin/* bookmarks move. That's what makes it safe to run at any point, including with uncommitted local changes sitting in your working tree.

What does git fetch origin do?

git fetch origin contacts the remote named origin — the default name Git assigns when you clone — and downloads every branch and tag that remote tracks, updating all of your origin/* remote-tracking branches in one go. Adding a branch name after it narrows the scope to that single ref, which is noticeably faster on a repo with dozens of branches. Either form leaves your checked-out branch and working tree completely alone.

Is git pull the same as git fetch?

No. Every git pull runs a git fetch as its first step, but not the other way around. After the download, pull keeps going and merges or rebases the fetched commits into whatever branch you have checked out. People often say git pull and fetch as though the two were interchangeable; the half of pull that isn't fetch is exactly the half that can change your files and create commits.

What is the difference between git fetch and git pull?

git fetch only downloads and updates remote-tracking branches. git pull runs that same fetch internally, then immediately merges — or rebases, if configured — the result into your current branch. The download step is identical; the difference is entirely in what happens after: fetch stops, pull keeps going and changes your checked-out branch.

Should I use git fetch or git pull?

Use git fetch whenever you don't already know what's waiting on the remote. It gives you a chance to look before anything changes: run git fetch, then git log --oneline main..origin/main and git diff main origin/main to see exactly what's incoming, and only then run git merge or git rebase deliberately. Reach for git pull when you already know what's coming and want it applied now — and pin it with --ff-only or --rebase so Git can't quietly pick a reconciliation strategy for you.

What is the difference between fetch and pull in GitHub?

The commands behave identically regardless of where the repository is hosted — GitHub changes nothing about Git itself. What differs is the wrapper around them. GitHub Desktop's toolbar button reads Fetch origin, then switches to Pull origin once a fetch finds commits you don't have, keeping the two steps visible and separate. On github.com there is no fetch or pull at all: buttons like Sync fork and Update branch merge on GitHub's servers, and you still need a local fetch or pull afterwards. A GitHub pull request is neither command.

What are remote-tracking branches?

Remote-tracking branches — origin/main, origin/feature-x — are local bookmarks for where a branch sat on the remote the last time you fetched or cloned. You can check one out or diff against it, but you can't commit directly to it; the only thing that moves origin/main is your next git fetch, or the fetch git pull runs internally. They're separate from FETCH_HEAD, which is overwritten by every fetch instead of persisting per branch.

What is FETCH_HEAD?

FETCH_HEAD is a file — .git/FETCH_HEAD — that git fetch writes on every run, listing each ref it downloaded along with its commit SHA and source. git pull's merge step reads it: a plain git pull runs git merge FETCH_HEAD, or rebases against it, using whichever fetched ref matches your branch's configured upstream. Unlike a remote-tracking branch, FETCH_HEAD is transient — the next fetch overwrites it completely.