agentsclimarketplace

Cleanup local branches

Skill pekral/cursor-rules/skills/cleanup-local-branches

PHP and Laravel Cursor rules — coding standards, testing, and conventions for the Cursor editor. Install via Composer.

Install
npx -y skills add pekral/cursor-rules --skill cleanup-local-branches

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when cleaning up local Git branches after origin pruning. Deletes local branches whose upstream was deleted on origin (marked gone) and local branches with no origin counterpart that have been inactive for more than six months, while always protecting the current branch and the default branches. Previews every deletion before running it.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.7 KB, as published. Nobody here has run it

Cleanup Local Branches

Purpose

Prune dead local branches so the working copy only keeps branches that are still alive on origin or still recently active.

Two deletion categories:

  • Gone — local branches whose upstream tracking branch was deleted on origin (e.g. the PR was merged and the origin branch removed).
  • Stale — local branches that have no counterpart on origin and have not received a commit for more than six months.

Constraints

  • Apply @rules/git/general.mdc
  • Output must be in English
  • This skill deletes local refs only — it never deletes, force-pushes, or modifies any branch on origin
  • Never delete the currently checked-out branch
  • Never delete protected branches: main, master, develop, development, production, staging, or any branch matching release/* or hotfix/*
  • Always print the full deletion preview (branch, category, last commit date, integration status, planned action) before deleting anything
  • Never rewrite history, force-push, or run git gc / git reflog expire
  • Determine integration status by patch identity against the default branch (git cherry), so squash- and rebase-merged branches are recognized as integrated
  • Never delete a stale branch that is not integrated into the default branch automatically — keep it and report it unless the user explicitly authorizes force deletion (its commits never reached origin and are unrecoverable)

Use when

  • The user asks to clean up, prune, or "pročistit" local branches that no longer exist on origin
  • Local branches piled up after their pull requests were merged and the origin branches were deleted
  • The repository accumulated old experiment branches that were never pushed and are no longer needed

Execution

1. Refresh remote state

Update remote-tracking refs and prune deleted ones so the gone markers and origin counterparts are accurate:

git fetch --prune origin

If the repository has no origin remote, stop and report that the skill needs an origin remote to decide which branches are alive.

2. Record protected refs and the default branch

  • Current branch: git rev-parse --abbrev-ref HEAD
  • Protected set: main, master, develop, development, production, staging, plus any branch matching release/* or hotfix/*
  • Default branch (the integration target used for merge detection in step 5):
git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's@^origin/@@' \
  || git remote show origin | sed -n 's/.*HEAD branch: //p'

Exclude the current branch and every protected branch from both candidate groups below.

3. Build candidate group A — gone upstream

List local branches whose upstream was deleted on origin:

git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads

A branch is a gone candidate when its %(upstream:track) value is exactly [gone].

4. Build candidate group B — stale without origin counterpart

List local branches with their last commit time and upstream:

git for-each-ref --format='%(refname:short)|%(committerdate:unix)|%(upstream)' refs/heads

Compute the six-month cutoff timestamp:

date -v-6m +%s 2>/dev/null || date -d '6 months ago' +%s   # BSD/macOS first, GNU fallback

A branch is a stale candidate when all of the following hold:

  • it has no upstream on origin — the %(upstream) field is empty (or does not start with refs/remotes/origin/), and git ls-remote --heads origin <branch> returns no rows (it genuinely does not exist on origin), and
  • its %(committerdate:unix) is older than the cutoff timestamp from above.

A branch already captured in group A is not re-listed in group B.

5. Determine merge status

Classify each candidate by whether its work is already integrated into the default branch. Use content-based detection, not ref ancestry — git branch --merged / git branch -d only recognize fast-forward / merge-commit history and would report a squash-merged or rebase-merged branch as unmerged even though its changes already landed (the common case for a gone branch whose PR was squashed or rebased).

Detect integration by patch identity. Plain git cherry compares individual commits, which catches rebase / cherry-pick but not squash merges (a squash collapses many commits into one with a different patch id). To cover squash as well, synthesize a single commit holding the branch's net diff on top of the merge base and ask whether that patch already exists on the default branch:

default="origin/<default-branch>"   # the freshly-fetched remote ref, never the possibly-stale local branch
base=$(git merge-base "$default" "<branch>")
probe=$(git commit-tree "$(git rev-parse '<branch>^{tree}')" -p "$base" -m probe)
git cherry "$default" "$probe"   # one line: '- <sha>' ⇒ integrated, '+ <sha>' ⇒ not integrated
  • Integrated — the probe line starts with -: the branch's net change is already present on the default branch (via fast-forward, merge, squash, or rebase) → safe to remove.
  • Not integrated — the probe line starts with +: the branch carries changes absent from the default branch → it may hold unpushed local-only work.

git branch -d still refuses squash/rebase-integrated branches (it uses ref ancestry), so deletion of an integrated branch uses git branch -D. This is safe precisely because step 5 already proved the work is on the default branch.

6. Preview before deleting

Print one row per candidate with: branch name, category (gone / stale), last commit date, integration status (integrated / not integrated), and the planned action. Group rows by category. Do not delete anything before this preview is shown.

  • Interactive run: show the preview and ask the user to confirm before deleting; for not integrated branches, include them only when the user authorizes discarding the unmerged commits.
  • Autonomous run (e.g. invoked by another skill or a scheduled task):
    • gone category — delete every branch. A gone upstream means the PR lifecycle ended on origin; integrated branches delete cleanly, and not integrated ones are also removed because the branch is already gone from origin and the local ref is the lifecycle remnant. List each deletion with its integration status in the report.
    • stale category — delete only integrated branches; keep every not integrated branch (it never existed on origin, so its commits are local-only and unrecoverable once deleted). List the kept ones in the report with the git branch -D <branch> command the user can run manually.

7. Delete

  • Use git branch -D <branch> for every branch cleared for deletion in step 6 — -D is required because git branch -d rejects squash/rebase-integrated branches even after step 5 proved their work is on the default branch.
  • A not integrated branch is deleted only when step 6 cleared it (every gone branch; a stale branch only on explicit interactive authorization).
  • Delete one branch per command so a single failure does not abort the rest; capture and report any failure.

8. Verify and report

Confirm the deletions with git branch -vv and produce the report described below.


Output

  • Preview (before deletion): candidates grouped by category with branch, last commit date, integration status (integrated / not integrated), and planned action.
  • Result (after deletion):
    • Deleted branches grouped by category (gone, stale), each with its integration status
    • Kept branches with the reason (protected, current, stale + not integrated — needs force, still on origin, active within six months)
    • Any deletion that failed, with the error

Keep the report concise and in English.


Done when

  • Remote state was refreshed with git fetch --prune origin
  • Both candidate groups were computed with the protected set and the current branch excluded
  • The deletion preview was shown before any branch was deleted
  • Integration status was computed by patch identity (git cherry) against the default branch
  • Eligible branches were deleted (every gone branch; stale branches only when integrated, or not-integrated on explicit authorization)
  • The final report lists deleted and kept branches with reasons, plus any failures

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.