Skip to content

Commit a552612

Browse files
authored
feat: add --from-current flag and fix zsh completions (#25)
1 parent 0d5363e commit a552612

File tree

7 files changed

+111
-36
lines changed

7 files changed

+111
-36
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
88

99
### Added
1010

11+
- `--from-current` flag for `git gtr new` command to create worktrees from the current branch instead of the default branch (useful for creating parallel variant worktrees)
12+
- `run` command to execute commands in worktrees without navigation (e.g., `git gtr run <branch> npm test`)
13+
- Directory copying support via `gtr.copy.includeDirs` and `gtr.copy.excludeDirs` to copy entire directories (e.g., `node_modules`, `.venv`, `vendor`) when creating worktrees, avoiding dependency reinstallation
14+
- OpenCode AI adapter for AI tool integration
15+
1116
### Changed
1217

1318
### Deprecated
@@ -16,6 +21,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
1621

1722
### Fixed
1823

24+
- Zsh completion: Fixed word array normalization to correctly handle `gtr` and `git-gtr` direct invocations (not just `git gtr`)
25+
- Zsh completion: Fixed `new` command options to complete at any position, not just after the first argument
26+
- Zsh completion: Added `--editor` completion for `editor` command with list of available editors
27+
- Zsh completion: Added `--ai` completion for `ai` command with list of available AI tools
28+
- Zsh completion: Added `--porcelain` completion for `list`/`ls` commands
29+
- Zsh completion: Added `--global` completion for `config` command
30+
1931
### Security
2032

2133
## [1.0.0] - 2025-11-14

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ Test changes using this comprehensive checklist (from CONTRIBUTING.md):
7272
./bin/gtr new brand-new-feature
7373
# Expected: Creates new branch and worktree
7474

75+
# Test --from-current flag
76+
git checkout -b test-from-current-base
77+
./bin/gtr new variant-1 --from-current
78+
# Expected: Creates variant-1 from test-from-current-base (not main)
79+
7580
# Test --force and --name flags together
7681
./bin/gtr new test-feature --force --name backend
7782
# Expected: Creates folder "test-feature-backend" on same branch

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,13 @@ cat >> ~/.zshrc <<'EOF'
152152
# Enable completions
153153
fpath=(~/.zsh/completions $fpath)
154154
autoload -Uz compinit && compinit
155+
156+
# Load git-gtr completions (REQUIRED - must be sourced after compinit)
157+
source ~/.zsh/completions/_git-gtr
155158
EOF
156159

160+
# Clear completion cache and reload
161+
rm -f ~/.zcompdump*
157162
source ~/.zshrc
158163
```
159164

@@ -175,6 +180,7 @@ Create a new git worktree. Folder is named after the branch.
175180
```bash
176181
git gtr new my-feature # Creates folder: my-feature
177182
git gtr new hotfix --from v1.2.3 # Create from specific ref
183+
git gtr new variant-1 --from-current # Create from current branch
178184
git gtr new feature/auth # Creates folder: feature-auth
179185
git gtr new feature-auth --name backend --force # Same branch, custom name
180186
git gtr new my-feature --name descriptive-variant # Optional: custom name without --force
@@ -183,6 +189,7 @@ git gtr new my-feature --name descriptive-variant # Optional: custom name with
183189
**Options:**
184190

185191
- `--from <ref>`: Create from specific ref
192+
- `--from-current`: Create from current branch (useful for parallel variant work)
186193
- `--track <mode>`: Tracking mode (auto|remote|local|none)
187194
- `--no-copy`: Skip file copying
188195
- `--no-fetch`: Skip git fetch

bin/gtr

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ main() {
111111
cmd_create() {
112112
local branch_name=""
113113
local from_ref=""
114+
local from_current=0
114115
local track_mode="auto"
115116
local skip_copy=0
116117
local skip_fetch=0
@@ -125,6 +126,10 @@ cmd_create() {
125126
from_ref="$2"
126127
shift 2
127128
;;
129+
--from-current)
130+
from_current=1
131+
shift
132+
;;
128133
--track)
129134
track_mode="$2"
130135
shift 2
@@ -195,9 +200,25 @@ cmd_create() {
195200
fi
196201
fi
197202

198-
# Determine from_ref
203+
# Determine from_ref with precedence: --from > --from-current > default
199204
if [ -z "$from_ref" ]; then
200-
from_ref=$(resolve_default_branch "$repo_root")
205+
if [ "$from_current" -eq 1 ]; then
206+
# Get current branch (try modern git first, then fallback)
207+
from_ref=$(git branch --show-current 2>/dev/null)
208+
if [ -z "$from_ref" ]; then
209+
from_ref=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
210+
fi
211+
212+
# Handle detached HEAD state
213+
if [ -z "$from_ref" ] || [ "$from_ref" = "HEAD" ]; then
214+
log_warn "Currently in detached HEAD state - falling back to default branch"
215+
from_ref=$(resolve_default_branch "$repo_root")
216+
else
217+
log_info "Creating from current branch: $from_ref"
218+
fi
219+
else
220+
from_ref=$(resolve_default_branch "$repo_root")
221+
fi
201222
fi
202223

203224
# Construct folder name for display
@@ -988,6 +1009,7 @@ CORE COMMANDS (daily workflow):
9881009
new <branch> [options]
9891010
Create a new worktree (folder named after branch)
9901011
--from <ref>: create from specific ref
1012+
--from-current: create from current branch (for parallel variants)
9911013
--track <mode>: tracking mode (auto|remote|local|none)
9921014
--no-copy: skip file copying
9931015
--no-fetch: skip git fetch
@@ -1078,6 +1100,11 @@ WORKFLOW EXAMPLES:
10781100
# Chain commands together
10791101
git gtr new hotfix && git gtr editor hotfix && git gtr ai hotfix
10801102
1103+
# Create variant worktrees from current branch (for parallel work)
1104+
git checkout feature/user-auth
1105+
git gtr new variant-1 --from-current # Creates variant-1 from feature/user-auth
1106+
git gtr new variant-2 --from-current # Creates variant-2 from feature/user-auth
1107+
10811108
# When finished
10821109
git gtr rm feature/user-auth --delete-branch
10831110

completions/_git-gtr

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
#compdef git-gtr
1+
#compdef _git-gtr git-gtr gtr
2+
3+
# Register gtr as a git subcommand for completion
4+
zstyle ':completion:*:*:git:*' user-commands gtr:'Git worktree management'
5+
26
# Zsh completion for git gtr
37

4-
_git_gtr() {
5-
# Note: When called via 'git gtr', the words array is [git-gtr, <command>, ...]
6-
# Git's completion system invokes this function automatically
8+
_git-gtr() {
9+
# Normalize invocation so we always see: [git, gtr, <command>, ...]
10+
# Handles: `git gtr`, `gtr`, and `git-gtr` invocations
11+
if [[ $words[1] != git ]]; then
12+
# Handle both `gtr ...` and `git-gtr ...`
13+
words=(git gtr "${words[@]:1}")
14+
(( CURRENT += 1 ))
15+
fi
716

817
local -a commands
918
commands=(
@@ -29,58 +38,72 @@ _git_gtr() {
2938
# Add special ID '1' for main repo
3039
all_options=("1" "${branches[@]}")
3140

41+
# Early handler for `new` command - handle all positions
42+
if (( CURRENT >= 4 )) && [[ $words[3] == new ]]; then
43+
_arguments \
44+
'1:branch name:' \
45+
'--from[Base ref]:ref:' \
46+
'--from-current[Create from current branch]' \
47+
'--track[Track mode]:mode:(auto remote local none)' \
48+
'--no-copy[Skip file copying]' \
49+
'--no-fetch[Skip git fetch]' \
50+
'--force[Allow same branch in multiple worktrees]' \
51+
'--name[Custom folder name suffix]:name:' \
52+
'--yes[Non-interactive mode]'
53+
return
54+
fi
55+
56+
# Early handler for `list/ls` command - add --porcelain
57+
if (( CURRENT >= 4 )) && [[ $words[3] == (list|ls) ]]; then
58+
_arguments '--porcelain[Machine-readable output]'
59+
return
60+
fi
61+
3262
# Complete the gtr subcommand (new, go, editor, etc.)
33-
if (( CURRENT == 2 )); then
63+
if (( CURRENT == 3 )); then
3464
_describe 'commands' commands
3565
# Complete arguments to the subcommand
36-
elif (( CURRENT == 3 )); then
37-
case "$words[2]" in
38-
go|run|editor|ai|rm)
66+
elif (( CURRENT == 4 )); then
67+
case "$words[3]" in
68+
go|run|rm)
3969
_describe 'branch names' all_options
4070
;;
41-
new)
42-
_arguments \
43-
'--from[Base ref]:ref:' \
44-
'--track[Track mode]:mode:(auto remote local none)' \
45-
'--no-copy[Skip file copying]' \
46-
'--no-fetch[Skip git fetch]' \
47-
'--force[Allow same branch in multiple worktrees]' \
48-
'--name[Custom folder name suffix]:name:' \
49-
'--yes[Non-interactive mode]'
71+
editor)
72+
_describe 'branch names' all_options
73+
;;
74+
ai)
75+
_describe 'branch names' all_options
5076
;;
5177
config)
5278
_values 'config action' get set add unset
5379
;;
5480
esac
5581
# Complete subsequent arguments
56-
elif (( CURRENT >= 4 )); then
57-
case "$words[2]" in
82+
elif (( CURRENT >= 5 )); then
83+
case "$words[3]" in
84+
editor)
85+
_arguments '--editor[Editor to use]:editor:(cursor vscode zed idea pycharm webstorm vim nvim emacs sublime nano atom none)'
86+
;;
87+
ai)
88+
_arguments '--ai[AI tool to use]:tool:(aider claude codex cursor continue opencode none)'
89+
;;
5890
rm)
5991
_arguments \
6092
'--delete-branch[Delete branch]' \
6193
'--force[Force removal even if dirty]' \
6294
'--yes[Non-interactive mode]'
6395
;;
6496
config)
65-
case "$words[3]" in
97+
case "$words[4]" in
6698
get|set|add|unset)
67-
_values 'config key' \
68-
'gtr.worktrees.dir' \
69-
'gtr.worktrees.prefix' \
70-
'gtr.defaultBranch' \
71-
'gtr.editor.default' \
72-
'gtr.ai.default' \
73-
'gtr.copy.include' \
74-
'gtr.copy.exclude' \
75-
'gtr.copy.includeDirs' \
76-
'gtr.copy.excludeDirs' \
77-
'gtr.hook.postCreate' \
78-
'gtr.hook.postRemove'
99+
_arguments \
100+
'--global[Use global git config]' \
101+
'*:config key:(gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.editor.default gtr.ai.default gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.postRemove)'
79102
;;
80103
esac
81104
;;
82105
esac
83106
fi
84107
}
85108

