How to squash commits in pull requests

Squashing commits combines multiple small commits into a single cohesive commit, creating cleaner project history and easier code review. As the creator of CoreUI, a widely used open-source UI library, I’ve squashed thousands of commits in pull requests throughout my 25 years of development experience. The most straightforward approach is using GitHub’s squash and merge feature or interactive rebase for local squashing before pushing. This method consolidates work-in-progress commits, fixes typos, and incremental changes into meaningful atomic commits representing complete features.

Use GitHub’s squash and merge button or git rebase -i to combine multiple commits into single commit.

Method 1: GitHub Squash and Merge (easiest):

# After PR is approved, use GitHub interface
# 1. Click "Squash and merge" dropdown
# 2. Select "Squash and merge"
# 3. Edit combined commit message
# 4. Click "Confirm squash and merge"

Using GitHub CLI:

# Squash and merge PR with custom message
gh pr merge 123 --squash --body "Add user authentication

Implemented JWT-based authentication with:
- Login and registration endpoints
- Token refresh mechanism
- Protected route middleware
- Unit tests for auth service

Closes #45"

# View commits that will be squashed
gh pr view 123 --json commits

Method 2: Interactive Rebase (before merging):

# View commits in your feature branch
git log --oneline

# Start interactive rebase (last 5 commits)
git rebase -i HEAD~5

# Or rebase from main branch
git rebase -i main

Interactive rebase editor opens:

pick abc1234 Add login endpoint
pick def5678 Fix typo in login
pick ghi9012 Add registration endpoint
pick jkl3456 Update tests
pick mno7890 Fix lint errors

# Rebase instructions at bottom

Change to squash commits:

pick abc1234 Add login endpoint
squash def5678 Fix typo in login
pick ghi9012 Add registration endpoint
squash jkl3456 Update tests
squash mno7890 Fix lint errors

# This creates 2 commits total

Save and close editor. Git opens commit message editor:

# Combined commit message for squashed commits
Add authentication endpoints

Implemented login and registration endpoints with:
- JWT token generation
- Password hashing with bcrypt
- Input validation
- Comprehensive test coverage

Closes #45

Push squashed commits:

# Force push to update PR (rewrites history)
git push --force-with-lease origin feature-branch

Method 3: Squash Merge Locally:

# Checkout main branch
git checkout main

# Ensure main is up to date
git pull origin main

# Squash merge feature branch
git merge --squash feature-branch

# Review staged changes
git status
git diff --cached

# Create single commit
git commit -m "Add user authentication

Complete authentication system with login, registration,
and JWT token management.

Closes #45"

# Push to main
git push origin main

Here GitHub’s squash and merge button automatically combines all PR commits into one during merge. The interactive rebase with -i flag allows choosing which commits to squash using pick and squash keywords. The pick keeps a commit while squash combines it with the previous pick. Force push with –force-with-lease safely updates the PR branch after rewriting history, failing if remote has unexpected changes. The –squash flag on git merge stages all changes without committing, allowing manual commit message creation. Combined commit messages should explain the complete feature rather than individual changes.

Best Practice Note:

This is the commit squashing strategy we use in CoreUI development to maintain clean, readable project history while preserving detailed development context in PR discussions. Only squash commits after PR is approved to preserve review context during code review, write comprehensive squash commit messages that explain the feature’s purpose and implementation approach, and use –force-with-lease instead of –force when rewriting history to prevent accidentally overwriting others’ work.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
CSS Selector for Parent Element
CSS Selector for Parent Element

How to loop through a 2D array in JavaScript
How to loop through a 2D array in JavaScript

How to check if an element is visible in JavaScript
How to check if an element is visible in JavaScript

How to conditionally add attributes to React components
How to conditionally add attributes to React components

Answers by CoreUI Core Team