Interactive Rebase Guide: Clean Up Your Git History
Interactive rebase is the most powerful tool for rewriting your commit history. This guide covers squashing, rewording, dropping, and reordering commits, step by step.
Advanced13 min readUpdated
What is interactive rebase?
git rebase -i (interactive rebase) opens an editor listing your commits, each prefixed with an action keyword. You change the keywords to rewrite history: squash merges commits, reword edits messages, drop removes commits, edit pauses so you can amend, and reorder lets you rearrange the sequence.
The short version
- 1Start:
git rebase -i HEAD~N(N = number of commits to edit) - 2In the editor: change
picktosquash,fixup,reword, ordrop - 3Save and close: Git applies the changes, pausing for conflicts or message edits
- 4Resolve any conflicts:
git add <file>thengit rebase --continue - 5Push the rewritten branch:
git push --force-with-lease
Why it matters
You've been working on a feature for two days. Your commit history looks like this: 'WIP', 'fix', 'fix again', 'actually fix', 'typo', 'debug log remove this'. Now you're opening a PR and you don't want your team to see that mess, or worse, have it live in the repo forever.
A clean commit history isn't vanity: it's documentation. When someone runs git bisect to find a regression, or git blame to understand why a line changed, each commit should tell a clear story. Interactive rebase is the tool that lets you rewrite history before it's shared, turning a chaotic work log into a readable narrative.
Where it usually goes wrong
- Running
git rebase -ion a branch that has already been pushed and shared - Squashing too aggressively: one giant commit tells no story either
- Forgetting to force-push after rewriting a branch (
git push --force-with-lease) - Getting confused by the reversed order in the rebase editor (oldest commit first, newest last)
Interactive rebase, step by step
Step 1: Start an interactive rebase
Run
git rebase -i HEAD~Nwhere N is the number of commits back you want to edit. Git opens your editor with a list of those commits, oldest at the top.Terminal# Rebase the last 4 commits interactivelygit rebase -i HEAD~4# Rebase all commits since branching from maingit rebase -i main# Rebase from a specific commit (exclusive - that commit is not included)git rebase -i a3b2c1d# The editor opens with something like:# pick f7a1b2c WIP: user auth# pick 3d9e4f5 fix# pick a1c2d3e fix again# pick 8b7e6f4 Add user authentication moduleTip: The list shows oldest commit at top, newest at bottom. This is the opposite of
git log, which shows newest first. The rebase replays commits top to bottom.Step 2: Learn the rebase action keywords
Each line in the editor starts with an action. Change the keyword to control what happens to that commit.
Terminal# Available actions (use the full word or just the first letter):# pick p - keep the commit as-is# reword r - keep the commit, but edit its message# edit e - pause rebase here so you can amend the commit# squash s - merge into the previous commit, combining messages# fixup f - merge into the previous commit, discard this message# drop d - remove this commit entirely# exec x - run a shell command after this commit# Example: squash 3 WIP commits into the last one# pick f7a1b2c WIP: user auth# squash 3d9e4f5 fix# squash a1c2d3e fix again# reword 8b7e6f4 Add user authentication moduleTip:
fixupis likesquashbut silently discards the squashed commit's message: ideal for 'typo', 'fix', 'wip' commits where the message adds no value.Step 3: Squash commits into one
The most common use: combine several small commits into a single meaningful one. Mark all commits you want to merge with
squash(ors), keeping the top commit aspick. Git prompts you to write a combined message.Terminal# Before: 4 noisy commits# pick a1b2c3 Add login form HTML# squash d4e5f6 Add form styles# squash 7g8h9i Wire up form submit handler# squash j0k1l2 Add form validation# Git opens another editor to write the combined message:# This is a combination of 4 commits.# Add login form HTML# Add form styles# Wire up form submit handler# Add form validation## Edit to: "Add login form with validation"# Save and close → one clean commitStep 4: Reword a commit message
Use
reword(orr) to fix a bad commit message. The commit content stays identical, only the message changes. Git pauses and opens your editor for each rewarded commit.Terminal# Change 'reword' on the commits you want to rename# pick a1b2c3 fix auth bug# reword d4e5f6 wip remove later# pick 7g8h9i add rate limiting# Git pauses at 'd4e5f6' and opens an editor.# Change the message to something meaningful:# "Sanitize email input before passing to auth provider"# Save → rebase continuesStep 5: Drop unwanted commits
Change
picktodrop(ord) to completely remove a commit from history. Use this to remove debug logs, accidentally committed files, or obsolete experiments.Terminal# Remove a commit entirely# pick a1b2c3 Add feature flag for dark mode# drop d4e5f6 console.log debugging session# pick 7g8h9i Clean up feature flag logic# That commit is gone from history after the rebase.# Warning: if later commits depend on the dropped commit,# you may get conflicts during the rebase.Careful: Dropping a commit that later commits depend on will cause conflicts during rebase. If that happens,
git rebase --abortto start over and reconsider your approach.In Gitoryx: Gitoryx's visual rebase editor shows all commits as cards: drag to reorder, click to squash, drop, or reword.How it works Step 6: Reorder commits
Physically cut and paste lines in the editor to reorder commits. Git replays them in the new order. Useful when a logically distinct commit got mixed up with unrelated work.
Terminal# Before reordering:# pick a1b2c3 Add dark mode toggle# pick d4e5f6 Fix typo in README ← should be first# pick 7g8h9i Refactor theme system# After reordering (move the README fix to top):# pick d4e5f6 Fix typo in README# pick a1b2c3 Add dark mode toggle# pick 7g8h9i Refactor theme systemTip: Reordering can cause conflicts if commits touch the same lines. Git will pause and ask you to resolve them, just like a merge conflict.
Step 7: Split a commit into two
Use
editto pause at a commit. Then usegit reset HEAD~1to unstage it, selectively stage and commit the first part, stage and commit the second part, thengit rebase --continue.Terminal# Mark the commit to split as 'edit'# edit a1b2c3 Add user model and admin panel# Rebase pauses at that commit. Now:# Undo the commit but keep the changesgit reset HEAD~1# Selectively commit the first partgit add src/models/user.tsgit commit -m "Add user model"# Commit the second partgit add src/admin/panel.tsxgit commit -m "Add admin panel scaffold"# Resume the rebasegit rebase --continueStep 8: Push the rewritten branch
After interactive rebase, your local branch has a different history than the remote. A normal
git pushwill be rejected. Use--force-with-lease: it's safer than--forcebecause it refuses to push if someone else has pushed to the branch since your last fetch.Terminal# After rebase, push the rewritten historygit push --force-with-lease origin feature/user-auth# Never use plain --force on shared branches# --force-with-lease refuses if the remote has new commits you haven't seenCareful: Never rebase a branch that other developers are working on. Rewriting history changes commit SHAs. Anyone who already pulled will have a diverged branch. Interactive rebase is safe only on branches that are private to you.
Mistakes to avoid with interactive rebase
- Rebasing a shared branch
- Interactive rebase rewrites every commit SHA from the rebase point onward. If teammates have already pulled those commits, their branches will diverge from yours and they'll need to
git reset --hardor re-clone. This is one of the most disruptive things you can do to a shared repo. - Fix: Only rebase branches that haven't been pushed, or branches that are exclusively yours. If you've already pushed, coordinate with your team before force-pushing the rewritten history.
- Confusing squash and fixup
- Both
squashandfixupmerge commits upward, butsquashopens an editor to combine the messages, whilefixupsilently discards the squashed commit's message. Choosing the wrong one either loses a meaningful message or adds noise from a 'wip' message. - Fix: Use
fixupfor commits with meaningless messages ('fix', 'wip', 'typo'). Usesquashwhen the messages contain useful context you want to preserve in the combined message. - Squashing too aggressively
- Collapsing 30 commits into one 'Add feature X' makes the history clean but kills granularity. Future
git bisectruns or blame archaeology become harder when all changes share one commit. - Fix: Aim for cohesive, logically complete commits, not necessarily one per PR. A PR might legitimately have 3–5 meaningful commits that tell the story of how the feature was built.
- Forgetting --force-with-lease after rebase
- After rewriting history with interactive rebase,
git pushwill be rejected because the remote and local histories diverge. Newcomers often trygit pullfirst, which creates a mess of merge commits that re-introduces the original noisy history. - Fix: After any interactive rebase on a previously pushed branch, always use
git push --force-with-lease. Nevergit pullafter rebasing.
Interactive rebase in Gitoryx
Gitoryx has a built-in interactive rebase editor that lets you drag commits to reorder them, click to squash or drop, and edit messages inline, without touching the terminal or memorizing rebase commands.
- Gitoryx's visual rebase editor shows all commits as cards: drag to reorder, click to squash, drop, or reword.
- No need to remember action keywords: pick, squash, fixup, drop are available as buttons on each commit card.
- Edit commit messages inline in the rebase editor before the rebase runs.
- Gitoryx highlights which commits are already pushed so you know when force-pushing will be required.
- Conflict resolution during rebase is handled in Gitoryx's integrated diff view, with no terminal conflict markers.
Free plan · no account required · macOS, Windows and Linux
Interactive rebase: frequently asked questions
- What is git rebase -i?
git rebase -i(interactive rebase) lets you rewrite the history of a series of commits before sharing them. You can squash, reorder, drop, reword, or split commits using a text editor. It's the primary tool for cleaning up a messy local commit history.- What is the difference between squash and fixup in interactive rebase?
- Both merge a commit into the one above it.
squashopens an editor so you can write a combined commit message from both commits.fixupsilently discards the lower commit's message and keeps only the upper commit's message. Usefixupfor 'wip' and 'fix' commits; usesquashwhen the message contains useful information. - Is it safe to use git rebase -i on a pushed branch?
- No, unless the branch is exclusively yours. Interactive rebase rewrites commit SHAs. If other developers have pulled those commits, their branches will diverge after you force-push. Only rebase commits that haven't been shared, or coordinate carefully with your team before rewriting a shared branch.
- How do I undo an interactive rebase?
- If the rebase is still in progress, run
git rebase --abortto cancel and return to the original state. If the rebase completed, usegit reflogto find the SHA of your branch before the rebase, thengit reset --hard <sha>to restore it. - What does 'edit' do in interactive rebase?
- Marking a commit as
editpauses the rebase after applying that commit. You can then amend the commit (git commit --amend), split it into multiple commits, or make other changes. Rungit rebase --continuewhen you're done to resume. - How do I squash all commits on my feature branch into one?
- Run
git rebase -i main(or whatever your base branch is). In the editor, keep the first commit aspickand change all others tosquashorfixup. Save, and Git combines them and prompts you for the final commit message. - Why does git push fail after interactive rebase?
- Interactive rebase rewrites commit SHAs, so your local branch and the remote branch have diverged histories. Git rejects a normal push to prevent overwriting. Use
git push --force-with-leaseto push the rewritten history safely. - Can I rebase in the middle of a rebase?
- No, you can't start a new rebase while one is in progress. If you're stuck, either
git rebase --continue(after resolving conflicts),git rebase --skip(to skip the current conflicting commit), orgit rebase --abort(to abandon the rebase entirely).
Related reading
- Git Rebase vs Merge: What's the Difference?Tutorial · 10 min
- How to Undo a Commit in Git (The Right Way)Tutorial · 10 min
- Git Cherry Pick Tutorial: Apply Specific CommitsTutorial · 9 min
- Git Interactive Rebase: A Practical GuideBlog · 19 min