Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this

## [Unreleased]

### Fixed

- `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.

## [2.11.0] - 2026-08-19

### Added
Expand Down
6 changes: 5 additions & 1 deletion bin/git-gtr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
# Portable, cross-platform git worktree management
# Invoked as: git gtr <command> (git subcommand via PATH discovery)

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

# Debug: show file:line:function on set -e failures
if [ -n "${GTR_DEBUG:-}" ]; then
Expand Down
66 changes: 66 additions & 0 deletions tests/debug_trap.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env bats
# Tests for the GTR_DEBUG error trap in bin/git-gtr
#
# The trap is only useful when bin/git-gtr enables errtrace. An ERR trap is
# inherited by functions, command substitutions and subshells only under
# 'set -E'; with plain 'set -e' it never fires, because every command runs
# inside main() and then a cmd_* handler. These tests run the real binary as a
# subprocess so that the option line in bin/git-gtr is actually exercised.

load test_helper

setup() {
setup_integration_repo

# A git shim that fails one specific config write and passes everything else
# through, standing in for an unexpected git failure at an unguarded call
# site (cfg_set in lib/config.sh).
REAL_GIT=$(command -v git)
SHIM_DIR=$(mktemp -d)
cat > "$SHIM_DIR/git" <<SCRIPT
#!/usr/bin/env bash
if [ "\$1" = "config" ]; then
case "\$*" in
*--get*|*--list*|*--file*) : ;;
*gtr.editor.default*) exit 4 ;;
esac
fi
exec "$REAL_GIT" "\$@"
SCRIPT
chmod +x "$SHIM_DIR/git"
}

teardown() {
[ -n "${SHIM_DIR:-}" ] && rm -rf "$SHIM_DIR"
teardown_integration_repo
}

@test "GTR_DEBUG reports file, line and function for an unguarded failure" {
run env PATH="$SHIM_DIR:$PATH" GTR_DEBUG=1 \
"$PROJECT_ROOT/bin/git-gtr" config set gtr.editor.default vim
[ "$status" -ne 0 ]
[[ "$output" == *"ERROR at "* ]]
[[ "$output" == *"lib/config.sh"* ]]
[[ "$output" == *"cfg_set()"* ]]
}

@test "the error trap stays silent when GTR_DEBUG is unset" {
run env PATH="$SHIM_DIR:$PATH" \
"$PROJECT_ROOT/bin/git-gtr" config set gtr.editor.default vim
[ "$status" -ne 0 ]
[[ "$output" != *"ERROR at "* ]]
}

@test "GTR_DEBUG does not report anything for a successful command" {
run env GTR_DEBUG=1 "$PROJECT_ROOT/bin/git-gtr" config set gtr.editor.default vim
[ "$status" -eq 0 ]
[[ "$output" != *"ERROR at "* ]]
}

@test "GTR_DEBUG does not report handled errors" {
# cmd_go reports a missing worktree itself; that is a deliberate error path,
# not an unguarded failure, so the trap must not add noise to it.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
run env GTR_DEBUG=1 "$PROJECT_ROOT/bin/git-gtr" go no-such-branch
[ "$status" -ne 0 ]
[[ "$output" != *"ERROR at "* ]]
}
Loading