What Is Git Squash? Clean Up Your Commits

Ever stared at a git history cluttered with “fix typo” and “oops” commits? What is git squash and why do professional developers swear by it?
Git squash lets you combine multiple commits into one clean, meaningful change. This powerful version control technique helps maintain a readable git history while preserving your work. Instead of showing every step of your coding journey (including mistakes), squashing presents a polished snapshot of feature completion.
Whether you’re preparing a pull request for code review or cleaning up your local git repository, understanding how to effectively collapse commits will transform your git workflow. Linus Torvalds designed git to be flexible, and squashing exemplifies this philosophy by letting developers rewrite history in useful ways.
In this guide, you’ll learn:
- How git combines multiple commits behind the scenes
- Step-by-step instructions for different squashing methods
- Real-world scenarios where squashing improves collaboration
- Common mistakes to avoid when rewriting commit history
What Is Git Squash?
Git squash is the process of combining multiple commits into a single commit. It’s commonly used during a rebase to clean up a commit history before merging, making the project history more concise and easier to understand by reducing unnecessary or messy commits.
How Git Squash Works

Git squash is a powerful technique in version control that lets developers combine multiple commits into one. It’s not a standalone git command but rather a concept applied through different git operations. Many developers use squashing to maintain a clean git history in their repositories.
The Technical Basics of Squashing
When you squash commits, git doesn’t simply delete your previous work. Behind the scenes, git creates a new commit that contains all the changes from the commits being combined. Your code collaboration efforts remain intact while the commit tree gets simplified.
The process works by:
- Identifying the range of commits to combine
- Applying all changes as a single patch
- Creating a new commit with these combined changes
- Replacing the selected commit range with this new commit
Git commit messages from the original commits don’t disappear automatically. During the squashing process, git gives you the opportunity to create a new message. Many developers write helpful commit messages that summarize all the changes, while others prefer to keep the most descriptive message from the original commits.
Code change consolidation happens at the file level. Git tracks changes line by line, so when commits are squashed, git repository management becomes more streamlined by showing the net effect of several changes rather than each incremental step.
Different Ways to Squash Commits
There are several approaches to squashing in git workflow optimization. Each serves different needs in your software development process.
Using interactive rebase (git rebase -i)
The most common method involves git interactive rebase. This approach offers the most flexibility for commit manipulation:
git rebase -i HEAD~3
This command starts an interactive rebase session where you can mark commits as “squash” or “fixup” to combine them with previous commits. The interactive mode gives you full control over which commits to combine and how to handle their messages.
Squashing during merges with –squash flag
For those who prefer simplicity, the git squash merge option provides a straightforward way to combine all commits from a branch:
git merge --squash feature-branch
This condenses all commits from feature-branch into a single set of changes, which you can then commit with a new message. This approach works well for pull request workflows in GitHub or GitLab.
Other Git commands that can combine commits
Some developers use alternative methods like git reset combined with git commit to achieve similar results:
git reset --soft HEAD~3
git commit -m "Combined message"
This technique works best for quick commit compression of recent changes that haven’t been pushed to remote repositories yet.
What Makes a Good Squashed Commit
A good squashed commit achieves balance. It should combine related changes while keeping the git history meaningful.
When writing commit messages for squashed commits, be thorough but concise. Explain the what and why, not just the how. Good messages help other developers understand your changes without needing to read each line of code.
Keep related changes together when squashing. If you fixed multiple bugs, group the fixes logically. If you’re working on distinct features, consider keeping them as separate commits even after squashing smaller incremental changes within each feature.
Remember that commit size matters. A massive commit containing unrelated changes makes code review difficult. GitLab and GitHub both offer options to view changes in manageable chunks, but a well-structured commit helps your colleagues review your work more effectively.
Step-by-Step Guide to Squashing Commits

