What is Git Interactive Rebase
Git interactive rebase is one of Git's most powerful tools for rewriting commit history. It allows you to reorganize, modify, combine, or delete commits before sharing your work, creating a clean and professional history that facilitates code review and debugging.
🎯 What You Can Do with Interactive Rebase
- Squash: Combine multiple commits into one
- Reword: Modify commit messages
- Edit: Modify the content of a commit
- Reorder: Reorder commits
- Drop: Delete unwanted commits
- Fixup: Combine commits discarding messages
⚠️ Golden Rule: NEVER Rebase Public Commits!
Rebase rewrites Git history. Never rebase commits already pushed to shared branches - this would cause problems for all collaborators. Use rebase only on local commits or on personal feature branches before merging.
Starting Interactive Rebase
To start an interactive rebase, specify how many commits back you want to modify with
git rebase -i HEAD~N where N is the number of commits.
# Rewrite the last 3 commits
git rebase -i HEAD~3
# Rebase all commits from main branch
git rebase -i main
# Rebase from a specific commit (excluded)
git rebase -i abc1234
# See which commits will be included before rebase
git log --oneline HEAD~5..HEAD
The Interactive Editor
When you start the interactive rebase, Git opens your configured text editor showing the list of commits and available options:
pick abc1234 Add user authentication
pick def5678 Fix typo in auth module
pick ghi9012 Add password validation
pick jkl3456 Update tests for auth
# Rebase 0123456..jkl3456 onto 0123456 (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
Common Interactive Rebase Operations
1. Squash: Combining Commits
Squashing combines multiple commits into one, perfect for consolidating WIP (work in progress) commits or small fixes into a logical and complete commit.
# Original history
pick abc1234 Add login form
pick def5678 Fix button styling
pick ghi9012 Add validation
pick jkl3456 Fix validation bug
# Modify to:
pick abc1234 Add login form
squash def5678 Fix button styling
squash ghi9012 Add validation
squash jkl3456 Fix validation bug
# Result: 1 commit combining all 4
After saving, Git will ask you to create a new commit message:
# This is a combination of 4 commits.
# This is the 1st commit message:
Add login form
# This is the commit message #2:
Fix button styling
# This is the commit message #3:
Add validation
# This is the commit message #4:
Fix validation bug
# You can modify into a single clearer message:
feat: implement user login with form validation
- Created login form component
- Added email and password validation
- Styled form buttons
- Fixed validation edge cases
Before Squash:
abc1234 Add login form
def5678 Fix button styling
ghi9012 Add validation
jkl3456 Fix validation bug
After Squash:
xyz7890 feat: implement user
login with validation
2. Fixup: Squash Without Message
fixup is like squash, but discards the commit message, keeping only
the previous commit's message. Faster when intermediate messages aren't needed.
pick abc1234 Add user profile page
fixup def5678 Fix typo
fixup ghi9012 Fix linting errors
fixup jkl3456 Remove console.log
# Result: 1 commit with message "Add user profile page"
# Messages from the other 3 are automatically discarded
💡 Pro Tip: git commit --fixup
When committing, you can use --fixup to mark it as a fix for a previous commit:
# During development
git commit -m "Add feature X"
# ... work ...
git commit --fixup abc1234 # abc1234 is the hash of the commit to fix
# Then when rebasing
git rebase -i --autosquash HEAD~5
# Git automatically organizes fixup commits!
3. Reword: Modifying Messages
Change pick to reword to modify only the commit message,
without touching the content.
pick abc1234 Add user auth
reword def5678 Fixed bug
pick ghi9012 Update tests
# When you save, Git opens the editor for def5678
# and you can change "Fixed bug" to something more descriptive:
fix: resolve authentication timeout issue
Fixed a race condition that caused authentication
to timeout under heavy load.
4. Edit: Modifying Commit Content
Use edit to stop the rebase and allow you to modify the commit
(add/remove files, modify code).
pick abc1234 Add payment module
edit def5678 Add validation
pick ghi9012 Add tests
# When you reach def5678, the rebase stops
# Now you can modify files
vim src/validation.ts
# Add modifications to the commit
git add src/validation.ts
git commit --amend --no-edit
# Or create new commits
git commit -m "Additional changes"
# Continue the rebase
git rebase --continue
5. Drop: Deleting Commits
Change pick to drop (or delete the line) to completely
remove a commit from history.
pick abc1234 Add feature X
drop def5678 Add debug logging
pick ghi9012 Add feature Y
# Or simply delete the line:
pick abc1234 Add feature X
pick ghi9012 Add feature Y
# Commit def5678 disappears from history
6. Reorder: Reordering Commits
Simply move the lines in the editor to change the order of commits.
# Original order
pick abc1234 Add tests
pick def5678 Add feature
pick ghi9012 Add documentation
# Reorder for better logic
pick def5678 Add feature
pick abc1234 Add tests
pick ghi9012 Add documentation
# Commits are reapplied in the new order
⚠️ Watch Out for Conflicts
Reordering commits can cause conflicts if commits depend on each other. If a commit modifies code added by a later commit, you'll have conflicts.
Splitting a Commit
Sometimes you need to split a large commit into smaller and more focused commits.
Use edit and git reset.
# Start rebase and mark the commit to split with 'edit'
git rebase -i HEAD~3
# When the rebase stops at the commit to split:
# 1. Reset the commit (keeps changes as unstaged)
git reset HEAD^
# 2. Now you have all changes as uncommitted
git status
# 3. Create smaller and focused commits
git add src/user.model.ts
git commit -m "Add user model"
git add src/user.service.ts
git commit -m "Add user service"
git add src/user.component.ts
git commit -m "Add user component"
# 4. Continue the rebase
git rebase --continue
# Result: 1 commit split into 3 logical commits
Error Handling and Recovery
Aborting a Rebase in Progress
# If something goes wrong during rebase
git rebase --abort
# Returns to the state before the rebase
Resolving Conflicts During Rebase
# If you encounter conflicts during rebase
# Git stops and shows conflicted files
# 1. Resolve conflicts in files
vim conflicted-file.ts
# 2. Add resolved files
git add conflicted-file.ts
# 3. Continue the rebase
git rebase --continue
# If the conflict is on a commit you want to skip
git rebase --skip
Recovering from a Failed Rebase
# Git saves everything in reflog - you can always recover
# See complete operation history
git reflog
# Example output:
# abc1234 HEAD@{{ '{' }}0{{ '}' }}: rebase finished
# def5678 HEAD@{{ '{' }}1{{ '}' }}: rebase: checkout main
# ghi9012 HEAD@{{ '{' }}2{{ '}' }}: commit: Add feature
# Return to state before rebase
git reset --hard HEAD@{{ '{' }}2{{ '}' }}
# Or create a backup branch
git branch backup-before-rebase HEAD@{{ '{' }}2{{ '}' }}
Professional Workflow with Interactive Rebase
📋 Best Practice Workflow
1. During Development: Frequent Commits
# Commit often with WIP messages
git commit -m "WIP: add login form"
git commit -m "WIP: fix styling"
git commit -m "WIP: add validation"
git commit -m "WIP: fix bug"
2. Before Push: History Cleanup
# Combine WIP into logical commits
git rebase -i HEAD~10
# Squash all WIP into:
# - feat: implement login with validation
# - test: add login form tests
# - docs: update authentication docs
3. Before Pull Request: Final Review
# Make sure base branch is updated
git fetch origin
git rebase -i origin/main
# Reorder if necessary
# Improve commit messages
# Remove debug/test commits
Advanced Commands and Tricks
Autosquash with --autosquash
# During development, mark fixups
git commit --fixup abc1234
git commit --fixup def5678
# On rebase, Git automatically organizes
git rebase -i --autosquash HEAD~10
# Fixup commits are automatically
# positioned after the commit they fix
Exec: Run Commands During Rebase
pick abc1234 Add feature A
exec npm test
pick def5678 Add feature B
exec npm test
pick ghi9012 Add feature C
exec npm test
# Git runs tests after each commit
# If tests fail, rebase stops
Root Rebase: Rebase from First Commit
# Rebase from the beginning of history
git rebase -i --root
# Useful for complete repository cleanup
When NOT to Use Interactive Rebase
❌ Situations to Avoid
- Public branches: Never rebase main, develop, or other shared branches
- Already pushed commits: If others have based work on your commits
- During complex conflicts: Merge might be simpler
- Important history: When exact chronology is crucial for audit
Conclusion
Git interactive rebase is a powerful tool for maintaining a clean and professional Git history. Use it to consolidate work commits into logical and well-described commits before sharing your code. Always remember: rebase only local commits, never public commits!
🎯 Interactive Rebase Checklist
- ✅ Use only on unpushed local commits
- ✅ Backup branch before complex rebase:
git branch backup - ✅ Squash WIP/debug commits before push
- ✅ Write clear commit messages after squash
- ✅ Test code after rebase
- ✅ Use
git reflogfor recovery if needed
📚 Next in the Git Series
In the next article we'll explore Git Rebase vs Merge, understanding when to use which strategy and best practices for integrating changes in professional teams.







