The rebase vs merge debate is one of the most common discussions in Git workflow. Both commands integrate changes from one branch into another, but they do it in fundamentally different ways. Understanding when to use each is crucial for maintaining a clean, navigable project history.
How Git Merge Works
Git merge creates a new "merge commit" that ties together the histories of both branches. It preserves the complete history and chronological order of commits.
When you run git merge feature, Git finds the common ancestor of both branches, then creates a new commit that combines the changes from both.
A---B---C feature
/ \
D---E---F---G---H main (merge commit)
$ git checkout main
$ git merge featureHow Git Rebase Works
Git rebase moves (or "replays") your commits on top of another branch. It rewrites commit history to create a linear sequence of commits.
When you run git rebase main from your feature branch, Git takes each commit in your branch, temporarily removes it, updates your branch to match main, then re-applies each commit one by one on top.
Before rebase:
A---B---C feature
/
D---E---F---G main
After rebase:
A'--B'--C' feature
/
D---E---F---G main
$ git checkout feature
$ git rebase mainVisual Comparison
╔═══════════════════════════════════════════╗
║ MERGE RESULT ║
║ ║
║ D---E---F---G---H main ║
║ \ / ║
║ A---B---C feature ║
║ ║
║ ✓ Preserves all commits ║
║ ✓ Shows merge point ║
║ ✗ Non-linear history ║
╚═══════════════════════════════════════════╝
╔═══════════════════════════════════════════╗
║ REBASE RESULT ║
║ ║
║ D---E---F---G---A'--B'--C' main ║
║ ║
║ ✓ Clean linear history ║
║ ✓ Easy to read ║
║ ✗ Rewrites commit hashes ║
╚═══════════════════════════════════════════╝When to Use Merge
- Public/shared branches — Merge never rewrites history, so it is safe for branches others are working on.
- Preserving context — The merge commit shows when a feature branch was integrated and by whom.
- Team collaboration — When multiple people work on the same branch, merge avoids conflicts from history rewriting.
- Release branches — Merging release branches preserves the exact integration point.
# Merge with merge commit (recommended for main)
$ git checkout main
$ git merge --no-ff feature
$ git push origin mainWhen to Use Rebase
- Feature branches (before merging to main) — Rebase creates a clean linear history before the final merge.
- Keeping up to date — Rebase your feature branch onto the latest main to get new changes without merge commits.
- Before PR submission — A rebased branch with clean commits is easier to review.
- Cleaning up local commits — Interactive rebase lets you squash, reorder, and edit commits before sharing.
# Update feature branch with latest main
$ git checkout feature
$ git rebase main
# If conflicts occur, resolve them, then:
$ git add .
$ git rebase --continueInteractive Rebase: Squash, Fixup, Reorder
Interactive rebase (git rebase -i) is one of the most powerful Git features. It lets you modify commits before sharing them.
$ git rebase -i HEAD~4
# Editor opens:
pick a1b2c3d Add user authentication
pick e4f5g6h Fix typo in auth
pick i7j8k9l Add password validation
pick m0n1o2p Fix lint error
# Change to:
pick a1b2c3d Add user authentication
fixup e4f5g6h Fix typo in auth # merge into previous, discard message
pick i7j8k9l Add password validation
fixup m0n1o2p Fix lint error # merge into previous, discard message
# Result: 2 clean commits instead of 4
# Commands:
# pick = use commit as-is
# squash = merge into previous commit, keep message
# fixup = merge into previous commit, discard message
# reword = use commit but edit message
# drop = remove commit entirelyThe Golden Rule: Never Rebase Public Branches
If a branch has been pushed and others are working on it, DO NOT rebase it. Rebase rewrites commit hashes, which means other developers will have divergent histories and will face painful merge conflicts.
Golden Rule: Only rebase commits that have NOT been pushed to a shared remote, or that only you are working on.
# ❌ DANGEROUS: Rebasing a shared branch
$ git checkout main
$ git rebase feature # NEVER do this!
# ✅ SAFE: Rebasing your own feature branch
$ git checkout my-feature # only I work on this
$ git rebase main # safe!
$ git push --force-with-lease origin my-featureMerge vs Rebase: Complete Comparison
| Criteria | Merge | Rebase |
|---|---|---|
| History | Non-linear, preserves branches | Linear, clean |
| Commit hashes | Preserved | Changed (new hashes) |
| Conflict resolution | Once (in merge commit) | Per commit (may resolve multiple times) |
| Traceability | High (merge commit shows context) | Medium (linear but no branch info) |
| Safety | Safe (no history rewrite) | Risky (rewrites history) |
| For shared branches | ✅ | ❌ |
| For personal branches | ✅ | ✅ |
| Reversibility | Easy (git revert) | Harder (requires reflog) |
| git bisect | Harder (non-linear) | Easier (linear history) |
Common Git Workflows
Feature Branch Workflow
Create a feature branch, develop, rebase onto main, then merge with --no-ff. This gives a linear history with clear merge points.
$ git checkout -b feature/login
# ... develop ...
$ git rebase main # clean up history
$ git checkout main
$ git merge --no-ff feature/login # merge with commit
$ git branch -d feature/loginGitflow Workflow
Uses develop, feature, release, and hotfix branches. Always merge (never rebase) between long-lived branches.
# Always merge between long-lived branches
$ git checkout develop
$ git merge --no-ff feature/login
$ git checkout release/1.0
$ git merge --no-ff develop
$ git checkout main
$ git merge --no-ff release/1.0
$ git tag v1.0Trunk-Based Development
Short-lived feature branches (< 1 day). Rebase onto main frequently, merge quickly. Favored by high-performing teams.
# Short-lived branch, rebase often
$ git checkout -b fix/typo
# ... quick fix ...
$ git rebase main
$ git checkout main
$ git merge fix/typo # fast-forward
$ git pushDangerous Scenarios
| Scenario | Risk | Prevention |
|---|---|---|
| Rebase main/master | Everyone's history breaks | Never rebase main |
| Rebase shared feature branch | Collaborators face divergent history | Use merge for shared branches |
| git push --force | Overwrites remote changes | Use --force-with-lease instead |
| Wrong conflict resolution during rebase | Code loss or corruption | Review each conflict, use git rebase --abort to restart |
| Rebase branch with merge commits | Merge commits flattened, confusing | Use --rebase-merges or avoid |
Frequently Asked Questions
Can I undo a rebase?
Yes. Use git reflog to find the commit hash before the rebase, then git reset --hard <hash> to restore it. The reflog keeps a record of all HEAD movements for 90 days by default.
Should I rebase or merge when updating my feature branch?
If you are the only person working on the feature branch, rebase is preferred for a cleaner history. If others are also working on it, use merge to avoid rewriting shared history.
What is git pull --rebase?
Instead of merging the remote branch into your local branch (creating a merge commit), git pull --rebase replays your local commits on top of the remote changes. It keeps your history linear.
What happens if I rebase a branch that has been pushed?
You will need to force push (git push --force-with-lease). Anyone who has pulled the old commits will have divergent history and will need to reset or re-clone. This is why you should only rebase branches that you alone work on.
Is squash merge the same as rebase + squash?
Similar result, different mechanism. GitHub's "Squash and merge" button squashes all commits into one and creates a merge commit. Rebase -i with squash lets you choose which commits to combine and gives you more control.
Which is better for open source contributions?
Most open source projects prefer rebased, clean commits. Before submitting a PR, rebase onto the latest main and squash fixup commits. This makes the PR easier to review and the history cleaner.