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
mainand a divergedfeaturebranch - Update
main, then rebasefeatureonto 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:
mainchangedapp.pyto say "Hello from MAIN..."feature/awesome-changechanged 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 --skipgit 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:
rewordto change a messagesquash/fixupto combine commits- Reorder lines to reorder commits
editto 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-leaseover--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