Skip to content

perf(git): batch collect_status_local to <= 7 subprocesses per module - #13

Merged
log0u7 merged 10 commits into
mainfrom
feat/perf-tdd
Sep 21, 2026
Merged

log0u7 merged 10 commits into
mainfrom
feat/perf-tdd

Conversation

@log0u7

@log0u7 log0u7 commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Summary

Performance and dead-code pass with zero behavior change: collect_status_local (runs once per module inside status/update/check loops) drops from 9 git subprocesses to 7 (6 with a declared branch), the 200-line health check becomes a table of check Funcrefs, never-read state and unreachable branches are removed, and the documented-but-ignored opts.force of the check command is now real. Public API surface untouched, no breaking change.

Type of change

  • feat (new feature)
  • fix (bug fix)
  • docs
  • test
  • refactor
  • chore / ci

Related issue

n/a (performance + dead-code sweep from a repo audit; no issue tracking it)

How was this tested?

Vim 9.1 (Debian). TDD: the subprocess budget test (tests/perf.vader) was written RED against 2.2.10, then made GREEN by the batching. Characterization tests pinned exact statuses/messages before each refactor (health statuses, update skip messages, log line format).

  • make test-ci passes (214/214)
  • make test-async passes (16/16)
  • make test-install-smoke passes (install/declare changes)
  • Verified manually on Vim 8.2+ / Linux
  • New logic covered by a Vader test, written test-first when possible
    (bug fixes: failing regression test before the fix)

Checklist

  • Commits follow Conventional Commits (type(scope): subject)
  • README.md / doc/plugin_manager.txt / CHANGELOG.md updated if behavior changed
  • No new network call at startup (opt-in only, behind a g:plugin_manager_* flag defaulting to off)
  • Linux only, Vim 8.2 floor respected (no Vim 9+ features, guard v:version < 802 untouched)

collect_status_local runs once per module inside status/update/check
loops. Worst case today: 9 subprocesses (per-module .gitmodules config
read + upstream/show-ref cascade). Pin budgets <= 7 / <= 6 (Red) and
keep a behavior guard on the returned status dict.
collect_status_local runs once per module inside status/update/check
loops. Two cuts:
- the per-module `git config -f .gitmodules` branch read now comes from
  the parse_modules cache (mtime-cached); the direct read is kept only
  when parse_modules is unavailable (vim_dir without .git) so hostile
  branch values still reach sanitize_branch (#9 parity)
- the sequential upstream/show-ref cascade (rev-parse @{upstream},
  show-ref origin/main, origin/master, rev-parse origin/HEAD,
  show-ref origin/<branch>) collapses into one for-each-ref call with
  the same selection order; its sha listing also replaces the remote
  rev-parse for origin/* refs (verbatim .gitmodules branches keep
  their rev-parse - they resolve LOCAL refs, v2.2.10 behavior)

Worst case per module: 9 subprocesses -> 7 (6 with .gitmodules branch).
tests/perf.vader pins the budget and the returned status dict.
Tier B dead-code sweep, zero behavior change except where noted:
- async: drop never-read job state fields ('id', 'started', 'queued')
  and a dead local (l:opts)
- plugin: drop g:plugin_manager_periodic_timer (written, never read)
- syntax: drop highlight keywords no code emits (Synced, Skipped,
  timed out, Stashing changes); 'Pending' stays (update.vim uses it)
- ui: drop the 'pending' glyph key, the legacy numeric status branch of
  get_status_glyph (all callers/tests pass keywords now), the
  unreachable replace path of update_sidebar and the list branch of
  log_detail
- delete ftdetect/pluginmanager.vim (only matched a literal file named
  PluginManager; the sidebar sets its own filetype) and the Makefile.test
  forwarding shim
- check: opts.force is now real (was documented, never read): force
  fetches and leaves the cache untouched so the next startup check
  re-fetches (tests/check.vader pins the contract)
health#execute was a single 200-line function of sequential if/else
blocks. Checks are now a table of Funcrefs (s:checks) returning
[status, label, detail] items; three custom checks (git version, log
probe, submodule scan) moved to named helpers. Report order, messages
and the both-problems submodule case are unchanged - pinned first by a
characterization test (tests/health.vader: exact statuses on a healthy
fixture).
…e-read

- sanitize_url/sanitize_cmd share one regex impl (s:sanitize_url_impl)
- log debug/warn/trace share s:leveled; the doubled level prefix
  ('DEBUG:c:DEBUG:msg') collapses to a single level marker - log line
  format now '| comp | EXTERNAL | DEBUG:msg' (tests/log.vader pinned)
- backup: drop a redundant get_config('vim_dir') re-read inside
  s:backup_vimrc_file
- rejected as behavior-changing (audit over-flagged): declare footer
  dedup (sync/async messages differ), header+info extraction (zero net
  lines), add.vim catch dedup (marginal), inline helptags -> helper
  (install outcome on helptags failure would change)
The single-plugin and all-plugins update paths duplicated three blocks
verbatim: the pinned-module handoff (s:handle_pin), the detached-HEAD /
custom-branch skip text (s:skip_status, UI copy now pinned by
tests/update.vader) and the pull command construction (s:pull_cmd).
Path-specific logic that differs (pull-step UI text 'Pulling changes'
vs 'Updating', stash bookkeeping, helptags+auto-commit in the single
path, batch finalize) stays per-path: merging the flows would trade
real behavior differences for churn. Full pipeline unification is
deliberately NOT done (audit over-flagged it).
- CONTRIBUTING: drop ftdetect/ and Makefile.test from the tree, list
  the new perf.vader and health.vader characterization suites
- CHANGELOG: [Unreleased] documents the subprocess batching, the
  health refactor, the single level prefix in debug/trace log lines,
  the now-real opts.force and the dead-code removals
…h setup

Two CI-only failures, both git-environment dependent:
- the fixture renamed HEAD to main AFTER the first commit; on stock
  images (init.defaultBranch unset -> master) that left HEAD an unborn
  symref, so `update-ref refs/remotes/origin/main HEAD` failed and the
  cascade found no remote branch. Point HEAD at main BEFORE the commit.
- `git config -f` calls ran without -C: git does repo discovery from
  the cwd first, so a cwd inside an unrelated (or broken) repository
  could leak in or abort. Both parse_modules and the collect_status_local
  fallback now use `git -C <vim_dir> config -f ...`.

Verified in an AlmaLinux 9 container (vim 8.2.2637, git 2.52): 214/214
Vader + 16/16 async smoke.
@log0u7
log0u7 merged commit c2f650f into main Sep 21, 2026
32 checks passed
@log0u7
log0u7 deleted the feat/perf-tdd branch September 21, 2026 18:37
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