Git Merge vs Rebase: Which One Should You Use?
Merge and rebase both combine branches but leave very different histories. Learn what each really does, the golden rule, and how to choose for your team.
Sooner or later, every developer hits this moment: your feature branch has fallen behind main, and Git offers you two ways to catch up. Merge or rebase. Both get the job done, both are used daily by professional teams, and the internet is full of people arguing about them with religious intensity.
Here's the calm version: they solve the same problem with different trade-offs, there's one safety rule you must never break, and once you understand what each command actually does to your history, the choice becomes boring. Boring is good.
This article assumes you're comfortable with commits, branches, and pushing to a remote. If those still feel wobbly, read Git for Beginners first and come back.
What a branch really is
To understand merge and rebase, you need one piece of Git's internals, and it's a small one.
Every commit in Git points to its parent commit, forming a chain going back to the very first commit. A branch is nothing more than a movable label pointing at one commit. That's it. main is a label. feature-login is a label. When you commit on a branch, Git creates the new commit and slides the label forward to point at it.
So when we say two branches have "diverged," we mean the labels point at commits on different chains that split from a common ancestor. Both branches below grew from commit o: main gained commits A through C, and your feature branch gained D and E:
o --- A --- B --- C <- main
\
D --- E <- feature-login
Merge and rebase are simply two different answers to the question: how do we combine the work on these two chains?
What git merge does
Merge takes two branch tips, finds their common ancestor, and creates a new commit that has both tips as parents. That new commit is called a merge commit, and it's the only commit merge creates. Nothing existing is touched.
git switch main
git pull
git merge feature-login
The history afterward looks like this:
o --- A --- B --- C ------ M <- main
\ /
D --- E ---------------
Commit M is the merge commit. It ties the two chains together, and its existence is the record that a merge happened.
Two properties fall out of this:
- Merge is non-destructive. Every commit that existed before still exists, unchanged, with the same IDs. Nothing is rewritten, so nothing can be lost or duplicated.
- History reflects reality. The graph shows that two lines of work happened in parallel and were joined at a specific moment. Six months later, you can see exactly what shipped together.
The cost is visual noise. On a busy team, git log --graph becomes a tangle of crossing lines, and if you merge main into a long-lived feature branch repeatedly, the branch fills up with "Merge branch 'main' into feature-login" commits that carry no information about your actual work.
One special case worth knowing: if main hasn't moved since you branched, there's nothing to weave together, and Git just slides the main label forward to your tip. That's a fast-forward merge, and it creates no merge commit at all.
What git rebase does
Rebase answers the same question differently: instead of weaving the two chains together, it picks your commits up and replays them, one by one, on top of the other branch. Your work ends up sitting on a new base, hence the name.
git switch feature-login
git fetch origin
git rebase origin/main
Starting from the same diverged history, the result is:
o --- A --- B --- C <- main
\
D' --- E' <- feature-login
Notice the primes: D' and E'. This is the crucial detail. Rebase does not move your commits, because commits in Git are immutable. It creates brand-new commits with the same changes but different parents, and therefore different commit IDs. The originals still exist in Git's internals for a while, but your branch label now points at the copies. Rebase rewrites history.
What you gain is a perfectly linear story: your work now reads as if you started it from the latest main all along. git log is a straight line. Reviewers see your commits cleanly stacked on current code, and when the branch is finally merged, it can often fast-forward with no merge commit at all.
What you pay is the rewrite. If a commit exists in two places with two different IDs, Git sees two different commits, and that's where people get hurt.
One practical note: because your rebased branch no longer matches what you previously pushed, pushing it again requires force. Use the safer flag:
git push --force-with-lease
Unlike plain --force, this refuses to push if someone else has pushed to the branch since you last fetched, so you can't accidentally stomp a teammate's commits.
The golden rule: never rebase shared history
Here is the one rule that is not a matter of taste: never rebase commits that other people may have built work on. In practice, that means never rebase main, develop, or any branch other people pull from.
Why is this so serious? Say you rebase main and force-push it. Every teammate's copy of main still points at the old commits. Their new work is built on commits that, as far as the remote is concerned, no longer belong to history. When they pull, Git tries to reconcile the two versions, and they end up with duplicated commits, conflicts that make no sense, and a repository that needs manual surgery. Multiply that by every person on the team.
The flip side makes the rule easy to live with: your own feature branch, which nobody else has based work on, is yours to rebase as often as you like. Rebasing your local work before anyone depends on it is completely safe and very common.
If you keep one sentence from this article, keep this one: rebase your own unshared work freely, and never rewrite a branch that others pull from.
How teams actually use them
Most real teams land on one of three workflows:
Merge everything. Feature branches are merged into main with merge commits, and main is merged back into long-lived branches to keep them current. Simple, safe, zero rewritten history. The graph gets noisy, but tools cope. Good default for teams new to Git.
Rebase locally, merge to share. You rebase your feature branch onto main regularly while you work, keeping it current and conflict-free in small doses. When it's approved, it merges into main. You get clean, reviewable branches and an honest mainline. This is probably the most common professional setup, and git pull --rebase (which rebases your local commits onto the remote instead of creating pointless pull-merge commits) fits naturally into it.
Squash merges. The platform button on GitHub or GitLab compresses the whole feature branch into a single commit on main. Nobody cares how messy the branch was; main reads one commit per feature. Teams that value a tidy mainline over granular history love this.
None of these is wrong. What matters is that the team picks one and everyone follows the golden rule.
A quick word on interactive rebase
Rebase has a second job beyond catching up with main. Interactive mode lets you edit your own recent commits before sharing them:
git rebase -i HEAD~3
Git opens an editor listing your last three commits, and you choose what happens to each: squash small fixups into the commit they belong to, reword bad messages, reorder, or drop commits entirely. It's the standard way to turn "wip", "fix typo", "actually fix it" into one coherent commit before you open a pull request. The golden rule applies here too: clean up commits before they're shared, not after.
The decision table
| Situation | Reach for |
|---|---|
Bringing a finished feature into main | Merge (or your team's PR button) |
Updating your feature branch with the latest main | Rebase your branch onto main |
| Anything involving a branch others pull from | Merge, always |
| Cleaning up your own commits before a PR | Interactive rebase |
| Pulling when you have local commits | git pull --rebase |
| Unsure and nobody to ask | Merge, it's the safe default |
Frequently asked questions
Does rebase delete my original commits?
Not immediately. Rebase creates new commits and moves your branch label to them; the originals become unreferenced and are cleaned up by Git much later. Until then, git reflog can find them, which is your safety net if a rebase goes wrong: git reset --hard back to the pre-rebase entry and you're restored.
Which one does the GitHub merge button use?
All three, depending on settings: "Create a merge commit" is a true merge, "Squash and merge" compresses the branch into one commit, and "Rebase and merge" replays your commits onto the base branch. Repository admins choose which options are allowed, which is effectively how a team enforces its workflow.
What if I rebased a shared branch by accident?
Tell your team immediately, before anyone pulls. The fastest fix is usually restoring the branch to its previous state using git reflog on a machine that has the old commits, then force-pushing the restoration with --force-with-lease. The longer people work on top of the broken state, the messier the cleanup, so speed and communication matter more than the exact recovery command.
Is one of them objectively better?
No. Merge optimizes for truthful history and safety; rebase optimizes for readable history and clean reviews. Strong teams use both: rebase for private work, merge for shared work. Anyone who tells you one is always right is selling a style preference as a law.
Where to go from here
The commands are the easy part; the confidence comes from understanding the graph, and that comes from practice on a repo where mistakes cost nothing. Make a throwaway repository, create a divergence like the diagrams above, and run both commands while watching git log --oneline --graph change.
If you'd rather build that understanding step by step with guided, interactive practice, from your first commit through branching, rebasing, and team workflows on GitHub, that's exactly what the Git & GitHub course is for.
Kodion Team
Kodion Editorial
Written by the team that builds Kodion's courses: working engineers who test every example in the interactive editor before it ships. How we create and review content
Continue Learning
📚 Related Articles
Git for Beginners: The Only 8 Commands You Need to Start
Git feels overwhelming because tutorials teach 40 commands when you need 8. Learn the daily workflow, what each command actually does, and how to undo mistakes.
Build Your First REST API: A Beginner's Roadmap
Design and build a working REST API in Python with FastAPI: endpoints, Pydantic models, status codes, and curl tests, explained step by step.