Git Squash Commits: Complete Guide (Interactive Rebase, Merge --squash, Fixup) for 2026
Squashing commits is one of the fastest ways to make pull requests easier to review. It turns a noisy sequence of WIP, typo, and fix-later commits into a clean, logical history. But squash is history rewriting, so the workflow must be deliberate.
This guide focuses on practical scenarios: cleaning feature branches before review, using fixup plus autosquash, integrating with merge --squash, and recovering from mistakes without losing work.
Table of Contents
What Squash Actually Changes
A squash combines multiple commits into one commit. That new commit has a new hash and new parent relationship. Even if file content is the same, the commit identity is different.
# Before
A - B - C - D (feature)
# After squash into one commit
A - E (feature)
Because hashes change, squash is safe on your own feature branch but risky on a branch other people already pulled.
When to Squash (and When Not To)
| Scenario | Recommendation | Why |
|---|---|---|
| Local feature branch before PR | Squash with interactive rebase | Cleaner review, linear history, fewer noisy commits |
| Shared branch with multiple collaborators | Avoid rebasing squash | Rewriting hashes will break others' branch state |
| Maintainer merging PR to main | Consider merge --squash |
One integration commit while preserving PR discussion context |
| Audit-heavy repos | Squash carefully with policy | Over-squashing can hide decision timeline details |
Interactive Rebase Squash Workflow
Use this when you want to clean up your own branch before opening or updating a pull request.
# 1) Make sure you are on your feature branch
git checkout feature/refactor-auth
# 2) Fetch latest main
git fetch origin
# 3) Rebase interactively from merge-base to tip
git rebase -i origin/main
Your editor opens a list of commits from oldest to newest:
pick 5f0a1d2 Add auth middleware
pick a23bb9c Fix typo in middleware log
pick c02df3f Add tests for auth middleware
pick de99111 WIP: rename helper function
Convert cleanup commits to squash or fixup:
pick 5f0a1d2 Add auth middleware
fixup a23bb9c Fix typo in middleware log
squash c02df3f Add tests for auth middleware
fixup de99111 WIP: rename helper function
After save/exit:
# Resolve any conflicts if prompted
git add -A
git rebase --continue
# Verify rewritten history
git log --oneline --decorate -n 8
If branch was already pushed, update remote safely:
git push --force-with-lease origin feature/refactor-auth
--force-with-lease, not --force: it refuses to overwrite unexpected remote updates and prevents accidental teammate history loss.
Fixup + Autosquash Workflow
For long-lived branches, make targeted fix commits immediately and auto-fold them later. This is faster than manually editing each rebase todo list.
# Create a fixup commit targeting an older commit
git commit --fixup 5f0a1d2
# Autosquash reorders and marks fixups automatically
git rebase -i --autosquash origin/main
Configure autosquash by default:
git config --global rebase.autosquash true
This pattern keeps active development fast while still producing clean PR history before review.
Using git merge --squash
merge --squash is different from interactive rebase. It does not rewrite the feature branch. Instead, it stages the net diff of another branch as one new commit on the current branch.
# On main (or integration branch)
git checkout main
git pull --ff-only origin main
git merge --squash feature/refactor-auth
git commit -m "feat(auth): refactor middleware and tests"
Use this when maintainers want one clean commit in main but do not need each intermediate feature-branch commit preserved in the mainline history.
Team Policy Patterns
Most teams avoid random per-PR choices by defining one policy per repository:
- Linear-history policy: contributors rebase+squash before review, maintainers fast-forward or rebase-merge.
- Squash-at-merge policy: contributors can keep noisy branch history; maintainer always squash-merges to main.
- Audit-preserving policy: keep granular commits for incident-sensitive repos, squash only trivial fixup commits.
Whichever policy you choose, document force-push rules explicitly. Ambiguous rewrite rules are a common source of avoidable incidents.
Recovery if Squash Goes Wrong
If you accidentally squashed wrong commits, your old branch tip is usually still in reflog.
# Inspect recent HEAD movements
git reflog --date=iso -n 20
# Create a safety branch from old commit
git branch recovery/pre-squash <old-hash>
# Optionally restore current branch
git checkout feature/refactor-auth
git reset --hard <old-hash>
If the bad rewrite was pushed, coordinate with your team, then push the fixed history with --force-with-lease. Do not repeatedly force-push without communication.
FAQ
Should I squash every PR into one commit?
No. A single commit is not always better. Keep separate commits when they represent meaningful independent changes that help review or future bisecting.
Is squash better than merge commits?
Not universally. Squash improves readability, but merge commits preserve integration context. Pick based on team needs, not preference alone.
Can I squash after opening a PR?
Yes. Squash locally, force-push with lease, and the PR updates automatically in GitHub.
Will squash reduce conflicts?
Sometimes during review, yes. But squash does not magically remove real code conflicts; it mostly reduces history noise and commit count.
What is the fastest safe default?
Personal feature branches: develop normally, use --fixup, then run git rebase -i --autosquash origin/main before push/PR update.
Need broader undo/rewrite decisions? Keep these nearby: