feat(init): add ctrl-n keybinding to create new worktree from fzf picker#141
feat(init): add ctrl-n keybinding to create new worktree from fzf picker#141
Conversation
WalkthroughAdds ctrl-n support to the interactive picker across Bash, Zsh, and Fish: picker exposes a ctrl-n:new action, scripts prompt for a branch name when triggered, invoke Changes
Sequence DiagramsequenceDiagram
actor User
participant Shell as "Shell\n(runner)"
participant fzf as "fzf\n(picker)"
participant git_gtr as "git gtr\n(new)"
User->>Shell: run interactive command
Shell->>fzf: launch picker with --expect=ctrl-n (header includes "ctrl-n:new")
User->>fzf: press ctrl-n
fzf-->>Shell: return "ctrl-n" selection
Shell->>User: prompt "Branch name: "
User->>Shell: enter branch name
Shell->>git_gtr: run `git gtr new "<branch>"`
git_gtr-->>Shell: exit status
Shell->>User: display result
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
lib/commands/init.sh (1)
115-117: Prefer|| return $?over barereturn $?to satisfy theset -eguard guideline.
command git gtr new "$_gtr_branch"followed byreturn $?on the next line is dead code whenset -eis active—ifgit gtr newexits non-zero, the function exits before thereturn. The|| return $?form makes the propagation explicit and safe regardless ofset -estate.♻️ Proposed fix (bash)
- command git gtr new "$_gtr_branch" - return $? + command git gtr new "$_gtr_branch" || return $? + return 0The same pattern applies to the zsh block (lines 233–234).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/commands/init.sh` around lines 115 - 117, Replace the pattern that runs the git gtr command then returns its exit code with an explicit short-circuit so failures propagate under set -e: change the block using the variable _gtr_branch and the call command git gtr new "$_gtr_branch" followed by return $? to a single line command git gtr new "$_gtr_branch" || return $?; apply the same replacement in the zsh block that likewise invokes command git gtr new "$_gtr_branch".tests/init.bats (1)
208-280: New ctrl-n test block LGTM; consider adding split-logic coverage.The 12 tests correctly verify structural presence of
--expect=ctrl-n, the header label, thegit gtr newinvocation, and the prompt string across all three shells. The fish assertion at line 261 (*'git gtr new "$_gtr_branch"'*) correctly matches the actualcommand git gtr new "$_gtr_branch"via the leading*wildcard.One minor gap: the output-parsing lines (
head -1/sed -n '2p'for bash/zsh,$_gtr_selection[1]/$_gtr_selection[2]for fish) that implement the two-line fzf split are untested. Given that the rest of the test file uses the same static-pattern style, adding a couple of assertions would catch any future regression in the parsing logic:+@test "bash output splits fzf output into key and line" { + run cmd_init bash + [ "$status" -eq 0 ] + [[ "$output" == *'head -1 <<< "$_gtr_selection"'* ]] + [[ "$output" == *"sed -n '2p' <<< \"\$_gtr_selection\""* ]] +} + +@test "fish output splits fzf output into key and line" { + run cmd_init fish + [ "$status" -eq 0 ] + [[ "$output" == *'_gtr_selection[1]'* ]] + [[ "$output" == *'_gtr_selection[2]'* ]] +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/init.bats` around lines 208 - 280, Add tests exercising the two-line fzf split/parse logic so regressions in the selection parsing are caught: extend the new ctrl-n test block to assert that for bash/zsh the parsing lines (head -1 and sed -n '2p') result in the expected branch and description (e.g. check the first line appears where head -1 is used and the second line where sed -n '2p' is used), and for fish assert $_gtr_selection[1] holds the branch and $_gtr_selection[2] holds the description; reference the existing tests invoking cmd_init and the variables/_parsing expressions ($_gtr_selection, head -1, sed -n '2p') so the new assertions match the existing output-check style.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/commands/init.sh`:
- Around line 115-117: Replace the pattern that runs the git gtr command then
returns its exit code with an explicit short-circuit so failures propagate under
set -e: change the block using the variable _gtr_branch and the call command git
gtr new "$_gtr_branch" followed by return $? to a single line command git gtr
new "$_gtr_branch" || return $?; apply the same replacement in the zsh block
that likewise invokes command git gtr new "$_gtr_branch".
In `@tests/init.bats`:
- Around line 208-280: Add tests exercising the two-line fzf split/parse logic
so regressions in the selection parsing are caught: extend the new ctrl-n test
block to assert that for bash/zsh the parsing lines (head -1 and sed -n '2p')
result in the expected branch and description (e.g. check the first line appears
where head -1 is used and the second line where sed -n '2p' is used), and for
fish assert $_gtr_selection[1] holds the branch and $_gtr_selection[2] holds the
description; reference the existing tests invoking cmd_init and the
variables/_parsing expressions ($_gtr_selection, head -1, sed -n '2p') so the
new assertions match the existing output-check style.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
lib/commands/init.shtests/init.bats
2c6e5de to
3202292
Compare
3202292 to
b132162
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/commands/init.sh (1)
360-372:⚠️ Potential issue | 🟠 MajorFix Fish key parsing to handle special keys in single-line
--expectoutput.When
count $_gtr_selectionis1, the code assumes Enter and sets_gtr_keyto empty. However, if a special key likectrl-nis pressed without a selection (or if fzf outputs only the key), Fish command substitution may collapse it to a single item, causing the key to be misplaced into_gtr_line. This bypasses the checks forctrl-n,ctrl-a, andctrl-eat lines 367–380.Suggested patch
+ set -l _gtr_key "" + set -l _gtr_line "" if test (count $_gtr_selection) -eq 1 - set -l _gtr_key "" - set -l _gtr_line "$_gtr_selection[1]" + if contains -- "$_gtr_selection[1]" ctrl-n ctrl-a ctrl-e + set _gtr_key "$_gtr_selection[1]" + else + set _gtr_line "$_gtr_selection[1]" + end else - set -l _gtr_key "$_gtr_selection[1]" - set -l _gtr_line "$_gtr_selection[2]" + set _gtr_key "$_gtr_selection[1]" + set _gtr_line "$_gtr_selection[2]" end🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/commands/init.sh` around lines 360 - 372, When parsing fzf output in init.sh, the branch that handles count $_gtr_selection -eq 1 incorrectly assumes the single item is always the selected line; instead detect if that single item is a special key and populate _gtr_key (and clear _gtr_line) or otherwise populate _gtr_line (and clear _gtr_key). Update the block that sets _gtr_key/_gtr_line so that when count $_gtr_selection is 1 you test the value of $_gtr_selection[1] against the known special keys (e.g., "ctrl-n","ctrl-a","ctrl-e" or a pattern like "ctrl-") and set _gtr_key="that_value" and _gtr_line="" for keys, otherwise set _gtr_key="" and _gtr_line="that_value"; keep the existing downstream checks that compare $_gtr_key to "ctrl-n" etc.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/commands/init.sh`:
- Around line 110-116: The unguarded read -r _gtr_branch can cause the script to
exit under set -e when the user sends EOF (Ctrl-D); update both occurrences of
read -r _gtr_branch so EOF doesn't propagate a nonzero exit: either guard the
call (e.g. use "read -r _gtr_branch || true" or an if/! read check) and treat
EOF as an empty branch name, preserving the existing empty-check and
returning/canceling gracefully before invoking command git gtr new; ensure you
change both occurrences that reference _gtr_branch.
In `@tests/init.bats`:
- Around line 411-483: Tests that assert the exact fzf --expect string are stale
(they expect "--expect=ctrl-a,ctrl-e") and must accept the new
"--expect=ctrl-n,ctrl-a,ctrl-e" ordering; locate assertions in tests/init.bats
that check for the exact "--expect=ctrl-a,ctrl-e" (look for the pattern string
used with run cmd_init) and change them to assert presence of the individual
keys instead (e.g. assert output contains "ctrl-a" and contains "ctrl-e" or
assert output contains "--expect=" and contains "ctrl-a" and "ctrl-e"), or use a
glob/regex that allows additional keys like "ctrl-n" to be present.
---
Outside diff comments:
In `@lib/commands/init.sh`:
- Around line 360-372: When parsing fzf output in init.sh, the branch that
handles count $_gtr_selection -eq 1 incorrectly assumes the single item is
always the selected line; instead detect if that single item is a special key
and populate _gtr_key (and clear _gtr_line) or otherwise populate _gtr_line (and
clear _gtr_key). Update the block that sets _gtr_key/_gtr_line so that when
count $_gtr_selection is 1 you test the value of $_gtr_selection[1] against the
known special keys (e.g., "ctrl-n","ctrl-a","ctrl-e" or a pattern like "ctrl-")
and set _gtr_key="that_value" and _gtr_line="" for keys, otherwise set
_gtr_key="" and _gtr_line="that_value"; keep the existing downstream checks that
compare $_gtr_key to "ctrl-n" etc.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
lib/commands/init.shtests/init.bats
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
lib/commands/init.sh (1)
233-239:⚠️ Potential issue | 🟡 MinorSame unguarded
readissue in Zsh handler.Consistent with the Bash handler, the
read -r _gtr_branchat line 235 should be guarded to handle EOF gracefully.Proposed fix
if [ "$_gtr_key" = "ctrl-n" ]; then printf "Branch name: " >&2 - read -r _gtr_branch + read -r _gtr_branch || true [ -z "$_gtr_branch" ] && return 0 command git gtr new "$_gtr_branch" return $? fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/commands/init.sh` around lines 233 - 239, The Zsh key handler uses an unguarded read for _gtr_branch (when _gtr_key == "ctrl-n") which can fail on EOF; modify the block around the read -r _gtr_branch so the read is checked and handles failure gracefully (e.g., use a conditional/read return check such as if ! read -r _gtr_branch; then return 0; fi) before calling command git gtr new "$_gtr_branch", keeping the existing early-return when _gtr_branch is empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/commands/init.sh`:
- Around line 92-116: The branch-name prompt's unguarded read (read -r
_gtr_branch) can fail on EOF/ctrl-D and, under set -e, abort before the
subsequent empty-string check; update the ctrl-n branch handling to check the
return status of read -r _gtr_branch and return 0 if the read fails (EOF) before
proceeding to the [ -z "$_gtr_branch" ] check, so that functions like git gtr
new are only called when a valid branch name is obtained.
---
Duplicate comments:
In `@lib/commands/init.sh`:
- Around line 233-239: The Zsh key handler uses an unguarded read for
_gtr_branch (when _gtr_key == "ctrl-n") which can fail on EOF; modify the block
around the read -r _gtr_branch so the read is checked and handles failure
gracefully (e.g., use a conditional/read return check such as if ! read -r
_gtr_branch; then return 0; fi) before calling command git gtr new
"$_gtr_branch", keeping the existing early-return when _gtr_branch is empty.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
lib/commands/init.shtests/init.bats
| local _gtr_selection _gtr_key _gtr_line _gtr_branch | ||
| _gtr_selection="$(printf '%s\n' "$_gtr_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' \ | ||
| --header='enter:cd │ ctrl-n:new │ ctrl-e:editor │ ctrl-a:ai │ ctrl-d:delete │ ctrl-y:copy │ ctrl-r:refresh' \ | ||
| --expect=ctrl-n,ctrl-a,ctrl-e \ | ||
| --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% \ | ||
| --expect=ctrl-a,ctrl-e \ | ||
| --bind='ctrl-d:execute(git gtr rm {2} > /dev/tty 2>&1 < /dev/tty)+reload(git gtr list --porcelain)' \ | ||
| --bind='ctrl-y:execute(git gtr copy {2} > /dev/tty 2>&1 < /dev/tty)' \ | ||
| --bind='ctrl-r:reload(git gtr list --porcelain)')" || return 0 | ||
| [ -z "$_gtr_selection" ] && return 0 | ||
| _gtr_key="$(head -1 <<< "$_gtr_selection")" | ||
| _gtr_line="$(sed -n '2p' <<< "$_gtr_selection")" | ||
| if [ "$_gtr_key" = "ctrl-n" ]; then | ||
| printf "Branch name: " >&2 | ||
| read -r _gtr_branch | ||
| [ -z "$_gtr_branch" ] && return 0 | ||
| command git gtr new "$_gtr_branch" | ||
| return $? | ||
| fi |
There was a problem hiding this comment.
Guard read to handle EOF (Ctrl-D) gracefully.
The read -r _gtr_branch at line 112 is unguarded. If a user's interactive shell has set -e active and they press Ctrl-D (EOF) instead of entering a branch name, the read command will return non-zero and the function will exit before reaching the empty-string check on line 113. Although interactive shells typically don't have set -e active, defensive coding would guard this.
Note: A previous review flagged this same issue and it was marked as addressed, but the current code does not reflect the fix.
Proposed fix
if [ "$_gtr_key" = "ctrl-n" ]; then
printf "Branch name: " >&2
- read -r _gtr_branch
+ read -r _gtr_branch || true
[ -z "$_gtr_branch" ] && return 0
command git gtr new "$_gtr_branch"
return $?
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| local _gtr_selection _gtr_key _gtr_line _gtr_branch | |
| _gtr_selection="$(printf '%s\n' "$_gtr_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' \ | |
| --header='enter:cd │ ctrl-n:new │ ctrl-e:editor │ ctrl-a:ai │ ctrl-d:delete │ ctrl-y:copy │ ctrl-r:refresh' \ | |
| --expect=ctrl-n,ctrl-a,ctrl-e \ | |
| --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% \ | |
| --expect=ctrl-a,ctrl-e \ | |
| --bind='ctrl-d:execute(git gtr rm {2} > /dev/tty 2>&1 < /dev/tty)+reload(git gtr list --porcelain)' \ | |
| --bind='ctrl-y:execute(git gtr copy {2} > /dev/tty 2>&1 < /dev/tty)' \ | |
| --bind='ctrl-r:reload(git gtr list --porcelain)')" || return 0 | |
| [ -z "$_gtr_selection" ] && return 0 | |
| _gtr_key="$(head -1 <<< "$_gtr_selection")" | |
| _gtr_line="$(sed -n '2p' <<< "$_gtr_selection")" | |
| if [ "$_gtr_key" = "ctrl-n" ]; then | |
| printf "Branch name: " >&2 | |
| read -r _gtr_branch | |
| [ -z "$_gtr_branch" ] && return 0 | |
| command git gtr new "$_gtr_branch" | |
| return $? | |
| fi | |
| local _gtr_selection _gtr_key _gtr_line _gtr_branch | |
| _gtr_selection="$(printf '%s\n' "$_gtr_porcelain" | fzf \ | |
| --delimiter=$'\t' \ | |
| --with-nth=2 \ | |
| --ansi \ | |
| --layout=reverse \ | |
| --border \ | |
| --prompt='Worktree> ' \ | |
| --header='enter:cd │ ctrl-n:new │ ctrl-e:editor │ ctrl-a:ai │ ctrl-d:delete │ ctrl-y:copy │ ctrl-r:refresh' \ | |
| --expect=ctrl-n,ctrl-a,ctrl-e \ | |
| --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-d:execute(git gtr rm {2} > /dev/tty 2>&1 < /dev/tty)+reload(git gtr list --porcelain)' \ | |
| --bind='ctrl-y:execute(git gtr copy {2} > /dev/tty 2>&1 < /dev/tty)' \ | |
| --bind='ctrl-r:reload(git gtr list --porcelain)')" || return 0 | |
| [ -z "$_gtr_selection" ] && return 0 | |
| _gtr_key="$(head -1 <<< "$_gtr_selection")" | |
| _gtr_line="$(sed -n '2p' <<< "$_gtr_selection")" | |
| if [ "$_gtr_key" = "ctrl-n" ]; then | |
| printf "Branch name: " >&2 | |
| read -r _gtr_branch || true | |
| [ -z "$_gtr_branch" ] && return 0 | |
| command git gtr new "$_gtr_branch" | |
| return $? | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/commands/init.sh` around lines 92 - 116, The branch-name prompt's
unguarded read (read -r _gtr_branch) can fail on EOF/ctrl-D and, under set -e,
abort before the subsequent empty-string check; update the ctrl-n branch
handling to check the return status of read -r _gtr_branch and return 0 if the
read fails (EOF) before proceeding to the [ -z "$_gtr_branch" ] check, so that
functions like git gtr new are only called when a valid branch name is obtained.
Pull Request
Description
Motivation
Fixes # (issue)
Type of Change
Testing
Manual Testing Checklist
Tested on:
Core functionality tested:
git gtr new <branch>- Create worktreegit gtr go <branch>- Navigate to worktreegit gtr editor <branch>- Open in editor (if applicable)git gtr ai <branch>- Start AI tool (if applicable)git gtr rm <branch>- Remove worktreegit gtr list- List worktreesgit gtr config- Configuration commands (if applicable)Test Steps
Expected behavior:
Actual behavior:
Breaking Changes
Checklist
Before submitting this PR, please check:
git gtr(production) and./bin/gtr(development)Additional Context
License Acknowledgment
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache License 2.0.
Summary by CodeRabbit
New Features
Tests