From 1268a589d6996e3fac012907203efc59ab0936f1 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 9 Jul 2026 08:27:35 +0200 Subject: [PATCH] Write the command log on the UI thread LogAction and LogCommand are called from git worker goroutines (every command a worker runs logs itself, and controllers log an action before kicking off their worker), where they set the Extras view's Autoscroll flag and append to GuiLog while the UI thread reads both when it lays out and draws the view. Bounce the writes onto the UI thread instead. Use the background variant so the bounce doesn't count towards lazygit being busy: writing the command log is incidental display work, and a foreground task would let an in-flight log write refuse a concurrent repo switch (the same reason view-buffer renders and toasts are backgrounded). Ordering between successive log calls is preserved by the bounce FIFO. Co-Authored-By: Claude Opus 4.8 (1M context) --- pkg/gui/command_log_panel.go | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/pkg/gui/command_log_panel.go b/pkg/gui/command_log_panel.go index 8f2e06b98..6f7976c3e 100644 --- a/pkg/gui/command_log_panel.go +++ b/pkg/gui/command_log_panel.go @@ -27,10 +27,20 @@ func (gui *Gui) LogAction(action string) { return } - gui.Views.Extras.Autoscroll = true + // LogAction and LogCommand are called both from the UI thread and from git + // worker goroutines, so bounce the writes onto the UI thread: they touch the + // view's autoscroll flag and the GuiLog slice, which the layout/draw code + // reads. Ordering between successive log calls is preserved by the FIFO the + // bounce enqueues onto. It's a background bounce because writing the command + // log is incidental display work that must not count towards lazygit being + // busy (otherwise it could block a repo switch). + gui.onUIThreadBackground(func() error { + gui.Views.Extras.Autoscroll = true - gui.GuiLog = append(gui.GuiLog, action) - fmt.Fprint(gui.Views.Extras, "\n"+style.FgYellow.Sprint(action)) + gui.GuiLog = append(gui.GuiLog, action) + fmt.Fprint(gui.Views.Extras, "\n"+style.FgYellow.Sprint(action)) + return nil + }) } func (gui *Gui) LogCommand(cmdStr string, commandLine bool) { @@ -38,17 +48,23 @@ func (gui *Gui) LogCommand(cmdStr string, commandLine bool) { return } - gui.Views.Extras.Autoscroll = true - textStyle := theme.DefaultTextColor if !commandLine { // if we're not dealing with a direct command that could be run on the command line, // we style it differently to communicate that textStyle = style.FgMagenta } - gui.GuiLog = append(gui.GuiLog, cmdStr) indentedCmdStr := " " + strings.ReplaceAll(cmdStr, "\n", "\n ") - fmt.Fprint(gui.Views.Extras, "\n"+textStyle.Sprint(indentedCmdStr)) + + // See the comment in LogAction: bounce onto the UI thread since we may be + // called from a git worker, in the background so it can't block a repo switch. + gui.onUIThreadBackground(func() error { + gui.Views.Extras.Autoscroll = true + + gui.GuiLog = append(gui.GuiLog, cmdStr) + fmt.Fprint(gui.Views.Extras, "\n"+textStyle.Sprint(indentedCmdStr)) + return nil + }) } func (gui *Gui) printCommandLogHeader() {