Understand git merge --no-ff

Create a merge commit when a fast-forward would otherwise be possible, preserve ancestry, and understand the limits of merge-based rollback.

Best practices
Illustration of Git branches and version history

git merge --no-ff requests a merge commit even when Git could move the current branch directly to the other branch's tip. It preserves an explicit integration point in the graph. If the other history is already included, Git can still report “Already up to date” without creating a redundant commit.

Compare the resulting histories

Suppose feature-x added C and D after main's B, while main itself did not advance. A normal fast-forward can produce:

A---B---C---D  main, feature-x

Merging with --no-ff instead records M:

A---B---------M  main
     \       /
      C-----D    feature-x
git switch main
git merge --no-ff feature-x

The feature commits remain in the ancestry. Deleting the feature branch afterward removes its ref name, not the commits reachable through the merge. The topology alone does not retain every historical branch label; the merge message can record useful context.

Why a team might use it

git log --first-parent main gives a view of main's integration path, including these merge points and any ordinary commits made directly there. A merge commit can correspond to one reviewed feature when the workflow consistently maintains that boundary; the flag does not guarantee one merge per feature or pull request.

To reverse a merge's change relative to its first parent, git revert -m 1 MERGE_COMMIT creates a new corrective commit. Confirm that parent one is the intended mainline and review conflicts. Reverting does not remove the merge's ancestry; merging the same branch again does not automatically reintroduce changes that the revert removed. Future integration may require reverting the revert or creating new changes.

Configure the policy at the right scope

# Default for merges in this repository:
git config --local merge.ff false
# Alternatively, set merge options for main:
git config --local branch.main.mergeOptions --no-ff

Choose one policy deliberately and inspect other merge/pull settings. --global broadens a default to other repositories; it is not needed merely to configure one project. Git Flow often uses explicit merge commits, while other workflows prefer different integration methods.

Compare related options

--ff-only refuses an integration that cannot fast-forward. It does not rebase commits for you. A normal merge with fast-forwards allowed creates a merge commit only when needed, subject to configuration and special cases such as some tag merges.

git merge --squash prepares the combined changes without recording merge ancestry or creating the final commit automatically. You commit the staged result yourself. That can simplify the visible mainline, but the original feature commits are not made ancestors by the squash commit.

To inspect a proposed non-fast-forward merge before committing, combine --no-ff --no-commit. --no-commit alone cannot pause a fast-forward because no merge commit is being created.

See git-merge, git-revert, and merge configuration.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting