Advanced Git: Rebasing Like a Pro
Rebase keeps your history tidy and your code reviews sane. You replay your feature branch on top of the latest main
, no extra merge bubbles, easier bisects, cleaner blame.
What you'll learn
- Set up a repo with
main
and a divergedfeature
branch - Update
main
, then rebasefeature
onto it - Resolve conflicts and finish cleanly
- Use interactive rebase, autosquash,
--onto
, and preserve merges
Prereqs
- Git 2.39+
- Terminal access
Set up the lab
Clone this repo: https://github.com/markcallen/git-rebase-example
# Clone the repo
git clone https://github.com/markcallen/git-rebase-example
cd git-rebase-example
# Unzip and run:
./scripts/setup_repo.sh
cd rebase-lab
# Inspect the graph
git log --oneline --graph --decorate --all
What you'll see:
main
changedapp.py
to say "Hello from MAIN..."feature/awesome-change
changed the same line to "Hello from
FEATURE..."- A conflict is guaranteed, perfect for practice.
Basic rebase (the everyday move)
Replay feature commits on top of main
:
git checkout feature/awesome-change
git rebase main
Git will pause at the conflicting commit. Open app.py
, decide how the message should read, then:
git add app.py
git rebase --continue
Repeat if there's another conflict. When it finishes, your feature now sits neatly on top of main
.
Abort if needed
git rebase --abort
Push safely after a rebase
Rewriting history changes commit IDs. Use a safe force:
git push --force-with-lease origin feature/awesome-change
It refuses to overwrite remote work you don't have locally.
Conflict ergonomics
- See what will replay:
git log --oneline --decorate --graph main..feature/awesome-change
- Stashed changes? Let Git help:
git rebase --autostash main
- Bad step? Skip or abort:
git rebase --skip
git rebase --abort
Interactive rebase: edit, reorder, squash
Polish history before it lands.
# Edit the last 3 commits on the current branch
git rebase -i HEAD~3
In the editor:
reword
to change a messagesquash
/fixup
to combine commits- Reorder lines to reorder commits
edit
to pause and modify code, thengit add -A && git rebase --continue
Autosquash (fast cleanup)
Mark follow-up commits as fixup!
/squash!
and let Git auto-place
them:
git commit --fixup <hash>
git rebase -i --autosquash main
Power move: --onto
Transplant a range of commits onto a different base. Think "take commits
after A from feature
and drop them onto release/1.2
."
git rebase --onto release/1.2 A feature
Syntax recap: --onto <newbase> <upstream> <branch>
Keep merge structure (when you must)
If your feature branch contains merges you want to preserve:
git rebase --rebase-merges main
This rebuilds the merge graph on top of main
.
Team hygiene that prevents pain
- Prefer
--force-with-lease
over--force
- Don't rebase shared branches without agreement
- Enable conflict reuse:
git config rerere.enabled true
- CI: protect
main
, optionally require linear history
Cheatsheet
# Basic rebase
git checkout feature && git rebase main
# Interactive N commits
git rebase -i HEAD~N
# Autosquash fixup! commits
git commit --fixup <hash>
git rebase -i --autosquash main
# Abort / continue / skip
git rebase --abort
git rebase --continue
git rebase --skip
# Rebase onto another base
git rebase --onto <newbase> <upstream> <branch>
# Preserve merges
git rebase --rebase-merges main
# Safe push after rebase
git push --force-with-lease