Skip to content

fix(tab-configs): shell-escape worktree path prefix so spaces in repo names don't break git worktree add / cd (#11144)#11392

Open
david-engelmann wants to merge 1 commit into
warpdotdev:masterfrom
david-engelmann:david/11144-worktree-config-quoting
Open

fix(tab-configs): shell-escape worktree path prefix so spaces in repo names don't break git worktree add / cd (#11144)#11392
david-engelmann wants to merge 1 commit into
warpdotdev:masterfrom
david-engelmann:david/11144-worktree-config-quoting

Conversation

@david-engelmann
Copy link
Copy Markdown

Description

The "+ > New worktree config" sidebar flow generated commands like

git worktree add -b turquoise-palo-verde /Users/luizv/.warp/worktrees/2026-05 Site da Jô/turquoise-palo-verde && cd /Users/luizv/.warp/worktrees/2026-05 Site da Jô/turquoise-palo-verde

with the repo-derived path interpolated without quoting or escaping. The user's shell tokenized the path on the space, so git worktree add received four positional args and printed its usage: help instead of creating the worktree. No explicit error surfaced — the command appears to "complete" — so the failure is non-obvious.

Fixes #11144.

Root cause

Two parallel call sites embed the repo-derived path into a generated command, neither one escaped:

File Function Code (master)
app/src/user_config/mod.rs:250-253 materialize_default_worktree_config let worktree_path = generated_worktree_path_string(...) then string-substituted into the command template
app/src/tab_configs/session_config.rs:118-122 build_tab_config (autogenerated branch) format!("git worktree add -b {autogenerated_branch_name} {worktree_path}")
app/src/tab_configs/session_config.rs:125-129 build_tab_config (named branch) format!("git worktree add -b {worktree_branch_name} {worktree_path}")

Both produce a TabConfig whose worktree commands embed a bare unescaped worktree_path. The user's shell parses that path verbatim, so any space / $ / backtick / ( / ) in the path component breaks the command.

Fix