Using Interactive Rebase to Squash Commits
Interactive rebase is the Swiss Army knife of git history modification. Here’s how to use it effectively:
- First, identify how many commits you want to squash. Use
git log
to check your commit history. - Run the interactive rebase command:
git rebase -i HEAD~N
Replace N with the number of commits you want to include.
- Your default text editor will open with a list of commits. Change the word “pick” to “squash” or “s” for the commits you want to combine. The first commit should remain as “pick”.
- Save and close the editor. Another window will open allowing you to edit the commit message.
- Write a comprehensive message describing all the changes.
- Save and close. Git will combine the commits.
During this process, git might detect conflicts if the same lines were changed in different commits. You’ll need to resolve these manually before continuing the rebase operation.
The command line can be intimidating for beginners, but git GUIs like GitKraken or SourceTree offer visual interfaces for squashing that some developers find more intuitive.
Squashing During Merge Operations
The --squash
flag provides a simpler approach:
- Make sure you’re on the branch you want to merge into:
git checkout main
- Use the merge command with the squash flag:
git merge --squash feature-branch
- This stages all changes but doesn’t commit them automatically.
- Create a new commit with a clear message:
git commit -m "Add user authentication feature"
This method differs from interactive rebase because it doesn’t rewrite history. It simply takes all the changes from one branch and applies them as uncommitted changes to your current branch.
Merge squashing works best when you’ve completed a feature and want to present it as a single logical unit. This approach is popular in distributed version control environments where keeping the main branch history clean is a priority.
Squashing the Last N Commits
For quick history cleanup of recent work:
- Use the soft reset method:
git reset --soft HEAD~3
This preserves your changes while removing the last three commits.
- Create a new combined commit:
git commit -m "Complete user profile editing features"
This technique is particularly useful during personal development before pushing changes to remote repositories. It helps you collapse those “oops” commits where you fixed small mistakes.
The HEAD reference is crucial in these operations. It points to the tip of your current branch, and by specifying HEAD~N, you’re telling git to go back N commits from your current position.
Remember that squashing commits that have already been pushed and shared with others can cause problems. Git push will be rejected if you try to force-push squashed commits over previously pushed ones, unless you use -f
(force) option, which should be used with extreme caution.
When dealing with local branches that haven’t been shared, squashing helps maintain a clean commit tree without the clutter of incremental development steps. Source code management becomes more organized when the history reflects logical units of work rather than moments in time.
Continuous integration environments often benefit from squashed commits as they represent complete, testable features rather than potentially broken intermediate states. This leads to more reliable automated testing and clearer points for rollbacks if necessary.
By mastering these squashing techniques, you’ll improve your git workflow and make code collaboration smoother for everyone on your team.
Real-World Squash Scenarios
Git squash transforms messy commit histories into clean, logical sequences. Let’s explore practical scenarios where commit compression truly shines.
Cleaning Up a Feature Branch Before Pull Request
The most common squashing scenario happens right before submitting a pull request. Developers often make dozens of small commits while building features – some to save progress, others to fix bugs along the way.
When identifying commit groups that should be combined, look for:
- Several commits addressing the same component
- A main implementation commit followed by small fixes
- Commits with messages like “WIP” or “Fix typo”
Squashing creates a logical commit sequence that tells a story. Instead of seeing twenty commits with cryptic messages, reviewers get three or four substantial commits that each represent a complete thought or feature component. This makes code review workflows far more effective.
# Example of condensing 15 commits into 3 logical units
git rebase -i HEAD~15
Best timing for squashing in the development cycle:
- After feature completion but before code review
- When merging a long-running branch back to main
- After addressing review feedback but before final merge
BitBucket, GitHub, and GitLab all provide options to squash commits during merge operations. This repository maintenance feature helps teams maintain clean git history without manual interactive rebasing.
Fixing Mistakes with Squash
We all make mistakes. Git squash lets you hide those embarrassing “oops” commits where you fixed a typo or forgot to remove debugging code.
Using interactive rebase, you can mark these fixup commits with “fixup” instead of “squash”:
pick abc1234 Add user authentication feature
fixup def5678 Fix typo in auth function name
fixup ghi9101 Remove debug console.log
This combines the fixes with their parent commits while discarding the fix messages entirely. Git command usage like this saves teams from history littered with trivial corrections.
For frequent small fixes, some developers create git aliases:
git config --global alias.fixup "commit --fixup"
Then you can quickly create a fixup commit with:
git fixup abc1234
This marks the commit to be automatically combined during the next interactive rebase, making git commit manipulation more efficient.
Team Collaboration with Squash
Teams need clear standards for when to squash. Some follow a “squash before share” approach, while others prefer preserving detailed history.
Setting team standards might include:
- Always squash topic branches before merging to main
- Never squash commits in the main branch
- Squash WIP and fixup commits, but preserve logical steps
Communication practices matter too. When squashing affects shared code, inform teammates through chat or comments on the pull request. This prevents confusion when someone’s local branch suddenly diverges from the remote after a force push.
Some teams configure GitHub PR squash options in their repositories to ensure consistent practices across all contributions. This git repository management approach works well for large open source projects with many contributors.
Squash Best Practices and Common Mistakes
Squashing is powerful but requires judgment. Learn when to squash, when to refrain, and how to avoid common pitfalls.
When You Should Squash Commits
Feature completion milestones mark perfect squashing opportunities. Once a feature works properly and passes tests, combine the incremental development commits into a cohesive whole.
Before creating pull requests:
Pull requests should tell a story. Reviewers don’t need to see every step of your thought process – they need to understand the final solution. Squashing helps create this narrative by grouping related small changes.
When working on a large feature, consider an intermediate approach: squash related commits into logical groups rather than one massive commit. For example, separate data model changes, UI implementation, and test additions.
Git workflow optimization often includes squashing:
- Implementation commits with their immediate fixes
- Multiple style or formatting changes
- Several commits that together complete a single user story
When You Should Not Squash Commits
Preserving development steps sometimes provides value. Consider keeping separate commits when:
- Different commits address distinct concerns or features
- The commit sequence demonstrates an important technical decision progression
- You’re documenting an approach for educational purposes
After pushing to shared branches:
Once commits have been pushed and others have based work on them, squashing creates more problems than it solves. Git terminal warriors might handle the consequences, but less experienced team members will struggle.
Linus Torvalds, the creator of Git, designed the tool to maintain history integrity. When multiple developers collaborate, preserving the shared history often outweighs the benefits of a cleaner log.
Some changes deserve separate commits even if they’re small. Security fixes, performance improvements, and accessibility changes often warrant their own entries in history to make them easy to identify later.
Common Squash Mistakes and How to Avoid Them
Losing important context by over-squashing happens frequently. When everything becomes one massive commit, useful information vanishes. Context matters – keep meaningful distinctions between different parts of your work.
Creating massive, hard-to-understand commits makes review difficult. A single commit changing hundreds of files across multiple concerns becomes practically unreviewable. Aim for commits that represent logical units of work.
Squashing commits that have already been pushed creates headaches. If others have pulled your branch, your force push after squashing will cause their history to diverge, requiring complex operations to reconcile. The golden rule: don’t rewrite history that others rely on.
Handling merge conflicts during squash operations requires care. When conflicts arise during interactive rebase:
- Fix the conflict in each file
- Stage the fixes with
git add
- Continue with
git rebase --continue
- If things get too complex, abort with
git rebase --abort
and try a different approach
Software engineering teams often establish git best practices that include:
- Maximum commit size guidelines
- Standards for commit messages
- Rules about when squashing is appropriate
Some companies use DevOps tools to enforce these policies through CI/CD pipelines and git hooks, ensuring history remains clean without manual intervention.
Command line interface proficiency helps resolve issues when squashing goes wrong. The git reflog command serves as a safety net, allowing you to find commits that were “lost” during a rebase operation:
git reflog
# Find the SHA of the commit before your troublesome rebase
git reset --hard SHA
This technique recovers from almost any git history modification mistake. Source code management becomes less stressful when you understand these recovery options.
Remember that the goal of squashing isn’t perfect history – it’s useful history. A commit history should help other developers understand the project’s evolution, not document every keystroke along the way. Finding the balance takes practice, but the results make collaboration smoother for everyone involved in the git workflow.
Git Squash in Different Workflows
Git squash adapts to various team structures and platforms. Different teams leverage squashing differently, depending on their specific needs and tools.
Squash in GitHub and Pull Request Workflows
GitHub’s built-in “Squash and merge” option simplifies commit compression for teams. Click a button, and GitHub handles the complex git commands. Simple.
When using this feature, pay attention to PR titles and descriptions. GitHub automatically generates the squashed commit message based on these fields. A descriptive PR title becomes your commit heading, while the description fills the body.
Best practices for PR titles:
- Include a ticket/issue number for traceability
- Start with a verb to indicate action (Add, Fix, Update)
- Keep it under 50 characters when possible
- Mention the component being changed
GitHub handles commit messages during squash differently than manual interactive rebase. By default, it combines all commit messages into one, appending them to the PR description. This preserves the context but can create unwieldy messages.
# Example of how GitHub formats squashed commit messages
Add user authentication feature (#123)
* Create authentication controller
* Add login form component
* Implement JWT token handling
* Fix redirect after login
For frequent contributors, GitHub offers repository settings to customize this behavior. Repository maintenance becomes easier when team members follow consistent practices.
Squash in GitLab and Merge Request Workflows
GitLab provides similar squash options during merges but with some key differences. The platform supports:
- Default squash settings at project level
- Per-merge request squash toggle
- Customizable commit message templates
GitLab’s configuration settings for teams allow repository administrators to enforce squashing policies. Some teams require squashing for all merge requests, while others leave it optional.
Key GitLab squash settings:
- Squash always – Forces squashing for all merge requests
- Squash when pipeline succeeds – Automatically squashes after CI passes
- Default squash commit message – Template for standardizing messages
Unlike GitHub, GitLab preserves authorship information more explicitly in the interface. This makes git history rewriting less disruptive to attribution. Code collaboration remains seamless while maintaining clean history.
Squash in Continuous Integration Environments
Clean history helps with CI/CD in several ways. Squashing creates discrete, testable units that represent complete features rather than partial implementations.
When a test fails, bisecting a clean history makes finding the problematic change easier. With each commit representing a logical, complete change, debugging becomes more efficient.
Integration with build tools and pipelines benefits from squashed commits as well. Jenkins, CircleCI, and other DevOps tools can trigger builds on each commit. Fewer, more meaningful commits reduce unnecessary builds.
Some teams configure git hooks to automatically suggest squashing when commits match certain patterns:
# Pre-push hook example that suggests squashing
if git log @{u}.. | grep -i "fixup" > /dev/null; then
echo "WARNING: You have fixup commits that should be squashed before pushing"
exit 1
fi
This approach to git workflow optimization ensures code quality while maintaining an organized repository.
Advanced Squash Techniques
Beyond basic squashing, advanced techniques help tackle complex situations and automate common tasks.
Partial Squashing and Cherry-Picking
Sometimes you want to retain some commits while combining others. Partial squashing gives you this flexibility.
To select specific commits to combine:
- Start an interactive rebase that includes all commits of interest
- Mark only certain commits as “squash” or “fixup”
- Leave others as “pick”
This maintains some granularity while cleaning history. The technique works well for feature branches with several distinct components.
# Example of partial squashing
pick abc1234 Add user model
pick def5678 Add user controller
squash ghi9101 Fix user controller validation
pick jkl1121 Add user views
squash mno3141 Fix spacing in views
squash pqr5161 Update view tests
Using cherry-pick with squash creates even more targeted changes. First cherry-pick a range of commits, then squash them:
git cherry-pick A..B
git reset --soft HEAD~3
git commit -m "Combined feature X changes"
This approach lets you extract specific changes from one branch, combine them, and apply them elsewhere. Source code management becomes more surgical, especially in complex projects.
Automating Squash Operations
Git aliases save time for routine squashing tasks. Add these to your git config:
# Create a fixup commit automatically
git config --global alias.fixup "commit --fixup"
# Squash all fixup commits automatically
git config --global alias.squash-all "!f(){ git rebase -i --autosquash \$(git merge-base HEAD ${1:-master}); }; f"
The second alias automatically squashes all commits marked as fixup between your current branch and the target branch (defaulting to master if not specified).
Scripts help with more complex scenarios:
#!/bin/bash
# squash-wip.sh - Squashes all commits with "WIP" in the message
COMMITS=$(git log --format="%h %s" | grep -i wip | cut -d' ' -f1)
if [ -n "$COMMITS" ]; then
for commit in $COMMITS; do
git commit --fixup=$commit
done
git rebase -i --autosquash HEAD~$(echo "$COMMITS" | wc -l)
fi
Git hooks enforce squash policies by checking commit messages or branch names before allowing pushes. This automation ensures consistent practices across teams.
For open source projects, maintaining clean git history through squashing helps new contributors understand the codebase. Linus Torvalds and other software development leaders advocate for meaningful commit history that tells a story.
Recovering from Squash Mistakes
Git reflog serves as a safety net for recovering lost commits. If you squash too aggressively or make other mistakes, the reflog remembers everything:
git reflog
# Find the SHA before your mistake
git reset --hard SHA
The reflog records all HEAD movements for about 90 days by default. This gives you plenty of time to recover from history rewriting accidents.
For more complex recovery scenarios:
- Create a new branch from the current state
- Reset hard to the pre-mistake state
- Cherry-pick the changes you want to keep
- Merge or replace the problematic branch
Safety measures before major history changes include:
- Creating backup branches
- Pushing to remote backup branches
- Committing all working changes
- Understanding exactly which commits will be affected
Remember that git commit manipulation can be dangerous. The famous git command status “You are now in ‘detached HEAD‘ state” often appears during recovery operations. Don’t panic – it simply means your HEAD isn’t pointing to a named branch.
With these advanced techniques, even complex git history modification becomes manageable. Version control tidying becomes less intimidating, and maintaining a clean repository becomes second nature. Your team will appreciate the clear, logical history that results from thoughtful squashing practices.
FAQ on What Is Git Squash
What exactly is git squash?
Git squash is a technique for combining multiple commits into a single commit. It’s not a standalone git command but rather an operation performed during interactive rebase or merge. Squashing helps clean your git history by condensing numerous small commits into logical, cohesive changes that make your repository more maintainable.
How do I squash my last 3 commits?
git rebase -i HEAD~3
Change “pick” to “squash” for the commits you want to combine (usually all except the first one). Save, then edit the combined commit message. This git interactive mode allows you to rewrite history for cleaner version control, making your git workflow more professional.
Can I squash commits that have already been pushed?
Yes, but with caution. Squashing pushed commits requires a force push (git push -f
), which rewrites shared history. This can cause problems for teammates who’ve already pulled those commits. Never force push to main branches in collaborative projects. History rewriting should be limited to personal feature branches.
What’s the difference between merge and squash?
Regular merge preserves all commit history from the source branch. Squash merge takes all changes from the source branch and applies them as a single new commit on the target branch. GitHub’s “Squash and merge” option provides this code change consolidation with a single click in pull request workflows.
Is squashing commits a good practice?
It depends on your team’s workflow. Squashing creates a cleaner, more readable git log by grouping related changes. But it also removes the detailed step-by-step development history. Most teams prefer squashing for feature branches before merging to main branches, maintaining a clean commit tree while preserving important milestones.
How do I squash commits in GitHub?
GitHub offers a “Squash and merge” button when merging pull requests. This automatically combines all PR commits into one. You can edit the resulting commit message before confirming. For repository maintenance, some teams configure GitHub to make squashing the default merge option for cleaner source code management.
Can I recover commits after squashing?
Yes. Use git reflog
to find the SHA of commits before your squash operation, then use git reset --hard SHA
to restore them. Git keeps a record of HEAD movements in the reflog for about 90 days by default, providing a safety net for git history modification mishaps.
What should I write in a squashed commit message?
A good squashed commit message summarizes all combined changes concisely. Start with a brief, descriptive title (under 50 characters). Then add details about what changed and why. Don’t just concatenate all previous messages. GitLab and other platforms let you customize templates for consistent messaging across teams.
Will squashing lose any code changes?
No, squashing preserves all code changes. It only combines the commits, not the content. The resulting commit contains all modifications from the squashed commits. Your code collaboration efforts remain intact. Only the granular history of how those changes evolved gets simplified during commit compression.
When should I avoid squashing commits?
Avoid squashing when:
- Working on shared branches others have based work on
- The individual commits represent meaningful, separate changes
- Detailed history provides useful context for future developers
- Your team uses git bisect regularly to find bugs
- You’re contributing to open source projects with specific commit guidelines
Linus Torvalds designed git to preserve history, so consider whether detailed steps or clean summaries better serve your software development needs.
Conclusion
Understanding what is git squash transforms how developers maintain their repositories. This technique isn’t just about tidying commit history – it’s about crafting a narrative that helps future contributors understand your code’s evolution. By collapsing multiple commits into cohesive units, you create a git log that tells a story rather than documenting every keystroke.
Git commit manipulation requires judgment. Squashing every commit would lose valuable context, while never squashing creates noise. The key benefits of adopting squashing in your workflow include:
- Cleaner pull requests that reviewers can actually understand
- More meaningful git blame output for debugging
- Easier bisecting when hunting for bugs
- Improved repository performance with fewer refs to track
Command line proficiency with git squash commands takes practice. Start small by combining fixup commits on personal branches before tackling more complex interactive rebasing scenarios. Whether you’re using GitHub’s one-click solutions or crafting custom git aliases for commit aggregation, the goal remains the same: a git history that helps rather than hinders.
As Linus Torvalds wisely structured git, version control should serve developers, not the other way around.
- What Is a Bare Repository? When and Why to Use One - June 11, 2025
- What Is Git Bisect? Debugging with Binary Search - June 10, 2025
- What Is Upstream in Git? Explained with Examples - June 9, 2025