diff --git a/cli/bash/commands/basectl/subcommands/gh.sh b/cli/bash/commands/basectl/subcommands/gh.sh index fd17f42f..a4b40aaf 100644 --- a/cli/bash/commands/basectl/subcommands/gh.sh +++ b/cli/bash/commands/basectl/subcommands/gh.sh @@ -543,6 +543,7 @@ Purpose: Note: Runs in dry-run mode by default. Pass --yes to apply changes. + --dry-run and --yes are mutually exclusive; choose one when overriding the default. Options: --days Minimum age for stale branch reporting. Defaults to 30. @@ -603,6 +604,7 @@ Purpose: Note: Runs in dry-run mode by default. Pass --yes to apply changes. + --dry-run and --yes are mutually exclusive; choose one when overriding the default. Options: --dry-run Preview worktrees that would be removed (default). diff --git a/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh b/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh index 5b109018..ab85bae0 100644 --- a/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh +++ b/cli/bash/commands/basectl/subcommands/gh_branch_worktree.sh @@ -128,6 +128,19 @@ base_gh_branch_stale_format_error() { base_gh_usage_error base_gh_branch_usage "$message" } +base_gh_validate_prune_mode() { + local usage_function="$1" + local dry_run_requested="$2" + local yes_requested="$3" + + if ((dry_run_requested && yes_requested)); then + base_gh_usage_error "$usage_function" \ + "Options '--dry-run' and '--yes' cannot be used together; choose either '--dry-run' (preview) or '--yes' (apply)." + return $? + fi + return 0 +} + base_gh_format_unix_date() { local timestamp="$1" local formatted @@ -424,14 +437,15 @@ base_gh_branch_prune_remote_tracking_refs() { base_gh_branch_prune() { local dry_run=1 remote=0 default_branch status=0 + local dry_run_requested=0 yes_requested=0 while (($#)); do case "$1" in --dry-run) - dry_run=1 + dry_run_requested=1 ;; --yes) - dry_run=0 + yes_requested=1 ;; --remote) remote=1 @@ -448,6 +462,9 @@ base_gh_branch_prune() { shift done + base_gh_validate_prune_mode base_gh_branch_usage "$dry_run_requested" "$yes_requested" || return $? + ((yes_requested)) && dry_run=0 + base_gh_require_git_repo || return 1 default_branch="$(base_gh_default_branch)" if ((dry_run)); then @@ -505,14 +522,15 @@ base_gh_worktree_prune() { local dry_run=1 default_branch current_worktree local path branch merge_source merge_status physical_path local removed=0 skipped_current=0 skipped_dirty=0 skipped_unmerged=0 failed=0 candidates=0 + local dry_run_requested=0 yes_requested=0 while (($#)); do case "$1" in --dry-run) - dry_run=1 + dry_run_requested=1 ;; --yes) - dry_run=0 + yes_requested=1 ;; -h|--help) base_gh_worktree_usage @@ -526,6 +544,9 @@ base_gh_worktree_prune() { shift done + base_gh_validate_prune_mode base_gh_worktree_usage "$dry_run_requested" "$yes_requested" || return $? + ((yes_requested)) && dry_run=0 + base_gh_require_git_repo || return 1 default_branch="$(base_gh_default_branch)" current_worktree="$(base_gh_resolve_physical_path "$(git rev-parse --show-toplevel)")" || return 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..d437d372 100644 --- a/cli/bash/commands/basectl/tests/gh-branch-worktree.bats +++ b/cli/bash/commands/basectl/tests/gh-branch-worktree.bats @@ -72,6 +72,22 @@ load ./basectl_helpers.bash [[ "$output" == *"ERROR: --days must be a positive integer."* ]] } +@test "basectl gh prune rejects conflicting execution flags in either order" { + local args command_args + + for args in \ + "branch prune --dry-run --yes" \ + "branch prune --yes --dry-run" \ + "worktree prune --dry-run --yes" \ + "worktree prune --yes --dry-run"; do + read -r -a command_args <<<"$args" + run_basectl gh "${command_args[@]}" + + [ "$status" -eq 2 ] + [[ "$output" == *"Options '--dry-run' and '--yes' cannot be used together"* ]] + done +} + @test "basectl gh branch cleanup returns merge source without module global" { run env \ HOME="$TEST_HOME" \