Detect external repo changes via background polling#5662
Open
stefanhaller wants to merge 10 commits into
Open
Conversation
The schema annotated refreshInterval and fetchInterval with minimum=0, but the background routines reject a value of 0 (they require interval > 0 and otherwise log it as invalid and disable the feature). So 0 is not actually a valid value; switch to exclusiveMinimum=0 so the schema matches what the code accepts.
For certain kinds of performance investigations it is useful to see this, and doesn't terribly pollute the log, so just do this always.
Several downstream conditions in Refresh() relied on multi-scope predicates to express "if X is in scope, Y also needs refreshing". This makes it hard to add new code that needs to ask "does this refresh re-read refs?", because the answer involves mirroring one of those predicates and keeping them in sync forever. Expand the co-refreshing relationships once, up front, right after the scope set is built. The downstream conditions then collapse to single-scope checks against the (now-expanded) set. Behavior is preserved. Two of the scattered multi-scope conditions are intentionally left as-is because they express subsumption rather than co-refresh (one branch already does the work of another internally — expanding would cause double-refresh), and one expresses mid-function coupling on a flag set inside the COMMITS/BRANCHES block.
A cheap fingerprint of local branches and HEAD that future code can poll to detect when refs have moved externally. Branches come from a porcelain for-each-ref. HEAD is read directly from .git/HEAD: that avoids spawning a child process and captures the symref-or-hash distinction we need to tell "detached at X" apart from "on a branch pointing at X" — they share a commit hash, which is exactly the situation at the end of a rebase when HEAD reattaches to the branch. The reftable backend doesn't keep a real .git/HEAD (it writes a fixed stub), so when we see that stub or the file is unreadable we fall back to porcelain commands, which are backend-agnostic. Uses DontLog so a future polling caller won't spam the command log. Not yet wired up to any caller.
Two settings to control the upcoming background polling mechanism: - git.autoDetectExternalChanges (default true) is the on/off switch, parallel to autoFetch/autoRefresh - refresher.externalChangeCheckInterval (default 2 seconds) is the poll cadence Disabling is the bool's job, not a magic 0 interval, matching the existing convention. Not yet referenced by any code.
Add the storage and snapshot-update half of the external-change-detection mechanism. RefreshHelper now keeps a mutex-protected snapshot string and exposes accessors for it; Refresh captures a fresh snapshot at the start of any refresh whose scope set includes COMMITS or BRANCHES. We capture before reading the git state, not after. Capturing after would let an external change that lands between the git state read and the snapshot (say, the next step of a rebase running in another terminal) leave the stored snapshot newer than what we actually rendered; the poller would then see no difference and never refresh again, stranding the UI on the intermediate state. Capturing first keeps the snapshot from running ahead of the render, so if disk moves during the refresh the next poll catches it. No reader of the snapshot exists yet — the polling goroutine that consumes it comes in a later commit. Keeping the snapshot hook in its own commit isolates the invariant that the snapshot stays in sync with what the UI has observed, which is what makes the poller's change-detection predicate work across in-app commands and focus-in refreshes.
Add a 2-second background poll that calls Status.RefsSnapshot and compares against the snapshot stored at the end of the last refs- touching refresh. On a diff, trigger a full refresh — same scope as the focus-in handler, because once we know something changed externally we can't be sure what (an agent might have created a worktree or stashed something alongside the commit we detected). Refresh runs in SYNC mode because goEvery already serializes iterations via <-done: a slow refresh delays the next tick naturally instead of letting work stack. The post-refresh hook from the previous commit updates the snapshot, so in-app commands don't cause the next poll to spuriously re-fire. Disabled in the integration test config, like autoRefresh and autoFetch, because demo replays make repo changes throughout the run; at 2-second cadence the resulting full refreshes compete with the demo's own choreography and push some demos past their 40-second timeout. Also list the two new config keys in checkForChangedConfigsThatDontAutoReload so a config edit warns the user that lazygit needs a restart.
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.
Lazygit refreshes its UI when its terminal window gets the focus, which is enough for the situation where the user makes a commit in their IDE or in another git client. It isn't enough for the case that a coding agent makes commits in the background. Improve this by doing a light-weight poll of the git state in the background, and refresh when a change is detected.
Addresses #5554.