@bojangles/gwt
v1.5.2
Published
A fast, hackable zsh toolkit for git worktrees — create, switch, open, list, and clean up worktrees with short commands and an fzf picker.
Maintainers
Readme
gwt - git worktree toolkit
A fast, hackable zsh toolkit for git worktrees. Create, switch, open, list, and clean up worktrees with short commands and an fzf picker — without leaving your shell.
gwa add / create a worktree
gws switch to a worktree (cd)
gwo open a worktree in your editor
gwx branch -- cmd run a command in a worktree (no cd)
gwr remove a worktree
gwl status dashboard
gwclean remove stale worktrees
gwt doctor check your setup
gwt update | uninstallWhy
Switching branches in place is disruptive: you stash or commit half-done work, your editor reloads, and your build output and node_modules get invalidated — so the next install or rebuild crawls. Git worktrees fix this by checking out each branch in its own folder, so main, a feature branch, and a hotfix can all be live at once and you switch between them instantly.
The catch is that raw git worktree is verbose and leaves you juggling paths by hand. gwt wraps it in short commands and an fzf picker — create or adopt a branch's worktree, jump between them, open one in your editor, and clean up merged ones — without leaving your shell or memorizing a single path.
Install
npx @bojangles/gwtThen restart your shell (or source ~/.zshrc) and verify:
exec zsh # start a fresh zsh session — or just open a new terminal (zsh is macOS's default)
gwt doctorThat's it — no sudo, nothing system-wide. gwt is a sourced zsh plugin, so the installer copies it to ~/.gwt/gwt.zsh and adds a single source line to your ~/.zshrc.
Prerequisites
Required
- git ≥ 2.7
- zsh
Optional (each just enables one feature — gwt doctor tells you what's missing)
- fzf — interactive pickers (without it, pass branch names explicitly)
- an editor command for
gwo/gwa -o(seeGWT_OPEN_CMD) - a clipboard command for
gwa -c—pbcopyon macOS (the default); on Linux setGWT_CLIPBOARD_CMDtoxclip/wl-copy - trash — makes
gwr/gwcleannear-instant by moving worktrees to the Trash instead of a slow recursive delete (auto-detected; setGWT_TRASH_CMDfor a different tool) - zsh completion (
compinit) — Tab completion
Install-time only: Node (the npx installer uses it). Node is not a runtime dependency and is not checked by gwt doctor.
Quick start
cd ~/projects/my-app
gwa feature-123 # create a worktree for a new branch off HEAD
gwa -m feature-124 # ...or base the new branch on your local default branch
gwa # no name → fzf: adopt an existing branch, or type a new one
gws # fzf-switch between worktrees (gws main jumps to the primary)
gwl # dashboard: branch · dirty · ahead/behind · last commit
gwr feature-123 # remove the worktree (keep the branch; -d also deletes it)
gwclean # remove stale (merged / gone-upstream) worktrees
gwo -g # Pick and open a worktree in any project-even outside a repositoryFeatures
At a glance — see Commands below for exact syntax and flags.
| Feature | What you get |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Create & adopt | |
| 🔎 Fuzzy picker | Pick an existing branch, or type a new name to create it — and it still works without fzf (just pass names) |
| 🌿 Adopt a pushed branch | Refreshes origin first, so a teammate's just-pushed branch shows up and is adopted, not re-created locally |
| 🎯 Branch off your default | gwa -m cuts the new branch from main (your default) instead of the current HEAD |
| 🌱 Seed new worktrees | Copies your gitignored files (.env, …) into every new worktree |
| 🪝 Post-create hook | Runs any command in a fresh worktree — install deps, kick off a build, whatever you need |
| Work across worktrees | |
| 🚀 Native shell cd | Jump right into a worktree — gwt is a sourced zsh plugin, so your shell actually moves (a compiled binary can't) |
| ⚡ Jump in on create | gwa -s creates or adopts a worktree and drops you straight into it, in one step |
| 🏃 Run without switching | gwx <branch> -- cmd runs a command in another worktree and streams it live — -d backgrounds it, -a runs it in all. No cd |
| 📊 Status dashboard | gwl shows dirty state, ahead/behind, and the last commit across all your worktrees |
| 🌐 Cross-project reach | -g (auto-on when you're outside a repo) makes gwo/gws/gwr/gwx/gwl span every repo under GWT_WORKTREE_DIR — jump to, or clean up, any project's worktree from anywhere |
| 🧭 Origin tracking | Remembers what each branch was cut from — shown by gwl -b |
| Clean up | |
| 🧹 Stale cleanup | gwclean clears merged and upstream-gone worktrees in one go |
| 🗑️ Instant, recoverable removal | Moves worktrees to the Trash — fast even with a huge node_modules, and undo-able |
| Comfort & setup | |
| 📦 Zero-install & hackable | One npx, no daemon or binary — it's plain zsh you can read and tweak |
| 🩺 Setup doctor | gwt doctor checks your environment and shows the effective config |
Commands
| Command | What it does |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| gwa [-c\|-o\|-s] [-m] [-d] [--no-fetch] [<branch>] [<start-point>] | Create or adopt a worktree. No <branch> → fzf picker: adopt an existing branch, or type a new name to create it. Refreshes origin first so colleagues' branches are current (see GWT_GWA_FETCH).-c — copy the "open" command to the clipboard (default)-o — open it in your editor-s — switch into it (cd)-m — base a new branch on your default branch (not HEAD)-d — run GWT_POST_INIT_CMD in the background so it opens/returns immediately (see GWT_POST_INIT_DETACH); --wait forces the foreground--no-fetch — skip the origin refresh this runshort flags bundle: gwa -omd |
| gwo [-g] [<branch>] | Open a worktree in your editor.No <branch> → picker (else the most recent).-g — pick from every repo under GWT_WORKTREE_DIR (auto-on outside a repo) |
| gws [-o] [-g] [<branch>] | Switch to a worktree (cd). No <branch> → picker.-o — also open it in your editor-g — pick from every repo under GWT_WORKTREE_DIR (auto-on outside a repo) |
| gwx [-d] [-a] [-g] [<branch>] -- <command> | Run a command in a worktree without cd-ing there. No <branch> → picker. Everything after -- runs verbatim; output streams live and the exit code passes through.-d — detach: run in the background, output → ~/.gwt/logs/<repo>/<branch>.log-a — run in all worktrees of the repo in parallel (continues on error, then a summary); -da backgrounds one job per worktree-g — pick from every repo under GWT_WORKTREE_DIR (single target; auto-on outside a repo; can't combine with -a) |
| gwr [-d\|-D] [-g] [<branch>] [--force] | Remove a worktree. The branch is kept by default. No <branch> → multi-select picker.-d — also delete the branch (safe: refuses if unmerged)-D — also delete the branch (force)-g — pick from every repo under GWT_WORKTREE_DIR (auto-on outside a repo)--force — passed straight through to git |
| gwl [-g] [-p] [-b] | Status dashboard — branch · dirty · ahead/behind · last commit.-g — every repo under GWT_WORKTREE_DIR (auto-on outside a repo)-p — also show each path + short SHA-b — show what each new branch was cut fromshort flags bundle: gwl -gbp = -g -b -p |
| gwclean [-n] | Remove stale worktrees (merged, or upstream gone).-n — dry-run (preview only) |
| gwp | Prune git's stale worktree bookkeeping (git worktree prune). |
| gwt / gwt -h | Help — bare lists commands; -h adds configuration. |
| gwt -v | Version — prints the bare version string. |
| gwt doctor | Diagnostics — checks your setup and shows the effective configuration. |
| gwt update | Update to the latest published version. |
| gwt uninstall | Uninstall gwt (asks first; -y to skip). |
gwt, gwt -h, gwt -v, gwt doctor, gwt update, and gwt uninstall work from anywhere. The worktree commands need to be run inside a git repository.
Configuration
Set these in your shell (or ~/.zshrc); gwt doctor shows the effective values.
| Variable | Purpose | Default |
| ---------------------- | ---------------------------------------------------------------------------------------------- | ----------------------- |
| GWT_WORKTREE_DIR | Base folder holding all worktrees ($dir/<repo>/<branch>) | ~/dev/workspace |
| GWT_COPY_FILES | Gitignored files copied into each new worktree (if present) | (.env) |
| GWT_OPEN_CMD | Command to open a worktree ({} = its path, {ws} = a generated .code-workspace) | code -n {} |
| GWT_CLIPBOARD_CMD | Command reading stdin → clipboard, for gwa -c | pbcopy (macOS) |
| GWT_POST_INIT_CMD | Command run inside a new worktree after creation | (none) |
| GWT_POST_INIT_DETACH | Run GWT_POST_INIT_CMD in the background so gwa opens/returns now (per-run -d / --wait) | (off) |
| GWT_PICKER_OPTIONS | Extra fzf options for the pickers | (none) |
| GWT_TRASH_CMD | Command to trash a path → fast gwr/gwclean (''=native) | auto (trash if found) |
| GWT_GWA_FETCH | Refresh origin before the gwa picker/lookup (0=off; per-run gwa --no-fetch) | 1 (on) |
GWT_COPY_FILES example for a real project:
GWT_COPY_FILES=(.env .npmrc application/config/config-private.php .vscode/launch.json)Opening a worktree as a workspace — {ws}
{} opens the worktree as a plain folder. {ws} instead opens a generated .code-workspace file, which gets you a guaranteed new window with the worktree as a workspace root:
GWT_OPEN_CMD='code -n {ws}'The file lives at ~/.gwt/workspaces/<repo>/<branch>.code-workspace, is reused on later opens (so VS Code remembers that worktree's tabs and layout), and is deleted when gwr or gwclean removes the worktree.
Because the file is saved, closing the window never asks you to "save workspace?" — that prompt belongs to the untitled workspaces you get from adding a folder ad hoc.
Don't reach for
--add. It looks like the right flag and isn't:code --addattaches the folder to whichever window was last active instead of opening a new one,-n --addsilently discards the-n, and--addcannot populate a window it just created. It can also write into a saved.code-workspacefile you already had open.{ws}exists precisely because the VS Code CLI has no "new window and add folder" path.
Update / uninstall
gwt update # upgrade to the latest published version (wraps npx …@latest)
gwt uninstall # remove the ~/.zshrc line + ~/.gwt (asks first; -y to skip)Manual equivalents if ever needed:
- To update: run
npx @bojangles/gwt@latest - To uninstall: delete the
# gwtblock from~/.zshrcandrm -rf ~/.gwt.
Development
Work on a clone with edits live on the next shell (no reinstall per change):
git clone [email protected]:bojangles-m/gwt.git
cd gwt
npm run dev # or: sh scripts/link.sh — symlinks ~/.gwt/gwt.zsh → src/gwt.zshA dev/clone build reports version 0.0.0-dev (the real version is stamped in only by the npx installer).
Manual / clone install
No npx? Clone and source it directly:
git clone [email protected]:bojangles-m/gwt.git ~/.gwt-src
echo 'source ~/.gwt-src/src/gwt.zsh' >> ~/.zshrcPlatform
Primary: macOS - the defaults are mac-flavored (pbcopy, code);
Linux:
- Configure
GWT_OPEN_CMDwith your preferred editor. - Configure
GWT_CLIPBOARD_CMDwith your preferred clipboard tool (xclip -selection clipboardorwl-copy).
License
MIT — see LICENSE.
