Basic rebase

git rebase <base>          # replay current branch's commits on top of <base>
git rebase origin/main     # common case: bring branch up to date with remote main

Interactive rebase

git rebase -i <base>       # opens editor to squash, reorder, edit, or drop commits
git rebase -i HEAD~3       # rewrite last 3 commits

Common actions in the editor:

  • pick — keep the commit as-is
  • reword — keep the commit, edit its message
  • squash — fold into the previous commit, merging both messages
  • fixup — same as squash but discards this commit’s message
  • edit — pause rebase here so you can amend the commit
  • drop — remove the commit entirely

Rebase onto a different base (--onto)

git rebase --onto <newbase> <upstream> <branch>
  • <newbase>: where to plant the commits — any commit-ish (local branch, origin/main, SHA)
  • <upstream>: the old base to rebase away from; commits between <upstream> and <branch> are what get moved
  • <branch>: local branch being rebased

Useful when a branch was forked off a feature branch and you want to rebase it directly onto main:

git rebase --onto main feature-a feature-b

Conflict resolution

# When rebase stops at a conflict:
# 1. Fix the conflicting files
git add <resolved-files>
git rebase --continue
 
# Or skip the conflicting commit entirely
git rebase --skip

Stacked branches (chained PRs)

When PRs are chained (feature-b branched off feature-a, which branched off main), the tip branch already contains all the commits from the chain. Rebasing and merging the tip lands everything at once.

# 1. Preview what will be edited
git log master..HEAD --oneline
 
# 2. Rebase onto latest master, squash messy intermediate commits
git rebase -i master
 
# 3. Fast-forward merge — errors if history isn't linear
git merge --ff-only <branch>
 
# 4. Verify locally, then push
git push --force-with-lease   # safer than --force; fails if someone else pushed

Keep natural commit order

Reordering commits that depend on each other causes conflicts. If branches were chained (A into B into C), preserve that order in the rebase.

Pre-commit hook workarounds during rebase

Hooks run on every replayed commit. Two escape hatches:

# Skip hooks for just the amend step
git commit --amend --no-verify
 
# Skip hooks when continuing a rebase
GIT_HOOKS_PATH=/tmp/empty-hooks git rebase --continue

Recovery

# Bail out mid-rebase
git rebase --abort
 
# Nuclear undo after rebase completes
git reflog                          # find pre-rebase HEAD
git reset --hard <pre-rebase-HEAD>

Notes

  • --ff-only is the safety check — if it errors, your history isn’t linear yet
  • --force-with-lease over --force — won’t clobber a teammate’s push
  • Verify locally (run git log, test the merge) before pushing