Squash merge detection union
Skill kjuhwa/skills-hub/skills/workflow/squash-merge-detection-union
Self-correcting knowledge corpus for Claude Code — 9 stable shape clusters, bias-correction pipeline baked into contribution flow. 47 papers, 45 techniques, 1.1k skills.
npx -y skills add kjuhwa/skills-hub --skill squash-merge-detection-unionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 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
Classify "can this branch be cleaned up?" using the union of `git branch --merged` + `git cherry` patch-equivalence + `gh pr list` state, with clear semantics for each signal.
SKILL.md
5.2 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Three-Way Merged-Branch Detection (--merged ∪ cherry ∪ PR state)
When to use
- You clean up stale git worktrees / branches and want to distinguish "safe to delete" from "unmerged, don't touch."
- Squash merges break
git branch --merged mainbecause the squashed commit onmainhas a different SHA than any of the feature commits. Ancestry-based checks say "unmerged" even when the work is long shipped. - You have
ghCLI available for GitHub repos and want to use it as a tie-breaker when git alone is ambiguous.
Steps
Run checks in this order, returning early on a definitive signal:
-
Fast path —
git branch --merged <main>. If the branch appears in the output (minus any leading*), it was fast-forward or merge-commit merged. Return "safe, no open PR."const { stdout } = await exec('git', ['branch', '--merged', mainBranch]); const merged = stdout.split('\n').map(b => b.trim().replace(/^\* /, '')); if (merged.includes(branchName)) return { safe: true, openPr: false }; -
Squash-merge detection —
git cherry <base> <branch>. Lines starting with-mean "patch already in upstream" (squash-merged or cherry-picked). Lines starting with+mean "genuinely not in upstream." If every line is-(or the list is empty), the branch is patch-equivalent to upstream — safe to delete.const lines = stdout.split('\n').filter(l => l.trim()); if (lines.length === 0) return true; return lines.every(line => line.startsWith('-')); -
PR state —
gh pr list --head <branch> --state all --json state --limit 1. Parse the JSON, mapMERGED→ safe,CLOSED→ safe-if-includeClosed-flag,OPEN→ unsafe (and flagopenPr: true), anything else → unknown (treat as unsafe). -
Error handling per signal:
- For
branch --merged/git cherry: expected errors (unknown revision, bad revision, not a git repo, ENOENT) → returnfalse/null, let the next signal try. Unexpected errors (permission denied, corruption) → throw. - For
gh: missing binary (ENOENT or "command not found") → return'NONE'silently (gh is a soft dependency). Other errors → log at warn with stdout preview. - For the remote-URL precheck: if origin isn't
github.com, return'NONE'and skipgh. Non-GitHub remotes are out of scope.
- For
-
Cache PR-state lookups per cleanup invocation.
getPrStatetakes an optionalMap<string, PrState>so repeated queries across many branches don't re-execgh(it's slow, ~100-300 ms per call). -
Combine signals with OR logic in a wrapper (
computeBranchCleanupSafetyor similar): the branch is safe iff any of the three signals says so. The return type should include{ safe: boolean, openPr: boolean }so callers can message the user differently for "has open PR" vs "just stale."
Counter / Caveats
- Do not run
git cherrywithout the--mergedfast path — it's O(commits-on-branch) whereas--mergedis O(heads). Order matters. git cherryon an unpushed branch vs. a branch that has been rebased ontomaincan give surprising output (the rebased commits are patch-equivalent only to specific upstream commits). Test with a rebase scenario.ghasks the GitHub API, which is rate-limited. The per-invocation cache is mandatory for bulk cleanup. Don't switch to unauthenticated API even as a fallback.ghreturnsstate: 'MERGED'even if the merge commit was force-pushed (a maintainer amend). Treating it as safe is correct because the platform considers it merged.- Non-GitHub remotes (Gitea, GitLab, custom) will fall through to the ancestry checks only. That's fine — just document it.
Evidence
packages/git/src/branch.ts:isBranchMerged(lines 186-221) —git branch --merged.isPatchEquivalent(lines 236-271) —git cherrywith lines starting with-meaning already-upstream.
packages/isolation/src/pr-state.ts(91 lines):gh pr list --json statewrapper with per-invocation cache, GitHub-only remote guard, graceful handling of missingghbinary.packages/core/src/services/cleanup-service.ts:562-578: the three-signal union insidecomputeBranchCleanupSafety/classifyForCleanup:if (await isBranchMerged(...)) return { safe: true, openPr: false }; if (await isPatchEquivalent(...)) return { safe: true, openPr: false }; const prState = await getPrState(...); if (prState === 'MERGED') return { safe: true, openPr: false }; if (prState === 'CLOSED') return { safe: includeClosed, openPr: false }; if (prState === 'OPEN') return { safe: false, openPr: true };- Commit SHA: d89bc767d291f52687beea91c9fcf155459be0d9.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.