Squash several commits into one
How do I combine multiple commits into a single commit?
Short answer
git rebase -i HEAD~n opens an editor where you mark commits as squash or fixup to fold them into the one above. For a quick all-in-one collapse, git reset --soft HEAD~n followed by a fresh commit achieves the same result with no editor at all.
git reset --soft HEAD~3
git commit -m "One clean commit"
Collapses the last 3 commits into a single new one
Does this match your situation?
- Your branch has a trail of 'wip', 'fix typo' and 'try again' commits.
- A reviewer asked you to clean up the history before merging.
- You want one tidy commit per logical change.
Step-by-step fix
The quick way: soft reset and recommit
Moving the branch back n commits with --soft leaves every change staged. Committing once turns them into a single commit. This is the fastest approach when you want all of them collapsed into one.
step 1git reset --soft HEAD~3 git commit -m "Add inventory system with save support"The precise way: interactive rebase
Use this when you want to keep some commits separate. Git lists them oldest-first; change pick to squash to merge a commit into the one above it while combining messages, or fixup to merge it and discard its message.
step 2git rebase -i HEAD~5 # In the editor: # pick a1b2c3d Add inventory system # squash f4e5d6c wip # squash 9a8b7c6 fix typo # pick 1122334 Add save supportThe first line must stay pick — there's nothing above it to squash into.
Write one good message
With squash, Git opens an editor containing all the original messages so you can compose a single coherent one. Delete the noise and describe the change as a whole.
step 3Add inventory system with save support - Grid-based inventory with stacking - Persists to the save file on scene change - Covered by unit testsPush the rewritten branch
Squashing replaces commits with new hashes, so an ordinary push is rejected. Force with lease so you can't accidentally clobber someone else's push.
step 4git push --force-with-leaseOnly on your own branch. If others are working on it, use the platform's Squash and merge button instead.
Why this works
Squashing works because Git doesn't care how many commits produced a given tree — it only records snapshots and parents. A soft reset moves the branch pointer back while leaving the index at your latest state, so the difference between the new base and the index is exactly the combined change of all the commits you skipped. Committing that difference produces one commit with the same final content. Interactive rebase does the same thing per-group, replaying commits and merging the ones you marked.
How to stop it happening again
- Commit in logical units as you go, rather than by the clock.
- Use git commit --fixup <hash> plus git rebase -i --autosquash to mark fixups automatically.
- Or just enable Squash and merge on your pull requests and stop worrying about local history.
Commands used in this guide
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
How do I squash the last 3 commits into one?
git reset --soft HEAD~3 followed by git commit -m "message" is the fastest way — the soft reset leaves all changes staged and the new commit records them as one. Alternatively use git rebase -i HEAD~3 and mark the lower two as squash.
What is the difference between squash and fixup?
Both merge a commit into the one above it. squash opens an editor so you can combine the commit messages, while fixup silently discards the squashed commit's message — ideal for 'fix typo' commits that add nothing to the story.
Can I squash commits that are already pushed?
Yes, but it rewrites history, so you'll need git push --force-with-lease and anyone else on the branch must reset to the new history. On a shared branch, prefer your platform's Squash and merge option, which does it server-side at merge time.
Related rescue guides
How do I break a large commit into multiple commits?
Mark the commit as edit in git rebase -i, then run git reset HEAD~ to turn it back into unstaged changes. Stage the pieces one at a time with git add -p, commit each, and finish with git rebase --continue.
Read the fix →Always fixableHow do I edit a Git commit message?
For the most recent commit, git commit --amend -m "new message" replaces it. For older commits, git rebase -i lets you mark any of them as reword. Both rewrite history, so if the commit is already pushed you'll need git push --force-with-lease and a word to your team.
Read the fix →Usually recoverableMy rebase went wrong and my commits are gone — how do I get them back?
The pre-rebase commits are still in the object database. git reflog shows the position from before the rebase started, and ORIG_HEAD is a shortcut to it — git reset --hard ORIG_HEAD usually restores everything in one command.
Read the fix →Always fixableHow do I undo the last commit but keep my work?
git reset --soft HEAD~1 removes the commit and leaves every change staged, ready to recommit. Use --mixed (the default) if you also want the changes unstaged, and only use --hard if you genuinely want the work destroyed. If the commit is already pushed, revert it instead of resetting.
Read the fix →