mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Fix Files Panel artefacts during rebase commands (#5661)
Several commands (rewording or amending an earlier commit, custom patch operations, etc.) are implemented by starting an interactive rebase that stops at a commit, amending it, and continuing. When no conflict occurs, the user isn't meant to notice a rebase happened at all. But a background file refresh can fire while the rebase is mid-flight and render a dirty working copy of whatever the behind-the-scenes rebase is doing (e.g. applying a custom patch). To fix this, we pause the background routines for the duration of any waiting-status operation — exactly the window in which lazygit is driving the git operation itself and will refresh once at the end. The boundary is also right for the conflict case: when a rebase stops on a conflict the operation returns, the pause releases, and background refreshes resume for the interactive resolution that follows.
This commit is contained in:
commit
fcaf1512bd
|
|
@ -3,6 +3,7 @@ package gui
|
|||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gocui"
|
||||
|
|
@ -13,17 +14,27 @@ import (
|
|||
type BackgroundRoutineMgr struct {
|
||||
gui *Gui
|
||||
|
||||
// if we've suspended the gui (e.g. because we've switched to a subprocess)
|
||||
// we typically want to pause some things that are running like background
|
||||
// file refreshes
|
||||
pauseBackgroundRefreshes bool
|
||||
// When this is greater than zero, the background routines (e.g. file refresh)
|
||||
// skip their work. We pause them while the gui is suspended (e.g. for a
|
||||
// subprocess) and while lazygit is itself driving a git operation that would
|
||||
// otherwise be caught mid-flight (see the waiting-status helpers). It's a
|
||||
// count rather than a bool because these pause scopes can overlap.
|
||||
pauseRefreshesCount atomic.Int32
|
||||
|
||||
// a channel to trigger an immediate background fetch; we use this when switching repos
|
||||
triggerFetch chan struct{}
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) PauseBackgroundRefreshes(pause bool) {
|
||||
self.pauseBackgroundRefreshes = pause
|
||||
if pause {
|
||||
self.pauseRefreshesCount.Add(1)
|
||||
} else {
|
||||
self.pauseRefreshesCount.Add(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) backgroundRefreshesPaused() bool {
|
||||
return self.pauseRefreshesCount.Load() > 0
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||
|
|
@ -124,7 +135,7 @@ func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan stru
|
|||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
doit := func(retriggered bool) {
|
||||
if self.pauseBackgroundRefreshes {
|
||||
if self.backgroundRefreshesPaused() {
|
||||
return
|
||||
}
|
||||
self.gui.c.OnWorker(func(gocui.Task) error {
|
||||
|
|
|
|||
|
|
@ -66,12 +66,22 @@ func (self *AppStatusHelper) WithWaitingStatus(message string, f func(gocui.Task
|
|||
}
|
||||
|
||||
func (self *AppStatusHelper) WithWaitingStatusImpl(message string, f func(gocui.Task) error, task gocui.Task) error {
|
||||
// A waiting status means lazygit is driving a git operation itself (often
|
||||
// one that internally runs a rebase and continues it). Pause the background
|
||||
// routines for its duration so they don't refresh from an intermediate
|
||||
// state and reveal, say, the half-finished history of a reword.
|
||||
self.c.PauseBackgroundRefreshes(true)
|
||||
defer self.c.PauseBackgroundRefreshes(false)
|
||||
|
||||
return self.statusMgr().WithWaitingStatus(message, self.renderAppStatus, func(waitingStatusHandle *status.WaitingStatusHandle) error {
|
||||
return f(appStatusHelperTask{task, waitingStatusHandle})
|
||||
})
|
||||
}
|
||||
|
||||
func (self *AppStatusHelper) WithWaitingStatusSync(message string, f func() error) error {
|
||||
self.c.PauseBackgroundRefreshes(true)
|
||||
defer self.c.PauseBackgroundRefreshes(false)
|
||||
|
||||
return self.statusMgr().WithWaitingStatus(message, func() {}, func(*status.WaitingStatusHandle) error {
|
||||
stop := make(chan struct{})
|
||||
defer func() { close(stop) }()
|
||||
|
|
|
|||
|
|
@ -68,6 +68,14 @@ func (self *InlineStatusHelper) WithInlineStatus(opts InlineStatusOpts, f func(g
|
|||
visible := view.Visible && self.windowHelper.TopViewInWindow(context.GetWindowName(), false) == view
|
||||
if visible && context.IsItemVisible(opts.Item) {
|
||||
self.c.OnWorker(func(task gocui.Task) error {
|
||||
// An inline status is just a waiting status rendered on the item
|
||||
// rather than in the bottom line, so it gets the same treatment:
|
||||
// pause the background routines while we drive the operation. (The
|
||||
// off-screen branch below goes through WithWaitingStatus, which
|
||||
// already does this.)
|
||||
self.c.PauseBackgroundRefreshes(true)
|
||||
defer self.c.PauseBackgroundRefreshes(false)
|
||||
|
||||
self.start(opts)
|
||||
defer self.stop(opts)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ func (self *guiCommon) Resume() error {
|
|||
return self.gui.resume()
|
||||
}
|
||||
|
||||
func (self *guiCommon) PauseBackgroundRefreshes(pause bool) {
|
||||
self.gui.BackgroundRoutineMgr.PauseBackgroundRefreshes(pause)
|
||||
}
|
||||
|
||||
func (self *guiCommon) Context() types.IContextMgr {
|
||||
return self.gui.State.ContextMgr
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ type IGuiCommon interface {
|
|||
Suspend() error
|
||||
Resume() error
|
||||
|
||||
// Pause or resume the background routines. Calls nest, so every pause must be balanced
|
||||
// by a resume.
|
||||
PauseBackgroundRefreshes(pause bool)
|
||||
|
||||
Context() IContextMgr
|
||||
ContextForKey(key ContextKey) Context
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue