summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2021-10-24 10:43:48 +1100
committerJesse Duffield <jessedduffield@gmail.com>2021-10-30 18:26:06 +1100
commitf704707d291387b2c1d7432c7700fb5398432f18 (patch)
tree4d9a54fbfb789af24f49e215f70b7bfe664f1616
parent01d82749b17cd7a048d69c8386044d79548f7893 (diff)
stream output from certain git commands in command log panel
-rw-r--r--go.mod4
-rw-r--r--go.sum4
-rw-r--r--pkg/app/app.go8
-rw-r--r--pkg/commands/dummies.go14
-rw-r--r--pkg/commands/git.go7
-rw-r--r--pkg/commands/oscommands/exec_live.go153
-rw-r--r--pkg/commands/oscommands/exec_live_default.go113
-rw-r--r--pkg/commands/oscommands/exec_live_win.go59
-rw-r--r--pkg/commands/oscommands/os.go33
-rw-r--r--pkg/commands/remotes.go8
-rw-r--r--pkg/commands/sync.go10
-rw-r--r--pkg/commands/tags.go2
-rw-r--r--pkg/gui/commit_message_panel.go2
-rw-r--r--pkg/gui/extras_panel.go31
-rw-r--r--pkg/gui/gpg.go51
-rw-r--r--pkg/gui/layout.go3
-rw-r--r--pkg/i18n/english.go4
-rw-r--r--vendor/github.com/jesseduffield/gocui/escape.go39
-rw-r--r--vendor/github.com/jesseduffield/gocui/gui.go1
-rw-r--r--vendor/github.com/jesseduffield/gocui/view.go109
-rw-r--r--vendor/golang.org/x/sys/unix/sockcmsg_linux.go22
-rw-r--r--vendor/golang.org/x/sys/windows/aliases.go4
-rw-r--r--vendor/golang.org/x/sys/windows/eventlog.go1
-rw-r--r--vendor/golang.org/x/sys/windows/mksyscall.go1
-rw-r--r--vendor/golang.org/x/sys/windows/race.go1
-rw-r--r--vendor/golang.org/x/sys/windows/race0.go1
-rw-r--r--vendor/golang.org/x/sys/windows/service.go2
-rw-r--r--vendor/golang.org/x/sys/windows/str.go1
-rw-r--r--vendor/golang.org/x/sys/windows/syscall.go1
-rw-r--r--vendor/golang.org/x/sys/windows/syscall_windows.go10
-rw-r--r--vendor/golang.org/x/sys/windows/types_windows.go273
-rw-r--r--vendor/golang.org/x/sys/windows/zsyscall_windows.go107
-rw-r--r--vendor/modules.txt4
33 files changed, 871 insertions, 212 deletions
diff --git a/go.mod b/go.mod
index 0149feda5..81b248c47 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@ require (
github.com/integrii/flaggy v1.4.0
github.com/iriri/minimal/gitignore v0.3.2 // indirect
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
- github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f
+ github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
@@ -43,7 +43,7 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
- golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 // indirect
+ golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
diff --git a/go.sum b/go.sum
index 1d3c41de1..c7bdb6337 100644
--- a/go.sum
+++ b/go.sum
@@ -84,6 +84,8 @@ github.com/jesseduffield/gocui v0.3.1-0.20211017091015-8bf4a4666b77 h1:MQUxSxVBT
github.com/jesseduffield/gocui v0.3.1-0.20211017091015-8bf4a4666b77/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f h1:JHrb78pj+gYC3KiJKL1WW6lYzlatBIF46oREn68plTM=
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
+github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0 h1:To4mMbu6oQpbbyHa4WtMTc/DHa9dqiRWZpDLMNK+Hdk=
+github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/minimal v0.0.0-20211018110810-9cde264e6b1e h1:WZc73tBVMMhcO6zXyZBItLEF4jgBpBH0lFCZzDgrjDg=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
@@ -195,6 +197,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA=
+golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
diff --git a/pkg/app/app.go b/pkg/app/app.go
index bb7485cca..f24af6e58 100644
--- a/pkg/app/app.go
+++ b/pkg/app/app.go
@@ -127,7 +127,13 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
return app, err
}
- app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr, app.Config, git_config.NewStdCachedGitConfig(app.Log))
+ app.GitCommand, err = commands.NewGitCommand(
+ app.Log,
+ app.OSCommand,
+ app.Tr,
+ app.Config,
+ git_config.NewStdCachedGitConfig(app.Log),
+ )
if err != nil {
return app, err
}
diff --git a/pkg/commands/dummies.go b/pkg/commands/dummies.go
index a8178385d..ba052fe9b 100644
--- a/pkg/commands/dummies.go
+++ b/pkg/commands/dummies.go
@@ -1,6 +1,9 @@
package commands
import (
+ "io"
+ "io/ioutil"
+
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
@@ -17,10 +20,11 @@ func NewDummyGitCommand() *GitCommand {
func NewDummyGitCommandWithOSCommand(osCommand *oscommands.OSCommand) *GitCommand {
newAppConfig := config.NewDummyAppConfig()
return &GitCommand{
- Log: utils.NewDummyLog(),
- OSCommand: osCommand,
- Tr: i18n.NewTranslationSet(utils.NewDummyLog(), newAppConfig.GetUserConfig().Gui.Language),
- Config: newAppConfig,
- GitConfig: git_config.NewFakeGitConfig(map[string]string{}),
+ Log: utils.NewDummyLog(),
+ OSCommand: osCommand,
+ Tr: i18n.NewTranslationSet(utils.NewDummyLog(), newAppConfig.GetUserConfig().Gui.Language),
+ Config: newAppConfig,
+ GitConfig: git_config.NewFakeGitConfig(map[string]string{}),
+ GetCmdWriter: func() io.Writer { return ioutil.Discard },
}
}
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index a1a925de7..8750ecf51 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -1,6 +1,7 @@
package commands
import (
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -40,6 +41,11 @@ type GitCommand struct {
// Push to current determines whether the user has configured to push to the remote branch of the same name as the current or not
PushToCurrent bool
+
+ // this is just a view that we write to when running certain commands.
+ // Coincidentally at the moment it's the same view that OnRunCommand logs to
+ // but that need not always be the case.
+ GetCmdWriter func() io.Writer
}
// NewGitCommand it runs git commands
@@ -77,6 +83,7 @@ func NewGitCommand(
DotGitDir: dotGitDir,
PushToCurrent: pushToCurrent,
GitConfig: gitConfig,
+ GetCmdWriter: func() io.Writer { return ioutil.Discard },
}
gitCommand.PatchManager = patch.NewPatchManager(log, gitCommand.ApplyPatch, gitCommand.ShowFileDiff)
diff --git a/pkg/commands/oscommands/exec_live.go b/pkg/commands/oscommands/exec_live.go
new file mode 100644
index 000000000..6ca70a5ec
--- /dev/null
+++ b/pkg/commands/oscommands/exec_live.go
@@ -0,0 +1,153 @@
+package oscommands
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "os/exec"
+ "regexp"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/go-errors/errors"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+// DetectUnamePass detect a username / password / passphrase question in a command
+// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password or passphrase
+// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
+func (c *OSCommand) DetectUnamePass(cmdObj ICmdObj, writer io.Writer, promptUserForCredential func(string) string) error {
+ ttyText := ""
+ errMessage := c.RunCommandWithOutputLive(cmdObj, writer, func(word string) string {
+ ttyText = ttyText + " " + word
+
+ prompts := map[string]string{
+ `.+'s password:`: "password",
+ `Password\s*for\s*'.+':`: "password",
+ `Username\s*for\s*'.+':`: "username",
+ `Enter\s*passphrase\s*for\s*key\s*'.+':`: "passphrase",
+ }
+
+ for pattern, askFor := range prompts {
+ if match, _ := regexp.MatchString(pattern, ttyText); match {
+ ttyText = ""
+ return promptUserForCredential(askFor)
+ }
+ }
+
+ return ""
+ })
+ return errMessage
+}
+
+// Due to a lack of pty support on windows we have RunCommandWithOutputLiveWrapper being defined
+// separate for windows and other OS's
+func (c *OSCommand) RunCommandWithOutputLive(cmdObj ICmdObj, writer io.Writer, handleOutput func(string) string) error {
+ return RunCommandWithOutputLiveWrapper(c, cmdObj, writer, handleOutput)
+}
+
+type cmdHandler struct {
+ stdoutPipe io.Reader
+ stdinPipe io.Writer
+ close func() error
+}
+
+// RunCommandWithOutputLiveAux runs a command and return every word that gets written in stdout
+// Output is a function that executes by every word that gets read by bufio
+// As return of output you need to give a string that will be written to stdin
+// NOTE: If the return data is empty it won't write anything to stdin
+func RunCommandWithOutputLiveAux(
+ c *OSCommand,
+ cmdObj ICmdObj,
+ writer io.Writer,
+ // handleOutput takes a word from stdout and returns a string to be written to stdin.
+ // See DetectUnamePass above for how this is used to check for a username/password request
+ handleOutput func(string) string,
+ startCmd func(cmd *exec.Cmd) (*cmdHandler, error),
+) error {
+ c.Log.WithField("command", cmdObj.ToString()).Info("RunCommand")
+ c.LogCommand(cmdObj.ToString(), true)
+ cmd := cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8").GetCmd()
+
+ var stderr bytes.Buffer
+ cmd.Stderr = io.MultiWriter(writer, &stderr)
+
+ handler, err := startCmd(cmd)
+ if err != nil {
+ return err
+ }
+
+ defer func() {
+ if closeErr := handler.close(); closeErr != nil {
+ c.Log.Error(closeErr)
+ }
+ }()
+
+ tr := io.TeeReader(handler.stdoutPipe, writer)
+
+ go utils.Safe(func() {
+ scanner := bufio.NewScanner(tr)
+ scanner.Split(scanWordsWithNewLines)
+ for scanner.Scan() {
+ text := scanner.Text()
+ output := strings.Trim(text, " ")
+ toInput := handleOutput(output)
+ if toInput != "" {
+ _, _ = handler.stdinPipe.Write([]byte(toInput))
+ }
+ }
+ })
+
+ err = cmd.Wait()
+ if err != nil {
+ return errors.New(stderr.String())
+ }
+
+ return nil
+}
+
+// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines
+// For specific comments about this function take a look at: bufio.ScanWords
+func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ start := 0
+ for width := 0; start < len(data); start += width {
+ var r rune
+ r, width = utf8.DecodeRune(data[start:])
+ if !isSpace(r) {
+ break
+ }
+ }
+ for width, i := 0, start; i < len(data); i += width {
+ var r rune
+ r, width = utf8.DecodeRune(data[i:])
+ if isSpace(r) {
+ return i + width, data[start:i], nil
+ }
+ }
+ if atEOF && len(data) > start {
+ return len(data), data[start:], nil
+ }
+ return start, nil, nil
+}
+
+// isSpace is also copied from the bufio package and has been modified to also captures new lines
+// For specific comments about this function take a look at: bufio.isSpace
+func isSpace(r rune) bool {
+ if r <= '\u00FF' {
+ switch r {
+ case ' ', '\t', '\v', '\f':
+ return true
+ case '\u0085', '\u00A0':
+ return true
+ }
+ return false
+ }
+ if '\u2000' <= r && r <= '\u200a' {
+ return true
+ }
+ switch r {
+ case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
+ return true
+ }
+ return false
+}
diff --git a/pkg/commands/oscommands/exec_live_default.go b/pkg/commands/oscommands/exec_live_default.go
index 95d5d09fb..827db9ed3 100644
--- a/pkg/commands/oscommands/exec_live_default.go
+++ b/pkg/commands/oscommands/exec_live_default.go
@@ -4,95 +4,34 @@
package oscommands
import (
- "bufio"
- "bytes"
- "strings"
- "unicode/utf8"
-
- "github.com/go-errors/errors"
- "github.com/jesseduffield/lazygit/pkg/utils"
+ "io"
+ "os/exec"
"github.com/creack/pty"
)
-// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout
-// Output is a function that executes by every word that gets read by bufio
-// As return of output you need to give a string that will be written to stdin
-// NOTE: If the return data is empty it won't written anything to stdin
-func RunCommandWithOutputLiveWrapper(c *OSCommand, cmdObj ICmdObj, output func(string) string) error {
- c.Log.WithField("command", cmdObj.ToString()).Info("RunCommand")
- c.LogCommand(cmdObj.ToString(), true)
- cmd := cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8").GetCmd()
-
- var stderr bytes.Buffer
- cmd.Stderr = &stderr
-
- ptmx, err := pty.Start(cmd)
-
- if err != nil {
- return err
- }
-
- go utils.Safe(func() {
- scanner := bufio.NewScanner(ptmx)
- scanner.Split(scanWordsWithNewLines)
- for scanner.Scan() {
- toOutput := strings.Trim(scanner.Text(), " ")
- _, _ = ptmx.WriteString(output(toOutput))
- }
- })
-
- err = cmd.Wait()
- ptmx.Close()
- if err != nil {
- return errors.New(stderr.String())
- }
-
- return nil
-}
-
-// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines
-// For specific comments about this function take a look at: bufio.ScanWords
-func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
- start := 0
- for width := 0; start < len(data); start += width {
- var r rune
- r, width = utf8.DecodeRune(data[start:])
- if !isSpace(r) {
- break
- }
- }
- for width, i := 0, start; i < len(data); i += width {
- var r rune
- r, width = utf8.DecodeRune(data[i:])
- if isSpace(r) {
- return i + width, data[start:i], nil
- }
- }
- if atEOF && len(data) > start {
- return len(data), data[start:], nil
- }
- return start, nil, nil
-}
-
-// isSpace is also copied from the bufio package and has been modified to also captures new lines
-// For specific comments about this function take a look at: bufio.isSpace
-func isSpace(r rune) bool {
- if r <= '\u00FF' {
- switch r {
- case ' ', '\t', '\v', '\f':
- return true
- case '\u0085', '\u00A0':
- return true
- }
- return false
- }
- if '\u2000' <= r && r <= '\u200a' {
- return true
- }
- switch r {
- case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
- return true
- }
- return false
+func RunCommandWithOutputLiveWrapper(
+ c *OSCommand,
+ cmdObj ICmdObj,
+ writer io.Writer,
+ output func(string) string,
+) error {
+ return RunCommandWithOutputLiveAux(
+ c,
+ cmdObj,
+ writer,
+ output,
+ func(cmd *exec.Cmd) (*cmdHandler, error) {
+ ptmx, err := pty.Start(cmd)
+ if err != nil {
+ return nil, err
+ }
+
+ return &cmdHandler{
+ stdoutPipe: ptmx,
+ stdinPipe: ptmx,
+ close: ptmx.Close,
+ }, nil
+ },
+ )
}
diff --git a/pkg/commands/oscommands/exec_live_win.go b/pkg/commands/oscommands/exec_live_win.go
index aa17242e3..5b61e478b 100644
--- a/pkg/commands/oscommands/exec_live_win.go
+++ b/pkg/commands/oscommands/exec_live_win.go
@@ -3,8 +3,61 @@
package oscommands
+import (
+ "bytes"
+ "io"
+ "os/exec"
+ "sync"
+)
+
+type Buffer struct {
+ b bytes.Buffer
+ m sync.Mutex
+}
+
+func (b *Buffer) Read(p []byte) (n int, err error) {
+ b.m.Lock()
+ defer b.m.Unlock()
+ return b.b.Read(p)
+}
+func (b *Buffer) Write(p []byte) (n int, err error) {
+ b.m.Lock()
+ defer b.m.Unlock()
+ return b.b.Write(p)
+}
+
// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there
-// TODO: Remove this hack and replace it with a proper way to run commands live on windows
-func RunCommandWithOutputLiveWrapper(c *OSCommand, cmdObj ICmdObj, output func(string) string) error {
- return c.RunCommand(cmdObj.ToString())
+// TODO: Remove this hack and replace it with a proper way to run commands live on windows. We still have an issue where if a password is requested, the request for a password is written straight to stdout because we can't control the stdout of a subprocess of a subprocess. Keep an eye on https://github.com/creack/pty/pull/109
+func RunCommandWithOutputLiveWrapper(
+ c *OSCommand,
+ cmdObj ICmdObj,
+ writer io.Writer,
+ output func(string) string,
+) error {
+ return RunCommandWithOutputLiveAux(
+ c,
+ cmdObj,
+ writer,
+ output,
+ func(cmd *exec.Cmd) (*cmdHandler, error) {
+ stdoutReader, stdoutWriter := io.Pipe()
+ cmd.Stdout = stdoutWriter
+
+ buf := &Buffer{}
+ cmd.Stdin = buf
+
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ // because we don't yet have windows support for a pty, we instead just
+ // pass our standard stream handlers and because there's no pty to close
+ // we pass a no-op function for that.
+ return &cmdHandler{
+ stdoutPipe: stdoutReader,
+ stdinPipe: buf,
+ close: func() error { return nil },
+ }, nil
+ },
+ )
}
diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go
index f0c9525a5..cb41edff4 100644
--- a/pkg/commands/oscommands/os.go
+++ b/pkg/commands/oscommands/os.go
@@ -7,7 +7,6 @@ import (
"os"
"os/exec"
"path/filepath"
- "regexp"
"strings"
"sync"
@@ -217,38 +216,6 @@ func (c *OSCommand) ShellCommandFromString(commandStr string) *exec.Cmd {
return c.ExecutableFromString(shellCommand)
}
-// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper
-func (c *OSCommand) RunCommandWithOutputLive(cmdObj ICmdObj, output func(string) string) error {
- return RunCommandWithOutputLiveWrapper(c, cmdObj, output)
-}
-
-// DetectUnamePass detect a username / password / passphrase question in a command
-// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password or passphrase
-// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
-func (c *OSCommand) DetectUnamePass(cmdObj ICmdObj, promptUserForCredential func(string) string) error {
- ttyText := ""
- errMessage := c.RunCommandWithOutputLive(cmdObj, func(word string) string {
- ttyText = ttyText + " " + word
-
- prompts := map[string]string{
- `.+'s password:`: "password",
- `Password\s*for\s*'.+':`: "password",
- `Username\s*for\s*'.+':`: "username",
- `Enter\s*passphrase\s*for\s*key\s*'.+':`: "passphrase",
- }
-
- for pattern, askFor := range prompts {
- if match, _ := regexp.MatchString(pattern, ttyText); match {
- ttyText = ""
- return promptUserForCredential(askFor)
- }
- }
-
- return ""
- })
- return errMessage
-}
-
// RunCommand runs a command and just returns the error
func (c *OSCommand) RunCommand(formatString string, formatArgs ...interface{}) error {
_, err := c.RunCommandWithOutput(formatString, formatArgs...)
diff --git a/pkg/commands/remotes.go b/pkg/commands/remotes.go
index 0839f9583..11e5dc26f 100644
--- a/pkg/commands/remotes.go
+++ b/pkg/commands/remotes.go
@@ -2,6 +2,8 @@ package commands
import (
"fmt"
+
+ "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
func (c *GitCommand) AddRemote(name string, url string) error {
@@ -23,7 +25,11 @@ func (c *GitCommand) UpdateRemoteUrl(remoteName string, updatedUrl string) error
func (c *GitCommand) DeleteRemoteBranch(remoteName string, branchName string, promptUserForCredential func(string) string) error {
command := fmt.Sprintf("git push %s --delete %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(branchName))
cmdObj := c.NewCmdObjFromStr(command)
- return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
+ return c.DetectUnamePass(cmdObj, promptUserForCredential)
+}
+
+func (c *GitCommand) DetectUnamePass(cmdObj oscommands.ICmdObj, promptUserForCredential func(string) string) error {
+ return c.OSCommand.DetectUnamePass(cmdObj, c.GetCmdWriter(), promptUserForCredential)
}
// CheckRemoteBranchExists Returns remote branch
diff --git a/pkg/commands/sync.go b/pkg/commands/sync.go
index 3f94c5f4e..35f258f4b 100644
--- a/pkg/commands/sync.go
+++ b/pkg/commands/sync.go
@@ -38,7 +38,7 @@ func (c *GitCommand) Push(opts PushOpts) error {
}
cmdObj := c.NewCmdObjFromStr(cmdStr)
- return c.OSCommand.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
+ return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
}
type FetchOptions struct {
@@ -59,7 +59,7 @@ func (c *GitCommand) Fetch(opts FetchOptions) error {
}
cmdObj := c.NewCmdObjFromStr(cmdStr)
- return c.OSCommand.DetectUnamePass(cmdObj, func(question string) string {
+ return c.DetectUnamePass(cmdObj, func(question string) string {
if opts.PromptUserForCredential != nil {
return opts.PromptUserForCredential(question)
}
@@ -95,17 +95,17 @@ func (c *GitCommand) Pull(opts PullOptions) error {
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
cmdObj := c.NewCmdObjFromStr(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:")
- return c.OSCommand.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
+ return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
}
func (c *GitCommand) FastForward(branchName string, remoteName string, remoteBranchName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git fetch %s %s:%s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
- return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
+ return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
func (c *GitCommand) FetchRemote(remoteName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git fetch %s", c.OSCommand.Quote(remoteName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
- return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
+ return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
diff --git a/pkg/commands/tags.go b/pkg/commands/tags.go
index af40ae603..9aa327dd3 100644
--- a/pkg/commands/tags.go
+++ b/pkg/commands/tags.go
@@ -15,5 +15,5 @@ func (c *GitCommand) DeleteTag(tagName string) error {
func (c *GitCommand) PushTag(remoteName string, tagName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git push %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(tagName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
- return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
+ return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go
index 7ed9669ab..edc86eff7 100644
--- a/pkg/gui/commit_message_panel.go
+++ b/pkg/gui/commit_message_panel.go
@@ -22,9 +22,9 @@ func (gui *Gui) handleCommitConfirm() error {
cmdStr := gui.GitCommand.CommitCmdStr(message, flags)
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdStr, gui.Tr.Spans.Commit, true))
+ _ = gui.returnFromContext()
return gui.withGpgHandling(cmdStr, gui.Tr.CommittingStatus, func() error {
gui.Views.CommitMessage.ClearTextArea()
- _ = gui.returnFromContext()
return nil
})
}
diff --git a/pkg/gui/extras_panel.go b/pkg/gui/extras_panel.go
index ee106223e..7d68bb1ec 100644
--- a/pkg/gui/extras_panel.go
+++ b/pkg/gui/extras_panel.go
@@ -1,5 +1,11 @@
package gui
+import (
+ "io"
+
+ "github.com/jesseduffield/lazygit/pkg/gui/style"
+)
+
func (gui *Gui) handleCreateExtrasMenuPanel() error {
menuItems := []*menuItem{
{
@@ -48,3 +54,28 @@ func (gui *Gui) scrollDownExtra() error {
return nil
}
+
+func (gui *Gui) getCmdWriter() io.Writer {
+ return &prefixWriter{writer: gui.Views.Extras, prefix: style.FgMagenta.Sprintf("\n\n%s\n", gui.Tr.GitOutput)}
+}
+
+// Ensures that the first write is preceded by writing a prefix.
+// This allows us to say 'Git output:' before writing the actual git output.
+// We could just write directly to the view in this package before running the command but we already have code in the commands package that writes to the same view beforehand (with the command it's about to run) so things would be out of order.
+type prefixWriter struct {
+ prefix string
+ prefixWritten bool
+ writer io.Writer
+}
+
+func (self *prefixWriter) Write(p []byte) (n int, err error) {
+ if !self.prefixWritten {
+ self.prefixWritten = true
+ // assuming we can write this prefix in one go
+ _, err = self.writer.Write([]byte(self.prefix))
+ if err != nil {
+ return