Enhanced Git worktree management with interactive features
A Git custom command that makes Git worktrees easier to use with interactive selection, safer destructive flows, repository migration, diagnostics, and compact dashboards.
git-wt uses a bare repository structure
where Git data lives in .bare/ and each branch gets its own sibling
worktree directory.
.bare/ for Git datagit wt init so git wt switch changes directory automaticallywt.beforeadd, wt.afteradd, wt.beforeremove, wt.afterremove)git wt remove --sweepgit wt doctorgit wt statusgit wt list --json or native --porcelain -zgit wt agent-skillHomebrew
brew install ahmedelgabri/tap/git-wt
mise
mise use "github:ahmedelgabri/git-wt"
Nix Flakes
nix run github:ahmedelgabri/git-wt
# Clone with the bare worktree layout
git wt clone https://github.com/user/repo.git
# Migrate an existing repo
git wt migrate
# Create a worktree interactively
git wt add
# Switch between worktrees
cd "$(git wt switch)"
# Or source the shell integration once (bash/zsh/fish) so
# `git wt switch` changes directory by itself
eval "$(git-wt init zsh)"
# Show repository health
git wt doctor
# Show status for all worktrees
git wt status
# Install the agent skill
git wt agent-skill
# Sweep safe cleanup candidates
git wt remove --sweep --dry-run
When you clone with git wt clone, you get:
repo/
├── .bare/ # Git data (bare repository)
├── .git # Points to .bare
└── main/ # Worktree for default branch
git wt list
git wt ls --json
git wt list --porcelain -z
ls is an alias for list. JSON mode emits a stable array of non-bare worktrees and cannot be combined with native list options. Without --json, Git’s options and output pass through unchanged. See the JSON schema.
Migration is experimental. It converts the repository in place with renames, so files keep their modes, extended attributes, and ACLs and no extra disk space is needed. It moves per-worktree Git metadata into the linked worktree, keeps private refs isolated from other worktrees, and verifies config, remotes, refs, index, stashes, and status at the final paths. Every step is journaled: a failed or interrupted migration rolls back, and after a crash the next git wt migrate restores the original layout. Include paths that break on relocation stop migration. Stop other writers first. Removal protects tracked modifications, non-ignored untracked files, and commits without another retained ref; discarding them requires --force with an explicit target. Ignored files, including build output and ignored .env files, do not block removal or cleanup and are deleted with the worktree. Cleanup filters never accept --force, and a gone upstream alone is not a deletion candidate. Remote deletion follows each target’s configured upstream and checks for concurrent changes. Updates respect Git’s configured tag-pruning and pull strategy, including one-off git -c overrides. See migration and removal safety for details and recovery guidance.
Run shell commands around worktree creation and removal. Hooks are configured through Git config, so they can be scoped per repository or globally with --global.
# Validate the repository before creating a worktree
git config --add wt.beforeadd './scripts/check-worktree.sh'
# Copy generated files into each new worktree
git config --add wt.afteradd 'cp ../main/compile_commands.json .'
# Clean up files while the worktree still exists
git config --add wt.beforeremove './scripts/cleanup-worktree.sh'
# Notify another tool after removal is complete
git config --add wt.afterremove 'workspace-registry remove "$GIT_WT_PATH"'
| Hook | When it runs | Working directory | Failure behavior |
|---|---|---|---|
wt.beforeadd |
After add arguments and fetching are complete, immediately before creation | Bare repository root | Prevents worktree creation |
wt.afteradd |
After creation and upstream configuration | New worktree | Leaves the worktree in place and exits non-zero |
wt.beforeremove |
Immediately before removal | Worktree being removed | Preserves the worktree and exits non-zero |
wt.afterremove |
After worktree and branch cleanup | Bare repository root | Removal remains complete and the command exits non-zero |
Hooks inherit the full calling environment, including all GIT_* variables. Unset repository selectors such as GIT_DIR in your hook if you want Git to discover the repository from the hook’s working directory. git-wt adds the lifecycle context through these environment variables:
GIT_WT_EVENT: beforeadd, afteradd, beforeremove, or afterremoveGIT_WT_PATH: absolute worktree pathGIT_WT_BRANCH: branch name, or empty for detached or unresolved casesGIT_WT_BARE_ROOT: absolute bare repository rootEach configured value runs with sh -c. Repeated git config --add values run in order and stop at the first failure for that event; multiline values are supported. Hook output goes to stderr so successful git wt add output remains machine-readable.
Before-hooks are not transactional: the subsequent Git operation can still fail after a hook succeeds, so side effects should be idempotent. After-hook failures cannot roll back an operation that already completed. Removing the current worktree or a locked worktree is rejected before any hook runs; for stale, missing, or prunable worktrees the removal proceeds with the hooks skipped. DEBUG=1 echoes hooks instead of running them.
Hooks apply to git wt add and git wt remove; initial worktrees created by clone or migrate do not trigger git-wt add hooks. Migration suppresses native hooks while creating worktrees and restoring Git metadata.
| Command | Description |
|---|---|
clone <url> |
Clone a repo with the bare worktree structure |
migrate |
Convert an existing repo to the bare worktree structure |
add [options] ... |
Create a new worktree |
remove [worktree] |
Remove worktrees directly or by safe cleanup filters |
doctor |
Run repository diagnostics |
agent-skill |
Install the git-wt agent skill |
init <shell> |
Print shell integration for automatic directory switching |
status |
Show a compact dashboard for linked worktrees |
list / ls |
List worktrees with native Git output or JSON |
switch |
Interactive worktree selection |
update |
Fetch remotes and update the default branch |
Native git worktree commands (lock, unlock, move, prune, repair) are also supported as pass-through commands.
Install an Agent Skills-compatible skill so coding
agents can discover and use git-wt workflows:
git wt agent-skill
By default this writes ~/.agents/skills/git-wt/SKILL.md. Use
git wt agent-skill --dir ~/.claude/skills for a different skill root,
--print to review the skill, or --force to overwrite an existing copy.
Claude Code can create and remove worktrees
automatically during agentic sessions. Configure the WorktreeCreate and
WorktreeRemove hooks in your project or user settings.json to delegate
those operations to git wt, keeping every worktree consistent with the bare
repository layout:
{
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "git wt add \"$(cat /dev/stdin | jq -r '.name')\""
}
]
}
],
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "echo y | git wt rm \"$(cat /dev/stdin | jq -r '.worktree_path')\""
}
]
}
]
}
The hooks receive a JSON payload on stdin. WorktreeCreate reads the .name
field (the branch name) and passes it to git wt add. WorktreeRemove reads
.worktree_path and passes it to git wt rm; the leading echo y | confirms
the interactive prompt non-interactively.
2.48.0 or newer