From d52c0a604f830ffac5340aaac662bf575cd64690 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 2 Aug 2026 15:38:57 +0200 Subject: [PATCH] Give task commands a Terminate method instead of exposing their process The task stop path terminates the still-running command by pulling its *os.Process out of the Cmd interface and applying one global strategy (TerminateProcessGracefully) to it. That shape can't accommodate the upcoming fix for orphaned process trees on Windows: there, stopping a pty task requires terminating the entire process tree via a job object whose handle lives with the pty, not with the process. And the two Cmd implementations genuinely need different strategies anyway: a process-group kill (the likely future fix for #5675 on Unix) is only safe for pty children, which run as session leaders, while plain commands share lazygit's own process group. So let each Cmd implementation decide how to terminate itself, and drop GetProcess, which had no other callers. No change in behavior. Co-Authored-By: Claude Fable 5 --- pkg/gui/pty.go | 6 +++--- pkg/tasks/tasks.go | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/gui/pty.go b/pkg/gui/pty.go index 2184621ae..719f5c348 100644 --- a/pkg/gui/pty.go +++ b/pkg/gui/pty.go @@ -49,9 +49,9 @@ type ptyCmd struct { wait func() error } -func (p ptyCmd) Wait() error { return p.wait() } -func (p ptyCmd) String() string { return p.cmd.String() } -func (p ptyCmd) GetProcess() *os.Process { return p.process } +func (p ptyCmd) Wait() error { return p.wait() } +func (p ptyCmd) String() string { return p.cmd.String() } +func (p ptyCmd) Terminate() error { return oscommands.TerminateProcessGracefully(p.process) } // Some commands need to output for a terminal to active certain behaviour. // For example, git won't invoke the GIT_PAGER env var unless it thinks it's diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go index 3a964c838..3768e0c19 100644 --- a/pkg/tasks/tasks.go +++ b/pkg/tasks/tasks.go @@ -4,7 +4,6 @@ import ( "bufio" "fmt" "io" - "os" "os/exec" "sync" "sync/atomic" @@ -24,7 +23,9 @@ import ( type Cmd interface { Wait() error String() string - GetProcess() *os.Process + // Terminate makes the process stop early, as gracefully as the platform + // allows. It doesn't wait for the process to exit. + Terminate() error } // ExecCmd adapts *exec.Cmd to Cmd. @@ -32,8 +33,11 @@ type ExecCmd struct { *exec.Cmd } -func (c ExecCmd) GetProcess() *os.Process { - return c.Process +// Terminate sends SIGTERM on Unix. On Windows it does nothing, so a stopped +// command keeps running until it next writes to its (by then closed) output +// pipe. +func (c ExecCmd) Terminate() error { + return oscommands.TerminateProcessGracefully(c.Process) } // This file revolves around running commands that will be output to the main panel @@ -213,10 +217,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (Cmd, io.Reader), prefix // when flicking through several very long diffs when diff.algorithm = histogram is // being used, in which case multiple git processes continue to calculate expensive // diffs in the background even though they have been stopped already. - // - // Unfortunately this will do nothing on Windows, so Windows users will have to live - // with the higher CPU usage. - if err := oscommands.TerminateProcessGracefully(cmd.GetProcess()); err != nil { + if err := cmd.Terminate(); err != nil { self.Log.Errorf("error when trying to terminate cmd task: %v; Command: %v", err, cmd.String()) }