Merge commit, squash or rebase: choosing a pull request merge strategy
Merge commits, squash merges and rebase merges all land the same code but leave very different history behind. This comparison explains what each method does on GitHub and in plain Git, what you lose with each, and how to pick one rule for your team.
Updated 8 min read

In this article
When a pull request is approved, someone presses a button, and the code lands on the main branch. Which button they press decides what your history will look like for years: whether every work-in-progress commit survives, whether the change can be reverted in one step, whether git bisect can find a regression cleanly and whether commit signatures still verify.
Most hosting services offer the same three choices. GitHub calls them Create a merge commit, Squash and merge and Rebase and merge; GitLab and others offer close equivalents. Teams often leave all three enabled and let each author choose, which produces a history that follows no rule at all.
This article compares the three methods using the GitHub documentation and the Git reference manual, lists the real trade-offs, and ends with a short decision guide you can adopt as a team rule. It does not argue that one method is always right; the Pro Git book itself says the question of merging versus rebasing "is not that simple".
What each method actually does
Merge commit
GitHub's default Merge pull request option adds all commits from the feature branch to the base branch inside a merge commit, and the documentation states that the pull request "is merged using the --no-ff option". In plain Git, that is:
git switch main
git merge --no-ff feature/invoice-exportThe base branch gains every original commit plus one merge commit with two parents. Nothing is rewritten, so commit hashes and signatures stay the same.
Squash and merge
With Squash and merge, the pull request's commits "are squashed into a single commit" and merged into the base branch using the fast-forward option. The feature branch's individual commits do not appear on main; one new commit containing the combined change does. Locally, the closest equivalent is:
git switch main
git merge --squash feature/invoice-export
git commit # write one message that describes the whole changeRebase and merge
Rebase and merge adds every commit from the topic branch onto the base branch individually, without a merge commit. GitHub notes that this "resembles a fast-forward merge by maintaining a linear project history", but it achieves this by creating new commits. The local equivalent is a rebase followed by a fast-forward:
git switch feature/invoice-export
git rebase main
git switch main
git merge --ff-only feature/invoice-export
Side-by-side comparison
| Question | Merge commit | Squash and merge | Rebase and merge |
|---|---|---|---|
| Commits added to main | All branch commits plus one merge commit | One new commit | All branch commits, recreated |
| History shape | Non-linear (merge commits with two parents) | Linear | Linear |
| Original commit hashes kept | Yes | No | No (GitHub always creates new SHAs) |
| Individual authorship and timing kept | Yes | No, per GitHub's documented disadvantages | Yes, as separate commits |
| Revert the whole pull request | Revert the merge commit (with -m 1) | Revert one commit | Revert each commit, or a range |
| Work-in-progress commits on main | Yes | No | Yes, unless the author cleans them up first |
| Commit signature verification on GitHub | Original signatures kept | New commit created by GitHub | Commits added without signature verification, per GitHub |
| Extra work for the author | None | None | May need to rebase locally, resolve conflicts and force-push |
Merge commits: complete history, noisier log
A merge commit records what actually happened: which commits were written, in what order, and when the branch was joined. Nothing is rewritten, so nobody who based work on the branch has to recover from changed hashes. The Pro Git book describes this view as treating history as "a record of what actually happened".
The cost is readability. Every small fix-up commit ("fix lint", "address review") lands on main, and the log becomes a graph rather than a line. Some of those commits may not even build, which can mislead a bisect session. Git has an answer for that: git bisect start --first-parent follows only the first parent of each merge, so a bisect over merge-based history identifies the merge that introduced a regression and ignores broken commits inside merged branches.
Choose merge commits when you need an audit trail of individual commits, when long-lived branches are merged into each other, or when commits are signed and you want those signatures preserved on the main branch.
Squash and merge: one pull request, one commit
Squashing produces the simplest history: each pull request becomes a single commit on main, which is easy to read, easy to revert and friendly to bisect because every commit on main passed review and CI as a whole. GitHub's documentation presents it as a way to get "a more streamlined Git history" without keeping work-in-progress commits.
GitHub also lists the disadvantages plainly, and they are worth reading before you enable it:
- You lose information about when specific changes were made and who authored the squashed commits.
- If you keep working on the same head branch after a squash merge and open a new pull request between the same branches, the previously squashed commits appear again and you may resolve the same conflicts repeatedly. The practical rule: delete the branch after a squash merge and start a new one.
- Commands that rely on the original commit IDs become less useful; GitHub gives
git rerereas an example.
Squash merging works best when pull requests are small and focused, which is good practice anyway. If a pull request contains several independent changes that deserve their own commits, squashing hides that structure; the better fix is usually to split the pull request.
Rebase and merge: linear history, rewritten commits
Rebasing keeps each commit while producing a straight line of history. When authors curate their commits (one logical change per commit, each one building and passing tests), this gives the most readable and bisect-friendly history of all three methods.
The trade-offs come from rewriting. GitHub documents that its Rebase and merge button behaves slightly differently from git rebase: it always updates committer information and creates new commit SHAs, and it drops commits that were empty to begin with. It also adds the commits to the base branch without commit signature verification, because GitHub cannot sign on the author's behalf; the documented workaround is to rebase and merge locally and push the result.
Rebasing can also require authors to resolve conflicts locally and force-push their branch. The Git manual for git push explains why a plain --force is risky: if someone else pushed to the branch while you were rebasing, a blind force push "will lose their work". Use --force-with-lease, which only updates the remote branch if it still points at the commit you expect:
git fetch origin
git rebase origin/main
git push --force-with-lease origin feature/invoice-exportThe golden rule from Pro Git still applies: "Do not rebase commits that exist outside your repository and that people may have based work on." Rebasing your own feature branch before merging is fine; rebasing a shared branch that others build on is not.
Helpful rebase options
git commit --fixup=<commit>followed bygit rebase -i --autosquash mainfolds review fixes into the commits they belong to, so the branch is clean before a rebase merge.git rebase --update-refsautomatically updates other branches that point at commits being rebased, which helps when you stack several dependent branches.git rebase --rebase-mergeskeeps the branching structure inside the rebased range instead of flattening it.
Settings that enforce the choice
A strategy only works if the repository enforces it. On GitHub, you can enforce one method by enabling only that method in the repository's pull request settings. Related controls interact with it:
- Require linear history on a protected branch prevents anyone from pushing merge commits to that branch, which rules out the merge-commit method.
- Rulesets can restrict merge methods per branch, but GitHub warns that if the repository's allowed methods conflict with the ruleset's method rule, merging becomes impossible. Keep the two in agreement.
- Merge queues take the choice away from the person merging: GitHub notes that the queue controls the merge method.
GitLab and other hosts offer equivalent project-level settings; check your host's documentation for the exact names.

