Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/content/docs/switch.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ $ wt switch https://github.com/owner/repo/pull/123 # ...or paste the PR's URL

The `--create` flag creates a new branch from `--base` — the default branch unless specified. Without `--create`, the branch must already exist. Switching to a remote branch (e.g., `wt switch feature` when only `origin/feature` exists) creates a local tracking branch.

`--create` from a remote base is the exception. For the branch it creates, `wt` defaults git's `branch.autoSetupMerge` to `simple` instead of git's own `true`, so the new branch tracks its base only when the two share a name: `--create release --base origin/release` tracks `origin/release`, while `--create feature --base origin/release` — and the bare `--base release` that resolves to it — gets no upstream. Git's default would have `feature` track `origin/release`, so under `push.default = upstream` a bare `git push` would push the new work to `release`. A `branch.autoSetupMerge` set in git config takes precedence over that default. Publishing such a branch takes `git push --set-upstream origin <branch>`, or git's `push.autoSetupRemote = true` set once, after which a bare `git push` from the new worktree publishes it and configures its tracking.
One rule decides the upstream, whatever `branch.autoSetupMerge` is set to: a new branch tracks the remote branch it starts from only when the two share a name. Switching to `origin/feature` shares it, so that branch tracks. `--create` need not: `--create release --base origin/release` tracks `origin/release`, while `--create feature --base origin/release` — and the bare `--base release` that resolves to it — gets no upstream. Git's default would have `feature` track `origin/release`, so under `push.default = upstream` a bare `git push` would push the new work to `release`. Publishing such a branch takes `git push --set-upstream origin <branch>`, or git's `push.autoSetupRemote = true` set once, after which a bare `git push` from the new worktree publishes it and configures its tracking.

## Creating worktrees

Expand Down
2 changes: 1 addition & 1 deletion plugins/worktrunk/skills/worktrunk/reference/switch.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ $ wt switch https://github.com/owner/repo/pull/123 # ...or paste the PR's URL

The `--create` flag creates a new branch from `--base` — the default branch unless specified. Without `--create`, the branch must already exist. Switching to a remote branch (e.g., `wt switch feature` when only `origin/feature` exists) creates a local tracking branch.

`--create` from a remote base is the exception. For the branch it creates, `wt` defaults git's `branch.autoSetupMerge` to `simple` instead of git's own `true`, so the new branch tracks its base only when the two share a name: `--create release --base origin/release` tracks `origin/release`, while `--create feature --base origin/release` — and the bare `--base release` that resolves to it — gets no upstream. Git's default would have `feature` track `origin/release`, so under `push.default = upstream` a bare `git push` would push the new work to `release`. A `branch.autoSetupMerge` set in git config takes precedence over that default. Publishing such a branch takes `git push --set-upstream origin <branch>`, or git's `push.autoSetupRemote = true` set once, after which a bare `git push` from the new worktree publishes it and configures its tracking.
One rule decides the upstream, whatever `branch.autoSetupMerge` is set to: a new branch tracks the remote branch it starts from only when the two share a name. Switching to `origin/feature` shares it, so that branch tracks. `--create` need not: `--create release --base origin/release` tracks `origin/release`, while `--create feature --base origin/release` — and the bare `--base release` that resolves to it — gets no upstream. Git's default would have `feature` track `origin/release`, so under `push.default = upstream` a bare `git push` would push the new work to `release`. Publishing such a branch takes `git push --set-upstream origin <branch>`, or git's `push.autoSetupRemote = true` set once, after which a bare `git push` from the new worktree publishes it and configures its tracking.

## Creating worktrees

Expand Down
2 changes: 1 addition & 1 deletion skills/worktrunk/reference/switch.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ $ wt switch https://github.com/owner/repo/pull/123 # ...or paste the PR's URL

The `--create` flag creates a new branch from `--base` — the default branch unless specified. Without `--create`, the branch must already exist. Switching to a remote branch (e.g., `wt switch feature` when only `origin/feature` exists) creates a local tracking branch.

`--create` from a remote base is the exception. For the branch it creates, `wt` defaults git's `branch.autoSetupMerge` to `simple` instead of git's own `true`, so the new branch tracks its base only when the two share a name: `--create release --base origin/release` tracks `origin/release`, while `--create feature --base origin/release` — and the bare `--base release` that resolves to it — gets no upstream. Git's default would have `feature` track `origin/release`, so under `push.default = upstream` a bare `git push` would push the new work to `release`. A `branch.autoSetupMerge` set in git config takes precedence over that default. Publishing such a branch takes `git push --set-upstream origin <branch>`, or git's `push.autoSetupRemote = true` set once, after which a bare `git push` from the new worktree publishes it and configures its tracking.
One rule decides the upstream, whatever `branch.autoSetupMerge` is set to: a new branch tracks the remote branch it starts from only when the two share a name. Switching to `origin/feature` shares it, so that branch tracks. `--create` need not: `--create release --base origin/release` tracks `origin/release`, while `--create feature --base origin/release` — and the bare `--base release` that resolves to it — gets no upstream. Git's default would have `feature` track `origin/release`, so under `push.default = upstream` a bare `git push` would push the new work to `release`. Publishing such a branch takes `git push --set-upstream origin <branch>`, or git's `push.autoSetupRemote = true` set once, after which a bare `git push` from the new worktree publishes it and configures its tracking.

