Skip to content

Run --execute in the worktree even under --no-cd - #4179

Merged
max-sixty merged 2 commits into
mainfrom
execute-in-target
Sep 17, 2026
Merged

max-sixty merged 2 commits into
mainfrom
execute-in-target

Conversation

@max-sixty

Copy link
Copy Markdown
Owner

Problem

--no-cd took the --execute program back to the invoking directory along with the shell. wt switch feature --no-cd -x pwd printed the directory you ran it from, so a program that means "act on the new worktree" — an editor, an agent — had to be handed {{ worktree_path }} explicitly.

Nothing decided that. One change_directory() call did two jobs: it wrote the shell's cd directive and it recorded the program's working directory in OutputState::target_dir, which execute() read back. Skipping the directive therefore also dropped the program's directory. The behavior first appears in the docs in #3977, which described what the shared value already did, and #4043 then matched the Executing (--execute) @ … header to it.

In #4042 the reporter wanted exactly this combination — open the worktree in Zed, leave the terminal where it was — and had to work around it by passing the path as an argument.

Solution

The program's directory becomes an argument rather than a leftover of the directive. handle_switch_output computes it once and returns it as SwitchDisplayPaths::execute_dir; execute() takes it from the caller; OutputState loses target_dir, so the two decisions can't be re-coupled through global state. change_directory() now only moves the shell.

--no-cd writes no directive and leaves the shell where it stands. The program starts in the worktree the switch selected, with the subdirectory position resolve_subdir_in_target preserves, exactly as under --cd.

Two consequences worth reading in the diff:

  • The git-discovery scrub (GIT_DIR and friends, Inherited GIT_* discovery vars are forwarded into user hooks, misdirecting hook git commands (can pollute core.worktree) #3373) was conditional on having a target directory, so it didn't run under --no-cd. That directory is now always one wt chose, which is the condition CLAUDE.md sets for scrubbing, so the scrub is unconditional.
  • The header annotates the program's directory whenever the user's shell won't be there — which now includes --no-cd, where it previously read a bare Executing (--execute):. The updated snapshot shows both halves: pwd prints the worktree, and the header names it.

This changes documented behavior of a flag combination rather than any flag or config key. --execute means "run this against the worktree I just selected", and --no-cd is a statement about the shell; the old behavior made the second silently override the first.

Thanks to @yajo for reporting #4042.

This was written by Claude Code on behalf of max-sixty

🤖 Generated with Claude Code

max-sixty and others added 2 commits September 17, 2026 15:58
`--no-cd` moved the `--execute` program back to the invoking directory along with the shell, because one `change_directory()` call both wrote the shell's cd directive and recorded the program's working directory in global state. Nothing decided that; the coupling fell out of the shared value, and #4043 documented it in the help and matched the header to it.

The program's directory is now an argument: `handle_switch_output` returns it as `SwitchDisplayPaths::execute_dir`, and `execute()` takes it from the caller rather than from `OutputState`, which loses `target_dir` entirely. `--no-cd` writes no directive and leaves the shell where it stands; the program still starts in the worktree the switch selected, subdirectory position preserved. Since that directory is always one wt chose, the git-discovery scrub now applies unconditionally too.

So `wt switch feature --no-cd -x code -- .` opens the worktree and leaves the terminal where it was — the request behind #4042, which the reporter had to work around by passing `{{ worktree_path }}` as an argument.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The chained builder leaves nothing to mutate outside `#[cfg(unix)]`, so `let mut cmd` tripped `-D unused_mut` on Windows. Shadow under the cfg instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@max-sixty
max-sixty merged commit 5b2d3c0 into main Sep 17, 2026
38 checks passed
@max-sixty
max-sixty deleted the execute-in-target branch September 17, 2026 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants