A plain git rebase main silently replays commits one by one in their
original order. Interactive (-i) gives you a shell menu over those commits.
Starting it
git rebase -i main # all commits on the current branch since main
git rebase -i HEAD~5 # the last 5 commits
git rebase -i <sha> # commits after the given sha
An editor opens:
pick a1b2c3d add login form skeleton
pick d4e5f6a fix typo
pick 7a8b9c0 wire up backend
pick b1c2d3e address review comments
pick f4e5d6c another fix
# Commands:
# p, pick = use commit
# r, reword = use commit, edit the message
# e, edit = use commit, stop to amend it
# s, squash = meld into previous (combine messages)
# f, fixup = meld into previous, discard its message
# d, drop = remove commit
# x = run a shell command
# b, break = stop here (like edit but without changing the commit)
Change pick to the command you want, save, and exit. Git replays history
according to the new instructions.
Most common uses
Squash noise before a PR.
pick a1b2c3d feat: add login form
squash d4e5f6a fix typo
squash 7a8b9c0 wire up backend
Three commits become one "feat: add login form" with the combined changes. Git prompts you to edit the merged message.
Fixup without editing the message.
Same as squash, but no editor opens. The first commit's message is kept; the others are discarded.
pick a1b2c3d feat: add login form
fixup d4e5f6a fix typo
Reword.
Fix a commit message that is no longer the last one (for the last commit,
git commit --amend is enough).
reword a1b2c3d add login form skeleton
pick d4e5f6a real change
Drop.
Delete an accidental commit (debug print, temporary TODO).
pick a1b2c3d feat: add login form
drop d4e5f6a console.log everywhere
pick 7a8b9c0 real next change
Reordering.
If the commit order is illogical, just move lines around in the editor. Git applies them in the new order (as long as the changes are compatible).
After interactive rebase
Local history is rewritten; the commits have new SHAs. If the branch was
already pushed, you need push --force-with-lease.
Pitfalls
- Do not rebase public branches. Rewritten history breaks everyone who cloned the old version.
- Reordering can cause conflicts. If the second commit depends on the first and you put it first, you will hit a conflict and have to resolve it.
dropdeletes, it does not revert. To publicly undo a commit, usegit revert.dropremoves the commit from history as if it never existed.xis powerful but risky.xruns a shell command after each commit is applied. For example,x make teststops the rebase if tests fail. Useful for verifying that each commit is atomic.