summaryrefslogtreecommitdiffstats
path: root/pkg/gui
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2019-02-16 21:01:17 +1100
committerJesse Duffield <jessedduffield@gmail.com>2019-02-16 21:01:17 +1100
commite011e9bc4238b97a70ac6e4b35a862a028ce55e4 (patch)
treeb4812f422ee3b38844796617299a5cecc7835369 /pkg/gui
parentad93b4c863dfaa6a1cb6bb740d0dba87fef14404 (diff)
more work on rebasing feature
Diffstat (limited to 'pkg/gui')
-rw-r--r--pkg/gui/branches_panel.go96
-rw-r--r--pkg/gui/context.go76
-rw-r--r--pkg/gui/gui.go36
-rw-r--r--pkg/gui/keybindings.go42
-rw-r--r--pkg/gui/merge_panel.go23
-rw-r--r--pkg/gui/rebase_options_panel.go40
-rw-r--r--pkg/gui/view_helpers.go12
7 files changed, 165 insertions, 160 deletions
diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go
index 54f7de305..bd6db352c 100644
--- a/pkg/gui/branches_panel.go
+++ b/pkg/gui/branches_panel.go
@@ -98,43 +98,6 @@ func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error {
// specific functions
-func (gui *Gui) handleRebase(g *gocui.Gui, v *gocui.View) error {
-
- selectedBranch := gui.getSelectedBranch().Name
- checkedOutBranch := gui.State.Branches[0].Name
- title := "Rebasing"
- prompt := fmt.Sprintf("Are you sure you want to rebase %s onto %s?", checkedOutBranch, selectedBranch)
-
- return gui.createConfirmationPanel(g, v, title, prompt,
- func(g *gocui.Gui, v *gocui.View) error {
- if selectedBranch == checkedOutBranch {
- return gui.createErrorPanel(g, gui.Tr.SLocalize("CantRebaseOntoSelf"))
- }
-
- if err := gui.GitCommand.RebaseBranch(selectedBranch); err != nil {
- if !strings.Contains(err.Error(), "When you have resolved this problem") {
- return gui.createErrorPanel(gui.g, err.Error())
- }
-
- if err := gui.refreshSidePanels(g); err != nil {
- return err
- }
- return gui.createConfirmationPanel(g, v, "Auto-rebase failed", gui.Tr.SLocalize("FoundConflicts"),
- func(g *gocui.Gui, v *gocui.View) error {
- return gui.refreshSidePanels(g)
- }, func(g *gocui.Gui, v *gocui.View) error {
- if err := gui.GitCommand.AbortRebaseBranch(); err != nil {
- return err
- }
- return gui.refreshSidePanels(g)
- },
- )
- }
-
- return gui.refreshSidePanels(g)
- }, nil)
-}
-
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
if gui.State.Panels.Branches.SelectedLine == -1 {
return nil
@@ -263,28 +226,45 @@ func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *c
}
func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
- checkedOutBranch := gui.State.Branches[0]
- selectedBranch := gui.getSelectedBranch()
- defer gui.refreshSidePanels(g)
- if checkedOutBranch.Name == selectedBranch.Name {
+ checkedOutBranch := gui.State.Branches[0].Name
+ selectedBranch := gui.getSelectedBranch().Name
+ if checkedOutBranch == selectedBranch {
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
}
- 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())
+ prompt := gui.Tr.TemplateLocalize(
+ "ConfirmMerge",
+ Teml{
+ "checkedOutBranch": checkedOutBranch,
+ "selectedBranch": selectedBranch,
+ },
+ )
+ return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("MergingTitle"), prompt,
+ func(g *gocui.Gui, v *gocui.View) error {
+
+ err := gui.GitCommand.Merge(selectedBranch)
+ return gui.handleGenericMergeCommandResult(err)
+ }, nil)
+}
+
+func (gui *Gui) handleRebase(g *gocui.Gui, v *gocui.View) error {
+ checkedOutBranch := gui.State.Branches[0].Name
+ selectedBranch := gui.getSelectedBranch().Name
+ if selectedBranch == checkedOutBranch {
+ return gui.createErrorPanel(g, gui.Tr.SLocalize("CantRebaseOntoSelf"))
}
- return nil
+ prompt := gui.Tr.TemplateLocalize(
+ "ConfirmRebase",
+ Teml{
+ "checkedOutBranch": checkedOutBranch,
+ "selectedBranch": selectedBranch,
+ },
+ )
+ return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("RebasingTitle"), prompt,
+ func(g *gocui.Gui, v *gocui.View) error {
+
+ err := gui.GitCommand.RebaseBranch(selectedBranch)
+ return gui.handleGenericMergeCommandResult(err)
+ }, nil)
}
func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
@@ -296,10 +276,10 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
return nil
}
if branch.Pushables == "?" {
- return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with no upstream")
+ return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("FwdNoUpstream"))
}
if branch.Pushables != "0" {
- return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with commits to push")
+ return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("FwdCommitsToPush"))
}
upstream := "origin" // hardcoding for now
message := gui.Tr.TemplateLocalize(
diff --git a/pkg/gui/context.go b/pkg/gui/context.go
new file mode 100644
index 000000000..f6ff147e2
--- /dev/null
+++ b/pkg/gui/context.go
@@ -0,0 +1,76 @@
+package gui
+
+func (gui *Gui) titleMap() map[string]string {
+ return map[string]string{
+ "commits": gui.Tr.SLocalize("DiffTitle"),
+ "branches": gui.Tr.SLocalize("LogTitle"),
+ "files": gui.Tr.SLocalize("DiffTitle"),
+ "status": "",
+ "stash": gui.Tr.SLocalize("DiffTitle"),
+ }
+}
+
+func (gui *Gui) contextTitleMap() map[string]map[string]string {
+ return map[string]map[string]string{
+ "main": {
+ "staging": gui.Tr.SLocalize("StagingMainTitle"),
+ "merging": gui.Tr.SLocalize("MergingMainTitle"),
+ },
+ }
+}
+
+func (gui *Gui) setMainTitle() error {
+ currentViewName := gui.g.CurrentView().Name()
+ var newTitle string
+ if context, ok := gui.State.Contexts[currentViewName]; ok {
+ newTitle = gui.contextTitleMap()[currentViewName][context]
+ } else if title, ok := gui.titleMap()[currentViewName]; ok {
+ newTitle = title
+ } else {
+ return nil
+ }
+ gui.getMainView().Title = newTitle
+ return nil
+}
+
+func (gui *Gui) changeContext(viewName, context string) error {
+ // todo: store somewhere permanently
+ if gui.State.Contexts[viewName] == context {
+ return nil
+ }
+
+ contextMap := gui.getContextMap()
+
+ gui.g.DeleteKeybindings(viewName)
+
+ bindings := contextMap[viewName][context]
+ for _, binding := range bindings {
+ if err := gui.g.SetKeybinding(viewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
+ return err
+ }
+ }
+ gui.State.Contexts[viewName] = context
+ gui.setMainTitle()
+ return nil
+}
+
+func (gui *Gui) setInitialContexts() error {
+ contextMap := gui.getContextMap()
+
+ initialContexts := map[string]string{
+ "main": "merging",
+ }
+
+ for viewName, context := range initialContexts {
+ bindings := contextMap[viewName][context]
+ for _, binding := range bindings {
+ if err := gui.g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
+ return err
+ }
+ }
+ }
+
+ gui.State.Contexts = initialContexts
+
+ return nil
+}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 637db680a..dfaf48cf7 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -217,6 +217,9 @@ func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
return func(g *gocui.Gui) error {
v := gui.g.CurrentView()
if v != focusedView {
+ if err := gui.onFocusChange(); err != nil {
+ return err
+ }
if err := gui.onFocusLost(focusedView); err != nil {
return err
}
@@ -229,6 +232,14 @@ func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
}
}
+func (gui *Gui) onFocusChange() error {
+ currentView := gui.g.CurrentView()
+ for _, view := range gui.g.Views() {
+ view.Highlight = view == currentView
+ }
+ return gui.setMainTitle()
+}
+
func (gui *Gui) onFocusLost(v *gocui.View) error {
if v == nil {
return nil
@@ -306,31 +317,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
v.FgColor = gocui.ColorWhite
}
- // v, err = g.SetView("staging", leftSideWidth+panelSpacing, 0, width-1, optionsTop, gocui.LEFT)
- // if err != nil {
- // if err.Error() != "unknown view" {
- // return err
- // }
- // v.Title = gui.Tr.SLocalize("StagingTitle")
- // v.Highlight = true
- // v.FgColor = gocui.ColorWhite
- // if _, err := g.SetViewOnBottom("staging"); err != nil {
- // return err
- // }
- // }
-
- // v, err = g.SetView("merging", leftSideWidth+panelSpacing, 0, width-1, optionsTop, gocui.LEFT)
- // if err != nil {
- // if err.Error() != "unknown view" {
- // 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 err.Error() != "unknown view" {
return err
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 2b5672595..df6bd8ee3 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -417,48 +417,6 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
return nil
}
-func (gui *Gui) setInitialContexts() error {
- contextMap := gui.getContextMap()
-
- initialContexts := map[string]string{
- "main": "merging",
- }
-
- for viewName, context := range initialContexts {
- bindings := contextMap[viewName][context]
- for _, binding := range bindings {
- if err := gui.g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
- return err
- }
- }
- }
-
- gui.State.Contexts = initialContexts
-
- return nil
-}
-
-func (gui *Gui) changeContext(viewName, context string) error {
- // todo: store somewhere permanently
- if gui.State.Contexts[viewName] == context {
- return nil
- }
-
- contextMap := gui.getContextMap()
-
- gui.g.DeleteKeybindings(viewName)
-
- bindings := contextMap[viewName][context]
- for _, binding := range bindings {
- if err := gui.g.SetKeybinding(viewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
- return err
- }
- }
- gui.State.Contexts[viewName] = context
-
- return nil
-}
-
func (gui *Gui) getContextMap() map[string]map[string][]*Binding {
return map[string]map[string][]*Binding{
"main": {
diff --git a/pkg/gui/merge_panel.go b/pkg/gui/merge_panel.go
index 59922e087..fccfb3a59 100644
--- a/pkg/gui/merge_panel.go
+++ b/pkg/gui/merge_panel.go
@@ -270,27 +270,16 @@ func (gui *Gui) handleCompleteMerge() error {
filesView := gui.getFilesView()
gui.stageSelectedFile(gui.g)
gui.refreshFiles()
+ // if we got conflicts after unstashing, we don't want to call any git
+ // commands to continue rebasing/merging here
+ if gui.State.WorkingTreeState == "normal" {
+ return gui.handleEscapeMerge(gui.g, gui.getMainView())
+ }
// if there are no more files with merge conflicts, we should ask whether the user wants to continue
if !gui.anyFilesWithMergeConflicts() {
// ask if user wants to continue
if err := gui.createConfirmationPanel(gui.g, filesView, "continue", gui.Tr.SLocalize("ConflictsResolved"), func(g *gocui.Gui, v *gocui.View) error {
- if err := gui.genericRebaseCommand("continue"); err != nil {
- if err == gui.Errors.ErrSubProcess {
- return 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 {
- // HERE is the place for this special error panel
- gui.createErrorPanel(gui.g, err.Error())
- }
- }
- return gui.refreshSidePanels(gui.g)
+ return gui.genericMergeCommand("continue")
}, nil); err != nil {
return err
}
diff --git a/pkg/gui/rebase_options_panel.go b/pkg/gui/rebase_options_panel.go
index 7ecfc629d..11afbbeca 100644
--- a/pkg/gui/rebase_options_panel.go
+++ b/pkg/gui/rebase_options_panel.go
@@ -28,11 +28,7 @@ func (gui *Gui) handleCreateRebaseOptionsMenu(g *gocui.Gui, v *gocui.View) error
handleMenuPress := func(index int) error {
command := options[index].value
- err := gui.genericRebaseCommand(command)
- if err != nil {
- return gui.createErrorPanel(gui.g, err.Error())
- }
- return nil
+ return gui.genericMergeCommand(command)
}
var title string
@@ -45,7 +41,7 @@ func (gui *Gui) handleCreateRebaseOptionsMenu(g *gocui.Gui, v *gocui.View) error
return gui.createMenu(title, options, handleMenuPress)
}
-func (gui *Gui) genericRebaseCommand(command string) error {
+func (gui *Gui) genericMergeCommand(command string) error {
status := gui.State.WorkingTreeState
if status != "merging" && status != "rebasing" {
@@ -56,7 +52,8 @@ func (gui *Gui) genericRebaseCommand(command string) error {
// we should end up with a command like 'git merge --continue'
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
- if status == "merging" {
+ // TODO: find a way to make the commit automatic
+ if status == "merging" && command != "abort" && gui.Config.GetUserConfig().GetBool("git.merging.manualCommit") {
sub := gui.OSCommand.PrepareSubProcess("git", commandType, fmt.Sprintf("--%s", command))
if sub != nil {
gui.SubProcess = sub
@@ -64,6 +61,33 @@ func (gui *Gui) genericRebaseCommand(command string) error {
}
return nil
}
+ result := gui.GitCommand.GenericMerge(commandType, command)
+ if err := gui.handleGenericMergeCommandResult(result); err != nil {
+ return err
+ }
+ return nil
+}
- return gui.OSCommand.RunCommand(fmt.Sprintf("git %s --%s", commandType, command))
+func (gui *Gui) handleGenericMergeCommandResult(result error) error {
+ if err := gui.refreshSidePanels(gui.g); err != nil {
+ return err
+ }
+ if result == nil {
+ return nil
+ } else if result == gui.Errors.ErrSubProcess {
+ return result
+ } else if strings.Contains(result.Error(), "No changes - did you forget to use") {
+ return gui.genericMergeCommand("skip")
+ } else if strings.Contains(result.Error(), "When you have resolved this problem") || strings.Contains(result.Error(), "fix conflicts") {
+ // TODO: generalise this title to support merging and rebasing
+ return gui.createConfirmationPanel(gui.g, gui.getFilesView(), gui.Tr.SLocalize("FoundConflictsTitle"), gui.Tr.SLocalize("FoundConflicts"),
+ func(g *gocui.Gui, v *gocui.View) error {
+ return nil
+ }, func(g *gocui.Gui, v *gocui.View) error {
+ return gui.genericMergeCommand("abort")
+ },
+ )
+ } else {
+ return gui.createErrorPanel(gui.g, result.Error())
+ }
}
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index 45c470978..53cf80de5 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -127,19 +127,11 @@ func (gui *Gui) returnFocus(g *gocui.Gui, v *gocui.View) error {
}
// pass in oldView = nil if you don't want to be able to return to your old view
+// TODO: move some of this logic into our onFocusLost and onFocus hooks
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 should never stack confirmation panels
if oldView != nil && oldView.Name() != "confirmation" {
- oldView.Highlight = false
- message := gui.Tr.TemplateLocalize(
- "settingPreviewsViewTo",
- Teml{
- "oldViewName": oldView.Name(),
- },
- )
- gui.Log.Info(message)
-
// second class panels should never have focus restored to them because
// once they lose focus they are effectively 'destroyed'
secondClassPanels := []string{"confirmation", "menu"}
@@ -148,7 +140,7 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
}
}
- newView.Highlight = true
+ gui.Log.Info("setting highlight to true for view" + newView.Name())
message := gui.Tr.TemplateLocalize(
"newFocusedViewIs",
Teml{