Chapter 5.3 — Interactive Rebase — squash, reorder, edit☕ 24 min read

Interactive Rebase — squash, reorder, edit, drop

Messy commits = reviewer ka headache. Clean commits = professional developer.

01What is Interactive Rebase

git rebase -i HEAD~N opens your text editor where you can modify the last N commits. This is Git's history editor — your chance to clean up before the world sees your commits.

The editor shows a list of commits from OLDEST (top) to NEWEST (bottom) — the reverse of git log. Each commit has an action word before it: pick, squash, fixup, reword, edit, or drop.

You change the action words, save and close the editor, and Git replays the commits with your chosen actions. It is like telling Git: "replay my history, but this time do it MY way."

The most common use: squash WIP commits into one clean commit before creating a Pull Request. Nobody needs to see your "fix typo" and "actually fix the typo" commits.

CRITICAL RULE: Never interactive rebase commits that have been pushed to a shared branch. Rebase rewrites history — it changes commit hashes. If others have already pulled those commits, you create divergent history for the entire team.

# Open last 5 commits in editor
git rebase -i HEAD~5

# Editor shows:
pick abc1234 feat: add login
pick def5678 fix: typo in login
pick ghi9012 debug: add console.log
pick jkl3456 debug: remove console.log
pick mno7890 feat: add logout

# Change to:
pick abc1234 feat: add login
fixup def5678 fix: typo in login
fixup ghi9012 debug: add console.log
fixup jkl3456 debug: remove console.log
pick mno7890 feat: add logout

# Result: 2 clean commits instead of 5 messy ones!
02Squash Commits

Interactive rebase gives you 6 actions to control how each commit is replayed:

  • pick = keep this commit as-is. Default action. Git replays it normally.
  • squash = combine with the commit ABOVE it, and EDIT the combined commit message. Git opens your editor.
  • fixup = combine with the commit ABOVE it, but DISCARD this commit's message. Faster — no editor opens.
  • reword = keep the commit, but let me EDIT its message. Code stays the same, only message changes.
  • edit = pause rebase at this commit so I can amend it. Add files, split the commit, or make changes.
  • drop = delete this commit ENTIRELY. Its changes are removed from history. Or just delete the line.

Order matters: commits are processed TOP to BOTTOM (oldest first). When you squash or fixup, the commit merges INTO the one above it (the older commit).

If you reorder lines, Git will replay commits in that new order. This is powerful but can cause conflicts if later commits depend on earlier ones.

# squash: combine + edit message
pick abc1234 feat: add login
squash def5678 fix: typo
# Opens editor to write: "feat: add login with typo fix"

# fixup: combine + discard message (faster)
pick abc1234 feat: add login
fixup def5678 fix: typo
# Auto-combines. Message stays: "feat: add login"

# reword: change message only
reword abc1234 feat: add login
# Opens editor to change message. Code unchanged.

# edit: pause to add more changes
edit abc1234 feat: add login
# Rebase pauses here.
# Make changes, git add, git commit --amend
# git rebase --continue

# drop: remove commit entirely
drop abc1234 feat: add login
# This commit's changes are GONE.
💡 Pro Tip: Use fixup instead of squash when you don't need to edit the commit message. It's faster because Git doesn't open the editor. Rule: if the fix is minor (typo, debug removal), use fixup. If you need to combine messages meaningfully, use squash.
03Reorder & Edit

The #1 use of interactive rebase: turning messy WIP commits into clean PR-ready commits.

Scenario: you have 10 commits with messages like "wip", "fix", "typo", "actually fix", "pls work". You wrote code quickly and committed often. Good for you — frequent commits prevent lost work. But nobody reviewing your PR needs to see that chaos.

Before creating the PR, run git rebase -i HEAD~10, squash the messy ones into meaningful commits. Result: 2-3 clean commits that reviewers can actually understand.

Remember the direction: the commit you squash INTO is the one ABOVE it in the list (the older commit). After squashing, Git opens an editor for you to write the combined commit message.

# Before: 8 messy commits
git log --oneline
# h8i9j0k pls work
# g7f6e5d actually fix the bug
# f4d3c2b typo
# e2b1a0c fix tests
# d0a9b8c add tests
# c8b7a6c WIP: feature in progress
# b6a5c4d feat: start feature
# a4b3c2d initial commit

# Rebase last 7 (keep initial commit)
git rebase -i HEAD~7

