diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2019-02-16 21:01:17 +1100 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2019-02-16 21:01:17 +1100 |
commit | e011e9bc4238b97a70ac6e4b35a862a028ce55e4 (patch) | |
tree | b4812f422ee3b38844796617299a5cecc7835369 /pkg/gui | |
parent | ad93b4c863dfaa6a1cb6bb740d0dba87fef14404 (diff) |
more work on rebasing feature
Diffstat (limited to 'pkg/gui')
-rw-r--r-- | pkg/gui/branches_panel.go | 96 | ||||
-rw-r--r-- | pkg/gui/context.go | 76 | ||||
-rw-r--r-- | pkg/gui/gui.go | 36 | ||||
-rw-r--r-- | pkg/gui/keybindings.go | 42 | ||||
-rw-r--r-- | pkg/gui/merge_panel.go | 23 | ||||
-rw-r--r-- | pkg/gui/rebase_options_panel.go | 40 | ||||
-rw-r--r-- | pkg/gui/view_helpers.go | 12 |
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{ |