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:
@@ -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"`
|
||||
|
||||
@@ -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{}{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user