Git Interactive Rebase Guide | Squash, Fixup, Reorder & Clean History
이 글의 핵심
Interactive rebase lets you rewrite commit history before merging — squash WIP commits, fix messages, reorder changes, and split large commits. This guide covers every rebase command with real workflow examples.
Why Interactive Rebase?
Before rebase: After rebase:
abc123 WIP abc789 Add user authentication
def456 WIP: actually works ↑ clean, single commit
ghi789 Fix typo ready to merge
jkl012 Add user authentication
Interactive rebase lets you clean up messy development history before it becomes permanent in main.
Common use cases:
- Combine WIP commits into meaningful commits before PR
- Fix a bad commit message from 3 commits ago
- Remove a commit that shouldn’t be in the branch
- Reorder commits for logical clarity
- Split one large commit into focused changes
Basic Syntax
# Rebase last N commits interactively
git rebase -i HEAD~3 # Rebase last 3 commits
# Rebase from a specific commit (exclusive — that commit is not included)
git rebase -i abc1234
# Rebase onto another branch (rebase feature onto main)
git rebase -i main
# Rebase from the point where branch diverged from main
git rebase -i $(git merge-base HEAD main)
The Rebase Editor
When you run git rebase -i HEAD~4, an editor opens:
pick a1b2c3d Add user model
pick e4f5a6b Add auth routes
pick 7c8d9e0 WIP: half-working login
pick 1f2a3b4 Fix login endpoint
# Rebase a1b2c3d..1f2a3b4 onto 9f8e7d6 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, meld into previous commit (keep message)
# f, fixup = like squash, but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# b, break = stop here
# d, drop = remove commit
# l, label = label current HEAD with a name
Commands in Detail
squash / fixup — Combine Commits
# Before
pick a1b2c3d Add user model
pick e4f5a6b Add auth routes
pick 7c8d9e0 WIP: half-working login
pick 1f2a3b4 Fix login endpoint
# After editing — squash/fixup into one commit
pick a1b2c3d Add user model
pick e4f5a6b Add auth routes
pick 7c8d9e0 WIP: half-working login
f 1f2a3b4 Fix login endpoint # fixup: combine, discard message
# squash will open another editor to combine messages
# fixup (f) silently drops the commit message — use for minor fixes
# Squash all WIP commits before pushing
git rebase -i HEAD~5
# Change all "pick" except the first to "squash" or "fixup"
reword — Fix a Commit Message
# Change "pick" to "reword" (or "r")
pick a1b2c3d Add user model
reword e4f5a6b fix thing # ← message needs fixing
pick 7c8d9e0 Add auth routes
# Git will stop at the reword commit and open editor for the message
edit — Stop to Amend a Commit
pick a1b2c3d Add user model
edit e4f5a6b Add auth routes # ← stop here
pick 7c8d9e0 Add login
# During the rebase, git stops at 'edit' commits:
# You can then:
git add forgotten-file.ts
git commit --amend # Add to the commit
git rebase --continue # Resume
drop — Remove a Commit
pick a1b2c3d Add user model
drop e4f5a6b Accidental debug logs # ← delete this commit
pick 7c8d9e0 Add auth routes
# Or just delete the line — same effect
Reorder Commits
# Before (in rebase editor — just move lines)
pick a1b2c3d Add user model
pick e4f5a6b Add auth routes
pick 7c8d9e0 Add tests
# After (reordered — tests before auth routes)
pick a1b2c3d Add user model
pick 7c8d9e0 Add tests # moved up
pick e4f5a6b Add auth routes
# Note: conflicts are possible if commits depend on each other
Common Workflows
Clean Up Before a PR
# You have 6 messy commits on your feature branch
git log --oneline origin/main..HEAD
# 1a2b3c4 fix typo
# 5d6e7f8 WIP 2
# 9g0h1i2 WIP: almost
# 3j4k5l6 add tests
# 7m8n9o0 add auth
# 1p2q3r4 setup
# Clean them up
git rebase -i origin/main
# In the editor:
pick 1p2q3r4 setup
squash 7m8n9o0 add auth # combine with setup? No — fixup/squash adjacent
pick 7m8n9o0 add auth
squash 9g0h1i2 WIP: almost
squash 5d6e7f8 WIP 2
pick 3j4k5l6 add tests
fixup 1a2b3c4 fix typo
# Result: clean 3-commit history
Fix a Commit Message Mid-History
git rebase -i HEAD~5
# Change "pick" to "reword" on the commit with the bad message
# Git stops, opens editor — fix the message
# git rebase --continue
Split One Large Commit
git rebase -i HEAD~3
# Mark the large commit as "edit"
# When git stops:
git reset HEAD~1 # Unstage the commit (keep changes)
git add src/models/ # Stage part 1
git commit -m "Add user model"
git add src/routes/ # Stage part 2
git commit -m "Add auth routes"
git rebase --continue # Continue
git commit —fixup for Automated Squash
# Make a fixup commit for a specific earlier commit
git commit --fixup=abc1234 # Creates: "fixup! <original message>"
# Automatically squash all fixup commits
git rebase -i --autosquash HEAD~5
# fixup commits are automatically placed after their target and marked 'fixup'
Handling Conflicts During Rebase
# When a conflict occurs:
git status # See conflicting files
# Edit files to resolve conflicts
git add resolved-file.ts # Stage resolved files
git rebase --continue # Continue to next commit
# Skip a commit that causes unresolvable conflict (rare)
git rebase --skip
# Abort entirely and return to pre-rebase state
git rebase --abort
Rebase vs Merge
# Workflow: keep feature branch up to date with main
# Option 1: Merge (creates merge commit)
git checkout feature
git merge main
# History: f1 → f2 → merge(f2, m3) → f3
# Option 2: Rebase (linear history)
git checkout feature
git rebase main
# History: m1 → m2 → m3 → f1' → f2' → f3'
# (commits are replayed on top of main)
# Most teams use:
git fetch origin
git rebase origin/main # Update feature branch
# Then: create PR → squash merge or regular merge into main
Safe Rebase Rules
# ✅ Safe: rebase commits that are only on your local branch
git rebase -i HEAD~5 # Local commits only
# ✅ Safe: rebase your feature branch onto main (before others pull it)
git rebase origin/main
# ⚠️ Risky: rebase commits you've pushed to a shared branch
# Anyone who pulled those commits will have diverged history
# If you must:
git push --force-with-lease # Safer than --force (fails if remote was updated)
# ❌ Never: rebase main, develop, or any shared branch
git checkout main
git rebase feature # DO NOT DO THIS
Undo a Rebase
# git reflog shows all recent HEAD positions — even after rebase
git reflog
# HEAD@{0}: rebase finished
# HEAD@{1}: rebase: Add auth routes
# HEAD@{2}: checkout: moving from main to feature
# HEAD@{5}: commit: last good state ← find this
# Reset to pre-rebase state
git reset --hard HEAD@{5}
Quick Reference
| Command | What it does |
|---|---|
pick | Keep commit as-is |
reword | Keep commit, edit message |
edit | Stop here to amend |
squash | Combine with previous, keep both messages |
fixup | Combine with previous, discard this message |
drop | Delete this commit |
--autosquash | Auto-arrange fixup! commits |
--abort | Cancel rebase, restore original state |
--continue | After resolving conflict, continue |
--skip | Skip problematic commit |
Related posts: