Fast-Forward Merge
A fast-forward merge is the simplest kind. Git just slides the branch pointer forward — no merge commit is created and history stays linear. Fast-forwards happen when the destination branch has not moved since you branched off, so Git has nothing to actually merge — it can just advance the pointer.
The setup
Before merge
A───B───C ◀── main (HEAD)
\
D───E ◀── feature-xmain is at C. feature-x is at E. Crucially, feature-x contains every commit on main plus two more (D, E).
The merge
git switch main git merge feature-x # Updating c204c1d..1f9ab2c # Fast-forward # src/login.js | 5 +++-- # 1 file changed, 3 insertions(+), 2 deletions(-)
The result
After merge
A───B───C───D───E ◀── main, feature-x
Both branches now point at E. The history is straight. There is no merge commit because Git did not have to create one — feature-x already contained main’s history in its entirety.
Why it’s called fast-forward
Think of main as a movie playing on a tape. To merge in feature-x Git just hits fast-forward on the tape until main is in the same place as feature-x. There is no editing, no combining, no new content — just advancing a pointer.
When fast-forward is possible
You branched off, made commits, and the destination branch did not move while you worked.
No one else committed to
mainsince you branched off.You never merged anything into
mainfrom another source.
Solo work and small teams often see lots of fast-forwards. Larger teams see fewer, because main moves more often.
When fast-forward is NOT possible
The destination moved while you worked
A───B───C───F ◀── main (HEAD)
\
D───E ◀── feature-xmain has commit F that feature-x does not. To combine them Git must create a merge commit (or you must rebase first). FF is impossible.
Refusing fast-forward (--no-ff)
Force a merge commit
git merge --no-ff feature-x
Result with --no-ff
A───B───C───────M ◀── main
\ /
D───E───┘ ◀── feature-xEven though FF was possible, Git created a merge commit. This is what Gitflow uses: history clearly shows “a feature branch was integrated here”.
Requiring fast-forward (--ff-only)
Refuse anything else
git merge --ff-only feature-x # If FF is not possible: # fatal: Not possible to fast-forward, aborting.
Useful for keeping history strictly linear. If FF fails, you rebase feature-x onto main first, then try again.
Setting a global default
Make 'git pull' refuse merges
git config --global pull.ff only # 'git pull' will only fast-forward; if it can't, it errors out # Forcing you to explicitly merge or rebase
FF vs FF-with-merge in pull requests
GitHub, GitLab, and Bitbucket all let you pick the merge method for PRs:
Create a merge commit — equivalent to
--no-ff. Linear-feature visible in history.Squash and merge — combines all PR commits into one, then FFs.
Rebase and merge — rebases PR commits onto target, then FFs. No merge commit, linear history.
Fast-forward — only allowed if no divergence. Some platforms expose this directly.
Pros and cons
✅ Linear history is easy to read with
git log.✅ No “noise” merge commits cluttering the log.
❌ You lose the record of which work was on a branch. If your team values that information, use
--no-ff.❌ Reverting a whole feature is slightly harder than reverting a single merge commit.
git log --oneline --graph --all to see the topology. If your branch already includes main, an FF will happen automatically.