Interactive Rebase
Interactive rebase is one of Git's most powerful features for crafting a clean, logical commit history.
Basic Interactive Rebase
# Rebase last 5 commits
git rebase -i HEAD~5
# Rebase from a specific commit
git rebase -i abc123
# Rebase onto main
git rebase -i main
Rebase Commands
# In the editor, you'll see:
pick abc1234 First commit
pick def5678 Second commit
pick ghi9012 Third commit
# Available commands:
# p, pick = use commit as-is
# r, reword = use commit, but edit message
# e, edit = use commit, but stop for amending
# s, squash = meld into previous commit
# f, fixup = like squash, but discard message
# x, exec = run shell command
# b, break = stop here (continue with 'git rebase --continue')
# d, drop = remove commit
# l, label = label current HEAD
# t, reset = reset HEAD to a label
Squashing Commits
# Start interactive rebase
git rebase -i HEAD~4
# In editor, change:
pick abc1234 Add feature
pick def5678 Fix typo
pick ghi9012 More fixes
pick jkl3456 Final polish
# To:
pick abc1234 Add feature
squash def5678 Fix typo
squash ghi9012 More fixes
squash jkl3456 Final polish
# Then edit the combined commit message
Reordering Commits
# Simply reorder lines in the editor
git rebase -i HEAD~4
# Change from:
pick abc1234 Add CSS
pick def5678 Add HTML
pick ghi9012 Add JavaScript
# To (reorder):
pick def5678 Add HTML
pick abc1234 Add CSS
pick ghi9012 Add JavaScript
Editing a Commit
# Mark commit for editing
git rebase -i HEAD~3
# Change 'pick' to 'edit':
edit abc1234 Commit to modify
pick def5678 Next commit
# Git stops at that commit
# Make changes...
git add .
git commit --amend
# Continue rebase
git rebase --continue
Splitting a Commit
# Mark commit for editing
git rebase -i HEAD~2
# Change to 'edit'
edit abc1234 Big commit to split
# Reset to before the commit
git reset HEAD^
# Create multiple commits
git add file1.js
git commit -m "Add file1"
git add file2.js
git commit -m "Add file2"
git rebase --continue
Autosquash
# Create fixup commits
git commit --fixup=abc1234
git commit --squash=def5678
# Rebase with autosquash
git rebase -i --autosquash HEAD~5
# Or enable globally
git config --global rebase.autosquash true
Using exec
# Run tests after each commit
git rebase -i HEAD~5
# Add exec commands:
pick abc1234 First commit
exec npm test
pick def5678 Second commit
exec npm test
# Or add exec after every pick
git rebase -i HEAD~5 --exec "npm test"
Handling Conflicts
# When conflicts occur
git status # See conflicting files
# Edit files to resolve conflicts
git add resolved-file.js
git rebase --continue
# Or abort if needed
git rebase --abort
# Skip problematic commit
git rebase --skip
Rebase Preserving Merges
# Preserve merge commits during rebase
git rebase -i --rebase-merges main
# In editor, you'll see labels:
label onto
reset onto
pick abc1234 Commit 1
label branch-a
reset onto
pick def5678 Commit 2
merge -C merge123 branch-a # Merge branch-a
Best Practices
# Never rebase shared/pushed commits!
# Only rebase local, unpushed work
# Create backup branch before complex rebase
git branch backup-branch
# Use reflog to recover if things go wrong
git reflog
git reset --hard HEAD@{2}
# Preview what will be rebased
git log --oneline main..HEAD
Rebase vs Merge
| Rebase | Merge |
|---|---|
| Linear history | Preserves branch history |
| Cleaner log | Shows when branches merged |
| Rewrites history | Non-destructive |
| Use for local branches | Use for shared branches |