with one click
squash-commits
// Squashes multiple commits on the current branch into a single commit with an auto-generated message, then pushes.
// Squashes multiple commits on the current branch into a single commit with an auto-generated message, then pushes.
Adds a GitMCP URL for a GitHub repository to the project's MCP configuration (.cursor/mcp.json).
Manage GitHub issues - create, edit, close, comment, assign, and delegate to Copilot. Uses GitHub MCP.
Safely rebases the current feature branch onto a target branch (default: main) with automatic stash handling and conflict detection.
| name | squash-commits |
| description | Squashes multiple commits on the current branch into a single commit with an auto-generated message, then pushes. |
You are a Git workflow assistant that squashes commits on the current branch into a single commit.
| Parameter | Required | Default | Description |
|---|---|---|---|
mode | No | since-branch | How to determine which commits to squash |
base_branch | No | main | For since-branch mode: the branch we branched from |
commit_count | No | - | For count mode: number of commits to squash |
since_commit | No | - | For since-commit mode: squash all commits after this SHA |
since-branch (Default)Squash all commits since branching from a base branch.
Example invocations:
maindevelopfeature/basecountSquash a specific number of recent commits.
Example invocations:
since-commitSquash all commits after a specific commit SHA.
Example invocations:
Before doing anything, verify ALL of the following. If ANY check fails, STOP and report the issue:
.git/rebase-merge, .git/MERGE_HEADorigin remote must be configured (for push)If anything fails after we start modifying history:
git rebase --abort if in rebasegit reset --hard ORIG_HEAD to restore if reset was used# Save current branch name
CURRENT_BRANCH=$(git branch --show-current)
# Verify not on protected branch
case "$CURRENT_BRANCH" in
main|master|develop)
echo "ERROR: Cannot squash commits on protected branch '$CURRENT_BRANCH'"
exit 1
;;
esac
# Save current HEAD for recovery
ORIGINAL_HEAD=$(git rev-parse HEAD)
# Check for uncommitted changes
if ! git diff --cached --quiet || ! git diff --quiet; then
echo "ERROR: You have uncommitted changes. Please commit or stash them first."
exit 1
fi
For since-branch mode (default):
BASE_BRANCH="${USER_SPECIFIED_BASE:-main}"
# Find the merge-base (where this branch diverged from base)
MERGE_BASE=$(git merge-base HEAD "$BASE_BRANCH" 2>/dev/null || \
git merge-base HEAD "origin/$BASE_BRANCH" 2>/dev/null)
if [ -z "$MERGE_BASE" ]; then
echo "ERROR: Cannot find common ancestor with '$BASE_BRANCH'"
exit 1
fi
# Count commits to squash
COMMIT_COUNT=$(git rev-list --count "$MERGE_BASE"..HEAD)
For count mode:
COMMIT_COUNT=$USER_SPECIFIED_COUNT
# Verify we have enough commits
TOTAL_COMMITS=$(git rev-list --count HEAD)
if [ "$COMMIT_COUNT" -gt "$TOTAL_COMMITS" ]; then
echo "ERROR: Requested $COMMIT_COUNT commits but branch only has $TOTAL_COMMITS"
exit 1
fi
# Calculate the base commit
MERGE_BASE=$(git rev-parse "HEAD~$COMMIT_COUNT")
For since-commit mode:
SINCE_COMMIT=$USER_SPECIFIED_COMMIT
# Verify commit exists and is an ancestor
if ! git merge-base --is-ancestor "$SINCE_COMMIT" HEAD; then
echo "ERROR: Commit '$SINCE_COMMIT' is not an ancestor of current HEAD"
exit 1
fi
MERGE_BASE=$SINCE_COMMIT
COMMIT_COUNT=$(git rev-list --count "$MERGE_BASE"..HEAD)
if [ "$COMMIT_COUNT" -lt 2 ]; then
echo "ERROR: Need at least 2 commits to squash. Found: $COMMIT_COUNT"
exit 1
fi
echo "Will squash $COMMIT_COUNT commits into one"
echo "Commits to be squashed:"
git log --oneline "$MERGE_BASE"..HEAD
Analyze all commits being squashed and create a meaningful message:
# Collect all commit messages
ALL_MESSAGES=$(git log --format="%s%n%b" "$MERGE_BASE"..HEAD)
# Collect summary of files changed
FILES_CHANGED=$(git diff --stat "$MERGE_BASE"..HEAD)
# Get the branch name for context
BRANCH_CONTEXT=$(echo "$CURRENT_BRANCH" | sed 's/[-_]/ /g')
Generate a commit message that includes:
Example generated message:
feat: Add user authentication flow
Squashed commits:
- Add login form component
- Implement JWT token handling
- Add password validation
- Fix login redirect bug
- Update tests for auth flow
Files changed: 12 files, +450/-23 lines
Method: Soft Reset + Commit (simpler than interactive rebase)
# Soft reset to merge base - keeps all changes staged
git reset --soft "$MERGE_BASE"
# Commit with the generated message
git commit -m "$GENERATED_MESSAGE"
If this fails:
# Restore original state
git reset --hard "$ORIGINAL_HEAD"
echo "ERROR: Squash failed. Original state restored."
exit 1
# Force push with lease for safety
if ! git push --force-with-lease origin "$CURRENT_BRANCH"; then
echo "WARNING: Push failed. Possible reasons:"
echo " - Someone else pushed to this branch"
echo " - Remote branch doesn't exist yet (try: git push -u origin $CURRENT_BRANCH)"
echo ""
echo "Local squash is intact. You can:"
echo " - Retry with: git push --force-with-lease origin $CURRENT_BRANCH"
echo " - Or undo with: git reset --hard $ORIGINAL_HEAD"
fi
echo ""
echo "=== Squash Complete ==="
echo "Branch: $CURRENT_BRANCH"
echo "Commits squashed: $COMMIT_COUNT → 1"
echo "New commit: $(git rev-parse --short HEAD)"
echo ""
echo "Original HEAD was: $ORIGINAL_HEAD"
echo "To undo: git reset --hard $ORIGINAL_HEAD && git push --force-with-lease"
| Scenario | Action |
|---|---|
| On protected branch (main/master/develop) | STOP immediately |
| Uncommitted changes | STOP: ask user to commit or stash |
| Base branch doesn't exist | STOP: report and suggest alternatives |
| Less than 2 commits | STOP: nothing to squash |
| Reset fails | Restore with git reset --hard ORIG_HEAD |
| Push fails | Report warning, keep local squash, provide recovery command |
Report the following:
{branch_name}{since-branch|count|since-commit}{branch_name|commit_sha}{count}git log --oneline of commits being squashed){success|failed}{success|failed|skipped}--force-with-lease to prevent overwriting others' workWhen generating the commit message, the agent should:
Analyze the branch name — Extract feature/fix/refactor context
feature/user-auth → "feat: User authentication"fix/login-bug → "fix: Login bug"refactor/api-cleanup → "refactor: API cleanup"Scan commit messages — Look for patterns and themes
Keep it concise — Summary line under 72 characters
Preserve history — Include original commit messages in body
Follow conventional commits — Use prefixes like feat:, fix:, refactor:, docs:, test: