Fast-Forward: When It's Trivially Easy

Predict before you run it. Then force the opposite outcome on purpose, and see exactly why teams do that.

feature/oven-temps was branched off master's current tip, and master has not received a single new commit since. Before merging, the real, answerable question is: will this be a fast-forward, or will git create a real merge commit?\n\nThis lesson proves the prediction, then deliberately does the opposite on an equivalent second branch — forcing a real merge commit with --no-ff even though a fast-forward was just as possible. The same real starting condition, two genuinely different real outcomes, entirely by choice.

Predicting Before Running Anything

git log --oneline master..feature/oven-temps
git log --oneline feature/oven-temps..master

Real commits ahead on the branch, genuinely nothing on master's side — the real precondition for a fast-forward.

Verifying the Real Fast-Forward

git switch master
git merge feature/oven-temps

git's own real output says "Fast-forward" — the branch pointer simply advances, since there was nothing to combine.

Forcing the Opposite, On Purpose

git merge --no-ff feature/fridge-organization -m "Merge pull request: fridge organization tips"

The exact same real starting condition (a branch directly ahead) — but --no-ff genuinely forces a merge commit anyway, "Merge made by the 'ort' strategy" appearing even though nothing required it.

Why Force It

A fast-forward leaves no real trace that a feature branch ever existed — its commits blend invisibly into master's own line. A forced merge commit is a real, permanent, searchable marker: "this exact feature was integrated here," useful for tooling, changelogs, and anyone reading history later who wants to know what shipped together.

Fast-forward merge

When the target branch has made no commits since the source branch diverged, git can genuinely just move the target's pointer forward — no new commit, no combining, since there is nothing to combine. Confirmed directly in this lab: "Updating da919ea..c926ae3 / Fast-forward" with no merge commit created.

git merge --no-ff

Forces a real merge commit even when a fast-forward is technically possible. Confirmed directly in this lab on an equivalent starting condition to the fast-forward above: "Merge made by the 'ort' strategy" appeared instead, by deliberate choice.

🔮 Predict, Then Verify the Fast-Forward

Check both directions of divergence, predict the outcome, then merge and confirm.

cd ~/team-recipes && git switch -c feature/oven-temps
cd ~/team-recipes && printf '\nOven Preheat Times: allow 15 minutes for a full preheat.\n' >> pantry-checklist.md
cd ~/team-recipes && git add pantry-checklist.md
cd ~/team-recipes && git commit -m 'Add oven preheat guidance to pantry checklist'
cd ~/team-recipes && printf '\nAlways preheat before baking cookies or bread.\n' >> pantry-checklist.md
cd ~/team-recipes && git add pantry-checklist.md
cd ~/team-recipes && git commit -m 'Note preheat importance for baked goods'
cd ~/team-recipes && git log --oneline master..feature/oven-temps
cd ~/team-recipes && git log --oneline feature/oven-temps..master
cd ~/team-recipes && git switch master
cd ~/team-recipes && git merge feature/oven-temps

student@lab:~$ cd ~/team-recipes && git switch -c feature/oven-temps Switched to a new branch 'feature/oven-temps' student@lab:~$ cd ~/team-recipes && printf '\nOven Preheat Times: allow 15 minutes for a full preheat.\n' >> pantry-checklist.md student@lab:~$ cd ~/team-recipes && git add pantry-checklist.md student@lab:~$ cd ~/team-recipes && git commit -m 'Add oven preheat guidance to pantry checklist' [feature/oven-temps ad823e4] Add oven preheat guidance to pantry checklist 1 file changed, 2 insertions(+) student@lab:~$ cd ~/team-recipes && printf '\nAlways preheat before baking cookies or bread.\n' >> pantry-checklist.md student@lab:~$ cd ~/team-recipes && git add pantry-checklist.md student@lab:~$ cd ~/team-recipes && git commit -m 'Note preheat importance for baked goods' [feature/oven-temps c926ae3] Note preheat importance for baked goods 1 file changed, 2 insertions(+) student@lab:~$ cd ~/team-recipes && git log --oneline master..feature/oven-temps c926ae3 (HEAD -> feature/oven-temps) Note preheat importance for baked goods ad823e4 Add oven preheat guidance to pantry checklist student@lab:~$ cd ~/team-recipes && git log --oneline feature/oven-temps..master student@lab:~$ cd ~/team-recipes && git switch master Switched to branch 'master' Your branch is up to date with 'origin/master'. student@lab:~$ cd ~/team-recipes && git merge feature/oven-temps Updating da919ea..c926ae3 Fast-forward pantry-checklist.md | 4 ++++ 1 file changed, 4 insertions(+)

