From 23dde98897b1169f36181868340e70d051113788 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 25 Feb 2026 01:04:56 -0800 Subject: [PATCH 1/5] feat(init): cache output for faster shell startup Cache generated shell integration to ~/.cache/gtr/ with version-stamped invalidation. On warm cache, sourcing the file directly avoids the subprocess overhead entirely (~5ms vs ~60ms). - init now writes cache file keyed by version + func name + shell - Cache auto-invalidates when GTR_VERSION changes - Docs updated to recommend source pattern as primary setup method - 5 new tests for cache creation, hit, invalidation, and key isolation --- CLAUDE.md | 2 +- README.md | 18 ++++++++++-- bin/git-gtr | 2 +- lib/commands/help.sh | 18 +++++++++--- lib/commands/init.sh | 46 +++++++++++++++++++++--------- tests/init.bats | 67 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 23 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 949a9bb..7b55960 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -112,7 +112,7 @@ cmd_editor() → resolve_target() → load_editor_adapter() → editor_open() **`.gtrconfig`**: Team-shared config using gitconfig syntax, parsed via `git config -f`. Keys map differently from git config (e.g., `gtr.copy.include` → `copy.include`, `gtr.hook.postCreate` → `hooks.postCreate`). See the .gtrconfig Key Mapping table in README or `docs/configuration.md`. -**`init` command**: Outputs shell functions for `gtr cd ` navigation. Users add `eval "$(git gtr init bash)"` to their shell rc file. +**`init` command**: Outputs shell functions for `gtr cd ` navigation. Output is cached to `~/.cache/gtr/` and auto-invalidates on version change. Users add `eval "$(git gtr init bash)"` to their shell rc file, or source the cache file directly for faster startup. **`clean --merged`**: Removes worktrees whose PRs/MRs are merged. Auto-detects GitHub (`gh`) or GitLab (`glab`) from the `origin` remote URL. Override with `gtr.provider` config for self-hosted instances. diff --git a/README.md b/README.md index 22cae0a..1234140 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ git gtr run my-feature npm test # Run tests # Navigate to worktree gtr cd # Interactive picker (requires fzf + shell integration) -gtr cd my-feature # Requires: eval "$(git gtr init bash)" +gtr cd my-feature # Requires shell integration (see below) cd "$(git gtr go my-feature)" # Alternative without shell integration # List all worktrees @@ -214,8 +214,18 @@ cd "$(git gtr go 1)" # Navigate to main repo **Tip:** For easier navigation, use `git gtr init` to enable `gtr cd`: ```bash -# Add to ~/.bashrc or ~/.zshrc (one-time setup) -eval "$(git gtr init bash)" +# Bash (add to ~/.bashrc) +_gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.bash" +[[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" +source "$_gtr_init" 2>/dev/null; unset _gtr_init + +# Zsh (add to ~/.zshrc) +_gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh" +[[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" +source "$_gtr_init" 2>/dev/null; unset _gtr_init + +# Fish (add to ~/.config/fish/config.fish) +git gtr init fish | source # Then navigate with: gtr cd # Interactive worktree picker (requires fzf) @@ -223,6 +233,8 @@ gtr cd my-feature gtr cd 1 ``` +The cache auto-regenerates on first run and auto-invalidates when git-gtr is updated. To force-regenerate: `rm -rf ~/.cache/gtr` + With [fzf](https://github.com/junegunn/fzf) installed, `gtr cd` (no arguments) opens a command palette with git log preview and keybindings: `ctrl-e` editor, `ctrl-a` AI, `ctrl-d` delete, `ctrl-y` copy, `ctrl-r` refresh. > **Note:** If `gtr` conflicts with another command (e.g., GNU `tr` from coreutils), use `--as` to pick a different name: diff --git a/bin/git-gtr b/bin/git-gtr index 2929911..b2b3cc2 100755 --- a/bin/git-gtr +++ b/bin/git-gtr @@ -110,7 +110,7 @@ main() { local _shell_name _shell_name="$(basename "${SHELL:-bash}")" log_error "'cd' requires shell integration (subprocesses cannot change your shell's directory)" - log_info "Set up with: eval \"\$(git gtr init $_shell_name)\"" + log_info "Set up with: eval \"\$(git gtr init $_shell_name)\" (output is cached for fast startup)" log_info "Then use: gtr cd []" exit 1 ;; diff --git a/lib/commands/help.sh b/lib/commands/help.sh index e3aef18..c8ea3d2 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -103,6 +103,7 @@ Usage: git gtr go Prints the absolute path to the specified worktree. Useful for navigation with cd or for scripting. For direct cd support, use shell integration: eval "$(git gtr init bash)" # then: gtr cd + (output is cached automatically for fast startup) Special: Use '1' for the main repo root: git gtr go 1 @@ -345,18 +346,26 @@ Usage: git gtr init [--as ] Generates shell functions for enhanced features like 'gtr cd ' which changes directory to a worktree. Add to your shell configuration. +Output is cached to ~/.cache/gtr/ for fast shell startup (~1ms vs ~60ms). +The cache auto-invalidates when git-gtr is updated. To force-regenerate: + rm -rf ~/.cache/gtr + Supported shells: bash, zsh, fish Options: --as Set custom function name (default: gtr) Useful if 'gtr' conflicts with another command (e.g., GNU tr) -Setup: +Setup (sources cached output directly for fast startup): # Bash (add to ~/.bashrc) - eval "$(git gtr init bash)" + _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.bash" + [[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" + source "$_gtr_init" 2>/dev/null; unset _gtr_init # Zsh (add to ~/.zshrc) - eval "$(git gtr init zsh)" + _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh" + [[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" + source "$_gtr_init" 2>/dev/null; unset _gtr_init # Fish (add to ~/.config/fish/config.fish) git gtr init fish | source @@ -558,7 +567,8 @@ SETUP & MAINTENANCE: init [--as ] Generate shell integration for cd support (bash, zsh, fish) --as : custom function name (default: gtr) - Usage: eval "$(git gtr init bash)" + Output is cached for fast startup (auto-invalidates on update) + See git gtr help init for recommended setup With fzf: 'gtr cd' opens a command palette (preview, editor, AI, delete) version diff --git a/lib/commands/init.sh b/lib/commands/init.sh index daf30c0..02a0698 100644 --- a/lib/commands/init.sh +++ b/lib/commands/init.sh @@ -50,19 +50,13 @@ cmd_init() { return 1 fi + # Resolve generator function + local generator case "$shell" in - bash) - _init_bash | sed "s/__FUNC__/$func_name/g" - ;; - zsh) - _init_zsh | sed "s/__FUNC__/$func_name/g" - ;; - fish) - _init_fish | sed "s/__FUNC__/$func_name/g" - ;; - "") - show_command_help - ;; + bash) generator="_init_bash" ;; + zsh) generator="_init_zsh" ;; + fish) generator="_init_fish" ;; + "") show_command_help; return 0 ;; *) log_error "Unknown shell: $shell" log_error "Supported shells: bash, zsh, fish" @@ -70,13 +64,36 @@ cmd_init() { return 1 ;; esac + + # Generate output (cached to ~/.cache/gtr/, auto-invalidates on version change) + local cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/gtr" + local cache_file="$cache_dir/init-${func_name}.${shell}" + local cache_stamp="# gtr-cache: version=${GTR_VERSION:-unknown} func=$func_name shell=$shell" + + # Return cached output if version matches + if [ -f "$cache_file" ]; then + local first_line + first_line="$(head -1 "$cache_file")" + if [ "$first_line" = "$cache_stamp" ]; then + tail -n +2 "$cache_file" + return 0 + fi + fi + + # Generate, cache, and output + local output + output="$("$generator" | sed "s/__FUNC__/$func_name/g")" + mkdir -p "$cache_dir" + printf '%s\n%s\n' "$cache_stamp" "$output" > "$cache_file" + printf '%s\n' "$output" } _init_bash() { cat <<'BASH' -# git-gtr shell integration +# git-gtr shell integration (output is cached to ~/.cache/gtr/) # Add to ~/.bashrc: # eval "$(git gtr init bash)" +# Faster: source the cache file directly (see git gtr help init) __FUNC__() { if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then @@ -179,9 +196,10 @@ BASH _init_zsh() { cat <<'ZSH' -# git-gtr shell integration +# git-gtr shell integration (output is cached to ~/.cache/gtr/) # Add to ~/.zshrc: # eval "$(git gtr init zsh)" +# Faster: source the cache file directly (see git gtr help init) __FUNC__() { emulate -L zsh diff --git a/tests/init.bats b/tests/init.bats index 2c5f844..c7433e0 100644 --- a/tests/init.bats +++ b/tests/init.bats @@ -5,6 +5,13 @@ load test_helper setup() { source "$PROJECT_ROOT/lib/commands/init.sh" + # Isolate cache to temp dir so tests don't pollute ~/.cache or each other + export XDG_CACHE_HOME="$BATS_TMPDIR/gtr-init-cache-$$" + export GTR_VERSION="test" +} + +teardown() { + rm -rf "$BATS_TMPDIR/gtr-init-cache-$$" } # ── Default function name ──────────────────────────────────────────────────── @@ -219,3 +226,63 @@ setup() { [[ "$output" == *"command git gtr"* ]] [[ "$output" == *"git gtr list --porcelain"* ]] } + +# ── caching (default behavior) ────────────────────────────────────────────── + +@test "init creates cache file and returns output" { + local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init zsh + [ "$status" -eq 0 ] + [[ "$output" == *"gtr()"* ]] + [ -f "$cache_dir/gtr/init-gtr.zsh" ] + rm -rf "$cache_dir" +} + +@test "init returns cached output on second call" { + local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" + # First call: generates and caches + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init bash + [ "$status" -eq 0 ] + local first_output="$output" + # Second call: reads from cache + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init bash + [ "$status" -eq 0 ] + [ "$output" = "$first_output" ] + rm -rf "$cache_dir" +} + +@test "cache invalidates when version changes" { + local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" + # Generate with version 1.0.0 + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="1.0.0" run cmd_init zsh + [ "$status" -eq 0 ] + # Check cache stamp + local stamp + stamp="$(head -1 "$cache_dir/gtr/init-gtr.zsh")" + [[ "$stamp" == *"version=1.0.0"* ]] + # Regenerate with version 2.0.0 + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="2.0.0" run cmd_init zsh + [ "$status" -eq 0 ] + stamp="$(head -1 "$cache_dir/gtr/init-gtr.zsh")" + [[ "$stamp" == *"version=2.0.0"* ]] + rm -rf "$cache_dir" +} + +@test "cache uses --as func name in cache key" { + local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init bash --as myfn + [ "$status" -eq 0 ] + [[ "$output" == *"myfn()"* ]] + [ -f "$cache_dir/gtr/init-myfn.bash" ] + rm -rf "$cache_dir" +} + +@test "cache works for all shells" { + local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" + for sh in bash zsh fish; do + XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init "$sh" + [ "$status" -eq 0 ] + [ -f "$cache_dir/gtr/init-gtr.${sh}" ] + done + rm -rf "$cache_dir" +} From ee381109ceb1c1ddc7a3a6a3236de957acad65a8 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 25 Feb 2026 01:08:22 -0800 Subject: [PATCH 2/5] docs: remove standalone eval references, use source pattern everywhere Update all remaining docs, error messages, and hints to point to the source pattern or `git gtr help init` instead of bare eval. Doctor detection also matches source-pattern users (gtr/init- in rc). --- CLAUDE.md | 2 +- bin/git-gtr | 4 ++-- lib/commands/doctor.sh | 10 ++-------- lib/commands/help.sh | 4 ++-- lib/commands/init.sh | 12 ++++-------- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7b55960..aabea71 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -112,7 +112,7 @@ cmd_editor() → resolve_target() → load_editor_adapter() → editor_open() **`.gtrconfig`**: Team-shared config using gitconfig syntax, parsed via `git config -f`. Keys map differently from git config (e.g., `gtr.copy.include` → `copy.include`, `gtr.hook.postCreate` → `hooks.postCreate`). See the .gtrconfig Key Mapping table in README or `docs/configuration.md`. -**`init` command**: Outputs shell functions for `gtr cd ` navigation. Output is cached to `~/.cache/gtr/` and auto-invalidates on version change. Users add `eval "$(git gtr init bash)"` to their shell rc file, or source the cache file directly for faster startup. +**`init` command**: Outputs shell functions for `gtr cd ` navigation. Output is cached to `~/.cache/gtr/` and auto-invalidates on version change. Users source the cache file directly in their shell rc for fast startup (see `git gtr help init`). **`clean --merged`**: Removes worktrees whose PRs/MRs are merged. Auto-detects GitHub (`gh`) or GitLab (`glab`) from the `origin` remote URL. Override with `gtr.provider` config for self-hosted instances. diff --git a/bin/git-gtr b/bin/git-gtr index b2b3cc2..9770fb7 100755 --- a/bin/git-gtr +++ b/bin/git-gtr @@ -110,8 +110,8 @@ main() { local _shell_name _shell_name="$(basename "${SHELL:-bash}")" log_error "'cd' requires shell integration (subprocesses cannot change your shell's directory)" - log_info "Set up with: eval \"\$(git gtr init $_shell_name)\" (output is cached for fast startup)" - log_info "Then use: gtr cd []" + log_info "Run 'git gtr help init' for setup instructions" + log_info "Then use: gtr cd []" exit 1 ;; *) diff --git a/lib/commands/doctor.sh b/lib/commands/doctor.sh index df749bc..da49668 100644 --- a/lib/commands/doctor.sh +++ b/lib/commands/doctor.sh @@ -128,16 +128,10 @@ cmd_doctor() { fish) _rc_file="$HOME/.config/fish/config.fish" ;; *) _rc_file="" ;; esac - if [ -n "$_rc_file" ] && [ -f "$_rc_file" ] && grep -q 'git gtr init' "$_rc_file" 2>/dev/null; then + if [ -n "$_rc_file" ] && [ -f "$_rc_file" ] && grep -qE 'git gtr init|gtr/init-' "$_rc_file" 2>/dev/null; then echo "[OK] Shell integration: loaded (gtr cd available)" elif [ -n "$_rc_file" ]; then - local _init_hint - if [ "$_shell_name" = "fish" ]; then - _init_hint="git gtr init fish | source" - else - _init_hint="eval \"\$(git gtr init $_shell_name)\"" - fi - echo "[i] Shell integration: $_init_hint in ${_rc_file##*/} for gtr cd" + echo "[i] Shell integration: run 'git gtr help init' for setup instructions" fi echo "" diff --git a/lib/commands/help.sh b/lib/commands/help.sh index c8ea3d2..d10f719 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -102,8 +102,8 @@ Usage: git gtr go Prints the absolute path to the specified worktree. Useful for navigation with cd or for scripting. For direct cd support, use shell integration: - eval "$(git gtr init bash)" # then: gtr cd - (output is cached automatically for fast startup) + git gtr help init # see setup instructions + gtr cd # then navigate directly Special: Use '1' for the main repo root: git gtr go 1 diff --git a/lib/commands/init.sh b/lib/commands/init.sh index 02a0698..4ee3e8a 100644 --- a/lib/commands/init.sh +++ b/lib/commands/init.sh @@ -90,10 +90,8 @@ cmd_init() { _init_bash() { cat <<'BASH' -# git-gtr shell integration (output is cached to ~/.cache/gtr/) -# Add to ~/.bashrc: -# eval "$(git gtr init bash)" -# Faster: source the cache file directly (see git gtr help init) +# git-gtr shell integration (cached to ~/.cache/gtr/) +# Setup: see git gtr help init __FUNC__() { if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then @@ -196,10 +194,8 @@ BASH _init_zsh() { cat <<'ZSH' -# git-gtr shell integration (output is cached to ~/.cache/gtr/) -# Add to ~/.zshrc: -# eval "$(git gtr init zsh)" -# Faster: source the cache file directly (see git gtr help init) +# git-gtr shell integration (cached to ~/.cache/gtr/) +# Setup: see git gtr help init __FUNC__() { emulate -L zsh From eb0339d8a23da15de3433eaa7ebade64a06acef4 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 25 Feb 2026 19:24:24 -0800 Subject: [PATCH 3/5] Address CodeRabbit review: set -e safety, docs accuracy, test isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make cache write best-effort in cmd_init (print output first, guard mkdir/write with || true) so set -e failures don't swallow shell init - Add || true guards to eval/source in README and help.sh setup examples to protect users with set -e in their shell rc - Fix misleading "auto-invalidates on update" wording in help text — the cached setup pattern only regenerates when the cache file is missing - Simplify cache tests to use setup()'s XDG_CACHE_HOME instead of a shared per-PID cache_dir that can leak between tests on failure --- README.md | 10 +++++----- lib/commands/help.sh | 13 +++++++------ lib/commands/init.sh | 7 ++++--- tests/init.bats | 34 ++++++++++++---------------------- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 1234140..dca33b6 100644 --- a/README.md +++ b/README.md @@ -216,13 +216,13 @@ cd "$(git gtr go 1)" # Navigate to main repo ```bash # Bash (add to ~/.bashrc) _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.bash" -[[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" -source "$_gtr_init" 2>/dev/null; unset _gtr_init +[[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" || true +source "$_gtr_init" 2>/dev/null || true; unset _gtr_init # Zsh (add to ~/.zshrc) _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh" -[[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" -source "$_gtr_init" 2>/dev/null; unset _gtr_init +[[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" || true +source "$_gtr_init" 2>/dev/null || true; unset _gtr_init # Fish (add to ~/.config/fish/config.fish) git gtr init fish | source @@ -233,7 +233,7 @@ gtr cd my-feature gtr cd 1 ``` -The cache auto-regenerates on first run and auto-invalidates when git-gtr is updated. To force-regenerate: `rm -rf ~/.cache/gtr` +The cache generates on first run and refreshes the next time `git gtr init ` runs. To force-regenerate: `rm -rf ~/.cache/gtr` With [fzf](https://github.com/junegunn/fzf) installed, `gtr cd` (no arguments) opens a command palette with git log preview and keybindings: `ctrl-e` editor, `ctrl-a` AI, `ctrl-d` delete, `ctrl-y` copy, `ctrl-r` refresh. diff --git a/lib/commands/help.sh b/lib/commands/help.sh index d10f719..ba17eb3 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -347,8 +347,9 @@ Generates shell functions for enhanced features like 'gtr cd ' which changes directory to a worktree. Add to your shell configuration. Output is cached to ~/.cache/gtr/ for fast shell startup (~1ms vs ~60ms). -The cache auto-invalidates when git-gtr is updated. To force-regenerate: - rm -rf ~/.cache/gtr +The cache refreshes the next time 'git gtr init ' runs (checks version). +With the recommended setup below, it regenerates when the cache file is missing. +To force-regenerate: rm -rf ~/.cache/gtr Supported shells: bash, zsh, fish @@ -359,13 +360,13 @@ Options: Setup (sources cached output directly for fast startup): # Bash (add to ~/.bashrc) _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.bash" - [[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" - source "$_gtr_init" 2>/dev/null; unset _gtr_init + [[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" || true + source "$_gtr_init" 2>/dev/null || true; unset _gtr_init # Zsh (add to ~/.zshrc) _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh" - [[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" - source "$_gtr_init" 2>/dev/null; unset _gtr_init + [[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" || true + source "$_gtr_init" 2>/dev/null || true; unset _gtr_init # Fish (add to ~/.config/fish/config.fish) git gtr init fish | source diff --git a/lib/commands/init.sh b/lib/commands/init.sh index da03503..e35af99 100644 --- a/lib/commands/init.sh +++ b/lib/commands/init.sh @@ -80,12 +80,13 @@ cmd_init() { fi fi - # Generate, cache, and output + # Generate, output, and cache (output first so set -e cache failures don't swallow it) local output output="$("$generator" | sed "s/__FUNC__/$func_name/g")" - mkdir -p "$cache_dir" - printf '%s\n%s\n' "$cache_stamp" "$output" > "$cache_file" printf '%s\n' "$output" + if mkdir -p "$cache_dir" 2>/dev/null; then + printf '%s\n%s\n' "$cache_stamp" "$output" > "$cache_file" 2>/dev/null || true + fi } _init_bash() { diff --git a/tests/init.bats b/tests/init.bats index 7853336..e34fc8f 100644 --- a/tests/init.bats +++ b/tests/init.bats @@ -507,59 +507,49 @@ teardown() { # ── caching (default behavior) ────────────────────────────────────────────── @test "init creates cache file and returns output" { - local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init zsh + GTR_VERSION="9.9.9" run cmd_init zsh [ "$status" -eq 0 ] [[ "$output" == *"gtr()"* ]] - [ -f "$cache_dir/gtr/init-gtr.zsh" ] - rm -rf "$cache_dir" + [ -f "$XDG_CACHE_HOME/gtr/init-gtr.zsh" ] } @test "init returns cached output on second call" { - local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" # First call: generates and caches - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init bash + GTR_VERSION="9.9.9" run cmd_init bash [ "$status" -eq 0 ] local first_output="$output" # Second call: reads from cache - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init bash + GTR_VERSION="9.9.9" run cmd_init bash [ "$status" -eq 0 ] [ "$output" = "$first_output" ] - rm -rf "$cache_dir" } @test "cache invalidates when version changes" { - local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" # Generate with version 1.0.0 - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="1.0.0" run cmd_init zsh + GTR_VERSION="1.0.0" run cmd_init zsh [ "$status" -eq 0 ] # Check cache stamp local stamp - stamp="$(head -1 "$cache_dir/gtr/init-gtr.zsh")" + stamp="$(head -1 "$XDG_CACHE_HOME/gtr/init-gtr.zsh")" [[ "$stamp" == *"version=1.0.0"* ]] # Regenerate with version 2.0.0 - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="2.0.0" run cmd_init zsh + GTR_VERSION="2.0.0" run cmd_init zsh [ "$status" -eq 0 ] - stamp="$(head -1 "$cache_dir/gtr/init-gtr.zsh")" + stamp="$(head -1 "$XDG_CACHE_HOME/gtr/init-gtr.zsh")" [[ "$stamp" == *"version=2.0.0"* ]] - rm -rf "$cache_dir" } @test "cache uses --as func name in cache key" { - local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init bash --as myfn + GTR_VERSION="9.9.9" run cmd_init bash --as myfn [ "$status" -eq 0 ] [[ "$output" == *"myfn()"* ]] - [ -f "$cache_dir/gtr/init-myfn.bash" ] - rm -rf "$cache_dir" + [ -f "$XDG_CACHE_HOME/gtr/init-myfn.bash" ] } @test "cache works for all shells" { - local cache_dir="$BATS_TMPDIR/gtr-cache-test-$$" for sh in bash zsh fish; do - XDG_CACHE_HOME="$cache_dir" GTR_VERSION="9.9.9" run cmd_init "$sh" + GTR_VERSION="9.9.9" run cmd_init "$sh" [ "$status" -eq 0 ] - [ -f "$cache_dir/gtr/init-gtr.${sh}" ] + [ -f "$XDG_CACHE_HOME/gtr/init-gtr.${sh}" ] done - rm -rf "$cache_dir" } From 2f9ec290db35116f781124bb97b8e2aa2bc2a40f Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 25 Feb 2026 19:30:36 -0800 Subject: [PATCH 4/5] Refactor Fish shell setup in README and help.sh to use cache initialization - Update Fish setup instructions to use a cached initialization file instead of direct command execution. - Implement checks for the existence of the cache file before attempting to initialize, improving robustness. - Ensure compatibility with users who have set -e in their shell configuration by adding error handling to source commands. --- README.md | 4 +++- lib/commands/help.sh | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dca33b6..e3b0ec6 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,9 @@ _gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh" source "$_gtr_init" 2>/dev/null || true; unset _gtr_init # Fish (add to ~/.config/fish/config.fish) -git gtr init fish | source +set -l _gtr_init (test -n "$XDG_CACHE_HOME" && echo $XDG_CACHE_HOME || echo $HOME/.cache)/gtr/init-gtr.fish +test -f "$_gtr_init"; or git gtr init fish >/dev/null 2>&1 +source "$_gtr_init" 2>/dev/null # Then navigate with: gtr cd # Interactive worktree picker (requires fzf) diff --git a/lib/commands/help.sh b/lib/commands/help.sh index ba17eb3..bb18307 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -369,7 +369,9 @@ Setup (sources cached output directly for fast startup): source "$_gtr_init" 2>/dev/null || true; unset _gtr_init # Fish (add to ~/.config/fish/config.fish) - git gtr init fish | source + set -l _gtr_init (test -n "$XDG_CACHE_HOME" && echo $XDG_CACHE_HOME || echo $HOME/.cache)/gtr/init-gtr.fish + test -f "$_gtr_init"; or git gtr init fish >/dev/null 2>&1 + source "$_gtr_init" 2>/dev/null # Custom function name (avoids conflict with coreutils gtr) eval "$(git gtr init zsh --as gwtr)" From 6da0d4daff4de68b8cc6d62b3e3dd21e25baca1e Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Wed, 25 Feb 2026 19:38:43 -0800 Subject: [PATCH 5/5] Fix misleading cache wording in help summary Change "auto-invalidates on update" to "refreshes when 'git gtr init' runs" to match the accurate description in `git gtr help init`. --- lib/commands/help.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/help.sh b/lib/commands/help.sh index bb18307..db2f340 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -570,7 +570,7 @@ SETUP & MAINTENANCE: init [--as ] Generate shell integration for cd support (bash, zsh, fish) --as : custom function name (default: gtr) - Output is cached for fast startup (auto-invalidates on update) + Output is cached for fast startup (refreshes when 'git gtr init' runs) See git gtr help init for recommended setup With fzf: 'gtr cd' opens a command palette (preview, editor, AI, delete)