Skip to content

Worktrees

stax has two related worktree stories:

  • Repo-wide awarenessrestack, sync, cascade, and metadata operations run in the right linked checkout automatically. See Multi-worktree behavior.
  • The st worktree command family — create, enter, inspect, and clean up worktree lanes. This page is the canonical guide.

For the parallel AI workflow built on top, see AI worktree lanes.

Quick start

# Open the interactive dashboard
st wt

# Create a fresh lane with a random name
st wt c

# Create or reuse a named lane
st wt c payments-api

# Check out a fetched remote branch into a worktree
st wt c origin/payments-api

# Start a new lane from an explicit base
st wt c payments-api --from main

# Jump back into a lane
st wt go payments-api

# Make this lane the branch checked out in the main worktree
st wt promote

# Inventory
st wt ls
st wt ll

# Print the absolute path (scripting)
st wt path payments-api

# Remove / clean up
st wt rm payments-api
st wt cleanup --dry-run
st wt prune
st wt rs

How create and go resolve targets

st wt c [name] is convenience-first:

  • no name → random two-word lane slug
  • name matches an existing worktree → reuse it
  • name matches an existing branch → create a worktree for that branch
  • name matches a fetched branch on the configured remote → create a local tracking branch and worktree
  • otherwise → create a new branch and a new worktree

st wt go [name] only opens existing worktrees. With no name, it opens a picker.

Selectors accepted by go, path, rm, and reuse paths in create:

  • worktree name
  • full branch name
  • unique branch suffix (e.g. payments-api matching cesar/payments-api)
  • fetched remote branch name or configured-remote-qualified name (e.g. payments-api or origin/payments-api)
  • absolute worktree path

Base branch rules for new lanes

  • fetched remote branches keep their remote tip and upstream tracking branch
  • --from <branch> explicitly sets the base
  • otherwise if the current branch is tracked by stax → new lane stacks on current
  • otherwise → new lane starts from trunk

--pick chooses an existing local branch interactively. --name <label> lets the worktree directory label differ from the branch name. --no-verify skips worktree hooks for that command.

Command map

Command Aliases What it does
st worktree st wt Interactive dashboard (TTY) or worktree help
st wt c [name] st worktree create, st wtc Create or reuse a lane; supports --from, --pick, --name, --agent, --run, --tmux, --no-verify
st lane [name] [prompt] Fast AI-lane entrypoint (see AI lanes)
st wt go [name] st worktree go, st wtgo Enter an existing worktree; supports --agent, --run, --tmux, --no-verify
st wt ls st worktree list, st w, st wtls Compact NAME / BRANCH / PATH inventory (--json)
st wt ll st worktree ll, st wtll Rich status view with managed/dirty/rebase/conflict/marker/prunable state (--json)
st wt path <name> st worktree path <name> Print absolute path
st wt rm [name] st worktree remove, st wtrm Remove one worktree (wt rm removes current); supports -f/--force, --delete-branch
st wt promote st worktree promote Retire the current linked worktree and check its branch out in the main worktree
st wt prune st worktree prune, st wtprune Remove stale git worktree bookkeeping only
st wt cleanup st worktree cleanup, st wt clean Prune + bulk-remove safe detached/merged lanes (--dry-run, --yes, -f)
st wt restack st worktree restack, st wtrs, st wt rs Restack all stax-managed worktrees

Launch other tools inside a lane

st wt c ui-polish  --run "cursor ."
st wt c review-pass --agent codex --tmux -- "address the open PR comments"
st wt go review-pass --agent codex --tmux
  • --agent supports claude, codex, gemini, opencode
  • --model requires --agent
  • --run and --agent are mutually exclusive
  • anything after -- is passed through to the agent/command
  • --tmux creates/reuses a session named after the worktree unless --tmux-session overrides

If you run create / go without shell integration and without a launcher, stax prints the cd command to copy.

Managed vs unmanaged

st wt ls shows every Git worktree, including ones created outside stax. The important distinction is whether the branch has stax metadata:

  • new branches created by st wt c foomanaged
  • existing tracked branches opened as lanes → stay managed
  • fetched remote branches opened as local tracking branches → unmanaged until st branch track
  • existing plain Git branches opened as worktrees → unmanaged until st branch track

Managed lanes behave like first-class stax branches: they show in st ls, participate in restack/sync/undo, and are targeted by st wt restack. Unmanaged lanes still show up in ls, ll, go, rm, and prune, but stax keeps history-rewriting operations conservative.

Dashboard

Run st wt in an interactive terminal.

  • Left pane: all Git worktrees, including unmanaged
  • Right pane: branch, base, path, status, tmux session details
Key Action
Enter Attach / switch to the tmux session for the selected worktree
c Create a lane and open it in tmux
d Remove the selected worktree
R Restack all stax-managed worktrees
? Show help
q / Esc Quit

The dashboard is a control plane, not an embedded shell. The stack/patch TUI is documented separately in Interactive TUI.

Shell integration

st setup is the one-shot onboarding command for shell integration, AI skills, and auth:

st setup                    # full interactive onboarding
st setup --yes              # accept defaults, install skills, import auth from gh
st setup --install-skills   # install shell integration + skills without prompting
st setup --skip-skills      # install shell integration without the skills prompt
st setup --auth-from-gh     # install shell integration and import auth from gh
st setup --skip-auth        # install shell integration without auth onboarding
st setup --print            # print the snippet for manual install

