Skip to content
CAMPUX Cloud Bootcamp
Field notes · Version control
Git merge vs git rebase

Git merge vs git rebase: when to use which

By Captain O7 min read

Two commands, one job: bring the work on one branch together with another. They get you to the same files but leave behind very different histories, and picking the wrong one on a shared branch is how you ruin a teammate's afternoon.

New to cloud? CAMPUX is a free, build-first course. Start here →

git merge combines two branches with a merge commit, preserving the exact history of both — non-destructive but noisier. git rebase moves your branch's commits on top of another branch's tip, producing a linear history but rewriting commit hashes. Rule of thumb: rebase local work, never rebase shared or pushed branches. That sentence is the whole article in miniature. The rest is why it is true and what the commands actually do.

git merge joins branches with a merge commit; git rebase replays commits into a linear history with new hashes.git mergegit rebasemerge commitfeaturereplayed, linear (new hashes)
Figuremerge keeps both histories and ties them together with a merge commit — honest but branchy. rebase replays your commits on top of the target branch for a clean, linear history, but it rewrites their hashes. The golden rule: rebase your own local work, never a branch others have pulled.

The short answer

Both commands take the commits from one branch and integrate them with another. The difference is what they do to the record. Merge is honest to a fault: it writes a new commit that ties the two branch tips together and keeps every original commit exactly where it happened, forked line and all. Rebase is a tidier: it lifts your commits off where they started and replays them one by one on top of the other branch, so the log comes out as a single straight line, as if you had done your work after everyone else's all along.

Neither is a trick. Merge keeps the truth and pays for it with a busier graph. Rebase buys a clean graph and pays for it by rewriting commits — which is harmless on your own machine and a real problem once those commits are out in the world. Hold that trade-off in mind and every rule below follows from it.

What git merge does (the merge commit)

Say you branched feature off main, did a few commits, and meanwhile someone else pushed to main. To bring their work into yours, you check out your branch and merge:

git checkout feature
git merge main

Because the two branches have both moved on since they split, Git cannot just slide one pointer forward. Instead it creates a merge commit — a special commit with two parents, one pointing back into main and one into feature — and that commit becomes the new tip. Your original commits keep their hashes and their place in history. Nothing is rewritten. If you run git log --graph afterward you will see the two lines diverge and then rejoin at the merge commit.

There is one case where merge does not add a commit at all. If the branch you are merging in has not diverged — main is simply a few commits behind your branch with no new work of its own — Git does a fast-forward and just moves the pointer up. No merge commit, because there is nothing to reconcile. You can force a merge commit anyway with git merge --no-ff when you want the branch's shape recorded on purpose.

The strength of merge is that it never lies about how the code came to be. The cost is that a repository with dozens of active branches accumulates a lot of merge commits, and reading the history can feel like tracing a subway map.

What git rebase does (linear history, new hashes)

Rebase solves the same "my branch is behind" problem from the other direction. Instead of pulling main into your branch with a merge commit, you re-base your branch onto the current tip of main:

git checkout feature
git rebase main

Git figures out which commits are unique to feature, sets them aside, fast-forwards your branch to the tip of main, and then replays your commits one at a time on top. The result is a straight line: main's history, then your commits, no fork. The log reads as if you started your work this morning against the latest code.

Here is the subtle part. A commit's hash is computed from its content and its parent. When rebase replays your commits onto a new base, each one has a new parent, so each one gets a new hash. They carry the same diff and message, but as far as Git is concerned they are new commits. The originals are left behind, unreferenced. This is why people say rebase "rewrites history" — it does, literally, mint new commits in place of the old ones.

Rebase is also the tool for cleaning up your own commits before anyone sees them. Interactive rebase lets you squash, reorder, reword, or drop commits:

git rebase -i HEAD~3

That opens the last three commits in an editor where you mark what to do with each. It is the standard way to turn six scrappy work-in-progress commits into two clean ones before review. Worth noting: -i needs an interactive editor, so it will not run in a non-interactive setting like this article's build environment or a CI script — you use it at your own terminal. Everything else here runs fine unattended.

Merge records what happened. Rebase records what you wish had happened. Both are useful; only one is safe to point at shared history.

The golden rule: never rebase shared history

This is the one rule that matters more than all the style preferences: do not rebase commits that other people have already pulled. The reason falls straight out of the hash rewriting above.

When you rebase, your old commits are replaced by new ones with new hashes. If those old commits only ever lived on your machine, nobody notices — there is nothing to disagree with. But if you had already pushed them and a teammate pulled them, their repository now holds the originals while yours holds the rewritten copies. To Git these are two different lines of history that happen to contain the same changes. Your teammate's next pull turns into a mess of phantom conflicts and duplicated commits, and the only way to push your rewritten branch is a force-push that can overwrite work they based on the originals.

So the boundary is simple: rebase is for private commits. The moment a branch is shared — pushed to a branch others build on, or merged into main — treat its history as frozen and use merge to integrate further changes. If you truly must rewrite a shared branch, coordinate with everyone who has it and use git push --force-with-lease, which at least refuses to clobber commits you have not seen. That is a break-glass move, not a habit.

