1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-11-23 22:24:51 +02:00

Trigger immediate background fetch when switching repos (#5047)

If background fetching is on (which it is by default), we usually run
the first background fetch right after opening lazygit, which is nice
because it immediately fetches all the stuff that's new. However, when
switching to a different repo from within lazygit (either with the
recent repos menu or by going into or out of a submodule) we didn't do
that, and you'd have to wait for the next regular background fetch to
come along. I'm finding myself pressing `f` in the Files panel to
manually fetch after entering a submodule, and I shouldn't have to do
that.

This PR makes it so that when you switch repos, we trigger a background
fetch immediately (unless the last one for this repo was less than the
auto-fetch interval ago, in which case it's unnecessary).
This commit is contained in:
Stefan Haller
2025-11-16 17:37:12 +01:00
committed by GitHub
4 changed files with 68 additions and 17 deletions

View File

@@ -49,6 +49,14 @@ type RefresherConfig struct {
FetchInterval int `yaml:"fetchInterval" jsonschema:"minimum=0"`
}
func (c *RefresherConfig) RefreshIntervalDuration() time.Duration {
return time.Second * time.Duration(c.RefreshInterval)
}
func (c *RefresherConfig) FetchIntervalDuration() time.Duration {
return time.Second * time.Duration(c.FetchInterval)
}
type GuiConfig struct {
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-author-color
AuthorColors map[string]string `yaml:"authorColors"`

View File

@@ -17,6 +17,9 @@ type BackgroundRoutineMgr struct {
// we typically want to pause some things that are running like background
// file refreshes
pauseBackgroundRefreshes bool
// a channel to trigger an immediate background fetch; we use this when switching repos
triggerFetch chan struct{}
}
func (self *BackgroundRoutineMgr) PauseBackgroundRefreshes(pause bool) {
@@ -40,7 +43,7 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
if userConfig.Git.AutoRefresh {
refreshInterval := userConfig.Refresher.RefreshInterval
if refreshInterval > 0 {
go utils.Safe(func() { self.startBackgroundFilesRefresh(refreshInterval) })
go utils.Safe(self.startBackgroundFilesRefresh)
} else {
self.gui.c.Log.Errorf(
"Value of config option 'refresher.refreshInterval' (%d) is invalid, disabling auto-refresh",
@@ -76,6 +79,16 @@ func (self *BackgroundRoutineMgr) startBackgroundFetch() {
self.gui.waitForIntro.Wait()
fetch := func() error {
// Do this on the UI thread so that we don't have to deal with synchronization around the
// access of the repo state.
self.gui.onUIThread(func() error {
// There's a race here, where we might be recording the time stamp for a different repo
// than where the fetch actually ran. It's not very likely though, and not harmful if it
// does happen; guarding against it would be more effort than it's worth.
self.gui.State.LastBackgroundFetchTime = time.Now()
return nil
})
return self.gui.helpers.AppStatus.WithWaitingStatusImpl(self.gui.Tr.FetchingStatus, func(gocui.Task) error {
return self.backgroundFetch()
}, nil)
@@ -86,41 +99,53 @@ func (self *BackgroundRoutineMgr) startBackgroundFetch() {
_ = fetch()
userConfig := self.gui.UserConfig()
self.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), self.gui.stopChan, fetch)
self.triggerFetch = self.goEvery(userConfig.Refresher.FetchIntervalDuration(), self.gui.stopChan, fetch)
}
func (self *BackgroundRoutineMgr) startBackgroundFilesRefresh(refreshInterval int) {
func (self *BackgroundRoutineMgr) startBackgroundFilesRefresh() {
self.gui.waitForIntro.Wait()
self.goEvery(time.Second*time.Duration(refreshInterval), self.gui.stopChan, func() error {
userConfig := self.gui.UserConfig()
self.goEvery(userConfig.Refresher.RefreshIntervalDuration(), self.gui.stopChan, func() error {
self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
return nil
})
}
func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
// returns a channel that can be used to trigger the callback immediately
func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan struct{}, function func() error) chan struct{} {
done := make(chan struct{})
retrigger := make(chan struct{})
go utils.Safe(func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
doit := func() {
if self.pauseBackgroundRefreshes {
return
}
self.gui.c.OnWorker(func(gocui.Task) error {
_ = function()
done <- struct{}{}
return nil
})
// waiting so that we don't bunch up refreshes if the refresh takes longer than the
// interval, or if a retrigger comes in while we're still processing a timer-based one
// (or vice versa)
<-done
}
for {
select {
case <-ticker.C:
if self.pauseBackgroundRefreshes {
continue
}
self.gui.c.OnWorker(func(gocui.Task) error {
_ = function()
done <- struct{}{}
return nil
})
// waiting so that we don't bunch up refreshes if the refresh takes longer than the interval
<-done
doit()
case <-retrigger:
ticker.Reset(interval)
doit()
case <-stop:
return
}
}
})
return retrigger
}
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
@@ -134,3 +159,9 @@ func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
return err
}
func (self *BackgroundRoutineMgr) triggerImmediateFetch() {
if self.triggerFetch != nil {
self.triggerFetch <- struct{}{}
}
}

View File

@@ -26,7 +26,7 @@ func (gui *Gui) resetHelpersAndControllers() {
helperCommon := gui.c
recordDirectoryHelper := helpers.NewRecordDirectoryHelper(helperCommon)
reposHelper := helpers.NewRecentReposHelper(helperCommon, recordDirectoryHelper, gui.onNewRepo)
reposHelper := helpers.NewRecentReposHelper(helperCommon, recordDirectoryHelper, gui.onSwitchToNewRepo)
rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon)
refsHelper := helpers.NewRefsHelper(helperCommon, rebaseHelper)
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon)

View File

@@ -12,6 +12,7 @@ import (
"sort"
"strings"
"sync"
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazycore/pkg/boxlayout"
@@ -233,7 +234,6 @@ type GuiRepoState struct {
Modes *types.Modes
SplitMainPanel bool
LimitCommits bool
SearchState *types.SearchState
StartupStage types.StartupStage // Allows us to not load everything at once
@@ -254,6 +254,8 @@ type GuiRepoState struct {
ScreenMode types.ScreenMode
CurrentPopupOpts *types.CreatePopupPanelOpts
LastBackgroundFetchTime time.Time
}
var _ types.IRepoStateAccessor = new(GuiRepoState)
@@ -306,6 +308,16 @@ func (self *GuiRepoState) GetSplitMainPanel() bool {
return self.SplitMainPanel
}
func (gui *Gui) onSwitchToNewRepo(startArgs appTypes.StartArgs, contextKey types.ContextKey) error {
err := gui.onNewRepo(startArgs, contextKey)
if err == nil && gui.UserConfig().Git.AutoFetch && gui.UserConfig().Refresher.FetchInterval > 0 {
if time.Since(gui.State.LastBackgroundFetchTime) > gui.UserConfig().Refresher.FetchIntervalDuration() {
gui.BackgroundRoutineMgr.triggerImmediateFetch()
}
}
return err
}
func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.ContextKey) error {
var err error
gui.git, err = commands.NewGitCommand(