Git Rebase vs Merge: What's the Difference and When to Use Each
Merge and rebase both bring changes from one branch into another, but they shape history differently. How each works, fast-forward and squash merges, interactive rebase, the golden rule of rebasing, resolving conflicts during a rebase, and a sensible team policy.
Your feature branch is behind main, and you need to bring in the latest changes — or you're ready to combine your work into main. Git offers two ways: merge and rebase. Both end up with the same code. They differ in the history they leave behind, and that difference matters for collaboration, debugging and safety.
(Branches new to you? Read git branches explained first.)
The setup
main: A---B---C
\
feature: D---E
You branched from B, made commits D and E. Meanwhile someone added C to main.
Merge: join the histories
git switch feature
git merge main
main: A---B---C
\ \
feature: D---E---M
Git creates a merge commit M with two parents, tying the histories together. Nothing that already existed is changed.
Pros: safe, non-destructive, records exactly what happened and when branches came together. Cons: history gets busy — lots of "Merge branch 'main' into feature" commits and a tangled graph.
Rebase: replay your commits on top
git switch feature
git rebase main
main: A---B---C
\
feature: D'---E'
Git takes your commits D and E, sets them aside, moves the branch to the tip of main, and replays them as new commits D' and E' — same changes, new identities (new hashes).
Pros: a clean, linear history, as if you'd started from the latest main. Easier to read and to search with git bisect.
Cons: it rewrites history. The original D and E are replaced. That's fine for your own private branch, and dangerous for shared ones.
The golden rule of rebasing
Never rebase commits that other people have based work on.
If you rebase a branch someone else has pulled, their copy has D and E while yours has D' and E'. Git sees them as different commits; the next sync produces duplicates and confusing conflicts.
In practice:
- rebasing your own feature branch before others use it: fine;
- rebasing
main, or a branch your teammates are working on: don't.
After rebasing a branch you've already pushed (to your own PR), you'll need to force-push. Use the safer form, which refuses if someone else pushed in the meantime:
git push --force-with-lease
Fast-forward merges
If main hasn't moved since you branched, merging can simply move main's pointer forward — no merge commit needed. That's a fast-forward. Rebasing your branch first makes a fast-forward merge possible, which is why "rebase then merge" produces perfectly linear history.
Squash merges
GitHub's Squash and merge button combines all of a PR's commits into one commit on main. It's popular because:
maingets one tidy commit per feature,- messy "fix typo" and "WIP" commits disappear,
- the PR keeps the detailed history if anyone needs it.
The trade-off: you lose the individual commits on main. For most small teams, that's a good trade. (Write good commit messages still matters for the squashed message.)
Interactive rebase: tidy before you share
Interactive rebase lets you rewrite your own recent commits — reorder, combine ("squash"), reword or drop them — before opening a PR:
git rebase -i main
It opens a list of your commits with actions (pick, squash, reword, drop). Great for turning twelve messy commits into three meaningful ones. Note that it needs an interactive editor, so it doesn't suit fully automated agents.
Conflicts during a rebase
Rebase replays commits one at a time, so you may resolve conflicts commit by commit:
git rebase main
# CONFLICT in src/app.ts
# ...fix the file...
git add src/app.ts
git rebase --continue
Changed your mind?
git rebase --abort
returns everything to how it was before you started. (Merge conflicts explained.) If something goes badly wrong after the fact, git reflog can still find your original commits — see how to undo almost anything in git.
Side by side
| Merge | Rebase | |
|---|---|---|
| History | Preserved, with merge commits | Rewritten, linear |
| Safe on shared branches | Yes | No |
| Conflicts | Resolved once | Possibly per commit |
| Needs force-push after | No | Yes, if already pushed |
| Best for | Integrating shared branches | Updating your own branch; tidying before review |
A sensible team policy
- Feature branches are personal. Rebase them on
mainfreely to stay up to date. - Tidy with interactive rebase before requesting review, if you like.
- Merge PRs into
mainwith squash merge (or rebase merge) for a clean history. - Never rewrite
main. Protect it in GitHub settings. - Use
--force-with-lease, never plain--force.
A note for AI agents
Tell your agents the policy explicitly in CLAUDE.md — for example "Never force-push. Never rebase main. Update feature branches with git rebase origin/main." Agents that improvise with history-rewriting commands can make a mess that's hard to untangle. (How to write a CLAUDE.md.)
The summary
- Merge joins histories with a merge commit; rebase replays your commits on top for a linear history.
- Never rebase commits others have based work on.
- Squash merges give
mainone clean commit per PR. - Use
--force-with-leaseafter rebasing a pushed branch;git rebase --abortto back out.
EasySpawn works with GitHub as the home of your code: Claude Code works on branches on a persistent server and opens pull requests, so your main stays protected and every change arrives through review. See how it works or join the waitlist.
Related: Git Stash Explained · What Is a Pull Request? · How to Read a Diff · Monorepo or Separate Repos? · Git Cherry-Pick Explained
Keep reading
Codespaces vs Coder vs Ona: Cloud Development Environments Compared (2026)
An honest comparison of the main cloud development environments in 2026 — GitHub Codespaces, Coder, Ona (formerly Gitpod) and DevPod-style tools — by who runs them, pricing model, environment definition, persistence, and what each is actually for.
Getting AI to Write Tests That Actually Catch Bugs
Ask an AI for tests and you'll get plenty: tests that mock everything, assert nothing useful, and pass no matter what the code does. How to get tests that fail when behaviour breaks — what to test, how to prompt, how to check a test is real, and how tests become the agent's safety net.