Git worktree manager with .env syncing and IDE integration
Streamline your git worktree workflow: create isolated branches, sync environment files, install dependencies, give each worktree its own editor color, and open your IDE — all in one command.
npm install -g git-wtreegitwtree <command>
# or
gwt <command>Add this once to your shell rc — it enables gwt switch (jumping between worktrees) and, on zsh + oh-my-zsh, frees the gwt name from the git plugin's alias:
# ~/.zshrc (after sourcing oh-my-zsh) — or ~/.bashrc / config.fish
eval "$(gitwtree shell-init zsh)" # or: bash | fishThen restart your shell (or source ~/.zshrc).
Why
gitwtreeand notgwthere? oh-my-zsh's git plugin aliasesgwt(andgwta,gwtls, …) togit worktree, which shadows this CLI.gitwtreeis never aliased, so the bootstrap always works; the snippet it prints clears those aliases and defines agwtfunction that wins. If you skip this step andgwtrunsgit worktree, that alias is why — rungitwtreedirectly, or add the integration above.
| Command | Description |
|---|---|
gwt add <branch> [--from <base>] |
Create a worktree, sync .env files, and run the setup hook |
gwt pr <number> |
Create a worktree from a GitHub pull request |
gwt rm [branch] [--force] |
Remove a worktree (picker if omitted; guards unsaved changes) |
gwt ls |
List all worktrees |
gwt open [branch] |
Open a worktree in your IDE (picker if omitted) |
gwt switch [query] |
cd to another worktree (needs the shell wrapper) |
gwt shell-init [shell] |
Print the shell function enabling gwt switch |
gwt sync-env [query] [--apply] |
Re-copy .env from main into a worktree (--all for every) |
gwt config |
Show current configuration |
gwt config ide |
Configure your IDE |
gwt config scan-dirs [dirs] |
Set directories to scan for .env files |
gwt config setup [commands...] |
Post-create commands (auto / none / custom) |
gwt config teardown [commands...] |
Pre-remove commands run in the worktree (none to clear) |
gwt config theme [on|off] |
Toggle per-worktree VS Code color + window title |
gwt config statusline [on|off] |
Toggle the Claude Code branch statusline |
gwt help |
Show help |
Creates a git worktree for the given branch, copies .env files from the main repo, and runs the setup hook.
- If the branch doesn't exist, it's created from
HEADby default — use--fromto specify a different base. - If the branch already exists locally, it fetches the latest remote changes and resets to them (handles force-pushes cleanly).
gwt add my-feature # create from HEAD
gwt add my-feature --from production # create from production
gwt add codex/fix-bug # checkout existing branch, reset to remoteCreates a worktree from a GitHub pull request (then runs the same .env sync, setup hook, and theming as gwt add). The worktree lives at <repo>-pr-<number>.
gwt pr 1234 # specific PR
gwt pr # no number → pick from open PRs (arrow-key picker, requires gh)- If
ghis installed, it runsgh pr checkoutinside the worktree — you get the PR's real branch with push tracking (works for forks too), so you can push fixes back. - Otherwise it falls back to
git fetch origin pull/<number>/headinto a localpr-<number>branch (review only).
Opens a worktree in your configured IDE. On first use, a wizard will prompt you to choose your IDE.
gwt open my-feature # substring match on branch or path
gwt open # no argument → arrow-key pickerWorktrees are resolved from git worktree list (the same source as gwt ls): the argument is matched as a substring of the branch or path, and you get a picker when it's ambiguous or omitted. The same resolution powers gwt switch and gwt rm, so a PR worktree (dir repo-pr-<n>, different branch) is found whether you pass its branch, pr-<n>, or pick it.
To reconfigure your IDE at any time:
gwt config idegwt switch [query] changes your shell's directory to another worktree. Because a binary can't change its parent shell's working directory, this needs a small shell function. Add it to your shell rc once:
# ~/.zshrc or ~/.bashrc
eval "$(gwt shell-init zsh)" # or: bash | fishThen:
gwt switch my-feature # cd to the worktree whose branch matches "my-feature"
gwt switch # no query → arrow-key picker
gwt sw my-feature # aliasquery is a substring match on the branch name. If it matches exactly one worktree you go straight there; if it's ambiguous or omitted, you get an arrow-key picker (same style as the rest of the prompts). (gwt path [query] is the underlying primitive the wrapper calls — it resolves the worktree and writes the path back to the wrapper.)
By default, gwt add recursively scans the repo for .env* files (excluding node_modules, .git, dist, etc.) and copies them into the new worktree.
To restrict scanning to specific directories:
gwt config scan-dirs apps/api,apps/webTo reset back to auto scan:
gwt config scan-dirs --resetgwt add only copies .env files at creation time. When you refresh them on your main checkout (e.g. after pulling a new database), gwt sync-env re-copies them into existing worktrees. The main worktree is always the source (detected automatically, so it works from any worktree); it's never a target.
It is a dry run by default — it lists, per worktree, which files would be copied and which would overwrite an existing one — and only writes with --apply:
gwt sync-env # pick a worktree, preview the changes
gwt sync-env my-feature # preview for the worktree matching "my-feature"
gwt sync-env my-feature --apply # actually copy into that worktree
gwt sync-env --all # preview across every secondary worktree
gwt sync-env --all --apply # copy into all of themgwt add runs a setup hook after creating the worktree, and gwt rm runs a teardown hook before removing it. Both run inside the worktree.
Setup defaults to auto: if a package.json is present it runs <package-manager> install (plus <pm> run prepare when that script exists); otherwise it does nothing — so non-Node repos stay untouched. Override it with your own commands for any stack:
gwt config setup # show current value
gwt config setup "bundle install" # Ruby
gwt config setup "go mod download" "make dev" # multiple commands, in order
gwt config setup none # do nothing
gwt config setup auto # back to auto-detectionTeardown is empty by default. Use it to release resources tied to a worktree (databases, containers, ports) before it's deleted. If a teardown command fails, removal is aborted unless you pass --force:
gwt config teardown "docker compose down"
gwt config teardown none # cleargwt rm <branch> refuses to remove a worktree that has uncommitted changes, untracked files, or unpushed commits — to avoid losing work. Re-run with --force to remove anyway:
gwt rm my-feature
gwt rm my-feature --forceTo make parallel windows easy to tell apart, gwt add gives each worktree its own visual identity:
- A deterministic color (derived from the branch name) is applied to the VS Code / Cursor title bar and activity bar, plus a worktree-aware
window.title. Written to the worktree's.vscode/settings.jsonand merged into any existing settings without dropping your keys or comments. - A branch statusline is written to
.claude/settings.local.jsonso each Claude Code session shows its branch.
Both files are kept out of git status automatically — skip-worktree when the file is tracked, the worktree's local info/exclude otherwise. Your shared .gitignore is never touched.
Toggle either feature (both on by default):
gwt config theme off # disable color + window title
gwt config statusline off # disable the Claude statuslineWorktrees are created as siblings of your repo directory:
~/projects/
myrepo/ ← main repo
myrepo-my-feature ← worktree created by gwt
VS Code, Cursor, Zed, WebStorm, IntelliJ IDEA, PyCharm, GoLand, Vim, Neovim, Sublime Text — or any custom IDE via the "Other" option in the wizard.
- Node.js >= 18
- Git >= 2.5
MIT