Skip to main content
TF
By Rohit V.9 min readArticle

Git Merge vs Rebase: When Should You Use Each

TF
ToolsFuel Team
Web development tools & tips
Two developers reviewing code branches together on a laptop

Photo by Unsplash on Unsplash

The Short Version

> Quick answer: `git merge` and `git rebase` both combine work from two branches, but they go about it in opposite ways. Merge ties the two branch tips together with a new *merge commit*, keeping the exact history of what happened — including the fact that work happened in parallel. Rebase instead lifts your branch's commits off and replays them one by one on top of the other branch, giving you a straight, linear history with no merge commit. Merge is safe and non-destructive; rebase rewrites commit history, so you never run it on a branch other people have already pulled. Reach for merge on shared branches and when you want an honest record; reach for rebase on your own local feature branch to tidy it up before opening a pull request.

I got burned by this early on. I rebased a branch a teammate had already checked out, force-pushed it, and then spent an afternoon untangling duplicated commits and a very confused Git state. So I care about getting the distinction right. Both commands answer the same question — "how do I bring these two lines of work together?" — with completely different philosophies about history.


This post walks through what each command does under the hood, the trade-offs, the one rule that keeps you out of trouble, and the workflow I actually use every day.

What Merge Actually Does

When you run `git merge feature` while sitting on `main`, Git looks at both branch tips and their common ancestor, then creates a single new commit — the merge commit — that has two parents. That commit records the moment the two histories joined. Your feature commits stay exactly where they were, with their original hashes, timestamps, and order untouched.

There's a special case worth knowing: the
fast-forward merge. If `main` hasn't moved since you branched off it, Git doesn't need a merge commit at all — it just slides the `main` pointer forward to your feature tip. Clean and simple. You only get a real merge commit when both branches have new work, meaning the history genuinely diverged. If you'd rather force a merge commit even when a fast-forward is possible (some teams like the explicit marker that a feature landed), you pass `--no-ff`.

The big win with merge is honesty. The graph shows what really happened: you branched here, worked in parallel, and joined back there. Nothing gets rewritten, so it's non-destructive and it's genuinely hard to lose work. That's why it's the default for pulling feature branches into a shared `main`, and why it's the calmer choice if you're still building Git muscle memory.


The downside? On a busy repo, merge commits pile up. If ten people merge feature branches every day, your `git log` turns into a braided mess of merge commits and criss-crossing lines. It's accurate, but reading it can be a chore. Some of that noise is just the price of an honest record — and most days I'd rather have the truth than a pretty graph. Clear release conventions help keep things legible even when the graph is busy; I wrote about that in
semantic versioning.

What Rebase Actually Does

Close-up of hands typing git commands on a mechanical keyboard

Photo by Unsplash on Unsplash

Rebase takes a different route entirely. When you run `git rebase main` from your feature branch, Git sets your feature commits aside, moves your branch's base to the current tip of `main`, and then replays each of your commits on top, one at a time, in order. The result is a linear history — it looks as though you started your work from the latest `main`, even though you actually branched off earlier.

Here's the crucial part: replaying a commit creates a *new* commit with a *new* hash. The originals are effectively discarded and rewritten. The content is usually identical, but the identity changes. That's what people mean when they say rebase "rewrites history," and it's exactly why the command is both powerful and dangerous.


The payoff is a clean, readable log. No merge commits, no tangled graph — just a straight line you can follow from the newest commit all the way back to the start of the project. And when you rebase your feature onto `main` before merging, the eventual merge can fast-forward, leaving `main` perfectly linear.


Then there's
interactive rebase (`git rebase -i`), which is the feature I'd miss most if it vanished. It lets you rewrite your own commits before you share them: squash five "wip" commits into one clean commit, reword a bad message, reorder things, or drop a commit you never meant to keep. I use it constantly to turn the messy reality of how I actually wrote a feature into a tidy story that's easy to review. A reviewer who sees three logical commits instead of twenty "fix typo" commits will quietly thank you. The Atlassian merging-vs-rebasing guide has good diagrams if you want to watch the graph transform step by step.

