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 diverged feature branch
  • Update main, then rebase feature 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 changed app.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 message
  • squash/fixup to combine commits
  • Reorder lines to reorder commits
  • edit to pause and modify code, then git 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