$ Git Game

Fast-forward, rebased, or merged: how a pull lands

A pull that brings something in lands in one of three ways, and Git names each. Which one you get depends on whether you had commits of your own, and on whether you asked for a rebase.

Fast-forward: you had nothing new

$ git pull
From ../origin
   ab5768d..7a20dd4  main       -> origin/main
Updating ab5768d..7a20dd4
Fast-forward
 api.py | 1 +
 1 file changed, 1 insertion(+)

The fetch reports the remote's move, old tip to new with two dots: the new tip follows from the old. Your main was at the old one with nothing of its own, so Git simply moves it there: Updating, Fast-forward, and the files that changed. No new commit is made, and your history is now exactly the remote's. It is the same move a push needs, seen from the other side (the non-fast-forward page).

Rebased: you had commits, and asked for a rebase

$ git pull --rebase
Successfully rebased and updated refs/heads/main.
$ git push
To ../origin.git
   7a20dd4..febe3f7  main -> main

You had a commit the remote didn't. --rebase takes it off, moves main to the remote's tip, and replays your commit on top: Successfully rebased and updated refs/heads/main. Your commit's hash changes, since it has a new parent, and the history stays one straight line. The push after it is a fast-forward for the remote. If the replay hits a line both commits changed, it stops on a conflict instead.

Merged: you had commits, and didn't ask

$ git pull --no-rebase
Merge made by the 'ort' strategy.
 api.py | 1 +
 1 file changed, 1 insertion(+)
$ git push
To ../origin.git
   7a20dd4..31ac6fe  main -> main

Without --rebase, Git keeps both histories and ties them with a merge commit: Merge made by the 'ort' strategy. (ort is the name of Git's merge engine.) Your commit keeps its hash, and the history forks and rejoins. The push sends two commits, yours and the merge. Which of the two to prefer is a team's policy; Git only insists that you choose, the first time the branches diverge, and remembers the choice in pull.rebase.

Why the game does this to you

The pack's pull is Git's. With nothing of yours it fast-forwards, even under --rebase, and the day log prints Fast-forward; with commits of yours it rebases or merges as the op says. In Git Game a plain pull costs one op and a rebase two, and a plain pull that merges something of yours takes a merge token, one of the game's three penalty tokens. A fast-forward takes none, because Git makes no merge commit.

Play a day where this happens

One tap starts a game against the bot, and the hub shows what your pull would do before you send it.

play the bot now

Output captured from Git 2.50.1 (Apple Git-155) by scripts/git-output.sh, in a scratch repository with two clones, ana's and bot's, and fixed dates, so the hashes are the same on every run. It is captured again at every Git release.