diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index cd1a9a69b..54a9301e2 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -338,18 +338,38 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) onBranchesTabClick(tabIndex int) error { contexts := []string{"local-branches", "remotes", "tabs"} branchesView := gui.getBranchesView() - branchesView.Context = contexts[tabIndex] branchesView.TabIndex = tabIndex - switch contexts[tabIndex] { + return gui.switchBranchesPanelContext(contexts[tabIndex]) +} + +// TODO: make this switch tabs as well if necessary +func (gui *Gui) switchBranchesPanelContext(context string) error { + branchesView := gui.getBranchesView() + branchesView.Context = context + + switch context { case "local-branches": if err := gui.renderListPanel(branchesView, gui.State.Branches); err != nil { return err } + if err := gui.handleBranchSelect(gui.g, gui.getBranchesView()); err != nil { + return err + } case "remotes": if err := gui.renderListPanel(branchesView, gui.State.Remotes); err != nil { return err } + if err := gui.handleRemoteSelect(gui.g, gui.getBranchesView()); err != nil { + return err + } + case "remote-branches": + if err := gui.renderListPanel(branchesView, gui.State.RemoteBranches); err != nil { + return err + } + if err := gui.handleRemoteBranchSelect(gui.g, gui.getBranchesView()); err != nil { + return err + } } return nil diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go index efd3c5fae..1dee4a3cd 100644 --- a/pkg/gui/commit_files_panel.go +++ b/pkg/gui/commit_files_panel.go @@ -51,11 +51,7 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error { - commitsView, err := g.View("commits") - if err != nil { - return err - } - return gui.switchFocus(g, v, commitsView) + return gui.switchFocus(g, v, gui.getCommitsView()) } func (gui *Gui) handleCheckoutCommitFile(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index e8056f858..f4a343626 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -116,6 +116,10 @@ type remotePanelState struct { SelectedLine int } +type remoteBranchesState struct { + SelectedLine int +} + type commitPanelState struct { SelectedLine int SpecificDiffMode bool @@ -140,16 +144,17 @@ type statusPanelState struct { } type panelStates struct { - Files *filePanelState - Branches *branchPanelState - Remotes *remotePanelState - Commits *commitPanelState - Stash *stashPanelState - Menu *menuPanelState - LineByLine *lineByLinePanelState - Merging *mergingPanelState - CommitFiles *commitFilesPanelState - Status *statusPanelState + Files *filePanelState + Branches *branchPanelState + Remotes *remotePanelState + RemoteBranches *remoteBranchesState + Commits *commitPanelState + Stash *stashPanelState + Menu *menuPanelState + LineByLine *lineByLinePanelState + Merging *mergingPanelState + CommitFiles *commitFilesPanelState + Status *statusPanelState } type guiState struct { @@ -160,7 +165,8 @@ type guiState struct { CommitFiles []*commands.CommitFile DiffEntries []*commands.Commit Remotes []*commands.Remote - MenuItemCount int // can't store the actual list because it's of interface{} type + RemoteBranches []*commands.Branch // using Branch for now because they're basically the same + MenuItemCount int // can't store the actual list because it's of interface{} type PreviousView string Platform commands.Platform Updating bool @@ -188,13 +194,14 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma DiffEntries: make([]*commands.Commit, 0), Platform: *oSCommand.Platform, Panels: &panelStates{ - Files: &filePanelState{SelectedLine: -1}, - Branches: &branchPanelState{SelectedLine: 0}, - Remotes: &remotePanelState{SelectedLine: 0}, - Commits: &commitPanelState{SelectedLine: -1}, - CommitFiles: &commitFilesPanelState{SelectedLine: -1}, - Stash: &stashPanelState{SelectedLine: -1}, - Menu: &menuPanelState{SelectedLine: 0}, + Files: &filePanelState{SelectedLine: -1}, + Branches: &branchPanelState{SelectedLine: 0}, + Remotes: &remotePanelState{SelectedLine: 0}, + RemoteBranches: &remoteBranchesState{SelectedLine: -1}, + Commits: &commitPanelState{SelectedLine: -1}, + CommitFiles: &commitFilesPanelState{SelectedLine: -1}, + Stash: &stashPanelState{SelectedLine: -1}, + Menu: &menuPanelState{SelectedLine: 0}, Merging: &mergingPanelState{ ConflictIndex: 0, ConflictTop: true, @@ -606,6 +613,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { {view: filesView, context: "", selectedLine: gui.State.Panels.Files.SelectedLine, lineCount: len(gui.State.Files)}, {view: branchesView, context: "local-branches", selectedLine: gui.State.Panels.Branches.SelectedLine, lineCount: len(gui.State.Branches)}, {view: branchesView, context: "remotes", selectedLine: gui.State.Panels.Remotes.SelectedLine, lineCount: len(gui.State.Remotes)}, + {view: branchesView, context: "remote-branches", selectedLine: gui.State.Panels.RemoteBranches.SelectedLine, lineCount: len(gui.State.Remotes)}, {view: commitsView, context: "", selectedLine: gui.State.Panels.Commits.SelectedLine, lineCount: len(gui.State.Commits)}, {view: stashView, context: "", selectedLine: gui.State.Panels.Stash.SelectedLine, lineCount: len(gui.State.StashEntries)}, } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 3b469d81e..a67894999 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -414,6 +414,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handlePrevBranchesTab, }, + { + ViewName: "branches", + Contexts: []string{"remote-branches"}, + Key: gocui.KeyEsc, + Modifier: gocui.ModNone, + Handler: gui.handleRemoteBranchesEscape, + Description: gui.Tr.SLocalize("ReturnToRemotesList"), + }, { ViewName: "commits", Key: 's', @@ -1026,6 +1034,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleRemotesClick, }, + { + ViewName: "branches", + Contexts: []string{"remotes"}, + Key: gocui.KeyEnter, + Modifier: gocui.ModNone, + Handler: gui.handleRemoteEnter, + }, { ViewName: "commits", Key: gocui.MouseLeft, diff --git a/pkg/gui/list_view.go b/pkg/gui/list_view.go index 98dd9414e..bdfa33f1c 100644 --- a/pkg/gui/list_view.go +++ b/pkg/gui/list_view.go @@ -75,6 +75,15 @@ func (gui *Gui) getListViews() []*listView { gui: gui, rendersToMainView: true, }, + { + viewName: "branches", + context: "remote-branches", + getItemsLength: func() int { return len(gui.State.RemoteBranches) }, + getSelectedLine: func() *int { return &gui.State.Panels.RemoteBranches.SelectedLine }, + handleItemSelect: gui.handleRemoteBranchSelect, + gui: gui, + rendersToMainView: true, + }, { viewName: "commits", getItemsLength: func() int { return len(gui.State.Commits) }, diff --git a/pkg/gui/remote_branches_panel.go b/pkg/gui/remote_branches_panel.go new file mode 100644 index 000000000..93978bc3c --- /dev/null +++ b/pkg/gui/remote_branches_panel.go @@ -0,0 +1,69 @@ +package gui + +import ( + "fmt" + "strings" + + "github.com/fatih/color" + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/commands" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +// list panel functions + +func (gui *Gui) getSelectedRemoteBranch() *commands.Branch { + selectedLine := gui.State.Panels.RemoteBranches.SelectedLine + if selectedLine == -1 || len(gui.State.RemoteBranches) == 0 { + return nil + } + + return gui.State.RemoteBranches[selectedLine] +} + +func (gui *Gui) handleRemoteBranchesClick(g *gocui.Gui, v *gocui.View) error { + itemCount := len(gui.State.RemoteBranches) + handleSelect := gui.handleRemoteBranchSelect + selectedLine := &gui.State.Panels.RemoteBranches.SelectedLine + + return gui.handleClick(v, itemCount, selectedLine, handleSelect) +} + +func (gui *Gui) handleRemoteBranchSelect(g *gocui.Gui, v *gocui.View) error { + if gui.popupPanelFocused() { + return nil + } + + gui.State.SplitMainPanel = false + + if _, err := gui.g.SetCurrentView(v.Name()); err != nil { + return err + } + + gui.getMainView().Title = "Remote Branch" + + remote := gui.getSelectedRemote() + remoteBranch := gui.getSelectedRemoteBranch() + if remoteBranch == nil { + return gui.renderString(g, "main", "No branches for this remote") + } + + gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, gui.State.MenuItemCount, v) + if err := gui.focusPoint(0, gui.State.Panels.RemoteBranches.SelectedLine, len(gui.State.RemoteBranches), v); err != nil { + return err + } + + go func() { + graph, err := gui.GitCommand.GetBranchGraph(fmt.Sprintf("%s/%s", remote.Name, remoteBranch.Name)) + if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") { + graph = gui.Tr.SLocalize("NoTrackingThisBranch") + } + _ = gui.renderString(g, "main", fmt.Sprintf("%s/%s\n\n%s", utils.ColoredString(remote.Name, color.FgRed), utils.ColoredString(remoteBranch.Name, color.FgGreen), graph)) + }() + + return nil +} + +func (gui *Gui) handleRemoteBranchesEscape(g *gocui.Gui, v *gocui.View) error { + return gui.switchBranchesPanelContext("remotes") +} diff --git a/pkg/gui/remotes_panel.go b/pkg/gui/remotes_panel.go index d40515716..38ec8b44b 100644 --- a/pkg/gui/remotes_panel.go +++ b/pkg/gui/remotes_panel.go @@ -68,3 +68,18 @@ func (gui *Gui) refreshRemotes() error { return nil } + +func (gui *Gui) handleRemoteEnter(g *gocui.Gui, v *gocui.View) error { + // naive implementation: get the branches and render them to the list, change the context + remote := gui.getSelectedRemote() + + gui.State.RemoteBranches = remote.Branches + + newSelectedLine := 0 + if len(remote.Branches) == 0 { + newSelectedLine = -1 + } + gui.State.Panels.RemoteBranches.SelectedLine = newSelectedLine + + return gui.switchBranchesPanelContext("remote-branches") +} diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 076567386..8c72252cb 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -110,6 +110,8 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { return gui.handleBranchSelect(g, v) case "remotes": return gui.handleRemoteSelect(g, v) + case "remote-branches": + return gui.handleRemoteBranchSelect(g, v) default: return errors.New("unknown branches panel context: " + branchesView.Context) } diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index f11ce7342..ca76f0a5a 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -760,6 +760,9 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "EnterUpstream", Other: `Enter upstream as ' '`, + }, &i18n.Message{ + ID: "ReturnToRemotesList", + Other: `return to remotes list`, }, ) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index ee53cc1b9..62c762fdd 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -846,6 +846,9 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "notTrackingRemote", Other: "(not tracking any remote)", + }, &i18n.Message{ + ID: "ReturnToRemotesList", + Other: `return to remotes list`, }, ) } diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 3bd6e93f0..bd9d4bc10 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -743,6 +743,9 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "EnterUpstream", Other: `Enter upstream as ' '`, + }, &i18n.Message{ + ID: "ReturnToRemotesList", + Other: `return to remotes list`, }, ) }