Skip to content

Commit 540fa2c

Browse files
authored
fix: make GTR_DEBUG actually report the failing location (#199)
bin/git-gtr installed an ERR trap when GTR_DEBUG was set but ran under 'set -e' alone. An ERR trap is inherited by functions, command substitutions and subshells only under 'set -E', and every command runs inside main() and a cmd_* handler, so the trap never fired. Switch the option line to 'set -eE'. With no ERR trap installed the option has no effect, so the default path is unchanged. Adds tests/debug_trap.bats covering the function and subshell contexts, plus silence on success, on a handled error path, and when GTR_DEBUG is unset.
1 parent cd72301 commit 540fa2c

3 files changed

Lines changed: 93 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- `GTR_DEBUG=1` now reports the file, line and function of an unexpected failure. `bin/git-gtr` installed an `ERR` trap but ran under `set -e` alone, so the trap was never inherited by functions; since every command runs inside `main()` and a `cmd_*` handler, the variable had no observable effect. The script now uses `set -eE`, which changes nothing when the trap is not installed.
12+
913
## [2.11.0] - 2026-08-19
1014

1115
### Added

bin/git-gtr

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
# Portable, cross-platform git worktree management
44
# Invoked as: git gtr <command> (git subcommand via PATH discovery)
55

6-
set -e
6+
# -E propagates the ERR trap below into functions, command substitutions and
7+
# subshells. Without it the GTR_DEBUG trap never fires, because every command
8+
# runs inside main() and a cmd_* handler. With no ERR trap installed, -E has no
9+
# effect, so the default path behaves exactly as it did with plain -e.
10+
set -eE
711

812
# Debug: show file:line:function on set -e failures
913
if [ -n "${GTR_DEBUG:-}" ]; then

tests/debug_trap.bats

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bats
2+
# Tests for the GTR_DEBUG error trap in bin/git-gtr
3+
#
4+
# The trap is only useful when bin/git-gtr enables errtrace. An ERR trap is
5+
# inherited by functions, command substitutions and subshells only under
6+
# 'set -E'; with plain 'set -e' it never fires, because every command runs
7+
# inside main() and then a cmd_* handler. These tests run the real binary as a
8+
# subprocess so that the option line in bin/git-gtr is actually exercised.
9+
10+
load test_helper
11+
12+
setup() {
13+
setup_integration_repo
14+
15+
# A git shim that fails one specific config write and passes everything else
16+
# through, standing in for an unexpected git failure at an unguarded call
17+
# site (cfg_set in lib/config.sh).
18+
REAL_GIT=$(command -v git)
19+
SHIM_DIR=$(mktemp -d)
20+
cat > "$SHIM_DIR/git" <<SCRIPT
21+
#!/usr/bin/env bash
22+
if [ "\$1" = "config" ]; then
23+
case "\$*" in
24+
*--get*|*--list*|*--file*) : ;;
25+
*gtr.editor.default*) exit 4 ;;
26+
esac
27+
fi
28+
exec "$REAL_GIT" "\$@"
29+
SCRIPT
30+
chmod +x "$SHIM_DIR/git"
31+
}
32+
33+
teardown() {
34+
[ -n "${SHIM_DIR:-}" ] && rm -rf "$SHIM_DIR"
35+
teardown_integration_repo
36+
}
37+
38+
@test "GTR_DEBUG reports file, line and function for an unguarded failure" {
39+
run env PATH="$SHIM_DIR:$PATH" GTR_DEBUG=1 \
40+
"$PROJECT_ROOT/bin/git-gtr" config set gtr.editor.default vim
41+
[ "$status" -ne 0 ]
42+
[[ "$output" == *"ERROR at "* ]]
43+
[[ "$output" == *"lib/config.sh"* ]]
44+
[[ "$output" == *"cfg_set()"* ]]
45+
}
46+
47+
@test "the error trap stays silent when GTR_DEBUG is unset" {
48+
run env PATH="$SHIM_DIR:$PATH" \
49+
"$PROJECT_ROOT/bin/git-gtr" config set gtr.editor.default vim
50+
[ "$status" -ne 0 ]
51+
[[ "$output" != *"ERROR at "* ]]
52+
}
53+
54+
@test "GTR_DEBUG does not report anything for a successful command" {
55+
run env GTR_DEBUG=1 "$PROJECT_ROOT/bin/git-gtr" config set gtr.editor.default vim
56+
[ "$status" -eq 0 ]
57+
[[ "$output" != *"ERROR at "* ]]
58+
}
59+
60+
@test "GTR_DEBUG does not report handled errors" {
61+
# cmd_go reports a missing worktree itself; that is a deliberate error path,
62+
# not an unguarded failure, so the trap must not add noise to it.
63+
run env GTR_DEBUG=1 "$PROJECT_ROOT/bin/git-gtr" go no-such-branch
64+
[ "$status" -ne 0 ]
65+
[[ "$output" != *"ERROR at "* ]]
66+
}
67+
68+
@test "GTR_DEBUG reports a failure raised inside a subshell" {
69+
# cmd_run executes the requested command in a subshell:
70+
# (cd "$worktree_path" && "${run_args[@]}")
71+
# A failing command there is only reported when the ERR trap is inherited by
72+
# subshells, which is the other half of what errtrace buys.
73+
run env GTR_DEBUG=1 "$PROJECT_ROOT/bin/git-gtr" run 1 false
74+
[ "$status" -ne 0 ]
75+
[[ "$output" == *"ERROR at "* ]]
76+
[[ "$output" == *"lib/commands/run.sh"* ]]
77+
[[ "$output" == *"cmd_run()"* ]]
78+
}
79+
80+
@test "a successful command under gtr run reports nothing" {
81+
run env GTR_DEBUG=1 "$PROJECT_ROOT/bin/git-gtr" run 1 true
82+
[ "$status" -eq 0 ]
83+
[[ "$output" != *"ERROR at "* ]]
84+
}

0 commit comments

Comments
 (0)