Files
rdbms-playground/scripts/wt-rm.sh
T
claude@clouddev1 4570c4e1ea fix(workflow): wt-rm removes only the named worktree (was auto-sweeping merged ones)
The first wt-clean.sh removed every worktree whose branch was merged into
origin/main — but long-lived branches (website, the ci line) are merged too,
so it would have deleted their worktrees. Caught on review before any real use.

Replace the auto-sweep with an explicit `wt-rm <branch>` that removes only the
named worktree; safety comes from git's own refusals — it won't remove a dirty
worktree (no --force) and deletes the local branch only when it's merged into
origin/main, otherwise keeps it (and its unmerged commits).

Update test-wt.sh to 15 checks (incl. "unnamed worktree untouched", primary
refused, dirty refused, unmerged branch preserved) and the wt-clean references
in ADR-0059 / CLAUDE.md / the README index.
2026-06-23 21:27:41 +00:00

69 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# wt-rm.sh — remove the worktree for a named branch.
#
# EXPLICIT BY DESIGN: it removes ONLY the branch you name. There is no
# scanning and no "is it merged into main?" heuristic to decide *what* to
# remove — long-lived branches (e.g. `website`, the `ci` line) are merged
# into main too, so auto-detection would happily delete them. Naming the
# target is the safety.
#
# Safety still comes from git's own refusals, not from cleverness here:
# - the worktree won't be removed if it has uncommitted/untracked changes
# (no `--force`);
# - the local branch is deleted ONLY if it is fully merged into
# `origin/main` — otherwise the worktree goes but the branch (and its
# unmerged commits) is kept.
#
# USAGE
# scripts/wt-rm.sh <branch>
# scripts/wt-rm.sh feat/clause-concept-hints
#
# For a worktree whose directory you already deleted by hand, use the
# built-in `git worktree prune` (it only clears bookkeeping — never a
# directory or a branch).
set -euo pipefail
log() { printf ' wt-rm: %s\n' "$*" >&2; }
die() { printf 'wt-rm: ERROR: %s\n' "$*" >&2; exit 1; }
[ $# -eq 1 ] || die "usage: wt-rm.sh <branch>"
branch="$1"
git rev-parse --git-dir >/dev/null 2>&1 || die "not inside a git repo"
# Resolve the named branch to its worktree path.
path=""; cur=""
while read -r key val; do
case "$key" in
worktree) cur="$val" ;;
branch) [ "${val#refs/heads/}" = "$branch" ] && path="$cur" ;;
esac
done < <(git worktree list --porcelain)
[ -n "$path" ] || die "no worktree is checked out on branch '$branch' (see: git worktree list)"
main_wt="$(git worktree list --porcelain | awk '/^worktree /{print $2; exit}')"
here="$(git rev-parse --show-toplevel)"
[ "$path" != "$main_wt" ] || die "refusing to remove the primary checkout ($path)"
[ "$path" != "$here" ] || die "refusing to remove the worktree you're standing in — run this from elsewhere"
log "removing worktree $path (branch $branch)"
git worktree remove "$path" \
|| die "could not remove $path — it likely has uncommitted or untracked changes; clean it (or remove by hand) first"
# Delete the local branch only if it is safely merged into origin/main;
# otherwise keep it so unmerged commits are never lost. (No auto-fetch — it
# judges against your current origin/main; run `git fetch` first if you want
# a freshly-merged branch to be eligible.)
if git rev-parse --verify --quiet origin/main >/dev/null \
&& git merge-base --is-ancestor "$branch" origin/main; then
git branch -D "$branch" >/dev/null
log "deleted local branch $branch (merged into origin/main)"
else
log "kept local branch $branch — not merged into origin/main; delete with 'git branch -D $branch' if you're sure"
fi
log "done"