Introducing git-wt: Worktrees Simplified
In my previous post, I explained why the bare repo pattern makes git worktrees actually pleasant to use. But setting it up manually requires 10+ commands. And the day-to-day friction - upstream tracking, orphaned branches, missing fetches - adds up.
So I built git-wt to handle the tedious parts.
How custom git commands work: Any executable named
git-<cmd>in your$PATHbecomesgit cmd. Git’s own commands work this way - runls $(git --exec-path)to see them all.
The Problem with Native Worktrees
Even with the bare repo pattern, native git worktree has friction:
No upstream tracking. Create a worktree from origin/feature, your first push fails with “no upstream configured.” Every single time.
Orphaned branches. Remove a worktree, the local branch stays. Six months later, git branch scrolls for pages.
Manual fetching. Forget to fetch before creating a worktree? You’re working with stale refs.
Setup dance. Want the bare repo structure? Memorize 10 commands or dig through notes.
What git-wt Does
| Command | What it does |
|---|---|
clone | Sets up bare structure automatically |
migrate | Converts existing repos (preserves all your work) |
add | Creates worktrees with auto-fetch and upstream |
remove | Removes worktree AND cleans up the local branch |
destroy | Nuclear: worktree + local branch + remote branch |
update | Fetches everything, fast-forwards default branch |
switch | Interactive picker (fzf) to jump between worktrees |
Everything else passes through to git worktree - list, prune, lock all work.
Clone with Structure
git wt clone https://github.com/user/repo.git
That’s it. Three things in your folder: .bare/, .git, and a main/
worktree. Compare to the 10 manual commands from the previous post.
The result:
my-project/
├── .bare/ # all git data lives here
├── .git # pointer file
└── main/ # worktree: main branch
Add a Worktree (Interactive Mode)
git wt add
Fetches first. Shows branches in fzf with a commit preview pane. Pick the branch, worktree created, upstream configured. Done.
Add a Worktree (Direct Mode)
git wt add feature-auth origin/feature-auth # existing branch
git wt add -b my-feature my-feature # new branch
Both modes auto-configure upstream tracking. Your first push just works.
Clean Up Properly
git wt remove feature-payments
Removes the worktree directory AND deletes the local branch. No more orphans.
Go Nuclear
git wt destroy old-experiment
Removes worktree, local branch, AND remote branch. Shows you exactly what will be deleted. Requires typing the branch name to confirm - no accidental destruction.
Migrate Existing Repos
⚠️ Experimental: This command restructures your repository. It has worked on repos I’ve tested, but as a destructive action, proceed with caution until more users have validated it.
Already have a repo cloned the normal way?
cd my-repo
git wt migrate
Converts your existing clone to the bare structure. Preserves all your branches, stashes, and history. Your current working directory becomes a worktree.
Update Everything
git wt update
Fetches all remotes, fast-forwards the default branch (main/master) if possible. Run this periodically to keep your worktrees current.
Switch Between Worktrees
git wt switch
Interactive fzf picker showing all worktrees. Select one and it prints the path. Pair with cd $(git wt switch) or bind to a shell function.
you can add this to your .bashrc or .zshrc:
# Jump to a worktree
wt() {
local dir
dir=$(git wt switch)
[[ -n "$dir" ]] && cd "$dir"
}
Now wt gives you an interactive picker that drops you into the selected worktree.
Installation
Homebrew
brew tap ahmedelgabri/git-wt
brew install git-wt
Nix
nix run github:ahmedelgabri/git-wt
Or add to your flake:
{
inputs.git-wt.url = "github:ahmedelgabri/git-wt";
}
Then use inputs.git-wt.packages.${system}.default.
Manual
Copy the git-wt script somewhere in your $PATH. It’s a single bash file with no dependencies beyond git and standard unix tools (fzf for interactive mode).
Shell Completion
Includes completions for zsh, bash, and fish. (installed automatically with Homebrew and Nix)
Why a Wrapper?
The bare repo pattern is powerful but has sharp edges. git-wt smooths them:
- Correct setup by default:
cloneandmigratehandle all the config - Upstream tracking:
addsets it up automatically - Clean deletion:
removeanddestroyprevent orphans - Discoverability: Commands match mental model (“add a worktree”, not “add a worktree and also set upstream and also remember to fetch first”)
The goal is making worktrees feel as natural as git checkout - but with full isolation.
Try It
# New project
git wt clone https://github.com/some/repo.git
cd repo/main
# Existing project
cd existing-repo
git wt migrate
Work like this for a week. The friction disappears.
Links:
- git-wt on GitHub
- Git Worktrees Done Right - the bare repo pattern explained