Advanced Git Rebasing Techniques: Interactive Guide
Why Interactive Rebasing?
Interactive rebasing allows you to rewrite your commit history before sharing it with others. It's like editing a draft before publishing – you can clean up, reorganize, and polish your work.
Key Benefits: Cleaner history, logical commit grouping, fixing commit messages, removing unwanted commits, and reordering work flow.
When to Use Interactive Rebase
- Before Pull Requests: Clean up your feature branch before merging
- After Code Review: Incorporate feedback into existing commits
- Historical Cleanup: Fix old commit messages or split large commits
- Branch Maintenance: Keep long-running branches tidy
- Learning/Experimentation: Safe way to practice Git manipulation
Golden Rule: Never rebase commits that have been shared with others (pushed to remote). Rebasing rewrites history, which can cause serious issues for collaborators.
Interactive Rebase Interface
When you run git rebase -i, Git opens an editor with a list of commits and available actions:
pick b2c3d4e Fix login validation bug
pick c3d4e5f Add password reset feature
pick d4e5f6g Update documentation
# Rebase a1b2c3d..d4e5f6g onto f5g6h7i (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
Available Commands
- pick (p): Keep commit as-is (default)
- reword (r): Keep commit but edit its message
- edit (e): Pause at commit for modifications
- squash (s): Combine with previous commit
- fixup (f): Squash and discard message
- drop (d): Remove commit entirely
- exec (x): Run shell command during rebase
Step-by-Step Interactive Rebase Guide
Step 1: Start Interactive Rebase
Decide how many commits you want to edit:
git rebase -i HEAD~3
# Edit all commits since branching from main
git rebase -i main
# Edit specific commit range
git rebase -i a1b2c3d
Pro Tip: Use git log --oneline -10 to see recent commits and their hashes before starting.
Step 2: Edit the Rebase Plan
In the editor, change commands for each commit:
pick b2c3d4e Fix login validation bug
squash c3d4e5f Add password reset feature
fixup d4e5f6g Update documentation
Save and close the editor. Git will proceed through each command.
Step 3: Handle Each Action
Rewording a Commit
Git opens an editor with the current message. Edit it:
Add user auth module
# Better message
feat: implement user authentication
- Add JWT token generation
- Implement login endpoint
- Add password hashing
- Write authentication tests
Squashing Commits
Git combines commits and asks for a new message:
# This is the 1st commit message:
Add password reset feature
# This is the commit message #2:
Update documentation
# Please enter the commit message for your changes...
Best Practice: When squashing, write a comprehensive message that covers all changes.
Editing a Commit
Git pauses at the commit. Make your changes:
Stopped at a1b2c3d... Add user auth module
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Step 4: Complete the Rebase
After all actions are processed, verify the result:
git log --oneline -5
# Verify nothing is broken
git status
npm test
Advanced Squashing Strategies
1. Grouping Related Changes
Combine commits that belong together logically:
squash
squash
squash
pick
Result: One cohesive "Add user management feature" commit.
2. Separating Fixes from Features
Keep bug fixes separate from feature development:
fixup
fixup
pick
Result: Clean feature commits with fixes incorporated.
3. Splitting Large Commits
Use edit to split one commit into multiple:
git rebase -i HEAD~5
# Change to 'edit' for the commit to split
# When Git pauses:
git reset HEAD~
git add -p # Stage changes piece by piece
git commit -m "First logical change"
git commit -m "Second logical change"
git rebase --continue
Rebasing vs Merging: When to Choose
| Aspect | Rebasing | Merging |
|---|---|---|
| History | Linear, clean timeline | Shows actual branching/merging |
| Complexity | Higher risk if not careful | Safer, preserves all history |
| Use Case | Feature branches before PR | Long-lived branches, releases |
| Team Size | Small teams, experienced users | Large teams, beginners |
| Conflict Resolution | Resolve once per commit | Resolve once at merge |
Modern Approach: Many teams use rebase for feature branches (keeps history clean) and merge for main branch integration (preserves merge context).
Practical Workflows
1. Pre-Pull Request Cleanup
git fetch origin
git rebase origin/main
git rebase -i HEAD~10 # Clean up last 10 commits
# Squash WIP commits, reword messages, reorder logically
git push -f # Force push cleaned branch
2. Incorporating Code Review Feedback
git rebase -i main
# Mark relevant commits as 'edit'
# At each edit stop, make changes
git add .
git commit --amend
git rebase --continue
3. Fixing Historical Issues
git log --oneline --grep="typo"
# Rebase from before that commit
git rebase -i a1b2c3d^ # ^ means parent
# Change 'pick' to 'reword' or 'edit'
git push -f # Only if not shared!
Advanced Techniques
1. Using Exec for Automation
Run commands during rebase for consistency checks:
pick a1b2c3d Add feature A
exec npm test
pick b2c3d4e Add feature B
exec npm run lint
pick c3d4e5f Add feature C
exec npm test
2. Reordering Commits
Simply change the order in the rebase plan:
pick
pick
3. Rebase with Merge Conflicts
Handle conflicts during rebase:
# 1. Resolve conflicts in files
git status # See conflicted files
git add resolved-file.js
# 2. Continue rebase
git rebase --continue
# 3. Or skip/skip this commit
git rebase --skip
# 4. Or abort entirely
git rebase --abort
Command Reference
Basic Rebase Commands
git rebase main
# Interactive rebase last 5 commits
git rebase -i HEAD~5
# Rebase with auto-stashing
git rebase --autostash main
# Continue after conflict resolution
git rebase --continue
Advanced Options
git rebase main feature-branch
# Keep empty commits
git rebase --keep-empty
# Preserve merges during rebase
git rebase -p main
# Verify signatures
git rebase --verify-signatures
Recovery Commands
git rebase --abort
# View rebase status
git status
# Find original branch tip
git reflog
git reset --hard ORIG_HEAD
Frequently Asked Questions
Git provides several recovery options:
- During rebase: Use
git rebase --abortto return to pre-rebase state - After bad push: Use
git reflogto find the previous state, thengit reset --hard HEAD@{n} - For shared branches: Coordinate with team, may need to revert and re-apply
- Automatic backup: Git keeps original ref in ORIG_HEAD:
git reset --hard ORIG_HEAD
Prevention: Always backup branches with git branch backup-branch before complex rebases.
Both combine commits, but differently:
- fixup (f): Discard the commit message entirely. Use for typo fixes, minor adjustments, or follow-up commits that don't need separate documentation.
- squash (s): Keep the commit message for editing. Use when combining related feature commits where you want to preserve and merge their messages.
Example:
fixup b2c3d4e Fix typo in variable name
squash c3d4e5f Add login tests
fixup d4e5f6g Update package.json
Result: One commit with message combining "Implement login feature" and "Add login tests", with the minor fixes incorporated silently.
Short answer: Yes, but with extreme caution.
When it's safe:
- You're the only one working on the branch
- No open pull requests reference it
- You can force push without affecting others
Procedure:
# 2. Make sure you have latest
git fetch origin
git rebase origin/main
# 3. Force push with lease (safest)
git push --force-with-lease
# 4. Notify team push is complete
Never rebase: main/master branches, release branches, or any branch others are actively using.
Use git cherry-pick with rebase or selective interactive rebase:
git rebase -i HEAD~10
# Change unwanted commits to 'drop'
# Method 2: Cherry-pick specific commits to new branch
git checkout -b new-feature main
git cherry-pick a1b2c3d b2c3d4e c3d4e5f
# Method 3: Rebase with --onto for complex cases
git rebase --onto main feature-a~3 feature-a
--onto explanation: Takes commits from feature-a starting after the 3rd commit back, and rebases them onto main.
Both modify history, but at different scales:
| Aspect | git commit --amend |
git rebase -i |
|---|---|---|
| Scope | Only the most recent commit | Multiple commits (any number) |
| Use Case | Fix typo in last commit, add forgotten file | Clean up series of commits before sharing |
| Complexity | Simple, single operation | Complex, multi-step process |
| History Impact | Changes only HEAD commit | Rewrites all commits after chosen point |
Best Practice: Use --amend for immediate fixes, use rebase -i for periodic cleanup before pushing.