From d914d40b2e7bfaeda966f2bd539bd45aef8cebe0 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 7 Jan 2020 11:22:15 +0900 Subject: [PATCH 1/3] Use ASCII on Windows with east asian locale --- go.mod | 2 +- pkg/gui/gui.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c2270c9a0..118c2b621 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.11 // indirect - github.com/mattn/go-runewidth v0.0.7 // indirect + github.com/mattn/go-runewidth v0.0.7 github.com/mgutz/str v1.2.0 github.com/nicksnyder/go-i18n/v2 v2.0.3 github.com/onsi/ginkgo v1.10.3 // indirect diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index ba379fc56..5b2f9cc14 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "math" "os" + "runtime" "sync" // "io" @@ -28,6 +29,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/updates" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/mattn/go-runewidth" "github.com/sirupsen/logrus" ) @@ -782,6 +784,8 @@ func (gui *Gui) Run() error { } defer g.Close() + g.ASCII = runtime.GOOS == "windows" && runewidth.IsEastAsian() + if gui.Config.GetUserConfig().GetBool("gui.mouseEvents") { g.Mouse = true } From eb2bfd3848bc5178670380f77b675a27d94d2515 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 7 Jan 2020 20:12:36 +1100 Subject: [PATCH 2/3] allow hard resetting to upstream branch --- pkg/commands/git.go | 14 +++++++------- pkg/commands/git_test.go | 8 +++++--- pkg/gui/files_panel.go | 13 ++++++++++--- pkg/i18n/english.go | 3 +++ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 08eb78edf..ab71f80e3 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -265,7 +265,7 @@ func includesInt(list []int, a int) bool { // ResetAndClean removes all unstaged changes and removes all untracked files func (c *GitCommand) ResetAndClean() error { - if err := c.ResetHardHead(); err != nil { + if err := c.ResetHard("HEAD"); err != nil { return err } @@ -961,14 +961,14 @@ func (c *GitCommand) RemoveUntrackedFiles() error { return c.OSCommand.RunCommand("git clean -fd") } -// ResetHardHead runs `git reset --hard HEAD` -func (c *GitCommand) ResetHardHead() error { - return c.OSCommand.RunCommand("git reset --hard HEAD") +// ResetHardHead runs `git reset --hard` +func (c *GitCommand) ResetHard(ref string) error { + return c.OSCommand.RunCommand("git reset --hard " + ref) } -// ResetSoftHead runs `git reset --soft HEAD` -func (c *GitCommand) ResetSoftHead() error { - return c.OSCommand.RunCommand("git reset --soft HEAD") +// ResetSoft runs `git reset --soft HEAD` +func (c *GitCommand) ResetSoft(ref string) error { + return c.OSCommand.RunCommand("git reset --soft " + ref) } // DiffCommits show diff between commits diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 4991714f1..0429165de 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -2106,10 +2106,11 @@ func TestGitCommandRemoveUntrackedFiles(t *testing.T) { } } -// TestGitCommandResetHardHead is a function. -func TestGitCommandResetHardHead(t *testing.T) { +// TestGitCommandResetHard is a function. +func TestGitCommandResetHard(t *testing.T) { type scenario struct { testName string + ref string command func(string, ...string) *exec.Cmd test func(error) } @@ -2117,6 +2118,7 @@ func TestGitCommandResetHardHead(t *testing.T) { scenarios := []scenario{ { "valid case", + "HEAD", test.CreateMockCommand(t, []*test.CommandSwapper{ { Expect: `git reset --hard HEAD`, @@ -2134,7 +2136,7 @@ func TestGitCommandResetHardHead(t *testing.T) { for _, s := range scenarios { t.Run(s.testName, func(t *testing.T) { gitCmd.OSCommand.command = s.command - s.test(gitCmd.ResetHardHead()) + s.test(gitCmd.ResetHard(s.ref)) }) } } diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 463db817e..aa2e8592e 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -604,14 +604,21 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error { description: gui.Tr.SLocalize("softReset"), command: "git reset --soft HEAD", handler: func() error { - return gui.GitCommand.ResetSoftHead() + return gui.GitCommand.ResetSoft("HEAD") }, }, { description: gui.Tr.SLocalize("hardReset"), command: "git reset --hard HEAD", handler: func() error { - return gui.GitCommand.ResetHardHead() + return gui.GitCommand.ResetHard("HEAD") + }, + }, + { + description: gui.Tr.SLocalize("hardResetUpstream"), + command: "git reset --hard @{upstream}", + handler: func() error { + return gui.GitCommand.ResetHard("@{upstream}") }, }, { @@ -624,7 +631,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error { handleMenuPress := func(index int) error { if err := options[index].handler(); err != nil { - return err + return gui.createErrorPanel(gui.g, err.Error()) } return gui.refreshFiles() diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 55a607090..c6c423285 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -753,6 +753,9 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "hardReset", Other: "hard reset", + }, &i18n.Message{ + ID: "hardResetUpstream", + Other: "hard reset to upstream branch", }, &i18n.Message{ ID: "viewResetOptions", Other: `view reset options`, From 09aabce3cd38312e854fdbb29046acba42a67f38 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 7 Jan 2020 20:24:10 +1100 Subject: [PATCH 3/3] allow commits to be checked out --- pkg/commands/git.go | 2 +- pkg/gui/branches_panel.go | 13 +++++++------ pkg/gui/commits_panel.go | 11 +++++++++++ pkg/gui/keybindings.go | 7 +++++++ pkg/gui/remote_branches_panel.go | 2 +- pkg/gui/tags_panel.go | 2 +- pkg/i18n/english.go | 6 ++++++ 7 files changed, 34 insertions(+), 9 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index ab71f80e3..0975ba501 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -515,7 +515,7 @@ func (c *GitCommand) DiscardUnstagedFileChanges(file *File) error { return c.OSCommand.RunCommand("git checkout -- %s", quotedFileName) } -// Checkout checks out a branch, with --force if you set the force arg to true +// Checkout checks out a branch (or commit), with --force if you set the force arg to true func (c *GitCommand) Checkout(branch string, force bool) error { forceArg := "" if force { diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index f78d86ab8..985be932e 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -128,7 +128,7 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error { return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch")) } branch := gui.getSelectedBranch() - return gui.handleCheckoutBranch(branch.Name) + return gui.handleCheckoutRef(branch.Name) } func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error { @@ -165,17 +165,17 @@ func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { }, nil) } -func (gui *Gui) handleCheckoutBranch(branchName string) error { - if err := gui.GitCommand.Checkout(branchName, false); err != nil { +func (gui *Gui) handleCheckoutRef(ref string) error { + if err := gui.GitCommand.Checkout(ref, false); err != nil { // note, this will only work for english-language git commands. If we force git to use english, and the error isn't this one, then the user will receive an english command they may not understand. I'm not sure what the best solution to this is. Running the command once in english and a second time in the native language is one option if strings.Contains(err.Error(), "Please commit your changes or stash them before you switch branch") { // offer to autostash changes return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("AutoStashTitle"), gui.Tr.SLocalize("AutoStashPrompt"), func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + branchName); err != nil { + if err := gui.GitCommand.StashSave(gui.Tr.SLocalize("StashPrefix") + ref); err != nil { return gui.createErrorPanel(g, err.Error()) } - if err := gui.GitCommand.Checkout(branchName, false); err != nil { + if err := gui.GitCommand.Checkout(ref, false); err != nil { return gui.createErrorPanel(g, err.Error()) } @@ -198,12 +198,13 @@ func (gui *Gui) handleCheckoutBranch(branchName string) error { } gui.State.Panels.Branches.SelectedLine = 0 + gui.State.Panels.Commits.SelectedLine = 0 return gui.refreshSidePanels(gui.g) } func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error { gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", "", func(g *gocui.Gui, v *gocui.View) error { - return gui.handleCheckoutBranch(gui.trimmedContent(v)) + return gui.handleCheckoutRef(gui.trimmedContent(v)) }) return nil } diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index 22ea6fe7e..3f82584c1 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -611,3 +611,14 @@ func (gui *Gui) handleCreateLightweightTag(commitSha string) error { return gui.handleCommitSelect(g, v) }) } + +func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error { + commit := gui.getSelectedCommit(g) + if commit == nil { + return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch")) + } + + return gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error { + return gui.handleCheckoutRef(commit.Sha) + }, nil) +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 8d036fc07..1deb6f7e3 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -596,6 +596,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { ViewName: "commits", Key: gocui.KeySpace, Modifier: gocui.ModNone, + Handler: gui.handleCheckoutCommit, + Description: gui.Tr.SLocalize("checkoutCommit"), + }, + { + ViewName: "commits", + Key: 'h', + Modifier: gocui.ModNone, Handler: gui.handleToggleDiffCommit, Description: gui.Tr.SLocalize("CommitsDiff"), }, diff --git a/pkg/gui/remote_branches_panel.go b/pkg/gui/remote_branches_panel.go index af9835999..2de045e4d 100644 --- a/pkg/gui/remote_branches_panel.go +++ b/pkg/gui/remote_branches_panel.go @@ -79,7 +79,7 @@ func (gui *Gui) handleCheckoutRemoteBranch(g *gocui.Gui, v *gocui.View) error { if remoteBranch == nil { return nil } - if err := gui.handleCheckoutBranch(remoteBranch.RemoteName + "/" + remoteBranch.Name); err != nil { + if err := gui.handleCheckoutRef(remoteBranch.RemoteName + "/" + remoteBranch.Name); err != nil { return err } return gui.switchBranchesPanelContext("local-branches") diff --git a/pkg/gui/tags_panel.go b/pkg/gui/tags_panel.go index e1f9d364a..e287156ac 100644 --- a/pkg/gui/tags_panel.go +++ b/pkg/gui/tags_panel.go @@ -90,7 +90,7 @@ func (gui *Gui) handleCheckoutTag(g *gocui.Gui, v *gocui.View) error { if tag == nil { return nil } - if err := gui.handleCheckoutBranch(tag.Name); err != nil { + if err := gui.handleCheckoutRef(tag.Name); err != nil { return err } return gui.switchBranchesPanelContext("local-branches") diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index c6c423285..d9a2dcee9 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -924,6 +924,12 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "FetchingRemoteStatus", Other: "fetching remote", + }, &i18n.Message{ + ID: "checkoutCommit", + Other: "checkout commit", + }, &i18n.Message{ + ID: "SureCheckoutThisCommit", + Other: "Are you sure you want to checkout this commit?", }, ) }