Org Branch Cleanup
Purpose
Remove organization branch clutter without guessing which refs are safe to delete.
The bundled scripts/org_branch_cleanup.py separates the work into read-only audit, explicit apply, and full verification phases. Dry-run evidence is the default; deletion requires an exact organization confirmation.
When to use
- A GitHub organization has accumulated old or merged development branches.
- Bot, agent, migration, or dependency branches remain after pull requests merge.
- A maintainer wants an evidence-backed cleanup ledger rather than broad
git push --delete. - Branch-heavy security, state, archive, or generated-data repositories need to be separated from ordinary development repos.
- A previous cleanup stopped midway and must resume without repeating completed deletions.
Workflow
- Read the target repository or organization instructions before mutation.
- Confirm GitHub authentication and rate-limit headroom:
gh auth statusgh api rate_limit --jq '.resources | {core,graphql}'
- Run a read-only audit:
Start withpython3 scripts/org_branch_cleanup.py audit \ --org example-org \ --output /tmp/example-org-branch-audit--include-repo-regex '^one-repo$'when proving access or policy. Audit output is immutable by default; use a new directory rather than--overwrite. - Inspect
audit-summary.json,audit-errors.tsv, anddelete-candidates.jsonl.- Do not apply a partial audit. Fix failed repository queries or explicitly justify
--allow-partial. - Default candidates are same-repository PR heads merged at least 14 days ago.
- Closed-unmerged branches and unassociated stale branches are report-only.
- Default, fork, archived, protected, and release-like refs are excluded.
- Repositories matching GHSA, state, or archive patterns are excluded by default. Audit them separately under an explicit retention policy.
- Do not apply a partial audit. Fix failed repository queries or explicitly justify
- Apply only after the candidate set is reviewed:
python3 scripts/org_branch_cleanup.py apply \ --candidates /tmp/example-org-branch-audit/delete-candidates.jsonl \ --confirm-org example-org \ --output /tmp/example-org-branch-applyapplyrequires the clean siblingaudit-summary.json. Do not bypass this with--allow-unverified-candidatesor--allow-partial-auditwithout an explicit operator decision. - Treat each preflight skip as a safety result, not a failure.
moved: branch SHA changed after the snapshot.protected: REST or ruleset protection blocked deletion.open-pr: a live pull request now uses the branch.missing: GitHub or another operator already removed it.
- Verify every deletion:
python3 scripts/org_branch_cleanup.py verify \ --ledger /tmp/example-org-branch-apply/deleted.tsv \ --output /tmp/example-org-branch-apply/verification-rerun.tsv - Report exact deleted, skipped, retained, excluded-repository, and verification counts. Preserve the ledger until the cleanup is accepted.
Safety rules
- Never delete default, protected, release, pages, stable, or active-PR branches.
- Never infer safety from branch age or naming alone.
- Require a same-repository PR whose head name matches the current branch tip.
- Re-read the live SHA, protection status, and open PRs immediately before deletion.
- Refuse organization mismatch between the candidate ledger and
--confirm-org. - Keep closed-unmerged deletion disabled unless the operator explicitly passes
--allow-closed-unmerged. - Stop broad scans when generated or security repos contain thousands of refs; define retention policy first.
- If a proxy or wrapper stalls, preserve ledgers and resume with the real GitHub CLI. Do not restart shared daemons owned by other sessions.
Inputs
- GitHub organization login.
- Authenticated
ghCLI with repository administration permission. - Output directory for snapshots and ledgers.
- Optional retention windows and repository or branch exclusion regexes.
- Optional
--gh-binoverride for a compatible GitHub CLI wrapper.
Outputs
snapshot.jsonl: immutable branch and PR-tip evidence.delete-candidates.jsonl: old merged branches eligible for live preflight.closed-unmerged-review.jsonl: abandoned PR heads requiring explicit approval.stale-unassociated.tsv: old branches with insufficient deletion proof.deleted.tsvandskipped.tsv: resumable mutation ledger.verification.tsv: post-delete ref state.- JSON audit and apply summaries with exact counts and timestamps.