fetch vs pull; origin/main vs main; divergence
Intermediate Core Git Git CLI
Why this matters
git status says "Your branch is up to date with 'origin/main'" and five minutes later your push is refused. Both are correct, and the reason is the single most useful fact about remotes: origin/main is a bookmark, not a live view of the server.
The bookmark
$ git statusOn branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree cleanEvery word of that is about your own repository. origin/main is a remote tracking branch: a file in your .git recording where the remote's main was the last time you talked to the server. Nothing updates it in the background; only fetch, pull and push do.
So "up to date with origin/main" means "up to date with what I last saw". If you have not fetched since this morning, a colleague may have pushed ten commits and the sentence is still printed, word for word.
The fix is one command, and it costs nothing:
$ git fetchFrom https://gitlab.com/northwind-trails/trailguide
6050e19..f75f2fe main -> origin/mainNow git status is answering about the current server.
The three states
After fetching, your branch stands in one of three relationships to its bookmark. The short status names it in numbers:
$ git status -sbYour branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)| Output | State | Meaning | Next command |
|---|---|---|---|
## main...origin/main |
up to date | identical | nothing |
## main...origin/main [ahead 1] |
ahead | you have commits the remote lacks | git push |
## main...origin/main [behind 1] |
behind | the remote has commits you lack | git pull |
## main...origin/main [ahead 1, behind 3] |
diverged | both sides moved | pull, resolve, then push |
The long form says it in sentences:
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)"Can be fast-forwarded" in the behind case is the good news: your branch has nothing of its own, so pulling just moves it forward with no merge and no possible conflict.
fetch or pull?
Both bring the remote's commits into your repository. The difference is what happens next:
git fetch |
git pull |
|
|---|---|---|
Updates origin/main |
yes | yes |
| Updates your branch | no | yes |
| Changes your files | no | yes |
| Can produce a conflict | no | yes |
| Safe with uncommitted work | yes | refuses, to protect you |
Because fetch cannot disturb anything, it is safe to run at any moment, even in the middle of an edit. That makes it the better habit in two situations:
- You want to know, not to act. Fetch, then
git log --oneline main..origin/mainto read what is coming before accepting it. - You are mid-edit. Fetch tells you whether a colleague has touched the same area, without forcing you to stop.
git pull is the right command when you actually want the changes, which is most mornings.
What pull does with a diverged branch
By default, pull merges, and a diverged branch gets a merge commit:
$ git pullMerge made by the 'ort' strategy.
data/trails.csv | 1 +
docs/faq.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)With --rebase it replays your commits on top of the incoming ones instead, leaving a straight line:
$ git pull --rebaseSuccessfully rebased and updated refs/heads/main.The trade is the same one as lesson 6.7: a merge keeps the true shape and never rewrites anything; a rebase is tidier and rewrites your unpushed commits. Because pull --rebase only ever rewrites commits you have not pushed, it is safe, and many teams set it as the default:
$ git config --global pull.rebase trueLesson 3.5 set pull.rebase false as the beginner default, which is the simpler one to reason about. Switch when your team asks for a linear history.
Common mistakes
- Trusting "up to date" without fetching. The most common cause of a surprise rejected push.
- Pulling with uncommitted work. Git refuses and names the files. Commit or stash first.
- Reading "diverged" as an error. It is a description. Pull, then push.
- Fetching and expecting the files to change. That is exactly what fetch does not do; the point is that it is safe.
git pullwhen you meant to inspect. Fetch, look, then decide.
Try it yourself
Goal: produce all three states and read each one.
- Make a commit on the website (edit
docs/faq.mdin the browser). Do not fetch yet, and rungit status: it still says up to date. - Run
git fetch, thengit status -sb. You are now behind. - Make a local commit as well, then
git status -sb. You are now diverged. - Run
git pulland read the output, thengit status -sbagain, then push.
Expected result: step 1 prints "up to date" although the remote has moved; step 2 shows [behind 1]; step 3 shows [ahead 1, behind 1]; step 4 makes a merge commit (or rebases) and leaves you ahead by the merge, ready to push.
Show solution
Step 1 is the lesson in one screen: the sentence was true about the bookmark and false about the server. Everything else follows from that. If step 4 produces a conflict because you edited the same lines in the browser and locally, you have arrived early at Section 11; git merge --abort backs out.