| name | git-work |
| description | Opinionated git worktree helper that makes worktrees work like branches. |
git-work
git-work is a CLI that wraps git worktree into a branch-per-directory workflow. Each branch lives in its own subdirectory. Context-switching is cd, not git checkout.
Layout
my-project/
├── .bare/ # bare git repo shared by all worktrees
├── .git # pointer file: gitdir: ./.bare
├── main/ # worktree for branch main
├── feature-login/ # worktree for branch feature/login
└── fix-typo/ # worktree for branch fix-typo
/ in branch names is replaced with - for the directory name. The real branch name is always used in git commands.
Shell wrapper (gw)
The gw shell function wraps git-work and automatically cds into any path printed to stdout. Set it up once per shell:
eval "$(git-work activate bash)"
git-work activate fish | source
Use gw <command> in interactive shells. Call git-work <command> directly in scripts (no cd side-effect).
stdout / stderr contract
stdout is machine-readable only — directory paths. All human-readable messages, warnings, and errors go to stderr. This is what allows gw to cd reliably.
Commands
Start a project
gw clone git@github.com:org/repo.git
gw clone https://github.com/org/repo.git my-project
cd ~/projects/my-repo
gw init
clone creates <dir>/.bare (bare repo), writes <dir>/.git pointer, and adds a worktree for HEAD.
init moves .git/ → .bare/, moves working files into <root>/<current-branch>/, wires up worktree linkage, and stashes/restores uncommitted changes. Safe to re-run: repairs state if the layout already exists but is broken.
Navigate between branches
gw co main
gw co login
gw co -
gw co feature/remote
gw co -b feature/new
Fuzzy matching order:
- Exact directory name match
- Substring match (
login → feature-login)
- Jaro-Winkler similarity, threshold 0.85
Ambiguous matches (multiple candidates pass) exit non-zero and list candidates on stderr.
gw co <branch> without -b will auto-create a worktree only if a remote branch with that exact name exists. Otherwise it errors.
Remove a worktree
gw rm feature-login
gw rm login
gw rm --yes feature-login
gw rm --force old-branch
Removes the worktree directory and runs git branch -d (or -D with --force). Refuses to remove HEAD branch without --force. If you run gw rm from inside the worktree being removed, the tool prints the HEAD worktree path so the shell wrapper moves you there.
Sync with remote
gw sync
gw sync --dry-run
gw sync --force
HEAD branch is never pruned.
List worktrees
gw ls
Prints a table: directory name, branch name, * for current worktree.
Hooks (mise integration)
On every new worktree creation (gw co -b or remote auto-create), git-work fires a post-create hook:
- If the source worktree is
mise-trusted, runs mise trust in the new worktree.
- Runs
mise run worktree-setup in the new worktree if the task exists.
If the task does not exist, a warning is printed and execution continues. If the task exits non-zero, worktree creation is rolled back.
Configure via git config (stored in .bare):
git -C .bare config git-work.hooks.mise.trust false
git -C .bare config git-work.hooks.mise.task bootstrap
git -C .bare config git-work.hooks.mise.task ""
Common patterns for agents
Determine current worktree and project root:
pwd
git -C .bare rev-parse --show-toplevel
The project root is the parent of the current worktree directory. .bare/ always lives at the project root.
Check what worktrees exist:
gw ls
git -C ../.bare worktree list
Create a branch and immediately work in it:
gw co -b feature/my-task
Clean up after merging:
gw rm feature/my-task --yes
gw sync
Check if inside a git-work project:
A git-work project has .bare/ at its root (one level up from the current worktree). If ../.bare is a bare git repo, you are inside a git-work layout.
Error cases
| Situation | Behavior |
|---|
gw co <branch> — no worktree, no remote | exits non-zero, error on stderr |
gw co <branch> — multiple fuzzy matches | exits non-zero, candidates on stderr |
gw co -b <branch> — worktree already exists | exits non-zero |
gw rm <head-branch> without --force | exits non-zero |
| mise task exits non-zero | worktree creation rolled back, exits non-zero |
| Not inside a git-work project | exits non-zero |