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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <branch>` navigation. Users add `eval "$(git gtr init bash)"` to their shell rc file.
**`init` command**: Outputs shell functions for `gtr cd <branch>` 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.

Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -214,15 +214,27 @@ 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)
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:
Expand Down
2 changes: 1 addition & 1 deletion bin/git-gtr
Original file line number Diff line number Diff line change
Expand Up @@ -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 [<branch>]"
exit 1
;;
Expand Down
18 changes: 14 additions & 4 deletions lib/commands/help.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Usage: git gtr go <branch>
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 <branch>
(output is cached automatically for fast startup)

Special:
Use '1' for the main repo root: git gtr go 1
Expand Down Expand Up @@ -345,18 +346,26 @@ Usage: git gtr init <shell> [--as <name>]
Generates shell functions for enhanced features like 'gtr cd <branch>'
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 <name> 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
Expand Down Expand Up @@ -558,7 +567,8 @@ SETUP & MAINTENANCE:
init <shell> [--as <name>]
Generate shell integration for cd support (bash, zsh, fish)
--as <name>: 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
Expand Down
46 changes: 32 additions & 14 deletions lib/commands/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,50 @@ 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"
log_info "Run 'git gtr init --help' for usage"
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
Expand Down Expand Up @@ -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
Expand Down
67 changes: 67 additions & 0 deletions tests/init.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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 ────────────────────────────────────────────────────
Expand Down Expand Up @@ -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"
}