Skip to content

perf: parallelize picker annotations and add spinners for slow git ops#21

Merged
RodrigoEspinosa merged 1 commit into
masterfrom
perf/picker-parallel-spinner
Jun 29, 2026
Merged

perf: parallelize picker annotations and add spinners for slow git ops#21
RodrigoEspinosa merged 1 commit into
masterfrom
perf/picker-parallel-spinner

Conversation

@RodrigoEspinosa

Copy link
Copy Markdown
Owner

Why

Two issues with the recent additions to wt:

  • The interactive picker (and wt -l) felt slow on repos with many worktrees.
  • Deleting a worktree appeared to freeze until it finished, with no feedback.

The picker's cost was structural, not the language: annotate_worktrees ran git status + git rev-list serially, once per worktree, before fzf even appeared.

What changed

Parallel + streaming picker

  • Split per-worktree decoration into annotate_one, fanned out with xargs -P across all cores, streaming rows into fzf as they finish. N × git status serial → ~ wall-clock, and fzf paints immediately.
  • Branch/path are passed as NUL-delimited args (-0 -n2) so names/paths with spaces survive and the tab separator can't be mangled (this had also been silently breaking the ! dirty flag).
  • Trade-off: row order is now non-deterministic; fzf sorts on match anyway, so it's invisible in the picker.

Spinners for slow git operations

  • A spinner runs while git worktree remove, git worktree add, and wt pr (network fetch + checkout) execute.
  • Non-TTY (scripts/CI/tests): falls back to a plain run with a one-line wt: <action>... breadcrumb. stdout stays reserved for the result path; the cursor is restored on Ctrl-C; exit codes propagate unchanged.

Version bumped to 0.6.1; CHANGELOG updated.

Note on stack

I looked into rewriting in Go/Rust for speed — it wouldn't help. The time is git stat-ing files, not Bash (measured startup: 0.002s). A port shelling out to git the same way would be just as slow. The only real reason to switch would be single-binary distribution, which isn't what these issues were about.

Verification

  • All 57 bats tests pass; shellcheck clean.
  • Smoke-tested live: parallel annotate with correct */!/↑↓ flags, spaced base-dir paths render as one field, create emits only the path on stdout, TTY spinner animates + cleans up.

🤖 Generated with Claude Code

The interactive picker and `wt -l` computed each worktree's status
(`git status` + `git rev-list`) serially before fzf appeared, blocking on
repos with many worktrees. Split the per-worktree decoration into
annotate_one and fan it out with `xargs -P`, streaming rows into fzf as
they finish. Branch and path are passed as NUL-delimited args so values
with spaces survive and the tab separator can't be mangled.

Slow git operations no longer look frozen: a spinner runs while
`git worktree remove`, `git worktree add`, and `wt pr` (fetch + checkout)
execute. Falls back to a plain run with a one-line breadcrumb when output
isn't a terminal, keeping stdout reserved for the result path.

Bump version to 0.6.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RodrigoEspinosa RodrigoEspinosa merged commit 81cd27e into master Jun 29, 2026
3 checks passed
@RodrigoEspinosa RodrigoEspinosa deleted the perf/picker-parallel-spinner branch June 29, 2026 13:03
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