Skip to content

Commit d6fd5da

Browse files
authored
feat(init): cache output for faster shell startup (#142)
1 parent dd46f0f commit d6fd5da

File tree

7 files changed

+131
-38
lines changed

7 files changed

+131
-38
lines changed

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ cmd_editor() → resolve_target() → load_editor_adapter() → editor_open()
112112

113113
**`.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`.
114114

115-
**`init` command**: Outputs shell functions for `gtr cd <branch>` navigation. Users add `eval "$(git gtr init bash)"` to their shell rc file.
115+
**`init` command**: Outputs shell functions for `gtr cd <branch>` 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`).
116116

117117
**`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.
118118

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ git gtr run my-feature npm test # Run tests
101101

102102
# Navigate to worktree
103103
gtr cd # Interactive picker (requires fzf + shell integration)
104-
gtr cd my-feature # Requires: eval "$(git gtr init bash)"
104+
gtr cd my-feature # Requires shell integration (see below)
105105
cd "$(git gtr go my-feature)" # Alternative without shell integration
106106

107107
# List all worktrees
@@ -214,15 +214,29 @@ cd "$(git gtr go 1)" # Navigate to main repo
214214
**Tip:** For easier navigation, use `git gtr init` to enable `gtr cd`:
215215

216216
```bash
217-
# Add to ~/.bashrc or ~/.zshrc (one-time setup)
218-
eval "$(git gtr init bash)"
217+
# Bash (add to ~/.bashrc)
218+
_gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.bash"
219+
[[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" || true
220+
source "$_gtr_init" 2>/dev/null || true; unset _gtr_init
221+
222+
# Zsh (add to ~/.zshrc)
223+
_gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh"
224+
[[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" || true
225+
source "$_gtr_init" 2>/dev/null || true; unset _gtr_init
226+
227+
# Fish (add to ~/.config/fish/config.fish)
228+
set -l _gtr_init (test -n "$XDG_CACHE_HOME" && echo $XDG_CACHE_HOME || echo $HOME/.cache)/gtr/init-gtr.fish
229+
test -f "$_gtr_init"; or git gtr init fish >/dev/null 2>&1
230+
source "$_gtr_init" 2>/dev/null
219231

220232
# Then navigate with:
221233
gtr cd # Interactive worktree picker (requires fzf)
222234
gtr cd my-feature
223235
gtr cd 1
224236
```
225237

238+
The cache generates on first run and refreshes the next time `git gtr init <shell>` runs. To force-regenerate: `rm -rf ~/.cache/gtr`
239+
226240
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.
227241

228242
> **Note:** If `gtr` conflicts with another command (e.g., GNU `tr` from coreutils), use `--as` to pick a different name:

bin/git-gtr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ main() {
110110
local _shell_name
111111
_shell_name="$(basename "${SHELL:-bash}")"
112112
log_error "'cd' requires shell integration (subprocesses cannot change your shell's directory)"
113-
log_info "Set up with: eval \"\$(git gtr init $_shell_name)\""
114-
log_info "Then use: gtr cd [<branch>]"
113+
log_info "Run 'git gtr help init' for setup instructions"
114+
log_info "Then use: gtr cd [<branch>]"
115115
exit 1
116116
;;
117117
*)

lib/commands/doctor.sh

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,10 @@ cmd_doctor() {
128128
fish) _rc_file="$HOME/.config/fish/config.fish" ;;
129129
*) _rc_file="" ;;
130130
esac
131-
if [ -n "$_rc_file" ] && [ -f "$_rc_file" ] && grep -q 'git gtr init' "$_rc_file" 2>/dev/null; then
131+
if [ -n "$_rc_file" ] && [ -f "$_rc_file" ] && grep -qE 'git gtr init|gtr/init-' "$_rc_file" 2>/dev/null; then
132132
echo "[OK] Shell integration: loaded (gtr cd available)"
133133
elif [ -n "$_rc_file" ]; then
134-
local _init_hint
135-
if [ "$_shell_name" = "fish" ]; then
136-
_init_hint="git gtr init fish | source"
137-
else
138-
_init_hint="eval \"\$(git gtr init $_shell_name)\""
139-
fi
140-
echo "[i] Shell integration: $_init_hint in ${_rc_file##*/} for gtr cd"
134+
echo "[i] Shell integration: run 'git gtr help init' for setup instructions"
141135
fi
142136

143137
echo ""

