From d8ea3f5a0a46bd96a17f209821e7f202220d50f9 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 12 Aug 2026 00:38:15 -0700 Subject: [PATCH 1/2] Fix rm exit status on removal failures --- lib/commands/remove.sh | 15 ++++++++++++--- tests/cmd_remove.bats | 20 ++++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/commands/remove.sh b/lib/commands/remove.sh index fda759e..776086e 100644 --- a/lib/commands/remove.sh +++ b/lib/commands/remove.sh @@ -14,6 +14,7 @@ cmd_remove() { local delete_branch="${_arg_delete_branch:-0}" local yes_mode="${_arg_yes:-0}" local force="${_arg_force:-0}" + local had_failure=0 resolve_repo_context || exit 1 @@ -22,13 +23,17 @@ cmd_remove() { for identifier in "${_pa_positional[@]}"; do # Resolve target branch local is_main worktree_path branch_name - resolve_worktree "$identifier" "$repo_root" "$base_dir" "$prefix" || continue - + if ! resolve_worktree "$identifier" "$repo_root" "$base_dir" "$prefix"; then + had_failure=1 + continue + fi + is_main="$_ctx_is_main" worktree_path="$_ctx_worktree_path" branch_name="$_ctx_branch" # Cannot remove main repository if [ "$is_main" = "1" ]; then log_error "Cannot remove main repository" + had_failure=1 continue fi @@ -41,6 +46,7 @@ cmd_remove() { BRANCH="$branch_name"; then if [ "$force" -eq 0 ]; then log_error "Pre-remove hook failed for $branch_name. Use --force to skip hooks." + had_failure=1 continue else log_warn "Pre-remove hook failed, continuing due to --force" @@ -49,6 +55,7 @@ cmd_remove() { # Remove the worktree if ! remove_worktree "$worktree_path" "$force"; then + had_failure=1 continue fi @@ -73,4 +80,6 @@ cmd_remove() { log_warn "Post-remove hook failed for $branch_name" fi done -} \ No newline at end of file + + return "$had_failure" +} diff --git a/tests/cmd_remove.bats b/tests/cmd_remove.bats index a926b91..24c4643 100644 --- a/tests/cmd_remove.bats +++ b/tests/cmd_remove.bats @@ -24,15 +24,14 @@ teardown() { [ "$status" -eq 1 ] } -@test "cmd_remove skips unknown branch and continues" { - # cmd_remove uses 'continue' for individual failures, not 'exit' +@test "cmd_remove fails for an unknown branch" { run cmd_remove nonexistent - [ "$status" -eq 0 ] + [ "$status" -eq 1 ] } @test "cmd_remove cannot remove main repo" { run cmd_remove 1 - [ "$status" -eq 0 ] # continues past error, doesn't exit + [ "$status" -eq 1 ] # Main repo should still exist [ -d "$TEST_REPO" ] } @@ -56,10 +55,22 @@ teardown() { create_test_worktree "hook-block" git config --add gtr.hook.preRemove "exit 1" run cmd_remove hook-block + [ "$status" -eq 1 ] # Worktree should still exist (hook blocked removal) [ -d "$TEST_WORKTREES_DIR/hook-block" ] } +@test "cmd_remove fails when git refuses to remove a dirty worktree" { + create_test_worktree "dirty-rm" + touch "$TEST_WORKTREES_DIR/dirty-rm/untracked" + + run cmd_remove dirty-rm + + [ "$status" -eq 1 ] + [ -d "$TEST_WORKTREES_DIR/dirty-rm" ] + [[ "$output" == *"contains modified or untracked files"* ]] +} + @test "cmd_remove --force skips failed pre-remove hook" { create_test_worktree "force-rm" git config --add gtr.hook.preRemove "exit 1" @@ -78,6 +89,7 @@ teardown() { create_test_worktree "good-rm" # Try to remove both a nonexistent and existing worktree run cmd_remove nonexistent good-rm + [ "$status" -eq 1 ] # The good one should have been removed despite the bad one failing [ ! -d "$TEST_WORKTREES_DIR/good-rm" ] } From 4e4881602e2c6eb31203f1b576c7e9383e520572 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 12 Aug 2026 00:47:20 -0700 Subject: [PATCH 2/2] Test public rm failure status --- tests/cmd_remove.bats | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/cmd_remove.bats b/tests/cmd_remove.bats index 24c4643..529eaed 100644 --- a/tests/cmd_remove.bats +++ b/tests/cmd_remove.bats @@ -29,6 +29,11 @@ teardown() { [ "$status" -eq 1 ] } +@test "git gtr rm propagates a failed exit status" { + run env PATH="$PROJECT_ROOT/bin:$PATH" git gtr rm nonexistent + [ "$status" -eq 1 ] +} + @test "cmd_remove cannot remove main repo" { run cmd_remove 1 [ "$status" -eq 1 ]