After installation:

  • st wt c ... moves the parent shell into the new lane
  • st wt go ... moves the parent shell into the selected lane
  • st wt promote moves the parent shell to the main worktree after a successful handoff
  • st lane ... moves the parent shell into the selected lane
  • st wt rm (no arg) can relocate the shell before removing the current worktree
  • sw <name> becomes a quick alias for st wt go <name>

Supports bash, zsh, and fish.

Windows

On Windows, worktree commands work but the parent shell cannot auto-cd, sw is unavailable, and tmux integration is not supported. Manually cd to the printed path after st wt c, st wt go, or st wt promote. See Windows notes.

Promote a lane to the main worktree

Run st wt promote inside a linked worktree when you want to continue that branch in the repository's main worktree. Stax detaches the linked checkout, checks the same branch out in the main worktree, then removes or parks the old lane according to the existing warm-slot configuration.

Promotion preserves the branch, commits, upstream, PR linkage, and stax metadata. It does not merge, delete, untrack, or automatically stash anything. Both the current lane and main worktree must be clean, unlocked, and free of an in-progress merge, rebase, or conflicts. If switching fails, Stax restores the original checkouts and reports any rollback problem. Git may report a removal failure only after it has already unregistered the old lane; in that case Stax keeps the completed handoff in the main worktree, removes a dangling .git file when safe, and tells you to inspect any remaining files in the old lane path.

With shell integration, the current shell moves to the main worktree only after the handoff succeeds. Without it, Stax prints the main worktree path and a copyable cd command.

Cleanup and safety

Command What it does
st wt rm [name] Remove one live worktree (no name = current)
st wt rm --delete-branch Also delete the branch and its stax metadata
st wt promote Keep the branch and make it the main-worktree checkout
st wt prune Clear stale git worktree bookkeeping only — never removes a live directory
st wt cleanup Prune bookkeeping, then bulk-remove safe candidates
st wt rs Restack all stax-managed lanes

cleanup is intentionally conservative. It only targets:

  • detached worktrees
  • stax-managed worktrees whose branches are already merged into trunk

It skips worktrees that are current, locked, mid-rebase, mid-merge, in conflict, or dirty (unless -f/--force). Use --dry-run to preview and --yes for non-interactive runs.

Location, config, and hooks

By default, managed worktrees live outside the repository at:

~/.stax/worktrees/<repo>

Override in ~/.config/stax/config.toml, or set shared project overrides in repo-root stax.toml:

[worktree]
# root_dir = ""             # default external root
# root_dir = ".worktrees"   # keep worktrees inside the repo
# root_dir = ".."           # keep lanes beside the main checkout

# Warm-slot recycling: a removed worktree is parked and reused by the next lane
# instead of being deleted, keeping built gitignored deps on disk.
reuse_slots = true        # default; set false to always cold-create / real-remove

# Maximum number of idle warm slots to keep parked.
max_idle_slots = 4

# Optional: command run (non-fatally) inside a slot after it is adopted, to
# re-sync deps (e.g. "pnpm install", "uv sync").
# reconcile = "pnpm install"

[worktree.hooks]
post_create = ""   # blocking hook before launch
post_start  = ""   # background hook after creation
post_go     = ""   # background hook after entering an existing worktree
pre_remove  = ""   # blocking hook before removal
post_remove = ""   # background hook after removal
  • Relative root_dir values resolve from the main repo root. Configure them globally in ~/.config/stax/config.toml to reuse the same layout across repos.
  • To keep a trunk clone and its lanes as siblings, place the trunk clone one level below a plain container directory and set root_dir = ".." globally:
my-repo/
  development/  # trunk clone
  feature-x/    # stax-managed lane
  feature-y/    # stax-managed lane
  • Roots inside the repository, such as .worktrees, are added to .gitignore automatically. Roots outside the repository, including .., leave .gitignore untouched.
  • root_dir cannot resolve to the main repo directory itself. Use a subdirectory or an external directory.
  • Warm-slot recycling (default): removing a clean, merged-equivalent worktree parks it instead of deleting it — stax moves it off its lane branch, resets it hard to trunk, and runs git clean -fd. That never uses -x, so gitignored dependency directories (node_modules, .venv, vendor, …) survive on disk.
  • The next create / lane adopts an idle parked slot instead of a cold git worktree add: it switches the slot to the fresh lane branch, resets to the base, cleans untracked files, and runs the reconcile hook. Adopting reuses the same directory, so the built deps are already there.
  • reconcile re-syncs deps after adoption (e.g. pnpm install, uv sync). It is non-fatal — a missing or failing command only warns and never fails the create.
  • max_idle_slots caps how many idle slots are kept; parking beyond the cap falls back to a real removal, and worktree cleanup evicts the oldest excess slots.
  • A --force (or confirmed) dirty removal never parks — it always does a real git worktree remove.
  • Set reuse_slots = false to opt out entirely: cold git worktree add on create, real git worktree remove on removal, and no pool manifest.
  • post_create and pre_remove are blocking; post_start, post_go, post_remove run in the background.
  • --no-verify on create / go skips hooks for that command.

For the full config surface, see Configuration.