Releases: shhac/git-wt
v0.13.1
Full Changelog: v0.13.0...v0.13.1
v0.13.0
v0.12.0
Highlights
Shell tab-completion. Cobra-generated completion scripts for bash, zsh, fish and powershell, plus dynamic candidate completion on the commands where it actually helps:
gwt go <TAB> # existing worktree branches (current excluded)
gwt rm <TAB> # removable worktrees (main + already-typed args excluded)
gwt add <TAB> # local + <remote>/<rest> refs not already in a worktree
Candidates carry descriptions matching gwt ls — location and recency, formatted with the same column layout. Shells that support completion-with-descriptions (zsh, fish, bash-v2, powershell) show e.g.:
feat-x -- #feat-x 2h 3m
paul/auth -- #paul/auth 1d 4h
right next to each branch. bash-v1 falls back to plain branch names.
One-line setup (homebrew users)
Already on brew install shhac/tap/git-wt? Just brew upgrade git-wt — the formula now installs completion scripts automatically alongside the binary. zsh and fish pick them up via the standard brew paths; bash users need brew install bash-completion@2.
Source-build setup
git-wt completion bash > ~/.local/share/bash-completion/completions/git-wt
git-wt completion zsh > "${fpath[1]}/_git-wt"
git-wt completion fish > ~/.config/fish/completions/git-wt.fish
The shell function generated by git-wt alias <name> now appends a self-binding line, so eval "$(git-wt alias gwt)" wires both the wrapper AND tab-completion on the alias in one step. Opt out with git-wt alias gwt --no-completion.
Under the hood
internal/cli/completion.go— newgit-wt completion <shell>subcommandinternal/cli/completion_helpers.go— pure candidate builders + 3ValidArgsFunctionsinternal/cli/alias.go— completion-binding template +--no-completionflag- Reuses the existing
columnWidths+padRight+ui.HumanSinceso completion descriptions stay in lockstep withgwt lsoutput
Install
brew install shhac/tap/git-wt # new
brew upgrade shhac/tap/git-wt # existing — picks up completions
v0.11.0
Highlights
New git-wt config command + ${...} templating. Persistent settings stored under git config wt.* with type validation, scope precedence (--local > --global), and template-variable resolution.
Headline use case:
git config --global wt.parentDir '${repoParent}/${repo}.worktrees'
Every repo now uses sibling <reponame>.worktrees/ directories — one global setting, per-repo result.
Breaking change
Default parentDir renamed from .gwt to .worktrees. Pre-1.0 so we just made the change. Existing users can pin the old path with one line:
git config --global wt.parentDir '${repoPath}/.gwt'
Old worktrees in .gwt/ remain tracked by git and visible in gwt list — only the default destination for new worktrees moves.
What's in git-wt config
git-wt config # list all keys + effective values
git-wt config parentDir # show one key (raw + resolved)
git-wt config parentDir '../wt-${repo}' # set --local (current repo)
git-wt config --global parentDir '${repoParent}/${repo}.worktrees'
git-wt config --unset parentDir # idempotent
Three keys ship:
wt.parentDir(string, templated) — parent directory for new worktreeswt.plain(bool) — always run with--plain(also honorsNO_COLOR)wt.fd(int, 3-9) — default fd for the wrapper protocol
Template variables: ${repo}, ${repoPath}, ${repoParent}, ${home}. Unknown vars error at set time AND at use time (no silent fallback).
Precedence: CLI flag > git config --local wt.* > git config --global wt.* > built-in default.
Quality
- New
internal/config/package + per-KeyValidatorfield (per-key constraints like FD's 3-9 range live with their key, not in a generic switch). - New typed
*git.ExitErrorwithExitCode()/Unwrap()—UnsetandGetScopedbranch on exit codes instead of string-matching. applyConfigDefaultsdecomposed into small per-flag helpers (applyBoolFlagDefault,applyIntFlagDefault,readUnchangedFlagConfig).wt.ResolveParentDirnow resolves relative--parent-dirpaths against the repo root (was: against CWD — surprising when run from a subdir).- ~25 new unit tests + 11 new E2E tests covering the new surface.
Install
brew install shhac/tap/git-wt # new users
brew upgrade shhac/tap/git-wt # existing
v0.10.0
Features
- New
ejectcommand. Moves the currently-checked-out branch from the main working tree into a new worktree. Stashes uncommitted changes (including untracked files), switches the main tree tomain/master(or--base), creates the worktree, and restores the stashed changes inside it.gwt eject— eject current branch using its name as the leafgwt eject <leaf>— override the directory name (mirrorsadd's shape)gwt eject --base <branch>— override the auto-detected main/mastergwt eject --parent-dir <path>— override the default.gwt/parent- Interactive confirm by default;
--non-interactiveskips - Refuses cleanly when HEAD is detached, current branch is the base, run from inside an existing worktree, or no main/master exists
- Shell wrapper (
gwt alias) now alsocd's aftergwt eject.
Internal
- Stash plumbing (
StashPush,StashApply,StashDropBySHA,ShortStashRef) lifted from the eject command intointernal/wt/stash.goso it's reusable by future commands. NewStashApplyOutcomeenum (StashApplied/StashAppliedKeptStash/StashApplyFailed) replaces the previous bool that collapsed three distinguishable cases. - Repo-state helpers (
CurrentBranch,IsWorkingTreeDirty,DetectBaseBranch) and aNewLocalAddRefconstructor moved tointernal/wt/. - Eject's rollback rewritten as a LIFO compensation stack — each successful mutation registers its inverse, and the failure paths just call
rollback()once. - Shared
defaultedLeafhelper betweenaddandeject(was verbatim duplicated). runEjectshrank from 56 lines to 14 via aplanEjectextraction;executeEjectsignature collapsed from 7 positional args to a single*ejectPlan.
Fixes
- Eject rollback was silently broken.
git stash pop --index <SHA>only acceptsstash@{N}references, not raw SHAs, so the rollback's stash-restore always failed. The error was discarded by_, _ =, leaving the user's dirty state stranded in the stash list. Fixed viaapply + drop-by-SHA; rollback errors now surface as warnings.
Tests
- 3 new E2E tests pinning the rollback paths and by-SHA drop semantics, plus unit tests for the new
wt/stash.gohelpers including a full push/apply/drop roundtrip.
v0.9.0
Features
- New
addcommand for opening an existing branch in a worktree. Pairs withnew:newcreates the branch,addattaches a worktree to one that already exists.gwt add <branch>— checks out an existing local branch into<repo>/.gwt/<branch>/gwt add origin/<branch>— creates a local branch tracking the remote (DWIM via--track -b)gwt add <leaf> <branch|remote-ref>— overrides the leaf directory name- Honours
--parent-dir,--no-copy,--copy-file-config(same asnew)
- Shell wrapper (
gwt alias) now alsocd's aftergwt add, alongsidego/new/rm.
Internal
- Refactored the shared pipeline between
newandaddinto a newinternal/cli/worktree_create.go(~50 lines of duplication removed). Each command'sRunEis now ~25 lines of orchestration. - New shared helpers:
requireMutableRepo,wt.ResolveParentDir,prepareWorktreeSite,finalizeWorktreeSite. AddRefResolutionnow owns thegit worktree addargv construction via aWorktreeAddArgsmethod; renamedinternal/wt/add.gotorefresolve.gosince the resolver is command-agnostic.- Split
warnIfParentNotIgnoredinto pure (relInsideRepo) + git-wrapper (isPathIgnored) + caller for testability.
Tests
- Added E2E coverage for previously unverified guards: merge-in-progress refusal, bare-repo refusal, copy-config-failure-is-non-fatal contract,
new --parent-dirparity withadd, DWIM local-name correctness onadd <leaf> origin/<rest>. - Pinned bare-repo refusal to its current error path so a future
Inspectreorder is loud, not silent.
Docs
- README and AGENTS.md document the
addcommand.
v0.8.3
- fix[fd]: require write mode for wrapper-protocol fd
Fixes a crash in Linux container environments (Docker, GitHub Actions
ubuntu runners) where a read-only cgroup fd leaks as fd 3, causing
git-wt go|new|rm to fail with error: write fd3: bad file descriptor.
The wrapper-protocol fd check now requires write mode.
v0.8.2
Fixes
-
fix[alias]: detect subcommand after leading global flags.
gwt --debug go <branch>(and any pre-subcommand flag like--plain,-n,--fd) was falling through to the wrapper's pass-through branch, skipping the cd. The generated shell wrapper now walks$@to find the real subcommand. The trailing-flag form (gwt go <branch> --debug) still works and is now redundant rather than required.Action required for existing users: re-run
eval "$(git-wt alias gwt)"(or your usual rc line) after upgrading so your shell picks up the new wrapper.
Improvements
-
feat[debug]: instrument picker sessions and
emit-target. Interactive commands (go,rm) now show the full timeline including the picker open/close span (pick.one,pick.many,pick.confirm) and the path emit (emit-target). Useful for distinguishing "the tool is slow" from "I was deciding."Awk filter for picker-only durations:
awk -F'\t' '$5 ~ /^pick\./'.
v0.8.1
- feat[debug]: tab-separate columns with
-placeholder for empties
The --debug timeline (introduced in v0.8.0) now uses tab-separated columns:
[elapsed] op#N status took name detail [err]
Awk-friendly extraction:
# slowest git invocations
gwt --debug rm <branch> 2>&1 | awk -F'\t' '$3=="done"{print $4,$5,$6}' | sort -h
# only failures
gwt --debug ls 2>&1 | awk -F'\t' '$3=="failed"{print $5,$6,$7}'Non-trailing empty columns are filled with - so positional default-awk
also stays consistent across start/done/failed rows. The internal lipgloss
dim style was preserving tabs only after setting TabWidth(-1) — lipgloss
otherwise expands embedded tabs to 4 spaces when output goes to a TTY.
v0.8.0
- feat: wire --debug flag to operation timeline
The previously dead --debug flag now drives an operation timeline written to
stderr. Every git subprocess prints a paired start/done line with a per-op id
and duration; targeted spans cover rm's chdir bounce and new's
config-copy phase. Useful for diagnosing slow gwt rm runs (typically the
working-tree status scan inside git worktree remove).