Repository navigation
feat(worktree): show checkout progress while a worktree is created - #24093
Conversation
Review status: ready for review (head 3607360)The problem. Creating a worktree in a large repo spends most of its time in the file checkout that What changes for the user. For a local create whose checkout lasts more than about a second, the panel now reads "Checking out files… 23%" with a thin progress bar under it, and the sidebar row shows the same text (the row doesn't change height). When git finishes writing files, the bar goes away and the label returns to "Creating worktree…" for the last few steps. Fast checkouts, sparse creates, SSH and CLI/automation creates, and folder workspaces look the same as before. In a non-English Orca, every status line on the create card is now translated, so the label never switches language partway through a create. Fixed during review
Deferred (low priority, not blocking)
What was verified
Not verified
|
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Passive git stderr observer —
GitExecOptions.onStderris added on the shared runner as a second, passive listener on theexecFilestderr stream;execFileCaptureToTerminationomits it andgitExecFileAsyncrejects theonStderr+terminationBarriercombination up front, so capture, errors, limits, and timeout handling are unchanged. - Progress-record reader —
src/shared/git-progress-records.tsparsesTitle: NN% (x/y)records across chunk boundaries,\r/\n/\r\nredraws, the narrow-COLUMNS title-on-its-own-line layout, and a bounded unfinished-line tail. - Checkout policy —
worktree-checkout-progress.tsreports increases only and closes on the, done.record;worktree-add.tswires it togit worktree addonly when a listener is supplied and the add is not--no-checkout, closing it once the process settles. - Delivery — the local create attaches the reader only when
args.creationIdis set; the meter rides the existingcreateWorktree:progressevent as an optionalcheckoutfield (null= done), and the renderer bridge stores it or clears it on every event. - UI and i18n — the create panel shows a
Progressbar labelled by the status line (aria-labelledby); all six create-phase labels now go throughtranslate(), with entries added to all six locale catalogs, and Retry/Continue clear the meter.
I read the complete diff, traced the main-to-renderer state path (including the hasChange no-op merge and the status === 'creating' && phase === 'creating' gate that hides a meter left from a failed or earlier phase), and ran the touched suites locally — the progress parser, checkout policy, IPC forwarding, bridge, panel, and real-git tests all pass. The documented exclusions (SSH, CLI/runtime, sparse, LFS filtering) are deliberate and correctly fenced by noCheckout/creationId.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
A local create spends most of its time in the checkout `git worktree add` performs, and the card only said "Creating worktree…" for that whole time. Git already writes its "Updating files: N%" meter into our stderr pipe, so the add call reads it passively (same command line and environment) and the create card shows "Checking out files… N%" with a thin bar. All six create labels are translated so a create never switches language mid-flight. Rebuilt on main from the version stacked on the create-gating branch. On main a create can also claim a prepared checkout; that path never runs `git worktree add`, so it keeps today's "Creating worktree…" label with no bar, and a missed claim falls back to the plain add, which shows the bar.
3607360 to
1e5b7ce
Compare
Comments Outside DiffThese findings could not be posted inline.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to
|
…eckout progress Claiming a prepared checkout never runs `git worktree add`, so the create card keeps its label with no bar; this pins that at the finalize step, including the retarget `reset --hard`.
|
Moved onto What the user sees: the same as before the move. A local create whose checkout takes more than about a second shows "Checking out files… N%" with a thin bar; SSH, CLI/runtime, sparse and folder-workspace creates are unchanged. One thing that is different on How it was moved:
Checks: |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Regression test for prepared-checkout finalize —
worktree-preparation-base-oid.test.tsgains a test that passesonCheckoutProgressintofinalizePreparedWorktreeon the retarget path and pins that claiming a prepared checkout reports no meter: nogitExeccall carriesonStderrand the listener is never invoked.
This increment is test-only. I ran the file (6/6 pass) and pnpm tc:node (clean).
One note: Greptile's P1 at line 117 is a false positive. The refs/heads/main base resolves targetHead to the refreshed oid while the prepared HEAD probe resolves to the original, so the retarget reset does run and the expect(...reset...).toBe(true) assertion holds — verified by the passing run.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
# Conflicts: # src/main/git/command-runner/git-exec-file.ts # src/main/git/worktree-add.ts # src/preload/api/worktrees-bridge.ts # src/renderer/src/i18n/locales/en.json # src/renderer/src/i18n/locales/es.json # src/renderer/src/i18n/locales/fr.json # src/renderer/src/i18n/locales/ja.json # src/renderer/src/i18n/locales/ko.json # src/renderer/src/i18n/locales/zh.json
…ract Drops unit tests that mirror the implementation; the real-git flow, the parser captures, the cross-version Git contract and the create card test remain.





ELI5
When you create a worktree in a big repo, most of the wait is Git writing out every file.
Until now the create card just said "Creating worktree…" the whole time, so you couldn't tell whether it was moving, stuck, or almost done.
Git already reports how far along it is; Orca was throwing that away.
This PR shows it: the card now reads "Checking out files… 45%" with a thin progress bar that fills as Git writes the files.
Refreshed on current main (2026-10-15)
mainhad moved a long way since this PR was approved on 10-02, somainwas merged in (no rebase, no force-push).git-exec-file.ts),worktree-add.ts, the preload bridge and the six locale catalogs.main's runner changes (fix(git): reduce queries and preserve data across execution hosts #24602, fix(git): preserve SSH review context and worktree ownership #24945, and fix(wsl): run WSL git with Windows Git line-ending settings on checkouts Windows Git wrote #27778's Windows/WSL checkout routing) are kept as they are; the stderr observer sits on top of them. The catalogs were merged key by key, keeping every keymainadded.mainis line-for-line the same as the approved version (checked file by file).What Changed
The problem. Checking out a large repo takes 5–15 s on a busy Mac, and minutes when the disk is contended.
For that whole time, the sidebar row and the in-tab creation panel showed only "Creating worktree…", a spinner and a clock.
Orca's style guide asks for real progress on anything that takes more than 3 s.
Why it happened. Git already writes a progress meter (
Updating files: 45% (13770/30600)) into the pipe Orca reads whilegit worktree addruns.Orca collected that text only to use as an error message and dropped it when the create succeeded.
What is different for users:
git worktree add, so there is no meter to read. The bar appears on creates that do their own checkout: when no spare matches (another base, an existing branch, an expired or already-used spare, or the spare isn't armed yet), or when the spare fails and the create falls back to a normal checkout.How (mechanism):
git worktree addhas no--progressflag (Git 2.25 and 2.50 both reject it). It doesn't need one: it checks out through a childgit reset --hard, which prints its meter into a pipe after about a second on every Git from 2.25 on, Windows included. Orca only reads bytes Git was already writing.GitExecOptions.onStderr). The runner still collects stderr exactly as before, so error messages, output limits, timeouts and exit handling are unchanged. If the observer throws, the runner catches it, logs once and detaches it, so progress reporting can never fail a create. Asking for an observer together with the termination-barrier mode, which doesn't support it, fails before the command is queued instead of being silently ignored.src/shared/git-progress-records.ts). It splits on\rand\n, keeps a bounded tail of an unfinished line, handles lines split across reads, and handles the narrow-terminal layout where Git prints the title on its own line.src/main/git/worktree-checkout-progress.ts). It reports increases only, which drops the one same-percentage line Git reprints after each one-second tick. Every increase is reported as soon as Git prints it, so a checkout produces at most about 100 reports. There is no timer, and reporting stops when Git reports done or the command settles, so nothing can arrive after a failure.git worktree addcalls only when a pending card is waiting for it. The result travels on the existingcreateWorktree:progressevent as one new optional field,checkout(nullmeans done); no new channel. The renderer shows the bar only while the create's status and phase are both "creating", and Retry and Continue clear it.Progresscomponent at the slimh-1.5height already used by the update card, labelled by the status text. The new label and the create's five existing status labels go throughtranslate(), with entries in all six locale catalogs, so the label never switches language partway through a create.Why
worktree add. The create keeps the runner's queueing, WSL routing, timeouts, process cleanup, tracing and error text. A separate spawn path would have to rebuild all of that.Not in this PR
mainan SSH repo is served by an Orca server on that host, and these creates all go through the runtime create path. That path emits no phase events and has no pending card in the renderer to update; it only sends a "created" event once the create finishes. Adding a meter there means a new optional field on what the server publishes, which needs its own change with mixed-version clients in mind.git worktree addmeter to show. Two waits on that path still show only "Creating worktree…": waiting for a spare that is still being checked out, and moving the spare to a newer base commit (git reset --hard).worktree add --no-checkout, then agit checkoutthat prints no progress into a pipe unless given--progress. Changing that command is out of scope, and sparse checkouts are small by design.Where this fits
On 2026-09-28, creating a worktree took 80–268 s instead of 5–15 s, mostly because a create waited on an unfinished spare checkout while Orca's own background Git competed for the disk.
This PR doesn't fix that wait; it only makes a create's own checkout visible.
It lands on its own on
mainand doesn't depend on the other slow-create PRs (#23699, #24085, #22356).Differences from the common pattern
This matches the common pattern for checkout progress: it reads Git's own "Updating files: N% (x/y)" meter from the stderr of the existing
git worktree add, treats\ras a line break, hooks the reader into the shared Git runner while stderr is still captured for error messages, sends every change with no time-based limit as one more field on the create's existing progress channel, and shows the percentage as text.Linked Issue
No separate issue; this was listed as deferred work in #23699 and lands independently on
main.Visual Proof
Electron QA on head 7e54872 on a separate Mac (the M4Air), in a throwaway app profile and home, with no credentials. Grok drove the app over the Chrome DevTools protocol, and every create used a blank terminal. The fixture was a ~30k-file repo where 300 files go through a slow checkout filter, so a cold checkout takes several seconds. Each create was matched to its trace, which records whether it reused a prepared spare checkout or ran its own
git worktree add.A create that ran its own checkout (trace: no spare available,
git worktree add14.5 s): the panel shows "Checking out files… 19%" with the bar, and the sidebar row shows the same text at its usual height.The same create at 46%. Across the run the percentage only went up, from 9% to 99%. Then the bar went away, the label went back to "Creating worktree…", and the terminal opened.
The worktree opened in its terminal, with no leftover bar or percentage.
git worktree add(6 s, 14 s and 40 s), the meter rose with no step backwards, and each worktree opened.git worktree addlimit and the create failed. The error card showed no leftover bar or percentage. That limit is onmainand this PR doesn't change it.Testing
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Real Git binaries: a case in
src/shared/git-binary-compatibility.test.ts, which CI runs against Git 2.25.5, 2.38.1 and 2.49.1. It checks that a pipedworktree addwrites records the reader parses, ending in "done".src/shared/git-progress-records.test.tsuses the exact bytes captured from Git 2.25.5, including the narrow-terminal layout, re-splits each capture at every byte offset, ignores other stderr, and keeps a line that never ends bounded.src/main/git/worktree-checkout-progress-real-git.test.tsrunsaddWorktreeagainst real Git: the meter rises through 100% and then reports done; a failing add produces the byte-identical error and stderr with or without the observer; a throwing listener doesn't fail the create.src/main/ipc/worktrees-create-event-timing.test.ts: the local create forwards Git's meter oncreateWorktree:progressto the pending create it belongs to (fails if the wiring inworktree-remote.tsis removed).WorktreeCreationPanel.test.tsx: the bar's value and label while checking out; no bar without a meter or on a failed create.After the refresh: those files plus the
worktree addsuites, the pending-create and create-flow suites, and the runner admission suite pass locally (macOS); CI ran lint, typecheck and the full test shards on head 7e54872.Platforms: macOS locally and on the QA machine; Linux and Windows through CI. Windows' meter is covered by reading Git for Windows' source; WSL wasn't run.
Review
A fresh reviewer re-checked the merge result against the approved version: the production diff is unchanged,
main's runner changes are intact, the observer still reaches the real Git process on every localworktree addroute, no new create path was missed, and no spare-checkout path can report a meter. One suggestion (pin the path from Git's meter to the card's event) was taken.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
wsl.exe), the card falls back to today's label.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)