mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-27 12:32:37 +02:00
Make merge panel its own panel
This commit is contained in:
parent
e0ff46fe53
commit
9489a94473
4
.gitignore
vendored
4
.gitignore
vendored
@ -23,4 +23,6 @@ lazygit
|
|||||||
!.gitignore
|
!.gitignore
|
||||||
!.goreleaser.yml
|
!.goreleaser.yml
|
||||||
!.circleci/
|
!.circleci/
|
||||||
!.github/
|
!.github/
|
||||||
|
|
||||||
|
test/git_server/data
|
@ -4,6 +4,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/heroku/rollrus"
|
"github.com/heroku/rollrus"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
@ -11,6 +12,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||||
|
"github.com/shibukawa/configdir"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,9 +35,15 @@ func newProductionLogger(config config.AppConfigurer) *logrus.Logger {
|
|||||||
return log
|
return log
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDevelopmentLogger() *logrus.Logger {
|
func globalConfigDir() string {
|
||||||
|
configDirs := configdir.New("jesseduffield", "lazygit")
|
||||||
|
configDir := configDirs.QueryFolders(configdir.Global)[0]
|
||||||
|
return configDir.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDevelopmentLogger(config config.AppConfigurer) *logrus.Logger {
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
file, err := os.OpenFile("development.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
file, err := os.OpenFile(filepath.Join(globalConfigDir(), "development.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
|
panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function)
|
||||||
}
|
}
|
||||||
@ -48,7 +56,7 @@ func newLogger(config config.AppConfigurer) *logrus.Entry {
|
|||||||
environment := "production"
|
environment := "production"
|
||||||
if config.GetDebug() {
|
if config.GetDebug() {
|
||||||
environment = "development"
|
environment = "development"
|
||||||
log = newDevelopmentLogger()
|
log = newDevelopmentLogger(config)
|
||||||
} else {
|
} else {
|
||||||
log = newProductionLogger(config)
|
log = newProductionLogger(config)
|
||||||
}
|
}
|
||||||
|
@ -285,6 +285,14 @@ func (c *GitCommand) AbortRebaseBranch() error {
|
|||||||
return c.OSCommand.RunCommand("git rebase --abort")
|
return c.OSCommand.RunCommand("git rebase --abort")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) ContinueMergeBranch() error {
|
||||||
|
return c.OSCommand.RunCommand("git merge --continue")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) AbortMergeBranch() error {
|
||||||
|
return c.OSCommand.RunCommand("git merge --abort")
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch fetch git repo
|
// Fetch fetch git repo
|
||||||
func (c *GitCommand) Fetch() error {
|
func (c *GitCommand) Fetch() error {
|
||||||
return c.OSCommand.RunCommand("git fetch")
|
return c.OSCommand.RunCommand("git fetch")
|
||||||
@ -682,7 +690,40 @@ func (c *GitCommand) Ignore(filename string) error {
|
|||||||
|
|
||||||
// Show shows the diff of a commit
|
// Show shows the diff of a commit
|
||||||
func (c *GitCommand) Show(sha string) (string, error) {
|
func (c *GitCommand) Show(sha string) (string, error) {
|
||||||
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git show --color %s", sha))
|
show, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git show --color %s", sha))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is a merge commit, we need to go a step further and get the diff between the two branches we merged
|
||||||
|
revList, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git rev-list -1 --merges %s^...%s", sha, sha))
|
||||||
|
if err != nil {
|
||||||
|
// turns out we get an error here when it's the first commit. We'll just return the original show
|
||||||
|
return show, nil
|
||||||
|
}
|
||||||
|
if len(revList) == 0 {
|
||||||
|
return show, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to pull out 1a6a69a and 3b51d7c from this:
|
||||||
|
// commit ccc771d8b13d5b0d4635db4463556366470fd4f6
|
||||||
|
// Merge: 1a6a69a 3b51d7c
|
||||||
|
lines := utils.SplitLines(show)
|
||||||
|
if len(lines) < 2 {
|
||||||
|
return show, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secondLineWords := strings.Split(lines[1], " ")
|
||||||
|
if len(secondLineWords) < 3 {
|
||||||
|
return show, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeDiff, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git diff --color %s...%s", secondLineWords[1], secondLineWords[2]))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return show + mergeDiff, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRemoteURL returns current repo remote url
|
// GetRemoteURL returns current repo remote url
|
||||||
|
@ -53,7 +53,7 @@ func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
|
|||||||
|
|
||||||
branch := gui.getSelectedBranch()
|
branch := gui.getSelectedBranch()
|
||||||
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
||||||
return gui.renderListPanel(gui.getBranchesView(gui.g), gui.State.Branches)
|
return gui.renderListPanel(gui.getBranchesView(), gui.State.Branches)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gui.refreshStatus is called at the end of this because that's when we can
|
// gui.refreshStatus is called at the end of this because that's when we can
|
||||||
@ -67,9 +67,6 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
|||||||
gui.State.Branches = builder.Build()
|
gui.State.Branches = builder.Build()
|
||||||
|
|
||||||
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
||||||
if err := gui.resetOrigin(gui.getBranchesView(gui.g)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -82,6 +79,10 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
|||||||
func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
panelState := gui.State.Panels.Branches
|
panelState := gui.State.Panels.Branches
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleBranchSelect(gui.g, v)
|
return gui.handleBranchSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +90,9 @@ func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Branches
|
panelState := gui.State.Panels.Branches
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleBranchSelect(gui.g, v)
|
return gui.handleBranchSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,8 +112,7 @@ func (gui *Gui) handleRebase(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.GitCommand.RebaseBranch(selectedBranch); err != nil {
|
if err := gui.GitCommand.RebaseBranch(selectedBranch); err != nil {
|
||||||
gui.Log.Errorln(err)
|
return gui.createConfirmationPanel(g, v, "Auto-rebase failed", gui.Tr.SLocalize("FoundConflicts"),
|
||||||
if err := gui.createConfirmationPanel(g, v, "Rebase failed", "Damn, conflicts! To abort press 'esc', otherwise press 'enter'",
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return nil
|
return nil
|
||||||
}, func(g *gocui.Gui, v *gocui.View) error {
|
}, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -117,9 +120,8 @@ func (gui *Gui) handleRebase(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.refreshSidePanels(g)
|
return gui.refreshSidePanels(g)
|
||||||
}); err != nil {
|
},
|
||||||
gui.Log.Errorln(err)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.refreshSidePanels(g)
|
return gui.refreshSidePanels(g)
|
||||||
@ -251,6 +253,18 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
||||||
}
|
}
|
||||||
if err := gui.GitCommand.Merge(selectedBranch.Name); err != nil {
|
if err := gui.GitCommand.Merge(selectedBranch.Name); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "fix conflicts") {
|
||||||
|
return gui.createConfirmationPanel(g, v, "Auto-merge failed", gui.Tr.SLocalize("FoundConflicts"),
|
||||||
|
func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
return nil
|
||||||
|
}, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if err := gui.GitCommand.AbortMergeBranch(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.refreshSidePanels(g)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -27,13 +27,13 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
|
|||||||
_ = v.SetCursor(0, 0)
|
_ = v.SetCursor(0, 0)
|
||||||
_ = v.SetOrigin(0, 0)
|
_ = v.SetOrigin(0, 0)
|
||||||
_, _ = g.SetViewOnBottom("commitMessage")
|
_, _ = g.SetViewOnBottom("commitMessage")
|
||||||
_ = gui.switchFocus(g, v, gui.getFilesView(g))
|
_ = gui.switchFocus(g, v, gui.getFilesView())
|
||||||
return gui.refreshSidePanels(g)
|
return gui.refreshSidePanels(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
|
||||||
g.SetViewOnBottom("commitMessage")
|
g.SetViewOnBottom("commitMessage")
|
||||||
return gui.switchFocus(g, v, gui.getFilesView(g))
|
return gui.switchFocus(g, v, gui.getFilesView())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -87,6 +87,6 @@ func (gui *Gui) RenderCommitLength() {
|
|||||||
if !gui.Config.GetUserConfig().GetBool("gui.commitLength.show") {
|
if !gui.Config.GetUserConfig().GetBool("gui.commitLength.show") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := gui.getCommitMessageView(gui.g)
|
v := gui.getCommitMessageView()
|
||||||
v.Subtitle = gui.getBufferLength(v)
|
v.Subtitle = gui.getBufferLength(v)
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v := gui.getCommitsView(gui.g)
|
v := gui.getCommitsView()
|
||||||
v.Clear()
|
v.Clear()
|
||||||
fmt.Fprint(v, list)
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
@ -68,6 +68,9 @@ func (gui *Gui) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Commits
|
panelState := gui.State.Panels.Commits
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), false)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), false)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleCommitSelect(gui.g, v)
|
return gui.handleCommitSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +78,9 @@ func (gui *Gui) handleCommitsPrevLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Commits
|
panelState := gui.State.Panels.Commits
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleCommitSelect(gui.g, v)
|
return gui.handleCommitSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +98,7 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
|
|||||||
if err := gui.refreshCommits(g); err != nil {
|
if err := gui.refreshCommits(g); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if err := gui.refreshFiles(g); err != nil {
|
if err := gui.refreshFiles(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
gui.resetOrigin(commitView)
|
gui.resetOrigin(commitView)
|
||||||
|
@ -78,7 +78,6 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
|
|||||||
confirmationView.FgColor = gocui.ColorWhite
|
confirmationView.FgColor = gocui.ColorWhite
|
||||||
}
|
}
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
confirmationView.Clear()
|
|
||||||
return gui.switchFocus(gui.g, currentView, confirmationView)
|
return gui.switchFocus(gui.g, currentView, confirmationView)
|
||||||
})
|
})
|
||||||
return confirmationView, nil
|
return confirmationView, nil
|
||||||
@ -86,6 +85,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
|
|||||||
|
|
||||||
func (gui *Gui) onNewPopupPanel() {
|
func (gui *Gui) onNewPopupPanel() {
|
||||||
gui.g.SetViewOnBottom("commitMessage")
|
gui.g.SetViewOnBottom("commitMessage")
|
||||||
|
gui.g.SetViewOnBottom("menu")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
|
||||||
|
@ -35,23 +35,24 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.HasMergeConflicts {
|
|
||||||
return gui.refreshMergePanel(g)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, v); err != nil {
|
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if file.HasMergeConflicts {
|
||||||
|
return gui.refreshMergePanel(g)
|
||||||
|
} else {
|
||||||
|
if _, err := gui.g.SetViewOnBottom("merging"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
content := gui.GitCommand.Diff(file, false)
|
content := gui.GitCommand.Diff(file, false)
|
||||||
return gui.renderString(g, "main", content)
|
return gui.renderString(g, "main", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
func (gui *Gui) refreshFiles() error {
|
||||||
filesView, err := g.View("files")
|
filesView := gui.getFilesView()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
gui.refreshStateFiles()
|
gui.refreshStateFiles()
|
||||||
|
|
||||||
gui.g.Update(func(g *gocui.Gui) error {
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
@ -63,8 +64,8 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
fmt.Fprint(filesView, list)
|
fmt.Fprint(filesView, list)
|
||||||
|
|
||||||
if filesView == g.CurrentView() {
|
if filesView == gui.g.CurrentView() {
|
||||||
return gui.handleFileSelect(g, filesView)
|
return gui.handleFileSelect(gui.g, filesView)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -76,6 +77,9 @@ func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Files
|
panelState := gui.State.Panels.Files
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleFileSelect(gui.g, v)
|
return gui.handleFileSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +87,9 @@ func (gui *Gui) handleFilesPrevLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Files
|
panelState := gui.State.Panels.Files
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleFileSelect(gui.g, v)
|
return gui.handleFileSelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,13 +137,9 @@ func (gui *Gui) handleEnterFile(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.handleSwitchToMerge(g, v)
|
return gui.handleSwitchToMerge(g, v)
|
||||||
}
|
}
|
||||||
if !file.HasUnstagedChanges {
|
if !file.HasUnstagedChanges {
|
||||||
gui.Log.WithField("staging", "staging").Info("making error panel")
|
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("FileStagingRequirements"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("FileStagingRequirements"))
|
||||||
}
|
}
|
||||||
stagingView, err := g.View("staging")
|
stagingView := gui.getStagingView()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := gui.switchFocus(g, v, stagingView); err != nil {
|
if err := gui.switchFocus(g, v, stagingView); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -162,7 +165,7 @@ func (gui *Gui) handleFilePress(g *gocui.Gui, v *gocui.View) error {
|
|||||||
gui.GitCommand.UnStageFile(file.Name, file.Tracked)
|
gui.GitCommand.UnStageFile(file.Name, file.Tracked)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshFiles(g); err != nil {
|
if err := gui.refreshFiles(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +192,7 @@ func (gui *Gui) handleStageAll(g *gocui.Gui, v *gocui.View) error {
|
|||||||
_ = gui.createErrorPanel(g, err.Error())
|
_ = gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshFiles(g); err != nil {
|
if err := gui.refreshFiles(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +243,7 @@ func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.RemoveFile(file); err != nil {
|
if err := gui.GitCommand.RemoveFile(file); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,14 +258,14 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.Ignore(file.Name); err != nil {
|
if err := gui.GitCommand.Ignore(file.Name); err != nil {
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
|
||||||
if len(gui.stagedFiles()) == 0 && gui.State.WorkingTreeState == "normal" {
|
if len(gui.stagedFiles()) == 0 && gui.State.WorkingTreeState == "normal" {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
|
||||||
}
|
}
|
||||||
commitMessageView := gui.getCommitMessageView(g)
|
commitMessageView := gui.getCommitMessageView()
|
||||||
g.Update(func(g *gocui.Gui) error {
|
g.Update(func(g *gocui.Gui) error {
|
||||||
g.SetViewOnTop("commitMessage")
|
g.SetViewOnTop("commitMessage")
|
||||||
gui.switchFocus(g, filesView, commitMessageView)
|
gui.switchFocus(g, filesView, commitMessageView)
|
||||||
@ -342,7 +345,7 @@ func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshStateFiles() {
|
func (gui *Gui) refreshStateFiles() {
|
||||||
@ -353,27 +356,6 @@ func (gui *Gui) refreshStateFiles() {
|
|||||||
gui.updateWorkTreeState()
|
gui.updateWorkTreeState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) updateWorkTreeState() error {
|
|
||||||
merging, err := gui.GitCommand.IsInMergeState()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if merging {
|
|
||||||
gui.State.WorkingTreeState = "merging"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rebasing, err := gui.GitCommand.IsInRebaseState()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rebasing {
|
|
||||||
gui.State.WorkingTreeState = "rebasing"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
gui.State.WorkingTreeState = "normal"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
||||||
item, err := gui.getSelectedFile(g)
|
item, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -403,7 +385,7 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error {
|
|||||||
gui.refreshCommits(g)
|
gui.refreshCommits(g)
|
||||||
gui.refreshStatus(g)
|
gui.refreshStatus(g)
|
||||||
}
|
}
|
||||||
gui.refreshFiles(g)
|
gui.refreshFiles()
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -437,10 +419,6 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
|
||||||
mergeView, err := g.View("main")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
file, err := gui.getSelectedFile(g)
|
file, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != gui.Errors.ErrNoFiles {
|
if err != gui.Errors.ErrNoFiles {
|
||||||
@ -451,8 +429,7 @@ func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if !file.HasMergeConflicts {
|
if !file.HasMergeConflicts {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("FileNoMergeCons"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("FileNoMergeCons"))
|
||||||
}
|
}
|
||||||
gui.switchFocus(g, v, mergeView)
|
return gui.switchFocus(g, v, gui.getMergingView())
|
||||||
return gui.refreshMergePanel(g)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -461,7 +438,7 @@ func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("MergeAborted"))
|
gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("MergeAborted"))
|
||||||
gui.refreshStatus(g)
|
gui.refreshStatus(g)
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -469,7 +446,7 @@ func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.ResetAndClean(); err != nil {
|
if err := gui.GitCommand.ResetAndClean(); err != nil {
|
||||||
gui.createErrorPanel(g, err.Error())
|
gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,3 +456,12 @@ func (gui *Gui) openFile(filename string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) anyFilesWithMergeConflicts() bool {
|
||||||
|
for _, file := range gui.State.Files {
|
||||||
|
if file.HasMergeConflicts {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -82,6 +82,13 @@ type stagingPanelState struct {
|
|||||||
Diff string
|
Diff string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mergingPanelState struct {
|
||||||
|
ConflictIndex int
|
||||||
|
ConflictTop bool
|
||||||
|
Conflicts []commands.Conflict
|
||||||
|
EditHistory *stack.Stack
|
||||||
|
}
|
||||||
|
|
||||||
type filePanelState struct {
|
type filePanelState struct {
|
||||||
SelectedLine int
|
SelectedLine int
|
||||||
}
|
}
|
||||||
@ -104,11 +111,12 @@ type menuPanelState struct {
|
|||||||
|
|
||||||
type panelStates struct {
|
type panelStates struct {
|
||||||
Files *filePanelState
|
Files *filePanelState
|
||||||
Staging *stagingPanelState
|
|
||||||
Branches *branchPanelState
|
Branches *branchPanelState
|
||||||
Commits *commitPanelState
|
Commits *commitPanelState
|
||||||
Stash *stashPanelState
|
Stash *stashPanelState
|
||||||
Menu *menuPanelState
|
Menu *menuPanelState
|
||||||
|
Staging *stagingPanelState
|
||||||
|
Merging *mergingPanelState
|
||||||
}
|
}
|
||||||
|
|
||||||
type guiState struct {
|
type guiState struct {
|
||||||
@ -118,10 +126,6 @@ type guiState struct {
|
|||||||
StashEntries []*commands.StashEntry
|
StashEntries []*commands.StashEntry
|
||||||
PreviousView string
|
PreviousView string
|
||||||
HasMergeConflicts bool
|
HasMergeConflicts bool
|
||||||
ConflictIndex int
|
|
||||||
ConflictTop bool
|
|
||||||
Conflicts []commands.Conflict
|
|
||||||
EditHistory *stack.Stack
|
|
||||||
Platform commands.Platform
|
Platform commands.Platform
|
||||||
Updating bool
|
Updating bool
|
||||||
Panels *panelStates
|
Panels *panelStates
|
||||||
@ -132,21 +136,23 @@ type guiState struct {
|
|||||||
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
|
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
|
||||||
|
|
||||||
initialState := guiState{
|
initialState := guiState{
|
||||||
Files: make([]*commands.File, 0),
|
Files: make([]*commands.File, 0),
|
||||||
PreviousView: "files",
|
PreviousView: "files",
|
||||||
Commits: make([]*commands.Commit, 0),
|
Commits: make([]*commands.Commit, 0),
|
||||||
StashEntries: make([]*commands.StashEntry, 0),
|
StashEntries: make([]*commands.StashEntry, 0),
|
||||||
ConflictIndex: 0,
|
Platform: *oSCommand.Platform,
|
||||||
ConflictTop: true,
|
|
||||||
Conflicts: make([]commands.Conflict, 0),
|
|
||||||
EditHistory: stack.New(),
|
|
||||||
Platform: *oSCommand.Platform,
|
|
||||||
Panels: &panelStates{
|
Panels: &panelStates{
|
||||||
Files: &filePanelState{SelectedLine: -1},
|
Files: &filePanelState{SelectedLine: -1},
|
||||||
Branches: &branchPanelState{SelectedLine: 0},
|
Branches: &branchPanelState{SelectedLine: 0},
|
||||||
Commits: &commitPanelState{SelectedLine: -1},
|
Commits: &commitPanelState{SelectedLine: -1},
|
||||||
Stash: &stashPanelState{SelectedLine: -1},
|
Stash: &stashPanelState{SelectedLine: -1},
|
||||||
Menu: &menuPanelState{SelectedLine: 0},
|
Menu: &menuPanelState{SelectedLine: 0},
|
||||||
|
Merging: &mergingPanelState{
|
||||||
|
ConflictIndex: 0,
|
||||||
|
ConflictTop: true,
|
||||||
|
Conflicts: []commands.Conflict{},
|
||||||
|
EditHistory: stack.New(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +277,18 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v, err = g.SetView("merging", leftSideWidth+panelSpacing, 0, width-1, optionsTop, gocui.LEFT)
|
||||||
|
if err != nil {
|
||||||
|
if err != gocui.ErrUnknownView {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Title = gui.Tr.SLocalize("MergingTitle")
|
||||||
|
v.FgColor = gocui.ColorWhite
|
||||||
|
if _, err := g.SetViewOnBottom("merging"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if v, err := g.SetView("status", 0, 0, leftSideWidth, statusFilesBoundary, gocui.BOTTOM|gocui.RIGHT); err != nil {
|
if v, err := g.SetView("status", 0, 0, leftSideWidth, statusFilesBoundary, gocui.BOTTOM|gocui.RIGHT); err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
return err
|
||||||
@ -324,7 +342,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if gui.getCommitMessageView(g) == nil {
|
if gui.getCommitMessageView() == nil {
|
||||||
// doesn't matter where this view starts because it will be hidden
|
// doesn't matter where this view starts because it will be hidden
|
||||||
if commitMessageView, err := g.SetView("commitMessage", 0, 0, width/2, height/2, 0); err != nil {
|
if commitMessageView, err := g.SetView("commitMessage", 0, 0, width/2, height/2, 0); err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
@ -401,7 +419,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
// here is a good place log some stuff
|
// here is a good place log some stuff
|
||||||
// if you download humanlog and do tail -f development.log | humanlog
|
// if you download humanlog and do tail -f development.log | humanlog
|
||||||
// this will let you see these branches as prettified json
|
// this will let you see these branches as prettified json
|
||||||
// gui.Log.Info(utils.AsJson(gui.State.Branches[0:4]))
|
// gui.Log.Info(utils.AsJson(gui.State.Files))
|
||||||
|
|
||||||
return gui.resizeCurrentPopupPanel(g)
|
return gui.resizeCurrentPopupPanel(g)
|
||||||
}
|
}
|
||||||
@ -414,18 +432,18 @@ func (gui *Gui) promptAnonymousReporting() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) fetch(g *gocui.Gui) error {
|
func (gui *Gui) fetch() error {
|
||||||
gui.GitCommand.Fetch()
|
gui.GitCommand.Fetch()
|
||||||
gui.refreshStatus(g)
|
gui.refreshStatus(gui.g)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) updateLoader(g *gocui.Gui) error {
|
func (gui *Gui) updateLoader() error {
|
||||||
if view, _ := g.View("confirmation"); view != nil {
|
if view, _ := gui.g.View("confirmation"); view != nil {
|
||||||
content := gui.trimmedContent(view)
|
content := gui.trimmedContent(view)
|
||||||
if strings.Contains(content, "...") {
|
if strings.Contains(content, "...") {
|
||||||
staticContent := strings.Split(content, "...")[0] + "..."
|
staticContent := strings.Split(content, "...")[0] + "..."
|
||||||
if err := gui.renderString(g, "confirmation", staticContent+" "+utils.Loader()); err != nil {
|
if err := gui.renderString(gui.g, "confirmation", staticContent+" "+utils.Loader()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -433,7 +451,7 @@ func (gui *Gui) updateLoader(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderAppStatus(g *gocui.Gui) error {
|
func (gui *Gui) renderAppStatus() error {
|
||||||
appStatus := gui.statusManager.getStatusString()
|
appStatus := gui.statusManager.getStatusString()
|
||||||
if appStatus != "" {
|
if appStatus != "" {
|
||||||
return gui.renderString(gui.g, "appStatus", appStatus)
|
return gui.renderString(gui.g, "appStatus", appStatus)
|
||||||
@ -450,10 +468,10 @@ func (gui *Gui) renderGlobalOptions() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
|
func (gui *Gui) goEvery(interval time.Duration, function func() error) {
|
||||||
go func() {
|
go func() {
|
||||||
for range time.Tick(interval) {
|
for range time.Tick(interval) {
|
||||||
function(g)
|
function()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -472,10 +490,10 @@ func (gui *Gui) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.goEvery(g, time.Second*60, gui.fetch)
|
gui.goEvery(time.Second*60, gui.fetch)
|
||||||
gui.goEvery(g, time.Second*2, gui.refreshFiles)
|
gui.goEvery(time.Second*2, gui.refreshFiles)
|
||||||
gui.goEvery(g, time.Millisecond*50, gui.updateLoader)
|
gui.goEvery(time.Millisecond*50, gui.updateLoader)
|
||||||
gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus)
|
gui.goEvery(time.Millisecond*50, gui.renderAppStatus)
|
||||||
|
|
||||||
g.SetManagerFunc(gui.layout)
|
g.SetManagerFunc(gui.layout)
|
||||||
|
|
||||||
|
@ -153,11 +153,11 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
Handler: gui.handleFileRemove,
|
Handler: gui.handleFileRemove,
|
||||||
Description: gui.Tr.SLocalize("removeFile"),
|
Description: gui.Tr.SLocalize("removeFile"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "files",
|
ViewName: "files", // TODO: might make this for more views as well
|
||||||
Key: 'm',
|
Key: 'm',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSwitchToMerge,
|
Handler: gui.handleCreateRebaseOptionsMenu,
|
||||||
Description: gui.Tr.SLocalize("resolveMergeConflicts"),
|
Description: gui.Tr.SLocalize("ViewMergeRebaseOptions"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
Key: 'e',
|
Key: 'e',
|
||||||
@ -188,12 +188,6 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleStashSave,
|
Handler: gui.handleStashSave,
|
||||||
Description: gui.Tr.SLocalize("stashFiles"),
|
Description: gui.Tr.SLocalize("stashFiles"),
|
||||||
}, {
|
|
||||||
ViewName: "files",
|
|
||||||
Key: 'M',
|
|
||||||
Modifier: gocui.ModNone,
|
|
||||||
Handler: gui.handleAbortMerge,
|
|
||||||
Description: gui.Tr.SLocalize("abortMerge"),
|
|
||||||
}, {
|
}, {
|
||||||
ViewName: "files",
|
ViewName: "files",
|
||||||
Key: 'a',
|
Key: 'a',
|
||||||
@ -220,65 +214,69 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
|||||||
Description: gui.Tr.SLocalize("StageLines"),
|
Description: gui.Tr.SLocalize("StageLines"),
|
||||||
KeyReadable: "enter",
|
KeyReadable: "enter",
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: gocui.KeyEsc,
|
Key: gocui.KeyEsc,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleEscapeMerge,
|
Handler: gui.handleEscapeMerge,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: gocui.KeySpace,
|
Key: gocui.KeySpace,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handlePickHunk,
|
Handler: gui.handlePickHunk,
|
||||||
|
Description: gui.Tr.SLocalize("PickHunk"),
|
||||||
|
KeyReadable: "space",
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: 'b',
|
Key: 'b',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handlePickBothHunks,
|
Handler: gui.handlePickBothHunks,
|
||||||
|
Description: gui.Tr.SLocalize("PickBothHunks"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: gocui.KeyArrowLeft,
|
Key: gocui.KeyArrowLeft,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectPrevConflict,
|
Handler: gui.handleSelectPrevConflict,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: gocui.KeyArrowRight,
|
Key: gocui.KeyArrowRight,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectNextConflict,
|
Handler: gui.handleSelectNextConflict,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: gocui.KeyArrowUp,
|
Key: gocui.KeyArrowUp,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectTop,
|
Handler: gui.handleSelectTop,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: gocui.KeyArrowDown,
|
Key: gocui.KeyArrowDown,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectBottom,
|
Handler: gui.handleSelectBottom,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: 'h',
|
Key: 'h',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectPrevConflict,
|
Handler: gui.handleSelectPrevConflict,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: 'l',
|
Key: 'l',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectNextConflict,
|
Handler: gui.handleSelectNextConflict,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: 'k',
|
Key: 'k',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectTop,
|
Handler: gui.handleSelectTop,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: 'j',
|
Key: 'j',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handleSelectBottom,
|
Handler: gui.handleSelectBottom,
|
||||||
}, {
|
}, {
|
||||||
ViewName: "main",
|
ViewName: "merging",
|
||||||
Key: 'z',
|
Key: 'z',
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: gui.handlePopFileSnapshot,
|
Handler: gui.handlePopFileSnapshot,
|
||||||
|
Description: gui.Tr.SLocalize("Undo"),
|
||||||
}, {
|
}, {
|
||||||
ViewName: "branches",
|
ViewName: "branches",
|
||||||
Key: gocui.KeySpace,
|
Key: gocui.KeySpace,
|
||||||
|
@ -2,7 +2,6 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@ -50,7 +49,7 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.returnFocus(g, v)
|
return gui.returnFocus(g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error {
|
func (gui *Gui) createMenu(title string, items interface{}, handlePress func(int) error) error {
|
||||||
list, err := utils.RenderList(items)
|
list, err := utils.RenderList(items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -58,7 +57,7 @@ func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error
|
|||||||
|
|
||||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, list)
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, list)
|
||||||
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
||||||
menuView.Title = strings.Title(gui.Tr.SLocalize("menu"))
|
menuView.Title = title
|
||||||
menuView.FgColor = gocui.ColorWhite
|
menuView.FgColor = gocui.ColorWhite
|
||||||
menuView.Clear()
|
menuView.Clear()
|
||||||
fmt.Fprint(menuView, list)
|
fmt.Fprint(menuView, list)
|
||||||
@ -66,7 +65,13 @@ func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error
|
|||||||
|
|
||||||
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
|
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
|
||||||
selectedLine := gui.State.Panels.Menu.SelectedLine
|
selectedLine := gui.State.Panels.Menu.SelectedLine
|
||||||
return handlePress(selectedLine)
|
if err := handlePress(selectedLine); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := gui.g.SetViewOnBottom("menu"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.returnFocus(gui.g, menuView)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil {
|
if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
"github.com/golang-collections/collections/stack"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@ -21,6 +22,7 @@ func (gui *Gui) findConflicts(content string) ([]commands.Conflict, error) {
|
|||||||
var newConflict commands.Conflict
|
var newConflict commands.Conflict
|
||||||
for i, line := range utils.SplitLines(content) {
|
for i, line := range utils.SplitLines(content) {
|
||||||
trimmedLine := strings.TrimPrefix(line, "++")
|
trimmedLine := strings.TrimPrefix(line, "++")
|
||||||
|
gui.Log.Info(trimmedLine)
|
||||||
if trimmedLine == "<<<<<<< HEAD" || trimmedLine == "<<<<<<< MERGE_HEAD" || trimmedLine == "<<<<<<< Updated upstream" {
|
if trimmedLine == "<<<<<<< HEAD" || trimmedLine == "<<<<<<< MERGE_HEAD" || trimmedLine == "<<<<<<< Updated upstream" {
|
||||||
newConflict = commands.Conflict{Start: i}
|
newConflict = commands.Conflict{Start: i}
|
||||||
} else if trimmedLine == "=======" {
|
} else if trimmedLine == "=======" {
|
||||||
@ -65,28 +67,28 @@ func (gui *Gui) coloredConflictFile(content string, conflicts []commands.Conflic
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSelectTop(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleSelectTop(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.State.ConflictTop = true
|
gui.State.Panels.Merging.ConflictTop = true
|
||||||
return gui.refreshMergePanel(g)
|
return gui.refreshMergePanel(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSelectBottom(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleSelectBottom(g *gocui.Gui, v *gocui.View) error {
|
||||||
gui.State.ConflictTop = false
|
gui.State.Panels.Merging.ConflictTop = false
|
||||||
return gui.refreshMergePanel(g)
|
return gui.refreshMergePanel(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSelectNextConflict(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleSelectNextConflict(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.State.ConflictIndex >= len(gui.State.Conflicts)-1 {
|
if gui.State.Panels.Merging.ConflictIndex >= len(gui.State.Panels.Merging.Conflicts)-1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gui.State.ConflictIndex++
|
gui.State.Panels.Merging.ConflictIndex++
|
||||||
return gui.refreshMergePanel(g)
|
return gui.refreshMergePanel(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSelectPrevConflict(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleSelectPrevConflict(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.State.ConflictIndex <= 0 {
|
if gui.State.Panels.Merging.ConflictIndex <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gui.State.ConflictIndex--
|
gui.State.Panels.Merging.ConflictIndex--
|
||||||
return gui.refreshMergePanel(g)
|
return gui.refreshMergePanel(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,15 +136,15 @@ func (gui *Gui) pushFileSnapshot(g *gocui.Gui) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gui.State.EditHistory.Push(content)
|
gui.State.Panels.Merging.EditHistory.Push(content)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.State.EditHistory.Len() == 0 {
|
if gui.State.Panels.Merging.EditHistory.Len() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
prevContent := gui.State.EditHistory.Pop().(string)
|
prevContent := gui.State.Panels.Merging.EditHistory.Pop().(string)
|
||||||
gitFile, err := gui.getSelectedFile(g)
|
gitFile, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -152,22 +154,29 @@ func (gui *Gui) handlePopFileSnapshot(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handlePickHunk(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handlePickHunk(g *gocui.Gui, v *gocui.View) error {
|
||||||
conflict := gui.State.Conflicts[gui.State.ConflictIndex]
|
conflict := gui.State.Panels.Merging.Conflicts[gui.State.Panels.Merging.ConflictIndex]
|
||||||
gui.pushFileSnapshot(g)
|
gui.pushFileSnapshot(g)
|
||||||
pick := "bottom"
|
pick := "bottom"
|
||||||
if gui.State.ConflictTop {
|
if gui.State.Panels.Merging.ConflictTop {
|
||||||
pick = "top"
|
pick = "top"
|
||||||
}
|
}
|
||||||
err := gui.resolveConflict(g, conflict, pick)
|
err := gui.resolveConflict(g, conflict, pick)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if that was the last conflict, finish the merge for this file
|
||||||
|
if len(gui.State.Panels.Merging.Conflicts) == 1 {
|
||||||
|
if err := gui.handleCompleteMerge(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
gui.refreshMergePanel(g)
|
gui.refreshMergePanel(g)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
|
||||||
conflict := gui.State.Conflicts[gui.State.ConflictIndex]
|
conflict := gui.State.Panels.Merging.Conflicts[gui.State.Panels.Merging.ConflictIndex]
|
||||||
gui.pushFileSnapshot(g)
|
gui.pushFileSnapshot(g)
|
||||||
err := gui.resolveConflict(g, conflict, "both")
|
err := gui.resolveConflict(g, conflict, "both")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -177,6 +186,7 @@ func (gui *Gui) handlePickBothHunks(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) refreshMergePanel(g *gocui.Gui) error {
|
func (gui *Gui) refreshMergePanel(g *gocui.Gui) error {
|
||||||
|
panelState := gui.State.Panels.Merging
|
||||||
cat, err := gui.catSelectedFile(g)
|
cat, err := gui.catSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -184,47 +194,56 @@ func (gui *Gui) refreshMergePanel(g *gocui.Gui) error {
|
|||||||
if cat == "" {
|
if cat == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
gui.State.Conflicts, err = gui.findConflicts(cat)
|
gui.Log.Info(cat)
|
||||||
|
panelState.Conflicts, err = gui.findConflicts(cat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(gui.State.Conflicts) == 0 {
|
// handle potential fixes that the user made in their editor since we last refreshed
|
||||||
return gui.handleCompleteMerge(g)
|
if len(panelState.Conflicts) == 0 {
|
||||||
} else if gui.State.ConflictIndex > len(gui.State.Conflicts)-1 {
|
return gui.handleCompleteMerge()
|
||||||
gui.State.ConflictIndex = len(gui.State.Conflicts) - 1
|
} else if panelState.ConflictIndex > len(panelState.Conflicts)-1 {
|
||||||
|
panelState.ConflictIndex = len(panelState.Conflicts) - 1
|
||||||
}
|
}
|
||||||
hasFocus := gui.currentViewName(g) == "main"
|
|
||||||
content, err := gui.coloredConflictFile(cat, gui.State.Conflicts, gui.State.ConflictIndex, gui.State.ConflictTop, hasFocus)
|
gui.g.SetViewOnTop("merging")
|
||||||
|
hasFocus := gui.currentViewName(g) == "merging"
|
||||||
|
content, err := gui.coloredConflictFile(cat, panelState.Conflicts, panelState.ConflictIndex, panelState.ConflictTop, hasFocus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := gui.renderString(g, "merging", content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := gui.scrollToConflict(g); err != nil {
|
if err := gui.scrollToConflict(g); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.renderString(g, "main", content)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) scrollToConflict(g *gocui.Gui) error {
|
func (gui *Gui) scrollToConflict(g *gocui.Gui) error {
|
||||||
mainView, err := g.View("main")
|
panelState := gui.State.Panels.Merging
|
||||||
if err != nil {
|
if len(panelState.Conflicts) == 0 {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(gui.State.Conflicts) == 0 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
conflict := gui.State.Conflicts[gui.State.ConflictIndex]
|
mergingView := gui.getMergingView()
|
||||||
ox, _ := mainView.Origin()
|
conflict := panelState.Conflicts[panelState.ConflictIndex]
|
||||||
_, height := mainView.Size()
|
gui.Log.Info(utils.AsJson(conflict))
|
||||||
|
ox, _ := mergingView.Origin()
|
||||||
|
_, height := mergingView.Size()
|
||||||
conflictMiddle := (conflict.End + conflict.Start) / 2
|
conflictMiddle := (conflict.End + conflict.Start) / 2
|
||||||
newOriginY := int(math.Max(0, float64(conflictMiddle-(height/2))))
|
newOriginY := int(math.Max(0, float64(conflictMiddle-(height/2))))
|
||||||
return mainView.SetOrigin(ox, newOriginY)
|
gui.Log.Info(utils.AsJson("origin Y"))
|
||||||
|
gui.Log.Info(utils.AsJson(newOriginY))
|
||||||
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
|
return mergingView.SetOrigin(ox, newOriginY)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) switchToMerging(g *gocui.Gui) error {
|
func (gui *Gui) switchToMerging(g *gocui.Gui) error {
|
||||||
gui.State.ConflictIndex = 0
|
_, err := g.SetCurrentView("merging")
|
||||||
gui.State.ConflictTop = true
|
|
||||||
_, err := g.SetCurrentView("main")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -242,34 +261,44 @@ func (gui *Gui) renderMergeOptions() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleEscapeMerge(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleEscapeMerge(g *gocui.Gui, v *gocui.View) error {
|
||||||
filesView, err := g.View("files")
|
gui.State.Panels.Merging.EditHistory = stack.New()
|
||||||
if err != nil {
|
gui.g.SetViewOnBottom("merging")
|
||||||
return err
|
gui.refreshFiles()
|
||||||
|
// it's possible this method won't be called from the merging view so we need to
|
||||||
|
// ensure we only 'return' focus if we already have it
|
||||||
|
if gui.g.CurrentView() == gui.getMergingView() {
|
||||||
|
return gui.switchFocus(g, v, gui.getFilesView())
|
||||||
}
|
}
|
||||||
gui.refreshFiles(g)
|
return nil
|
||||||
return gui.switchFocus(g, v, filesView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCompleteMerge(g *gocui.Gui) error {
|
func (gui *Gui) handleCompleteMerge() error {
|
||||||
filesView, err := g.View("files")
|
filesView := gui.getFilesView()
|
||||||
if err != nil {
|
gui.stageSelectedFile(gui.g)
|
||||||
return err
|
gui.refreshFiles()
|
||||||
}
|
// if there are no more files with merge conflicts, we should ask whether the user wants to continue
|
||||||
gui.stageSelectedFile(g)
|
if !gui.anyFilesWithMergeConflicts() {
|
||||||
gui.refreshFiles(g)
|
// ask if user wants to continue
|
||||||
if rebase, err := gui.GitCommand.IsInRebaseState(); rebase && err == nil {
|
if err := gui.createConfirmationPanel(gui.g, filesView, "continue", "all merge conflicts, resolved. Continue?", func(g *gocui.Gui, v *gocui.View) error {
|
||||||
if err := gui.GitCommand.ContinueRebaseBranch(); err != nil {
|
if err := gui.genericRebaseCommand("continue"); err != nil {
|
||||||
if strings.Contains(err.Error(), "No changes - did you forget to use") {
|
if err == gui.Errors.ErrSubProcess {
|
||||||
if err := gui.GitCommand.SkipRebaseBranch(); err != nil {
|
return err
|
||||||
gui.Log.Errorln(err)
|
}
|
||||||
|
if strings.Contains(err.Error(), "No changes - did you forget to use") {
|
||||||
|
if err := gui.genericRebaseCommand("skip"); err != nil {
|
||||||
|
if err == gui.Errors.ErrSubProcess {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gui.createErrorPanel(gui.g, err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
gui.Log.Errorln(err)
|
|
||||||
}
|
}
|
||||||
}
|
return gui.refreshSidePanels(gui.g)
|
||||||
if err := gui.refreshSidePanels(g); err != nil {
|
}, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return gui.switchFocus(g, nil, filesView)
|
return gui.handleEscapeMerge(gui.g, gui.getMergingView())
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
)
|
)
|
||||||
@ -47,5 +48,5 @@ func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return bindings[index].Handler(g, v)
|
return bindings[index].Handler(g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createMenu(bindings, handleMenuPress)
|
return gui.createMenu(strings.Title(gui.Tr.SLocalize("menu")), bindings, handleMenuPress)
|
||||||
}
|
}
|
||||||
|
61
pkg/gui/rebase_options_panel.go
Normal file
61
pkg/gui/rebase_options_panel.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type option struct {
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDisplayStrings is a function.
|
||||||
|
func (r *option) GetDisplayStrings() []string {
|
||||||
|
return []string{r.value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCreateRebaseOptionsMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
options := []*option{
|
||||||
|
{value: "continue"},
|
||||||
|
{value: "abort"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if gui.State.WorkingTreeState == "rebasing" {
|
||||||
|
options = append(options, &option{value: "skip"})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMenuPress := func(index int) error {
|
||||||
|
command := options[index].value
|
||||||
|
return gui.genericRebaseCommand(command)
|
||||||
|
}
|
||||||
|
|
||||||
|
var title string
|
||||||
|
if gui.State.WorkingTreeState == "merging" {
|
||||||
|
title = gui.Tr.SLocalize("MergeOptionsTitle")
|
||||||
|
} else {
|
||||||
|
title = gui.Tr.SLocalize("RebaseOptionsTitle")
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.createMenu(title, options, handleMenuPress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) genericRebaseCommand(command string) error {
|
||||||
|
status := gui.State.WorkingTreeState
|
||||||
|
|
||||||
|
if status != "merging" && status != "rebasing" {
|
||||||
|
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("NotMergingOrRebasing"))
|
||||||
|
}
|
||||||
|
|
||||||
|
commandType := strings.Replace(status, "ing", "e", 1)
|
||||||
|
// we should end up with a command like 'git merge --continue'
|
||||||
|
|
||||||
|
sub := gui.OSCommand.PrepareSubProcess("git", commandType, fmt.Sprintf("--%s", command))
|
||||||
|
if sub != nil {
|
||||||
|
gui.SubProcess = sub
|
||||||
|
return gui.Errors.ErrSubProcess
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -44,7 +44,7 @@ func (gui *Gui) handleCreateRecentReposMenu(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.Errors.ErrSwitchRepo
|
return gui.Errors.ErrSwitchRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createMenu(recentRepos, handleMenuPress)
|
return gui.createMenu(gui.Tr.SLocalize("RecentRepos"), recentRepos, handleMenuPress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateRecentRepoList registers the fact that we opened lazygit in this repo,
|
// updateRecentRepoList registers the fact that we opened lazygit in this repo,
|
||||||
|
@ -76,7 +76,7 @@ func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
gui.State.Panels.Staging = nil
|
gui.State.Panels.Staging = nil
|
||||||
|
|
||||||
return gui.switchFocus(gui.g, nil, gui.getFilesView(gui.g))
|
return gui.switchFocus(gui.g, nil, gui.getFilesView())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStagingPrevLine(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStagingPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -138,7 +138,7 @@ func (gui *Gui) handleCycleLine(prev bool) error {
|
|||||||
// focusLineAndHunk works out the best focus for the staging panel given the
|
// focusLineAndHunk works out the best focus for the staging panel given the
|
||||||
// selected line and size of the hunk
|
// selected line and size of the hunk
|
||||||
func (gui *Gui) focusLineAndHunk() error {
|
func (gui *Gui) focusLineAndHunk() error {
|
||||||
stagingView := gui.getStagingView(gui.g)
|
stagingView := gui.getStagingView()
|
||||||
state := gui.State.Panels.Staging
|
state := gui.State.Panels.Staging
|
||||||
|
|
||||||
lineNumber := state.StageableLines[state.SelectedLine]
|
lineNumber := state.StageableLines[state.SelectedLine]
|
||||||
@ -209,7 +209,7 @@ func (gui *Gui) handleStageLineOrHunk(hunk bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gui.refreshFiles(gui.g); err != nil {
|
if err := gui.refreshFiles(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := gui.refreshStagingPanel(); err != nil {
|
if err := gui.refreshStagingPanel(); err != nil {
|
||||||
|
@ -46,11 +46,14 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
v := gui.getStashView(gui.g)
|
v := gui.getStashView()
|
||||||
v.Clear()
|
v.Clear()
|
||||||
fmt.Fprint(v, list)
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
return gui.resetOrigin(v)
|
if err := gui.resetOrigin(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -59,6 +62,9 @@ func (gui *Gui) handleStashNextLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Stash
|
panelState := gui.State.Panels.Stash
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), false)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), false)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleStashEntrySelect(gui.g, v)
|
return gui.handleStashEntrySelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +72,9 @@ func (gui *Gui) handleStashPrevLine(g *gocui.Gui, v *gocui.View) error {
|
|||||||
panelState := gui.State.Panels.Stash
|
panelState := gui.State.Panels.Stash
|
||||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), true)
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), true)
|
||||||
|
|
||||||
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return gui.handleStashEntrySelect(gui.g, v)
|
return gui.handleStashEntrySelect(gui.g, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +111,7 @@ func (gui *Gui) stashDo(g *gocui.Gui, v *gocui.View, method string) error {
|
|||||||
gui.createErrorPanel(g, err.Error())
|
gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
gui.refreshStashEntries(g)
|
gui.refreshStashEntries(g)
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
|
func (gui *Gui) handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
|
||||||
@ -114,7 +123,7 @@ func (gui *Gui) handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
|
|||||||
gui.createErrorPanel(g, err.Error())
|
gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
gui.refreshStashEntries(g)
|
gui.refreshStashEntries(g)
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles()
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
blue := color.New(color.FgBlue)
|
magenta := color.New(color.FgMagenta)
|
||||||
|
|
||||||
dashboardString := strings.Join(
|
dashboardString := strings.Join(
|
||||||
[]string{
|
[]string{
|
||||||
@ -58,7 +58,7 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
|
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
|
||||||
"Tutorial: https://youtu.be/VDXvbHZYeKY",
|
"Tutorial: https://youtu.be/VDXvbHZYeKY",
|
||||||
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
|
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
|
||||||
blue.Sprint("Buy Jesse a coffee: https://donorbox.org/lazygit"), // caffeine ain't free
|
magenta.Sprint("Buy Jesse a coffee: https://donorbox.org/lazygit"), // caffeine ain't free
|
||||||
}, "\n\n")
|
}, "\n\n")
|
||||||
|
|
||||||
return gui.renderString(g, "main", dashboardString)
|
return gui.renderString(g, "main", dashboardString)
|
||||||
@ -84,3 +84,24 @@ func lazygitTitle() string {
|
|||||||
__/ | __/ |
|
__/ | __/ |
|
||||||
|___/ |___/ `
|
|___/ |___/ `
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) updateWorkTreeState() error {
|
||||||
|
merging, err := gui.GitCommand.IsInMergeState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if merging {
|
||||||
|
gui.State.WorkingTreeState = "merging"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rebasing, err := gui.GitCommand.IsInRebaseState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rebasing {
|
||||||
|
gui.State.WorkingTreeState = "rebasing"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
gui.State.WorkingTreeState = "normal"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@ func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
|
|||||||
if err := gui.refreshBranches(g); err != nil {
|
if err := gui.refreshBranches(g); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := gui.refreshFiles(g); err != nil {
|
if err := gui.refreshFiles(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := gui.refreshCommits(g); err != nil {
|
if err := gui.refreshCommits(g); err != nil {
|
||||||
@ -101,7 +101,7 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
case "commitMessage":
|
case "commitMessage":
|
||||||
return gui.handleCommitFocused(g, v)
|
return gui.handleCommitFocused(g, v)
|
||||||
case "main":
|
case "merging":
|
||||||
// TODO: pull this out into a 'view focused' function
|
// TODO: pull this out into a 'view focused' function
|
||||||
gui.refreshMergePanel(g)
|
gui.refreshMergePanel(g)
|
||||||
v.Highlight = false
|
v.Highlight = false
|
||||||
@ -126,6 +126,20 @@ func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.switchFocus(g, v, previousView)
|
return gui.switchFocus(g, v, previousView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in lieu of a proper window system, we've got three panels that overlap,
|
||||||
|
// the main panel, the staging panel, and the merging panel. We will call this
|
||||||
|
// function whenever we might need to hide one of these panels
|
||||||
|
// this function introduces some unwanted technical debt but is necessary for this rebasing feature
|
||||||
|
func (gui *Gui) showCorrectMainPanel() error {
|
||||||
|
// if the files view is not focused or the current file is not in a merging state we hide the merging panel
|
||||||
|
if gui.g.CurrentView().Name() != "merging" && gui.g.CurrentView().Name() != "confirmation" {
|
||||||
|
if _, err := gui.g.SetViewOnBottom("merging"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// pass in oldView = nil if you don't want to be able to return to your old view
|
// pass in oldView = nil if you don't want to be able to return to your old view
|
||||||
func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
||||||
// we assume we'll never want to return focus to a confirmation panel i.e.
|
// we assume we'll never want to return focus to a confirmation panel i.e.
|
||||||
@ -169,6 +183,10 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := gui.showCorrectMainPanel(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return gui.newLineFocused(g, newView)
|
return gui.newLineFocused(g, newView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,9 +247,6 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
v.Clear()
|
v.Clear()
|
||||||
if err := v.SetOrigin(0, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
output := string(bom.Clean([]byte(s)))
|
output := string(bom.Clean([]byte(s)))
|
||||||
output = utils.NormalizeLinefeeds(output)
|
output = utils.NormalizeLinefeeds(output)
|
||||||
fmt.Fprint(v, output)
|
fmt.Fprint(v, output)
|
||||||
@ -255,38 +270,43 @@ func (gui *Gui) renderOptionsMap(optionsMap map[string]string) error {
|
|||||||
|
|
||||||
// TODO: refactor properly
|
// TODO: refactor properly
|
||||||
// i'm so sorry but had to add this getBranchesView
|
// i'm so sorry but had to add this getBranchesView
|
||||||
func (gui *Gui) getFilesView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getFilesView() *gocui.View {
|
||||||
v, _ := g.View("files")
|
v, _ := gui.g.View("files")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getCommitsView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getCommitsView() *gocui.View {
|
||||||
v, _ := g.View("commits")
|
v, _ := gui.g.View("commits")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getCommitMessageView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getCommitMessageView() *gocui.View {
|
||||||
v, _ := g.View("commitMessage")
|
v, _ := gui.g.View("commitMessage")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getBranchesView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getBranchesView() *gocui.View {
|
||||||
v, _ := g.View("branches")
|
v, _ := gui.g.View("branches")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getStagingView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getStagingView() *gocui.View {
|
||||||
v, _ := g.View("staging")
|
v, _ := gui.g.View("staging")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getMainView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getMainView() *gocui.View {
|
||||||
v, _ := g.View("main")
|
v, _ := gui.g.View("main")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getStashView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getStashView() *gocui.View {
|
||||||
v, _ := g.View("stash")
|
v, _ := gui.g.View("stash")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getMergingView() *gocui.View {
|
||||||
|
v, _ := gui.g.View("merging")
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,6 +447,36 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "Fetching",
|
ID: "Fetching",
|
||||||
Other: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
Other: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "MergingTitle",
|
||||||
|
Other: "Resolve merge conflicts",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "FoundConflicts",
|
||||||
|
Other: "Damn, conflicts! To abort press 'esc', otherwise press 'enter'",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "Undo",
|
||||||
|
Other: "undo",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "PickHunk",
|
||||||
|
Other: "pick hunk",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "PickBothHunks",
|
||||||
|
Other: "pick both hunks",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ViewMergeRebaseOptions",
|
||||||
|
Other: "view merge/rebase options",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "NotMergingOrRebasing",
|
||||||
|
Other: "You are currently neither rebasing nor merging",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "RecentRepos",
|
||||||
|
Other: "recent repositories",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "MergeOptionsTitle",
|
||||||
|
Other: "Merge Options",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "RebaseOptionsTitle",
|
||||||
|
Other: "Rebase Options",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user