Skip to content

Git: rebase vs merge

Mental model

Both integrate changes from one branch into another. Merge preserves the original graph shape by adding a join node. Rebase rewrites the graph to make it linear by replaying commits onto a new base.

What it looks like

"Merge and rebase both combine branches." People treat them as interchangeable ways to get code together.

What it really is

  • Merge: creates a new merge commit with two parents. The original commits on both sides remain exactly as they were. Full history preserved, graph stays branchy.
  • Rebase: takes your commits, computes the diffs they introduce, and replays those diffs on top of the target branch. This creates new commit objects with new SHAs. The old commits still exist until garbage collection, but no branch points to them anymore.
  • Interactive rebase (rebase -i): lets you edit, squash, reorder, or drop commits. Used for cleaning up local history, not just integrating branches.

Why it seems confusing

Both "combine branches," but one mutates history and one does not. "Rewriting history" sounds destructive, but it really means creating new commit objects — the old ones linger in the object store until GC. The confusion deepens because both produce a working result; the difference is in the commit graph shape.

What actually matters

  • Golden rule: never rebase commits that others have based work on. Shared history must not be rewritten — it forces everyone to reconcile diverged SHAs.
  • Merge is safe and transparent. It shows exactly when integration happened. Use it for shared/long-lived branches.
  • Rebase produces a clean linear history. Use it for local feature branches before merging to main.
  • After rebase, you must force-push (push --force-with-lease) if the branch was already pushed — another signal that history changed.

Common mistakes

  • Rebasing a shared branch (breaks collaborators' history).
  • Using merge for tiny local fixup commits (clutters log with merge commits; squash or rebase first).
  • Forgetting that rebase can cause conflicts at each replayed commit, not just once like merge.
  • Thinking rebase -i is only for integrating branches (it is the primary tool for editing local commit history).

Small examples

# Merge: preserves topology
       o---o---o  feature
      /         \
o---o---o---o----M  main    (M = merge commit, two parents)

# Rebase: linearizes history
       o---o---o  feature (old, now unreferenced)

o---o---o---o---o'--o'--o'  main + rebased feature
                (new SHAs, same diffs)
# Merge workflow
git checkout main
git merge feature          # creates merge commit

# Rebase workflow
git checkout feature
git rebase main            # replays feature commits onto main
git checkout main
git merge feature          # fast-forward, no merge commit

# Interactive rebase: clean up last 3 commits
git rebase -i HEAD~3      # squash, edit, reorder

One-line summary

Merge joins two histories with a merge commit; rebase replays your commits onto a new base, rewriting SHAs for a linear history.