A decision guide for teams
Use these questions in order and stop at the first "yes":
- Must original commits and signatures be preserved on the main branch (audit, compliance, signed releases)? Use merge commits, and teach people
git log --first-parentandgit bisect --first-parent. - Are pull requests small, and do you want one revertible commit per change with no history-curation work from authors? Use squash and merge, and turn on automatic deletion of head branches after merge (a repository setting on GitHub).
- Do authors already write clean, self-contained commits and want each one on main? Use rebase and merge, require
--force-with-leasefor force pushes, and decide how you will handle signed commits.
Whichever you pick, write it down in your contributing guide, enable only that method in the repository settings and make the pull request title or description the source for the final commit message, so the history on main explains why each change was made.
Summary
Merge commits keep everything and rewrite nothing, at the cost of a busier log. Squash and merge gives one tidy, revertible commit per pull request but discards individual commits and their authorship details. Rebase and merge gives a linear history with individual commits but rewrites them, which affects signatures and may require force-pushing. None of them is wrong. The mistake is leaving all three enabled without a rule. Pick one, enforce it in settings, and document it in one paragraph your team can find.
Frequently asked questions
Which merge method makes git bisect easiest?
Linear histories (squash or rebase) are simplest because every commit on the main branch was reviewed. With merge commits, run
git bisect start --first-parentso bisect only steps through merges on the main branch.Why does GitHub say rebased commits are not verified?
GitHub's Rebase and merge creates modified commits using the original data. It does not have the author's private signing key, so it cannot sign them. To keep signatures, rebase and merge locally and push the result.
Can I keep working on a branch after it was squash merged?
It is better not to. GitHub documents that a new pull request between the same branches will list the already-squashed commits again and may cause repeated conflicts. Start a new branch from the updated main branch instead.
Is --force-with-lease completely safe?
It is much safer than
--force: the push only succeeds if the remote branch still points at the commit your remote-tracking branch expects. Fetching right before the push without looking at the new commits can weaken that protection, so review what changed first.
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- About merge methods on GitHub — GitHub Docs
- git-rebase documentation — Git project
- git-merge documentation — Git project
- git-push documentation (--force-with-lease) — Git project
- Pro Git: Git Branching - Rebasing — Git project / Pro Git
- git-bisect documentation (--first-parent) — Git project
- git-revert documentation (--mainline) — Git project
- Managing the automatic deletion of branches — GitHub Docs
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
Related tools
- Directory profile
GitHub
Git-based platform for hosting code, reviewing pull requests, tracking issues and running CI/CD workflows, with free and per-user paid plans.
The article compares GitHub's three pull request merge methods.
- Directory profile
Git
Free, open-source distributed version control system for tracking code changes, branching and merging, created in 2005 and used by GitHub, GitLab and Bitbucket.
The trade-offs are explained with the Git reference manual and Pro Git.
- Directory profile
GitLab
DevSecOps platform combining Git repositories, merge request reviews, CI/CD pipelines and issue tracking, offered as SaaS, dedicated or self-managed.
The article notes GitLab's equivalent merge options.
More from Cloudsoft Zone

Resource guideWordPress
WordPress security hardening checklist for 2026 (WordPress 7.1)
A practical WordPress 7.1 hardening checklist based on the official handbook: updates, two-step logins, wp-config.php constants, file permissions, database privileges, firewalls, WP-CLI checksum checks and tested backups.
By Cloudsoft Zone

Troubleshooting guideCodeIgniter
CodeIgniter 4 validation not working? 9 causes and fixes
Fix CodeIgniter 4 validation problems fast: wrong input source, lost errors after redirect, permit_empty, strict JSON rules, is_unique placeholders and more.
By Cloudsoft Zone

Interview preparationInterview questions
Docker interview questions and answers for Docker Engine 29
18 Docker interview questions from images and volumes to build cache, Compose, networking, signals and the containerd image store, with short model answers.
By Cloudsoft Zone
