Skip to content

perf(list): build the ref snapshot from the branch inventories - #4290

Merged
max-sixty merged 3 commits into
mainfrom
ref-snapshot-reuse
Sep 27, 2026
Merged

max-sixty merged 3 commits into
mainfrom
ref-snapshot-reuse

Conversation

@max-sixty

Copy link
Copy Markdown
Owner

wt list and the picker open their task pool only once the post-skeleton ref snapshot is built. That snapshot rescanned refs/heads/, which the phase before the skeleton had already scanned into the cached branch inventory, and then refs/remotes/, one after the other. In the picker the preview panes' comparison base ran the same pair of scans a third time.

Repository::inventory_snapshot(base) replaces capture_refs_with_ahead_behind and builds the snapshot from the cached inventories, scanning only one that nobody has fetched yet. Tasks now resolve refs to the same SHAs the rows were built from. The comparison base builds its snapshot the same way, and the Remote⇅ upstream primer reads the same inventories, so it runs beside the snapshot instead of after it. capture_refs still scans fresh, and every command that moves a ref keeps using it; the docs that pointed at "a RefSnapshot captured at the moment of read" now name it.

The phase before the skeleton still scans refs/remotes/ only when remote rows are shown. Doing it unconditionally would make first paint wait on it in a repo with many remote branches and few worktrees, so plain wt list runs that scan once, after the skeleton.

Measured on a repo with 50 worktrees, 52 local branches and 103 remote-tracking branches, alternating with main, medians in ms from the start of collect:

skeleton task pool opens last result
wt list, main 58 93 240
wt list, this branch 58 78 221
picker, main 49 120 —
picker, this branch 38 47 —

for-each-ref scans per run went from 3 to 2 for wt list and from about 4.4 to 2 for the picker. The picker's skeleton difference is likely noise; nothing before its skeleton forks less.

inventory_snapshot_reuses_cached_inventories checks that the snapshot reads the cached scans; the existing ahead-behind tests now run through inventory_snapshot.

This was written by Claude Code on behalf of max-sixty

🤖 Generated with Claude Code

max-sixty and others added 3 commits September 26, 2026 21:39
The task pool opens only after the post-skeleton ref snapshot is built, and
that snapshot rescanned `refs/heads/` (already scanned before the skeleton)
and then `refs/remotes/`, one after the other. `inventory_snapshot` replaces
`capture_refs_with_ahead_behind` and builds from the cached branch
inventories, scanning only one nobody has fetched yet. The snapshot now
agrees with the rows built from the same inventories.

The `Remote⇅` primer reads the same inventories, so it runs beside the
snapshot instead of after it. The preview panes' comparison base builds its
snapshot the same way, which removes the picker's second `refs/remotes/`
scan. `capture_refs` still scans fresh for commands that move refs.

The pre-skeleton phase still scans `refs/remotes/` only when remote rows are
shown, so first paint never waits on refs it doesn't render.

On leaf (load ~50), median skeleton-to-pool: `wt list` 53ms -> 36ms, picker
86ms -> 16ms.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`inventory_snapshot` is as of the branch inventories, so the docs that told
code needing a current SHA to use "a RefSnapshot captured at the moment of
read" now name `capture_refs`. The primer's inventory read also drops its
early-return branch, which no test reaches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@max-sixty
max-sixty merged commit 4b8e6f9 into main Sep 27, 2026
38 checks passed
@max-sixty
max-sixty deleted the ref-snapshot-reuse branch September 27, 2026 06:01
max-sixty pushed a commit that referenced this pull request Sep 27, 2026
Four comments no longer matched the code after yesterday's `wt list`
perf work. This PR changes only comments, no behavior.

- **`src/commands/list/collect/mod.rs`**: the stale-default-branch note
said the picker skips the local branch inventory (#5) when branches are
hidden, so it can fork one `for-each-ref`. Since #4290, the first scan
phase primes `repo.local_branches()` unconditionally. The check reads
the cached inventory, so it never forks.
- **`src/commands/list/ci_status/mod.rs`**: `detect_uncached` said
"PR/MR detection always runs". Since #4289, `platform::detect_ci` skips
the lookup for a branch that was never pushed (`may_head_pr`).
- **`tests/integration_tests/push.rs`**: a test doc named
`finalize_worktree`, which #4286 renamed to `finalize_worktrees`.
- **`src/shell_exec.rs`**: `Cmd::run_concurrently` said at most
`max_concurrent_commands()` children run at once. A background batch
takes a single semaphore permit while other threads hold the rest, so
the cap holds per batch, not per process. The comment now says so.

No test accompanies this, since only comments change.

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.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.

2 participants