From 75a8c0c73e1ff0789c29e67629d93345d9d6b047 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 23 May 2025 15:02:48 +0200 Subject: [PATCH 1/2] Pass cmdObj instead of task to processOutput This is a preparation for the next commit, where we will need more from the cmdObj in processOutput. --- pkg/commands/oscommands/cmd_obj_runner.go | 6 +++--- pkg/commands/oscommands/cmd_obj_runner_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/commands/oscommands/cmd_obj_runner.go b/pkg/commands/oscommands/cmd_obj_runner.go index 6acd8d0fb..c8500d93d 100644 --- a/pkg/commands/oscommands/cmd_obj_runner.go +++ b/pkg/commands/oscommands/cmd_obj_runner.go @@ -10,7 +10,6 @@ import ( "time" "github.com/go-errors/errors" - "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/sasha-s/go-deadlock" "github.com/sirupsen/logrus" @@ -340,7 +339,7 @@ func (self *cmdObjRunner) runAndDetectCredentialRequest( tr := io.TeeReader(handler.stdoutPipe, cmdWriter) go utils.Safe(func() { - self.processOutput(tr, handler.stdinPipe, promptUserForCredential, cmdObj.GetTask()) + self.processOutput(tr, handler.stdinPipe, promptUserForCredential, cmdObj) }) }) } @@ -349,9 +348,10 @@ func (self *cmdObjRunner) processOutput( reader io.Reader, writer io.Writer, promptUserForCredential func(CredentialType) <-chan string, - task gocui.Task, + cmdObj *CmdObj, ) { checkForCredentialRequest := self.getCheckForCredentialRequestFunc() + task := cmdObj.GetTask() scanner := bufio.NewScanner(reader) scanner.Split(bufio.ScanBytes) diff --git a/pkg/commands/oscommands/cmd_obj_runner_test.go b/pkg/commands/oscommands/cmd_obj_runner_test.go index c906cea3f..280ba213b 100644 --- a/pkg/commands/oscommands/cmd_obj_runner_test.go +++ b/pkg/commands/oscommands/cmd_obj_runner_test.go @@ -120,8 +120,8 @@ func TestProcessOutput(t *testing.T) { reader := strings.NewReader(scenario.output) writer := &strings.Builder{} - task := gocui.NewFakeTask() - runner.processOutput(reader, writer, toChanFn(scenario.promptUserForCredential), task) + cmdObj := &CmdObj{task: gocui.NewFakeTask()} + runner.processOutput(reader, writer, toChanFn(scenario.promptUserForCredential), cmdObj) if writer.String() != scenario.expectedToWrite { t.Errorf("expected to write '%s' but got '%s'", scenario.expectedToWrite, writer.String()) From d5bd30474c732fea14edce3af5f215c03af3f409 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 23 May 2025 14:55:31 +0200 Subject: [PATCH 2/2] Kill background fetch when it requests a passphrase Previously we would enter a newline at the password prompt, which would cause the fetch to fail. The problem with this was that if you have many remotes, the fetch would sometimes hang for some reason; I don't totally understand how that happened, but I guess the many ssh processes requesting passwords would somehow interfere with each other. Avoid this by simply killing the git fetch process the moment it requests the first password. --- pkg/commands/oscommands/cmd_obj_runner.go | 40 +++++++++++++---------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/pkg/commands/oscommands/cmd_obj_runner.go b/pkg/commands/oscommands/cmd_obj_runner.go index c8500d93d..2d0b7034e 100644 --- a/pkg/commands/oscommands/cmd_obj_runner.go +++ b/pkg/commands/oscommands/cmd_obj_runner.go @@ -294,14 +294,10 @@ const ( Token ) -// Whenever we're asked for a password we just enter a newline, which will -// eventually cause the command to fail. +// Whenever we're asked for a password we return a nil channel to tell the +// caller to kill the process. var failPromptFn = func(CredentialType) <-chan string { - ch := make(chan string) - go func() { - ch <- "\n" - }() - return ch + return nil } func (self *cmdObjRunner) runWithCredentialHandling(cmdObj *CmdObj) error { @@ -360,16 +356,26 @@ func (self *cmdObjRunner) processOutput( askFor, ok := checkForCredentialRequest(newBytes) if ok { responseChan := promptUserForCredential(askFor) - if task != nil { - task.Pause() - } - toInput := <-responseChan - if task != nil { - task.Continue() - } - // If the return data is empty we don't write anything to stdin - if toInput != "" { - _, _ = writer.Write([]byte(toInput)) + if responseChan == nil { + // Returning a nil channel means we should kill the process. + // Note that we don't break the loop after this, because we + // still need to drain the output, otherwise the Wait() call + // later might block. + if err := Kill(cmdObj.GetCmd()); err != nil { + self.log.Error(err) + } + } else { + if task != nil { + task.Pause() + } + toInput := <-responseChan + if task != nil { + task.Continue() + } + // If the return data is empty we don't write anything to stdin + if toInput != "" { + _, _ = writer.Write([]byte(toInput)) + } } } }