Git Course 0%

fetch vs pull; origin/main vs main; divergence

Intermediate Core Git Git CLI ≈ 10 min

What you will learn

  • Why git status can say "up to date" when the remote has moved
  • The three states: ahead, behind, diverged, and what each means for your next command
  • When fetch is the better habit, and what pull --rebase changes

After this lesson you can

  • I never trust "up to date" without a fetch
  • I can read ahead, behind and diverged and choose the right next command

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

Terminal
$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

Every 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:

Terminal
$ git fetch
From https://gitlab.com/northwind-trails/trailguide
   6050e19..f75f2fe  main       -> origin/main

Now git status is answering about the current server.

The three states

Ahead, behind and diverged Three small graphs. Ahead: your main has two commits the remote does not, so a plain push works. Behind: the remote has two commits you do not, so a pull fast-forwards. Diverged: both sides have commits the other lacks, so a push is rejected and you must pull first, which produces either a merge commit or, with pull --rebase, a straight line. AHEAD you have 2 they lack origin/main main git push works, no questions BEHIND they have 2 you lack main origin/main git pull fast-forwards, no merge commit DIVERGED 1 each, different main origin/main push rejected git pull, then push git status names the state in words; git status -sb names it in numbers, for example [ahead 1, behind 3]. All three compare against the bookmark, so run git fetch first for an answer about the remote right now.
Ahead, behind and diverged: what each looks like and what to do next.

After fetching, your branch stands in one of three relationships to its bookmark. The short status names it in numbers:

Terminal
$ git status -sb
Your 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:

Output
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/main to 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:

Terminal
$ git pull
Merge 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:

Terminal
$ git pull --rebase
Successfully 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:

Terminal
$ git config --global pull.rebase true

Lesson 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 pull when you meant to inspect. Fetch, look, then decide.

Try it yourself

Goal: produce all three states and read each one.

  1. Make a commit on the website (edit docs/faq.md in the browser). Do not fetch yet, and run git status: it still says up to date.
  2. Run git fetch, then git status -sb. You are now behind.
  3. Make a local commit as well, then git status -sb. You are now diverged.
  4. Run git pull and read the output, then git status -sb again, 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.

Check yourself

1. git status says "up to date with 'origin/main'" but a colleague pushed an hour ago. Is Git wrong?
2. You are mid-edit and want to know whether anyone pushed. Which command?
3. ## main...origin/main [ahead 2, behind 1]. What is the situation?

Key terms

Fetch Pull Remote-tracking branch origin Merge Rebase