Syncs local repository with remote, cleans up merged branches, and reports status
Syncs your local git repository with remote, safely cleans up merged branches, and reports status. Use this when you want to remove stale branches after PRs are merged, especially with squash/rebase merges.
/plugin marketplace add zookanalytics/claude-devcontainer/plugin install git-workflow@claude-devcontainerPerforms comprehensive git repository synchronization and cleanup while preserving local work.
/git:cleanup
This command performs a safe sync and cleanup of your git repository by:
Run these commands in parallel (no dependencies between them):
git status --porcelain # Check for uncommitted changes
git stash list # Check for stashed changes
After both complete:
git status --porcelain shows changes: STOP and notify user concisely
Sync with remote and remove stale remote-tracking branches:
git fetch --all --prune
What this does:
Find local branches whose remote counterparts are gone:
git branch -vv
Analysis:
[origin/...: gone]For each candidate branch with [origin/...: gone], determine if it's safe to delete.
Understanding merged PRs:
[origin/branch-name: gone]git branch --mergedgit merge-base --is-ancestor) don't work for squash mergesParallel categorization strategy:
# First, get shared context (run these in parallel):
git branch --show-current # Current branch (protected)
git branch --merged origin/main # All traditionally merged branches
# Then, for ALL candidate branches simultaneously, check GitHub PR status:
# Launch these in parallel - each is independent:
gh pr list --state merged --head BRANCH_1 --json number,title
gh pr list --state merged --head BRANCH_2 --json number,title
gh pr list --state merged --head BRANCH_3 --json number,title
# ... one call per candidate branch, ALL IN PARALLEL
# For branches with no merged PR, check unique commits (can also be parallel):
git rev-list origin/main..BRANCH_NAME --count
Why parallel: Each gh pr list call takes 1-2 seconds.
With 5 branches:
Categorization logic per branch:
gh pr list --state merged --head BRANCH returns a PR → safe to deletegit branch --merged origin/main → safe to deletegit rev-list origin/main..BRANCH --count:
0 = has unpushed work → preserve
Categories:
git branch --merged origin/main (traditional merge), ORDelete branches that are safe to remove:
# Use -d flag for safe deletion (will fail if truly unmerged)
git branch -d BRANCH_NAME
# If -d fails but we verified it's merged via PR:
# This WILL happen with squash/rebase merges
git branch -D BRANCH_NAME
Deletion strategy:
git branch -d first (may succeed for traditional merges)git branch -D (expected for squash/rebase merges)git branch --merged origin/main:
git branch -d (will succeed)git branch -d (will succeed, branch is empty)Rules:
[origin/...: gone] AND one of:
gh pr list), ORgit branch --merged origin/main, OR-D only when PR is confirmed merged but -d fails (squash/rebase case)Provide comprehensive status report:
If everything is clean:
✅ Repository is fully synced with remote
Summary:
- No uncommitted changes
- No stashed changes
- Deleted X merged branches: [list]
- All local branches are up to date
If local work exists:
⚠️ Repository synced with local work preserved
Summary:
- Uncommitted changes: [files list]
Action needed: Commit or stash these changes
- Stashed changes: X entries found
Reminder: You have stashed work that may need attention
Run 'git stash list' to review
- Deleted X merged branches: [list]
- Branches with unpushed work: [list]
These branches were kept because they have commits not yet pushed to remote
The Challenge: When you merge a PR on GitHub using "Squash and merge" or "Rebase and merge", GitHub:
[origin/branch-name: gone]git branch --merged won't show it as merged (different commit SHAs)git merge-base --is-ancestor returns false (branch not in main's history)The Solution: This command uses a three-tiered approach to safely identify merged branches:
GitHub PR Check (gh pr list --state merged --head BRANCH)
Traditional Merge Check (git branch --merged origin/main)
Unique Commit Count (git rev-list origin/main..BRANCH --count)
Why git ancestry checks don't work:
git merge-base --is-ancestor checks if commits are in the history treeResult: Branches from merged PRs are safely deleted even with GitHub's squash/rebase merge strategies by checking GitHub's PR state directly.
git merge-base --is-ancestor to confirm mergesConcise error reporting:
Checking for uncommitted changes...
✓ Working tree is clean
Checking stash...
✓ Found 2 stashed entries
Fetching from remote and pruning stale references...
✓ Synced with origin
✓ Pruned 5 stale remote-tracking branches
Analyzing local branches...
Found 4 branches with deleted remotes:
- feat/user-auth [gone] - checking merge status...
✓ Found merged PR #45 on GitHub (squash merged)
- fix/login-bug [gone] - checking merge status...
✓ Found in git merged branches (traditional merge)
- docs/update-readme [gone] - checking merge status...
✓ No unique commits (empty branch)
- feat/new-feature [gone] - checking merge status...
⚠️ No merged PR found, has 3 unique commits (preserving)
Cleaning up merged branches...
✓ Deleted feat/user-auth (merged PR #45)
✓ Deleted fix/login-bug (traditional merge)
✓ Deleted docs/update-readme (no unique commits)
✅ Repository cleanup complete
Summary:
- Deleted 3 merged branches
- Kept 1 branch with unpushed work: feat/new-feature (3 unique commits)
- Current branch 'main' is up to date with origin/main
- Stashed changes: 2 entries (run 'git stash list' to review)