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.
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
| Pros | Cons |
|---|---|
| Preserves the true branching history | Merge commits add noise to git log on busy projects |
| Safe — never rewrites existing SHAs | History 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
| Pros | Cons |
|---|---|
Linear, easy-to-read git log | Rewrites SHAs — dangerous if others have your commits |
| No merge commit noise | Conflicts must be resolved commit-by-commit |
Makes git bisect and git log --oneline cleaner | Obscures the true parallelism of the work |
Side-by-side diagram
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
| Situation | Reach for |
|---|---|
| Syncing a local feature branch with main before opening a PR | git rebase main |
| Merging a finished PR into main on GitHub | git merge (or a squash merge via the UI) |
| Combining noisy WIP commits before pushing | git rebase -i |
| Long-lived shared branches that multiple people push to | git merge |
| You want a permanent record that two features were developed in parallel | git 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 feature — Add 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
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.
Practice this in an interview
All questionsDVC (and lakeFS) version raw datasets and model artifacts as immutable snapshots tied to Git commits, giving reproducibility and rollback. A feature store manages computed features for training and serving, its main job being to keep offline and online feature definitions in sync to prevent training-serving skew. They are complementary: DVC answers what data made this model, while a feature store answers how do I serve the same features consistently.
A git commit captures code, but an ML run also depends on the exact training data, hyperparameters, environment, and randomness, none of which live in Git. Datasets are too large for Git and change independently of code, so you need a data-versioning tool like DVC or lakeFS to pin a content hash of the data to the commit. Full reproducibility means versioning code, data, config, environment, and seeds together and linking them.