## Creating worktrees

Expand Down
51 changes: 32 additions & 19 deletions src/commands/worktree/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,11 @@ fn parse_ref_shortcut(input: &str) -> Option<(RefType, u32)> {
/// returns the remote-qualified form so the validation in
/// [`resolve_switch_target`] doesn't reject `wt switch -c new --base
/// remote-only-branch`. Git's rev-parse doesn't auto-expand `foo` to
/// `refs/remotes/origin/foo`. The new branch's upstream is unset downstream
/// to keep `git push` from targeting the base.
/// `refs/remotes/origin/foo`. `git worktree add` does DWIM the bare form, but
/// destructively: given `-b <name>` it drops the `-b` and creates the remote
/// branch's own name instead. So qualifying here is what keeps `--create new-wt
/// --base remote-only-branch` on `new-wt`, which
/// `test_switch_create_with_remote_only_base` pins.
fn resolve_base_ref(
repo: &Repository,
base: &str,
Expand Down Expand Up @@ -1044,24 +1047,34 @@ fn execute_switch(
let worktree_path_str = worktree_path.to_string_lossy();
let mut args: Vec<&str> = Vec::new();

// Safety: for an explicitly requested branch, default
// `branch.autoSetupMerge` to `simple` rather than git's
// `true`. Under `true`, `git worktree add -b feature
// origin/main` sets `feature` to track `origin/main`, so a
// bare `git push` under `push.default = upstream` pushes the
// new work to `main` (#713). `simple` is git's own narrower
// mode: it sets tracking only when the new branch's name
// matches the remote branch's, which is exactly the case
// where inherited tracking is correct. An explicit setting
// wins — `wt` picks a different default, it does not override
// the user's configuration.
// Safety: `wt` decides tracking for a branch it creates,
// rather than the user's `branch.autoSetupMerge`. `-c`
// outranks every config file, so the outcome is the same on
// every machine. Git's `simple` is the rule `wt` wants: an
// upstream only when the new branch's name matches the remote
// branch it starts from, which is exactly when inherited
// tracking is right. Git's default `true`, and `always`, would
// have `--create feature --base origin/release` track
// `origin/release`, so a bare `git push` under
// `push.default = upstream` pushes the new work to `release`
// (#713); `false` and `inherit` would deny the tracking that is
// the point of a same-named branch — `--create release --base
// origin/release`, and every DWIM `wt switch feature` from
// `origin/feature`, which is the tracking branch the docs
// promise. Both paths run under the one rule, so `wt switch`
// has one answer to state.
//
// Only the `--create` paths need it. The DWIM paths below
// create `feature` from `origin/feature`, where the names
// match and `simple` and `true` agree.
if *create_branch && repo.config_value("branch.autoSetupMerge")?.is_none() {
args.extend(["-c", "branch.autoSetupMerge=simple"]);
}
// `wt` sets git's rule rather than picking `--track` /
// `--no-track` from a name comparison of its own, because only
// git maps the base back to a remote branch through the fetch
// refspec. Splitting `<remote>/<branch>` reads
// `team/fork/release` as branch `fork/release`, so the verdict
// inverts both ways, and a refspec that renames into a
// sub-namespace does the same. `--track` is also a hard demand
// where `simple` is best-effort: it fails the whole command
// when the base isn't refspec-mapped, as in a single-branch
// clone holding a hand-fetched ref.
args.extend(["-c", "branch.autoSetupMerge=simple"]);

args.extend(["worktree", "add"]);

Expand Down
10 changes: 5 additions & 5 deletions src/commands/worktree/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ pub enum CreationMethod {
/// When `--base pr:N` / `--base mr:N` (same-repo) is paired with `--create`,
/// the user's intent is "create a new branch tracking the PR/MR's source
/// branch on the remote", so `git push` from the new worktree pushes back
/// to that PR/MR. `git worktree add -b new <bare-name>` doesn't set up
/// tracking on its own (only `<remote>/<branch>` triggers DWIM, and even
/// then we'd unset it via the issue-#713 safety check), so we capture the
/// (remote, branch) pair here and configure tracking explicitly after
/// `git worktree add` succeeds. `None` for any other base resolution.
/// to that PR/MR. That branch may carry any name, and
/// `branch.autoSetupMerge = simple` sets an upstream only where the new
/// branch shares it, so we capture the (remote, branch) pair here and
/// configure tracking explicitly after `git worktree add` succeeds.
/// `None` for any other base resolution.
base_pr_upstream: Option<(String, String)>,
},
/// Fork PR/MR: fetch from refs/pull/N/head or refs/merge-requests/N/head,
Expand Down
Loading
Loading