Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
DevelopmentComparison

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.

Cloudsoft Zone

Updated 8 min read

Code editor window with a file tree, an open source file and terminal output below it
Photograph: Cycling2 / CC0, via Wikimedia Commons
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:

Shell
git switch main
git merge --no-ff feature/invoice-export

The 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:

Shell
git switch main
git merge --squash feature/invoice-export
git commit            # write one message that describes the whole change

Rebase 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:

Shell
git switch feature/invoice-export
git rebase main
git switch main
git merge --ff-only feature/invoice-export
Orange Git logo showing a branching diamond symbol next to the word git
Git provides merge, squash and rebase locally; hosting services expose the same choices as merge buttons.

Side-by-side comparison

QuestionMerge commitSquash and mergeRebase and merge
Commits added to mainAll branch commits plus one merge commitOne new commitAll branch commits, recreated
History shapeNon-linear (merge commits with two parents)LinearLinear
Original commit hashes keptYesNoNo (GitHub always creates new SHAs)
Individual authorship and timing keptYesNo, per GitHub's documented disadvantagesYes, as separate commits
Revert the whole pull requestRevert the merge commit (with -m 1)Revert one commitRevert each commit, or a range
Work-in-progress commits on mainYesNoYes, unless the author cleans them up first
Commit signature verification on GitHubOriginal signatures keptNew commit created by GitHubCommits added without signature verification, per GitHub
Extra work for the authorNoneNoneMay 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 rerere as 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:

Shell
git fetch origin
git rebase origin/main
git push --force-with-lease origin feature/invoice-export

The 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 by git rebase -i --autosquash main folds review fixes into the commits they belong to, so the branch is clean before a rebase merge.
  • git rebase --update-refs automatically updates other branches that point at commits being rebased, which helps when you stack several dependent branches.
  • git rebase --rebase-merges keeps 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.

Diagram of Git operations moving changes between working directory, index, local repository and remote
Squash and rebase both create new commits in your local repository before they are pushed.

A decision guide for teams

Use these questions in order and stop at the first "yes":

  1. Must original commits and signatures be preserved on the main branch (audit, compliance, signed releases)? Use merge commits, and teach people git log --first-parent and git bisect --first-parent.
  2. 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).
  3. Do authors already write clean, self-contained commits and want each one on main? Use rebase and merge, require --force-with-lease for 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-parent so 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.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. About merge methods on GitHub — GitHub Docs
  2. git-rebase documentation — Git project
  3. git-merge documentation — Git project
  4. git-push documentation (--force-with-lease) — Git project
  5. Pro Git: Git Branching - Rebasing — Git project / Pro Git
  6. git-bisect documentation (--first-parent) — Git project
  7. git-revert documentation (--mainline) — Git project
  8. 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.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.

  • Rows of server racks with cabling in a data centre aisle

    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