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:
git checkout main
git merge featureThe 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:
git checkout feature
git rebase mainThere 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 merge | git rebase | |
|---|---|---|
| History shape | Branched, with a join point | Linear, straight line |
| Merge commit | Yes, one per merge | No |
| Original commits | Preserved exactly (same SHAs) | Rewritten (new SHAs) |
| When it happened | True chronological order | Replayed onto the new base |
| Safe on shared commits | Yes | No, this rewrites history |
| Conflict handling | Resolve once, in one commit | Resolve per replayed commit |
| Easy to undo | Yes (revert the merge commit) | Harder (needs the reflog) |
| Best for | Integrating shared/long-lived branches | Cleaning 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, notreset).
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
maininto your feature repeatedly (which sprinkles merge commits through your branch),git rebase mainreplays your work on the latestmainand keeps the branch linear. - You want a readable, bisectable history. A linear log is easier to read, and
git bisectworks 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:
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.
git checkout main
git pullThen 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:
git checkout feature
git rebase mainIf 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:
git add path/to/file
git rebase --continueTo bail out and put everything back the way it was before the rebase started:
git rebase --abortResolving 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.
- Pro Git, Git Branching: Rebasinggit-scm.com
- git-rebase, official Git documentationgit-scm.com
- git-merge, official Git documentationgit-scm.com





