Rebase vs merge, decided in one sentence: use merge
when the branch is shared with anyone else, use rebase when it's
still yours alone, and stop treating this as a personality question. Both commands solve the same
problem — getting one branch's commits into another — and both are correct tools that produce
different, inspectable results: a different commit graph, a different diff
for your reviewer, a different trail for git blame and git bisect to walk
later. This guide is the comparison the other two aren't: it won't re-teach you the step-by-step
mechanics of either command — those guides already do that in full — it will tell you, plainly, which
one wins in which situation and why the wrong choice on a shared branch is the one Git mistake that
can't be quietly walked back.
If you need the mechanics — how to actually run git rebase main, resolve a rebase
conflict, or use git rebase --onto for a stacked branch — the
dedicated rebase guide covers it start to finish. If you need the merge equivalent — fetching,
checking out, merging, and reading a merge conflict — the
merge guide does the same. For the exhaustive flag-by-flag reference behind both, Git's own
git-rebase documentation
and git-merge
documentation are the authoritative source. This page assumes you can already run either command
and answers a narrower, more useful question: given a specific situation, which one should you
actually reach for?
The Fast Answer: Rebase vs Merge in One Table
Git merge vs git rebase comes down to one question: has anyone else already pulled this branch? If yes, merge. If no — it's still local, still unpushed, or pushed only to a pull request nobody else has based work on — rebase is safe and usually produces a cleaner result. That single check resolves most rebase vs merge arguments before they start.
- Use merge when bringing an already-shared branch (main, develop, a release branch) into your work, or when integrating a finished feature branch that others may have built on top of.
- Use rebase when cleaning up your own, still-private branch before opening a pull request, or when keeping a personal feature branch current with main as it moves.
- Use squash merge when you want one branch's entire history to land as a single, tidy commit on main regardless of how messy the branch itself was — most teams' actual default, covered in full further down.
Everything past this point is detail in service of that one decision: what each command actually
changes, what it costs you, and where rebasing vs merging git history quietly shows up later in
places you don't expect — a reviewer's screen, a git blame result, a bisect run six
months from now.
What Each Command Actually Does to Your Commit History
Strip away the tutorials and both commands answer the same question — "how do I get feature's commits into main?" — with genuinely different mechanics. Merge creates a new commit with two parents: the tip of the branch you're on, and the tip of the branch you're merging in. Every existing commit on both branches stays exactly as it was, hash and all; merge only ever adds, never rewrites. Rebase replays commits instead — it takes each commit unique to your branch, one at a time, and reapplies it on top of a new base, which means every replayed commit gets a brand-new hash even though its content is identical. Nothing is deleted in either case; the two operations just leave a differently shaped record of how the work happened.
# Merge: adds one new two-parent commit, rewrites nothing
git checkout feature
git merge main
# feature now has a merge commit; every prior commit hash is untouched
# Rebase: replays feature's commits onto main's tip, new hashes
git checkout feature
git rebase main
# feature's commits are gone by their old hashes, replaced by new ones with identical content That difference in mechanism is the entire reason a rebase and merge produce different histories from identical starting points and identical final code. The files on disk can end up byte-for-byte identical either way — the git rebase vs merge choice doesn't change what the code looks like at the tip of the branch, only the paper trail describing how it got there.
The Core Difference Between Merge and Rebase: History Shape
The difference between merge and rebase, reduced to a shape, is a diamond versus a straight line. A repository built entirely on merges looks like a lattice when you graph it — branches split off, wander independently, and rejoin at merge commits, so the graph visibly shows every place two lines of work met. A repository built on rebasing looks like a single straight rope: every commit sits directly on top of the one before it, in the order it was replayed, with no merge commits marking where a branch used to exist at all.
Neither shape is objectively better — they optimize for different things. The lattice preserves an
honest record of when parallel work actually happened, which is valuable for audits, for
understanding what shipped together, and for a compliance reviewer asking "what was on main on this
exact date." The rope optimizes for readability after the fact: git log on a rebased
history reads like a clean, ordered story with no branching noise, which is exactly why so many teams
rebase feature branches before merging them — they want the honesty of merge for the integration point
and the readability of rebase for everything leading up to it.
This is also where "rebase and merge" as a phrase stops being contradictory and starts making sense: most real workflows use both, in sequence, rather than picking one forever. Rebase your own branch to clean it up, then merge — or squash-merge — the result into main. The two commands aren't rivals so much as two stages of the same pipeline, and the argument over rebase vs merge is really an argument about where to draw the line between "still mine to rewrite" and "now everyone's, and frozen."
Rebase vs Merge: The Full Comparison Table
Git merge vs git rebase stops being a two-way comparison the moment you open a hosting platform's merge dialog, because squash merge is right there beside the other two — and it behaves differently enough from a plain merge to deserve its own column here rather than a footnote.
| Dimension | Merge | Rebase | Squash Merge |
|---|---|---|---|
| Rewrites existing commits? | Never | Yes — every replayed commit gets a new hash | No, but originals are collapsed into one new commit |
| History shape | Branching, with merge commits marking join points | Linear — looks like one continuous line of work | Linear — one commit per feature on main |
| Safe on a shared branch? | Always | No — only on branches nobody else has pulled | Yes, applied at the integration point, not to shared history |
| Individual commit detail preserved? | Fully, plus a merge commit | Fully, with new hashes, no merge commit | Lost — collapsed into a single commit message |
| Conflict resolution style | One resolution pass covering everything at once | Potentially one pass per replayed commit | Same as merge — one pass, applied at squash time |
Effect on git blame | Blame still points at the original author and date | Blame points at the rebased commit's new hash and date | Blame points at whoever performed the squash, on the squash date |
Effect on git bisect | May land on a broken intermediate commit mid-branch | Each replayed commit should build and pass on its own | Bisect lands on whole-feature commits, never mid-feature |
| Best for | Integrating shared branches (main, develop, release branches) | Cleaning up your own branch before it's reviewed or merged | Landing a feature as one tidy unit, regardless of branch mess |
Read that table by row, not by column, and the pattern holds: rebase and squash merge both buy
cleaner history at the cost of some detail, while a plain merge buys a fully honest record at the cost
of extra noise in git log. None of the three is strictly better across every row — which
is the entire reason this remains a real decision instead of a settled default.
What Your Pull Request Reviewer Actually Sees
Almost nothing written about rebase vs merge covers what actually changes for the person reviewing your pull request — and it's the part that affects them daily, whether or not they ever type either command themselves. A merged branch shows up in a PR with every commit still intact, in its original order, each with its original author and timestamp — including the "fix typo," "wip," and "actually fix the thing this time" commits you made along the way, all visible in the PR's commit list even if the platform collapses the diff view into one unified comparison.
A rebased branch shows a different picture depending on when the rebase happened. Rebase before opening the PR, and the reviewer sees a clean, linear commit list from the start — no merge commits, no "merge main into feature" noise, just your work in order. Rebase during review, after comments have already landed on specific lines, and most platforms will re-anchor or drop those inline comments, because the commit hashes those comments were attached to no longer exist. That's a real, practical cost of rebasing mid-review that a "rebase is always cleaner" take skips entirely — clean history and preserved review context are sometimes in direct tension.
The practical rule that falls out of this: rebase before you open the pull request, not after comments start arriving. If a reviewer has already requested changes and you need to update main's latest into your branch mid-review, merge is usually the better move specifically because it won't disturb the comment thread anyone has already invested time in — a case where "rebase is always cleaner" is directly wrong for the audience that has to live with the result.
Conflicts: One Big Resolution vs Commit by Commit
Merge conflicts and rebase conflicts are resolved with the same conflict markers and the same instincts, but they arrive on a fundamentally different schedule. A merge presents every conflict at once, in a single pass — Git compares the two branch tips, flags every file where both sides touched the same lines, and waits for you to resolve the whole batch before you can commit. One resolution session, however many files are involved.
A rebase can conflict repeatedly, once per replayed commit. Because rebase reapplies
commits one at a time, it's entirely possible to resolve a conflict on commit three of seven, run
git rebase --continue, and immediately hit a second, unrelated conflict on commit five —
each one scoped to that specific commit's changes rather than the branch's total diff. That can feel
slower for a long-lived branch with many commits, but it also isolates each conflict to a smaller,
more understandable unit of change, which is frequently easier to reason about than one large
merge conflict spanning a branch's entire divergence.
One detail trips up almost everyone who's only ever resolved merge conflicts: rebase inverts which
side is "ours" and which is "theirs." In a merge, "ours" is your current branch and "theirs" is the
branch coming in. In a rebase, Git is technically replaying your commits onto the target, so "ours" is
the branch you're rebasing onto and "theirs" is the commit currently being replayed — backward
from what merge conflict habits teach you to expect, and worth double-checking with
git status rather than assuming. If a rebase turns into more conflict than it's worth,
git rebase --abort reverts everything to before you started, no different in spirit from
how aborting a merge undoes an in-progress merge cleanly.
The Golden Rule: Never Rebase a Shared Branch
Never rebase a branch that anyone else has already pulled. Every other git rebase vs merge trade-off in this guide is a judgement call; this one isn't, and it's the answer to almost every "is rebase dangerous" question that actually deserves a firm yes. Atlassian's merging vs rebasing tutorial states the same thing as the golden rule of rebasing, and it's the one point every credible guide on the subject agrees on.
Here's precisely why. Rebasing gives every replayed commit a new hash. If a teammate already pulled
the old commits before you rebased, their local branch and your newly rebased branch now share no
common history for those commits at all — as far as Git is concerned, they're unrelated commits that
happen to contain similar changes. Pushing your rebased branch gets rejected as a non-fast-forward
push; forcing it through with git push --force overwrites the shared branch's history on
the remote, and the next time your teammate pulls, their local commits look like they diverged from a
history that no longer exists upstream. Their unpushed work isn't gone — it typically survives in
their local git reflog — but reconciling it is a genuinely painful, manual, and avoidable
mess that a plain merge would never have caused.
The safer variant, git push --force-with-lease, at least refuses to overwrite the remote
if someone else has pushed to it since you last fetched — worth using by default over a bare
--force even on branches you're confident are still yours alone. But the real fix is
upstream of that flag entirely: know whether a branch is shared before you touch its history. If it's
only ever lived on your machine, or it's a pull request branch you're certain nobody has based work
on top of, rebase freely. If it's main, develop, a release branch, or
anything a second person might have already fetched, merge — and if you need to walk back a change
that's already landed there, revert the merge commit instead
of trying to rewrite history that isn't yours alone to rewrite.
Squash Merge: The Third Option in Every Merge Button
Every major hosting platform's merge button actually offers three choices, and the third one is the one most rebase-vs-merge writeups mention last, if at all, despite being the default plenty of teams actually pick: squash merge. It takes every commit on a branch — however many, however messy — and collapses them into exactly one new commit on the target branch, with one message you write at merge time.
Squash merge borrows something from each of the other two options. Like rebase, it produces a clean,
linear result on the target branch — no merge commit, no branching noise, just one entry in
git log per feature. Like merge, it doesn't touch the source branch's own commit history
at all until the branch is deleted, so it's just as safe to run against a shared branch as a normal
merge is. The trade-off is what it discards: every individual commit message, timestamp, and author
detail from the branch collapses into that single new commit, which is exactly why a branch with
commits worth preserving individually — say, a sequence someone might want to cherry-pick
piece by piece later — is a poor candidate for squashing.
If you already know you want one clean commit and you're doing the cleanup yourself before merging, rather than relying on the platform's button, the dedicated guide to squashing commits with interactive rebase covers exactly that — the same collapsing effect, performed manually, with full control over the resulting commit message before it ever reaches a PR.
How Each Choice Affects git blame, git bisect and git log
Rebase vs merge stops being an abstract style preference the first time someone runs
git blame on a line you touched, or git bisect lands on a commit from your
branch while hunting a regression. Both tools read the exact history your integration choice created,
which means the choice you made weeks or months ago is still shaping someone else's debugging session
today.
git blame attributes a line to whichever commit last touched it, using that commit's
recorded author and date. After a plain merge, blame still points at your original commit — same
author, same original timestamp, exactly as it was written. After a rebase, blame points at the
replayed commit's new hash; the author is preserved, but the commit date generally reflects when the
rebase happened, not when the change was originally written, which can make a line look more recently
touched than it really was. After a squash merge, blame collapses everything down to whoever performed
the squash and whatever date that landed — the most information loss of the three, by a wide margin,
for anyone trying to trace a specific line back to its original author and reasoning.
git bisect cares less about who touched a line and more about whether every commit it
might land on actually builds and passes tests on its own. A merge-heavy history can leave bisect
stuck on an intermediate commit from deep inside someone's feature branch — one that never had to
build cleanly by itself, because it was always going to be combined with the rest of the branch before
anyone ran it standalone. A rebased or squash-merged history tends to bisect more cleanly for exactly
the opposite reason: rebase encourages each replayed commit to stand on its own, and squash guarantees
bisect only ever lands on a complete, self-contained feature, never a half-finished intermediate step.
None of this makes merge "wrong" for archaeology or rebase "wrong" for bisecting — it means the choice
has a second audience beyond the person making it: everyone who runs git log --grep,
git blame, or git bisect against that history afterward, sometimes years
later, with no memory of which integration strategy produced it.
Merge vs Rebase on GitHub, GitLab and Bitbucket
Merge vs rebase GitHub, in practice, is a per-repository setting rather than a universal default. GitHub's pull request merge button offers three explicit options — "Create a merge commit," "Squash and merge," and "Rebase and merge" — and a repository admin can disable any of them under branch settings, which is how plenty of teams enforce "always squash" or "never rebase-merge" without relying on every contributor remembering the policy. A merge vs rebase GitHub debate therefore tends to end on the repository settings page rather than in a chat thread — whoever configures the options decides what every other contributor ever sees.
GitLab's merge request widget offers merge commit and fast-forward merge as the main methods, with squash available as a checkbox option on merge commits — different in structure from GitHub's three distinct buttons. Bitbucket's pull request merge dialog presents its own three-way choice — merge commit, squash, or fast-forward — with the specific option set configurable per repository by an admin, same as the other two platforms.
The practical upshot across all three: "rebase and merge" as a button label does something slightly
different from running git rebase yourself on the command line, even though it uses the
same underlying mechanism. The platform rebases your PR's commits onto the target branch's current tip
and fast-forwards the target to include them — no merge commit gets created, and the commits keep
their individual messages, but you never see or resolve the rebase locally unless a conflict forces
the platform to ask you to. Functionally it's the same linear-history outcome as a manual rebase
followed by a fast-forward merge; the platform just does both steps for you behind one button.
Choosing a Team Policy (and Writing It Down)
Git rebase vs merge stops causing recurring arguments the moment a team picks a default and writes it down somewhere everyone can find it — a CONTRIBUTING file, a repository's branch protection settings, or a pinned message in the team's chat. The specific choice matters less than the fact that it's explicit and enforced, ideally by disabling the non-default options in the platform's branch settings rather than trusting everyone to remember a written rule under deadline pressure.
A policy that works for most small-to-mid teams, as a starting point rather than a mandate: rebase your own feature branch against main freely, as often as you like, right up until you open the pull request. Once it's open and under review, stop rewriting it — merge main into it if it needs updating mid-review. At integration time, squash-merge by default, reserving a plain merge commit for the rare case where a branch's individual commits are genuinely worth preserving on main — a multi-step migration, for instance, where each commit is independently meaningful and someone may need to cherry-pick just one of them later.
Whatever the policy, put a name on the exception path too: what happens when someone rebases a shared
branch by accident, or a squash swallows commits someone actually needed individually. Knowing that
resetting to a known-good commit or recovering through
git reflog are the standard fallback moves — decided in advance, not improvised during an
incident — turns a rare mistake into a five-minute fix instead of a stressful afternoon.
Five Myths About Rebasing vs Merging
Rebasing vs merging Git branches attracts more confidently wrong advice than almost any other topic in version control, usually because each side of the argument overstates its case. Five claims worth retiring:
- "Rebase loses commits." It doesn't — it rewrites their hashes, but the content
survives, and the pre-rebase state remains recoverable through
git reflogfor as long as Git's default retention window holds, typically 30 to 90 days locally. Nothing is deleted; the risk is entirely about shared copies diverging, not content vanishing. If you've ever needed to fully remove a commit on purpose, that's a distinct, deliberate operation — not a side effect of a routine rebase. - "Rebase is always cleaner, so always rebase." Cleaner history and honest history are different goals, and rebase optimizes for the first at the cost of the second. A shared branch rebased mid-review can also orphan a reviewer's inline comments, as covered above — "cleaner" isn't free.
- "Merge is always safer, so always merge." Merge is always safe with respect to
rewriting history, but a repository built entirely on merges accumulates graph noise that makes
git logharder to read and can leavegit bisectstuck on intermediate commits that were never meant to stand alone, as covered earlier. - "Force-pushing after a rebase is inherently dangerous." Force-pushing your own,
unshared branch is routine and low-risk — it's force-pushing a branch someone else has already
pulled that causes real damage.
--force-with-leasespecifically guards against the shared case by refusing to overwrite unexpected remote changes. - "Pick one and use it for everything." The comparison table earlier in this guide exists precisely because no single choice wins on every dimension — shared-branch safety, history readability, review continuity, and blame accuracy each favor a different option depending on the situation.
If any of these come up while you're actively untangling a mistake rather than debating theory, undoing a recent commit and amending one in place are usually faster, lower-risk fixes than reaching straight for a rebase or a force-push.
Reviewing the Diff Before You Integrate
Whichever side of rebase vs merge you land on for a given branch, the step that actually catches
mistakes is the same either way: look at the diff before it lands. git diff main...feature
(three dots, comparing against the point where the branches diverged) shows exactly what a merge or a
rebase-and-fast-forward would introduce, without touching anything — a habit worth building regardless
of which integration method a team has standardized on.
Reading that output in a terminal, in raw +/- unified-diff form, works, but
it puts real weight on the reader to reconstruct the change mentally — more so once a rebase has
touched several commits' worth of small formatting or reordering noise alongside the actual logic
change. Pasting the same before-and-after code into a
side-by-side comparison instead lines the two versions up in aligned panes, with every addition
and removal highlighted in place — genuinely faster to scan than a raw patch once a change runs more
than a few lines, and it works identically regardless of whether the code came from a merge, a
rebase, or two versions of a file you're not even tracking in Git at all.
That last point matters more than it sounds: Diff Checker, this site's free extension, isn't a Git
client and doesn't touch branches or commits — it's a second, human-readable opinion on any diff, run
entirely in your browser. Paste a file's before version into one pane and its after version — pulled
from either side of a rebase, either side of a merge, or just two drafts you're comparing by hand —
into the other, and it lines them up side by side (or in a unified view, your choice) with syntax
highlighting across 17 languages, a Smart compare mode that ignores noise like reordered object keys,
and a "Show Diff Only" toggle that collapses everything except the lines that actually changed. For a
rebased branch specifically, where a raw git diff can be muddied by several small commits'
worth of changes layered on top of each other, seeing the true before-and-after side by side is often
the fastest way to confirm nothing unexpected slipped in before you merge.
None of that replaces Git's own tooling — git diff, git log -p, and a
platform's PR diff view remain the source of truth for what actually changed and why. It's a
complement for the moment a diff needs a second, calmer read: before a risky merge, before approving
someone else's rebase, or when explaining a change to someone who doesn't read patch syntax fluently
by habit. If you want to get comfortable with branches and commits in a low-stakes setting first, a
hands-on Git playground lets you practice both merging and
rebasing against a repository nobody actually depends on.
Frequently Asked Questions
What is the actual difference between merge and rebase?
Merge combines two branches by creating one new commit with two parents, leaving every existing commit's hash untouched. Rebase replays your branch's commits one at a time onto a new base commit, giving each replayed commit a brand-new hash even though its content is unchanged. The files at the tip of the branch can end up identical either way — the difference is entirely in the shape and honesty of the resulting history, not in the final code.
Should I use rebase or merge for my feature branch?
Rebase your own feature branch freely while it's still private and nobody else has pulled it — it keeps history linear and easy to read. Once the branch is shared, under review, or something anyone else has already fetched, switch to merge, since merge never rewrites commits and can't cause the diverged-history problem a rebase creates on shared work.
Is rebase or merge better for GitHub pull requests?
Rebase your branch before opening the pull request for a clean commit list from the start. Once the PR is open and review comments exist, prefer merging main into it instead of rebasing again — a mid-review rebase can orphan comments anchored to commit hashes that no longer exist after the rebase. At merge time, most teams default to squash merge, which discards individual commit messages but keeps main's history as one tidy commit per feature.
Does rebasing lose commits?
No — rebasing gives commits new hashes but doesn't delete their content, and the pre-rebase state
typically remains recoverable through git reflog for weeks. The real risk with
rebasing isn't lost content; it's that a rebased branch's new commit hashes no longer match a
teammate's copy of the old ones, which causes a painful reconciliation if that branch was already
shared.
Why is rebasing a shared branch considered dangerous?
Because rebasing rewrites every replayed commit's hash. If someone else already pulled the original commits, their copy and your rebased copy no longer share history for those commits at all. Pushing the rebased branch requires a force-push that overwrites the remote's history, and anyone who already had the old commits ends up with a diverged branch that takes manual work to reconcile — avoidable entirely by merging instead on any branch other people have already fetched.
What is squash merge, and how is it different from rebase?
Squash merge collapses every commit on a branch into exactly one new commit on the target branch, written at merge time — unlike rebase, which preserves each individual commit but gives it a new hash. Squash produces the same linear, noise-free result on the target branch as a rebase-and-merge would, but at the cost of losing individual commit messages and timestamps from the original branch, which matters if any of those commits were worth preserving on their own.