Summary
On fish shell, gtr cd (with no arguments, using the fzf interactive picker) shows the picker correctly, but pressing Enter exits silently without changing directory. The same flow works on bash/zsh.
Environment
- gtr version: v2.4.0
- fish: 3.7.0 (also reproduced on newer)
- fzf: installed and working
Root cause
In lib/commands/init.sh, inside _init_fish() (the gtr cd fzf path), _gtr_key and _gtr_line are declared with set -l inside an if/else block:
if test (count $_gtr_selection) -eq 1
set -l _gtr_key ""
set -l _gtr_line "$_gtr_selection[1]"
else
set -l _gtr_key "$_gtr_selection[1]"
set -l _gtr_line "$_gtr_selection[2]"
end
# ...
test -z "$_gtr_line"; and return 0
Unlike bash/zsh local, fish's set -l binds the variable to the innermost enclosing block (the if / else body), not the enclosing function. Once end is reached, _gtr_key and _gtr_line go out of scope and read as empty. The subsequent test -z "$_gtr_line"; and return 0 therefore always fires, so the function returns before __FUNC___run_post_cd_hooks "$dir" is ever called.
This is the same class of issue as fish-shell/fish-shell#4820. Ironically, the comment right above the if/else already acknowledges fish's quirky handling of empty command-substitution lines — but the fix below it reintroduces a different fish-specific scoping problem.
Reproduction (minimal)
#!/usr/bin/env fish
set _gtr_selection "/tmp/wt\tfeature-x" # Enter pressed → count == 1
if test (count $_gtr_selection) -eq 1
set -l _gtr_key ""
set -l _gtr_line "$_gtr_selection[1]"
else
set -l _gtr_key "$_gtr_selection[1]"
set -l _gtr_line "$_gtr_selection[2]"
end
echo "key='$_gtr_key' line='$_gtr_line'"
# → key='' line='' (both empty, scope already closed)
End-to-end repro with gtr itself:
git gtr init fish | source (or via the cache file from the README snippet)
- In a repo with at least one worktree, run
gtr cd
- Pick any entry, press Enter → fzf closes, no
cd happens, shell stays in the original directory
Workaround: gtr cd <branch> works because it takes the non-fzf code path (git gtr go $argv[2..]), which doesn't depend on these variables.
Suggested fix
Declare the variables in the function scope before the if/else, and drop -l inside the branches so they assign to the outer bindings:
set -l _gtr_key
set -l _gtr_line
if test (count $_gtr_selection) -eq 1
set _gtr_key ""
set _gtr_line "$_gtr_selection[1]"
else
set _gtr_key "$_gtr_selection[1]"
set _gtr_line "$_gtr_selection[2]"
end
Happy to send a PR if the approach looks good. Worth also adding a basic fish smoke test — the current test suite is bats-only, so fish-side regressions (like this one) slip through.
Summary
On fish shell,
gtr cd(with no arguments, using the fzf interactive picker) shows the picker correctly, but pressing Enter exits silently without changing directory. The same flow works on bash/zsh.Environment
Root cause
In
lib/commands/init.sh, inside_init_fish()(thegtr cdfzf path),_gtr_keyand_gtr_lineare declared withset -linside anif/elseblock:Unlike bash/zsh
local, fish'sset -lbinds the variable to the innermost enclosing block (theif/elsebody), not the enclosing function. Onceendis reached,_gtr_keyand_gtr_linego out of scope and read as empty. The subsequenttest -z "$_gtr_line"; and return 0therefore always fires, so the function returns before__FUNC___run_post_cd_hooks "$dir"is ever called.This is the same class of issue as fish-shell/fish-shell#4820. Ironically, the comment right above the
if/elsealready acknowledges fish's quirky handling of empty command-substitution lines — but the fix below it reintroduces a different fish-specific scoping problem.Reproduction (minimal)
End-to-end repro with gtr itself:
git gtr init fish | source(or via the cache file from the README snippet)gtr cdcdhappens, shell stays in the original directoryWorkaround:
gtr cd <branch>works because it takes the non-fzf code path (git gtr go $argv[2..]), which doesn't depend on these variables.Suggested fix
Declare the variables in the function scope before the
if/else, and drop-linside the branches so they assign to the outer bindings:Happy to send a PR if the approach looks good. Worth also adding a basic fish smoke test — the current test suite is bats-only, so fish-side regressions (like this one) slip through.