lib/commands/help.sh

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ Usage: git gtr go <branch>
102102
103103
Prints the absolute path to the specified worktree. Useful for navigation
104104
with cd or for scripting. For direct cd support, use shell integration:
105-
eval "$(git gtr init bash)" # then: gtr cd <branch>
105+
git gtr help init # see setup instructions
106+
gtr cd <branch> # then navigate directly
106107
107108
Special:
108109
Use '1' for the main repo root: git gtr go 1
@@ -345,21 +346,32 @@ Usage: git gtr init <shell> [--as <name>]
345346
Generates shell functions for enhanced features like 'gtr cd <branch>'
346347
which changes directory to a worktree. Add to your shell configuration.
347348
349+
Output is cached to ~/.cache/gtr/ for fast shell startup (~1ms vs ~60ms).
350+
The cache refreshes the next time 'git gtr init <shell>' runs (checks version).
351+
With the recommended setup below, it regenerates when the cache file is missing.
352+
To force-regenerate: rm -rf ~/.cache/gtr
353+
348354
Supported shells: bash, zsh, fish
349355
350356
Options:
351357
--as <name> Set custom function name (default: gtr)
352358
Useful if 'gtr' conflicts with another command (e.g., GNU tr)
353359
354-
Setup:
360+
Setup (sources cached output directly for fast startup):
355361
# Bash (add to ~/.bashrc)
356-
eval "$(git gtr init bash)"
362+
_gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.bash"
363+
[[ -f "$_gtr_init" ]] || eval "$(git gtr init bash)" || true
364+
source "$_gtr_init" 2>/dev/null || true; unset _gtr_init
357365
358366
# Zsh (add to ~/.zshrc)
359-
eval "$(git gtr init zsh)"
367+
_gtr_init="${XDG_CACHE_HOME:-$HOME/.cache}/gtr/init-gtr.zsh"
368+
[[ -f "$_gtr_init" ]] || eval "$(git gtr init zsh)" || true
369+
source "$_gtr_init" 2>/dev/null || true; unset _gtr_init
360370
361371
# Fish (add to ~/.config/fish/config.fish)
362-
git gtr init fish | source
372+
set -l _gtr_init (test -n "$XDG_CACHE_HOME" && echo $XDG_CACHE_HOME || echo $HOME/.cache)/gtr/init-gtr.fish
373+
test -f "$_gtr_init"; or git gtr init fish >/dev/null 2>&1
374+
source "$_gtr_init" 2>/dev/null
363375
364376
# Custom function name (avoids conflict with coreutils gtr)
365377
eval "$(git gtr init zsh --as gwtr)"
@@ -558,7 +570,8 @@ SETUP & MAINTENANCE:
558570
init <shell> [--as <name>]
559571
Generate shell integration for cd support (bash, zsh, fish)
560572
--as <name>: custom function name (default: gtr)
561-
Usage: eval "$(git gtr init bash)"
573+
Output is cached for fast startup (refreshes when 'git gtr init' runs)
574+
See git gtr help init for recommended setup
562575
With fzf: 'gtr cd' opens a command palette (preview, editor, AI, delete)
563576
564577
version

lib/commands/init.sh

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,49 @@ cmd_init() {
5050
return 1
5151
fi
5252

53+
# Resolve generator function
54+
local generator
5355
case "$shell" in
54-
bash)
55-
_init_bash | sed "s/__FUNC__/$func_name/g"
56-
;;
57-
zsh)
58-
_init_zsh | sed "s/__FUNC__/$func_name/g"
59-
;;
60-
fish)
61-
_init_fish | sed "s/__FUNC__/$func_name/g"
62-
;;
63-
"")
64-
show_command_help
65-
;;
56+
bash) generator="_init_bash" ;;
57+
zsh) generator="_init_zsh" ;;
58+
fish) generator="_init_fish" ;;
59+
"") show_command_help; return 0 ;;
6660
*)
6761
log_error "Unknown shell: $shell"
6862
log_error "Supported shells: bash, zsh, fish"
6963
log_info "Run 'git gtr init --help' for usage"
7064
return 1
7165
;;
7266
esac
67+
68+
# Generate output (cached to ~/.cache/gtr/, auto-invalidates on version change)
69+
local cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/gtr"
70+
local cache_file="$cache_dir/init-${func_name}.${shell}"
71+
local cache_stamp="# gtr-cache: version=${GTR_VERSION:-unknown} func=$func_name shell=$shell"
72+
73+
# Return cached output if version matches
74+
if [ -f "$cache_file" ]; then
75+
local first_line
76+
first_line="$(head -1 "$cache_file")"
77+
if [ "$first_line" = "$cache_stamp" ]; then
78+
tail -n +2 "$cache_file"
79+
return 0
80+
fi
81+
fi
82+
83+
# Generate, output, and cache (output first so set -e cache failures don't swallow it)
84+
local output
85+
output="$("$generator" | sed "s/__FUNC__/$func_name/g")"
86+
printf '%s\n' "$output"
87+
if mkdir -p "$cache_dir" 2>/dev/null; then
88+
printf '%s\n%s\n' "$cache_stamp" "$output" > "$cache_file" 2>/dev/null || true
89+
fi
7390
}
7491

