improvements: parallelize refreshGit sub-calls and gate polling on focus#166
Merged
jmaxdev merged 1 commit intoTrixtyAI:mainfrom Apr 21, 2026
Merged
Conversation
|
Thanks for the contribution! I'll review it as soon as possible. If you have still changes, please mark this PR as draft and all reviews will be cancelled. Tests reviews will be re-run only when the PR is marked as ready for review. |
There was a problem hiding this comment.
Pull request overview
This PR optimizes the Git Explorer’s periodic refresh behavior by reducing backend git process latency and avoiding refresh work when the app isn’t focused, improving responsiveness and lowering idle CPU usage in the desktop app.
Changes:
- Parallelized the four independent
refreshGitbackend invocations viaPromise.allSettled, handling each result independently. - Updated error handling so
get_git_statusremains the source of truth for “not a repo” / “dubious ownership” fallback behavior. - Tightened polling to require
document.hasFocus()and added an immediate refresh on window focus.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
refreshGit awaited four independent git invocations in sequence (get_git_status, get_git_branches, git_log, git_stash_list). Each spawns its own backend `git` child process, so the chain took roughly the sum of the four call latencies — ~150-400 ms per 5 s cycle on realistic repos. Running them concurrently drops that to roughly the max of any single call. Changes: - Use Promise.allSettled to dispatch the four sub-queries in parallel. A failure in any one no longer aborts the rest (git_log on a repo with zero commits used to throw and silently drop status parsing mid-flight; now each result is handled independently). - The "dubious ownership" detection and the no-repo fallback still key off the get_git_status result; the other three just degrade to empty arrays if they fail. - Tighten the interval guard: poll only when the window is visible AND document.hasFocus() is true. A visible-but-unfocused window (user typing in the browser) was still costing a full git refresh every 5 s, which dominated idle CPU on battery. - Add a focus listener that snaps back to a fresh refresh as soon as the user returns, instead of displaying up-to-5-s-stale data until the next tick.
e875e11 to
18a5139
Compare
matiaspalmac
added a commit
to matiaspalmac/ide
that referenced
this pull request
Apr 21, 2026
…ent overlapping refreshes Addresses review feedback on TrixtyAI#166: the interval tick and the new window-focus handler both checked `gitLoadingRef.current`, which tracks *user-initiated* git operations (commit, checkout, stash). That flag is not set during a polling refresh, so if the user switched focus back while a refresh was still in flight, the focus handler kicked off a second `refreshGit()` and the two polling refreshes raced. Whichever `Promise.allSettled` resolved second clobbered the first's state updates, sometimes displaying stale branch/log data. Added a separate `gitRefreshingRef` that is: - flipped to `true` at the top of `refreshGit` and reset in a `finally` at the bottom, so the flag accurately mirrors the refresh's lifetime including the error/early-return paths. - checked by both the 5 s interval and the focus handler before calling `refreshGit()`, so a pending refresh is never stacked with a new one. The dubious-ownership retry path (which calls `refreshGit()` recursively) clears the flag before the recursive call so the inner refresh isn't a no-op. `gitLoadingRef` stays distinct and continues to gate the interval during user-initiated operations — the two flags track different things and both matter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[Improvement]: Parallelize
refreshGitsub-calls and gate polling on focusDescription
refreshGitinGitExplorerComponent.tsxawaited four independent gitinvocations in sequence:
get_git_statusget_git_branchesgit_loggit_stash_listEach one spawns its own backend
gitchild process, so the chain tookroughly the sum of the four call latencies — measured in the
~150-400 ms range per 5 s poll cycle on realistic repos. Running them
concurrently drops that to roughly the max of any single call, and
the interval itself still ran full-speed while the window was visible
but unfocused, which dominated idle CPU on battery.
Change
All in
apps/desktop/src/addons/builtin.git-explorer/GitExplorerComponent.tsx:Promise.allSettledtogether instead of sequentialawaits. Afailure in any one no longer aborts the rest —
git_logon a repowith zero commits used to throw and silently drop status parsing
mid-flight; now each result is handled independently.
and the no-repo empty-state still key off the
get_git_statusrejection specifically. The other three degrade to empty arrays if
they fail, which matches the old per-call
try/catchfor log andstashes.
document.hasFocus().visibilityState === "visible"was too loose:a visible-but-unfocused window (user typing in the browser) still
cost a full git refresh every 5 s.
window.addEventListener("focus", …)handler that triggers a refresh immediately when the userreturns, instead of showing up-to-5-s-stale data until the next
tick.
Trade-offs
single Rust
get_git_snapshotcommand that runs them concurrentlyinside tokio. That is a larger change (new command, new type
bindings, more CI surface) and is out of scope for this PR — the
JS-side
Promise.allSettledalready captures the performance winwith a single-file diff.
Promise.allSettledfires all four processes even on a non-gitdirectory (the
get_git_statusrejection branch only runs afterall four settle). That is a wash in practice: the three other calls
fail fast on the same "not a git repository" error path the status
call takes.
git tab is active. That is intentional — users expect fresh state
after stepping away — and the existing
gitLoadingRefguardprevents overlapping refreshes from stacking up.
Verification
pnpm tsc --noEmitacross the desktop app → clean.pnpm eslinton the touched file → 0 findings.Promise.allSettledbranches:git_logfails → log clears, rest renders.get_git_statusfails with "dubious ownership" → existing confirmdialog fires and retries.
get_git_statusfails with "not a git repository" → silentempty-state branch, same as before.
Related Issue
Closes #84
Checklist