Skip to content
Codeloom
Git

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.

·9 min read · By Codeloom
Advanced 14 min read

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:

CommandShortEffect
pickpKeep the commit as-is
rewordrKeep the commit, edit its message
editePause at this commit for amendments
squashsMeld into the previous commit, combine messages
fixupfMeld into the previous commit, discard this message
dropdRemove the commit entirely

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)
Rebase --onto transplants a branch to a new base

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.