diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 2e90818be..5d8601861 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -505,6 +505,8 @@ func (gui *Gui) handleRefreshFiles() error { } func (gui *Gui) refreshStateFiles() error { + state := gui.State + // keep track of where the cursor is currently and the current file names // when we refresh, go looking for a matching name // move the cursor to there. @@ -517,22 +519,24 @@ func (gui *Gui) refreshStateFiles() error { files := gui.GitCommand.GetStatusFiles(commands.GetStatusFileOptions{}) // for when you stage the old file of a rename and the new file is in a collapsed dir + state.FileManager.RWMutex.Lock() for _, file := range files { if selectedNode != nil && selectedNode.Path != "" && file.PreviousName == selectedNode.Path { - gui.State.FileManager.ExpandToPath(file.Name) + state.FileManager.ExpandToPath(file.Name) } } - gui.State.FileManager.SetFiles(files) + state.FileManager.SetFiles(files) + state.FileManager.RWMutex.Unlock() if err := gui.fileWatcher.addFilesToFileWatcher(files); err != nil { return err } if selectedNode != nil { - newIdx := gui.findNewSelectedIdx(prevNodes[prevSelectedLineIdx:], gui.State.FileManager.GetAllItems()) + newIdx := gui.findNewSelectedIdx(prevNodes[prevSelectedLineIdx:], state.FileManager.GetAllItems()) if newIdx != -1 && newIdx != prevSelectedLineIdx { - newNode := gui.State.FileManager.GetItemAtIndex(newIdx) + newNode := state.FileManager.GetItemAtIndex(newIdx) // when not in tree mode, we show merge conflict files at the top, so you // can work through them one by one without having to sift through a large // set of files. If you have just fixed the merge conflicts of a file, we @@ -541,17 +545,17 @@ func (gui *Gui) refreshStateFiles() error { // conflicts: the user in this case would rather work on the next file // with merge conflicts, which will have moved up to fill the gap left by // the last file, meaning the cursor doesn't need to move at all. - leaveCursor := !gui.State.FileManager.InTreeMode() && newNode != nil && + leaveCursor := !state.FileManager.InTreeMode() && newNode != nil && selectedNode.File != nil && selectedNode.File.HasMergeConflicts && newNode.File != nil && !newNode.File.HasMergeConflicts if !leaveCursor { - gui.State.Panels.Files.SelectedLineIdx = newIdx + state.Panels.Files.SelectedLineIdx = newIdx } } } - gui.refreshSelectedLine(gui.State.Panels.Files, gui.State.FileManager.GetItemsLength()) + gui.refreshSelectedLine(state.Panels.Files, state.FileManager.GetItemsLength()) return nil } diff --git a/pkg/gui/filetree/file_manager.go b/pkg/gui/filetree/file_manager.go index f2e828a61..825ee0b01 100644 --- a/pkg/gui/filetree/file_manager.go +++ b/pkg/gui/filetree/file_manager.go @@ -1,6 +1,8 @@ package filetree import ( + "sync" + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/sirupsen/logrus" @@ -12,6 +14,7 @@ type FileManager struct { showTree bool log *logrus.Entry collapsedPaths CollapsedPaths + sync.RWMutex } func NewFileManager(files []*models.File, log *logrus.Entry, showTree bool) *FileManager { @@ -20,6 +23,7 @@ func NewFileManager(files []*models.File, log *logrus.Entry, showTree bool) *Fil log: log, showTree: showTree, collapsedPaths: CollapsedPaths{}, + RWMutex: sync.RWMutex{}, } } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 93fed0525..987bdbc7e 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -482,7 +482,7 @@ func (gui *Gui) Run() error { go utils.Safe(gui.startBackgroundFetch) } - gui.goEvery(time.Second*time.Duration(userConfig.Refresher.RefreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules) + gui.goEvery(time.Millisecond*time.Duration(userConfig.Refresher.RefreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules) g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout())) @@ -649,7 +649,7 @@ func (gui *Gui) startBackgroundFetch() { prompt: gui.Tr.NoAutomaticGitFetchBody, }) } else { - gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error { + gui.goEvery(time.Millisecond*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error { err := gui.fetch(false) return err }) diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index c219e1001..9dc0e29b1 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -79,6 +79,14 @@ func (gui *Gui) dispatchSwitchToRepo(path string) error { gui.GitCommand = newGitCommand gui.g.Update(func(*gocui.Gui) error { + // these two mutexes are used by our background goroutines (triggered via `gui.goEvery`. We don't want to + // switch to a repo while one of these goroutines is in the process of updating something + gui.Mutexes.FetchMutex.Lock() + defer gui.Mutexes.FetchMutex.Unlock() + + gui.Mutexes.RefreshingFilesMutex.Lock() + defer gui.Mutexes.RefreshingFilesMutex.Unlock() + gui.resetState("") return nil