fix: detect and clean orphaned worktree directories invisible to git #223
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/claude-custom!223
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "222-bug-cleanup-worktrees-sh-misses-orphaned"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
cleanup-worktrees.shonly scannedgit worktree list --porcelain, completely missing orphaned directories where git lost the worktree registration but the directory persisted on disk. On pal-e-platform alone, 37 of 67 agent worktree directories were orphans invisible to the cleanup script. This PR adds a filesystem scan that detects and removes these orphans.Changes
hooks/cleanup-worktrees.sh— Added orphaned worktree directory cleanup section (lines 100-193) that:{repo}/.claude/worktrees/agent-*on the filesystem for each known repogit worktree list --porcelainto identify orphans.gitfile + known branch: Extracts branch from gitdir metadata, verifies merged into default branch, removes if merged OR older than 7 days.gitfile + unknown branch: Age-based removal (>7 days).gitfile (empty shells): Age-based removal (>7 days)git worktree pruneper-repo after orphan cleanup/tmpclone cleanup to catch orphans without.gitdirectorieshooks/forgejo-helper.sh— Updatedremove_worktree_for_branchfunction (line 276+) to:agent-*dirs matching the merged branchgit worktree pruneafter cleanupTest Plan
bash -non both files.gitfile, 12K each) matching the expected patternReview Checklist
bash -non both modified filesRelated Notes
None -- this is a bug fix to existing hook scripts.
Related
cleanup-worktrees.sh only scanned `git worktree list --porcelain`, missing orphaned directories where git lost the worktree registration but the directory persisted on disk. This caused 37+ empty dirs to accumulate in pal-e-platform alone. Adds filesystem scan of {repo}/.claude/worktrees/agent-* that compares against git-tracked paths to find orphans. Three cleanup strategies: - Orphans with .git file + known branch: verify merged, then remove - Orphans with .git file + unknown branch: age-based removal (>7 days) - Orphans without .git file (empty shells): age-based removal (>7 days) Also updates remove_worktree_for_branch in forgejo-helper.sh to scan for orphaned directories matching the merged branch, and improves /tmp clone cleanup to catch orphans without .git directories. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>git branch --merged uses two-character padding (" " for non-current, "* " for current). The original regex ^[* ] only matched one character, missing non-current merged branches. Changed to ^[* ]+ to match one or more padding characters. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>PR #223 Review
DOMAIN REVIEW
Domain: Bash / Shell scripting / Git worktree management
This PR adds orphaned worktree directory detection to two hook scripts. The core problem is well-understood:
git worktree list --porcelainmisses directories where git lost the worktree registration but the directory persisted on disk.Safety analysis -- merge check before deletion:
The orphan cleanup correctly implements a tiered safety model:
.gitfile + known branch: checksgit branch --mergedbefore deleting. Unmerged branches younger than 7 days are preserved..gitfile + unknown branch: age-based only (7+ days)..gitfile (empty shells): age-based only (7+ days).This is sound. Active work on unmerged branches will not be deleted if the worktree is younger than 7 days.
Orphan detection correctness:
The
tracked_pathsset is built fromgit worktree list --porcelainoutput and checked withgrep -qFx(fixed string, exact line match). This correctly excludes all git-registered worktrees from orphan treatment. No false positives possible from the tracking check itself.The
forgejo-helper.shorphan scan uses exact string comparison ([ "$orphan_branch" = "$branch" ]) rather than regex -- this is correct and safe.BLOCKERS
None.
NITS
Regex injection in branch name matching (cleanup-worktrees.sh, orphan section):
$orphan_branchis interpolated directly into an ERE pattern. Branch names containing regex metacharacters (.,+,*,(,)) would cause incorrect matching. For example, branch123-fix.foowould also match123-fixXfoo. Consider usinggrep -qFwith the branch name after stripping the 2-charactergit branchprefix, or escaping the variable. Low severity since branch naming conventions avoid metacharacters, but worth hardening.DRY: duplicated orphan detection logic across both files. The gitdir-metadata-to-branch extraction pattern (read
.gitfile, parse gitdir path, read HEAD, stripref: refs/heads/prefix) is duplicated incleanup-worktrees.sh(lines ~130-145 in new code) andforgejo-helper.sh(lines ~330-345 in new code). Consider extracting to a shared function inforgejo-helper.sh(e.g.,_extract_branch_from_orphan_dir) that both can source. Not a blocker since they run in different hook contexts, but divergence risk over time./tmpcleanup broadening -- the relaxed filter now matches/tmp/{repo_name}-*directories without any.gitindicator. The glob pattern could match unrelated directories if another tool creates dirs starting with the repo name. The 7-day age gate mitigates severity, but adding a secondary check (e.g., presence of typical repo files likeREADME.mdor a known directory structure) would reduce false positive risk.git branchoutput format -- the regex^[* ]+is slightly loose.git branchuses a fixed 2-character prefix (*for current,for others). A more precise pattern would be^[* ] +(exactly one char from[*, space]followed by spaces). Functionally equivalent in practice.SOP COMPLIANCE
222-bug-cleanup-worktrees-sh-misses-orphanedmatches issue #222forgejo_admin/pal-e-platform#243PROCESS OBSERVATIONS
bash -nsyntax validation is also good practice.VERDICT: APPROVED