Skip to main content

Using Git Squash


Table of contents


  1. What is Squash?
  2. Why use Squash?
  3. Method 1: Interactive rebase
  4. Method 2: Merge --squash
  5. Method 3: Reset + Commit
  6. Practical exercise
  7. 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

SituationWhy Squash
"WIP" commitsClean up before merge
Repeated "fix typo"Professional history
Several small commitsA single coherent commit
Before a Pull RequestEasier 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

CommandAction
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

  1. Git prepares all the changes from the feature/login branch
  2. Those changes are placed in staging
  3. You create a single commit with all the changes
  4. 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

OptionWorking DirectoryStaging AreaHistory
--softPreservedPreservedRewritten
--mixed (default)PreservedResetRewritten
--hardResetResetRewritten

⚠️ 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 PROn already shared commits
Local WIP commitsOn merge commits
Branch cleanupOn main/master
Before sharingIf others are working on it
# 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

🔝 Back to table of contents