📤 Push and Clean Up

Send the fast-forwarded master, and remove the now-merged branch.

cd ~/team-recipes && git push
cd ~/team-recipes && git branch -d feature/oven-temps

student@lab:~$ cd ~/team-recipes && git push Enumerating objects: 8, done. Counting objects: 100% (8/8), done. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 699 bytes | 41.00 KiB/s, done. Total 6 (delta 3), reused 0 (delta 0), pack-reused 0 To ../recipes-origin.git da919ea..c926ae3 master -> master student@lab:~$ cd ~/team-recipes && git branch -d feature/oven-temps Deleted branch feature/oven-temps (was c926ae3).

⚡ Force the Opposite Outcome

On an equivalent new branch, force a real merge commit with --no-ff instead.

cd ~/team-recipes && git switch -c feature/fridge-organization
cd ~/team-recipes && printf '\nFridge Organization: dairy on top shelf, produce in the crisper.\n' >> pantry-checklist.md
cd ~/team-recipes && git add pantry-checklist.md
cd ~/team-recipes && git commit -m 'Add fridge organization tips'
cd ~/team-recipes && git switch master
cd ~/team-recipes && git merge --no-ff feature/fridge-organization -m 'Merge pull request: fridge organization tips'

student@lab:~$ cd ~/team-recipes && git switch -c feature/fridge-organization Switched to a new branch 'feature/fridge-organization' student@lab:~$ cd ~/team-recipes && printf '\nFridge Organization: dairy on top shelf, produce in the crisper.\n' >> pantry-checklist.md student@lab:~$ cd ~/team-recipes && git add pantry-checklist.md student@lab:~$ cd ~/team-recipes && git commit -m 'Add fridge organization tips' [feature/fridge-organization a262902] Add fridge organization tips 1 file changed, 2 insertions(+) student@lab:~$ cd ~/team-recipes && git switch master Switched to branch 'master' Your branch is up to date with 'origin/master'. student@lab:~$ cd ~/team-recipes && git merge --no-ff feature/fridge-organization -m 'Merge pull request: fridge organization tips' Merge made by the 'ort' strategy. pantry-checklist.md | 2 ++ 1 file changed, 2 insertions(+)

📊 Compare the Two Real Outcomes Directly

Push, clean up, and look at the graph — one integration invisible, one permanently marked.

cd ~/team-recipes && git push
cd ~/team-recipes && git branch -d feature/fridge-organization
cd ~/team-recipes && git log --oneline --graph -4

student@lab:~$ cd ~/team-recipes && git push Enumerating objects: 6, done. Counting objects: 100% (6/6), done. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 467 bytes | 77.00 KiB/s, done. Total 4 (delta 3), reused 0 (delta 0), pack-reused 0 To ../recipes-origin.git c926ae3..b6ac6f6 master -> master student@lab:~$ cd ~/team-recipes && git branch -d feature/fridge-organization Deleted branch feature/fridge-organization (was a262902). student@lab:~$ cd ~/team-recipes && git log --oneline --graph -4 * b6ac6f6 (HEAD -> master, origin/master, origin/HEAD) Merge pull request: fridge organization tips |\ | * a262902 Add fridge organization tips |/ * c926ae3 Note preheat importance for baked goods * ad823e4 Add oven preheat guidance to pantry checklist

Lab 2.3.1 complete. The exact same real starting condition, two deliberately different real outcomes:\n\n\n Fast-forward predicted and verified : ✅ confirmed\n --no-ff forced on an equivalent branch : ✅ real merge commit\n Both outcomes compared directly : ✅ in the real graph\n

Enable JavaScript to run the live terminal and track your progress.