Fast-Forward vs No-FF Merges: Preserving Branch History in Git
Decide between a fast-forward merge and --no-ff based on whether you want to preserve a feature branch as a distinct unit in Git history. Includes worked commands and checks.
26 Jul 2026, 15:45 UTC

Decide whether a feature branch should leave a merge commit
When you integrate a feature branch into main, Git chooses between two behaviors. If main has not moved since you created the branch, Git can perform a fast-forward: it simply moves the main pointer forward to the feature branch’s latest commit. No merge commit is created, and the branch’s commits appear as if they were made directly on main. If main has new commits, Git creates a merge commit with two parents.
The practical decision is whether to force a merge commit even when a fast-forward is possible, using git merge --no-ff. Use fast-forward for short-lived branches that exist only to isolate work temporarily. Use --no-ff when you want the branch to remain visible as a distinct group of commits, which helps with release tracking and later reverts.
Terms used here
- Fast-forward: a merge that advances the branch pointer without creating a new commit.
- Merge commit: a commit with two or more parents, recording that two lines of development were joined.
- Diverged: two branches have new commits since their common ancestor, so neither pointer is an ancestor of the other.
Worked example: fast-forward merge
Run these commands in the repository root. You need write access to the local repository and, if pushing, to the remote branch.
git checkout main git pull origin main git checkout -b feature/login-form # Edit files and commit on the feature branch git add . git commit -m "Add login form" git checkout main git merge feature/login-form
Because main has no new commits since feature/login-form branched off, Git fast-forwards. The feature commits now sit directly on main, and feature/login-form can be deleted without leaving a trace of the branch structure.
Expected check
git log --oneline --graph --decorate -n 8
You should see a single linear chain with no merge commit. If you see a commit with two parents, a fast-forward did not occur.
Worked example: no-ff merge
To preserve the branch as a unit of work, force a merge commit even when a fast-forward is possible:
git checkout main git merge --no-ff feature/login-form
Git opens an editor for a merge commit message. After saving, the history shows a merge commit whose first parent is main and whose second parent is the feature branch tip. This makes it easy to see which commits belonged to feature/login-form.
Comparison
| Merge style | Merge commit created | Branch structure visible | Best for |
|---|---|---|---|
| Fast-forward | No | No | Short-lived, disposable branches |
--no-ff | Yes | Yes | Feature branches you may need to track or revert |
Limits and common mistakes
- Fast-forward cannot happen if the branches diverged. If
mainreceived commits while you worked on the feature branch, Git creates a merge commit regardless of whether you pass--no-ff. - Using
--no-fffor every tiny branch clutters history. Reserve it for branches that represent meaningful work. - Deleting a branch after fast-forward removes the only evidence it existed. If you later need to know which commits came from that branch, the information is gone unless you used tags or
--no-ff. - Do not force push a rewritten history to shared branches. If you reset or rebase after a fast-forward merge on
main, collaborators’ repositories will diverge.
Rollback and recovery
A merge changes repository state. If the merge is stopped because of conflicts, abort it before committing:
git merge --abort
If you already committed a fast-forward merge locally and want to undo it, ORIG_HEAD points to the previous main position:
git reset --keep ORIG_HEAD
Use --hard only if you are certain you want to discard uncommitted work. Never reset a shared branch that others have pulled; instead, revert the merge commit with git revert -m 1 <merge-commit>.
Practical check
After merging, inspect the graph:
git log --oneline --graph --decorate -n 10
Confirm whether the result matches your intent: a straight line for fast-forward, or a merge commit node for --no-ff. Also run git status to ensure the working tree is clean and no merge is still in progress.