Skip to content

Instantly share code, notes, and snippets.

@donnyquixotic
Created July 15, 2025 21:57
Show Gist options
  • Select an option

  • Save donnyquixotic/44b1d11e6d599ea0e85915001a76c4b9 to your computer and use it in GitHub Desktop.

Select an option

Save donnyquixotic/44b1d11e6d599ea0e85915001a76c4b9 to your computer and use it in GitHub Desktop.
Creates alias `git squash` for simple sequential commit squashing
#!/bin/bash
# description:
# creates git alias 'squash' that squashes the last n commits with a new commit message
#
# usage:
# `git squash <n> "<commit-message>"` e.g. `git squash 3 "feat(scope): my new feature"`
# or
# `git squash -n <n> -m "<commit-message>"` e.g. `git squash -n 3 -m "feat(scope): my new feature"`
#
# params:
# <n> number of commits to squash (including current commit)
# <commit-message> the new commit message for the squashed commit
#
# executed commands:
# - validates input parameters
# - checks if enough commits exist to squash
# - resets soft to go back n commits (keeping changes staged)
# - commits with the new message
#
# assumptions:
# - you are in the root directory of the repository
# - you are on the correct branch
# - the number provided includes the most recent/current commit
#
# create alias:
# save this file and run `chmod +x squash-commits.sh && ./squash-commits.sh`
git config --global alias.squash '! _() {
# Parse arguments
if [ "$1" = "-n" ]; then
# Flag-based syntax: git squash -n 5 -m "message"
if [ -z "$2" ] || [ -z "$4" ] || [ "$3" != "-m" ]; then
echo "Usage: git squash -n <number> -m \"<commit-message>\""
return 1
fi
num_commits="$2"
commit_message="$4"
else
# Positional syntax: git squash 5 "message"
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: git squash <number> \"<commit-message>\""
echo " or: git squash -n <number> -m \"<commit-message>\""
return 1
fi
num_commits="$1"
commit_message="$2"
fi
# Validate that num_commits is a positive integer
if ! [[ "$num_commits" =~ ^[1-9][0-9]*$ ]]; then
echo "Error: Number of commits must be a positive integer"
return 1
fi
# Check if we have enough commits to squash
commit_count=$(git rev-list --count HEAD)
if [ "$num_commits" -gt "$commit_count" ]; then
echo "Error: Cannot squash $num_commits commits, only $commit_count commits exist"
return 1
fi
# Check if repository is clean (no uncommitted changes)
if ! git diff-index --quiet HEAD --; then
echo "Warning: You have uncommitted changes. They will be included in the squashed commit."
read -p "Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
return 1
fi
fi
echo "Squashing last $num_commits commits with message: \"$commit_message\""
# Reset to n commits back, keeping changes staged
git reset --soft HEAD~"$num_commits" &&
# Commit with the new message
git commit -m "$commit_message" &&
echo "✓ Successfully squashed $num_commits commits!"
}; _'
echo "Git alias 'squash' has been created successfully!"
echo ""
echo "Usage examples:"
echo " git squash 3 \"feat(auth): add user authentication\""
echo " git squash -n 5 -m \"refactor: optimize database queries\""
echo ""
echo "Note: This will squash the last n commits (including the current one) into a single commit."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment