#!/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 # 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="$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"