The Golden Rule and How I Actually Work

Here's the one rule that matters most: never rebase a branch that other people have already pulled. If a branch is local and private — you haven't pushed it, or nobody else is touching it — rebase all you want. The moment it's shared, rebasing rewrites the commits other people's work is now based on, and when they pull, Git sees diverged history and starts duplicating commits. That's the afternoon I lost. The short form: merge shared branches, rebase private ones.

So the workflow I settle on for most projects looks like this:


- While building a feature, I periodically run `git rebase main` (or `git pull --rebase`) on my *local* branch to keep it current with `main`. That keeps a pile of "merge main into feature" commits from cluttering my branch. - Right before I open the pull request, I do a quick interactive rebase to squash and reorder my commits into a sensible sequence. - Once the feature is reviewed and ready, it goes into `main` with a merge — often `--no-ff`, so there's a clear record that a whole feature landed at that point.


That combination gives me clean feature branches *and* an honest record on `main`. Plenty of teams pick one side and standardize instead — some ban rebase entirely for safety, some squash-merge everything — and that's completely fine. Consistency across the team matters more than which camp you land in. If your team already runs a specific package-manager and release flow, fold this into it; I covered some of those tooling trade-offs in
npm vs Yarn vs pnpm.

One last practical note. If a rebase goes sideways mid-replay because of a conflict, don't panic. You fix the conflicting files, `git add` them, and run `git rebase --continue`. If you'd rather bail out completely, `git rebase --abort` puts everything back exactly how it was before you started. Knowing that abort is always there is what makes rebase feel safe enough to experiment with on a local branch in the first place.


It's also worth saying that the merge-versus-rebase debate gets more heated than it deserves. Both produce the same *code* in the end — the files on disk are identical whichever route you take. The only thing that differs is the shape of the history, and that's a readability preference, not a correctness one. So when a teammate is dogmatic about it, I try to remember the stakes are low: pick a convention, write it down, and move on to the actual work. The commands are tools, not a moral position, and the best choice is the one your whole team applies the same way every time.

Frequently Asked Questions

Is git rebase safe to use?

On a local, unshared branch, yes — it's safe and genuinely useful. The danger only shows up when you rebase a branch other people have already pulled, because rewriting those commits creates diverged history and duplicate commits for everyone else. Stick to the golden rule (never rebase shared branches) and rebase becomes a low-risk way to keep your work tidy. And if a rebase goes wrong, git rebase --abort restores the exact state you started from.

Does rebase delete my commits?

Not in the sense of losing your work — it replaces each commit with a new one that has the same changes but a different hash. The old commits are still recoverable through git reflog for a while, so a botched rebase is almost always fixable. What actually changes is the commit identity and the branch's base point, which is why the history looks linear afterward.

What does git merge --no-ff do?

It forces Git to create a merge commit even when a fast-forward would have been possible. Without it, if main hasn't moved, Git just slides the pointer forward and you get no record that a feature branch ever existed. Teams use --no-ff when they want every feature to leave a visible marker in the history showing exactly when it landed.

Should beginners use merge or rebase?

Start with merge. It's non-destructive, hard to get badly wrong, and it keeps an honest record of what happened, which is exactly what you want while you're still learning how Git thinks. You can add rebase to your toolkit later, once fast-forwards and commit hashes feel natural. The other [developer explainers](/blog) on the site build up the fundamentals that make rebase click faster.

How do I undo a rebase that went wrong?

If you're still mid-rebase, git rebase --abort cancels it and restores your branch to its pre-rebase state. If the rebase already finished and you want the old version back, run git reflog to find the commit hash your branch pointed at before, then git reset --hard to that hash. The reflog is your safety net — Git remembers where your branches used to point even after history is rewritten.

Try ToolsFuel

23+ free online tools for developers, designers, and everyone. No signup required.

Browse All Tools