b8465ae1335a8e61656546bc335a7edac468ec90
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9832c0a4a8 |
fix(git): handle worktrees from forked PRs safely (#2693)
* fix(git): create worktrees from forked PRs via refs/pull/<n>/head fallback A worktree created from a linked GitHub PR whose head branch lives in a fork failed when the fork's head repository was missing (deleted fork) or unfetchable (auth, network): the dialog threw 'PR head repository URL is unavailable' before any git command ran, and the server had no fallback to refs/pull/<n>/head, which GitHub serves on the base repository. - NewWorktreeDialog: when pr.headRepo is absent, send a prRef config (refs/pull/<n>/head from origin) instead of throwing; the fork config now also carries prRef so the server can fall back when the fork fetch fails. - git service: fetchPullRequestHeadRef fetches refs/pull/<n>/head into refs/remotes/<remote>/pr-<n>-head (same refspec shape as fetchRemoteBranchRef) and both validateWorktreeCreate and attachGitWorktreeToCandidate fall back to it when the fork path fails; fallback worktrees get --no-track and no upstream config because a PR ref is not pushable. When both paths fail the original fork error surfaces. - Focused tests cover the prRef-only path and the fork-unreachable fallback. Fixes #2422 * fix(git): harden PR worktree fallback against stale fork refs (#12) After a fork fetch fails, resolve immediately from refs/pull/<n>/head instead of accepting a cached remotes/<fork>/<branch> tracking ref. Match the PR base repository by URL (not a hardcoded origin remote), store fetched PR heads under refs/openchamber/pull/<n>/head, and share one existing-mode resolver between validate and create. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(git): make PR head SHA authoritative and namespace private refs Reuse local/remote branches for linked PRs only when their tip matches pr.headSha; otherwise fall through to fork fetch / refs/pull. Store PR heads under refs/openchamber/github/<owner>/<repo>/pull/<n>/head, prefer HTTPS for direct base-repo fallback, and surface composite fork+fallback errors when both paths fail. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * test(ui): assert validate/create forward deleted-fork PR payload fields Guards the dialog wiring regression where validate omitted prRef while create included it, by asserting worktreeManager forwards prRef, prBaseRepoUrl, and related fields for deleted-fork configs. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor(git): always checkout linked PRs from refs/pull/<n>/head Move PR worktree resolution to the server. The UI now sends only pullRequest identity (number + baseRepoUrl + optional head fields); the server always fetches the authoritative PR head and best-effort configures fork upstream afterward. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor(ui): drop PrWorktreeConfig; send PR identity only Delete the prWorktreeConfig module. NewWorktreeDialog maps linked PRs straight to pullRequest identity, skips upstream defaults for that path, and leaves checkout + optional fork tracking to the server. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor(git): linked PRs are {number, baseRepoUrl} only Drop fork upstream / tracking and head/base owner-repo fields from the linked-PR worktree path. Fetch refs/pull/<n>/head, create --no-track, done. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(git): meet #2422 Must/Should without refs/pull fallback Linked PRs send fork identity only; the server provisions pr-<owner>, fetches the head branch, and fails clearly when the fork is missing or unreachable. Local reuse requires a matching headSha. Prefer HTTPS for headRepoUrl. Do not write upstream tracking when the upstream ref was never fetched. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(git): drop invalid upstream fallback and PR branch collisions Remove setBranchTrackingFallback: if upstream fetch fails, leave tracking unset. When a linked PR's head branch already exists locally with a different tip, create pr-<number> instead of git worktree add -b on the colliding name. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * fix(git): strip PR worktree create back to fork-remote provision (#15) Keep the original ensureRemoteName/Url path for linked fork PRs, prefer HTTPS clone URLs, fail clearly when the fork is unreachable, and leave upstream tracking unset when the upstream ref was never fetched. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> |
||
|
|
5d24d6cb2a |
fix: dedupe shared worktree ownership
Cherry-picked from claude branch 7b3bec48 (fix: dedupe shared worktree ownership and title sessions immediately). Only the worktree-dedupe half applies to main: partitionWorktreesByRegisteredProject and its callers (SessionSidebar, ElectronMiniChatApp, MobileApp, MobileSessionsSheet) plus tests and sidebar docs. The session-title half (title sessions immediately) remains on the claude branch: the session-title runtime and its claude-code transcript translator imports do not exist on main and cannot build there until the Claude harness lands. |
||
|
|
3fd6627196 |
feat: move sessions to new worktrees
Add a root-session action that creates a generated worktree from the session directory's current branch, transfers uncommitted changes, and moves the parent session plus its descendants through OpenCode's control-plane API. Reuse existing project/worktree topology and quick-create behavior, keep the UI non-blocking, reconcile live and global session state across directories, and roll back partial moves and failed worktree creation safely. Split worktree bootstrap readiness into directory-created, git-ready, and setup-ready phases across web and VS Code. Session moves wait for Git readiness while existing setup-aware flows continue waiting for full setup completion, and worktree removal is serialized with active bootstrap tasks. Expose the move only for idle root sessions, show localized progress and explanatory tooltips in the sidebar, and keep pending/ready worktree metadata synchronized with authoritative session attachments to avoid stale setup indicators. Add coverage for control-plane payloads, session-state migration, bootstrap phase ordering and compatibility, removal races, progress metadata, and fast-ready attachment races. |
||
|
|
fb173067f5 | fix(worktree): refresh changed discovery metadata | ||
|
|
6d7ea82d86 |
perf(worktree): skip unchanged store updates and content-aware persist (#1992)
* perf(worktree): skip unchanged store updates and content-aware persist - Add content-aware equality check before setState in all three discovery loops (SessionSidebar, ElectronMiniChatApp, MobileApp). Compares Map size and per-entry length + element references — avoids triggering 16+ subscriber re-renders when discovery finds the same worktrees. - Add content-hash guard to persistWorktreeMap subscription with try-catch. Avoids redundant localStorage writes when the Map reference changed but the content is identical. Serialization errors are caught and skipped. Contributes to #1990 * perf(worktree): extract shared worktreeMapsEqual, fix comparison, avoid double serialization - Extract worktreeMapsEqual() into worktreeManager.ts as a shared utility comparing worktree maps by path (not reference identity). This replaces the inline reference-comparison logic in all three discovery loops (SessionSidebar, ElectronMiniChatApp, MobileApp) that was ineffective because readStableProjectWorktrees creates new object instances on each call after cache expiry, making item !== value[i] always true. - Pass pre-serialized JSON to persistWorktreeMap to avoid double JSON.stringify on every persist. The subscriber already computes the serialized string for the content-hash check; pass it through instead of re-serializing inside persistWorktreeMap. - Deduplicate 3 copies of the same comparison logic into the shared util. * refactor(worktree): make worktreeMapsEqual generic over path-bearing type The helper's equality contract is element-wise path comparison, not anything specific to WorktreeMetadata. Generifying on `T extends { path: string }` documents the contract at the type level and keeps it reusable for any future map-of-arrays shape that has a path field. Call sites stay compatible since WorktreeMetadata has a required `path: string`. No runtime change. * refactor(worktree-store): clarify persist hash name and signature Drop the optional preSerialized parameter from persistWorktreeMap — its only caller (the subscriber) already builds the serialized string for the content-compare, so the dual-path body is dead code. persistWorktreeMap now takes the serialized string directly. Rename lastPersistedWorktreeHash → lastPersistedWorktreeSerialized (the variable holds the full JSON string, not a hash) and drop the try/catch around JSON.stringify: it cannot realistically throw on Map.entries() of WorktreeMetadata (no circular refs, no BigInt, no custom toJSON). The try/catch around setItem stays — it can throw on quota errors. No behavior change in the success path. * docs(worktree): trim repeated call-site comments Replace the 5-line explanation block (copy-pasted in all three discovery loops) with a one-liner that points at the worktreeMapsEqual JSDoc. The '16+ subscribers' framing is also dropped — the helper itself is general-purpose and the precise number was fuzzy. * fix(worktree): compare branch in worktreeMapsEqual to avoid stale sidebar label The helper compared entries by path only. An external git checkout between discoveries changes branch (and the derived label / headState) while path stays the same, so the helper returned true and the store update was skipped — leaving a stale branch label in the sidebar until the next worktree create/remove or project switch, since there is no periodic worktree-list refresh. Compare branch in the inner loop alongside path. Tighten the generic constraint to T extends { path: string; branch: string } so the contract is documented at the type level. worktreeStatus is intentionally NOT compared: status transitions go through setStoredWorktreeStatus, which writes a fresh Map reference that the persist subscriber picks up directly. Adding worktreeStatus to the contract would also force the sidebar to detect status changes that the persist path already handles, and would couple this helper to a field whose semantics differ from the discovery path. Fixes the staleness concern raised by openchamber-bot in PR #1992. * test(worktree): cover worktreeMapsEqual edge cases Documents the helper's equality contract and guards against regressions in the path+branch comparison. Eight cases: - two empty maps - identical entries (path and branch match in order) - same path, different branch — the F1 regression case - different paths at the same index - per-project array length mismatch - project-key count mismatch - positional reorder (helper is order-sensitive) - non-first-entry branch difference (subset detection) All 10 tests in the file pass (2 existing + 8 new). * ci: retrigger checks * test(worktree): add benchmark for worktreeMapsEqual and persist path Documents the actual cost of the PR #1992 optimizations on representative sizes (1-1000 worktrees per project, 1-50 projects), so future contributors can reproduce the numbers and detect regressions in the equality helper or the persist subscriber. Run with: `bun run packages/ui/src/lib/worktrees/worktreeManager.bench.ts` Measured on V8 (one example run): - worktreeMapsEqual early-exit (50×20 with first project differing): 412 ns/op vs 33,034 ns/op full sweep — ~80x speedup when any project actually changed. - F1 path+branch overhead vs path-only (10×50): +2.3 µs (+15.8%) on a full sweep; on the early-exit path the F1 cost is irrelevant. - Stringify dedup in persistWorktreeMap subscriber: 67% saved (552 µs per persist on 10×50). This is the main absolute win of the PR. - Content-compare guard: 19-29 ns/op, free relative to the stringify it gates. Bench file is standalone (import.meta.main guard) — does not run as part of `bun test`, does not import React, does not touch localStorage. --------- Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com> |
||
|
|
e0113c637d |
feat: support fast worktree-backed session flows
Add a directory-created fast path for worktree creation so session and send flows can continue once the target directory exists while Git attachment and bootstrap finish in the background. Track bootstrap status explicitly in shared UI contracts, including pending, ready, and failed states. Background watchers now surface failures and timeouts, update stored worktree metadata, and keep web and VS Code runtime behavior in parity. Move GitHub issue/PR worktree sessions and assistant-answer fork sessions onto the unified send path so provider, model, agent, and variant selections are preserved. The assistant-answer fork dialog can optionally create a worktree outside VS Code. Make worktree deletion dialogs close after linked-session cleanup while removing the worktree in the background, and clean up failed fast-create artifacts safely without recursively deleting user or agent-written files. Validation: bun test packages/ui/src/lib/worktrees/worktreeBootstrap.test.ts packages/ui/src/lib/worktrees/worktreeManager.test.ts; bun run type-check; bun run lint. |
||
|
|
570ae9dbc8 |
fix: keep worktree sessions in the right group
Prevents stale worktree lists from overwriting newly created worktrees Uses the created worktree path when selecting linked worktree sessions |