cleanup-git · diff
git:20260928.b87f747 to git:20261005.978baca
31 added, 10 removed. Audit A to A.
---
description: Remove merged local branches and stale git worktrees. Use when the user
says "cleanup branches", "prune worktrees", "tidy git", "remove merged branches",
"delete merged branches", "gone branches", or wants to clean local git state. NOT
for creating commits, creating worktrees, or configuring git hooks.
name: cleanup-git
---
# Cleanup Git
- All deletion goes through the cleanup script: the target repo's own `scripts/cleanup-git.sh` if it ships one, otherwise this skill's `scripts/cleanup-git.sh`. Do not hand-write branch or worktree deletion commands.
+ Use the target repo's own `scripts/cleanup-git.sh` if it ships one, otherwise this skill's script. Inspect a repo-provided script before relying on its safety policy. Use direct deletion only for a specifically approved exception described below; never pipe a branch list into force-delete.
```bash
scripts/cleanup-git.sh # preview (default)
- scripts/cleanup-git.sh --apply # delete after user approval
- scripts/cleanup-git.sh --apply --force # also delete branches with ahead commits
+ scripts/cleanup-git.sh --apply # execute the requested safe cleanup
+ scripts/cleanup-git.sh --apply --force --branch <name> # approved ahead-commit loss
scripts/cleanup-git.sh --base <ref> # override base detection
```
- The script fetches and prunes remotes, then picks the base from the remote default branch, then local or remote `main`, `master`, `trunk`, `develop`, or `dev`.
+ The script fetches and prunes remotes. It prefers the fetched remote default branch, then remote `main`, `master`, `trunk`, `develop`, or `dev`, then local fallbacks. `--base` overrides this choice.
## What the script decides
A branch or its worktree is a candidate when one of these holds:
- - `PR merged`: `gh pr view <branch>` reports `MERGED`. This catches squash and rebase merges; ahead commits count only past the PR head commit.
- - `upstream gone`: the tracking branch was deleted.
+ - `PR merged`: GitHub confirms the PR in the target repository and base, and its merge commit is reachable from the base. This catches squash, conflict-resolved squash, and rebase merges; ahead commits count only past the PR head.
- `merged`: the branch is an ancestor of the base.
+ - `squash merged`: the complete branch patch is equivalent to a commit in the base. This works offline without `gh`; conflict resolution can prevent equivalence.
+ - `upstream gone`: only a candidate signal, not merge proof. Unique commits still keep the branch.
- It always skips the current worktree, the current branch, the base, and `main`/`master`/`trunk`/`develop`/`dev`. It keeps dirty worktrees. It keeps candidates with ahead commits, or an unknown ahead count, unless `--force` is passed. A branch with none of these signals prints `skip ... (active)`; without `gh`, squash-merged branches land there.
+ It skips the current worktree, current branch, base, and `main`/`master`/`trunk`/`develop`/`dev`. It keeps locked worktrees and worktrees with tracked, untracked, or ignored files, or unknown status. It keeps candidates with ahead commits or unknown ahead counts unless `--force --branch <name>` is passed for one approved target. No proof prints `skip ... (active)`; do not treat that as proof of unmerged work.
## Workflow
- 1. Run the preview and show it. Read each line literally: `remove <worktree>` and `delete <branch>` are candidates; `KEEP ... (dirty)`, `KEEP ... (N ahead — use --force)`, and `KEEP ... (ahead unknown — run git fetch ...)` need the user; `skip` lines for the current, base, or protected branch are guarded; mention `skip ... (active)` lines when `gh` is unavailable.
- 2. Present every `KEEP` line as a decision for the user.
- 3. Ask before `--apply`. Add `--force` only when the user confirms the ahead commits are throwaway.
+ 1. Run the preview and show the safe removals and kept objects.
+ 2. An explicit cleanup request authorizes `--apply` for verified safe candidates. Run it without another confirmation; kept objects do not block safe removals. For a preview-only request, do not apply.
+ 3. Report what was removed and what remains. Do not ask about every kept object unless the user wants it removed. For a risky exception, show the exact branch/path, full tip OID, unique commits, and uncommitted files (including ignored files), then ask once for permission to discard them. A reply such as “I allow it” authorizes only that described scope; execute it yourself rather than telling the user to run it.
Stop if the directory is not a git repo. If no base is found, ask for `--base <ref>`. A fetch failure makes the preview use stale refs (say so) and makes `--apply` refuse.
+
+ ## Approved exceptions
+
+ After explicit user consent, recheck the tip and file list. If either changed,
+ ask again. For one worktree, use the using-git-worktrees cleanup script with
+ `--force <branch>`. For a branch-only exception, or a hook-blocked direct
+ cleanup, use the command-local marker:
+
+ ```bash
+ git -c cc-thingz.cleanupApproved=<full-tip-oid> worktree remove --force <exact-path>
+ git -c cc-thingz.cleanupApproved=<full-tip-oid> branch -D <exact-branch>
+ ```
+
+ Use separate commands, worktree first. The marker must match the current tip.
+ It does not authorize protected/current/locked worktrees, other branches,
+ `reset --hard`, `clean`, or force-push. Never save it with `git config`,
+ invent user consent, or use bulk `--force` when only one exception was approved.
+ The marker pins only the commit, not file contents; the agent must recheck
+ files immediately before executing. This is a consent attestation by the agent,
+ not cryptographic proof that the user approved; the hook is a mistake guard, not a security sandbox.
## Output
```text
GIT CLEANUP
Status: PREVIEW | APPLIED | BLOCKED
Base: <ref>
Remove: <branch/worktree> — <reason>
Keep: <branch/worktree> — <reason and decision needed>
Verification: <command> — pass/fail/not run
```