Fetching first lets you see what changed before it lands in your work.
`git fetch` contacts a remote, downloads any commits you don't have, and updates the remote-tracking branches (origin/main, origin/feature-x...). It never changes your local branches or working tree, so it is always safe to run.
`git pull` is git fetch followed by integrating the upstream of your current branch into it, by merge or rebase depending on pull.rebase or the flags --rebase / --no-rebase / --ff-only.
Fetching separately gives you a chance to look before you leap:
git fetch
git log --oneline main..origin/main # what's new upstream
git diff main...origin/main # what they changedThen integrate deliberately: git merge origin/main, git rebase origin/main, or git merge --ff-only origin/main if you have no local commits.
Some options worth knowing:
git fetch --allfetches every remote.git fetch --prune(orfetch.prune truein config) deletes remote-tracking branches whose branch was deleted on the server, sogit branch -rdoesn't fill up with ghosts.git pull --ff-onlyis a safe default for a branch you never commit to directly, such as a localmain: it updates if possible and refuses otherwise.
A good mental model: fetch updates your knowledge of the remote; merge or rebase changes your branch. git pull simply does both at once, which is convenient when you already know what to expect.
Example
git fetch --prune origin
git status
# Your branch is behind 'origin/main' by 3 commits, and can be fast-forwarded.
git log --oneline HEAD..origin/main # the 3 incoming commits
git merge --ff-only origin/main # same result as git pull --ff-only
git config --global fetch.prune true
git pull --rebase # fetch + rebase in one stepRemember
- fetch only updates origin/* refs; it never touches your work
- pull = fetch + merge/rebase into the current branch
- Fetch, inspect with main..origin/main, then integrate on purpose
- fetch.prune cleans up remote branches deleted on the server