mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-29 02:35:39 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df0a9f132 | ||
|
|
1e79ab78dd | ||
|
|
1e48afeb8f | ||
|
|
b8ad1883f5 | ||
|
|
582fd24d78 |
@@ -18,7 +18,7 @@ require (
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201007110350-cc51e317126e
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201010224802-8a6768078fd7
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
|
||||
@@ -63,8 +63,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201007110350-cc51e317126e h1:FYXSJfL6JKWB+1NjGqT5/kYJbMI9oMx+JAI3CcrtLsM=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201007110350-cc51e317126e/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201010224802-8a6768078fd7 h1:K3MGrjmpPtIhfXmKh/zsIF0CdmNKOkjpIwcUfAa/J2A=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201010224802-8a6768078fd7/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe h1:qsVhCf2RFyyKIUe/+gJblbCpXMUki9rZrHuEctg6M/E=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||
|
||||
@@ -143,18 +143,19 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string)
|
||||
return RunCommandWithOutputLiveWrapper(c, command, output)
|
||||
}
|
||||
|
||||
// DetectUnamePass detect a username / password question in a command
|
||||
// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password
|
||||
// The promptUserForCredential argument will be "username" or "password" and expects the user's password or username back
|
||||
// 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(command string, promptUserForCredential func(string) string) error {
|
||||
ttyText := ""
|
||||
errMessage := c.RunCommandWithOutputLive(command, func(word string) string {
|
||||
ttyText = ttyText + " " + word
|
||||
|
||||
prompts := map[string]string{
|
||||
`.+'s password:`: "password",
|
||||
`Password\s*for\s*'.+':`: "password",
|
||||
`Username\s*for\s*'.+':`: "username",
|
||||
`.+'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 {
|
||||
@@ -254,7 +255,7 @@ func (c *OSCommand) EditFile(filename string) (*exec.Cmd, error) {
|
||||
return nil, errors.New("No editor defined in $VISUAL, $EDITOR, or git config")
|
||||
}
|
||||
|
||||
splitCmd := str.ToArgv(fmt.Sprintf("%s %s", editor, filename))
|
||||
splitCmd := str.ToArgv(fmt.Sprintf("%s %s", editor, c.Quote(filename)))
|
||||
|
||||
return c.PrepareSubProcess(splitCmd[0], splitCmd[1:]...), nil
|
||||
}
|
||||
|
||||
@@ -227,6 +227,28 @@ func TestOSCommandEditFile(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"file/with space",
|
||||
func(name string, args ...string) *exec.Cmd {
|
||||
if name == "which" {
|
||||
return exec.Command("echo")
|
||||
}
|
||||
|
||||
assert.EqualValues(t, "vi", name)
|
||||
assert.EqualValues(t, "file/with space", args[0])
|
||||
|
||||
return nil
|
||||
},
|
||||
func(env string) string {
|
||||
return ""
|
||||
},
|
||||
func(cf string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
func(cmd *exec.Cmd, err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenPeeDeeP/xdg"
|
||||
yaml "github.com/jesseduffield/yaml"
|
||||
@@ -114,6 +115,9 @@ func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) {
|
||||
if os.IsNotExist(err) {
|
||||
file, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "read-only file system") {
|
||||
return base, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
file.Close()
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
type credentials chan string
|
||||
|
||||
// promptUserForCredential wait for a username or password input from the credentials popup
|
||||
// promptUserForCredential wait for a username, password or passphrase input from the credentials popup
|
||||
func (gui *Gui) promptUserForCredential(passOrUname string) string {
|
||||
gui.credentials = make(chan string)
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
@@ -17,9 +17,12 @@ func (gui *Gui) promptUserForCredential(passOrUname string) string {
|
||||
if passOrUname == "username" {
|
||||
credentialsView.Title = gui.Tr.CredentialsUsername
|
||||
credentialsView.Mask = 0
|
||||
} else {
|
||||
} else if passOrUname == "password" {
|
||||
credentialsView.Title = gui.Tr.CredentialsPassword
|
||||
credentialsView.Mask = '*'
|
||||
} else {
|
||||
credentialsView.Title = gui.Tr.CredentialsPassphrase
|
||||
credentialsView.Mask = '*'
|
||||
}
|
||||
|
||||
if err := gui.switchContext(gui.Contexts.Credentials.Context); err != nil {
|
||||
@@ -30,7 +33,7 @@ func (gui *Gui) promptUserForCredential(passOrUname string) string {
|
||||
return nil
|
||||
})
|
||||
|
||||
// wait for username/passwords input
|
||||
// wait for username/passwords/passphrase input
|
||||
userInput := <-gui.credentials
|
||||
return userInput + "\n"
|
||||
}
|
||||
@@ -70,10 +73,10 @@ func (gui *Gui) handleCredentialsViewFocused() error {
|
||||
func (gui *Gui) handleCredentialsPopup(cmdErr error) {
|
||||
if cmdErr != nil {
|
||||
errMessage := cmdErr.Error()
|
||||
if strings.Contains(errMessage, "Invalid username or password") {
|
||||
if strings.Contains(errMessage, "Invalid username, password or passphrase") {
|
||||
errMessage = gui.Tr.PassUnameWrong
|
||||
}
|
||||
// we are not logging this error because it may contain a password
|
||||
// we are not logging this error because it may contain a password or a passphrase
|
||||
gui.createErrorPanel(errMessage)
|
||||
} else {
|
||||
_ = gui.closeConfirmationPrompt(false)
|
||||
|
||||
@@ -19,6 +19,7 @@ func dutchTranslationSet() TranslationSet {
|
||||
CommitMessage: "Commit bericht",
|
||||
CredentialsUsername: "Gebruikersnaam",
|
||||
CredentialsPassword: "Wachtwoord",
|
||||
CredentialsPassphrase: "Voer een wachtwoordzin in voor de SSH-sleutel",
|
||||
PassUnameWrong: "Wachtwoord en/of gebruikersnaam verkeert",
|
||||
CommitChanges: "Commit veranderingen",
|
||||
AmendLastCommit: "wijzig laatste commit",
|
||||
|
||||
+11
-2
@@ -30,6 +30,7 @@ type TranslationSet struct {
|
||||
CommitMessage string
|
||||
CredentialsUsername string
|
||||
CredentialsPassword string
|
||||
CredentialsPassphrase string
|
||||
PassUnameWrong string
|
||||
CommitChanges string
|
||||
AmendLastCommit string
|
||||
@@ -428,7 +429,14 @@ type TranslationSet struct {
|
||||
NavigationTitle string
|
||||
}
|
||||
|
||||
const englishReleaseNotes = `## lazygit 0.23 Release Notes
|
||||
const englishReleaseNotes = `## lazygit 0.23.2 Release Notes
|
||||
|
||||
- Fixed bug where editing a file with spaces did not work
|
||||
- Fixed formatting issue with delta that rendered '[0;K' to the screen
|
||||
- Fixed bug where lazygit failed upon attempting to create a config file in a
|
||||
read-only filesystem
|
||||
|
||||
## lazygit 0.23 Release Notes
|
||||
|
||||
Custom Commands:
|
||||
- You can now create your own custom commands complete with menus and prompts
|
||||
@@ -519,7 +527,8 @@ func englishTranslationSet() TranslationSet {
|
||||
CommitMessage: "Commit message",
|
||||
CredentialsUsername: "Username",
|
||||
CredentialsPassword: "Password",
|
||||
PassUnameWrong: "Password and/or username wrong",
|
||||
CredentialsPassphrase: "Enter passphrase for SSH key",
|
||||
PassUnameWrong: "Password, passphrase and/or username wrong",
|
||||
CommitChanges: "commit changes",
|
||||
AmendLastCommit: "amend last commit",
|
||||
SureToAmend: "Are you sure you want to amend last commit? Afterwards, you can change commit message from the commits panel.",
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ func polishTranslationSet() TranslationSet {
|
||||
CommitMessage: "Wiadomość commita",
|
||||
CredentialsUsername: "Username",
|
||||
CredentialsPassword: "Password",
|
||||
PassUnameWrong: "Password and/or username wrong",
|
||||
CredentialsPassphrase: "Passphrase",
|
||||
PassUnameWrong: "Password, passphrase and/or username wrong",
|
||||
CommitChanges: "commituj zmiany",
|
||||
AmendLastCommit: "zmień ostatnie zatwierdzenie",
|
||||
SureToAmend: "Czy na pewno chcesz zmienić ostatnie zatwierdzenie? Możesz zmienić komunikat zatwierdzenia z panelu zatwierdzeń.",
|
||||
|
||||
+33
-4
@@ -16,6 +16,19 @@ type escapeInterpreter struct {
|
||||
csiParam []string
|
||||
curFgColor, curBgColor Attribute
|
||||
mode OutputMode
|
||||
instruction instruction
|
||||
}
|
||||
|
||||
const (
|
||||
NONE = 1 << iota
|
||||
ERASE_IN_LINE
|
||||
)
|
||||
|
||||
type instruction struct {
|
||||
kind int
|
||||
param1 int
|
||||
param2 int
|
||||
toWrite []rune
|
||||
}
|
||||
|
||||
type escapeState int
|
||||
@@ -57,10 +70,11 @@ func (ei *escapeInterpreter) runes() []rune {
|
||||
// terminal escape sequences.
|
||||
func newEscapeInterpreter(mode OutputMode) *escapeInterpreter {
|
||||
ei := &escapeInterpreter{
|
||||
state: stateNone,
|
||||
curFgColor: ColorDefault,
|
||||
curBgColor: ColorDefault,
|
||||
mode: mode,
|
||||
state: stateNone,
|
||||
curFgColor: ColorDefault,
|
||||
curBgColor: ColorDefault,
|
||||
mode: mode,
|
||||
instruction: instruction{kind: NONE},
|
||||
}
|
||||
return ei
|
||||
}
|
||||
@@ -73,6 +87,10 @@ func (ei *escapeInterpreter) reset() {
|
||||
ei.csiParam = nil
|
||||
}
|
||||
|
||||
func (ei *escapeInterpreter) instructionRead() {
|
||||
ei.instruction.kind = NONE
|
||||
}
|
||||
|
||||
// parseOne parses a rune. If isEscape is true, it means that the rune is part
|
||||
// of an escape sequence, and as such should not be printed verbatim. Otherwise,
|
||||
// it's not an escape sequence.
|
||||
@@ -131,6 +149,17 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
|
||||
return false, errCSIParseError
|
||||
}
|
||||
|
||||
ei.state = stateNone
|
||||
ei.csiParam = nil
|
||||
return true, nil
|
||||
case ch == 'K':
|
||||
p, err := strconv.Atoi(ei.csiParam[0])
|
||||
if err != nil {
|
||||
return false, errCSIParseError
|
||||
}
|
||||
ei.instruction.kind = ERASE_IN_LINE
|
||||
ei.instruction.param1 = p
|
||||
|
||||
ei.state = stateNone
|
||||
ei.csiParam = nil
|
||||
return true, nil
|
||||
|
||||
+34
@@ -400,6 +400,15 @@ func (v *View) Write(p []byte) (n int, err error) {
|
||||
}
|
||||
default:
|
||||
cells := v.parseInput(ch)
|
||||
if v.ei.instruction.kind != NONE {
|
||||
switch v.ei.instruction.kind {
|
||||
case ERASE_IN_LINE:
|
||||
v.eraseInLine()
|
||||
}
|
||||
v.ei.instructionRead()
|
||||
continue
|
||||
}
|
||||
|
||||
if cells == nil {
|
||||
continue
|
||||
}
|
||||
@@ -416,6 +425,31 @@ func (v *View) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (v *View) eraseInLine() {
|
||||
code := v.ei.instruction.param1
|
||||
switch code {
|
||||
case 0:
|
||||
// need to write till end of the line with cells containing the same bg colour as we currently have.
|
||||
|
||||
if len(v.lines) == 0 {
|
||||
v.lines = append(v.lines, []cell{})
|
||||
}
|
||||
nl := len(v.lines)
|
||||
width, _ := v.Size()
|
||||
cellCount := width - len(v.lines[nl-1])
|
||||
c := cell{
|
||||
fgColor: v.ei.curFgColor,
|
||||
bgColor: v.ei.curBgColor,
|
||||
chr: ' ',
|
||||
}
|
||||
for i := 0; i < cellCount; i++ {
|
||||
v.lines[nl-1] = append(v.lines[nl-1], c)
|
||||
}
|
||||
default:
|
||||
// don't recognise sequence. Until we merge the gocui master branch we can't handle going backwards.
|
||||
}
|
||||
}
|
||||
|
||||
// parseInput parses char by char the input written to the View. It returns nil
|
||||
// while processing ESC sequences. Otherwise, it returns a cell slice that
|
||||
// contains the processed data.
|
||||
|
||||
Vendored
+1
-1
@@ -104,7 +104,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20201007110350-cc51e317126e
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20201010224802-8a6768078fd7
|
||||
## explicit
|
||||
github.com/jesseduffield/gocui
|
||||
# github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe
|
||||
|
||||
Reference in New Issue
Block a user