7592
_init_bash() {
7693
cat <<'BASH'
77-
# git-gtr shell integration
78-
# Add to ~/.bashrc:
79-
# eval "$(git gtr init bash)"
94+
# git-gtr shell integration (cached to ~/.cache/gtr/)
95+
# Setup: see git gtr help init
8096
8197
__FUNC__() {
8298
if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then
@@ -196,9 +212,8 @@ BASH
196212

197213
_init_zsh() {
198214
cat <<'ZSH'
199-
# git-gtr shell integration
200-
# Add to ~/.zshrc:
201-
# eval "$(git gtr init zsh)"
215+
# git-gtr shell integration (cached to ~/.cache/gtr/)
216+
# Setup: see git gtr help init
202217
203218
__FUNC__() {
204219
emulate -L zsh

tests/init.bats

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ load test_helper
55

66
setup() {
77
source "$PROJECT_ROOT/lib/commands/init.sh"
8+
# Isolate cache to temp dir so tests don't pollute ~/.cache or each other
9+
export XDG_CACHE_HOME="$BATS_TMPDIR/gtr-init-cache-$$"
10+
export GTR_VERSION="test"
11+
}
12+
13+
teardown() {
14+
rm -rf "$BATS_TMPDIR/gtr-init-cache-$$"
815
}
916

1017
# ── Default function name ────────────────────────────────────────────────────
@@ -496,3 +503,53 @@ setup() {
496503
[[ "$output" == *"command git gtr"* ]]
497504
[[ "$output" == *"git gtr list --porcelain"* ]]
498505
}
506+
507+
# ── caching (default behavior) ──────────────────────────────────────────────
508+
509+
@test "init creates cache file and returns output" {
510+
GTR_VERSION="9.9.9" run cmd_init zsh
511+
[ "$status" -eq 0 ]
512+
[[ "$output" == *"gtr()"* ]]
513+
[ -f "$XDG_CACHE_HOME/gtr/init-gtr.zsh" ]
514+
}
515+
516+
@test "init returns cached output on second call" {
517+
# First call: generates and caches
518+
GTR_VERSION="9.9.9" run cmd_init bash
519+
[ "$status" -eq 0 ]
520+
local first_output="$output"
521+
# Second call: reads from cache
522+
GTR_VERSION="9.9.9" run cmd_init bash
523+
[ "$status" -eq 0 ]
524+
[ "$output" = "$first_output" ]
525+
}
526+
527+
@test "cache invalidates when version changes" {
528+
# Generate with version 1.0.0
529+
GTR_VERSION="1.0.0" run cmd_init zsh
530+
[ "$status" -eq 0 ]
531+
# Check cache stamp
532+
local stamp
533+
stamp="$(head -1 "$XDG_CACHE_HOME/gtr/init-gtr.zsh")"
534+
[[ "$stamp" == *"version=1.0.0"* ]]
535+
# Regenerate with version 2.0.0
536+
GTR_VERSION="2.0.0" run cmd_init zsh
537+
[ "$status" -eq 0 ]
538+
stamp="$(head -1 "$XDG_CACHE_HOME/gtr/init-gtr.zsh")"
539+
[[ "$stamp" == *"version=2.0.0"* ]]
540+
}
541+
542+
@test "cache uses --as func name in cache key" {
543+
GTR_VERSION="9.9.9" run cmd_init bash --as myfn
544+
[ "$status" -eq 0 ]
545+
[[ "$output" == *"myfn()"* ]]
546+
[ -f "$XDG_CACHE_HOME/gtr/init-myfn.bash" ]
547+
}
548+
549+
@test "cache works for all shells" {
550+
for sh in bash zsh fish; do
551+
GTR_VERSION="9.9.9" run cmd_init "$sh"
552+
[ "$status" -eq 0 ]
553+
[ -f "$XDG_CACHE_HOME/gtr/init-gtr.${sh}" ]
554+
done
555+
}

0 commit comments

Comments
 (0)