Git Merge vs Rebase: Which Should You Use? (With Examples)
git merge and git rebase both help you integrate changes from one branch into another. The difference is how they treat history:
- Merge preserves history and branch context (safe on shared branches).
- Rebase rewrites history by replaying commits on a new base (great for local cleanup).
Table of Contents
1. Quick decision: merge vs rebase
If you only remember one thing: merge is safer, rebase is cleaner.
| Situation | Use | Why |
|---|---|---|
| You’re on a personal feature branch and want a clean PR | git rebase main |
Keeps history linear; lets you squash/fixup before review. |
| Your feature branch is shared with teammates | git merge main |
Doesn’t rewrite commits others already pulled. |
| You’re updating your local branch after pulling from origin | git pull --rebase |
Avoids “merge bubbles” created by repeated pulls. |
| You want to preserve the fact that a feature was integrated as a unit | Merge commit (--no-ff) |
Creates a single integration point (useful for audits and reversions). |
| You want the simplest history (one commit per PR) | Squash merge | Collapses a feature branch into one commit on main. |
main. This gives you clean PRs and avoids rewriting shared history.
2. What git merge does (fast-forward vs merge commit)
When you merge, Git combines the histories of two branches. There are two common outcomes.
Fast-forward merge (no merge commit)
If the target branch has not diverged, Git can “fast-forward” the branch pointer:
# You are on main and it is behind feature
git switch main
git merge --ff-only feature
# History becomes linear (no extra merge commit)
Before:
main: A---B
feature: \
C---D
After fast-forward:
main: A---B---C---D
3-way merge (creates a merge commit)
If both branches have new commits, Git creates a merge commit with two parents:
git switch main
git merge feature
Before:
main: A---B---E
feature: \
C---D
After merge commit (M):
main: A---B---E---M
\ /
C---D
You can force a merge commit even when fast-forward is possible:
# Always create a merge commit for features
git merge --no-ff feature
Why would you do that? Because the merge commit becomes an “integration checkpoint” that groups a feature together (and can make it easier to revert the whole feature later).
3. What git rebase does (and why hashes change)
Rebase does not “join histories.” Instead, it takes commits from your branch and replays them on top of another base commit.
git switch feature
git fetch origin
git rebase origin/main
Because the parent commit changes, the rebased commits become new commits with new hashes (even if the code changes are identical).
Before:
main: A---B---C
feature: \
D---E
After rebase:
main: A---B---C
feature: \
D'---E'
If you want to clean up commits before sharing them, use interactive rebase:
# Reword, squash, fixup, reorder the last 5 commits
git rebase -i HEAD~5
If you’re new to interactive rebase, read the Git Rebase Complete Guide for step-by-step examples.
4. Comparison table
| Topic | Merge | Rebase |
|---|---|---|
| History | Preserves branch structure | Linearizes history |
| Commit hashes | Unchanged | Rewritten (new hashes) |
| Safety on shared branches | Safe | Risky (avoid) |
| Conflict resolution | Usually once (at merge commit) | Potentially multiple times (per commit) |
| Best for | Integrating work, preserving context | Cleaning up your own branch |
5. Copy-paste workflows
Workflow A: keep your feature branch up to date (merge)
Use this if the branch is shared, or you want zero history rewriting.
git switch feature
git fetch origin
git merge origin/main
Workflow B: keep your feature branch up to date (rebase)
Use this if the branch is yours and you want a clean, linear history.
git switch feature
git fetch origin
git rebase origin/main
If feature is already pushed and you rebased, update the remote safely:
git push --force-with-lease
--force-with-lease, not --force: it refuses to overwrite the remote branch if someone else pushed new commits.
Workflow C: avoid pull merge commits (recommended)
Many “mystery merge commits” come from a plain git pull. You can rebase on pull instead:
# One-time config
git config --global pull.rebase true
git config --global rebase.autoStash true
# Or per-command
git pull --rebase
Workflow D: inspect your history quickly
When in doubt, visualize the commit graph:
git log --graph --oneline --decorate --all
For more log recipes, see Git Log: The Complete Guide for 2026.
Workflow E: clean up commits before opening a PR
A simple “polish” flow for solo feature branches:
git switch feature
git fetch origin
git rebase -i origin/main
# After you’re happy
git push --force-with-lease
6. Pitfalls & best practices
Don’t rebase shared branches
If you have to ask “is this shared?”, assume it is. Use merge.
Know the escape hatches
# Abort an in-progress operation
git merge --abort
git rebase --abort
# Recover “lost” commits after a bad rebase
git reflog
git reset --hard <pre-rebase-hash>
Conflicts during rebase can repeat
With merge, you typically resolve conflicts once. With rebase, you might resolve similar conflicts multiple times (because each commit is replayed). If you rebase frequently, consider enabling rerere:
git config --global rerere.enabled true
Merging can be “clean” too
If you like linear history but don’t want rebase, you can enforce fast-forward merges:
# Merge only if fast-forward is possible
git merge --ff-only feature
Some teams prefer this because it avoids merge commits without rewriting history.
7. FAQ
Is rebase “better” than merge?
No. Rebase is better for rewriting your own work into a clean story. Merge is better for safely integrating work while keeping full context. Most teams use both: rebase locally, merge to main.
What about “rebase and merge” on GitHub/GitLab?
Those buttons typically perform a rebase-like integration to keep history linear. They can be great if your team values linear history, but remember: the commits on main may differ (hash-wise) from the ones on the feature branch.
How do I revert a merged feature?
If the feature was merged via a merge commit, you can revert the whole feature by reverting that merge commit:
git revert -m 1 <merge-commit>
If it was squash-merged, you revert the squash commit (a normal revert). If it was rebased, you revert individual commits that landed on main.
Related: Git Cherry-Pick vs Revert · Git Undo: Reset, Revert & Restore · Git Bisect