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 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-08-02 15:38:57 +02:00
parent 9b22c7dc77
commit d52c0a604f
2 changed files with 12 additions and 11 deletions

View file

@ -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

View file

@ -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())
}