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.