# Editor:
pick b6a5c4d feat: start feature
fixup c8b7a6c WIP: feature in progress
pick d0a9b8c add tests
fixup e2b1a0c fix tests
fixup f4d3c2b typo
fixup g7f6e5d actually fix the bug
fixup h8i9j0k pls work

# Result: 3 clean commits
# feat: start feature (includes WIP)
# add tests (includes fix tests)
# fix: the bug (includes typo, actually fix, pls work)
04Reword Messages

Reorder: move lines up or down in the rebase editor to change commit order. Git replays in the new order.

Use case: you committed a feature before its prerequisite. Or you want docs to come after the feature, not before. Just move the lines.

Edit: pause at a specific commit to add files, split a commit, or amend changes. This is the most powerful action — it lets you reshape a commit completely.

Splitting a commit: set it to edit, then when rebase pauses, run git reset HEAD~1 to unstage all changes, then use git add -p to stage partial changes and commit separately.

Warning: reordering can cause conflicts. If commit B depends on changes from commit A, but you put B before A, Git will pause for you to resolve the conflict.

# Original order (bad):
pick abc1234 docs: update README
pick def5678 feat: add feature
# The feature should come before docs!

# Reorder in editor:
pick def5678 feat: add feature
pick abc1234 docs: update README
# Save and close. Git replays in this order.

# Edit: pause to amend a commit
git rebase -i HEAD~3
# Change:
edit def5678 feat: add feature
# Rebase pauses at this commit.

# Make additional changes
echo "missed line" >> feature.js
git add feature.js
git commit --amend --no-edit
git rebase --continue

# Split a commit into two:
# At the "edit" pause:
git reset HEAD~1
# Now all changes are unstaged
git add -p  # stage first part
git commit -m "feat: first part of feature"
git add .   # stage remaining
git commit -m "feat: second part of feature"
git rebase --continue
05Split a Commit

drop = completely remove a commit and its changes from the branch history. The commit is erased from the replayed history.

Use case: you committed experimental code that didn't work and shouldn't be in the PR. Drop removes it cleanly.

Alternative: just delete the line from the rebase editor (same effect as drop). Either way, the commit and its changes vanish.

DANGER: if other commits depend on the dropped commit's code, they will conflict. If commit B added a function and commit C uses it, dropping B means C's code references a function that doesn't exist — conflict.

If you only want to remove a commit's changes from the current branch but keep them elsewhere, use git revert instead. Revert creates an "anti-commit" that undoes the changes without rewriting history.

# Before: 4 commits, but commit 3 was an experiment
git log --oneline
# d4e5f6g feat: final feature
# c3b2a1z experiment: tried something different
# b2a3c4d feat: add feature part 2
# a1b2c3e feat: add feature part 1

# Rebase to drop the experiment
git rebase -i HEAD~4

# Editor:
pick a1b2c3e feat: add feature part 1
pick b2a3c4d feat: add feature part 2
drop c3b2a1z experiment: tried something different
pick d4e5f6g feat: final feature

# Result: experiment commit and its code changes are gone
# If "final feature" depended on experiment code, you get a conflict

# Alternative: just delete the line (same effect)
pick a1b2c3e feat: add feature part 1
pick b2a3c4d feat: add feature part 2
pick d4e5f6g feat: final feature
# (deleted the experiment line)
The golden workflow for clean PRs: code freely with messy WIP commits, then before creating the PR, run git rebase -i origin/main. Squash your WIP commits into logical, reviewable units. Reviewers see 2-3 meaningful commits instead of 15 "fix typo" commits. This is the difference between amateur and professional Git usage.

Lo kar liya — Key Points:

  • ✅ git rebase -i HEAD~N opens an editor to modify the last N commits
  • ✅ squash combines a commit with the one above it and lets you edit the combined message
  • ✅ fixup combines a commit with the one above it but discards this commit's message (faster)
  • ✅ reword lets you change a commit's message without changing its code
  • ✅ edit pauses the rebase so you can amend the commit (add files, split into two)
  • ✅ drop completely removes a commit and its changes from the history
  • ✅ Reorder commits by moving lines up/down in the rebase editor
  • ✅ Never interactive rebase commits that have been pushed to a shared branch
Course Search
Search across all chapters & stages
📖

Search the course

Type any topic — branching, stash, rebase, hooks — and jump straight to that chapter.

merge branchesgit stashundo commitrebase