One friendly, everyday use of rebase that stays inside the rule is pulling with rebase:

git pull --rebase

Instead of creating a merge commit every time you sync a branch you are actively working on, this replays your local, unpushed commits on top of what you just fetched. Because the commits it rewrites are ones you have not shared yet, it is safe, and it keeps the history free of tiny "Merge branch main" commits. Many teams set git config --global pull.rebase true to make it the default.

A comparison table and team conventions

git mergegit rebase
HistoryPreserved exactly; branches fork and rejoin at a merge commitRewritten into a single linear line
Commit hashesUnchangedNew hashes for every replayed commit
Adds a commit?Yes, a merge commit (unless fast-forward)No extra commit; existing ones are recreated
Safety on shared branchesSafe — nothing is rewrittenUnsafe — never rebase what others have pulled
Reads likeThe real, sometimes tangled storyA clean, tidy narrative
Best forIntegrating branches others rely on; recording when work mergedUpdating your own branch onto main; polishing local commits before review

Teams tend to land on one of a few conventions, and any of them works as long as everyone follows it. A common one: rebase your feature branch onto main to keep it current and clean while you work, then merge it into main with a merge commit (or a squash) when it is done — private commits get rebased, the integration point gets a merge. Others rebase everything for a perfectly linear main; others merge everything and accept the busier graph in exchange for never thinking about the golden rule. The wrong move is mixing them without agreement, so the first thing to do on a new team is ask which way they lean.

A quick way to remember it

Rebase before you share, merge after. While your commits are still on your laptop, rebase freely — clean them up, catch up to main, make them read well. The instant they are pushed and someone else could be building on them, switch to merge and leave the past alone.

Handling conflicts in each

Both commands hit conflicts for the same reason: two changes touched the same lines. How you work through them differs.

With merge, all the conflicts surface at once. Git stops, marks the conflicted files, and waits. You edit each file to resolve the markers, git add the fixed files, and finish with a single commit:

git merge main
# fix the conflicted files, then
git add <files>
git commit

One conflict-resolution pass, one merge commit, done. If you decide you would rather not merge at all, git merge --abort puts you back exactly where you started.

With rebase, conflicts come one commit at a time, because Git is replaying your commits in order. It applies a commit, and if that commit conflicts it pauses so you can fix it. You resolve, git add the files, and then tell the rebase to keep going — you do not run git commit here, because the commit already exists and is being replayed:

# after fixing the conflicted files
git add <files>
git rebase --continue

If the same lines were touched across several of your commits, you may resolve a conflict more than once as each commit replays — that is the tax rebase charges for a linear history. Two escape hatches: git rebase --skip drops the current commit and moves on, and git rebase --abort cancels the whole operation and restores your branch to how it was before you started. Because a normal rebase leaves the original commits reachable, git reflog can also rescue you if you finish a rebase and then realise you preferred the old shape.

Where this fits

If you have both commands in your hands and you know which histories are private and which are shared, you already make the right call ninety percent of the time. The remaining ten percent is judgement about how much your team cares about a clean log versus a faithful one, and that is a conversation, not a command. Get the golden rule into your fingers first — rebase local, merge shared — and the rest is preference.

Questions people also ask

Is git rebase better than git merge?

Neither is better in the abstract; they answer different needs. Rebase gives you a clean, linear history and is ideal for tidying your own local commits before you share them. Merge preserves the true shape of history, including when two branches diverged and rejoined, and is the safe default for anything other people have already pulled. Most teams use both: rebase to polish local work, merge to integrate it.

When should I use git rebase?

Use rebase on commits that live only on your machine. The common cases are updating a feature branch onto the latest main before you open a pull request, and cleaning up your own messy commits with git rebase -i before review. Both rewrite history you have not shared yet, which is exactly when rewriting is safe. Once you have pushed and others may have pulled, stop rebasing.

Why is rebasing a shared branch dangerous?

Rebase replays your commits as new commits with new hashes, so it does not move the old commits, it replaces them. If a teammate already pulled the originals, their history and yours now disagree about the same work. Git sees two divergent lines, they get confusing merge conflicts and duplicated commits, and the usual fix is a force-push that can clobber their changes. That is why you never rebase branches others have pulled.

Does git rebase delete commits?

Not exactly. A normal rebase creates new commits with the same changes and new hashes, and the original commits are left unreferenced rather than erased. They stay in your local repository and the reflog for a while, so git reflog can help you recover a rebase that went wrong. Interactive rebase can genuinely drop a commit if you delete its line, but even then the old version is reachable through the reflog until Git garbage-collects it.

Further reading — the Git docs
Your next class · free
You've read the idea. Class 19 — Branching Strategies is where you build it, hands-on — no account needed.Start Class 19 →
Captain O
Founder & instructor · CAMPUX Cloud Engineering Bootcamp
Drilled in Class 17 — Git Fundamentals and Class 19 — Branching Strategies. Read next: What is Git? → · What does git fetch do? →