No Fast-Forward (--no-ff)
git merge --no-ff tells Git to always create a merge commit, even when a fast-forward would have been possible. The result: history clearly records that a branch was merged here, with one commit summarising the whole branch. Many teams (especially those following Gitflow) make this the default.
What --no-ff produces
Without --no-ff (fast-forward)
A───B───C───D───E ◀── main, feature-x (Linear — you cannot tell a branch ever existed)
With --no-ff (explicit merge commit)
A───B───C───────M ◀── main
\ /
D───E───┘ ◀── feature-x
(M is the merge commit — clearly marks where feature-x was integrated)The command
git switch main git merge --no-ff feature-x # (your editor opens for the merge commit message)
Why use --no-ff
Preserves the record of a branch.
git log --graphshows the diamond shape, so a year later you can still see “this was a feature branch.”Easy revert. Reverting one merge commit undoes the whole feature:
git revert -m 1 <merge-sha>.Workflow clarity. In Gitflow especially, the explicit merge commit is part of the model — branches into develop, develop into main, etc.
Audit trail. Code review, compliance, or change-management workflows often require a single trackable point of integration.
Why NOT use --no-ff
More noise in the log. Every merge adds a commit, regardless of how trivial.
git log --onelinebecomes harder to scan — half the lines are “Merge branch ...”.Teams that prefer truly linear history (often combined with rebase or squash) skip merge commits entirely.
Configuring --no-ff as default
Always create merge commits
# For 'git merge' git config --global merge.ff false # For 'git pull' (so it also creates merge commits when needed) git config --global pull.ff false
Only for some branches
Force --no-ff when merging into specific branches
# Inside the repo: git config branch.main.mergeOptions --no-ff git config branch.develop.mergeOptions --no-ff # Now any merge INTO main or develop forces --no-ff # Merges into feature branches still allow fast-forward
Writing the merge commit message
A useful merge commit
Merge branch 'feature/oauth-login' into main Add OAuth login via Google and GitHub. * New /api/auth/oauth routes * OAuth state stored in signed cookies * "Sign in with X" buttons on the login page * Updates LICENSE to credit oauth-passport Refs: PROJ-123
Git auto-fills “Merge branch '...' into ...” — but it’s worth taking ten seconds to add a real summary. The merge commit becomes a single-shot description of the feature.
The Gitflow context
Gitflow uses --no-ff everywhere:
Feature branches merge into
developwith--no-ff.Release branches merge into
main(and back intodevelop) with--no-ff.Hotfixes merge into both
mainanddevelopwith--no-ff.
The result: git log --graph --all shows a clear braided topology where every feature, release, and hotfix is visible.
Reverting a --no-ff merge
Undo a merge commit safely
# Find the merge commit git log --oneline --merges # Revert it (-m 1 keeps the first parent — usually main) git revert -m 1 <merge-sha> # Git creates a new commit that undoes the entire merge
--no-ff vs squash
--no-ff— keeps every commit on the branch + adds a merge commit. Full history preserved.--squash— collapses every commit into one. History compressed.Choose
--no-ffif you value detailed history and easy reverts. Choose--squashif you value clean log lines and brevity.
--no-ff is the safer choice for serious projects. You can always squash later (with git rebase -i), but reconstructing a lost branch history is much harder.