Git Rebase vs Merge: When to Use What
One of the most discussed decisions in Git is: use rebase or merge? Both integrate changes from one branch to another, but in radically different ways. Understanding the differences, advantages, and disadvantages of each is essential to maintaining a clean Git history and collaborating effectively in teams.
🎯 What You'll Learn
- Fundamental differences between rebase and merge
- When to use rebase and when merge
- Fast-forward merge vs merge commit
- The golden rule of rebase
How Merge Works
Git merge creates a new merge commit that joins the histories of two branches. It's a non-destructive operation: the original branches are not modified.
# Initial situation
main: A---B---C
\
feature: D---E
# Merge feature into main
git checkout main
git merge feature
# Result
main: A---B---C-------M
\ /
feature: D---E---
# M is the merge commit with two parents: C and E
# Switch to target branch
git checkout main
# Merge the feature branch
git merge feature
# Git automatically creates a merge commit
# Default message: "Merge branch 'feature' into main"
How Rebase Works
Git rebase rewrites history by moving commits from one branch onto a new base. Instead of creating a merge commit, commits are reapplied one by one onto the tip of the target branch.
# Initial situation
main: A---B---C
\
feature: D---E
# Rebase feature onto main
git checkout feature
git rebase main
# Result
main: A---B---C
\
feature: D'---E'
# D' and E' are NEW commits with same content as D and E
# but different hashes (because the parent changed)
# Switch to feature branch
git checkout feature
# Rebase onto main
git rebase main
# Feature commits are reapplied onto main
# If there are conflicts, resolve them and:
git add .
git rebase --continue
# Now you can fast-forward merge on main
git checkout main
git merge feature # Fast-forward (no merge commit)
Fast-Forward Merge
A fast-forward merge happens when the target branch has no new commits compared to the branch being merged. Git simply "moves forward" the pointer without creating a merge commit.
# Situation after rebase
main: A---B---C
\
feature: D'---E'
# Fast-forward merge
git checkout main
git merge feature
# Result (no merge commit)
main: A---B---C---D'---E'
|
feature:
To prevent fast-forward and force a merge commit:
git merge --no-ff feature
Key Differences
Merge:
- Branched history: Shows when branches diverged
- Non-destructive: Original history preserved
- Traceable: See exactly when merge happened
- Safe: No history rewriting
- Creates additional merge commits
Rebase:
- Linear history: As if you worked sequentially
- Destructive: Rewrites commits (new hashes)
- Clean: No noisy merge commits
- Risky: Never rebase public branches!
- Easier to read history
When to Use Merge
✅ Use Merge when:
- Integrating public branches (main, develop)
- You want to preserve history of exactly when branches diverged
- Working in a team and others have already pulled the branch
- You want to track milestones (completed features, releases)
- Following Git Flow (use
--no-ffto preserve structure)
# Feature branch completed
git checkout main
git merge --no-ff feature/user-auth
# Merge commit preserves feature history
# Clear when feature was integrated
# Other developers see complete structure
When to Use Rebase
✅ Use Rebase when:
- Working on local private branches
- You want clean linear history
- Updating your feature branch with latest changes from main
- You want to avoid "noisy" merge commits
- Before creating a Pull Request (for clean history)
# Your feature branch is behind main
git checkout feature/payment
git rebase main
# Now your branch has latest changes from main
# Linear history, easy to review in PR
# Resolve conflicts if necessary
# git add .
# git rebase --continue
The Golden Rule of Rebase
⚠️ GOLDEN RULE
Never rebase commits that have been pushed to shared public branches!
Rebase rewrites history by changing commit hashes. If other developers have already based their work on those commits, their repository becomes inconsistent and the next pull will cause conflicts and duplications.
# BAD! Others have already pulled main
git checkout main
git rebase feature
# This rewrites main, causing problems for everyone
# GOOD! feature is only yours, nobody pulled it
git checkout feature
git rebase main
# Update your private branch with latest changes
Recommended Workflow
Here's a workflow that combines the best of both:
# 1. Create feature branch from main
git checkout -b feature/new-feature main
# 2. Work on the feature
git commit -m "feat: add component"
git commit -m "feat: add tests"
# 3. Meanwhile, main has moved forward
# Update your branch with rebase (private branch, OK!)
git fetch origin
git rebase origin/main
# 4. History cleanup with interactive rebase (optional)
git rebase -i origin/main
# Squash WIP commits, fixup typos, etc.
# 5. Push feature branch
git push origin feature/new-feature
# 6. Create Pull Request on GitHub/GitLab
# 7. After code review, merge into main WITH merge commit
git checkout main
git merge --no-ff feature/new-feature
# Result: clean main history with clear milestones
Linear vs Branched History
History with Merge:
* Merge feature B
|\
| * Feature B commit 2
| * Feature B commit 1
* | Main commit 3
* | Main commit 2
|/
* Main commit 1
Pro: See exactly when features were integrated
Con: Can become chaotic with many merges
History with Rebase:
* Feature B commit 2
* Feature B commit 1
* Main commit 3
* Main commit 2
* Main commit 1
Pro: Linear, easy to follow
Con: Lose information about when branches diverged
Conflicts: Merge vs Rebase
Both can have conflicts, but they're handled differently:
# Conflicts during MERGE
git merge feature
# Resolve conflicts
git add .
git commit # Complete the merge
# Conflicts during REBASE
git rebase main
# Resolve conflicts for each conflicting commit
git add .
git rebase --continue # Move to next commit
# Repeat until done
# Abort rebase if necessary
git rebase --abort
Team Preferences
Different teams have different preferences. Some examples:
- Linux Kernel: Only merge (preserves complete history)
- Rails: Rebase for feature branches, merge for important integrations
- Google: Linear history with rebase (trunk-based development)
Conclusion
There's no universally "better" choice between merge and rebase. Merge preserves
history and is safe for public branches. Rebase creates clean linear history
but rewrites commits. The best strategy is often hybrid: use rebase to keep your
local feature branches clean, and merge with --no-ff to integrate into public branches.
🎯 Key Points
- Merge = non-destructive, creates merge commits, branched history
- Rebase = rewrites history, no merge commits, linear history
- Never rebase shared public branches
- Use rebase for private feature branches, merge for public integrations
- Fast-forward merge = no merge commit when possible







