TechEarl

git merge vs git rebase: Which Should You Use?

git merge keeps the true history and adds a merge commit; git rebase rewrites your commits onto a new base for a clean, linear log. Here is when each fits.

Ishan Karunaratne⏱️ 8 min readUpdated
Share thisCopied
git merge vs git rebase compared: when to use a merge commit and when to rebase for a linear history

Both git merge and git rebase integrate changes from one branch into another. The difference is what they do to history. git merge preserves the true history and ties the two branches together with a new merge commit. git rebase rewrites your commits so they sit on top of the other branch, producing a straight, linear history with no merge commit. The one rule that overrides everything else: never rebase commits you have already pushed and shared.

If you only remember one thing, remember that. The rest of this page explains why, and how to choose between them the rest of the time.

The core difference in one picture

Say you branched off main to build a feature, and while you worked, someone else pushed two commits to main. Your history has diverged: your branch and main share a common ancestor but have moved apart.

git merge joins them by creating a new commit with two parents:

bash
git checkout main
git merge feature

The log now has a fork and a join. The merge commit records the moment the two lines of work came back together, and every original commit keeps its real timestamp and parent.

git rebase does something different. It takes each commit on your feature branch, sets it aside, moves your branch to the tip of main, then replays your commits one by one on top:

bash
git checkout feature
git rebase main

There is no merge commit. Your commits now appear as if you had started your work from the latest main all along. The history is a single straight line.

merge vs rebase at a glance

git mergegit rebase
History shapeBranched, with a join pointLinear, straight line
Merge commitYes, one per mergeNo
Original commitsPreserved exactly (same SHAs)Rewritten (new SHAs)
When it happenedTrue chronological orderReplayed onto the new base
Safe on shared commitsYesNo, this rewrites history
Conflict handlingResolve once, in one commitResolve per replayed commit
Easy to undoYes (revert the merge commit)Harder (needs the reflog)
Best forIntegrating shared/long-lived branchesCleaning up your own local work

The takeaway from the table: merge is the honest record, rebase is the tidy story. Neither is wrong. They optimize for different things.

When to use git merge

Reach for merge when the branch has been shared, or when you want the history to record that a feature was developed in parallel.

  • The branch is public. If you or anyone else has pushed the branch and others may have based work on it, merging is the safe integration. It adds new commits and changes nothing that already exists.
  • You want an honest audit trail. A merge commit is a real event: "this feature landed here." On a team that reviews pull requests, that merge commit is a useful anchor that ties the work to the review.
  • You want easy reversibility. A bad feature merge can be undone by reverting the single merge commit. See git reset vs git revert for which undo tool fits a shared branch (hint: it is revert, not reset).

The cost is a busier graph. A repo with dozens of contributors and no rebasing develops the classic tangled, railroad-track log. For many teams that is an acceptable trade for never rewriting shared history.

When to use git rebase

Reach for rebase to clean up your own local commits before you share them, and to keep a feature branch up to date with main without littering it with merge commits.

  • Tidy your local branch before pushing. You made five "wip" commits and a typo fix. Rebasing (especially interactive rebase) lets you reorder, reword, and combine them into a clean sequence before anyone sees them. That is the topic of squashing commits with interactive rebase.
  • Keep a feature branch current. Instead of merging main into your feature repeatedly (which sprinkles merge commits through your branch), git rebase main replays your work on the latest main and keeps the branch linear.
  • You want a readable, bisectable history. A linear log is easier to read, and git bisect works cleanly on it because every commit is a real, ordered step.

The cost is that rebasing rewrites commits. Every replayed commit gets a new SHA, because its parent changed. That is harmless on commits only you have, and dangerous on commits other people already have.

The golden rule: never rebase shared commits

This is the one mistake that turns rebase from useful into destructive.

When you rebase, Git does not move your commits. It creates new commits with the same content but different SHAs, then abandons the originals. If those original commits exist only in your local repo, nobody notices. But if you already pushed them and a teammate pulled them, their repo still has the old SHAs while yours now has new ones. Your histories have silently forked, and the next push or pull turns into a mess of duplicate commits and conflicts. The first concrete symptom is usually a non-fast-forward push that Git refuses outright.

So the rule is simple:

Rebase commits that live only in your local repository. Never rebase commits you have already pushed to a shared branch.

If you have already pushed a branch and you rebase it anyway, the only way to update the remote is a force push:

bash
git push --force-with-lease

--force-with-lease is safer than a bare --force: it refuses to overwrite the remote if someone else pushed in the meantime, so you do not silently clobber a teammate's work. But even with the lease, force-pushing a branch other people work on is something you do only by agreement, never as a reflex. On a protected main it should be impossible anyway; see how to protect your main branch.

If you do rebase or reset something you wish you had not, your old commits are not gone immediately. Git keeps them in the reflog for a while, which is how you can recover lost commits with git reflog.

A safe rebase workflow

Here is the pattern I use to keep a feature branch current and clean without touching anything shared.

First, make sure I have the latest main. If you are unsure of the difference between updating your remote-tracking refs and merging them in, read git pull vs git fetch.

bash
git checkout main
git pull

Then move my feature branch onto the fresh main. A dirty working tree blocks the rebase, so if I have uncommitted changes I stash my work-in-progress first to park them out of the way:

bash
git checkout feature
git rebase main

If a conflict comes up, Git pauses on the offending commit. I fix the files, stage them, and continue. Staging with the staging area and git add is how I tell Git the conflict is resolved and the replayed commit can use my edited version:

bash
git add path/to/file
git rebase --continue

To bail out and put everything back the way it was before the rebase started:

bash
git rebase --abort

Resolving conflicts during a rebase uses the same skills as resolving them during a merge; the mechanics are covered in how to resolve merge conflicts in Git. The difference is that a rebase can ask you to resolve a conflict on each replayed commit, where a merge asks once.

So which should you use?

A practical default that works for most small teams:

  • Rebase your local feature branch to keep it current and to clean up your commits before you open a pull request. This is private work, so rewriting is safe and the result is a tidy, reviewable branch.
  • Merge the pull request into main. The merge commit marks where the feature landed and keeps shared history append-only.

That gives you a clean per-feature history and an honest integration history at the same time. It pairs naturally with a pull-request flow: read how to create a pull request for the other half, and Git workflows for teams for how merge and rebase fit into GitHub Flow, Git Flow, and trunk-based development.

If branches still feel abstract, the foundation is Git branching explained for beginners. And whenever any of this feels overwhelming, the Git for beginners hub walks through the whole picture from the start.

Sources

Authoritative references this article was fact-checked against.

Tagsgit merge vs rebaseGitVersion Controlmergerebasebranching

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

grep vs ripgrep vs ag: Which Search Tool to Use

grep is on every system and searches exactly what you point it at. ripgrep (rg) is the fast Rust-based default for code search: it skips .gitignore'd, hidden, and binary files unless told otherwise. ag (the_silver_searcher) was the older fast-grep, now largely superseded by ripgrep. This breaks down speed, defaults, regex engines, and exactly when to reach for each one.