Adopt a trackable working method now the repo is public: - PRs onto a protected main; --no-ff merge commits; one worktree per branch. - Reserve-first ADR numbering: scripts/adr-reserve.sh claims the next number atomically against main (push = compare-and-swap; ledger docs/adr/RESERVATIONS.log), so a number is stable from creation. - Worktree helpers scripts/wt-new.sh + wt-clean.sh. - Local-origin test harnesses (reserve 10/10, worktrees 7/7, shellcheck clean). Record the decision in ADR-0059, supersede ADR-0000's placeholder-until-merge numbering default, and add the lean CONTRIBUTING.md sections + CLAUDE.md operational rules.
73 lines
2.4 KiB
Bash
Executable File
73 lines
2.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# wt-clean.sh — tidy up worktrees whose branch has merged.
|
|
#
|
|
# WHAT it does (safe by default):
|
|
# 1. `git worktree prune` — drop admin entries for worktrees whose
|
|
# directory is already gone.
|
|
# 2. For every *other* worktree (never the primary checkout, never the one
|
|
# you're standing in) whose branch is fully merged into origin/main:
|
|
# remove the worktree and delete the local branch.
|
|
#
|
|
# SAFETY: it refuses to remove a worktree with uncommitted changes (no
|
|
# --force) and only ever touches branches git confirms are merged into
|
|
# origin/main, so unmerged work is never lost. Run it whenever; it's not
|
|
# tied to any moment.
|
|
#
|
|
# USAGE
|
|
# scripts/wt-clean.sh # do it
|
|
# scripts/wt-clean.sh --dry-run # show what it would remove
|
|
|
|
set -euo pipefail
|
|
|
|
log() { printf ' wt-clean: %s\n' "$*" >&2; }
|
|
|
|
dry=0
|
|
[ "${1:-}" = "--dry-run" ] && dry=1
|
|
|
|
git rev-parse --git-dir >/dev/null 2>&1 || { echo "wt-clean: not in a git repo" >&2; exit 1; }
|
|
|
|
log "fetching origin/main"
|
|
git fetch --quiet --prune origin main || true
|
|
|
|
log "pruning stale worktree entries"
|
|
if (( dry )); then
|
|
git worktree prune --dry-run -v
|
|
else
|
|
git worktree prune -v
|
|
fi
|
|
|
|
# The primary checkout and the current worktree are off-limits.
|
|
main_wt="$(git worktree list --porcelain | awk '/^worktree /{print $2; exit}')"
|
|
here="$(git rev-parse --show-toplevel)"
|
|
|
|
# Branches fully merged into origin/main (whitespace-trimmed, sans markers).
|
|
merged="$(git branch --format='%(refname:short)' --merged origin/main)"
|
|
is_merged() { grep -qxF "$1" <<<"$merged"; }
|
|
|
|
removed=0
|
|
# Iterate worktrees: paired "worktree <path>" / "branch refs/heads/<name>".
|
|
path=""; while read -r key val; do
|
|
case "$key" in
|
|
worktree) path="$val" ;;
|
|
branch)
|
|
br="${val#refs/heads/}"
|
|
if [ "$path" != "$main_wt" ] && [ "$path" != "$here" ] && is_merged "$br"; then
|
|
if (( dry )); then
|
|
log "would remove worktree $path (branch $br, merged)"
|
|
else
|
|
if git worktree remove "$path" 2>/dev/null; then
|
|
git branch -d "$br" >/dev/null 2>&1 || true
|
|
log "removed worktree $path + branch $br"
|
|
removed=$((removed+1))
|
|
else
|
|
log "skipped $path — has uncommitted changes (remove it by hand)"
|
|
fi
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
done < <(git worktree list --porcelain)
|
|
|
|
(( dry )) || log "done — removed $removed merged worktree(s)"
|