← Software Engineering Studio
Concept Explainer · Software Engineering

Git Rebase vs Merge

Both combine two branches. The difference is what the history looks like afterward — and whether you're allowed to rewrite it.

`git merge` and `git rebase` both integrate changes from one branch into another, and in most cases produce identical final file contents. What differs is the resulting commit graph. Merge adds a new "merge commit" that has two parents, faithfully recording that two branches diverged and were joined back together — the history is a true, if messier, record of what actually happened. Rebase instead takes your branch's commits and replays them one-by-one on top of the target branch's latest commit, producing a straight, linear history that looks as if you had started your branch from that latest commit all along. The commits themselves get new hashes because their parent has changed — this is the key fact that makes rebase dangerous on shared history.

git merge: preserves real history

merge commitmainfeature (own commits, untouched)
The feature branch's original commits are never rewritten. A new merge commit with two parents records that the branches diverged and rejoined — the graph shows exactly what happened, including when and how.

git rebase: rewrites history to look linear

one straight line — no merge commitreplayed with new hashes
Each original feature commit is re-applied one at a time on top of main's tip, generating brand-new commit hashes. The result looks exactly like the feature branch was created after main's latest commit, even though it wasn't.
Why this works

New hashes are the whole story.

A commit's hash in Git is derived partly from its parent commit's hash. When rebase changes a commit's parent (moving it to sit on top of main's latest commit instead of its original parent), the commit's own hash necessarily changes too — and every commit after it in the chain gets a new hash as a result. This is why rebasing is "rewriting history": the old commits still technically exist in Git's object database for a while, but your branch pointer now points at an entirely new set of commits with different identities. If anyone else already pulled the old commits, their local history and yours have now diverged in a way that's confusing to reconcile — this is the reason for the common rule "never rebase a branch other people have already pulled from."

Common misconception
"Rebase is just the modern, better way to merge."

Rebase isn't strictly better — it trades away information. A merge commit preserves the fact that work happened in parallel on a separate branch and exactly when it was integrated; a rebase erases that shape entirely, producing a history that reads as if everything happened in one sequential order, which wasn't literally true. For a personal feature branch you haven't shared yet, that trade is usually worth it — a clean, linear history is genuinely easier to read and bisect. But rebasing a branch that others have already pulled rewrites commits they already have, which can cause real confusion and duplicate work when they try to sync up. The safe rule most teams use: rebase your own local, unpublished commits freely; merge (or use a team-agreed pull strategy) once a branch is shared.

Git Rebase vs Merge — Concept Explainer

Explains the real difference between git merge and git rebase — a faithful two-parent merge commit versus a rewritten, linear history — using side-by-side commit graph diagrams, and covers when each is safe to use.

When to use merge

Use merge for integrating a shared or long-lived branch (like a feature branch others have pulled, or merging into main/develop) where preserving an accurate record of what happened and when matters, and where you can't guarantee no one else has already based work on the branch's current commits. Merge is also simply safer for beginners since it never rewrites existing commits.

When to use rebase

Use rebase to clean up your own local, not-yet-pushed commits before opening a pull request — squashing "fix typo" commits, reordering, or simply bringing your branch up to date with the latest main without a merge commit cluttering the log. Interactive rebase (`git rebase -i`) is also the standard tool for rewriting commit messages or combining commits before sharing work, since at that point nobody else has a copy of the old commits yet.

The cardinal rule

Never rebase commits that have already been pushed to a branch other people are using, unless the whole team has explicitly agreed to and coordinated around it. If you rebase a shared branch and force-push, anyone who already pulled the old commits will have a diverged, conflicting history with yours — resolving that mess is far more disruptive than the messier-but-honest history a merge would have produced.

Frequently asked questions

Does rebase lose any of my code changes?

No — rebase replays the same file changes from each commit, just with a new parent and new hash. The actual code content is preserved (unless a conflict arises during replay, which you resolve the same way as a merge conflict). What changes is the commit's identity/hash and its position in history, not its content.

What is "golden rule of rebasing"?

Never rebase a branch that other people have already pulled from or based their own work on. Rebasing rewrites commit history — if others already have the old commits, rebasing and force-pushing creates two divergent, conflicting histories for the same branch, which is confusing and error-prone to reconcile.

What does git rebase -i (interactive rebase) do differently?

Interactive rebase opens an editable list of the commits about to be replayed, letting you reorder them, squash multiple commits into one, edit individual commit messages, or drop commits entirely before they're replayed. It's the standard tool for tidying up a messy string of "wip" or "fix typo" commits into a clean, reviewable history before opening a pull request.

What is a "fast-forward merge" and how is it different from a normal merge?

A fast-forward merge happens when the target branch (e.g. main) hasn't moved since your feature branch diverged from it — Git can simply move main's pointer forward to your branch's latest commit with no merge commit needed, since there was nothing to reconcile. It only occurs when there's no divergent history to combine, which is functionally similar to what a rebase-then-merge sequence produces.

🎓

Try our Software Engineering & Cloud Studio

More calculators, simulators, and guides for this discipline.

Related tools & guides

How to Resolve a Git Merge ConflictHow to Undo the Last Git CommitSoftware Engineering Studio