mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-30 03:23:08 +03:00
more work on rebasing feature
This commit is contained in:
@ -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(
|
||||
|
76
pkg/gui/context.go
Normal file
76
pkg/gui/context.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
return gui.OSCommand.RunCommand(fmt.Sprintf("git %s --%s", commandType, command))
|
||||
result := gui.GitCommand.GenericMerge(commandType, command)
|
||||
if err := gui.handleGenericMergeCommandResult(result); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -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{
|
||||
|
Reference in New Issue
Block a user