Blog
5 min read

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:

  • main gets 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

  1. Feature branches are personal. Rebase them on main freely to stay up to date.
  2. Tidy with interactive rebase before requesting review, if you like.
  3. Merge PRs into main with squash merge (or rebase merge) for a clean history.
  4. Never rewrite main. Protect it in GitHub settings.
  5. 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 main one clean commit per PR.
  • Use --force-with-lease after rebasing a pushed branch; git rebase --abort to 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