Per-task git worktrees, automatic tmux session-per-project, never clobber another session.
ship feature-x # creates worktree off latest origin/main, opens it in tmux
ships # list every worktree of the current repo
land # remove the current worktree (branch + tmux session too)If you work on multiple branches in parallel — especially with AI coding agents running long-running tasks — sharing one git checkout across sessions is a foot-gun. A git checkout or git pull in one terminal silently yanks state out from under another, and gh pr merge --delete-branch can fail because some other worktree has main checked out.
ship-worktree solves that by:
- One worktree per task. Each
ship NAMEcreates a fresh worktree at<repo>/.worktrees/NAMEbranched from the latestorigin/main(fetched fresh every time — fails loudly if the fetch fails, so you never branch off stale state). - Per-project tmux sessions. Session names are
<repo>-<NAME>, soship claudeinacmeandship claudeinwidgetsopen two independent sessions. - Safe cleanup.
landremoves the worktree, deletes the branch, kills the tmux session. Run with no args inside a worktree to clean up the one you're in.
git clone https://github.com/pallaoro/ship-worktree.git ~/.ship-worktree
echo 'export PATH="$HOME/.ship-worktree/bin:$PATH"' >> ~/.zshrc
exec zshNot yet published. Coming soon.
| Command | What it does |
|---|---|
ship |
Create or attach worktree claude |
ship NAME |
Create or attach worktree NAME |
ships |
List every worktree (git worktree list) |
land |
Remove the current worktree (if you're in one) — otherwise prompt |
land NAME |
Remove worktree NAME |
When ship finds the worktree directory already exists (e.g. previous session wasn't landed cleanly), it offers:
- use it — attach to whatever branch is currently checked out there
- replace — wipe the worktree + branch, recreate fresh off
origin/main - cancel
All env vars are optional. Set them in your shell init.
| Variable | Default | Meaning |
|---|---|---|
SHIP_SPAWN |
bash |
Command to launch inside the new tmux session. Set to claude (or any other shell-agent CLI) to drop straight into it. |
SHIP_BASE_BRANCH |
main |
Branch new worktrees are forked from. |
SHIP_REMOTE |
origin |
Remote ship fetches the base branch from. |
SHIP_WORKTREE_DIR |
.worktrees |
Path (relative to the repo root) where worktrees are created. |
Example: drop straight into Claude Code, store worktrees under .claude/worktrees:
export SHIP_SPAWN=claude
export SHIP_WORKTREE_DIR=.claude/worktreesgit(≥ 2.20 forgit worktreeergonomics)tmuxbash≥ 4
Most "merge this branch" flows do something like:
git checkout main
git merge --ff feature
git pushThat's the operation that silently yanks state out from another worktree session if they happen to share a working tree. With ship, the recommended merge flow is GitHub-side only:
# inside the feature worktree
git push -u origin HEAD
gh pr create --fill
gh pr merge --squash # NO --delete-branch — gh tries to switch your local
# checkout back to main, which fails when main is
# checked out in another worktree.
land # then clean up locallyThat keeps every concurrent ship session safe.
MIT — see LICENSE.