Branches and merges
Fast-forward and merge commits
Recognize the two common results of merging a branch.
8 minute lesson
When you merge a branch, Git produces one of two results. Which one you get depends on a single question: did the receiving branch move since the other branch started? Once you can read both shapes in the history, merge output stops being mysterious.
Suppose add-search started at commit B and added commit C:
A---B main
\
C add-search
If main did not move, merging can fast-forward it. Git only moves the main reference to C:
A---B---C main, add-search
No merge commit is needed because C already contains B in its history. Nothing new is created; no files are combined. Git checks that B is an ancestor of C and slides the branch pointer forward. The message Fast-forward in the merge output tells you this is what happened.
Now imagine both branches moved:
A---B---D main
\
C add-search
Neither tip is an ancestor of the other, so a pointer move cannot represent both lines of work. A normal merge combines both histories in a new commit with two parents:
A---B---D---M main
\ /
C---' add-search
The merge commit M records that the histories joined. It does not mean a conflict occurred. Compatible changes merge automatically, and the output says something like Merge made by the 'ort' strategy.
You can also state your expectation up front. git merge --ff-only add-search refuses to run unless a fast-forward is possible, which is useful when you want history to stay a straight line. git merge --no-ff add-search forces a merge commit even when fast-forwarding was possible, so the branch remains visible as a grouped unit in history.
Use this read-only view to see which result happened:
git log --oneline --decorate --graph --all
A straight line means fast-forward. A diamond with a two-parent commit means a real merge.
Exercise: draw a graph where fast-forwarding is possible, then add one commit to main. Explain why the same merge now needs a merge commit.
Lesson completed