Interactive Rebase
Technical Foundation: Commit Graph Transformation
Interactive rebase represents Git’s most sophisticated history manipulation capability, enabling developers to refine commit sequences before integration into shared branches. Unlike standard rebase operations that mechanically replay commits, interactive mode provides granular control over each commit’s fate—allowing reordering, combination, modification, or deletion within a single workflow.
Need deeper context? Check out
Understanding HEAD to learn about the
HEAD~5 syntax used throughout this guide, and
Merge vs Rebase for guidance on when to use
rebasing vs merging.
Conceptual Architecture
Standard Rebase (Mechanical Replay):
Feature branch: A - B - C - D
↓ (rebase onto main)
Main branch: A - B - E - F - C' - D'Interactive Rebase (Selective Transformation):
Original: A - B - C - D - E - F
↓
Interactive: A - B - CE - F'
(C+D combined, E modified, F reordered)Key Distinction: Interactive rebase treats the commit sequence as an editable document rather than an immutable timeline.
Underlying Mechanics: How Interactive Rebase Works
Command Execution:
git rebase -i HEAD~5
# Or from specific commit:
git rebase -i <base-commit>Git’s Internal Process:
- Checkpoint Creation: Save current HEAD position to reflog
- Commit Extraction: Identify commits between base and HEAD
- TODO List Generation: Create edit script in
.git/rebase-merge/git-rebase-todo - Editor Launch: Open TODO list in configured editor
- Sequential Execution: Process each instruction line-by-line
- Branch Update: Move branch pointer to final commit
- Cleanup: Remove temporary rebase state
Technical Detail: Interactive rebase operates on a detached HEAD state during execution, preventing branch pointer confusion during commit replay.
The Interactive Rebase Command Set
Command Overview Table
| Command | Shorthand | Effect | Commit SHA Changes |
|---|---|---|---|
pick | p | Keep commit as-is | ✅ Changes (replay) |
reword | r | Keep changes, edit message | ✅ Changes |
edit | e | Pause for amendments | ✅ Changes |
squash | s | Combine with previous | ✅ Creates new commit |
fixup | f | Combine, discard message | ✅ Creates new commit |
exec | x | Execute shell command | ❌ No direct effect |
drop | d | Remove commit entirely | N/A (deleted) |
break | b | Pause rebase (for inspection) | N/A (temporary) |
label | l | Name current HEAD position | N/A (reference) |
reset | t | Reset HEAD to label | Varies |
merge | m | Create merge commit | ✅ Creates merge |
Command 1: pick - Preserve Commit
Purpose: Include commit in rebased history without modification.
Usage: Default command for all commits; typically used when no changes needed.
Example TODO List:
pick abc1234 Implement user authentication
pick def5678 Add JWT token generation
pick ghi9012 Fix authentication bugTechnical Behavior:
- Applies commit diff to current HEAD
- Creates new commit with same content
- New SHA-1 due to different parent reference
Use Case: Keeping most commits unchanged while modifying specific ones.
Command 2: reword - Modify Commit Message
Purpose: Change commit message without altering file changes.
Interactive Process:
# TODO list
pick abc1234 Implement authentication
reword def5678 Add tokens
pick ghi9012 Fix bug
# Git pauses at "reword" commit
# Editor opens with current message:
Add tokens
# Edit to:
Add JWT token generation with RSA-256 signing
Implements secure token-based authentication using industry-standard
JWT format with configurable expiration times.Technical Behavior:
- Replays commit changes
- Opens editor for message modification
- Creates new commit with updated message
- Continues rebase automatically after save
Use Case: Improving commit messages before code review or main branch integration.
Command 3: edit - Pause for Amendments
Purpose: Stop rebase at specific commit for arbitrary modifications.
Interactive Workflow:
# TODO list
pick abc1234 Implement authentication
edit def5678 Add token generation
pick ghi9012 Fix bug
# Git pauses:
Stopped at def5678... Add token generation
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continueModification Options During Edit:
Option 1: Amend Commit
# Make additional changes
vim src/auth.py
# Amend existing commit
git add src/auth.py
git commit --amend
git rebase --continueOption 2: Split Commit
# Reset to previous commit (keeps changes unstaged)
git reset HEAD^
# Create multiple commits
git add file1.py
git commit -m "First logical change"
git add file2.py
git commit -m "Second logical change"
git rebase --continueOption 3: Insert New Commits
# Create new commit before continuing
vim new_feature.py
git add new_feature.py
git commit -m "Add new feature"
git rebase --continueUse Case: Complex commit restructuring requiring arbitrary modifications.
Command 4: squash - Combine with Message Editing
Purpose: Merge commit into previous commit, combining both messages.
Interactive Process:
# TODO list
pick abc1234 Implement authentication
squash def5678 Add token generation
squash ghi9012 Fix typo in auth
# Git combines commits and opens editor:
# This is a combination of 3 commits.
# The first commit's message is:
Implement authentication
# This is the 2nd commit message:
Add token generation
# This is the 3rd commit message:
Fix typo in auth
# Edit combined message:
Implement complete authentication system
- Core authentication logic
- JWT token generation with RSA-256
- Session managementResult: Three commits become one with edited combined message.
Technical Behavior:
- Applies all diffs sequentially
- Combines commit messages
- Creates single new commit
- New SHA-1 reflecting combined changes
Use Case: Consolidating related commits (feature + subsequent fixes) into coherent unit.
Command 5: fixup - Combine Without Message
Purpose: Merge commit into previous, discarding this commit’s message.
Comparison with Squash:
# Using squash (message editor opens)
pick abc1234 Add feature
squash def5678 Fix typo
# Editor opens for combined message
# Using fixup (automatic)
pick abc1234 Add feature
fixup def5678 Fix typo
# No editor; keeps "Add feature" messageResult: Same file changes as squash, but automatic message handling.
Use Case: Fixing commits with “Fix typo” or “Oops, forgot file” messages that don’t deserve preservation.
Command 6: exec - Execute Shell Commands
Purpose: Run arbitrary commands between commit replays.
Use Cases:
Automated Testing:
pick abc1234 Add feature A
exec npm test
pick def5678 Add feature B
exec npm test
pick ghi9012 Add feature C
exec npm testResult: Rebase aborts if any test fails, pinpointing problematic commit.
Build Verification:
pick abc1234 Refactor module A
exec npm run build
pick def5678 Refactor module B
exec npm run buildResult: Ensures each commit builds successfully.
Code Quality Gates:
pick abc1234 Add new endpoint
exec npm run lint
exec npm run type-check
pick def5678 Add tests
exec npm testResult: Multi-stage verification for each commit.
Technical Behavior:
- Pauses rebase after previous commit
- Executes command in shell
- Continues if exit code 0
- Aborts if exit code non-zero
Recovery from Failed Exec:
# If command fails
git rebase --continue # Skip this exec
# Or
git rebase --abort # Cancel entire rebaseCommand 7: drop - Remove Commit
Purpose: Delete commit from history entirely.
Explicit vs. Implicit Deletion:
# Explicit (recommended)
pick abc1234 Add feature
drop def5678 Debug logging
pick ghi9012 Add tests
# Implicit (dangerous)
pick abc1234 Add feature
# def5678 deleted from list
pick ghi9012 Add testsWhy Explicit is Better:
- Clear intent in TODO list
- Recoverable via reflog
- Prevents accidental deletion
Use Case: Removing experimental commits or debug code before integration.
Warning: If dropped commit is depended on by later commits, conflicts will occur.
Advanced Interactive Rebase Patterns
Pattern 1: The “Clean Up Before PR” Workflow
Scenario: Feature branch with messy development history needs refinement before code review.
Initial History:
git log --oneline
abc1234 Implement user profile endpoint
def5678 Fix typo
ghi9012 Add validation
jkl3456 WIP: testing approach
mno7890 Remove debug logging
pqr1234 Add tests
stu5678 Fix test typoInteractive Rebase Strategy:
git rebase -i HEAD~7
# TODO list:
pick abc1234 Implement user profile endpoint
fixup def5678 Fix typo # Merge typo fix
squash ghi9012 Add validation # Combine validation
drop jkl3456 WIP: testing approach # Remove WIP
fixup mno7890 Remove debug logging # Clean up debug
pick pqr1234 Add tests
fixup stu5678 Fix test typo # Merge test fixResulting History:
git log --oneline
vwx9012 Implement user profile endpoint with validation
yza3456 Add testsBenefit: Two clear, reviewable commits instead of seven messy ones.
Pattern 2: Commit Reordering for Logical Flow
Scenario: Commits created in implementation order don’t reflect logical feature progression.
Original Order (Implementation):
abc1234 Add database schema
def5678 Implement API endpoint
ghi9012 Add frontend component
jkl3456 Add database migrations
mno7890 Add API testsLogical Order (Feature Flow):
pick jkl3456 Add database migrations # 1. Database first
pick abc1234 Add database schema # 2. Schema second
pick def5678 Implement API endpoint # 3. Backend third
pick mno7890 Add API tests # 4. Tests fourth
pick ghi9012 Add frontend component # 5. Frontend lastWhy Reorder:
- Logical progression: data layer → business logic → presentation
- Easier bisect debugging
- Clearer code review narrative
Conflict Risk: Reordering commits that modify same files may create conflicts.
Pattern 3: Splitting Monolithic Commits
Scenario: One large commit contains multiple logical changes.
Original Commit:
abc1234 Update user authentication system
- Add JWT support
- Refactor password hashing
- Update session management
- Fix security vulnerabilitySplit Strategy:
git rebase -i HEAD~1
# Change to edit
edit abc1234 Update user authentication system
# Reset to parent (keeps changes unstaged)
git reset HEAD^
# Create focused commits
git add src/jwt.py
git commit -m "Add JWT token support"
git add src/password.py
git commit -m "Refactor password hashing to use bcrypt"
git add src/session.py
git commit -m "Update session management"
git add src/security_fix.py
git commit -m "Fix authentication bypass vulnerability"
git rebase --continueResult: Four focused commits, each representing single logical change.
Benefit:
- Security fix can be cherry-picked independently
- Easier to revert specific changes
- Clear audit trail
Pattern 4: Autosquash Workflow
Concept: Mark fixup commits during development for automatic squashing during rebase.
Development Workflow:
# Initial feature commit
git commit -m "Add payment processing"
# Later: Find bug in payment processing
vim payment.py
git commit --fixup abc1234 # abc1234 = payment processing commit
# Later: Another fix
vim payment.py
git commit --fixup abc1234
# Even later: Add feature B
git commit -m "Add invoice generation"Current History:
abc1234 Add payment processing
def5678 fixup! Add payment processing
ghi9012 fixup! Add payment processing
jkl3456 Add invoice generationAutosquash Rebase:
git rebase -i --autosquash HEAD~4
# Git automatically generates:
pick abc1234 Add payment processing
fixup def5678 fixup! Add payment processing
fixup ghi9012 fixup! Add payment processing
pick jkl3456 Add invoice generationResult: Fixes automatically squashed into original commits.
Rebase Configuration for Better Workflows
Configure Git’s rebase behavior to streamline your workflow and reduce friction:
Essential Rebase Settings
# Enable autosquash by default
git config --global rebase.autosquash true
# Auto-stash changes before rebase, restore after
git config --global rebase.autoStash true
# Use --rebase-merges by default (preserves merge commits)
git config --global rebase.rebaseMerges true
# Update commit dates to current time when rebasing
git config --global rebase.updateRefs trueRecommended Rebase Configuration
For Interactive Workflow:
# Show abbreviated commit hashes in rebase todo list
git config --global rebase.abbreviateCommands false # Show full commands (pick, squash, etc.)
# Customize instruction format
git config --global rebase.instructionFormat "[%an - %ar] %s"
# Results in: [Alice - 2 days ago] Fix authentication bugFor Conflict Handling:
# Automatically apply fixup commits during conflicts
git config --global rebase.autosquash true
# Enable --rebase-merges to preserve merge commits
git config --global pull.rebase mergesVerifying Rebase Settings
# Check all rebase-related configuration
git config --global --get-regexp rebase
# Example output:
# rebase.autosquash true
# rebase.autostash true
# rebase.instructionformat [%an - %ar] %sWorkflow with Auto-Configuration
With these settings, your rebase workflow becomes:
# Before configuration:
git stash
git rebase -i --autosquash HEAD~4
git stash pop
# After configuration:
git rebase -i HEAD~4
# Auto-stash, autosquash, and unstash happen automatically!Conflict Resolution During Interactive Rebase
Understanding Rebase Conflicts
Why Conflicts Occur:
Interactive rebase replays commits sequentially. Each transformation may conflict with subsequent commits.
Example Scenario:
# Original commits
A: Add feature (modifies file.py lines 10-15)
B: Refactor feature (modifies file.py lines 12-20)
C: Fix bug (modifies file.py lines 14-16)
# Rebase operation: squash B into A
# New combined commit A+B modifies lines 10-20 differently
# Commit C expects old line 14 content → CONFLICTConflict Resolution Workflow
Step 1: Conflict Detection
git rebase -i HEAD~3
# Git applies changes, encounters conflict:
CONFLICT (content): Merge conflict in src/file.py
error: could not apply abc1234... Fix bug
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: git rebase --skip
Or abort: git rebase --abort.Step 2: Examine Conflict
git status
# both modified: src/file.py
vim src/file.py
# Shows conflict markers:
<<<<<<< HEAD (current rebased state)
def process(data):
return validate_v2(data) # New combined version
=======
def process(data):
return validate_v1(data) # Original C commit expected
>>>>>>> abc1234 (Fix bug)Step 3: Resolve Conflict
# Edit file to resolve
vim src/file.py
# Choose correct resolution
# Stage resolution
git add src/file.py
# Continue rebase
git rebase --continueStep 4: Iterate
# May encounter more conflicts
# Repeat resolution process until completeConflict Resolution Strategies
Strategy 1: Accept Rebased Version
# Keep changes from combined commits
git checkout --ours src/file.py
git add src/file.py
git rebase --continueStrategy 2: Accept Original Commit
# Keep changes from commit being replayed
git checkout --theirs src/file.py
git add src/file.py
git rebase --continueStrategy 3: Manual Merge
# Combine both changes intelligently
vim src/file.py
# Manual editing
git add src/file.py
git rebase --continueStrategy 4: Skip Problematic Commit
# If commit no longer needed
git rebase --skipStrategy 5: Abort and Reconsider
# If conflicts too complex
git rebase --abort
# Try different approach:
# - Smaller squashes
# - Different commit order
# - Split commits firstMinimizing Rebase Conflicts
Best Practice 1: Frequent Rebasing
# Rebase feature branch regularly
git fetch origin
git rebase origin/main
# Smaller diffs = fewer conflictsBest Practice 2: Logical Commit Organization
# Group related changes in same commit
# Reduces interdependencies between commitsBest Practice 3: Test After Each Major Operation
pick abc1234 Refactor module A
exec npm test
squash def5678 Continue refactoring
exec npm test💡 Useful Aliases
git config --global alias.rbi 'rebase -i'
git config --global alias.rbc 'rebase --continue'
git config --global alias.rba 'rebase --abort'These aliases streamline interactive rebase workflows, especially during conflict resolution where you may need to continue or abort multiple times.
Advanced Techniques and Edge Cases
Technique 1: Rebase with --onto
Purpose: Change the base of commit sequence without including intermediate commits.
Scenario: Move feature branch from old-main to new-main.
# Current state:
old-main: A - B - C
\
feature: D - E - F
new-main: A - B - G - H
# Goal: Rebase feature onto new-main
git rebase --onto new-main old-main feature
# Result:
new-main: A - B - G - H
\
feature: D' - E' - F'Interactive Variant:
git rebase -i --onto new-main old-main feature
# Opens editor for commit manipulation during transplantTechnique 2: Preserving Merge Commits
Problem: Standard rebase linearizes history, losing merge structure.
Solution: --rebase-merges (formerly --preserve-merges)
# Feature branch with internal merges:
A - B - C - M - E
| |
D ------+
# Standard rebase flattens:
A - B - C' - D' - M' - E'
# With --rebase-merges:
git rebase -i --rebase-merges main
# Preserves merge structure:
A - B - C' - M' - E'
| |
D' ------+Use Case: Complex feature branches with meaningful internal integration points.
Technique 3: Rebase with Exec for Continuous Validation
Pattern: Ensure every commit in history passes validation.
git rebase -i HEAD~10 --exec "npm test"
# Equivalent TODO list:
pick abc1234 Commit 1
exec npm test
pick def5678 Commit 2
exec npm test
pick ghi9012 Commit 3
exec npm test
# ... etcBenefit: Guarantees bisectable history—every commit is valid.
Technique 4: Interactive Rebase with Custom Editor Commands
Advanced TODO List Manipulation:
# Set custom editor with pre-populated commands
git config sequence.editor "vim"
# Create custom script for common patterns
#!/bin/bash
# rebase-script.sh
echo "pick $1" > /tmp/rebase-todo
echo "exec npm test" >> /tmp/rebase-todoRecovery and Safety Protocols
Recovery Technique 1: Abort Mid-Rebase
Simple Abort:
# At any point during rebase
git rebase --abort
# Returns to pre-rebase state
# No changes appliedUse Case: Conflicts too complex; want to restart with different strategy.
Recovery Technique 2: Using Reflog
Scenario: Completed rebase, but result is wrong.
# View reflog
git reflog
# Output:
abc1234 HEAD@{0}: rebase -i (finish): returning to refs/heads/feature
def5678 HEAD@{1}: rebase -i (squash): Commit message
ghi9012 HEAD@{2}: rebase -i (start): checkout main
jkl3456 HEAD@{3}: commit: Original feature work
# Reset to pre-rebase state
git reset --hard HEAD@{3}
# Original commits restoredRecovery Technique 3: ORIG_HEAD Reference
Immediate Post-Rebase Recovery:
# Git automatically sets ORIG_HEAD before rebase
git reset --hard ORIG_HEAD
# Faster than searching reflogLimitation: ORIG_HEAD only preserved until next operation that sets it.
Safety Practice: Backup Branch Before Rebase
Recommended Workflow:
# Create backup
git branch backup-feature
# Perform rebase
git rebase -i HEAD~10
# If successful, delete backup
git branch -D backup-feature
# If problems, restore from backup
git reset --hard backup-featurePerformance Considerations
Large Rebase Operations
Performance Characteristics:
- Small rebases (< 10 commits): Milliseconds
- Medium rebases (10-50 commits): Seconds
- Large rebases (50-200 commits): Minutes
- Very large rebases (200+ commits): Tens of minutes
Optimization Strategies:
Strategy 1: Incremental Rebasing
# Instead of:
git rebase -i HEAD~100
# Do:
git rebase -i HEAD~25
git rebase -i HEAD~25
git rebase -i HEAD~25
git rebase -i HEAD~25Strategy 2: Limit Scope
# Only rebase necessary commits
git rebase -i <specific-base-commit>Conflict Resolution Performance
Time Complexity: O(n × m)
- n = number of commits
- m = average conflict complexity per commit
Worst Case: Rebasing 50 commits where 40 have conflicts can take hours.
Mitigation: Abort and use merge instead if conflicts exceed 5-10 commits.
Integration with Team Workflows
Pre-PR Interactive Rebase Checklist
Before Creating Pull Request:
# 1. Update with latest main
git fetch origin
git rebase origin/main
# 2. Clean up commit history
git rebase -i origin/main
# 3. Verify tests pass for each commit
git rebase -i --exec "npm test" origin/main
# 4. Force push to feature branch
git push --force-with-lease origin feature-branch
# 5. Create pull requestTeam Guidelines for Interactive Rebase
Safe Rebase Zones:
- ✅ Personal feature branches (before PR)
- ✅ Local commits not yet pushed
- ✅ Feature branches with single developer
Dangerous Rebase Zones:
- ❌ Main/develop branches
- ❌ Shared feature branches (multiple developers)
- ❌ Released branches
- ❌ Any commits others have based work on
Summary: Interactive Rebase Mastery
Core Capabilities:
- Commit Combination: Squash and fixup for clean history
- Commit Reordering: Logical narrative over chronological
- Commit Splitting: One commit → multiple focused commits
- Message Refinement: Improve documentation before sharing
- Selective Deletion: Remove unwanted commits
- Automated Validation: Exec commands for quality gates
Strategic Application:
- Use before pull requests for clean, reviewable commits
- Apply to feature branches before main integration
- Leverage autosquash for iterative development
- Combine with exec for ensuring bisectable history
Safety Protocols:
- Always create backup branches
- Understand reflog recovery
- Use
--force-with-leasenot--force - Abort early if conflicts become overwhelming
Technical Excellence: Interactive rebase transforms raw development history into polished, maintainable project documentation. Master this tool to craft commit sequences that tell clear stories, enabling efficient code review, debugging, and project understanding.
Perfect your Git history Explore Advanced Rewriting Techniques