Skip to content

fix: orbit switch always fetches before checking out a remote branch - #22

Merged
zheng-weihao merged 1 commit into
mainfrom
fix-switch-stale-ref
Jul 28, 2026
Merged

zheng-weihao merged 1 commit into
mainfrom
fix-switch-stale-ref

Conversation

@zheng-weihao

@zheng-weihao zheng-weihao commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

Problem

orbit_ensure_remote_branch skipped the fetch whenever origin/<branch> existed locally. Switching to a branch whose remote history advanced or was force-pushed after the last fetch silently checked out the abandoned commit — and git status looked completely normal, because the upstream is the stale ref, so the staleness is invisible. Continue working there and your new commits stack on abandoned history (rebase/force-push workflows hit this constantly). Hit for real during the #20 work: after a rebase + force-push, switching back to the PR branch checked out the pre-rebase commit.

Fix

Drop the exists-guard in orbit_ensure_remote_branch: always fetch. When the fetch fails (offline), fall back to the local ref with a possibly-stale warning; fail only when there is no local ref at all.

Both call sites benefit:

  • orbit switch <branch> — checks out the current remote head
  • orbit add (default mode) — new worktrees are based on the fresh default branch, not a weeks-old ref

Tests

New regression test in 06_worktree_integration: switch to a remote branch → rewrite it via force-push → switch again from a second workspace (stale ref present in pool) → asserts the new head is checked out and the pool ref is refreshed. Verified red without the fix, green with it.

Full suite: 216/216 ok, make lint clean.

orbit_ensure_remote_branch skipped the fetch whenever origin/<branch>
existed locally, so switching to a branch whose remote history advanced or
was force-pushed after the last fetch silently checked out the abandoned
commit — and git status looked normal because the upstream IS the stale
ref.

Drop the exists-guard: fetch unconditionally, and when the fetch fails
(offline) fall back to the local ref with a possibly-stale warning; fail
only when there is no local ref at all. orbit add default mode goes through
the same path, so worktrees are now always based on the fresh default
branch too.

Regression test: switch to a remote branch, rewrite it via force-push,
switch again from a second workspace — asserts the new head is checked out
(verified red without the fix, green with it).

Signed-off-by: zheng-weihao <zheng-weihao@outlook.com>
@zheng-weihao
zheng-weihao force-pushed the fix-switch-stale-ref branch from 011b28a to 0edb836 Compare July 28, 2026 05:21
@zheng-weihao
zheng-weihao merged commit 03af97a into main Jul 28, 2026
2 checks passed
@zheng-weihao
zheng-weihao deleted the fix-switch-stale-ref branch July 28, 2026 05:26
zheng-weihao added a commit that referenced this pull request Jul 28, 2026
…mode docs

Review follow-ups for #16-#22:

- reconcile: never touch foreign fetch refspecs. The exact-refspec match
  also captured a user-configured wildcard (+refs/heads/*:...) converting
  the pool to full fetch, parsed its branch as '*', found no such remote
  branch, and silently removed it. Git refnames forbid '*', so any glob
  entry is foreign config — now skipped in both phases.
- switch: roll back the just-registered fetch refspec when the remote
  branch turns out not to exist. The leftover violated the invariant
  "every exact refspec points at a real remote branch" and broke every
  bare fetch in the pool until the next reconcile touchpoint.
- remove_fetch_refspec: prefer atomic `git config --unset --fixed-value`
  (git >= 2.26) over unset-all + re-add rebuild, which loses every fetch
  refspec if interrupted mid-rebuild. Older git keeps the rebuild path
  (floor is git 2.20).
- brief extract: recover from an unclosed HTML block after a 50-line
  budget instead of letting it swallow the rest of the README; typed
  fences — a ~~~ line inside a ``` fence is content, not a toggle.
- status: print the raw-mode conversion steering on stderr, closing the
  gap with context/done per the spec-warnings steering registry.
- docs: scoped is the default branch mode everywhere — SKILL.md,
  CONSTRAINTS.md (both sections), USAGE.md, spec-commands, recipes, and
  comparison still said "default = raw" / "scoped opt-in", contradicting
  the workflow section since #18.

Tests: 220/220 (wildcard preservation, failed-switch rollback, unclosed
HTML recovery, typed fences, status steering). Lint clean.

Signed-off-by: zheng-weihao <zheng-weihao@outlook.com>
zheng-weihao added a commit that referenced this pull request Jul 28, 2026
…mode docs (#23)

Review follow-ups for #16-#22:

- reconcile: never touch foreign fetch refspecs. The exact-refspec match
  also captured a user-configured wildcard (+refs/heads/*:...) converting
  the pool to full fetch, parsed its branch as '*', found no such remote
  branch, and silently removed it. Git refnames forbid '*', so any glob
  entry is foreign config — now skipped in both phases.
- switch: roll back the just-registered fetch refspec when the remote
  branch turns out not to exist. The leftover violated the invariant
  "every exact refspec points at a real remote branch" and broke every
  bare fetch in the pool until the next reconcile touchpoint.
- remove_fetch_refspec: prefer atomic `git config --unset --fixed-value`
  (git >= 2.26) over unset-all + re-add rebuild, which loses every fetch
  refspec if interrupted mid-rebuild. Older git keeps the rebuild path
  (floor is git 2.20).
- brief extract: recover from an unclosed HTML block after a 50-line
  budget instead of letting it swallow the rest of the README; typed
  fences — a ~~~ line inside a ``` fence is content, not a toggle.
- status: print the raw-mode conversion steering on stderr, closing the
  gap with context/done per the spec-warnings steering registry.
- docs: scoped is the default branch mode everywhere — SKILL.md,
  CONSTRAINTS.md (both sections), USAGE.md, spec-commands, recipes, and
  comparison still said "default = raw" / "scoped opt-in", contradicting
  the workflow section since #18.

Tests: 220/220 (wildcard preservation, failed-switch rollback, unclosed
HTML recovery, typed fences, status steering). Lint clean.

Signed-off-by: zheng-weihao <zheng-weihao@outlook.com>
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.

1 participant