Git Merge vs Rebase Explained
Understand the difference between git merge and git rebase, when to use each one, and how rebasing rewrites commit history compared to a regular merge commit.
Introduction
Both git merge and git rebase accomplish the same basic goal: bringing changes from one branch into another. But they do it in fundamentally different ways, and that difference shows up clearly in your commit history. Understanding both — and when to reach for each — is one of the more nuanced but important Git skills.
Setting the Scene
Imagine main has moved forward since you created feature:
main: A---B---C
\
feature: D---E
Both approaches want to combine feature into main, but they produce different results.
git merge: Preserving History As It Happened
git checkout main
git merge feature
This creates a new merge commit that has two parents, joining both histories together exactly as they occurred:
main: A---B---C-------F (F is the merge commit)
\ /
feature: D-------E
Nothing about the existing commits changes — merge only adds a new commit on top. This preserves an accurate, if sometimes noisy, record of exactly when and how branches diverged and recombined.
git rebase: Rewriting History for a Cleaner Line
git checkout feature
git rebase main
Rebase takes your commits (D and E) and replays them one at a time on top of the current tip of main (C), producing brand new commits with different hashes:
main: A---B---C
\
feature: D'---E' (D and E, replayed on top of C)
After rebasing, merging feature into main becomes a simple fast-forward, with no merge commit at all:
git checkout main
git merge feature
# Fast-forward, main now points directly at E'
main: A---B---C---D'---E'
The resulting history reads as a single straight line, as if you had written your commits directly on top of the latest main from the start.
The Trade-off in One Sentence
Merge preserves true history but can create a tangled graph with lots of merge commits; rebase creates a clean, linear history but rewrites commit hashes, which is unsafe for commits other people have already based work on.
The Golden Rule of Rebasing
Never rebase commits that have already been pushed and that someone else might have pulled. If two people have the old version of a commit, and you rebase it into a new commit with a different hash, anyone who already has the old one will run into confusing, duplicated, or conflicting history when they try to sync.
# Safe: rebasing your own local feature branch before it's pushed or shared
git checkout feature
git rebase main
# Risky: rebasing a branch that teammates have already pulled and built on
git checkout shared-branch
git rebase main
git push --force # rewrites history other people depend on
Interactive Rebase for Cleaning Up Commits
Beyond combining branches, rebase -i (interactive rebase) is commonly used to clean up your own commit history before opening a pull request — squashing small "fix typo" commits into the meaningful commit they belong with:
git rebase -i HEAD~3
pick f7f3f6d Add search bar component
squash 310154e fix typo
squash a5f4a0d address review comment
# becomes one clean commit: "Add search bar component"
Choosing Between Merge and Rebase in Practice
A common and reasonably safe convention:
- Use
rebaseto update your own local feature branch with the latestmain, before it is shared or as part of tidying commits before a pull request. - Use
merge(often via a pull request's "merge" button) to bring a completed, reviewed feature branch back intomain, preserving a record of when that feature was integrated. - Avoid rebasing any branch once other people have pulled it, unless the whole team is explicitly coordinating around a force-push.
Best Practices
- Rebase local, unpublished work to keep history clean before sharing it.
- Merge for integrating completed, reviewed feature branches back into a shared branch like
main. - Communicate clearly with your team before force-pushing a rebased branch that others might have already pulled.
- Use interactive rebase to squash noisy "WIP" or "fix typo" commits into meaningful units before opening a pull request.
- Configure
git pull --rebase(orgit config pull.rebase true) if your team prefers to avoid unnecessary merge commits when syncing with a remote branch.
Common Mistakes to Avoid
- Force-pushing a rebased branch that teammates have already based their own work on, causing confusing conflicts for everyone involved.
- Rebasing a long-lived shared branch instead of a personal feature branch.
- Resolving the same merge conflict repeatedly across many commits during a rebase, instead of using
git rebase --continuecorrectly or considering a merge instead for a particularly conflict-heavy branch. - Assuming rebase "loses" commits — the original commits are simply abandoned and eventually garbage collected, not instantly deleted, which is why recovery via
git reflogis often possible shortly after a mistake.
Interactive Rebase for Cleaning Up History
Beyond replaying commits onto a new base, git rebase -i (interactive rebase) lets you rewrite your own unpushed commit history before sharing it — squashing several small "fix typo" commits into one meaningful commit, reordering commits, or editing an old commit message:
git rebase -i HEAD~4
# opens an editor listing the last 4 commits, e.g.:
# pick a1b2c3d Add login form
# pick d4e5f6a Fix typo
# pick 7a8b9c0 Fix another typo
# pick 1c2d3e4 Add validation
# change "pick" to "squash" (or "s") on the typo-fix commits
After saving, Git combines the squashed commits into the one above them, prompting you to write a single combined commit message — turning four noisy commits into one clean "Add login form with validation" commit. This is purely a local history-cleanup tool and follows the exact same golden rule as regular rebasing: only rewrite commits that have not been pushed and shared with others, since squashing or reordering commits that other people have already built on top of will rewrite history out from under them. Many teams use interactive rebase routinely before opening a pull request, turning a messy sequence of work-in-progress commits into a small number of well-described, logically grouped commits that are much easier for a reviewer to read.
If a rebase goes wrong — a resolved conflict turns out to have been resolved incorrectly, or you simply want to start over — git rebase --abort returns the branch to exactly the state it was in before the rebase began, as if it had never happened. This safety net is worth remembering specifically because rebase conflicts can appear repeatedly, once per replayed commit, which feels more disorienting than a single merge conflict resolved once; knowing you can always cleanly back out makes it much less stressful to work through them one at a time.
Git also provides git rerere ("reuse recorded resolution"), which remembers how you resolved a specific conflict and automatically reapplies the same resolution if an identical conflict appears again — genuinely useful on long-lived branches that get rebased repeatedly against a fast-moving main branch, where the same handful of conflicts might otherwise need to be resolved by hand over and over.
Conclusion
Merge and rebase are not competing tools with one "correct" answer — they solve the same problem with different trade-offs between preserving true history and keeping that history clean and linear. Rebase your own unshared work freely; merge shared, reviewed work back together; and always respect the golden rule once a branch has been pushed for others to build on.
Article FAQ
References
Comments
Comments are coming soon. Meanwhile, share your feedback via our contact page.
Related Articles
View allGit Branching Strategies Explained
Compare popular Git branching strategies including Git Flow, GitHub Flow, and trunk-based development, with guidance on choosing the right one for your team.
System Design Interview Preparation Guide
A structured approach to preparing for system design interviews, covering the framework interviewers expect, common topics to study, and how to communicate y...
Top JavaScript Interview Questions and Answers
A curated list of common JavaScript interview questions with clear explanations, covering closures, hoisting, the event loop, prototypes, and equality compar...
Resume Tips for Developers
Practical resume tips for software developers, covering how to describe technical work with impact, formatting advice, and what to leave off a strong develop...