← Back to Blog

Git Rebase vs Merge: Which One to Use?

A Classic Scenario

The most common Git debate on any team: when integrating a feature branch, merge or rebase?

main:    A --- B --- C --- D
                         \
feature:                 E --- F

Both integrate feature into main, but the history looks completely different—affecting code review, rollback, and collaboration.

In One Sentence

  • merge: preserves true history with a merge commit—for shared branches
  • rebase: rewrites history into a straight line—for tidying local commits

Merge: Preserve True History

Operation

git checkout main
git merge feature

Result

main:  A --- B --- C --- D -------- G (merge commit)
                            \     /
feature:                     E --- F

Pros

  • Fully traceable history: --graph shows real branch forks and joins
  • Existing commits untouched: E, F hashes stay the same; pushed branches stay safe
  • Safe retry: failed resolution can be aborted anytime with git merge --abort
  • Zero cognitive load: commits stay as they are

Cons

  • Lots of merge commits (especially when syncing main often) clutter git log
  • Harder to see what a feature actually changed—--first-parent helps
  • Non-linear history adds minor noise to git bisect

When to Use

  • Shared long-lived branches (main/develop)
  • Multi-developer environments that forbid rewriting history
  • Release processes needing a full audit trail

Rebase: Rewrite into Linear History

Operation

git checkout feature
git rebase main
git checkout main
git merge feature   # fast-forward now—no merge commit

Result

main:    A --- B --- C --- D --- E' --- F'

E' and F' are new copies of E, F (completely different hashes) with identical content.

Pros

  • Clean linear history: git log reads top to bottom
  • Review-friendly: no noisy forks in git log --oneline --graph
  • More precise bisect: linear history makes binary search more reliable
  • Squash support: multiple WIP commits become one logical commit

Cons

  • Rewrites history: hashes change; pushed commits pollute shared repos
  • Repeated conflict resolution: every replayed commit can conflict again
  • Costly recovery: needs reflog or a backup branch
  • Fast-forward loses branch context: no record of when the feature was integrated

When to Use

  • Tidying local dev branches (squash/reorder before merging)
  • Personal repos / solo projects
  • Open-source contributions (rebase onto upstream tip after review)

Comparison Table

Dimension merge rebase
History shape forks + merge commits linear
Commit hashes unchanged all rewritten
Shared-branch safe ✅ ❌ (golden rule)
Conflict resolution once repeated per commit
Mistake recovery --abort is enough needs reflog
Code review experience fair better
bisect slightly noisy more precise
Records integration time ✅ merge commit ❌ lost

Golden Rule & Workflow Advice

The Golden Rule (non-negotiable)

Never rebase commits that have already been pushed to a shared repository.

Once teammates have pulled those commits, rebasing forks everyone's history, producing duplicate commits on pull—only force-push can repair it. This is a top source of collaboration accidents.

Solo Workflow

# Tidy locally however you like
git commit -m "wip: scaffold"
git commit -m "wip: core logic"
git rebase -i HEAD~2   # squash into one clean commit
git push

Feature-Branch Workflow (recommended combo)

# 1. Rebase onto latest main before merging—resolve conflicts, stay linear
git fetch origin
git rebase origin/main

# 2. Merge into main (fast-forward now)
git checkout main
git pull --ff-only
git merge feature
git push

Merge-Only Workflow (conservative teams)

git checkout main
git pull
git merge --no-ff feature   # force a merge commit, keep branch context
git push

--no-ff is a common convention for shared branches: even when fast-forward is possible, create the merge commit so "when was this integrated" is clearly recorded.

Conflict Handling

merge conflicts (once):

git merge feature
# resolve → git add → git commit

rebase conflicts (per commit):

git rebase main
# commit 1 conflicts → resolve → git add → git rebase --continue
# commit 2 conflicts → resolve → git add → git rebase --continue
# worst case → git rebase --abort to start over

Recovery: reflog Is Your Safety Net

Rebase merely "moves" commits; it doesn't physically delete them. Every HEAD movement is recorded in the reflog:

git reflog
# find the commit hash before the operation, e.g. abc1234
git reset --hard abc1234
Scenario Recovery command
Bad rebase result git reflog → git reset --hard <hash>
Accidentally deleted branch git reflog → git branch <name> <hash>
Regret after squash find the pre-squash hash in reflog
Lost after --abort reflog entry before the rebase

Note: reflog keeps 30 days by default; recovery is harder if commits were gc'd or never referenced by any branch.


Decision Guide

Scenario Recommendation
Tidying local WIP commits rebase -i (squash + reword)
Solo projects either; rebase is cleaner
Feature branch into shared main rebase to latest, then merge (linear)
Conservative / audit teams merge --no-ff (full history)
Already-pushed commits never rebase; use merge
Open source follow upstream convention, usually rebase

Bottom line: merge public history, rebase private history—this one rule prevents 90% of Git collaboration accidents.

For everyday Git commands and a quick reference, see our Git command cheat sheet.

Advertisement

Frequently Asked Questions

What is the core difference between rebase and merge?

Merge preserves true history: it creates a merge commit recording that two branches converged—history forks but stays fully traceable. Rebase rewrites history: it replays your branch's commits on top of the target branch, producing a clean linear history as if the branch never forked—but the commits are copied anew (all hashes change), effectively rewriting them.

Why is there a 'golden rule' of rebasing? When must you never rebase?

The golden rule: **never rebase commits already pushed to a shared repository**. Rebase rewrites commit hashes; if teammates have branched off those commits, their repos will diverge from the remote, producing confusing duplicate commits on pull, and only force-push can repair it—a collaboration accident waiting to happen. Only commits still local and unpushed are safe to rebase.

What can interactive rebase do?

`git rebase -i` opens an editor listing commits, letting you pick (keep), reword (edit message), edit (change content), squash (merge into previous), fixup (merge and drop message), or drop (delete) any commit. Common uses: tidying WIP commits into logically clear ones, squashing trivial commits, and fixing historic commit messages—making code review dramatically easier.

I lost commits after a rebase mistake—how do I recover?

Rebase doesn't physically delete commits immediately. Git's reflog records every HEAD movement, so recovery is possible **within 30 days**: run `git reflog` to find the commit hash before the operation, then `git reset --hard <hash>` to restore. If you've already force-pushed and the remote is the only copy, recovery gets much harder—another reason rebasing shared branches is forbidden.

← Back to Blog