Using Git Squash
Table of contents
- What is Squash?
- Why use Squash?
- Method 1: Interactive rebase
- Method 2: Merge --squash
- Method 3: Reset + Commit
- Practical exercise
- Best practices
1 - What is Squash?
Squash lets you combine several commits into a single commit.
Before the squash
A---B---C---D---E feature
| | | |
| | | └── "fix typo"
| | └────── "fix bug"
| └────────── "wip"
└────────────── "feat: login page"
After the squash
A---F feature
|
└── "feat: implement login page"
Commits B, C, D, E are combined into a single commit F.
🔝 Back to table of contents
2 - Why use Squash?
Typical use cases
| Situation | Why Squash |
|---|---|
| "WIP" commits | Clean up before merge |
| Repeated "fix typo" | Professional history |
| Several small commits | A single coherent commit |
| Before a Pull Request | Easier code review |
Advantages
- ✅ More readable history
- ✅ Each commit represents a complete feature
- ✅ Makes reverting easier (a single commit to undo)
- ✅ Simpler code review
Caution
⚠️ Never squash commits that have already been pushed to a shared branch!
🔝 Back to table of contents
3 - Method 1: Interactive rebase
This is the most common and most flexible method.
Syntax
git rebase -i HEAD~N
Where N is the number of commits to modify.
Concrete example
Suppose these 4 commits:
git log --oneline
# e1f2g3h fix: typo in login
# a1b2c3d wip
# x1y2z3a feat: add login form
# m1n2o3p Initial commit
To squash the last 3 commits:
git rebase -i HEAD~3
The editor opens
pick x1y2z3a feat: add login form
pick a1b2c3d wip
pick e1f2g3h fix: typo in login
# Rebase m1n2o3p..e1f2g3h onto m1n2o3p (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
Change to squash
Replace pick with squash (or s) for the commits to merge:
pick x1y2z3a feat: add login form
squash a1b2c3d wip
squash e1f2g3h fix: typo in login
Result
Git asks you to write a new commit message. The 3 commits are merged into one!
Useful commands in interactive rebase
| Command | Action |
|---|---|
pick (p) | Keep the commit as is |
reword (r) | Change the message |
edit (e) | Stop to modify |
squash (s) | Merge with the previous one, combine the messages |
fixup (f) | Merge with the previous one, discard the message |
drop (d) | Delete the commit |
🔝 Back to table of contents
4 - Method 2: Merge --squash
This method squashes an entire branch during the merge.
Syntax
git checkout main
git merge --squash feature/login
git commit -m "feat: implement login feature"
What happens
- Git prepares all the changes from the
feature/loginbranch - Those changes are placed in staging
- You create a single commit with all the changes
- The feature/login branch remains intact
Advantages
- ✅ Simple and fast
- ✅ No interactive rebase
- ✅ Ideal for feature branches
Caution
After a squash merge, the feature branch is NOT merged in the Git sense:
# La branche feature/login apparaîtra toujours comme non-mergée
git branch --merged # feature/login n'y sera pas
You have to delete it manually:
git branch -D feature/login
🔝 Back to table of contents
5 - Method 3: Reset + Commit
A simple but less elegant method.
Syntax
# Reset au commit de départ (garder les modifications)
git reset --soft HEAD~N
# Recréer un seul commit
git commit -m "feat: nouvelle fonctionnalité complète"
Example
# Revenir 3 commits en arrière
git reset --soft HEAD~3
# Vérifier les fichiers en staging
git status
# Créer un nouveau commit
git commit -m "feat: implement complete feature"
The types of Reset
| Option | Working Directory | Staging Area | History |
|---|---|---|---|
--soft | Preserved | Preserved | Rewritten |
--mixed (default) | Preserved | Reset | Rewritten |
--hard | Reset | Reset | Rewritten |
⚠️ Caution with
--hard: you lose the changes!
🔝 Back to table of contents
6 - Practical exercise
Setup
# Créer un dépôt de test
mkdir test-squash
cd test-squash
git init
# Créer plusieurs commits
echo "Line 1" > file.txt
git add file.txt
git commit -m "feat: initial file"
echo "Line 2" >> file.txt
git commit -am "wip"
echo "Line 3" >> file.txt
git commit -am "more wip"
echo "Line 4" >> file.txt
git commit -am "fix typo"
echo "Line 5" >> file.txt
git commit -am "final touches"
Check the history
git log --oneline
You should see 5 commits.
Squash the last 4 commits
git rebase -i HEAD~4
In the editor, keep the first one as pick and change the others to squash:
pick abc1234 wip
squash def5678 more wip
squash ghi9012 fix typo
squash jkl3456 final touches
Write the new message
When the editor opens, replace everything with:
feat: implement complete feature
This commit includes:
- Initial implementation
- Bug fixes
- Final touches
Check the result
git log --oneline
You should now see only 2 commits!
🔝 Back to table of contents
7 - Best practices
When to Squash?
| ✅ Do | ❌ Don't do |
|---|---|
| Before a PR | On already shared commits |
| Local WIP commits | On merge commits |
| Branch cleanup | On main/master |
| Before sharing | If others are working on it |
Recommended workflow
# 1. Développer avec plusieurs petits commits
git commit -m "wip: start feature"
git commit -m "wip: add validation"
git commit -m "fix: handle edge case"
# 2. Avant de créer la PR, squash
git rebase -i HEAD~3
# 3. Un seul commit propre
git push origin feature/my-feature
# 4. Créer la PR sur GitHub
Commit messages after a squash
Take advantage of the squash to write a good message:
feat: implement user authentication
- Add login form with email/password
- Implement JWT token handling
- Add remember me functionality
- Handle authentication errors
Closes #123