Advanced Git Rebase Techniques: Interactive, Autosquash, and Rebase Onto
Master interactive rebase, autosquash, fixup commits, and rebase --onto for clean Git history. Advanced techniques for rewriting, reorganizing, and cleaning up commits.
What you'll learn
- ✓Interactive rebase for rewriting commit history
- ✓Autosquash and fixup workflows for clean PRs
- ✓Using rebase --onto to transplant branches
- ✓Splitting, reordering, and editing commits
- ✓Safe rebase practices and recovery strategies
Prerequisites
- •Solid understanding of Git commits and branches
- •Experience with basic git rebase
- •Comfort with the command line
Beyond Basic Rebase
Basic rebase replays your commits on top of another branch. That is useful, but rebase’s real power lies in its advanced modes: interactive rebase for rewriting history, autosquash for automated cleanup, and --onto for surgical branch transplants. These techniques let you ship clean, logical commit histories that tell a clear story.
Interactive Rebase
Interactive rebase (git rebase -i) opens an editor showing your commits as a todo list. You can reorder, squash, edit, drop, or reword any commit.
Starting an interactive rebase
# Rebase the last 5 commits
git rebase -i HEAD~5
Git opens your editor with something like:
pick a1b2c3d feat: add user model
pick b2c3d4e feat: add user API endpoint
pick c3d4e5f fix: typo in user model
pick d4e5f6a feat: add user validation
pick e5f6a7b fix: validation edge case
Available commands
Each line starts with a command. Change pick to any of these:
| Command | Short | Effect |
|---|---|---|
pick | p | Keep the commit as-is |
reword | r | Keep the commit, edit its message |
edit | e | Pause at this commit for amendments |
squash | s | Meld into the previous commit, combine messages |
fixup | f | Meld into the previous commit, discard this message |
drop | d | Remove the commit entirely |
Squashing related commits
Combine the typo fix with the commit it fixes:
pick a1b2c3d feat: add user model
pick c3d4e5f fix: typo in user model
pick b2c3d4e feat: add user API endpoint
pick d4e5f6a feat: add user validation
pick e5f6a7b fix: validation edge case
Wait — first reorder so the fix is right after the commit it belongs to, then squash:
pick a1b2c3d feat: add user model
fixup c3d4e5f fix: typo in user model
pick b2c3d4e feat: add user API endpoint
pick d4e5f6a feat: add user validation
fixup e5f6a7b fix: validation edge case
Result: 3 clean commits instead of 5. The fixes are absorbed into their parent commits.
Rewording commit messages
pick a1b2c3d feat: add user model
reword b2c3d4e add endpoint
pick d4e5f6a feat: add user validation
Git will pause and open your editor to rewrite the message for b2c3d4e.
Dropping commits
Remove a commit entirely by changing pick to drop or deleting the line:
pick a1b2c3d feat: add user model
pick b2c3d4e feat: add user API endpoint
drop c3d4e5f debug: temporary logging (remove before merge)
pick d4e5f6a feat: add user validation
The Autosquash Workflow
Autosquash is a workflow that automates the reorder-and-squash process. Instead of manually moving commits around in interactive rebase, you mark commits at creation time.
Creating fixup commits
When you find a bug in an earlier commit, create a fixup commit:
# Fix a bug in commit a1b2c3d
vim src/models/user.js
git add src/models/user.js
# Create a fixup commit targeting a1b2c3d
git commit --fixup=a1b2c3d
This creates a commit with the message fixup! feat: add user model (prefixed with fixup! followed by the original commit’s message).
Creating squash commits
Similar to fixup, but keeps the message for manual editing:
git commit --squash=a1b2c3d
This creates a commit prefixed with squash!.
Running autosquash
When you are ready to clean up:
git rebase -i --autosquash HEAD~8
Git automatically reorders the fixup and squash commits to be right after their targets and sets the correct commands:
pick a1b2c3d feat: add user model
fixup f1f2f3f fixup! feat: add user model
pick b2c3d4e feat: add user API endpoint
pick d4e5f6a feat: add user validation
squash s1s2s3s squash! feat: add user validation
No manual reordering needed. Save and close the editor.
Making autosquash the default
If you use autosquash frequently, enable it globally:
git config --global rebase.autosquash true
Now every git rebase -i will automatically reorder fixup and squash commits.
Rebase —onto: Surgical Branch Transplants
git rebase --onto solves a problem that basic rebase cannot: moving a branch from one base to another, or extracting a range of commits.
The three-argument form
git rebase --onto <new-base> <old-base> <branch>
This means: take commits from <old-base> to <branch>, and replay them onto <new-base>.
Before:
main: A---B---C
\
feature-1: D---E
\
feature-2: F---G---H
After: git rebase --onto main feature-1 feature-2
main: A---B---C
\ \
feature-1: D---E F'---G'---H' (feature-2, now based on main) Use case: Branch was started from the wrong base
You branched feature-2 off feature-1, but now you want it based on main:
git rebase --onto main feature-1 feature-2
This takes only the commits unique to feature-2 (F, G, H) and replays them on main.
Use case: Removing commits from the middle
You want to remove commits 3 and 4 from a 6-commit branch:
git log --oneline
# f6 commit 6
# f5 commit 5
# f4 commit 4 (remove)
# f3 commit 3 (remove)
# f2 commit 2
# f1 commit 1
# Replay commits after f4 (which is f5 and f6) onto f2
git rebase --onto f2 f4
Result: commits 1, 2, 5, 6 remain. Commits 3 and 4 are gone.
Use case: Moving a feature to a release branch
# feature/search was based on develop, but needs to go into release/2.4
git rebase --onto release/2.4 develop feature/search
Editing a Commit Mid-Rebase
The edit command pauses rebase at a specific commit, letting you amend it:
git rebase -i HEAD~4
pick a1b2c3d feat: add user model
edit b2c3d4e feat: add user API endpoint
pick c3d4e5f feat: add validation
pick d4e5f6a feat: add tests
Git pauses at b2c3d4e. Now you can:
# Make changes
vim src/api/users.js
# Amend the commit
git add src/api/users.js
git commit --amend
# Continue the rebase
git rebase --continue
Splitting a Commit
Sometimes a commit does too much and should be two separate commits:
git rebase -i HEAD~3
edit a1b2c3d feat: add user model and API endpoint
pick b2c3d4e feat: add tests
When Git pauses at the commit:
# Undo the commit but keep the changes staged
git reset HEAD~1
# Stage and commit the first part
git add src/models/user.js
git commit -m "feat: add user model"
# Stage and commit the second part
git add src/api/users.js
git commit -m "feat: add user API endpoint"
# Continue
git rebase --continue
One commit becomes two, each with a focused purpose.
Handling Rebase Conflicts
Conflicts during rebase are resolved per-commit:
# Conflict occurs
git rebase -i main
# CONFLICT (content): Merge conflict in src/api/users.js
# Fix the conflict
vim src/api/users.js # Resolve conflict markers
git add src/api/users.js
# Continue to the next commit
git rebase --continue
# Or abort the entire rebase
git rebase --abort
If the same conflict appears repeatedly across commits, consider using rerere (reuse recorded resolution):
# Enable rerere
git config --global rerere.enabled true
# Git will remember how you resolved a conflict
# and apply the same resolution automatically next time
Safe Rebase Practices
1. Never rebase shared branches
Rebasing rewrites commit hashes. If others have based work on those commits, their history diverges from yours.
# SAFE: Rebase your local feature branch onto main
git checkout feature/search
git rebase main
# DANGEROUS: Rebase main (shared branch)
git checkout main
git rebase feature/search # Do not do this
2. Create a backup branch before complex rebases
git branch backup/feature-search feature/search
git rebase -i HEAD~10
# If things go wrong:
git rebase --abort
# Or restore from backup:
git checkout feature/search
git reset --hard backup/feature-search
3. Use the reflog as your safety net
If you forgot to create a backup:
git reflog
# Find the commit hash before the rebase started
git reset --hard HEAD@{n}
4. Rebase before opening a PR, not after
Clean up your commits before requesting review. If you rebase after receiving review comments, the reviewer loses context.
Putting It All Together: A PR Cleanup Workflow
Here is a complete workflow for cleaning up a feature branch before merging:
# 1. Start with your messy feature branch
git log --oneline main..feature/search
# h8 fix: another typo
# h7 wip: debugging
# h6 feat: add search filters
# h5 fix: search query escaping
# h4 feat: add search results page
# h3 wip: trying different approach
# h2 feat: add search API
# h1 feat: add search index
# 2. Create fixup commits for small fixes (if not already done)
# Already committed, so proceed to interactive rebase
# 3. Interactive rebase
git rebase -i main
# 4. Reorganize in the editor:
pick h1 feat: add search index
pick h2 feat: add search API
fixup h5 fix: search query escaping
pick h4 feat: add search results page
fixup h8 fix: another typo
pick h6 feat: add search filters
drop h3 wip: trying different approach
drop h7 wip: debugging
# 5. Result: 4 clean, logical commits
git log --oneline main..feature/search
# h6' feat: add search filters
# h4' feat: add search results page
# h2' feat: add search API
# h1' feat: add search index
# 6. Force push to update the PR
git push --force-with-lease origin feature/search
The --force-with-lease flag is safer than --force because it refuses to push if someone else has pushed to the branch since your last fetch.
Wrap-Up
Interactive rebase transforms messy development history into clean, reviewable commits. Autosquash automates the most common cleanup pattern. Rebase --onto handles branch transplants that basic rebase cannot. Together, these tools let you develop freely (committing WIP, fixups, experiments) and then polish the result before sharing. The key is to rebase only local, unshared branches and always have a recovery plan via backup branches or the reflog.
Related articles
- Git Git Rebasing Strategies for Clean Team Histories
Advanced git rebase strategies for teams: interactive rebase workflows, rebase --onto, autosquash, and policies that keep shared branches clean without rewriting public history.
- Git Git Hooks for CI/CD and Automation Pipelines
Build custom Git hooks that integrate with CI/CD pipelines: server-side hooks, push-based triggers, branch protection enforcement, and automated deployment gates.
- Git Git Monorepo Management at Scale
Manage large Git monorepos with sparse checkout, partial clone, subtrees, CODEOWNERS, and scaling strategies that keep performance acceptable as the repo grows.
- Git Git Rebase vs Merge: When to Use Which
A clear, practical guide to choosing between git rebase and git merge, with safe workflows for feature branches, shared branches, and pull requests.