86-
# Function is auto-discovered by git's completion system - do not call it here
109+
_git-gtr "$@"

completions/gtr.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ _git_gtr() {
4848
new)
4949
# Complete flags
5050
if [[ "$cur" == -* ]]; then
51-
COMPREPLY=($(compgen -W "--id --from --track --no-copy --no-fetch --force --name --yes" -- "$cur"))
51+
COMPREPLY=($(compgen -W "--id --from --from-current --track --no-copy --no-fetch --force --name --yes" -- "$cur"))
5252
elif [ "$prev" = "--track" ]; then
5353
COMPREPLY=($(compgen -W "auto remote local none" -- "$cur"))
5454
fi

completions/gtr.fish

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ complete -f -c git -n '__fish_git_gtr_needs_command' -a help -d 'Show help'
4848

4949
# New command options
5050
complete -c git -n '__fish_git_gtr_using_command new' -l from -d 'Base ref' -r
51+
complete -c git -n '__fish_git_gtr_using_command new' -l from-current -d 'Create from current branch'
5152
complete -c git -n '__fish_git_gtr_using_command new' -l track -d 'Track mode' -r -a 'auto remote local none'
5253
complete -c git -n '__fish_git_gtr_using_command new' -l no-copy -d 'Skip file copying'
5354
complete -c git -n '__fish_git_gtr_using_command new' -l no-fetch -d 'Skip git fetch'

0 commit comments

Comments
 (0)