Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
49 changes: 30 additions & 19 deletions src/commands/worktree/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,9 @@ 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`, though `git worktree add` would DWIM the bare
/// form on its own — so the qualification is load-bearing for the validation,
/// not for the creation.
Comment thread
worktrunk-bot marked this conversation as resolved.
Resolved
fn resolve_base_ref(
repo: &Repository,
base: &str,
Expand Down Expand Up @@ -1044,24 +1045,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