wt step
Run individual operations. The building blocks of wt merge — commit, squash, rebase, push — plus standalone utilities.
Examples
Commit with LLM-generated message:
wt step commit
Manual merge workflow with review between steps:
wt step commit
wt step squash
wt step rebase
wt step pushOperations
commit— Stage and commit with LLM-generated messagesquash— Squash all branch commits into one with LLM-generated messagerebase— Rebase onto target branchpush— Fast-forward target to current branchdiff— Show all changes since branching (committed, staged, unstaged, untracked)copy-ignored— Copy gitignored files between worktreesfor-each— [experimental] Run a command in every worktreepromote— [experimental] Put a branch into the main worktreeprune— Remove worktrees and branches merged into the default branchrelocate— [experimental] Move worktrees to expected paths
See also
wt merge— Runs commit → squash → rebase → hooks → push → cleanup automaticallywt hook— Run configured hooks
Command reference
wt step - Run individual operations
The building blocks of wt merge — commit, squash, rebase, push — plus standalone
utilities.
Usage: wt step [OPTIONS] <COMMAND>
Commands:
commit Stage and commit with LLM-generated message
squash Squash commits since branching
push Fast-forward target to current branch
rebase Rebase onto target
diff Show all changes since branching
copy-ignored Copy gitignored files to another worktree
for-each [experimental] Run command in each worktree
promote [experimental] Put a branch into the main worktree
prune [experimental] Remove worktrees merged into the default branch
relocate [experimental] Move worktrees to expected paths
Options:
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)
Subcommands
wt step commit
Stage and commit with LLM-generated message.
Stages all changes (including untracked files) and commits with an LLM-generated message.
Options
--stage
Controls what to stage before committing:
| Value | Behavior |
|---|---|
all | Stage all changes including untracked files (default) |
tracked | Stage only modified tracked files |
none | Don't stage anything, commit only what's already staged |
wt step commit --stage=tracked
Configure the default in user config:
[commit]
stage = "tracked"--show-prompt
Output the rendered LLM prompt to stdout without running the command. Useful for inspecting prompt templates or piping to other tools:
# Inspect the rendered prompt
wt step commit --show-prompt | less
# Pipe to a different LLM
wt step commit --show-prompt | llm -m gpt-5-nanoCommand reference
wt step commit - Stage and commit with LLM-generated message
Usage: wt step commit [OPTIONS]
Options:
-y, --yes
Skip approval prompts
--no-verify
Skip hooks
--stage <STAGE>
What to stage before committing [default: all]
Possible values:
- all: Stage everything: untracked files + unstaged tracked
changes
- tracked: Stage tracked changes only (like git add -u)
- none: Stage nothing, commit only what's already in the index
--show-prompt
Show prompt without running LLM
Outputs the rendered prompt to stdout for debugging or manual piping.
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)
wt step squash
Squash commits since branching. Stages changes and generates message with LLM.
Stages all changes (including untracked files), then squashes all commits since diverging from the target branch into a single commit with an LLM-generated message.
Options
--stage
Controls what to stage before squashing:
| Value | Behavior |
|---|---|
all | Stage all changes including untracked files (default) |
tracked | Stage only modified tracked files |
none | Don't stage anything, squash only committed changes |
wt step squash --stage=none
Configure the default in user config:
[commit]
stage = "tracked"--show-prompt
Output the rendered LLM prompt to stdout without running the command. Useful for inspecting prompt templates or piping to other tools:
wt step squash --show-prompt | lessCommand reference
wt step squash - Squash commits since branching
Stages changes and generates message with LLM.
Usage: wt step squash [OPTIONS] [TARGET]
Arguments:
[TARGET]
Target branch
Defaults to default branch.
Options:
-y, --yes
Skip approval prompts
--no-verify
Skip hooks
--stage <STAGE>
What to stage before committing [default: all]
Possible values:
- all: Stage everything: untracked files + unstaged tracked
changes
- tracked: Stage tracked changes only (like git add -u)
- none: Stage nothing, commit only what's already in the index
--show-prompt
Show prompt without running LLM
Outputs the rendered prompt to stdout for debugging or manual piping.
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)
wt step copy-ignored
Copy gitignored files to another worktree. Eliminates cold starts by copying build caches and dependencies.
Git worktrees share the repository but not untracked files. This command copies gitignored files to another worktree, eliminating cold starts.
Setup
Add to the project config:
# .config/wt.toml
[post-start]
copy = "wt step copy-ignored"What gets copied
All gitignored files are copied by default. Tracked files are never touched.
To limit what gets copied, create .worktreeinclude with gitignore-style patterns. Files must be both gitignored and in .worktreeinclude:
# .worktreeinclude
.env
node_modules/
target/Common patterns
| Type | Patterns |
|---|---|
| Dependencies | node_modules/, .venv/, target/, vendor/, Pods/ |
| Build caches | .cache/, .next/, .parcel-cache/, .turbo/ |
| Generated assets | Images, ML models, binaries too large for git |
| Environment files | .env (if not generated per-worktree) |
Features
- Uses copy-on-write (reflink) when available for space-efficient copies
- Handles nested
.gitignorefiles, global excludes, and.git/info/exclude - Skips existing files by default (safe to re-run)
--forceoverwrites existing files in the destination- Skips
.gitentries, VCS metadata directories (.jj,.hg, etc.), and other worktrees
Performance
Reflink copies share disk blocks until modified — no data is actually copied. For a 14GB target/ directory:
| Command | Time |
|---|---|
cp -R (full copy) | 2m |
cp -Rc / wt step copy-ignored | 20s |
Uses per-file reflink (like cp -Rc) — copy time scales with file count.
Use the post-start hook so the copy runs in the background. Use post-create instead if subsequent hooks or --execute command need the copied files immediately.
Language-specific notes
Rust
The target/ directory is huge (often 1-10GB). Copying with reflink cuts first build from ~68s to ~3s by reusing compiled dependencies.
Node.js
node_modules/ is large but mostly static. If the project has no native dependencies, symlinks are even faster:
[post-create]
deps = "ln -sf {{ primary_worktree_path }}/node_modules ."Python
Virtual environments contain absolute paths and can't be copied. Use uv sync instead — it's fast enough that copying isn't worth it.
Behavior vs Claude Code on desktop
The .worktreeinclude pattern is shared with Claude Code on desktop, which copies matching files when creating worktrees. Differences:
- worktrunk copies all gitignored files by default; Claude Code requires
.worktreeinclude - worktrunk uses copy-on-write for large directories like
target/— potentially 30x faster on macOS, 6x on Linux - worktrunk runs as a configurable hook in the worktree lifecycle
Command reference
wt step copy-ignored - Copy gitignored files to another worktree
Eliminates cold starts by copying build caches and dependencies.
Usage: wt step copy-ignored [OPTIONS]
Options:
--from <FROM>
Source worktree branch
Defaults to main worktree.
--to <TO>
Destination worktree branch
Defaults to current worktree.
--dry-run
Show what would be copied
--force
Overwrite existing files in destination
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)
wt step for-each
[experimental] Run command in each worktree. Executes sequentially with real-time output; continues on failure.
Executes a command sequentially in every worktree with real-time output. Continues on failure and shows a summary at the end.
Context JSON is piped to stdin for scripts that need structured data.
Template variables
All variables are shell-escaped. See wt hook template variables for the complete list and filters.
Examples
Check status across all worktrees:
wt step for-each -- git status --short
Run npm install in all worktrees:
wt step for-each -- npm install
Use branch name in command:
wt step for-each -- "echo Branch: {{ branch }}"
Pull updates in worktrees with upstreams (skips others):
git fetch --prune && wt step for-each -- '[ "$(git rev-parse @{u} 2>/dev/null)" ] || exit 0; git pull --autostash'
Note: This command is experimental and may change in future versions.
Command reference
wt step for-each - [experimental] Run command in each worktree
Executes sequentially with real-time output; continues on failure.
Usage: wt step for-each [OPTIONS] -- <ARGS>...
Arguments:
<ARGS>...
Command template (see --help for all variables)
Options:
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)
wt step prune
[experimental] Remove worktrees merged into the default branch.
Bulk-removes worktrees and branches that are integrated into the default branch, using the same criteria as wt remove's branch cleanup. Stale worktree entries are cleaned up too.
In wt list, candidates show _ (same commit) or ⊂ (content integrated). Run --dry-run to preview. See wt remove --help for the full integration criteria.
Locked worktrees and the main worktree are always skipped. The current worktree is removed last, triggering cd to the primary worktree. Pre-remove and post-remove hooks run for each removal.
Min-age guard
Worktrees younger than --min-age (default: 1 hour) are skipped. This prevents removing a worktree just created from the default branch — it looks "merged" because its branch points at the same commit.
wt step prune --min-age=0s # no age guard
wt step prune --min-age=2d # skip worktrees younger than 2 daysExamples
Preview what would be removed:
wt step prune --dry-run
Remove all merged worktrees:
wt step pruneCommand reference
wt step prune - [experimental] Remove worktrees merged into the default branch
Usage: wt step prune [OPTIONS]
Options:
--dry-run
Show what would be removed
-y, --yes
Skip approval prompts
--min-age <MIN_AGE>
Skip worktrees younger than this
[default: 1h]
--foreground
Run removal in foreground (block until complete)
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)
wt step relocate
[experimental] Move worktrees to expected paths. Relocates worktrees whose path doesn't match the worktree-path template.
Moves worktrees to match the configured worktree-path template.
Examples
Preview what would be moved:
wt step relocate --dry-run
Move all mismatched worktrees:
wt step relocate
Auto-commit and clobber blockers (never fails):
wt step relocate --commit --clobber
Move specific worktrees:
wt step relocate feature bugfixSwap handling
When worktrees are at each other's expected locations (e.g., alpha at
repo.beta and beta at repo.alpha), relocate automatically resolves
this by using a temporary location.
Clobbering
With --clobber, non-worktree paths at target locations are moved to
<path>.bak-<timestamp> before relocating.
Main worktree behavior
The main worktree can't be moved with git worktree move. Instead, relocate
switches it to the default branch and creates a new linked worktree at the
expected path. Untracked and gitignored files remain at the original location.
Skipped worktrees
- Dirty (without
--commit) — use--committo auto-commit first - Locked — unlock with
git worktree unlock - Target blocked (without
--clobber) — use--clobberto backup blocker - Detached HEAD — no branch to compute expected path
Note: This command is experimental and may change in future versions.
Command reference
wt step relocate - [experimental] Move worktrees to expected paths
Relocates worktrees whose path doesn't match the worktree-path template.
Usage: wt step relocate [OPTIONS] [BRANCHES]...
Arguments:
[BRANCHES]...
Worktrees to relocate (defaults to all mismatched)
Options:
--dry-run
Show what would be moved
--commit
Commit uncommitted changes before relocating
--clobber
Backup non-worktree paths at target locations
Moves blocking paths to <path>.bak-<timestamp>.
-h, --help
Print help (see a summary with '-h')
Global Options:
-C <path>
Working directory for this command
--config <path>
User config file path
-v, --verbose...
Verbose output (-v: hooks, templates; -vv: debug report)