Git Rebase — Interactive History Rewriting
A practical cheatsheet for interactive rebase to squash, fixup, reword, reorder, edit and drop commits, plus rebase onto, autosquash and reflog recovery.
Code
Git Rebase Cheatsheet — Interactive History Rewriting
Interactive rebase (git rebase -i) replays a range of commits one by one, letting you edit a
"todo list" to squash, reword, reorder, edit, or drop each one. This cheatsheet collects the verbs,
the --onto and --autosquash workflows, and how to recover with the reflog. Golden rule: only
rewrite commits you have not shared, or be ready to force-push.
Start an interactive rebase
# Edit the last N commits
git rebase -i HEAD~3
# Edit every commit since you branched off main
git rebase -i main
# Edit since a specific commit (the commit itself is the base, NOT included)
git rebase -i <base-commit-sha>
# Rebase the root: include the very first commit too
git rebase -i --root
This opens your $GIT_EDITOR with a todo list, oldest commit at the top, newest at the bottom.
The todo-list verbs
pick 2f1a0bc add login form # use the commit as-is
reword 9c3d4ef fix typo # use commit, but edit its message
edit a1b2c3d add validation # stop here to amend the commit (files/content)
squash 5e6f7a8 wip styling # merge into PREVIOUS commit, combine messages
fixup 8b9c0d1 wip more styling # merge into PREVIOUS commit, DISCARD this message
drop d4e5f6a debug print # remove the commit entirely
exec make test # run a shell command at this point
Short forms also work: p, r, e, s, f, d, x. To reorder, just move the lines.
To drop, either use drop or delete the line.
Finish/abort an in-progress rebase:
git rebase --continue # after resolving conflicts / finishing an `edit` stop
git rebase --skip # skip the current commit
git rebase --abort # bail out, return to the original state
Squash vs fixup
pick aaa add feature
squash bbb tweak feature # opens editor to write a combined message
pick aaa add feature
fixup bbb tweak feature # silently folds bbb into aaa, keeps aaa's message
Use squash when you want to curate the combined message; use fixup when the extra commit is
pure noise.
Reword a commit message
reword 9c3d4ef fix typo
Git replays up to that commit, then opens the editor so you can rewrite its message. To reword only the most recent commit, you don't need rebase at all:
git commit --amend
Edit (amend) an older commit
Mark it edit; the rebase pauses with that commit checked out:
git rebase -i HEAD~4 # set the target line to: edit <sha>
# rebase stops on that commit:
# change files, then:
git add <files>
git commit --amend # rewrite the commit (use --no-edit to keep its message)
git rebase --continue
You can also split one commit into two while stopped on an edit:
git reset HEAD^ # un-commit, keep changes in the working tree
git add part1 && git commit -m "first half"
git add part2 && git commit -m "second half"
git rebase --continue
rebase --onto (transplanting commits)
--onto moves a slice of commits onto a new base. Syntax:
git rebase --onto <new-base> <old-base> <branch>
# You branched feature off the wrong base; move it onto main.
# Replay commits after `wrong-base` onto `main`:
git rebase --onto main wrong-base feature
# Drop the first few commits of a branch: replay everything after
# <keep-from> directly onto main.
git rebase --onto main <keep-from> HEAD
# Move a sub-branch (topic) that sits on top of `feature` onto `main`:
git rebase --onto main feature topic
Read it as: take the commits in (<old-base>, <branch>] and re-apply them starting from
<new-base>.
Autosquash (automate fixups)
Create "fixup!" or "squash!" commits that target an earlier commit, then let rebase order and fold them automatically.
# Make a commit that will fold into <sha> (message is auto-prefixed):
git commit --fixup <sha>
git commit --squash <sha> # combine message instead of discarding
# Then run rebase with autosquash; the todo list is pre-arranged:
git rebase -i --autosquash <base>
Make it the default so you can drop the flag:
git config --global rebase.autosquash true
--fixup=amend:<sha> lets you change both the content and the message of the target commit during
autosquash.
Recovering with the reflog
The reflog records every position HEAD (and each branch) has had — even "lost" commits from a
botched rebase are reachable for ~90 days.
git reflog # list recent HEAD positions with shas
git reflog show <branch> # reflog for a specific branch
# Find the entry from BEFORE the rebase, e.g. "rebase (start)" or your last good commit:
# abc1234 HEAD@{5}: commit: good state
# Hard-reset the branch back to that point:
git reset --hard HEAD@{5}
# or by sha:
git reset --hard abc1234
If you only need one lost commit, cherry-pick it instead of resetting everything:
git cherry-pick <lost-sha>
Common workflows
1. Clean up a messy feature branch before opening a PR
git rebase -i main
# In the editor: keep the first real commit as `pick`,
# turn the "wip"/"fix" commits into `fixup`, `reword` the headline.
git push --force-with-lease
2. Quick fix to an older commit using autosquash
# Find the commit that introduced the bug:
git log --oneline
# Fix the code, then attach the fix to that commit:
git add src/auth.js
git commit --fixup 2f1a0bc
git rebase -i --autosquash main # rebase folds it in automatically
3. Move a branch off a stale base onto the latest main
git fetch origin
git rebase --onto origin/main old-main feature
# resolve any conflicts:
git add . && git rebase --continue
git push --force-with-lease
4. Undo a rebase that went wrong
git reflog
# spot the pre-rebase HEAD, e.g. HEAD@{7}
git reset --hard HEAD@{7}
Gotchas / tips
- Never rebase shared history unless everyone agrees — rewritten shas force collaborators to
re-sync. Coordinate, then push with
--force-with-lease(safer than--force: it refuses if the remote moved). squash/fixupfold into the commit above them in the list; the first line can't be a squash/fixup.- Conflicts pause the rebase. Fix files,
git addthem, thengit rebase --continue— do notgit commit. - The base commit in
git rebase -i <sha>is not included in the editable range; it's the starting point. - Set
git config --global rerere.enabled trueto have Git remember and reapply your conflict resolutions across repeated rebases. - Use
git rebase -i --exec "npm test" mainto run tests after every replayed commit and catch the exact one that breaks. - Lost commits are not gone immediately —
git reflogplusgit reset --hardorgit cherry-pickalmost always brings them back. - Prefer
git pull --rebaseto keep a linear history instead of merge commits when updating a branch.
Git Rebase — Interactive History Rewriting
A copy-and-go reference for rewriting Git history with git rebase -i. It covers the
interactive todo verbs (squash, fixup, reword, edit, drop, reorder), moving
branches with rebase --onto, automating cleanups with --autosquash, and getting your
work back with the reflog when something goes wrong.
When to use it
Reach for interactive rebase when you want to tidy a feature branch before sharing it:
collapse “wip” commits, fix a typo in an old message, reorder logically, or surgically edit a
commit deep in the branch. Use --onto to transplant commits onto a different base.
Use with care
Rebasing rewrites commit hashes, so only rebase commits you have not yet pushed to a shared
branch (or coordinate a force-push). If anything looks wrong, nothing is truly lost — git reflog
remembers where every branch pointed, so you can always reset back.
Full cheatsheet
The complete, copy-pasteable reference lives in the snippet: snippets/markdown.md.