Both call sites now route the path through ShellFamily::Posix.shell_escape (the same primitive used by the cd / open-folder / cli-install / git-checkout fixes — including PR #11391 (#11132) for the sibling grep / file_glob path quoting). The escape is applied only to the static repo-derived prefix; the {{autogenerated_branch_name}} / {{worktree_branch_name}} handlebars placeholder embedded in the path is left untouched so the tab-config render pass can still resolve it.

Each call site grew a small helper:

  • escape_worktree_path_prefix(worktree_path, branch_placeholder) in user_config/mod.rs
  • build_shell_safe_worktree_path(directory, branch_placeholder) in tab_configs/session_config.rs

Both share the same logic shape: split the rendered path at the placeholder, escape the prefix, re-join. Falls back to escaping the whole path if the placeholder is somehow absent (defense in depth).

fn build_shell_safe_worktree_path(directory: &Path, branch_placeholder: &str) -> String {
    use warp_util::path::ShellFamily;
    let raw = generated_worktree_path_string(directory, branch_placeholder);
    match raw.rsplit_once(branch_placeholder) {
        Some((prefix, suffix)) => {
            let escaped_prefix = ShellFamily::Posix.shell_escape(prefix);
            format!("{escaped_prefix}{branch_placeholder}{suffix}")
        }
        None => ShellFamily::Posix.shell_escape(&raw).into_owned(),
    }
}

Shell-level before / after

$ git worktree add -b turquoise-palo-verde /Users/me/My\ Project/turquoise-palo-verde
fatal: invalid reference: turquoise-palo-verde
… or 'usage: git worktree add …' if the bare unescaped path is supplied (which is what the user actually saw)

$ git worktree add -b turquoise-palo-verde /Users/me/My\ Project/turquoise-palo-verde
# (with the escape) — git receives exactly two positional args; worktree is created

Testing

11 new unit tests across the two existing sibling _tests.rs files cover both code paths:

  • spaces in the repo name (the original report — /Users/luizv/Developer/2026-05 Site da Jô)
  • $ in the repo name (would otherwise be expanded as a variable)
  • backtick in the repo name (would otherwise trigger command substitution)
  • handlebars placeholder is preserved through the escape
  • plain paths remain unchanged (regression guard against over-escaping)
  • the named-branch variant of build_tab_config (uses the same helper)
$ cargo nextest run -p warp -E 'test(worktree_path_quoting::)'
11 tests run: 11 passed; 0 failed

$ cargo nextest run -p warp -E 'test(tab_configs::) or test(user_config::)'
91 tests run: 91 passed   (no regression in existing tests)

$ cargo clippy -p warp --tests -- -D warnings
Finished (no warnings)

$ cargo fmt -p warp -- --check
clean

Relationship to #11132 / #11391

This fix is part of the same shell-escape cluster as PR #11391 (#11132), which covers the agent's grep / file_glob / is_file_path / is_git_repository helpers. The two PRs touch disjoint files (tab_configs/ + user_config/ here vs. ai/blocklist/action_model/execute/ there) and ship independently. Both use the same warp_util::path::ShellFamily::shell_escape primitive.

Server API dependencies

None — this is client-only.

Agent Mode

Unchecked (external contribution).

Changelog

CHANGELOG-BUG-FIX: "+ > New worktree config" no longer breaks when the repository's directory name contains spaces or shell metacharacters. The generated git worktree add / cd commands now shell-escape the path.

Fixes #11144

…acharacters in repo names don't break `git worktree add` / `cd` (warpdotdev#11144)

The "+ > New worktree config" sidebar flow generated commands like

    git worktree add -b turquoise-palo-verde /Users/x/.warp/worktrees/My Project/turquoise-palo-verde

with the repo-derived path interpolated **without quoting or escaping**.
The user's shell tokenized the path on the space, so `git worktree add`
received four positional args and printed its `usage:` help instead of
creating the worktree. No explicit error surfaced — the command appears
to "complete" — so the failure is non-obvious.

Fixes warpdotdev#11144.

Two parallel call sites needed the same fix:

* `app/src/user_config/mod.rs` — `materialize_default_worktree_config`,
  reached via the sidebar "+ > New worktree config" flow. Substitutes
  the repo-derived `worktree_path` into the rendered TOML commands.
* `app/src/tab_configs/session_config.rs` — `build_tab_config`, the
  in-Rust-code worktree config builder used by the session-config
  modal. Same `format!`-into-command pattern.

Both helpers now route the path through `ShellFamily::Posix.shell_escape`
(the same primitive used by the cd / open-folder / cli-install / git
checkout fixes — including PR warpdotdev#11132's grep / file_glob path quoting).
The escape is applied only to the static repo-derived prefix; the
`{{autogenerated_branch_name}}` / `{{worktree_branch_name}}` handlebars
placeholder embedded in the path is left untouched so the tab-config
render pass can still resolve it.

To keep the change reviewable, each call site grew a small helper:

* `escape_worktree_path_prefix(worktree_path, branch_placeholder)` in
  `user_config/mod.rs`
* `build_shell_safe_worktree_path(directory, branch_placeholder)` in
  `tab_configs/session_config.rs`

Both share the same logic shape: split the rendered path at the
placeholder, escape the prefix, re-join. Falls back to escaping the
whole path if the placeholder is somehow absent (defense in depth).

## Tests

11 new unit tests across the two existing sibling `_tests.rs` files,
covering both code paths:

* spaces in the repo name (the original report)
* `$` in the repo name (would otherwise be expanded as a variable)
* backtick in the repo name (would otherwise trigger command substitution)
* handlebars placeholder is preserved through the escape
* plain paths remain unchanged (regression guard against over-escaping)
* the named-branch variant of `build_tab_config` (uses the same helper)

```
$ cargo nextest run -p warp -E 'test(worktree_path_quoting::)'
11 tests run: 11 passed

$ cargo nextest run -p warp -E 'test(tab_configs::) or test(user_config::)'
91 tests run: 91 passed   (no regression in existing tests)

$ cargo clippy -p warp --tests -- -D warnings
clean

$ cargo fmt -p warp -- --check
clean
```

Sibling PRs in the same shell-escape family: warpdotdev#11391 (warpdotdev#11132) covers the
agent grep / file_glob / is_file_path helpers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cla-bot cla-bot Bot added the cla-signed label May 20, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 20, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 20, 2026

@david-engelmann

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR shell-escapes the static repo-derived prefix for generated worktree commands in build_tab_config and default worktree materialization, with unit coverage for spaces and shell metacharacters.

Concerns

  • ⚠️ [IMPORTANT] The New Worktree modal path still appears to be unfixed: handle_new_worktree_submit builds its TOML via crate::tab_configs::build_worktree_config_toml(...), whose commands still use generated_worktree_path_string(...) directly in app/src/tab_configs/tab_config.rs. That is the + > New worktree flow and will still generate bare /.../My Project/... paths for repos with spaces or metacharacters, so #11144 is only partially fixed.
  • ⚠️ [IMPORTANT] This is a user-facing behavior change, but the PR description has no screenshot or screen recording demonstrating the fixed flow end to end. Please attach visual evidence for the generated worktree tab/config flow.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New Worktree Config fails with spaces in the path

1 participant