Git Worktree Workflow
This setup ships a worktree helper command called ,w.
- Source:
home/exact_bin/executable_,w - Helpers:
home/exact_bin/utils/,w/ - GitHub flow wrapper:
home/exact_bin/executable_,gh-worktree
The goal is to make branch isolation + review + context switching cheap.
Preconditions
- You are inside a git repository.
,wis installed and onPATH.
Subcommands
add — create a worktree
,w add feat/my-change main
,w add origin/some-branch
,w add -q feat/quick
<branch_name>can be a local branch,origin/<branch>, oruser/<branch>.[base_branch]is optional; defaults to the current branch.- When
[base_branch]is a plain branch name (likemain), base ref resolution prefersorigin/<base>thenupstream/<base>before falling back to a local<base>branch. This avoids creating worktrees from stale local base branches. -q/--quietsuppresses informational output.- Adds a zoxide entry for the new path when zoxide is installed.
prs — check out PRs into worktrees
,w prs 12345
,w prs 12345 12346
,w prs --focus 12345
,w prs --awaiting
,w prs "label:bug"
- No arguments opens an interactive fzf multi-select picker.
--focusswitches/attaches to the created worktree's tmux session.--awaitinglists PRs awaiting your review (last 7 days by default; tune withCOMMA_W_AWAITING_DAYS).- Automatically adds contributor forks as remotes when needed.
- First-party PRs use a plain branch name (
feat/foo); third-party forks use<remote>__<branch>and write per-worktree push routing sogit pushtargets the fork. - Upstream tracking prefers canonical refs (
origin/<branch>, thenupstream/<branch>) for first-party remotes.
issue — create/reuse an issue worktree
,w issue 12345
,w issue --focus 12345
,w issue --branch my-fix 12345
,w issue https://github.com/elastic/kibana/issues/12345
- Reuses an existing issue worktree (metadata or issue-number heuristic) when one exists.
- Prompts for a branch name when creating a new worktree; branch is created as
<name>-<issue_number>. -b/--branchprovides the branch name non-interactively.--focusswitches/attaches to the worktree's tmux session.- If the target branch already exists locally but has no unique commits relative to the repo default branch, it is fast-forwarded to the latest default branch before worktree creation (prevents “stale branch pointer” worktrees from starting at months-old commits).
- If you already created a matching branch manually (via
,w add), entering that exact branch name links the issue metadata without renaming.
ls — list worktrees
,w ls
,w ls --long
,w ls --dirty
,w ls --porcelain
,w ls --sort path --no-header
- Default table: CUR, BRANCH, PATH, UPSTREAM, TMUX, STATE.
--longadds AHEAD/BEHIND columns.--dirtycomputes and shows dirty state (slow in large repos).--porcelainprints rawgit worktree list --porcelainoutput.--selectableprintsbranch<TAB>pathfor non-detached, non-locked worktrees (used by other subcommands).--full-pathdisables path shortening.--sort branch|pathcontrols row order.
switch — interactive worktree picker
,w switch
,w switch kibana
- Opens an fzf picker over selectable worktrees.
- If the argument exactly matches a branch or path, switches directly without opening fzf.
- Creates a tmux session for the worktree if one does not exist.
open — focus a worktree by name/path
,w open feat/my-change
,w open /path/to/worktree
- Accepts a branch name or absolute path.
- Creates a tmux session if needed and switches/attaches to it.
mv — move/rename a worktree
,w mv old-branch new-branch
,w mv --keep-path old-branch new-branch
,w mv --path ~/work/repo/new-dir old-branch new-branch
,w mv --focus old-branch new-branch
- Renames the branch and moves the worktree directory as a unit.
- Updates tmux session name and zoxide entries.
--keep-pathrenames only the branch (directory stays).--path <dir>overrides the destination directory.--focusswitches to the resulting tmux session after the move.
remove — clean up worktrees
,w remove
,w remove --paths /path/to/wt1 /path/to/wt2
,w remove --tmux-notify
- Interactive fzf multi-select by default.
- For each selected worktree: removes directory, deletes local branch, removes unused fork remotes, cleans empty parent dirs, purges from zoxide, kills tmux session.
- Protects the repository's actual default branch (detected from remote HEAD).
.DS_Storeis treated as ignorable so Finder metadata does not keep empty dirs alive.- Leftover files in otherwise-empty parents are bagged to
../.bag/worktree_remove/<wrapper>/<timestamp>/.... --pathsskips the picker and also allows removing detached worktrees.--tmux-notifyshows progress via tmux messages (useful from scripts).
prune — clean stale metadata
,w prune
,w prune --apply
,w prune --apply --all
- Default is dry-run (shows what would be cleaned).
--applyrunsgit worktree pruneand kills stale tmux sessions.--allconsiders tmux sessions across all repos, not just the current one.
doctor — check dependencies and state
,w doctor
- Checks for
git,fzf,gh,tmux,zoxide,bat. - Reports stale worktree paths and stale tmux sessions.
- Suggests
,w prune --applywhen issues are found.
GitHub Picker Integration
The fzf-based GitHub picker (prefix + G) calls ,gh-worktree for repo/bootstrap routing, and ,gh-worktree delegates to ,w for worktree creation/session management.
Reusable GitHub worktree flow
Use ,gh-worktree when you want the same repo/bootstrap + worktree behavior outside tmux pickers (for example, from agentic sessions):
,gh-worktree pr elastic/kibana 12345 --focus
,gh-worktree issue elastic/kibana 12345 --focus
,gh-worktree issue elastic/kibana 12345 --branch chore/fix-widget
,gh-worktree pr elastic/kibana 12345 --create-bg --quiet
- Resolves the local checkout root from a repo hint (
--repo-path) or the conventional wrapper path. - Bootstraps missing repos with
,gh-tforkunless--no-bootstrapis set. - Delegates PR/issue branch naming and worktree creation to
,w prs/,w issue. - In non-interactive contexts, agents should use
,gh-worktree issue <owner/repo> <issue_number> --branch <branch-base-name>for GitHub issues (otherwise,w issuewould prompt for branch input). - Supports
--print-rootto return the resolved repo root without creating a worktree.--print-rootresolves and exits before the interactive-branch guard, so an issue precheck (--print-rootwith no--branch) works non-interactively — the guard only applies when actually creating an issue worktree.
PR shortcuts (inside the GitHub picker):
enter(no marks): create/switch to the PR worktree and focus its tmux session via,gh-worktree pr ... --focus. Exits the picker.enter(items marked): batch worktree creation for all marked items (same asctrl-t). Stays in the picker.alt-b: same as single enter plus open the PR in Octo review in a new tmux window.alt-o: open the PR/issue in the browser.alt-y: copy the PR/issue URL to the clipboard.ctrl-t: explicit batch worktree creation for marked items.
Issue shortcuts:
enter(no marks): create/switch to the issue worktree and focus its tmux session via,gh-worktree issue ... --focus. Presents an interactive branch name prompt if the worktree doesn't exist yet.
Shared behavior:
- If the repo does not exist locally, actions bootstrap it first with
,gh-tfork <owner/repo>. - Bootstrap location:
elastic/*in~/work/<repo>, everything else in~/code/<repo>. - The picker shows
◆markers for PRs/issues that already have local worktrees, review status badges (approved,changes requested, `` pending), and CI status badges (●green/red/yellow for success/failure/pending).
Verification
,w ls
,w doctor
Confirm the expected worktree exists and tmux session switching works.
Rollback / Undo
Remove a worktree:
,w remove
If metadata is stale:
,w prune --apply