chore(workflow): branch-and-PR working method + ADR-number reservation (ADR-0059)
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.
This commit is contained in:
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/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)"
|
||||
Reference in New Issue
Block a user