diff --git a/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh b/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh index ac55e05f..4cda5445 100644 --- a/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh +++ b/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh @@ -517,13 +517,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 } @@ -621,7 +622,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 2a014108..d813c04f 100644 --- a/cli/bash/commands/basectl/tests/gh-branch-worktree.bats +++ b/cli/bash/commands/basectl/tests/gh-branch-worktree.bats @@ -788,6 +788,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 33c0dcf9..02ff26e3 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. + When `basectl gh branch prune` reports branches attached to worktrees, preview those worktrees with `basectl gh worktree prune`, apply the safe removals with `basectl gh worktree prune --yes`, and rerun the branch-prune command. The