From 2ce3941123d2995aa58d0fd929b1550e328e8852 Mon Sep 17 00:00:00 2001 From: Andrew Stilliard Date: Thu, 27 Aug 2026 09:50:35 +0100 Subject: [PATCH] feat(mk): drop a leading "worktree-" from the worktree folder name Claude Code creates worktrees as .claude/worktrees/ on a branch named worktree-. wt now derives the folder the same way, so `wt mk worktree-my-feature` lands in .claude/worktrees/my-feature instead of .claude/worktrees/worktree-my-feature. Both spellings still resolve: _wt_resolve matches the branch name or the folder basename, and completion already does substring matching. Co-Authored-By: Claude Opus 5 --- README.md | 2 +- lib/commands/help.sh | 3 ++- lib/commands/mk.sh | 2 +- lib/core.sh | 10 ++++++++++ test/helpers.bash | 2 +- test/mk.bats | 17 +++++++++++++++++ test/resolve.bats | 9 +++++++++ 7 files changed, 41 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9f1432d..31dc601 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ wt help # show usage Aliases: `add`/`create` → `mk`, `remove`/`del` → `rm`, `list` → `ls` -Worktrees are created in `.claude/worktrees/` inside the repo, the same place Claude Code puts them, so both tools see the same set. Add `.claude/worktrees/` to your `.gitignore` if it isn't already. Slashes in a branch name become dashes in the folder. +Worktrees are created in `.claude/worktrees/` inside the repo, the same place Claude Code puts them, so both tools see the same set. Add `.claude/worktrees/` to your `.gitignore` if it isn't already. Slashes in a branch name become dashes in the folder, and a leading `worktree-` is dropped (Claude Code names its branches that way, so `wt mk worktree-my-feature` creates `.claude/worktrees/my-feature`, matching what Claude Code would do). Set `wt.path` to put them somewhere else - `{name}` is the branch with slashes replaced, `{repo}` the repo's folder name, and a relative template resolves against the repo root so it means the same from any worktree: diff --git a/lib/commands/help.sh b/lib/commands/help.sh index f948dd4..3492b3d 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -45,7 +45,8 @@ wt.path: Where `wt mk` puts a worktree, if you don't want .claude/worktrees/: git config wt.path '../{repo}-{name}' # sibling of the repo git config --global wt.path '~/wt/{name}' # all repos, outside the tree - {name} is the branch with slashes replaced, {repo} the repo's folder name. + {name} is the branch with slashes replaced and a leading worktree- dropped, + {repo} the repo's folder name. A relative template resolves against the repo root. .worktreeinclude: diff --git a/lib/commands/mk.sh b/lib/commands/mk.sh index 8151f1c..fa6c311 100644 --- a/lib/commands/mk.sh +++ b/lib/commands/mk.sh @@ -15,7 +15,7 @@ _wt_mk() { set -- "${args[@]}" local branch="${1?usage: wt mk [path] [--base B] [--pre-hook P] [--post-hook P]}" local root; root=$(_wt_root) || return 1 - local safe="${branch//\//-}" + local safe; safe=$(_wt_safe_name "$branch") local dest="$2" [ -n "$dest" ] || { dest=$(_wt_dest_default "$root" "$safe") || return 1; } _wt_warn_unignored "$root" "$dest" diff --git a/lib/core.sh b/lib/core.sh index 8ca4e17..1bda688 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -57,6 +57,16 @@ _wt_copy_worktreeinclude() { done } +# folder name for a branch: slashes become dashes, and a leading "worktree-" is +# dropped (Claude Code names branches that way; the folder is already a worktree) +_wt_safe_name() { + local name="${1//\//-}" + case "$name" in + worktree-?*) printf '%s' "${name#worktree-}" ;; + *) printf '%s' "$name" ;; + esac +} + # where a new worktree goes: the wt.path template if set, else .claude/worktrees/. # {name} is the branch with slashes replaced, {repo} the repo's folder name. A relative # template resolves against the repo root, so it means the same from any worktree. diff --git a/test/helpers.bash b/test/helpers.bash index 67fa001..28fc16a 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -31,7 +31,7 @@ wt_common_setup() { # default worktree path wt mk uses for a branch, inside the test repo wt_dest() { - local safe="${1//\//-}" + local safe; safe=$(_wt_safe_name "$1") printf '%s' "$TEST_REPO/.claude/worktrees/$safe" } diff --git a/test/mk.bats b/test/mk.bats index 2fb0173..abb569e 100644 --- a/test/mk.bats +++ b/test/mk.bats @@ -45,6 +45,23 @@ teardown() { wt_common_teardown; } git worktree remove "$expected" } +@test "wt mk strips a leading worktree- from the folder name" { + local branch="worktree-back-in-stock-report" + wt mk "$branch" + local expected="$TEST_REPO/.claude/worktrees/back-in-stock-report" + [ -d "$expected" ] + [ "$(git -C "$expected" rev-parse --abbrev-ref HEAD)" = "$branch" ] + git worktree remove "$expected" +} + +@test "wt mk keeps a branch named exactly worktree- intact" { + local branch="worktree-" + wt mk "$branch" + local expected="$TEST_REPO/.claude/worktrees/worktree-" + [ -d "$expected" ] + git worktree remove "$expected" +} + @test "wt mk accepts explicit path" { local dest="$TEST_REPO-explicit" wt mk explicit-path "$dest" diff --git a/test/resolve.bats b/test/resolve.bats index ff4bf0c..1e5bb13 100644 --- a/test/resolve.bats +++ b/test/resolve.bats @@ -19,3 +19,12 @@ teardown() { wt_common_teardown; } result=$(_wt_resolve nonexistent) [ -z "$result" ] } + +@test "_wt_resolve finds a worktree- branch by both branch name and folder name" { + wt mk worktree-resolve-demo + cd "$TEST_REPO" + local expected="$TEST_REPO/.claude/worktrees/resolve-demo" + [ "$(_wt_resolve worktree-resolve-demo)" = "$expected" ] + [ "$(_wt_resolve resolve-demo)" = "$expected" ] + git worktree remove "$expected" +}