2018-08-14 11:05:26 +02:00
|
|
|
package gui
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/jesseduffield/gocui"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/git"
|
|
|
|
)
|
|
|
|
|
2018-12-06 13:18:17 +02:00
|
|
|
// list panel functions
|
|
|
|
|
|
|
|
func (gui *Gui) getSelectedBranch() *commands.Branch {
|
|
|
|
selectedLine := gui.State.Panels.Branches.SelectedLine
|
|
|
|
if selectedLine == -1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return gui.State.Branches[selectedLine]
|
|
|
|
}
|
|
|
|
|
|
|
|
// may want to standardise how these select methods work
|
|
|
|
func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
2019-03-02 04:22:02 +02:00
|
|
|
if gui.popupPanelFocused() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-06 13:18:17 +02:00
|
|
|
// This really shouldn't happen: there should always be a master branch
|
|
|
|
if len(gui.State.Branches) == 0 {
|
|
|
|
return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
|
|
|
|
}
|
|
|
|
branch := gui.getSelectedBranch()
|
|
|
|
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-07 09:52:31 +02:00
|
|
|
go func() {
|
|
|
|
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
|
|
|
}()
|
2018-12-06 13:18:17 +02:00
|
|
|
go func() {
|
|
|
|
graph, err := gui.GitCommand.GetBranchGraph(branch.Name)
|
|
|
|
if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") {
|
|
|
|
graph = gui.Tr.SLocalize("NoTrackingThisBranch")
|
|
|
|
}
|
|
|
|
_ = gui.renderString(g, "main", graph)
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-07 09:52:31 +02:00
|
|
|
func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
|
|
|
|
// here we tell the selected branch that it is selected.
|
|
|
|
// this is necessary for showing stats on a branch that is selected, because
|
|
|
|
// the displaystring function doesn't have access to gui state to tell if it's selected
|
|
|
|
for i, branch := range gui.State.Branches {
|
|
|
|
branch.Selected = i == gui.State.Panels.Branches.SelectedLine
|
|
|
|
}
|
|
|
|
|
|
|
|
branch := gui.getSelectedBranch()
|
|
|
|
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
2018-12-08 07:54:54 +02:00
|
|
|
return gui.renderListPanel(gui.getBranchesView(), gui.State.Branches)
|
2018-12-07 09:52:31 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 13:18:17 +02:00
|
|
|
// gui.refreshStatus is called at the end of this because that's when we can
|
|
|
|
// be sure there is a state.Branches array to pick the current branch from
|
|
|
|
func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
|
|
|
g.Update(func(g *gocui.Gui) error {
|
|
|
|
builder, err := git.NewBranchListBuilder(gui.Log, gui.GitCommand)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
gui.State.Branches = builder.Build()
|
|
|
|
|
|
|
|
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
2018-12-07 09:52:31 +02:00
|
|
|
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
2018-12-06 13:18:17 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return gui.refreshStatus(g)
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
|
2019-02-25 13:11:35 +02:00
|
|
|
if gui.popupPanelFocused() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-06 13:18:17 +02:00
|
|
|
panelState := gui.State.Panels.Branches
|
|
|
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
|
2018-12-08 07:54:54 +02:00
|
|
|
|
|
|
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-06 13:18:17 +02:00
|
|
|
return gui.handleBranchSelect(gui.g, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error {
|
2019-02-25 13:11:35 +02:00
|
|
|
if gui.popupPanelFocused() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-06 13:18:17 +02:00
|
|
|
panelState := gui.State.Panels.Branches
|
|
|
|
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true)
|
|
|
|
|
2018-12-08 07:54:54 +02:00
|
|
|
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-06 13:18:17 +02:00
|
|
|
return gui.handleBranchSelect(gui.g, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// specific functions
|
|
|
|
|
2018-08-14 11:05:26 +02:00
|
|
|
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
2018-12-06 13:18:17 +02:00
|
|
|
if gui.State.Panels.Branches.SelectedLine == -1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if gui.State.Panels.Branches.SelectedLine == 0 {
|
2018-08-16 07:16:32 +02:00
|
|
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
2018-12-06 13:18:17 +02:00
|
|
|
branch := gui.getSelectedBranch()
|
2018-08-14 11:05:26 +02:00
|
|
|
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
|
2018-12-06 13:18:17 +02:00
|
|
|
if err := gui.createErrorPanel(g, err.Error()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-12-07 09:52:31 +02:00
|
|
|
} else {
|
|
|
|
gui.State.Panels.Branches.SelectedLine = 0
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
2018-12-07 09:52:31 +02:00
|
|
|
|
2018-08-14 11:05:26 +02:00
|
|
|
return gui.refreshSidePanels(g)
|
|
|
|
}
|
|
|
|
|
2018-10-12 14:06:03 +02:00
|
|
|
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
|
2018-10-15 11:00:19 +02:00
|
|
|
pullRequest := commands.NewPullRequest(gui.GitCommand)
|
2018-10-12 14:06:03 +02:00
|
|
|
|
2018-12-06 13:18:17 +02:00
|
|
|
branch := gui.getSelectedBranch()
|
2018-10-12 14:06:03 +02:00
|
|
|
if err := pullRequest.Create(branch); err != nil {
|
|
|
|
return gui.createErrorPanel(g, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-02 15:58:18 +02:00
|
|
|
func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error {
|
2019-02-16 03:03:22 +02:00
|
|
|
if err := gui.createLoaderPanel(gui.g, v, gui.Tr.SLocalize("FetchWait")); err != nil {
|
2018-12-07 15:56:29 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
unamePassOpend, err := gui.fetch(g, v, true)
|
2018-12-10 09:22:52 +02:00
|
|
|
gui.HandleCredentialsPopup(g, unamePassOpend, err)
|
2018-12-07 15:56:29 +02:00
|
|
|
}()
|
|
|
|
return nil
|
2018-12-02 15:58:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-14 11:05:26 +02:00
|
|
|
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
|
2018-12-06 13:18:17 +02:00
|
|
|
branch := gui.getSelectedBranch()
|
2018-08-16 07:16:32 +02:00
|
|
|
message := gui.Tr.SLocalize("SureForceCheckout")
|
|
|
|
title := gui.Tr.SLocalize("ForceCheckoutBranch")
|
2018-08-14 22:29:17 +02:00
|
|
|
return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
2018-08-14 11:05:26 +02:00
|
|
|
if err := gui.GitCommand.Checkout(branch.Name, true); err != nil {
|
|
|
|
gui.createErrorPanel(g, err.Error())
|
|
|
|
}
|
|
|
|
return gui.refreshSidePanels(g)
|
|
|
|
}, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error {
|
2018-08-16 07:16:32 +02:00
|
|
|
gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", func(g *gocui.Gui, v *gocui.View) error {
|
2018-08-14 11:05:26 +02:00
|
|
|
if err := gui.GitCommand.Checkout(gui.trimmedContent(v), false); err != nil {
|
|
|
|
return gui.createErrorPanel(g, err.Error())
|
|
|
|
}
|
|
|
|
return gui.refreshSidePanels(g)
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleNewBranch(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
branch := gui.State.Branches[0]
|
2018-08-14 22:29:17 +02:00
|
|
|
message := gui.Tr.TemplateLocalize(
|
|
|
|
"NewBranchNameBranchOff",
|
2018-08-16 11:31:03 +02:00
|
|
|
Teml{
|
2018-08-14 22:29:17 +02:00
|
|
|
"branchName": branch.Name,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
gui.createPromptPanel(g, v, message, func(g *gocui.Gui, v *gocui.View) error {
|
2018-08-14 11:05:26 +02:00
|
|
|
if err := gui.GitCommand.NewBranch(gui.trimmedContent(v)); err != nil {
|
|
|
|
return gui.createErrorPanel(g, err.Error())
|
|
|
|
}
|
|
|
|
gui.refreshSidePanels(g)
|
|
|
|
return gui.handleBranchSelect(g, v)
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleDeleteBranch(g *gocui.Gui, v *gocui.View) error {
|
2018-08-21 11:32:17 +02:00
|
|
|
return gui.deleteBranch(g, v, false)
|
2018-08-15 15:02:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleForceDeleteBranch(g *gocui.Gui, v *gocui.View) error {
|
2018-08-21 11:32:17 +02:00
|
|
|
return gui.deleteBranch(g, v, true)
|
2018-08-15 15:02:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error {
|
2018-12-06 13:18:17 +02:00
|
|
|
selectedBranch := gui.getSelectedBranch()
|
|
|
|
if selectedBranch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2018-08-14 11:05:26 +02:00
|
|
|
checkedOutBranch := gui.State.Branches[0]
|
|
|
|
if checkedOutBranch.Name == selectedBranch.Name {
|
2018-08-16 07:16:32 +02:00
|
|
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantDeleteCheckOutBranch"))
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
2018-10-06 13:34:33 +02:00
|
|
|
return gui.deleteNamedBranch(g, v, selectedBranch, force)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *commands.Branch, force bool) error {
|
2018-08-15 15:02:01 +02:00
|
|
|
title := gui.Tr.SLocalize("DeleteBranch")
|
2018-12-06 09:26:05 +02:00
|
|
|
var messageID string
|
2018-08-21 12:06:42 +02:00
|
|
|
if force {
|
2018-12-06 09:26:05 +02:00
|
|
|
messageID = "ForceDeleteBranchMessage"
|
2018-08-21 12:06:42 +02:00
|
|
|
} else {
|
2018-12-06 09:26:05 +02:00
|
|
|
messageID = "DeleteBranchMessage"
|
2018-08-21 12:06:42 +02:00
|
|
|
}
|
2018-08-14 22:29:17 +02:00
|
|
|
message := gui.Tr.TemplateLocalize(
|
2018-12-06 09:26:05 +02:00
|
|
|
messageID,
|
2018-08-16 11:31:03 +02:00
|
|
|
Teml{
|
2018-08-14 22:29:17 +02:00
|
|
|
"selectedBranchName": selectedBranch.Name,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error {
|
2018-08-15 15:02:01 +02:00
|
|
|
if err := gui.GitCommand.DeleteBranch(selectedBranch.Name, force); err != nil {
|
2018-10-06 13:34:33 +02:00
|
|
|
errMessage := err.Error()
|
|
|
|
if !force && strings.Contains(errMessage, "is not fully merged") {
|
|
|
|
return gui.deleteNamedBranch(g, v, selectedBranch, true)
|
|
|
|
}
|
2018-12-06 09:26:05 +02:00
|
|
|
return gui.createErrorPanel(g, errMessage)
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
|
|
|
return gui.refreshSidePanels(g)
|
|
|
|
}, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
2019-02-16 12:01:17 +02:00
|
|
|
checkedOutBranch := gui.State.Branches[0].Name
|
|
|
|
selectedBranch := gui.getSelectedBranch().Name
|
|
|
|
if checkedOutBranch == selectedBranch {
|
2018-08-16 07:16:32 +02:00
|
|
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
2019-02-16 12:01:17 +02:00
|
|
|
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"))
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
2019-02-16 12:01:17 +02:00
|
|
|
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)
|
2018-08-14 11:05:26 +02:00
|
|
|
}
|
2018-12-07 09:52:31 +02:00
|
|
|
|
|
|
|
func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
branch := gui.getSelectedBranch()
|
|
|
|
if branch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if branch.Pushables == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if branch.Pushables == "?" {
|
2019-02-16 12:01:17 +02:00
|
|
|
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("FwdNoUpstream"))
|
2018-12-07 09:52:31 +02:00
|
|
|
}
|
|
|
|
if branch.Pushables != "0" {
|
2019-02-16 12:01:17 +02:00
|
|
|
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("FwdCommitsToPush"))
|
2018-12-07 09:52:31 +02:00
|
|
|
}
|
|
|
|
upstream := "origin" // hardcoding for now
|
|
|
|
message := gui.Tr.TemplateLocalize(
|
|
|
|
"Fetching",
|
|
|
|
Teml{
|
|
|
|
"from": fmt.Sprintf("%s/%s", upstream, branch.Name),
|
|
|
|
"to": branch.Name,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
go func() {
|
2019-02-16 03:03:22 +02:00
|
|
|
_ = gui.createLoaderPanel(gui.g, v, message)
|
2018-12-07 09:52:31 +02:00
|
|
|
if err := gui.GitCommand.FastForward(branch.Name); err != nil {
|
|
|
|
_ = gui.createErrorPanel(gui.g, err.Error())
|
|
|
|
} else {
|
|
|
|
_ = gui.closeConfirmationPrompt(gui.g)
|
|
|
|
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|