datarekha

Rebase vs merge

Should you merge main into your feature branch or rebase onto it? Learn what each does to history and when to pick which.

9 min read Intermediate Git Lesson 10 of 15

What you'll learn

  • What rebase does: it replays your commits on top of another branch, rewriting their SHAs into a linear chain
  • When to choose merge (preserve true history) vs rebase (clean linear log)
  • Interactive rebase with git rebase -i: squash, reword, and drop commits before sharing

Before you start

The last lesson promised this: a second way to fold a branch in, one that leaves no merge-commit bubble behind. It is called rebase, and it does exactly what was hinted — it replays your commits on top of the latest main so history reads as a straight line. The catch is that “replay” means rewriting your commits, which is why rebase is both loved and feared. Here is precisely what it does, when to reach for it over merge, and the one rule that keeps it safe.

The setup: two branches that have diverged

Suppose main has moved forward since you branched off. Your feature branch has two commits (F1, F2) and main has gained one (M1):

main:    A --- B --- M1
                \
feature:         F1 --- F2

You need to incorporate M1 into your feature branch. You have two options.


Option 1: Merge

Running git merge main from your feature branch creates a new merge commit (Mc) that has two parents — the tip of your branch and the tip of main.

# from your feature branch
git merge main
main:    A --- B --- M1
                \       \
feature:         F1 --- F2 --- Mc

The merge commit records exactly when and how the two lines of work joined. That is the entire point: the history is a faithful record of what actually happened.

Merge trade-offs

ProsCons
Preserves the true branching historyMerge commits add noise to git log on busy projects
Safe — never rewrites existing SHAsHistory can look tangled with many parallel branches
Ideal for long-lived, shared branches

Option 2: Rebase

Rebase (short for “re-apply onto a new base”) takes each of your branch’s commits and replays them — one by one — on top of the tip of the target branch. Each replayed commit gets a brand-new SHA because its parent has changed.

# from your feature branch
git rebase main

Git detaches your commits and re-applies them:

Before:
main:    A --- B --- M1
                \
feature:         F1 --- F2

After rebase:
main:    A --- B --- M1
                         \
feature:                  F1' --- F2'

F1' and F2' contain the same diffs as F1 and F2, but they have new SHAs and M1 is their ancestor. There is no merge commit — the result is a linear history.

Rebase trade-offs

ProsCons
Linear, easy-to-read git logRewrites SHAs — dangerous if others have your commits
No merge commit noiseConflicts must be resolved commit-by-commit
Makes git bisect and git log --oneline cleanerObscures the true parallelism of the work

Side-by-side diagram

git merge mainABM1mainF1F2Mcmergegit rebase mainABM1mainF1ʼnew SHAF2ʼnew SHALinear history — no merge commit
Left: merge preserves both lines of history with a merge commit (Mc). Right: rebase replays commits on top of main, producing a single linear chain with new SHAs (F1ʼ, F2ʼ).

Resolving conflicts during a rebase

Because rebase replays commits one at a time, a conflict can appear at any replay step. Git pauses and tells you which commit it is applying:

git rebase main
# CONFLICT (content): Merge conflict in src/auth.ts
# error: could not apply f1a3c2e... Add login validation

Fix the conflict in your editor, stage the resolved file, then continue:

# after editing the conflicted file
git add src/auth.ts
git rebase --continue

Git moves on to the next commit and repeats until done. If you want to bail out entirely:

git rebase --abort   # restores the branch to its pre-rebase state

The Golden Rule


Interactive rebase: tidying up before you share

git rebase -i (interactive rebase) lets you rewrite the history of your own local commits before pushing. You can squash noisy “WIP” commits into one clean commit, reword a message, or drop an accidental commit entirely.

# rewrite the last 3 commits interactively
git rebase -i HEAD~3

Git opens an editor with a list of commits and action keywords:

pick a1b2c3f Add login validation
pick 9d4e5f6 WIP: fix typo
pick 3c7a8b9 Fix edge case in token expiry

# Commands:
# p, pick   = use commit as-is
# r, reword = use commit but edit the message
# s, squash = fold into the previous commit
# d, drop   = remove the commit entirely

Change pick to squash on the second line and save:

pick a1b2c3f Add login validation
squash 9d4e5f6 WIP: fix typo
pick 3c7a8b9 Fix edge case in token expiry

Git combines the first two commits and prompts you for a combined message. The result is a cleaner two-commit history ready to push as a PR.

Interactive rebase is powerful precisely because it operates only on local, un-pushed commits — which is exactly where the Golden Rule permits rewriting.


When to use which

SituationReach for
Syncing a local feature branch with main before opening a PRgit rebase main
Merging a finished PR into main on GitHubgit merge (or a squash merge via the UI)
Combining noisy WIP commits before pushinggit rebase -i
Long-lived shared branches that multiple people push togit merge
You want a permanent record that two features were developed in parallelgit merge

In one breath

Merge and rebase both bring a branch up to date; they differ only in the shape of the history they leave. Merge creates a merge commit with two parents — a faithful, never-rewritten record that two lines converged, at the cost of a bubblier git log. Rebase replays your commits one by one on top of the target’s tip, giving each a new SHA and producing a clean linear history with no merge commit — but conflicts surface commit-by-commit, and those new SHAs are the danger. The Golden Rule is absolute: never rebase commits others have already pulled, because rewriting shared SHAs forks history into duplicates. Rebase freely on local-only commits — which is exactly where git rebase -i shines, letting you squash, reword, and drop WIP commits into a clean story before you push.

Practice

Before the quiz, decide and defend: you have three local commits on featureAdd login, WIP, fix typo — none pushed yet, and main has moved ahead. You want main’s latest changes and a tidy one-commit PR. Which two commands, in which order, get you there — and would your answer change if a teammate had already pulled your feature branch this morning? Say exactly why.

Quick check

0/3
Q1After running `git rebase main` on your feature branch, what is true about your branch's commits?
Q2A colleague pulled your feature branch yesterday. You then ran `git rebase main` and force-pushed the branch. What problem does this cause?
Q3Your team uses short-lived feature branches and squashes them when merging to main. A new teammate suggests always using `git merge` with `--no-ff` on the feature branch itself (to keep a record of every WIP commit in main's history). What is the strongest argument against this for your workflow?

A question to carry forward

Notice the one thing merge, rebase, and conflict resolution all quietly demanded: a clean working tree. Git will not let you switch branches or start a rebase while half-finished edits are lying around — it refuses rather than risk clobbering them. Which raises a very practical problem you will hit on your first real workday: you are deep in a messy, not-yet-committable change on feature, and a production bug on main needs a fix right now. You cannot commit broken work, and you cannot afford to lose it. So where do you park a pile of dirty changes for ten minutes while you deal with something else — and get them back exactly as they were? That shelf has a name, git stash, and it closes the chapter in the next lesson.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions

Related lessons

Explore further

Glossary terms
Cheat sheets
Skip to content