The difference between git pull and git fetch

git pull is git fetch followed by a merge. That one sentence is the whole answer, and it is easier to trust once you have watched what each half does to your repository.

fetch moves one pointer and nothing else

before fetch:  local main ea0365c   origin/main ea0365c
after  fetch:  local main ea0365c   origin/main a087a00   <- moved
               file.txt   one|                            <- unchanged

git fetch downloads the new commits and updates origin/main, the remote-tracking ref. Your branch does not move. Your files do not change. Afterwards your repository knows about work it has not adopted.

That is the whole difference. origin/main is git’s note of where the remote was; main is where you are.

Which is what lets you look before you leap

$ git log main..origin/main
a087a00 colleague commit

$ git diff --stat main origin/main
 file.txt | 1 +
 1 file changed, 1 insertion(+)

The main..origin/main range means “commits on the remote that I do not have”. This is the actual argument for fetching rather than pulling: on an unfamiliar repository, or before a release, you get to read what arrived before it lands in your working tree.

pull does the second half too

$ git pull
after pull: local main a087a00   file.txt one|two|

Same result you would get from fetch followed by merge. On a branch that has not diverged this is a fast-forward — your branch pointer simply advances, and no merge commit is created.

When both sides have moved

If you committed locally and the remote moved, there is nothing to fast-forward and git has to combine the two histories:

--no-rebase:            --rebase:
*   17ec289 Merge...    * fb288d3 my local commit
|\                      * a087a00 colleague commit
| * a087a00 colleague   * ea0365c first commit
* | e1f425a my local
|/

The default merges, producing the merge commit and the fork in the graph. --rebase replays your commit on top instead, giving a straight line.

Look at the hash in the rebase column: e1f425a became fb288d3. Rebasing does not move your commit, it makes a new one with the same changes. That is fine for commits only you have, and the usual warning applies once they are shared.

Whichever you prefer, set it explicitly so it does not depend on the machine:

$ git config --global pull.rebase true    # or false

Recent git versions warn when you pull into diverged history without having chosen, which is worth doing something about rather than ignoring.

fetch cannot leave you halfway

with an uncommitted edit:
  fetch: ok, origin/main now a087a00
  pull : error: Your local changes ... Aborting

fetch only writes to .git, so it works regardless of the state of your working tree. pull has to merge, and a merge can refuse — or, worse, stop partway through with conflict markers in your files.

This is why “fetch often, pull deliberately” is a reasonable habit. git fetch is one of the few git commands that cannot put your repository into a state you have to get out of.

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).