Git Rebase vs Merge: When to Use Which
git merge and git rebase both get your feature branch's changes onto main, and that surface-level similarity is exactly why people mix them up and end up with either a tangled history or a rewritten one they didn't mean to rewrite. They solve the same problem in fundamentally different ways, and picking the wrong one for the situation is how you get force-push disasters or unreadable commit graphs.
What each one actually does
Say main has moved on since you branched, and your feature branch has its own commits:
A---B---C feature
/
D---E---F---G main
Merge creates a new commit that ties the two histories together, with two parents:
git checkout feature
git merge main
A---B---C-------H feature
/ /
D---E---F---G-------- main
Nothing about A, B, or C changes. H is a new merge commit recording "these two lines of history are now joined." The full history — including the fact that you branched off before F and G landed — stays intact.
Rebase takes your commits and replays them, one by one, on top of the new base:
git checkout feature
git rebase main
D---E---F---G---A'---B'---C' feature
A', B', C' are new commits — same changes, same messages, but different parent, which means different commit hashes. The branch point where you diverged is gone; it now looks like you branched off G and wrote your three commits from there, in a straight line.
Why this distinction matters
The core trade-off: merge preserves what actually happened, rebase produces a cleaner-looking history that didn't literally happen that way. Both are useful — for different audiences.
- Merge is honest about the parallel work. If two people were both building on an old
mainand their branches diverged and reconverged, a merge commit records that truthfully. It's non-destructive — no existing commit is altered, so it's always safe on shared branches. - Rebase makes the history read as if it happened serially, which is easier to follow later —
git logshows one clean line instead of a lattice of merge commits — but it rewrites commit hashes, which is where the danger comes in.
The rule that actually matters: never rebase shared history
git checkout feature
git rebase main
git push --force-with-lease
If feature is a branch only you are working on, this is fine — you're rewriting commits nobody else has based work on. If feature (or worse, main) is a branch other people have already pulled and built on top of, rebasing it rewrites history out from under them: their local branch's history and the remote's diverge, and their next pull produces a confusing tangle of duplicate commits, or their local commits appear to vanish (they haven't — they're just no longer reachable from the branch they thought they were following).
The rule of thumb: rebase freely on branches only you push to, merge (or use a rebase-and-fast-forward via a PR tool) for anything already shared.
Interactive rebase: cleaning up before you share
The other common use of rebase isn't about a different base at all — it's cleaning up your own unpushed commits before opening a PR.
git rebase -i HEAD~4
This opens an editor listing your last four commits, letting you reorder them, squash "fix typo" and "actually fix typo" into the commit they belong to, or reword messages — all before anyone else has seen them. This is the safest, most common use of rebase in practice: turning a messy work-in-progress history into a small number of coherent commits, before it becomes shared history.
Merge conflicts feel different in each
With a merge, you resolve conflicts once, in the single merge commit. With a rebase, if multiple commits touch the same lines, you can hit the same conflict repeatedly — once per commit being replayed — which is more tedious but also more precise: you're resolving each historical change in the context it was actually made, not one giant diff at the end.
git rebase main
# CONFLICT in commit B
# ...resolve...
git add .
git rebase --continue
# CONFLICT in commit C, same lines
# ...resolve again...
git add .
git rebase --continue
If a rebase turns into a repeated-conflict slog, git rebase --abort gets you back to exactly where you started — worth remembering before you're three conflicts deep and unsure if you're making it worse.
A practical default
- Working on your own feature branch, want to catch up with
mainbefore continuing:rebase. - Cleaning up commits before opening a PR:
rebase -i. - Bringing a finished, reviewed feature branch into
main: whichever your team's convention is — both are defensible, and consistency matters more than the choice. - Anything already pushed and possibly pulled by someone else:
merge, or rebase only with explicit team agreement and a heads-up.
Takeaway
Merge tells the truth about parallel history at the cost of a more tangled graph; rebase produces a clean linear graph at the cost of rewriting commit identity. The line that actually matters isn't "which is better" — it's whether the commits you're about to rewrite are still only yours.