Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ git gtr ai my-feature # Start claude
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)"
cd "$(git gtr go my-feature)" # Alternative without shell integration

Expand Down Expand Up @@ -217,10 +218,14 @@ cd "$(git gtr go 1)" # Navigate to main repo
eval "$(git gtr init bash)"

# Then navigate with:
gtr cd # Interactive worktree picker (requires fzf)
gtr cd my-feature
gtr cd 1
gtr cd # interactive picker (requires fzf)
```
Comment thread
helizaga marked this conversation as resolved.

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:
>
> ```bash
Expand Down
7 changes: 7 additions & 0 deletions lib/commands/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ cmd_doctor() {
fi
fi

# Check fzf (optional, for interactive picker)
if command -v fzf >/dev/null 2>&1; then
echo "[OK] fzf: $(fzf --version 2>/dev/null | awk '{print $1}') (interactive picker available)"
else
echo "[i] fzf: not found (install for interactive picker: gtr cd)"
fi

echo ""
if [ "$issues" -eq 0 ]; then
echo "Everything looks good!"
Expand Down
12 changes: 12 additions & 0 deletions lib/commands/help.sh
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,17 @@ Setup:
After setup:
gtr cd my-feature # cd to worktree
gtr cd 1 # cd to main repo
gtr cd # interactive picker (requires fzf)
gtr <command> # same as git gtr <command>

Command palette (gtr cd with no arguments, requires fzf):
enter cd into selected worktree
ctrl-e open in editor
ctrl-a start AI tool
ctrl-d delete worktree (with confirmation)
ctrl-y copy files to worktree
ctrl-r refresh list
esc cancel
EOF
}

Expand Down Expand Up @@ -549,6 +559,7 @@ SETUP & MAINTENANCE:
Generate shell integration for cd support (bash, zsh, fish)
--as <name>: custom function name (default: gtr)
Usage: eval "$(git gtr init bash)"
With fzf: 'gtr cd' opens a command palette (preview, editor, AI, delete)

version
Show version
Expand All @@ -572,6 +583,7 @@ WORKFLOW EXAMPLES:
git gtr run feature/user-auth npm run dev # Start dev server

# Navigate to worktree directory
gtr cd # Interactive picker (requires fzf)
gtr cd feature/user-auth # With shell integration (git gtr init)
cd "$(git gtr go feature/user-auth)" # Without shell integration

Expand Down
75 changes: 71 additions & 4 deletions lib/commands/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,29 @@ __FUNC__() {
if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then
shift
local dir
dir="$(command git gtr go "$@")" && cd "$dir" && {
if [ "$#" -eq 0 ] && command -v fzf >/dev/null 2>&1; then
local _gtr_selection
_gtr_selection="$(command git gtr list --porcelain | fzf \
--delimiter=$'\t' \
--with-nth=2 \
--ansi \
--layout=reverse \
--border \
--prompt='Worktree> ' \
--header='enter:cd │ ctrl-e:editor │ ctrl-a:ai │ ctrl-d:delete │ ctrl-y:copy │ ctrl-r:refresh' \
--preview='git -C {1} log --oneline --graph --color=always -15 2>/dev/null; echo "---"; git -C {1} status --short 2>/dev/null' \
--preview-window=right:50% \
--bind='ctrl-e:execute(git gtr editor {2})' \
--bind='ctrl-a:execute(git gtr ai {2})' \
--bind='ctrl-d:execute(git gtr rm {2})+reload(git gtr list --porcelain)' \
--bind='ctrl-y:execute(git gtr copy {2})' \
--bind='ctrl-r:reload(git gtr list --porcelain)')" || return 0
[ -z "$_gtr_selection" ] && return 0
dir="$(printf '%s' "$_gtr_selection" | cut -f1)"
else
dir="$(command git gtr go "$@")" || return $?
fi
cd "$dir" && {
local _gtr_hooks _gtr_hook _gtr_seen _gtr_config_file
_gtr_hooks=""
_gtr_seen=""
Expand Down Expand Up @@ -155,7 +177,29 @@ __FUNC__() {
if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then
shift
local dir
dir="$(command git gtr go "$@")" && cd "$dir" && {
if [ "$#" -eq 0 ] && command -v fzf >/dev/null 2>&1; then
local _gtr_selection
_gtr_selection="$(command git gtr list --porcelain | fzf \
--delimiter=$'\t' \
--with-nth=2 \
--ansi \
--layout=reverse \
--border \
--prompt='Worktree> ' \
--header='enter:cd │ ctrl-e:editor │ ctrl-a:ai │ ctrl-d:delete │ ctrl-y:copy │ ctrl-r:refresh' \
--preview='git -C {1} log --oneline --graph --color=always -15 2>/dev/null; echo "---"; git -C {1} status --short 2>/dev/null' \
--preview-window=right:50% \
--bind='ctrl-e:execute(git gtr editor {2})' \
--bind='ctrl-a:execute(git gtr ai {2})' \
--bind='ctrl-d:execute(git gtr rm {2})+reload(git gtr list --porcelain)' \
--bind='ctrl-y:execute(git gtr copy {2})' \
--bind='ctrl-r:reload(git gtr list --porcelain)')" || return 0
[ -z "$_gtr_selection" ] && return 0
dir="$(printf '%s' "$_gtr_selection" | cut -f1)"
else
dir="$(command git gtr go "$@")" || return $?
fi
cd "$dir" && {
local _gtr_hooks _gtr_hook _gtr_seen _gtr_config_file
_gtr_hooks=""
_gtr_seen=""
Expand Down Expand Up @@ -232,8 +276,31 @@ _init_fish() {

function __FUNC__
if test (count $argv) -gt 0; and test "$argv[1]" = "cd"
set -l dir (command git gtr go $argv[2..])
and cd $dir
set -l dir
if test (count $argv) -eq 1; and type -q fzf
set -l _gtr_selection (command git gtr list --porcelain | fzf \
--delimiter=\t \
--with-nth=2 \
--ansi \
--layout=reverse \
--border \
--prompt='Worktree> ' \
--header='enter:cd │ ctrl-e:editor │ ctrl-a:ai │ ctrl-d:delete │ ctrl-y:copy │ ctrl-r:refresh' \
--preview='git -C {1} log --oneline --graph --color=always -15 2>/dev/null; echo "---"; git -C {1} status --short 2>/dev/null' \
--preview-window=right:50% \
--bind='ctrl-e:execute(git gtr editor {2})' \
--bind='ctrl-a:execute(git gtr ai {2})' \
--bind='ctrl-d:execute(git gtr rm {2})+reload(git gtr list --porcelain)' \
--bind='ctrl-y:execute(git gtr copy {2})' \
--bind='ctrl-r:reload(git gtr list --porcelain)')
or return 0
test -z "$_gtr_selection"; and return 0
set dir (string split \t -- "$_gtr_selection")[1]
else
set dir (command git gtr go $argv[2..])
or return $status
end
cd $dir
and begin
set -l _gtr_hooks
set -l _gtr_seen
Expand Down