fix(worktree): create branches with --no-track and auto-setup remote#1563
Merged
Conversation
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Orca <help@stably.ai>
- Probe push.autoSetupRemote with `git config --get` before writing so a deliberate user value at any scope (local/global/system) is preserved. - Include worktree path in the warn log for failed config writes. - Add test pinning the preserve-existing-value behavior. - Remove stray 00-review-context.md committed during review tooling. Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Orca <help@stably.ai>
Treat only exit code 1 from `git config --get push.autoSetupRemote` as "key unset". Other read failures (corrupt config, locked file, parse error) now re-throw to the outer warn handler instead of being silently treated as unset and overwriting whatever value the user actually has. Also: add test for the non-unset read-error path; convert the "preserves existing value" test from `.some()` predicates to a full-array `toEqual` matching sibling-test style; explicitly mock `config --get` (with code: 1) in the sparse-failure rollback test so it exercises the intended branch instead of the helper's empty- stdout fallthrough; document in the design notes that addSparseWorktree's rollback intentionally does not unset push.autoSetupRemote. Co-authored-by: Orca <help@stably.ai>
Why: addWorktree's post-create config probe has two ordering invariants worth pinning so a future refactor can't silently regress them: (1) `git config --get` succeeding with empty stdout still counts as "already set" so we don't overwrite an explicit empty value, and (2) the entire config block is skipped when `worktree add` itself rejects. Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Orca <help@stably.ai>
… version, add empty-stdout parity test JSDoc on local addWorktree now flags the push.autoSetupRemote side effect; both paths cross-reference each other so the next change keeps them in lockstep. Relay comment clarifies that the git version that matters is the SSH host's, not the client's. Adds the missing empty-stdout-as-already-set parity test on the relay side. Co-authored-by: Orca <help@stably.ai>
Stray file from local review workflow; should not ship in this PR. Co-authored-by: Orca <help@stably.ai>
Resolved conflicts in src/relay/git-handler.ts and src/relay/git-handler.test.ts: kept this PR's --no-track + push.autoSetupRemote state machine but moved it into addWorktreeOp in the extracted git-handler-worktree-ops.ts (per main's #1672 split), dropped the unused `track` param, and dropped the stale this.context.validatePath calls (context is _context post-#1672). Co-authored-by: Orca <help@stably.ai>
677f7a7 to
e46a686
Compare
Co-authored-by: Orca <help@stably.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
git worktree add --no-track, so an unpublished branch isn't reported as "behind by N" against its base ref bygit statusor by tools/agents that read upstream state.push.autoSetupRemote=true(locally, only when not already set at any scope) so a plaingit pushfrom the terminal createsorigin/<branch>and sets the upstream automatically — matching modern-git ergonomics without forcing-u.git config --getbefore writing, and only treat exit code 1 ("key unset") as a green light to write. Any other read failure (corrupt config, locked file, parse error) re-throws to the warn handler so we don't silently overwrite a deliberate user value.git push -uonce.--no-track+ probe-and-write state machine now runs on the relay side (inaddWorktreeOpinsrc/relay/git-handler-worktree-ops.ts, called fromGitHandler.addWorktreeinsrc/relay/git-handler.ts), so SSH-mounted repos get the samegit status/git pushUX as local repos. The optionaltrackfield is removed from the provider interface and the SSH IPC payload (one in-tree caller updated).true, existing value is preserved (including blank-string values), write failure warns without throwing, a non-unset read error is treated as a real failure (no--local set), and the config block is skipped entirely whenworktree additself fails. Mirror coverage exists for both the local and relay handlers.Verification
Unit tests: all passing.
src/main/git/worktree.test.ts(21 tests) — localaddWorktreestate machine.src/main/git/remove-worktree.test.ts(11 tests) — removal path.src/relay/git-handler.test.ts(29 tests) — relay handler, including 6 newaddWorktreecases mirroring the local matrix (--no-trackarg ordering, write-when-unset, preserve-existing, empty-stdout = "already set", non-unset read error short-circuit, write-failure warn-only, no-config-on-worktree-add-fail).src/main/providers/ssh-git-provider.test.ts(20 tests) — SSH provider IPC payload (already omitstrack).Combined run: 81/81 passing post-merge with
main.Live Electron verification — pre-merge (Orca dev build, repo
test-repos/my-git-projwithpush.autoSetupRemoteinitially unset at every scope):autoSetupRemoteunsetgit statusclean (no "behind");push.autoSetupRemote=truewritten locallybranch.<name>.remote/branch.<name>.mergekeys (i.e.--no-tracktook effect)git config --get-regexpempty)push.autoSetupRemote=falsepreset locallyfalse); branch still has no upstreamgit statuson new worktree## brennanb2025/...only)removeWorktree(id, force=true)Live Electron re-verification — post-merge (same setup, after merging
origin/maininto the branch on 2026-05-11):autoSetupRemoteunsetpush.autoSetupRemote=truewritten locallybranch.<name>.remote/.mergekeysconfig --get-regexpexit 1)push.autoSetupRemote=false, then create worktreefalse; branch still has no upstreamgit status -bfirst line## <branch>only — no[behind N]removeWorktree(id, force=true)Test artifacts cleaned up after each round (worktrees removed, branches deleted,
push.autoSetupRemoterestored to its initial unset state).Live remote-push verification (against personal fork
brennanb2025/orca, fresh worktree at/tmp/orca-fork-autosetup-test-*):git worktree add --no-track -b <branch> <wt> origin/maingit status -bshows nobranch.upstream/branch.abline)git config --get push.autoSetupRemotegit config --local push.autoSetupRemote true<repo>/.git/configextensions.worktreeConfiginvolved)git push(no-u) on the new commitorigin/<branch>AND sets upstream[new branch]+branch '...' set up to track 'origin/...'git rev-parse --abbrev-ref @{u}after pushorigin/<branch>Live SSH-relay verification (driving
GitHandler.addWorktreedirectly against a real bare-origin + working-clone setup — same code that runs on the SSH host; an in-process invocation exercises the entire relay state machine end-to-end since the SSH layer is just message transport):push.autoSetupRemoteunset locallygit config --getgit.addWorktreewithbase: 'origin/main'worktree add --no-track -b ... origin/main, then probe +--local set truebranch.<name>.remote/.mergekeys (--get-regexpexits 1)push.autoSetupRemoteafter creationtrue(written by relay)git status -b --porcelainfirst line## <branch>only — no[behind N]push.autoSetupRemote=false(local scope)false; relay does not overwriteworktree additself fails (duplicate branch)No regressions observed.
AI-agent confusion check (Claude Code spawned in a fresh worktree off
maincreated by Orca after this PR is in effect — the failure mode this PR is designed to fix):git status -b→ sees[behind N]→ suggestsgit pull/git rebasebefore letting you startgit rev-list --left-right --count, reports "no local work, no divergence" — no spurious sync suggestiongit status -b --porcelain=v2 --branchand tell me whatbranch.upstreamreports. Is this branch tracking anything?"branch.upstream origin/mainwould be present → agent confidently reports "tracking origin/main" (wrong-shaped — branch was never pushed)branch.upstreamline → agent correctly reads "this branch is not tracking any remote branch" and self-corrects its earlier "1 behind origin/main" answergit pushwork here, or do I really need-u origin <branch>? Check the repo's git config for any relevant push settings before answering."push.autoSetupRemoteset → agent says you need-u origin <branch>push.autoSetupRemote=true(local), correctly concludes plaingit pushwill create the remote and set upstream automatically; calls out the git ≥2.37 requirement and notesgit pullstill needs an upstream until the first pushThe agent reasons from observed git state rather than misreading
git statusas ground truth — both halves of the bug (false-positive "behind" framing and the-uceremony) are invisible to a calm agent and explainable when probed directly.Test plan
pnpm test src/main/git/worktree.test.tspnpm test src/main/git/remove-worktree.test.tspnpm test src/relay/git-handler.test.tspnpm test src/main/providers/ssh-git-provider.test.tsgit status(no "behind" line) — verified pre-merge and post-merge.git config --local push.autoSetupRemote false, create a worktree, confirm the local config is not overwritten — verified pre-merge and post-merge.git pushfrom a new worktree createsorigin/<branch>and sets upstream automatically — verified end-to-end againstbrennanb2025/orca.addWorktreeproduces the same end state as local (--no-tracktook effect,push.autoSetupRemote=truewritten,git statusshows no "behind", existing user value preserved, no config writes whenworktree addfails) — verified end-to-end against real git.branch.upstreamvia porcelain v2, (c) decide whethergit pushneeds-u. No spurious sync suggestions, correct read of "no upstream", correct read ofpush.autoSetupRemote=true— verified.Made with Orca 🐋