By admin , 29 April 2026

Three strategies, three histories

When integrating a feature branch into main, you have three options. Each produces a different shape of history; each suits different teams.

Merge commit

git checkout main
git merge --no-ff feature/login

Result: a single merge commit on main with the feature branch's commits as its second parent. The feature is visible as a unit; bisect and revert can target the merge.

Pros: preserves the truth of how development happened; reverting the feature is one command.

Cons: non-linear history; git log needs --graph to make sense.

Squash and merge

git checkout main
git merge --squash feature/login
git commit -m "Add OAuth login"

Result: a single new commit on main containing all the feature's changes. The feature branch's individual commits are discarded from main's history.

Pros: linear history; one commit per feature simplifies git log; perfect for teams whose intermediate commits are messy.

Cons: loses commit-level detail; git blame points at the squash commit, not the original author of a line.

Rebase and merge

git checkout feature/login
git rebase main
git checkout main
git merge --ff-only feature/login

Result: feature commits replayed on top of main, no merge commit. Linear history with full commit-level detail.

Pros: linear; preserves atomic commits; bisect-friendly.

Cons: requires discipline to keep feature commits clean; loses the "branch" boundary.

Decision matrix

  • Library or kernel-style project - rebase and merge. Atomic commits are valuable archaeology.
  • App with disciplined contributors - rebase and merge.
  • App with mixed disciplines - squash. One PR = one commit makes review and revert simple.
  • Project where parallel branches matter (e.g. Git Flow) - merge commits, with --no-ff.

Configuring on the host

GitHub, GitLab, and Bitbucket let you allow or disallow each strategy at the repo or branch level. Pick one default; allow others as exceptions.

gh api -X PATCH repos/:owner/:repo \
  -F allow_merge_commit=false \
  -F allow_squash_merge=true \
  -F allow_rebase_merge=true

Local equivalents

# Merge commit
git merge --no-ff feature

# Squash
git merge --squash feature && git commit

# Rebase and FF
git rebase main feature
git checkout main
git merge --ff-only feature

The cultural angle

Pick one default and stick with it. Mixing strategies on one project produces a confusing history. The "right" choice is the one your team actually follows; consistency beats theoretical correctness.

Reverting

  • Merge commit: git revert -m 1 <merge-sha> reverts the merge.
  • Squash: git revert <squash-sha> reverts the feature.
  • Rebase: git revert <sha1> <sha2>... for each commit.

Squash is easiest to revert; merge commits are second; rebased commits require enumerating each one.