A fast, small CLI for managing git worktrees: create, navigate, list, and clean
up worktrees with an interactive picker, automatic project-config copying, and
a shell wrapper that cds your parent shell.
Website: git-wt.paulie.app
- Create worktrees with one command, branched from any ref
- Navigate between worktrees with an arrow-key picker (or by branch name)
- Remove one or many with a multi-select picker; safety check on the main worktree; bounce-and-delete when you remove the worktree you're in
- List all worktrees with the current marker, sorted by last-modified
- Clean worktrees whose local branch is gone or whose upstream was deleted
- Project config copy controlled by
.git-wt-copy-files(gitignore-style) - Shell integration via a generated function that talks to the binary
over a configurable file descriptor — no
eval-quoting hazards - Branch names with slashes become nested directories (
paul/feature→paul/feature/) - Built with Go, single static binary, zero runtime dependencies (besides git)
From source (requires Go 1.22+):
git clone https://github.com/shhac/git-wt.git && cd git-wt
go build -o git-wt ./cmd/git-wt
mv git-wt ~/.local/bin/ # or anywhere on $PATHPre-built binaries are published on Releases (Linux + macOS).
Requirements: Git. Go is only needed to build from source.
Add to your shell config:
echo 'eval "$(git-wt alias gwt)"' >> ~/.zshrc # or ~/.bashrcNow gwt go/gwt new/gwt rm change directory in your parent shell;
everything else passes through unchanged.
gwt new feature-branch # creates <repo>/.gwt/feature-branch and cds in
gwt new paul/auth # nested: <repo>/.gwt/paul/auth
gwt go # interactive picker
gwt go feature-branch # direct nav
gwt rm # multi-select picker
gwt rm feature-branch --delete-branch
gwt list # or `gwt ls`
gwt clean --dry-run # show what would be removedThe default trees directory is <repo>/.gwt/ — you'll likely want to add
.gwt/ to your .gitignore (the tool prints a one-line hint when it
isn't). Override per-invocation with --parent-dir <path>.
| Command | Description |
|---|---|
new <branch> |
Create new worktree with branch. Flags: --from <ref>, --parent-dir <path>, --no-copy, --copy-file-config <path>. |
rm [branch...] |
Remove worktree(s); interactive multi-select if no args. Flags: --keep-branch, --delete-branch, --force. |
go [branch] |
Navigate to a worktree. Suffix match works (auth → paul/auth if unique). |
list (ls) |
List worktrees. The first column is the branch, second is the location, third is mtime. |
clean |
Remove worktrees whose branch is gone (locally or upstream). Flags: --dry-run, --no-fetch, --orphaned-only, --upstream-gone-only. |
alias <name> |
Print a shell function wrapper. Flags: --fd <N>, --plain, -n, --debug. |
-h, --help— show help-v, --version— print version--debug— verbose diagnostics--plain— no color, minimal formatting (also honorsNO_COLOR)-n, --non-interactive— disable prompts (auto-detected when stdin isn't a TTY)--fd <N>— file descriptor for the wrapper protocol (default3, range3-9)
↑/↓(ork/j) — navigate;Home/End(org/G) — jumpEnter— select / confirmSpace— toggle (multi-select);a— toggle all (multi-select)Esc,Ctrl-C,q— cancel (silent exit, no navigation)
By default git-wt creates worktrees inside the main repo at <repo>/.gwt/:
my-repo/
├── .git/
├── .gwt/ # default trees dir (add to .gitignore)
│ ├── feature-a/
│ └── paul/
│ └── feature-auth/
└── ... (your code)
Override with --parent-dir:
gwt new feature-x --parent-dir ../sibling-trees # outside the repo
gwt new feature-y --parent-dir ~/scratch # absolute pathWhen you run gwt new, the tool copies project-local files from the main
worktree into the new one. The list is controlled by
<repo>/.git-wt-copy-files (or --copy-file-config <path>). Format is
gitignore-ish: # comments, glob patterns, !-prefixed exclusions. When
the file is absent, a built-in default copies .env*, .claude/,
CLAUDE.local.md, .ai-cache/. See .git-wt-copy-files.example in the
repo for the schema.
gwt go/gwt new need to change the parent shell's cwd. The wrapper
opens fd N (default 3); the binary writes the destination path to fd N;
the wrapper cds the captured value. If the binary is run directly (no
wrapper), it falls back to printing the path on stdout with a copy/paste
hint on stderr — you can use cd "$(git-wt go feature-x)" if you prefer.
go build -o git-wt ./cmd/git-wt # build
go test -race ./... # all tests (~95 unit + 17 e2e)
./git-wt --helpE2E tests run against fresh repos under /tmp/ (Go's t.TempDir()),
never the project repo. CI runs the suite on Linux + macOS.
- Fork the repository
- Create a feature branch (
gwt new my-feature) - Commit your changes
- Push to the branch
- Open a Pull Request
Please ensure go test -race ./... passes and go vet ./... is clean.
MIT — see LICENSE.