From 01541a8e257da16937815371a928e51ffea89e9e Mon Sep 17 00:00:00 2001 From: codeforester <22363102+codeforester@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:40:58 -0700 Subject: [PATCH] Report incomplete worktree cleanup --- .../basectl/subcommands/gh_branch_worktree.sh | 7 ++-- .../basectl/tests/gh-branch-worktree.bats | 33 +++++++++++++++++++ docs/github-workflow.md | 4 +++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh b/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh index 5b109018..3edb180e 100644 --- a/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh +++ b/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh @@ -491,13 +491,14 @@ base_gh_worktree_prune_delete_branch() { upstream="$(base_gh_branch_upstream "$branch")" if [[ "$merge_source" != github && -n "$upstream" ]] && ! base_gh_branch_merged_to_ref "$branch" "$upstream"; then printf 'SKIP-BRANCH %s not fully merged to upstream %s\n' "$branch" "$upstream" - return 0 + return 1 fi if base_gh_branch_delete "$branch" "$merge_source"; then printf 'DELETE %s\n' "$branch" else printf 'SKIP-BRANCH %s git branch -d refused\n' "$branch" + return 1 fi } @@ -586,7 +587,9 @@ base_gh_worktree_prune() { elif git worktree remove "$path" >/dev/null 2>&1; then printf 'REMOVE %s (%s)\n' "$path" "$branch" removed=$((removed + 1)) - base_gh_worktree_prune_delete_branch "$branch" "$merge_source" + if ! base_gh_worktree_prune_delete_branch "$branch" "$merge_source"; then + failed=$((failed + 1)) + fi else printf 'FAIL %s (%s) git worktree remove failed\n' "$path" "$branch" failed=$((failed + 1)) diff --git a/cli/bash/commands/basectl/tests/gh-branch-worktree.bats b/cli/bash/commands/basectl/tests/gh-branch-worktree.bats index de0e5212..f2dfbc0e 100644 --- a/cli/bash/commands/basectl/tests/gh-branch-worktree.bats +++ b/cli/bash/commands/basectl/tests/gh-branch-worktree.bats @@ -760,6 +760,39 @@ EOF ! git -C "$repo" show-ref --verify --quiet refs/heads/merged-work } +@test "basectl gh worktree prune reports retained branches after deletion failure" { + local repo worktree + + repo="$TEST_TMPDIR/repo" + worktree="$TEST_TMPDIR/merged-worktree" + init_git_repo "$repo" + printf 'hello\n' > "$repo/README.md" + commit_all "$repo" "Initial commit" + git -C "$repo" branch merged-work + git -C "$repo" worktree add "$worktree" merged-work >/dev/null 2>&1 + + run env \ + HOME="$TEST_HOME" \ + BASE_HOME="$BASE_REPO_ROOT" \ + bash -c ' + cd "$1" + source "$BASE_HOME/base_init.sh" + source "$BASE_HOME/cli/bash/commands/basectl/subcommands/gh.sh" + base_gh_worktree_prune_delete_branch() { + printf "SKIP-BRANCH %s simulated branch delete failure\n" "$1" + return 1 + } + base_gh_subcommand_main worktree prune --yes + ' bash "$repo" + + [ "$status" -eq 1 ] + [[ "$output" == *"REMOVE "*"/merged-worktree (merged-work)"* ]] + [[ "$output" == *"SKIP-BRANCH merged-work simulated branch delete failure"* ]] + [[ "$output" == *"Summary: 1 removed, 1 skipped current/default, 0 skipped dirty, 0 skipped unmerged, 1 failed."* ]] + [ ! -e "$worktree" ] + git -C "$repo" show-ref --verify --quiet refs/heads/merged-work +} + @test "basectl gh worktree prune skips dirty worktrees" { local repo worktree diff --git a/docs/github-workflow.md b/docs/github-workflow.md index 680f984f..38c8707a 100644 --- a/docs/github-workflow.md +++ b/docs/github-workflow.md @@ -438,6 +438,10 @@ a merged GitHub PR, then deletes the now-free local branch when safe. Resolve an reported GitHub verification failures and rerun the preview before applying it with `--yes`. +If a worktree is removed but its local branch cannot be deleted, the command +retains the branch, reports the incomplete cleanup, and exits nonzero. Resolve +the branch condition before treating the cleanup as complete. + ## Superseded Pull Requests A pull request closed because another pull request supersedes it has a