From 7b225b98f5a24114d1fdef0f24fad1c7609ca6af Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Mon, 9 Mar 2026 11:14:36 -0700 Subject: [PATCH] Add shell wrapper support for new --cd --- README.md | 4 +- docs/configuration.md | 6 +- lib/commands/help.sh | 9 +- lib/commands/init.sh | 311 ++++++++++++++++++++++++++++++------------ tests/cmd_help.bats | 6 + tests/init.bats | 71 ++++++++++ 6 files changed, 312 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index e3b0ec6..2d8b57e 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ git gtr ai my-feature # Start claude git gtr run my-feature npm test # Run tests # Navigate to worktree +gtr new my-feature --cd # Create and cd (requires shell integration) gtr cd # Interactive picker (requires fzf + shell integration) gtr cd my-feature # Requires shell integration (see below) cd "$(git gtr go my-feature)" # Alternative without shell integration @@ -230,6 +231,7 @@ test -f "$_gtr_init"; or git gtr init fish >/dev/null 2>&1 source "$_gtr_init" 2>/dev/null # Then navigate with: +gtr new my-feature --cd # Create and land in the new worktree gtr cd # Interactive worktree picker (requires fzf) gtr cd my-feature gtr cd 1 @@ -360,7 +362,7 @@ git gtr config add gtr.copy.include "**/.env.example" # Run setup after creating worktrees git gtr config add gtr.hook.postCreate "npm install" -# Re-source environment after gtr cd (runs in current shell) +# Re-source environment after gtr cd or gtr new --cd (runs in current shell) git gtr config add gtr.hook.postCd "source ./vars.sh" # Disable color output (or use "always" to force it) diff --git a/docs/configuration.md b/docs/configuration.md index 69aba6c..feb59d6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -295,7 +295,7 @@ git gtr config add gtr.hook.preRemove "npm run cleanup" # Post-remove hooks git gtr config add gtr.hook.postRemove "echo 'Cleaned up!'" -# Post-cd hooks (run after gtr cd, in current shell) +# Post-cd hooks (run after gtr cd or gtr new --cd, in current shell) git gtr config add gtr.hook.postCd "source ./vars.sh" ``` @@ -306,11 +306,11 @@ git gtr config add gtr.hook.postCd "source ./vars.sh" | `postCreate` | After worktree creation | Setup, install dependencies | | `preRemove` | Before worktree deletion | Cleanup requiring directory access | | `postRemove` | After worktree deletion | Notifications, logging | -| `postCd` | After `gtr cd` changes directory | Re-source environment, update shell context | +| `postCd` | After `gtr cd` or `gtr new --cd` changes directory | Re-source environment, update shell context | > **Note:** Pre-remove hooks abort removal on failure. Use `--force` to skip failed hooks. > -> **Note:** `postCd` hooks run in the **current shell** (not a subshell) so they can modify environment variables. They only run via `gtr cd` (shell integration), not `git gtr go`. Failures warn but don't undo the `cd`. +> **Note:** `postCd` hooks run in the **current shell** (not a subshell) so they can modify environment variables. They only run via shell integration (`gtr cd`, `gtr new --cd`), not raw `git gtr` commands or `git gtr go`. Failures warn but don't undo the directory change. **Environment variables available in hooks:** diff --git a/lib/commands/help.sh b/lib/commands/help.sh index db2f340..75e881a 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -344,7 +344,8 @@ git gtr init - Generate shell integration 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. +and 'gtr new --cd', which can change the current shell directory. +Add to your shell configuration. Output is cached to ~/.cache/gtr/ for fast shell startup (~1ms vs ~60ms). The cache refreshes the next time 'git gtr init ' runs (checks version). @@ -377,6 +378,7 @@ Setup (sources cached output directly for fast startup): eval "$(git gtr init zsh --as gwtr)" After setup: + gtr new my-feature --cd # create and cd into worktree gtr cd my-feature # cd to worktree gtr cd 1 # cd to main repo gtr cd # interactive picker (requires fzf) @@ -568,7 +570,7 @@ SETUP & MAINTENANCE: Usage: eval "$(git gtr completion zsh)" init [--as ] - Generate shell integration for cd support (bash, zsh, fish) + Generate shell integration for gtr cd and gtr new --cd (bash, zsh, fish) --as : custom function name (default: gtr) Output is cached for fast startup (refreshes when 'git gtr init' runs) See git gtr help init for recommended setup @@ -596,6 +598,7 @@ WORKFLOW EXAMPLES: git gtr run feature/user-auth npm run dev # Start dev server # Navigate to worktree directory + gtr new hotfix --cd # Create and cd into worktree (with shell integration) 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 @@ -646,7 +649,7 @@ CONFIGURATION OPTIONS: gtr.hook.postCreate Post-create hooks (multi-valued) gtr.hook.preRemove Pre-remove hooks (multi-valued, abort on failure) gtr.hook.postRemove Post-remove hooks (multi-valued) - gtr.hook.postCd Post-cd hooks (multi-valued, shell integration only) + gtr.hook.postCd Post-cd hooks (multi-valued, gtr cd / gtr new --cd only) gtr.ui.color Color output mode (auto, always, never; default: auto) ──────────────────────────────────────────────────────────────────────────────── diff --git a/lib/commands/init.sh b/lib/commands/init.sh index e35af99..4496280 100644 --- a/lib/commands/init.sh +++ b/lib/commands/init.sh @@ -94,6 +94,45 @@ _init_bash() { # git-gtr shell integration (cached to ~/.cache/gtr/) # Setup: see git gtr help init +__FUNC___run_post_cd_hooks() { + local dir="$1" + + cd "$dir" && { + local _gtr_hooks _gtr_hook _gtr_seen _gtr_config_file + _gtr_hooks="" + _gtr_seen="" + # Read from git config (local > global > system) + _gtr_hooks="$(git config --get-all gtr.hook.postCd 2>/dev/null)" || true + # Read from .gtrconfig if it exists + _gtr_config_file="$(git rev-parse --show-toplevel 2>/dev/null)/.gtrconfig" + if [ -f "$_gtr_config_file" ]; then + local _gtr_file_hooks + _gtr_file_hooks="$(git config -f "$_gtr_config_file" --get-all hooks.postCd 2>/dev/null)" || true + if [ -n "$_gtr_file_hooks" ]; then + if [ -n "$_gtr_hooks" ]; then + _gtr_hooks="$_gtr_hooks"$'\n'"$_gtr_file_hooks" + else + _gtr_hooks="$_gtr_file_hooks" + fi + fi + fi + if [ -n "$_gtr_hooks" ]; then + # Deduplicate while preserving order + _gtr_seen="" + export WORKTREE_PATH="$dir" + export REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" + export BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" + while IFS= read -r _gtr_hook; do + [ -z "$_gtr_hook" ] && continue + case "$_gtr_seen" in *"|$_gtr_hook|"*) continue ;; esac + _gtr_seen="$_gtr_seen|$_gtr_hook|" + eval "$_gtr_hook" || echo "__FUNC__: postCd hook failed: $_gtr_hook" >&2 + done <<< "$_gtr_hooks" + unset WORKTREE_PATH REPO_ROOT BRANCH + fi + } +} + __FUNC__() { if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then shift @@ -147,40 +186,43 @@ __FUNC__() { else dir="$(command git gtr go "$@")" || return $? fi - cd "$dir" && { - local _gtr_hooks _gtr_hook _gtr_seen _gtr_config_file - _gtr_hooks="" - _gtr_seen="" - # Read from git config (local > global > system) - _gtr_hooks="$(git config --get-all gtr.hook.postCd 2>/dev/null)" || true - # Read from .gtrconfig if it exists - _gtr_config_file="$(git rev-parse --show-toplevel 2>/dev/null)/.gtrconfig" - if [ -f "$_gtr_config_file" ]; then - local _gtr_file_hooks - _gtr_file_hooks="$(git config -f "$_gtr_config_file" --get-all hooks.postCd 2>/dev/null)" || true - if [ -n "$_gtr_file_hooks" ]; then - if [ -n "$_gtr_hooks" ]; then - _gtr_hooks="$_gtr_hooks"$'\n'"$_gtr_file_hooks" - else - _gtr_hooks="$_gtr_file_hooks" - fi - fi + __FUNC___run_post_cd_hooks "$dir" + elif [ "$#" -gt 0 ] && [ "$1" = "new" ]; then + local -a _gtr_original_args=("$@") _gtr_new_args=() + local _gtr_arg _gtr_new_cd=0 _gtr_before_paths _gtr_after_paths + local _gtr_path _gtr_new_dir="" _gtr_new_count=0 _gtr_status + shift + for _gtr_arg in "$@"; do + if [ "$_gtr_arg" = "--cd" ]; then + _gtr_new_cd=1 + else + _gtr_new_args+=("$_gtr_arg") fi - if [ -n "$_gtr_hooks" ]; then - # Deduplicate while preserving order - _gtr_seen="" - export WORKTREE_PATH="$dir" - export REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" - export BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" - while IFS= read -r _gtr_hook; do - [ -z "$_gtr_hook" ] && continue - case "$_gtr_seen" in *"|$_gtr_hook|"*) continue ;; esac - _gtr_seen="$_gtr_seen|$_gtr_hook|" - eval "$_gtr_hook" || echo "__FUNC__: postCd hook failed: $_gtr_hook" >&2 - done <<< "$_gtr_hooks" - unset WORKTREE_PATH REPO_ROOT BRANCH + done + if [ "$_gtr_new_cd" -eq 1 ]; then + _gtr_before_paths="$(command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p')" + command git gtr new "${_gtr_new_args[@]}" + _gtr_status=$? + [ "$_gtr_status" -ne 0 ] && return "$_gtr_status" + _gtr_after_paths="$(command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p')" + while IFS= read -r _gtr_path; do + [ -z "$_gtr_path" ] && continue + case $'\n'"$_gtr_before_paths"$'\n' in + *$'\n'"$_gtr_path"$'\n'*) ;; + *) + _gtr_new_dir="$_gtr_path" + _gtr_new_count=$((_gtr_new_count + 1)) + ;; + esac + done <<< "$_gtr_after_paths" + if [ "$_gtr_new_count" -eq 1 ]; then + __FUNC___run_post_cd_hooks "$_gtr_new_dir" + return $? fi - } + echo "__FUNC__: created worktree, but could not determine new directory for --cd" >&2 + return 0 + fi + command git gtr "${_gtr_original_args[@]}" else command git gtr "$@" fi @@ -199,6 +241,13 @@ ___FUNC___completion() { local worktrees worktrees="1 $(git gtr list --porcelain 2>/dev/null | cut -f2 | tr '\n' ' ')" COMPREPLY=($(compgen -W "$worktrees" -- "$cur")) + elif [ "${COMP_WORDS[1]}" = "new" ] && [[ "$cur" == -* ]]; then + if type _git_gtr &>/dev/null; then + COMP_WORDS=(git gtr "${COMP_WORDS[@]:1}") + (( COMP_CWORD += 1 )) + _git_gtr + fi + COMPREPLY+=($(compgen -W "--cd" -- "$cur")) elif type _git_gtr &>/dev/null; then # Delegate to git-gtr completions (adjust words to match expected format) COMP_WORDS=(git gtr "${COMP_WORDS[@]:1}") @@ -215,6 +264,46 @@ _init_zsh() { # git-gtr shell integration (cached to ~/.cache/gtr/) # Setup: see git gtr help init +__FUNC___run_post_cd_hooks() { + emulate -L zsh + local dir="$1" + + cd "$dir" && { + local _gtr_hooks _gtr_hook _gtr_seen _gtr_config_file + _gtr_hooks="" + _gtr_seen="" + # Read from git config (local > global > system) + _gtr_hooks="$(git config --get-all gtr.hook.postCd 2>/dev/null)" || true + # Read from .gtrconfig if it exists + _gtr_config_file="$(git rev-parse --show-toplevel 2>/dev/null)/.gtrconfig" + if [ -f "$_gtr_config_file" ]; then + local _gtr_file_hooks + _gtr_file_hooks="$(git config -f "$_gtr_config_file" --get-all hooks.postCd 2>/dev/null)" || true + if [ -n "$_gtr_file_hooks" ]; then + if [ -n "$_gtr_hooks" ]; then + _gtr_hooks="$_gtr_hooks"$'\n'"$_gtr_file_hooks" + else + _gtr_hooks="$_gtr_file_hooks" + fi + fi + fi + if [ -n "$_gtr_hooks" ]; then + # Deduplicate while preserving order + _gtr_seen="" + export WORKTREE_PATH="$dir" + export REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" + export BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" + while IFS= read -r _gtr_hook; do + [ -z "$_gtr_hook" ] && continue + case "$_gtr_seen" in *"|$_gtr_hook|"*) continue ;; esac + _gtr_seen="$_gtr_seen|$_gtr_hook|" + eval "$_gtr_hook" || echo "__FUNC__: postCd hook failed: $_gtr_hook" >&2 + done <<< "$_gtr_hooks" + unset WORKTREE_PATH REPO_ROOT BRANCH + fi + } +} + __FUNC__() { emulate -L zsh if [ "$#" -gt 0 ] && [ "$1" = "cd" ]; then @@ -269,40 +358,45 @@ __FUNC__() { else dir="$(command git gtr go "$@")" || return $? fi - cd "$dir" && { - local _gtr_hooks _gtr_hook _gtr_seen _gtr_config_file - _gtr_hooks="" - _gtr_seen="" - # Read from git config (local > global > system) - _gtr_hooks="$(git config --get-all gtr.hook.postCd 2>/dev/null)" || true - # Read from .gtrconfig if it exists - _gtr_config_file="$(git rev-parse --show-toplevel 2>/dev/null)/.gtrconfig" - if [ -f "$_gtr_config_file" ]; then - local _gtr_file_hooks - _gtr_file_hooks="$(git config -f "$_gtr_config_file" --get-all hooks.postCd 2>/dev/null)" || true - if [ -n "$_gtr_file_hooks" ]; then - if [ -n "$_gtr_hooks" ]; then - _gtr_hooks="$_gtr_hooks"$'\n'"$_gtr_file_hooks" - else - _gtr_hooks="$_gtr_file_hooks" - fi - fi + __FUNC___run_post_cd_hooks "$dir" + elif [ "$#" -gt 0 ] && [ "$1" = "new" ]; then + local -a _gtr_original_args _gtr_new_args + local _gtr_arg _gtr_new_cd=0 _gtr_before_paths _gtr_after_paths + local _gtr_path _gtr_new_dir="" _gtr_new_count=0 _gtr_status + _gtr_original_args=("$@") + _gtr_new_args=() + shift + for _gtr_arg in "$@"; do + if [ "$_gtr_arg" = "--cd" ]; then + _gtr_new_cd=1 + else + _gtr_new_args+=("$_gtr_arg") fi - if [ -n "$_gtr_hooks" ]; then - # Deduplicate while preserving order - _gtr_seen="" - export WORKTREE_PATH="$dir" - export REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" - export BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" - while IFS= read -r _gtr_hook; do - [ -z "$_gtr_hook" ] && continue - case "$_gtr_seen" in *"|$_gtr_hook|"*) continue ;; esac - _gtr_seen="$_gtr_seen|$_gtr_hook|" - eval "$_gtr_hook" || echo "__FUNC__: postCd hook failed: $_gtr_hook" >&2 - done <<< "$_gtr_hooks" - unset WORKTREE_PATH REPO_ROOT BRANCH + done + if [ "$_gtr_new_cd" -eq 1 ]; then + _gtr_before_paths="$(command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p')" + command git gtr new "${_gtr_new_args[@]}" + _gtr_status=$? + [ "$_gtr_status" -ne 0 ] && return "$_gtr_status" + _gtr_after_paths="$(command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p')" + while IFS= read -r _gtr_path; do + [ -z "$_gtr_path" ] && continue + case $'\n'"$_gtr_before_paths"$'\n' in + *$'\n'"$_gtr_path"$'\n'*) ;; + *) + _gtr_new_dir="$_gtr_path" + _gtr_new_count=$((_gtr_new_count + 1)) + ;; + esac + done <<< "$_gtr_after_paths" + if [ "$_gtr_new_count" -eq 1 ]; then + __FUNC___run_post_cd_hooks "$_gtr_new_dir" + return $? fi - } + echo "__FUNC__: created worktree, but could not determine new directory for --cd" >&2 + return 0 + fi + command git gtr "${_gtr_original_args[@]}" else command git gtr "$@" fi @@ -311,6 +405,7 @@ __FUNC__() { # Completion for __FUNC__ wrapper ___FUNC___completion() { local at_subcmd=0 + local current_word="${words[CURRENT]:-}" (( CURRENT == 2 )) && at_subcmd=1 if [[ "${words[2]}" == "cd" ]] && (( CURRENT >= 3 )); then @@ -328,6 +423,10 @@ ___FUNC___completion() { _git-gtr fi + if [[ "${words[2]}" == "new" && "$current_word" == -* ]]; then + compadd -- --cd + fi + # When completing the subcommand position, also offer "cd" if (( at_subcmd )); then local -a extra=('cd:Change directory to worktree') @@ -344,6 +443,38 @@ _init_fish() { # Add to ~/.config/fish/config.fish: # git gtr init fish | source +function __FUNC___run_post_cd_hooks + set -l dir "$argv[1]" + cd $dir + and begin + set -l _gtr_hooks + set -l _gtr_seen + # Read from git config (local > global > system) + set -l _gtr_git_hooks (git config --get-all gtr.hook.postCd 2>/dev/null) + # Read from .gtrconfig if it exists + set -l _gtr_config_file (git rev-parse --show-toplevel 2>/dev/null)"/.gtrconfig" + set -l _gtr_file_hooks + if test -f "$_gtr_config_file" + set _gtr_file_hooks (git config -f "$_gtr_config_file" --get-all hooks.postCd 2>/dev/null) + end + # Merge and deduplicate + set _gtr_hooks $_gtr_git_hooks $_gtr_file_hooks + if test (count $_gtr_hooks) -gt 0 + set -lx WORKTREE_PATH "$dir" + set -lx REPO_ROOT (git rev-parse --show-toplevel 2>/dev/null) + set -lx BRANCH (git rev-parse --abbrev-ref HEAD 2>/dev/null) + for _gtr_hook in $_gtr_hooks + if test -n "$_gtr_hook" + if not contains -- "$_gtr_hook" $_gtr_seen + set -a _gtr_seen "$_gtr_hook" + eval "$_gtr_hook"; or echo "__FUNC__: postCd hook failed: $_gtr_hook" >&2 + end + end + end + end + end +end + function __FUNC__ if test (count $argv) -gt 0; and test "$argv[1]" = "cd" set -l dir @@ -403,34 +534,37 @@ function __FUNC__ set dir (command git gtr go $argv[2..]) or return $status end - cd $dir - and begin - set -l _gtr_hooks - set -l _gtr_seen - # Read from git config (local > global > system) - set -l _gtr_git_hooks (git config --get-all gtr.hook.postCd 2>/dev/null) - # Read from .gtrconfig if it exists - set -l _gtr_config_file (git rev-parse --show-toplevel 2>/dev/null)"/.gtrconfig" - set -l _gtr_file_hooks - if test -f "$_gtr_config_file" - set _gtr_file_hooks (git config -f "$_gtr_config_file" --get-all hooks.postCd 2>/dev/null) + __FUNC___run_post_cd_hooks "$dir" + else if test (count $argv) -gt 0; and test "$argv[1]" = "new" + set -l _gtr_new_cd 0 + set -l _gtr_new_args + for _gtr_arg in $argv[2..-1] + if test "$_gtr_arg" = "--cd" + set _gtr_new_cd 1 + else + set -a _gtr_new_args "$_gtr_arg" end - # Merge and deduplicate - set _gtr_hooks $_gtr_git_hooks $_gtr_file_hooks - if test (count $_gtr_hooks) -gt 0 - set -lx WORKTREE_PATH "$dir" - set -lx REPO_ROOT (git rev-parse --show-toplevel 2>/dev/null) - set -lx BRANCH (git rev-parse --abbrev-ref HEAD 2>/dev/null) - for _gtr_hook in $_gtr_hooks - if test -n "$_gtr_hook" - if not contains -- "$_gtr_hook" $_gtr_seen - set -a _gtr_seen "$_gtr_hook" - eval "$_gtr_hook"; or echo "__FUNC__: postCd hook failed: $_gtr_hook" >&2 - end - end + end + if test "$_gtr_new_cd" = "1" + set -l _gtr_before_paths (command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p') + command git gtr new $_gtr_new_args + set -l _gtr_status $status + test $_gtr_status -ne 0; and return $_gtr_status + set -l _gtr_after_paths (command git worktree list --porcelain 2>/dev/null | sed -n 's/^worktree //p') + set -l _gtr_new_paths + for _gtr_path in $_gtr_after_paths + if not contains -- "$_gtr_path" $_gtr_before_paths + set -a _gtr_new_paths "$_gtr_path" end end + if test (count $_gtr_new_paths) -eq 1 + __FUNC___run_post_cd_hooks "$_gtr_new_paths[1]" + return $status + end + echo "__FUNC__: created worktree, but could not determine new directory for --cd" >&2 + return 0 end + command git gtr $argv else command git gtr $argv end @@ -478,5 +612,6 @@ complete -f -c __FUNC__ -n '___FUNC___needs_subcommand' -a help -d 'Show help' # Worktree name completions for cd complete -f -c __FUNC__ -n '___FUNC___using_subcommand cd' -a '(echo 1; git gtr list --porcelain 2>/dev/null | cut -f2)' +complete -f -c __FUNC__ -n '___FUNC___using_subcommand new' -l cd -d 'Create and cd into the new worktree' FISH } diff --git a/tests/cmd_help.bats b/tests/cmd_help.bats index 73a6fb8..274f015 100644 --- a/tests/cmd_help.bats +++ b/tests/cmd_help.bats @@ -90,6 +90,12 @@ teardown() { [[ "$output" == *"--dry-run"* ]] } +@test "cmd_help init mentions gtr new --cd" { + run cmd_help init + [ "$status" -eq 0 ] + [[ "$output" == *"gtr new my-feature --cd"* ]] +} + # ── Alias mapping ──────────────────────────────────────────────────────────── @test "cmd_help ls maps to list help" { diff --git a/tests/init.bats b/tests/init.bats index e34fc8f..2f50c9d 100644 --- a/tests/init.bats +++ b/tests/init.bats @@ -153,6 +153,77 @@ teardown() { [[ "$output" == *"git gtr list --porcelain"* ]] } +# ── new --cd wrapper support ──────────────────────────────────────────────── + +@test "bash output intercepts new --cd and strips flag before delegating" { + run cmd_init bash + [ "$status" -eq 0 ] + [[ "$output" == *'[ "$1" = "new" ]'* ]] + [[ "$output" == *'if [ "$_gtr_arg" = "--cd" ]'* ]] + [[ "$output" == *'command git gtr new "${_gtr_new_args[@]}"'* ]] + [[ "$output" == *'command git gtr "${_gtr_original_args[@]}"'* ]] +} + +@test "zsh output intercepts new --cd and strips flag before delegating" { + run cmd_init zsh + [ "$status" -eq 0 ] + [[ "$output" == *'[ "$1" = "new" ]'* ]] + [[ "$output" == *'if [ "$_gtr_arg" = "--cd" ]'* ]] + [[ "$output" == *'command git gtr new "${_gtr_new_args[@]}"'* ]] + [[ "$output" == *'command git gtr "${_gtr_original_args[@]}"'* ]] +} + +@test "fish output intercepts new --cd and strips flag before delegating" { + run cmd_init fish + [ "$status" -eq 0 ] + [[ "$output" == *'test "$argv[1]" = "new"'* ]] + [[ "$output" == *'test "$_gtr_arg" = "--cd"'* ]] + [[ "$output" == *'command git gtr new $_gtr_new_args'* ]] +} + +@test "bash output uses worktree diff to locate the new directory for --cd" { + run cmd_init bash + [ "$status" -eq 0 ] + [[ "$output" == *"git worktree list --porcelain"* ]] + [[ "$output" == *'run_post_cd_hooks "$_gtr_new_dir"'* ]] + [[ "$output" == *'could not determine new directory for --cd'* ]] +} + +@test "zsh output uses worktree diff to locate the new directory for --cd" { + run cmd_init zsh + [ "$status" -eq 0 ] + [[ "$output" == *"git worktree list --porcelain"* ]] + [[ "$output" == *'run_post_cd_hooks "$_gtr_new_dir"'* ]] + [[ "$output" == *'could not determine new directory for --cd'* ]] +} + +@test "fish output uses worktree diff to locate the new directory for --cd" { + run cmd_init fish + [ "$status" -eq 0 ] + [[ "$output" == *"git worktree list --porcelain"* ]] + [[ "$output" == *'run_post_cd_hooks "$_gtr_new_paths[1]"'* ]] + [[ "$output" == *'could not determine new directory for --cd'* ]] +} + +@test "bash wrapper completions include --cd for new" { + run cmd_init bash + [ "$status" -eq 0 ] + [[ "$output" == *'compgen -W "--cd"'* ]] +} + +@test "zsh wrapper completions include --cd for new" { + run cmd_init zsh + [ "$status" -eq 0 ] + [[ "$output" == *'compadd -- --cd'* ]] +} + +@test "fish wrapper completions include --cd for new" { + run cmd_init fish + [ "$status" -eq 0 ] + [[ "$output" == *"using_subcommand new"* ]] + [[ "$output" == *"-l cd -d 'Create and cd into the new worktree'"* ]] +} + # ── Error cases ────────────────────────────────────────────────────────────── @test "unknown shell fails" {