mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
5c42c5b7d5
Two of the five machine-readable-output requests NathanaelRea opened (#3696–#3700), reviewed as a set and implemented where the gap was real. ## `wt config approvals list --format=json` (#3698) The command already computed the four distinctions an orchestrator needs — no commands, approved, approval-required, and stale — read-only, without prompting or writing. It had no `--format` flag, so the only way to learn that a non-interactive run would stop for approval was to run the operation and catch `NotInteractive`, or to pass `--yes` and approve whatever was there. ```json { "state": "approval_required", "commands": [ {"phase": "post-start", "name": "dev", "template": "npm run dev", "approved": false}, {"phase": "pre-merge", "template": "cargo test", "approved": true} ], "stale": ["some removed command"] } ``` `state` is what a caller branches on. `stale` stays a separate list rather than a fourth `state`, because it co-occurs with all three — and those are the approvals `--yes` would silently re-approve after their command template changed, which is exactly what an orchestrator preserving the approval model needs to see. A flag on the existing read command rather than a new `status` verb, matching `wt config show`, `wt config state get`, `wt config state logs`, and `wt list`. Closes #3698. ## `branch_outcome` on removal (#3700, partly) `wt remove --format=json` reported the branch as one boolean, collapsing five internal outcomes into two values: | Internal outcome | `branch_deleted` was | |---|---| | `Deleted` | `true` | | `Deferred` — handed to a detached process, result never observed | `true` | | `NotAttempted` — no branch, or `--no-delete-branch` | `false` | | `Retained` — a sibling worktree has it checked out | `false` | | `Retained` — **the CAS refused; the ref moved under us** | `false` | The last row is the exact race #3700 asks for protection against. Worktrunk already deletes with `git update-ref -d <ref> <oid>` and already fails closed when the ref has moved — then reported it as the same `false` that means "you asked me not to". And `Deferred` reported `true` on intent. `branch_outcome` names it instead: `deleted`, `deferred`, `not_attempted`, `retained_unmerged`, `retained_checked_out`, `retained_raced`, `retained_failed`. A caller that sees `retained_raced` knows to re-read the ref and retry, which is what the guard detects it for. **This does not close #3700.** That issue asks for an *input* — a caller-supplied expected OID that makes `wt` fail closed against the orchestrator's own observation. This is an *output*. They land in the same place on the default path, because the integration check already refuses to delete unintegrated content, so the caller was never going to lose commits — they just couldn't classify the refusal. Where the gap is real is `--force-delete` / `-D`, which takes the early return in `delete_branch_if_safe` and runs `git branch -D` with no integration check and no CAS. If an `--expected-oid` flag lands, it has to gate that path. ## Notes - **Output-format break.** `branch_deleted` is replaced, not supplemented, on `wt remove --format=json` and on `wt step prune --format=json`'s live path. Per CLAUDE.md, output formatting is on the flexible side of the interface line; flagging it here so the release changelog picks it up. - **`wt step prune --dry-run` keeps `branch_deleted`.** A dry run predicts; it runs nothing to have an outcome. Different thing, different name, documented as such. - **`retained_raced` and `retained_checked_out` have no deterministic CLI trigger.** Both come from windows between `wt`'s own fresh read and the ref mutation, which no hook can be scheduled inside. They're covered at the unit level (`branch_fate_from_result_mapping`, `branch_fate_json_outcome_is_distinct_per_fate`, and `cas_rejects_delete_when_branch_advances` in `src/git/remove.rs`, which drives the race with a stale snapshot). The integration tests cover the two reachable contrasts: `retained_unmerged` via a `pre-remove` hook that commits, and `not_attempted` via `--no-delete-branch`. - **`print_json` lives under `src/commands/list/`** and now has a third caller from outside that module. Worth a more central home; not moved here. ## The other three Reviewed but not implemented: - **#3696** — already possible. `wt --config-set 'list.json-schema = 2' list --format=json` pins the schema per invocation above every config layer, as does `WORKTRUNK_LIST__JSON_SCHEMA`. Answered on the issue; what's left is a docs gap and making an out-of-range value fail rather than degrade in JSON mode. - **#3697** — the machine-readable error channel. A real gap and the one policy call in the set; not started. - **#3699** — aimed at `wt config state logs --format=json`, which is a directory listing reconstructed from paths, under a model that overwrites. The append-only run record it wants is `commands.jsonl`. ## Testing `cargo run -- hook pre-merge --yes` green: 4533 tests, clippy, fmt, doctests, rustdoc, docs sync. > _This was written by Claude Code on behalf of max-sixty_ --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1186 lines
48 KiB
Markdown
1186 lines
48 KiB
Markdown
+++
|
|
title = "wt step"
|
|
description = "Run individual operations. The building blocks of wt merge — commit, squash, rebase, push — plus standalone utilities."
|
|
weight = 16
|
|
|
|
[extra]
|
|
group = "Commands"
|
|
+++
|
|
|
|
<!-- ⚠️ AUTO-GENERATED from `wt step --help-page` — edit src/cli/mod.rs to update -->
|
|
|
|
Run individual operations. The building blocks of wt merge — commit, squash, rebase, push — plus standalone utilities.
|
|
|
|
## Examples
|
|
|
|
Commit with LLM-generated message:
|
|
|
|
{% terminal(cmd="wt step commit") %}
|
|
<span class=c>◎</span> <span class=c>Generating commit message and committing changes... <span style='color:var(--bright-black,#555)'>(2 files, <span class=g>+26</span></span></span><span style='color:var(--bright-black,#555)'>)</span>
|
|
<span style='background:var(--bright-white,#fff)'> </span> <b>feat(validation): add input validation utilities</b>
|
|
<span class=g>✓</span> <span class=g>Committed changes @ <span class=d>a1b2c3d</span></span>
|
|
{% end %}
|
|
|
|
Manual merge workflow with review between steps:
|
|
|
|
{{ terminal(cmd="wt step commit|||wt step squash|||wt step rebase|||wt step push") }}
|
|
|
|
## Operations
|
|
|
|
- [`commit`](#wt-step-commit) — Stage and commit with [LLM-generated message](@/llm-commits.md)
|
|
- [`squash`](#wt-step-squash) — Squash all branch commits into one with [LLM-generated message](@/llm-commits.md)
|
|
- [`rebase`](#wt-step-rebase) — Rebase onto target branch
|
|
- [`push`](#wt-step-push) — Fast-forward target to current branch
|
|
- [`diff`](#wt-step-diff) — Show all changes since branching (committed, staged, unstaged, untracked)
|
|
- [`copy-ignored`](#wt-step-copy-ignored) — Copy gitignored files between worktrees
|
|
- [`eval`](#wt-step-eval) — <span class="badge-experimental"></span> Evaluate a template expression
|
|
- [`for-each`](#wt-step-for-each) — <span class="badge-experimental"></span> Run a command in every worktree
|
|
- [`promote`](#wt-step-promote) — <span class="badge-experimental"></span> Swap a branch into the main worktree
|
|
- [`prune`](#wt-step-prune) — Remove worktrees and branches merged into the default branch
|
|
- [`relocate`](#wt-step-relocate) — <span class="badge-experimental"></span> Move worktrees to expected paths
|
|
- [`tether`](#wt-step-tether) — <span class="badge-experimental"></span> Run a command; kill its whole process tree when its worktree is removed
|
|
- [`<alias>`](@/extending.md#aliases) — Run a configured command alias
|
|
|
|
## See also
|
|
|
|
- [`wt merge`](@/merge.md) — Runs commit → squash → rebase → hooks → push → cleanup automatically
|
|
- [`wt hook`](@/hook.md) — Run configured hooks
|
|
- [Aliases](@/extending.md#aliases) — Custom command templates run as `wt <name>`
|
|
|
|
## Command reference
|
|
|
|
{% terminal() %}
|
|
wt step - Run individual operations
|
|
|
|
The building blocks of <b>wt merge</b> — commit, squash, rebase, push — plus standalone utilities.
|
|
|
|
Usage: <b><span class=c>wt step</span></b> <span class=c>[OPTIONS]</span> <span class=c><COMMAND></span>
|
|
|
|
<b><span class=g>Commands:</span></b>
|
|
<b><span class=c>commit</span></b> Stage and commit with LLM-generated message
|
|
<b><span class=c>squash</span></b> Squash commits since branching
|
|
<b><span class=c>rebase</span></b> Rebase onto target
|
|
<b><span class=c>push</span></b> Fast-forward target to current branch
|
|
<b><span class=c>diff</span></b> Show all changes since branching
|
|
<b><span class=c>copy-ignored</span></b> Copy gitignored files to another worktree
|
|
<b><span class=c>eval</span></b> [experimental] Evaluate a template expression
|
|
<b><span class=c>for-each</span></b> [experimental] Run command in each worktree
|
|
<b><span class=c>promote</span></b> [experimental] Swap a branch into the main worktree
|
|
<b><span class=c>prune</span></b> [experimental] Remove worktrees merged into the default branch
|
|
<b><span class=c>relocate</span></b> [experimental] Move worktrees to expected paths
|
|
<b><span class=c>tether</span></b> [experimental] Run a command; kill its whole process tree when its worktree is
|
|
removed
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
# Subcommands
|
|
|
|
## wt step commit
|
|
|
|
Stage and commit with LLM-generated message.
|
|
|
|
See [LLM-generated commit messages](@/llm-commits.md) for configuration and prompt customization.
|
|
|
|
### Options
|
|
|
|
#### Staging
|
|
|
|
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 |
|
|
|
|
{{ terminal(cmd="wt step commit --stage=tracked") }}
|
|
|
|
Configure the default in user config:
|
|
|
|
```toml
|
|
[commit]
|
|
stage = "tracked"
|
|
```
|
|
|
|
#### Dry run
|
|
|
|
Render the prompt, print the LLM command, generate the message, and exit without staging, running hooks, or committing:
|
|
|
|
{{ terminal(cmd="wt step commit --dry-run") }}
|
|
|
|
Three sections are printed: the rendered prompt, the shell command that would invoke the LLM, and the message returned. The LLM call still happens — only the commit is skipped.
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step commit - Stage and commit with LLM-generated message
|
|
|
|
Usage: <b><span class=c>wt step commit</span></b> <span class=c>[OPTIONS]</span>
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-b</span></b>, <b><span class=c>--branch</span></b><span class=c> <BRANCH></span>
|
|
Branch to operate on (defaults to current worktree)
|
|
|
|
<b><span class=c>--stage</span></b><span class=c> <STAGE></span>
|
|
What to stage before committing [default: all]
|
|
|
|
Possible values:
|
|
- <b><span class=c>all</span></b>: Stage everything: untracked files + unstaged tracked changes
|
|
- <b><span class=c>tracked</span></b>: Stage tracked changes only (like <b>git add -u</b>)
|
|
- <b><span class=c>none</span></b>: Stage nothing, commit only what's already in the index
|
|
|
|
<b><span class=c>--dry-run</span></b>
|
|
Preview prompt, command, and generated message without committing
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--no-hooks</span></b>
|
|
Skip hooks
|
|
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the commit completes.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step squash
|
|
|
|
Squash commits since branching. Stages changes and generates message with LLM.
|
|
|
|
See [LLM-generated commit messages](@/llm-commits.md) for configuration and prompt customization.
|
|
|
|
### Options
|
|
|
|
#### Staging
|
|
|
|
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 |
|
|
|
|
{{ terminal(cmd="wt step squash --stage=none") }}
|
|
|
|
Configure the default in user config:
|
|
|
|
```toml
|
|
[commit]
|
|
stage = "tracked"
|
|
```
|
|
|
|
#### Dry run
|
|
|
|
Render the prompt, print the LLM command, generate the squash message, and exit without resetting, running hooks, or committing:
|
|
|
|
{{ terminal(cmd="wt step squash --dry-run") }}
|
|
|
|
Three sections are printed: the rendered prompt, the shell command that would invoke the LLM, and the message returned. The LLM call still happens — only the squash and commit are skipped.
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step squash - Squash commits since branching
|
|
|
|
Stages changes and generates message with LLM.
|
|
|
|
Usage: <b><span class=c>wt step squash</span></b> <span class=c>[OPTIONS]</span> <span class=c>[TARGET]</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c>[TARGET]</span>
|
|
Target branch
|
|
|
|
Defaults to default branch.
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>--stage</span></b><span class=c> <STAGE></span>
|
|
What to stage before committing [default: all]
|
|
|
|
Possible values:
|
|
- <b><span class=c>all</span></b>: Stage everything: untracked files + unstaged tracked changes
|
|
- <b><span class=c>tracked</span></b>: Stage tracked changes only (like <b>git add -u</b>)
|
|
- <b><span class=c>none</span></b>: Stage nothing, commit only what's already in the index
|
|
|
|
<b><span class=c>--dry-run</span></b>
|
|
Preview prompt, command, and generated message without squashing
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--no-hooks</span></b>
|
|
Skip hooks
|
|
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the squash completes.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step rebase
|
|
|
|
Rebase onto target.
|
|
|
|
A rebase puts the branch's commits on top of the target, which is what [`wt step push`](#wt-step-push) needs — a push fast-forwards only if the target is an ancestor of the branch. `wt merge` runs this step as part of its pipeline; on its own it brings a branch up to date with a target that has moved, without merging into it.
|
|
|
|
The target is any commit: a branch, a tag, a SHA.
|
|
|
|
### Examples
|
|
|
|
{{ terminal(cmd="wt step rebase # Rebase onto default branch|||wt step rebase develop # Rebase onto develop|||wt step rebase v1.2.0 # Rebase onto a tag") }}
|
|
|
|
### Outcomes
|
|
|
|
The first matching row wins:
|
|
|
|
| Branch and target | Result |
|
|
|-------------------|--------|
|
|
| The target is already an ancestor of the branch, with no merge commit in between | Nothing runs — `Already up to date` |
|
|
| The branch is an ancestor of the target, so it has no commits of its own | `Fast-forwarded to <target>` |
|
|
| Otherwise | The branch's commits replay onto the target's tip — refused outright if the two share no history |
|
|
|
|
A branch that merged the target into itself still rebases: the target is its ancestor, but the merge commit in between keeps the first row from applying.
|
|
|
|
When the target's local ref lags its upstream, the rows are measured against that upstream, which the result then names in place of the argument. [`wt merge`](@/merge.md) covers why.
|
|
|
|
### Conflicts
|
|
|
|
A conflicting commit leaves the rebase open rather than undoing it. The worktree keeps git's conflict markers, and the ways out are `git rebase --continue` once the conflict is resolved, `git rebase --skip`, or `git rebase --abort`. Until the rebase is settled, `wt step rebase`, `wt step squash`, `wt step push`, and `wt merge` refuse to run — as they do while any other git operation is open, a conflicted `git merge` included.
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step rebase - Rebase onto target
|
|
|
|
Usage: <b><span class=c>wt step rebase</span></b> <span class=c>[OPTIONS]</span> <span class=c>[TARGET]</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c>[TARGET]</span>
|
|
Target branch, tag, or commit
|
|
|
|
Defaults to default branch.
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the rebase completes.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step push
|
|
|
|
Fast-forward target to current branch.
|
|
|
|
Despite the name, no commits leave the repository. The target branch's ref moves forward locally, and a worktree holding that branch is updated along with it. Publishing is a separate `git push` to the remote afterward.
|
|
|
|
The target is a branch, and must already be an ancestor of the current branch. One that has moved ahead is refused, and there is no force variant — [`wt step rebase`](#wt-step-rebase) puts the branch back on top of it first.
|
|
|
|
### Examples
|
|
|
|
{{ terminal(cmd="wt step push # Fast-forward main to current branch|||wt step push develop # Fast-forward develop instead|||wt step push --no-ff # Merge commit instead of a fast-forward") }}
|
|
|
|
### Target worktree
|
|
|
|
When the target branch has a worktree of its own, that worktree's files move to the new commits too. Uncommitted changes there never move: the update carries any file the push doesn't touch — staged or not — exactly where it is, and a change touching a file the push does change is refused upfront, naming the file. If the sync can't be applied for any reason — a conflicting file appearing in the race window after the check, or a busy index — the update is rolled back whole, leaving branch and worktree as they were.
|
|
|
|
A worktree that is still registered but whose directory is gone is refused as well, since nothing can be synced into it — `git worktree prune` clears the registration.
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step push - Fast-forward target to current branch
|
|
|
|
Usage: <b><span class=c>wt step push</span></b> <span class=c>[OPTIONS]</span> <span class=c>[TARGET]</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c>[TARGET]</span>
|
|
Target branch
|
|
|
|
Defaults to default branch.
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>--no-ff</span></b>
|
|
Create a merge commit (no fast-forward)
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the push completes.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step diff
|
|
|
|
Show all changes since branching. Includes committed, staged, unstaged, and untracked files.
|
|
|
|
This is what `wt merge` would include — a single diff against the merge base.
|
|
|
|
### Operating on another worktree
|
|
|
|
`--branch` diffs another worktree's branch without leaving the current one:
|
|
|
|
{{ terminal(cmd="wt step diff --branch feature") }}
|
|
|
|
The branch must have a checked-out worktree.
|
|
|
|
### Extra git diff arguments
|
|
|
|
Arguments after `--` are forwarded to `git diff`:
|
|
|
|
{{ terminal(cmd="wt step diff -- --stat|||wt step diff -- --name-only|||wt step diff -- -- '*.rs'") }}
|
|
|
|
The diff is pipeable to tools like `delta`:
|
|
|
|
{{ terminal(cmd="wt step diff | delta") }}
|
|
|
|
### How it works
|
|
|
|
Equivalent to:
|
|
|
|
{{ terminal(cmd="cp __WT_QUOT__$(git rev-parse --git-dir)/index__WT_QUOT__ /tmp/idx|||GIT_INDEX_FILE=/tmp/idx git add --intent-to-add .|||GIT_INDEX_FILE=/tmp/idx git diff $(git merge-base HEAD $(wt config state default-branch))") }}
|
|
|
|
`git diff` ignores untracked files. `git add --intent-to-add .` registers them in the index without staging their content, making them visible to `git diff`. This runs against a copy of the real index so the original is never modified.
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step diff - Show all changes since branching
|
|
|
|
Includes committed, staged, unstaged, and untracked files.
|
|
|
|
Usage: <b><span class=c>wt step diff</span></b> <span class=c>[OPTIONS]</span> <span class=c>[TARGET]</span> <b><span class=c>[--</span></b> <span class=c><EXTRA_ARGS>...</span><b><span class=c>]</span></b>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c>[TARGET]</span>
|
|
Target branch
|
|
|
|
Defaults to default branch.
|
|
|
|
<span class=c>[EXTRA_ARGS]...</span>
|
|
Extra arguments forwarded to <b>git diff</b>
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-b</span></b>, <b><span class=c>--branch</span></b><span class=c> <BRANCH></span>
|
|
Branch to operate on (defaults to current worktree)
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step copy-ignored
|
|
|
|
Copy gitignored files to another worktree. Eliminates cold starts by copying build caches and dependencies.
|
|
|
|
### Setup
|
|
|
|
Add to the project config:
|
|
|
|
```toml
|
|
# .config/wt.toml
|
|
[post-start]
|
|
copy = "wt step copy-ignored"
|
|
```
|
|
|
|
### What gets copied
|
|
|
|
All gitignored files are copied by default, except for built-in excluded directories: VCS metadata (`.bzr/`, `.hg/`, `.jj/`, `.pijul/`, `.sl/`, `.svn/`), tool-state (`.conductor/`, `.entire/`, `.worktrees/`), and nested worktrees. Tracked files are never touched. Discovery handles nested `.gitignore` files, global excludes, and `.git/info/exclude`. Existing files in the destination are skipped, so re-running is safe; `--force` overwrites them.
|
|
|
|
To limit what gets copied further, create `.worktreeinclude` with gitignore-style patterns. Files must be **both** gitignored **and** in `.worktreeinclude`:
|
|
|
|
```text
|
|
# .worktreeinclude
|
|
.env
|
|
node_modules/
|
|
target/
|
|
```
|
|
|
|
After `.worktreeinclude` selects entries, you can add more gitignore-style excludes in user config, per-project user overrides, or project config:
|
|
|
|
```toml
|
|
[step.copy-ignored]
|
|
exclude = [".cache/", ".turbo/"]
|
|
```
|
|
|
|
To copy nothing unless `.worktreeinclude` exists — matching Claude Code desktop, where the file is required — pass `--require-include`:
|
|
|
|
{{ terminal(cmd="wt step copy-ignored --require-include") }}
|
|
|
|
Without `.worktreeinclude`, the command is a no-op (it reports that nothing was copied and why). With the file present, only matching files copy as above. To apply this across every repository, put the flag in a user-config hook: `post-start = "wt step copy-ignored --require-include"`.
|
|
|
|
### 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) |
|
|
|
|
### 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 `pre-start` instead if subsequent hooks or `--execute` command need the copied files immediately.
|
|
|
|
### Background-hook priority (experimental)
|
|
|
|
When invoked from a background hook pipeline (`post-*` hooks), `wt step copy-ignored` self-lowers its CPU and I/O priority — `taskpolicy -b` on macOS, `nice -n 19` plus `ionice -c 3` on Linux — so it yields to interactive work. Foreground callers (`pre-*` hooks, direct interactive use) run at normal priority so the user isn't waiting on a throttled copy.
|
|
|
|
wt signals background-hook context by exporting `WORKTRUNK_FOREGROUND=-1` into every detached hook pipeline; `copy-ignored` inspects that variable on entry. The variable name is experimental and may change.
|
|
|
|
### 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:
|
|
|
|
```toml
|
|
[pre-start]
|
|
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](https://code.claude.com/docs/en/desktop), which copies matching files when creating worktrees. Differences:
|
|
|
|
- worktrunk copies all gitignored files by default; Claude Code requires `.worktreeinclude`. Pass `--require-include` to match Claude Code (copy nothing without `.worktreeinclude`)
|
|
- worktrunk uses copy-on-write for large directories like `target/` (see Performance above)
|
|
- worktrunk runs as a configurable hook in the worktree lifecycle
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step copy-ignored - Copy gitignored files to another worktree
|
|
|
|
Eliminates cold starts by copying build caches and dependencies.
|
|
|
|
Usage: <b><span class=c>wt step copy-ignored</span></b> <span class=c>[OPTIONS]</span>
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>--from</span></b><span class=c> <FROM></span>
|
|
Source worktree branch
|
|
|
|
Defaults to main worktree.
|
|
|
|
<b><span class=c>--to</span></b><span class=c> <TO></span>
|
|
Destination worktree branch
|
|
|
|
Defaults to current worktree.
|
|
|
|
<b><span class=c>--dry-run</span></b>
|
|
Show what would be copied
|
|
|
|
<b><span class=c>--force</span></b>
|
|
Overwrite existing files in destination
|
|
|
|
<b><span class=c>--require-include</span></b>
|
|
Require .worktreeinclude to copy anything
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the copy completes.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step eval
|
|
|
|
<span class="badge-experimental"></span>
|
|
|
|
Evaluate a template expression. Prints the result to stdout for use in scripts and shell substitutions.
|
|
|
|
All [hook template variables and filters](@/hook.md#template-variables) are available.
|
|
|
|
### Examples
|
|
|
|
Get the port for the current branch:
|
|
|
|
{% terminal(cmd="wt step eval '__WT_OPEN__ branch | hash_port __WT_CLOSE__'") %}
|
|
16066
|
|
{% end %}
|
|
|
|
Use in shell substitution:
|
|
|
|
{{ terminal(cmd="curl http://localhost:$(wt step eval '__WT_OPEN__ branch | hash_port __WT_CLOSE__')/health") }}
|
|
|
|
Combine multiple values:
|
|
|
|
{% terminal(cmd="wt step eval '__WT_OPEN__ branch | hash_port __WT_CLOSE__,__WT_OPEN__ (__WT_QUOT__supabase-api-__WT_QUOT__ ~ branch) | hash_port __WT_CLOSE__'") %}
|
|
16066,16739
|
|
{% end %}
|
|
|
|
Use conditionals and filters:
|
|
|
|
{% terminal(cmd="wt step eval '__WT_OPEN__ branch | sanitize_db __WT_CLOSE__'") %}
|
|
feature_auth_oauth2_a1b
|
|
{% end %}
|
|
|
|
List the available template variables with `-v` (alongside the expansion, on stderr):
|
|
|
|
{% terminal(cmd="wt step eval -v '__WT_OPEN__ branch __WT_CLOSE__'") %}
|
|
○ eval template variables:
|
|
branch = feature/auth-oauth2
|
|
worktree_path = /home/user/projects/myapp-feature-auth-oauth2
|
|
○ eval source
|
|
{{ branch }}
|
|
○ eval result
|
|
feature/auth-oauth2
|
|
|
|
feature/auth-oauth2
|
|
{% end %}
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step eval - [experimental] Evaluate a template expression
|
|
|
|
Prints the result to stdout for use in scripts and shell substitutions.
|
|
|
|
Usage: <b><span class=c>wt step eval</span></b> <span class=c>[OPTIONS]</span> <span class=c><TEMPLATE></span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c><TEMPLATE></span>
|
|
Template expression to evaluate
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints <b>{name, template, result}</b> to stdout instead of the bare result.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step for-each
|
|
|
|
<span class="badge-experimental"></span>
|
|
|
|
Run command in each worktree. Executes sequentially with real-time output; continues past command failures.
|
|
|
|
A summary of successes and failures is shown at the end. A template-expansion error (a malformed `{{ … }}` argument) aborts the whole run; only command failures are tolerated and reported. Context JSON — a flat object of every template variable — is piped to stdin for scripts that need structured data.
|
|
|
|
### Arguments
|
|
|
|
Arguments after `--` are the program and its arguments — run directly, no shell.
|
|
|
|
{{ terminal(cmd="wt step for-each -- git status --short|||wt step for-each -- npm install") }}
|
|
|
|
For pipes, redirects, variables, or globs, wrap in `sh -c`:
|
|
|
|
{{ terminal(cmd="wt step for-each -- sh -c 'git status | wc -l'|||wt step for-each -- sh -c 'echo $HOME && git pull'") }}
|
|
|
|
### Template variables
|
|
|
|
Variables substitute into each argv element before exec. See [`wt hook` template variables](@/hook.md#template-variables) for the complete list and filters.
|
|
|
|
{{ terminal(cmd="wt step for-each -- echo 'Branch: __WT_OPEN__ branch __WT_CLOSE__'") }}
|
|
|
|
Each element is expanded fresh in every worktree, so `{{ branch }}` is that worktree's branch. An alias wrapping for-each renders templates earlier, in the invoking worktree; [deferring expansion in an alias](@/extending.md#deferring-expansion-to-a-nested-wt-command) shows how to keep a variable per-worktree.
|
|
|
|
### Examples
|
|
|
|
Pull updates in worktrees with upstreams (skips others):
|
|
|
|
{{ terminal(cmd="git fetch --prune && wt step for-each -- sh -c '[ __WT_QUOT__$(git rev-parse @{u} 2>/dev/null)__WT_QUOT__ ] || exit 0; git pull --autostash'") }}
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step for-each - [experimental] Run command in each worktree
|
|
|
|
Executes sequentially with real-time output; continues past command failures.
|
|
|
|
Usage: <b><span class=c>wt step for-each</span></b> <span class=c>[OPTIONS]</span> <b><span class=c>--</span></b> <span class=c><ARGS>...</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c><ARGS>...</span>
|
|
Command template (see --help for all variables)
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step promote
|
|
|
|
<span class="badge-experimental"></span>
|
|
|
|
Swap a branch into the main worktree. Exchanges branches and gitignored files between two worktrees.
|
|
|
|
**Experimental.** Use promote for temporary testing when the main worktree has special significance (Docker Compose, IDE configs, heavy build artifacts anchored to project root), and hooks & tools aren't yet set up to run on arbitrary worktrees. The idiomatic Worktrunk workflow does not use `promote`; instead each worktree has a full environment. `promote` is the only Worktrunk command which changes a branch in an existing worktree.
|
|
|
|
### Example
|
|
|
|
{{ terminal(cmd="# from ~/project (main worktree)|||wt step promote feature") }}
|
|
|
|
Before:
|
|
|
|
```
|
|
Branch Path
|
|
@ main ~/project
|
|
+ feature ~/project.feature
|
|
```
|
|
|
|
After:
|
|
|
|
```
|
|
Branch Path
|
|
@ feature ~/project
|
|
+ main ~/project.feature
|
|
```
|
|
|
|
To restore: `wt step promote main` from anywhere, or just `wt step promote` from the main worktree.
|
|
|
|
Without an argument, promotes the current branch — or restores the default branch if run from the main worktree.
|
|
|
|
### Requirements
|
|
|
|
- Both worktrees must be clean
|
|
- The branch must have an existing worktree
|
|
|
|
### Gitignored files
|
|
|
|
Gitignored files (build artifacts, `node_modules/`, `.env`) are swapped along with the branches so each worktree keeps the artifacts that belong to its branch. Files are discovered using the same mechanism as [`copy-ignored`](#wt-step-copy-ignored) and can be filtered with `.worktreeinclude`.
|
|
|
|
The swap uses `rename()` for each entry — fast regardless of entry size, since only filesystem metadata changes. If the worktree is on a different filesystem from `.git/`, it falls back to reflink copy.
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step promote - [experimental] Swap a branch into the main worktree
|
|
|
|
Exchanges branches and gitignored files between two worktrees.
|
|
|
|
Usage: <b><span class=c>wt step promote</span></b> <span class=c>[OPTIONS]</span> <span class=c>[BRANCH]</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c>[BRANCH]</span>
|
|
Branch to promote to main worktree
|
|
|
|
Defaults to current branch, or default branch from main worktree.
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the promote completes. The mismatch warning
|
|
still appears on stderr in JSON mode (safety signal).
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step prune
|
|
|
|
<span class="badge-experimental"></span>
|
|
|
|
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; a candidate whose hooks include an unapproved project command is skipped with `(approval required)` (pre-approve with `wt config approvals add`, or pass `--yes`).
|
|
|
|
### Min-age guard
|
|
|
|
Worktrees younger than `--min-age` (default: 1 day) are skipped. This prevents removing a worktree just created from the default branch — it looks "merged" because its branch points at the same commit.
|
|
|
|
{{ terminal(cmd="wt step prune --min-age=0s # no age guard|||wt step prune --min-age=2d # skip worktrees younger than 2 days") }}
|
|
|
|
### JSON output
|
|
|
|
`--format=json` prints one object per candidate to stdout. The two modes report different things, and name their fields accordingly: a live run reports `branch_outcome`, the executed outcome, using the vocabulary [`wt remove`](@/remove.md#json-output) documents; `--dry-run` reports `branch_deleted`, its prediction of whether the removal would take the branch, since it runs nothing to have an outcome. A dry run also carries `reason` and `target` (why the candidate qualifies, and what it was measured against).
|
|
|
|
### Examples
|
|
|
|
Preview what would be removed:
|
|
|
|
{{ terminal(cmd="wt step prune --dry-run") }}
|
|
|
|
Remove all merged worktrees:
|
|
|
|
{{ terminal(cmd="wt step prune") }}
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step prune - [experimental] Remove worktrees merged into the default branch
|
|
|
|
Usage: <b><span class=c>wt step prune</span></b> <span class=c>[OPTIONS]</span>
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>--dry-run</span></b>
|
|
Show what would be removed
|
|
|
|
<b><span class=c>--min-age</span></b><span class=c> <MIN_AGE></span>
|
|
Skip worktrees younger than this
|
|
|
|
[default: 1d]
|
|
|
|
<b><span class=c>--foreground</span></b>
|
|
Run removal in foreground (block until complete)
|
|
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step relocate
|
|
|
|
<span class="badge-experimental"></span>
|
|
|
|
Move worktrees to expected paths. Relocates worktrees whose path doesn't match the worktree-path template.
|
|
|
|
### Examples
|
|
|
|
Preview what would be moved:
|
|
|
|
{{ terminal(cmd="wt step relocate --dry-run") }}
|
|
|
|
Move all mismatched worktrees:
|
|
|
|
{{ terminal(cmd="wt step relocate") }}
|
|
|
|
Auto-commit and clobber blockers (never fails):
|
|
|
|
{{ terminal(cmd="wt step relocate --commit --clobber") }}
|
|
|
|
Move specific worktrees:
|
|
|
|
{{ terminal(cmd="wt step relocate feature bugfix") }}
|
|
|
|
### Swap 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. If that name is already taken,
|
|
the move counts up (`…-2`, `…-3`, …) until it finds a free name, so an
|
|
existing backup is never overwritten.
|
|
|
|
### 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.
|
|
|
|
### Dirty worktrees
|
|
|
|
Linked worktrees relocate as-is — `git worktree move` carries uncommitted
|
|
changes along. Only the main worktree skips when dirty (its `git checkout`
|
|
refuses), unless `--commit` is passed.
|
|
|
|
### Skipped worktrees
|
|
|
|
- **Dirty main worktree** (without `--commit`) — use `--commit` to auto-commit first
|
|
- **Locked** — unlock with `git worktree unlock`
|
|
- **Target blocked** (without `--clobber`) — use `--clobber` to backup blocker
|
|
- **Detached HEAD** — no branch to compute expected path
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step relocate - [experimental] Move worktrees to expected paths
|
|
|
|
Relocates worktrees whose path doesn't match the <b>worktree-path</b> template.
|
|
|
|
Usage: <b><span class=c>wt step relocate</span></b> <span class=c>[OPTIONS]</span> <span class=c>[BRANCHES]...</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c>[BRANCHES]...</span>
|
|
Worktrees to relocate (defaults to all mismatched)
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>--dry-run</span></b>
|
|
Show what would be moved
|
|
|
|
<b><span class=c>--commit</span></b>
|
|
Commit uncommitted changes before relocating
|
|
|
|
<b><span class=c>--clobber</span></b>
|
|
Backup non-worktree paths at target locations
|
|
|
|
Moves blocking paths to <b><path>.bak.<timestamp></b>. If that name is taken, counts up (<b>…-2</b>, <b>…-3</b>
|
|
, …) to a free name.
|
|
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Automation:</span></b>
|
|
<b><span class=c>--format</span></b><span class=c> <FORMAT></span>
|
|
Output format
|
|
|
|
JSON prints structured result to stdout after the relocate completes.
|
|
|
|
[default: text]
|
|
[possible values: text, json]
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
## wt step tether
|
|
|
|
<span class="badge-experimental"></span>
|
|
|
|
Run a command; kill its whole process tree when its worktree is removed. Teardown is automatic and needs no pre-remove hook; the group gets SIGTERM then SIGKILL.
|
|
|
|
### Why
|
|
|
|
A `post-start` hook to start a long-lived process and a `pre-remove` hook to
|
|
stop it is usually enough. But `pre-remove` only runs when worktrunk removes
|
|
the worktree, so a `git worktree remove`, an `rm -rf`, or a crashed hook skips
|
|
it. Across enough worktree churn some process is bound to outlive its worktree,
|
|
and with no cleanup these leaks accumulate (on macOS they eventually saturate
|
|
`fseventsd`). `tether` removes the need for a `pre-remove`: it ties the
|
|
command's lifetime to the worktree and kills the whole process group once the
|
|
worktree is gone.
|
|
|
|
### Arguments
|
|
|
|
Arguments after `--` are the program and its arguments, run directly, no shell.
|
|
|
|
{{ terminal(cmd="wt step tether -- npm run dev") }}
|
|
|
|
For pipes, redirects, variables, or globs, wrap in `sh -c`:
|
|
|
|
{{ terminal(cmd="wt step tether -- sh -c 'PORT=$P npm run dev | tee dev.log'") }}
|
|
|
|
To run the command from a subdirectory, pass the global `-C` flag (teardown
|
|
still watches the worktree root, so a server launched with a relative `-C` is
|
|
torn down with the worktree):
|
|
|
|
{{ terminal(cmd="wt step tether -C frontend -- npm run dev") }}
|
|
|
|
### Examples
|
|
|
|
Run a dev server, torn down automatically when the worktree goes away:
|
|
|
|
```toml
|
|
# .config/wt.toml
|
|
[post-start]
|
|
server = "wt step tether -- npm run dev -- --port {{ branch | hash_port }}"
|
|
```
|
|
|
|
### Command reference
|
|
|
|
{% terminal() %}
|
|
wt step tether - [experimental] Run a command; kill its whole process tree when its worktree is removed
|
|
|
|
Teardown is automatic and needs no <b>pre-remove</b> hook; the group gets <b>SIGTERM</b> then <b>SIGKILL</b>.
|
|
|
|
Usage: <b><span class=c>wt step tether</span></b> <span class=c>[OPTIONS]</span> <b><span class=c>--</span></b> <span class=c><COMMAND>...</span>
|
|
|
|
<b><span class=g>Arguments:</span></b>
|
|
<span class=c><COMMAND>...</span>
|
|
Command to run (after <b>--</b>, run directly, no shell)
|
|
|
|
<b><span class=g>Options:</span></b>
|
|
<b><span class=c>-h</span></b>, <b><span class=c>--help</span></b>
|
|
Print help (see a summary with '-h')
|
|
|
|
<b><span class=g>Global Options:</span></b>
|
|
<b><span class=c>-C</span></b><span class=c> <path></span>
|
|
Working directory for this command
|
|
|
|
<b><span class=c>--config</span></b><span class=c> <path></span>
|
|
User config file path
|
|
|
|
<b><span class=c>--config-set</span></b><span class=c> <toml></span>
|
|
Override config with inline TOML, e.g. --config-set list.full=true (repeatable)
|
|
|
|
<b><span class=c>-v</span></b>, <b><span class=c>--verbose</span></b><span class=c>...</span>
|
|
Verbose output (-v: info logs + hook/alias template variables on stderr; -vv: also debug
|
|
logs and raw subprocess output written to .git/wt/logs/). Set WORKTRUNK_VERBOSE=0|1|2 to
|
|
apply the same level everywhere — including shell completion, which no flag can reach
|
|
|
|
<b><span class=c>-y</span></b>, <b><span class=c>--yes</span></b>
|
|
Skip approval prompts
|
|
{% end %}
|
|
|
|
<!-- END AUTO-GENERATED -->
|