mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-19 21:28:28 +02:00
fetching branches without checking out
This commit is contained in:
parent
ca3afa2a39
commit
ff856b7630
@ -52,6 +52,9 @@ func newLogger(config config.AppConfigurer) *logrus.Entry {
|
||||
} else {
|
||||
log = newProductionLogger(config)
|
||||
}
|
||||
|
||||
// highly recommended: tail -f development.log | humanlog
|
||||
// https://github.com/aybabtme/humanlog
|
||||
log.Formatter = &logrus.JSONFormatter{}
|
||||
|
||||
if config.GetUserConfig().GetString("reporting") == "on" {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@ -12,11 +13,19 @@ import (
|
||||
type Branch struct {
|
||||
Name string
|
||||
Recency string
|
||||
Pushables string
|
||||
Pullables string
|
||||
Selected bool
|
||||
}
|
||||
|
||||
// GetDisplayStrings returns the dispaly string of branch
|
||||
func (b *Branch) GetDisplayStrings() []string {
|
||||
return []string{b.Recency, utils.ColoredString(b.Name, b.GetColor())}
|
||||
displayName := utils.ColoredString(b.Name, b.GetColor())
|
||||
if b.Selected && b.Pushables != "" && b.Pullables != "" {
|
||||
displayName = fmt.Sprintf("↑%s↓%s %s", b.Pushables, b.Pullables, displayName)
|
||||
}
|
||||
|
||||
return []string{b.Recency, displayName}
|
||||
}
|
||||
|
||||
// GetColor branch color
|
||||
|
@ -130,17 +130,6 @@ func (c *GitCommand) GetStashEntryDiff(index int) (string, error) {
|
||||
|
||||
// GetStatusFiles git status files
|
||||
func (c *GitCommand) GetStatusFiles() []*File {
|
||||
|
||||
// files := []*File{}
|
||||
// for i := 0; i < 100; i++ {
|
||||
// files = append(files, &File{
|
||||
// Name: strconv.Itoa(i),
|
||||
// DisplayString: strconv.Itoa(i),
|
||||
// Type: "file",
|
||||
// })
|
||||
// }
|
||||
// return files
|
||||
|
||||
statusOutput, _ := c.GitStatus()
|
||||
statusStrings := utils.SplitLines(statusOutput)
|
||||
files := []*File{}
|
||||
@ -165,7 +154,6 @@ func (c *GitCommand) GetStatusFiles() []*File {
|
||||
}
|
||||
files = append(files, file)
|
||||
}
|
||||
c.Log.Info(files) // TODO: use a dumper-esque log here
|
||||
return files
|
||||
}
|
||||
|
||||
@ -228,14 +216,24 @@ func (c *GitCommand) ResetAndClean() error {
|
||||
return c.OSCommand.RunCommand("git clean -fd")
|
||||
}
|
||||
|
||||
// UpstreamDifferenceCount checks how many pushables/pullables there are for the
|
||||
func (c *GitCommand) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
||||
return c.GetCommitDifferences("HEAD", "@{u}")
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetBranchUpstreamDifferenceCount(branchName string) (string, string) {
|
||||
upstream := "origin" // hardcoded for now
|
||||
return c.GetCommitDifferences(branchName, fmt.Sprintf("%s/%s", upstream, branchName))
|
||||
}
|
||||
|
||||
// GetCommitDifferences checks how many pushables/pullables there are for the
|
||||
// current branch
|
||||
func (c *GitCommand) UpstreamDifferenceCount() (string, string) {
|
||||
pushableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..HEAD --count")
|
||||
func (c *GitCommand) GetCommitDifferences(from, to string) (string, string) {
|
||||
command := "git rev-list %s..%s --count"
|
||||
pushableCount, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf(command, to, from))
|
||||
if err != nil {
|
||||
return "?", "?"
|
||||
}
|
||||
pullableCount, err := c.OSCommand.RunCommandWithOutput("git rev-list HEAD..@{u} --count")
|
||||
pullableCount, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf(command, from, to))
|
||||
if err != nil {
|
||||
return "?", "?"
|
||||
}
|
||||
@ -618,3 +616,8 @@ func (c *GitCommand) ApplyPatch(patch string) (string, error) {
|
||||
|
||||
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git apply --cached %s", filename))
|
||||
}
|
||||
|
||||
func (c *GitCommand) FastForward(branchName string) error {
|
||||
upstream := "origin" // hardcoding for now
|
||||
return c.OSCommand.RunCommand(fmt.Sprintf("git fetch %s %s:%s", upstream, branchName, branchName))
|
||||
}
|
||||
|
@ -557,8 +557,8 @@ func TestGitCommandMergeStatusFiles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGitCommandUpstreamDifferentCount is a function.
|
||||
func TestGitCommandUpstreamDifferentCount(t *testing.T) {
|
||||
// TestGitCommandGetCommitDifferences is a function.
|
||||
func TestGitCommandGetCommitDifferences(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
@ -610,7 +610,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
gitCmd := newDummyGitCommand()
|
||||
gitCmd.OSCommand.command = s.command
|
||||
s.test(gitCmd.UpstreamDifferenceCount())
|
||||
s.test(gitCmd.GetCommitDifferences("HEAD", "@{u}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/git"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@ -31,6 +30,9 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
||||
}()
|
||||
go func() {
|
||||
graph, err := gui.GitCommand.GetBranchGraph(branch.Name)
|
||||
if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") {
|
||||
@ -41,14 +43,23 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
return gui.renderListPanel(gui.getBranchesView(gui.g), gui.State.Branches)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
v, err := g.View("branches")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
builder, err := git.NewBranchListBuilder(gui.Log, gui.GitCommand)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -56,16 +67,13 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
||||
gui.State.Branches = builder.Build()
|
||||
|
||||
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
||||
|
||||
v.Clear()
|
||||
list, err := utils.RenderList(gui.State.Branches)
|
||||
if err != nil {
|
||||
if err := gui.resetOrigin(gui.getBranchesView(gui.g)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprint(v, list)
|
||||
|
||||
gui.resetOrigin(v)
|
||||
return gui.refreshStatus(g)
|
||||
})
|
||||
return nil
|
||||
@ -74,7 +82,6 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
||||
func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||
panelState := gui.State.Panels.Branches
|
||||
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
|
||||
|
||||
return gui.handleBranchSelect(gui.g, v)
|
||||
}
|
||||
|
||||
@ -99,7 +106,10 @@ func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := gui.createErrorPanel(g, err.Error()); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
gui.State.Panels.Branches.SelectedLine = 0
|
||||
}
|
||||
|
||||
return gui.refreshSidePanels(g)
|
||||
}
|
||||
|
||||
@ -213,3 +223,37 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 == "?" {
|
||||
return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with no upstream")
|
||||
}
|
||||
if branch.Pushables != "0" {
|
||||
return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with commits to push")
|
||||
}
|
||||
upstream := "origin" // hardcoding for now
|
||||
message := gui.Tr.TemplateLocalize(
|
||||
"Fetching",
|
||||
Teml{
|
||||
"from": fmt.Sprintf("%s/%s", upstream, branch.Name),
|
||||
"to": branch.Name,
|
||||
},
|
||||
)
|
||||
go func() {
|
||||
_ = gui.createMessagePanel(gui.g, v, "", message)
|
||||
if err := gui.GitCommand.FastForward(branch.Name); err != nil {
|
||||
_ = gui.createErrorPanel(gui.g, err.Error())
|
||||
} else {
|
||||
_ = gui.closeConfirmationPrompt(gui.g)
|
||||
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.Errors.ErrSubProcess
|
||||
}
|
||||
v.Clear()
|
||||
v.SetCursor(0, 0)
|
||||
v.SetOrigin(0, 0)
|
||||
g.SetViewOnBottom("commitMessage")
|
||||
gui.switchFocus(g, v, gui.getFilesView(g))
|
||||
_ = v.SetCursor(0, 0)
|
||||
_ = v.SetOrigin(0, 0)
|
||||
_, _ = g.SetViewOnBottom("commitMessage")
|
||||
_ = gui.switchFocus(g, v, gui.getFilesView(g))
|
||||
return gui.refreshSidePanels(g)
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,8 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
|
||||
panic(err)
|
||||
}
|
||||
gui.resetOrigin(commitView)
|
||||
return gui.handleCommitSelect(g, nil)
|
||||
gui.State.Panels.Commits.SelectedLine = 0
|
||||
return gui.handleCommitSelect(g, commitView)
|
||||
}, nil)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.Vie
|
||||
func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error {
|
||||
view, err := g.View("confirmation")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil // if it's already been closed we can just return
|
||||
}
|
||||
if err := gui.returnFocus(g, view); err != nil {
|
||||
panic(err)
|
||||
@ -77,11 +77,10 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
|
||||
confirmationView.Wrap = true
|
||||
confirmationView.FgColor = gocui.ColorWhite
|
||||
}
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
confirmationView.Clear()
|
||||
|
||||
if err := gui.switchFocus(gui.g, currentView, confirmationView); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gui.switchFocus(gui.g, currentView, confirmationView)
|
||||
})
|
||||
return confirmationView, nil
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
||||
fmt.Fprint(filesView, list)
|
||||
|
||||
if filesView == g.CurrentView() {
|
||||
gui.handleFileSelect(g, filesView)
|
||||
return gui.handleFileSelect(g, filesView)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -411,7 +411,7 @@ func (gui *Gui) pushWithForceFlag(currentView *gocui.View, force bool) error {
|
||||
|
||||
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
// if we have pullables we'll ask if the user wants to force push
|
||||
_, pullables := gui.GitCommand.UpstreamDifferenceCount()
|
||||
_, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
||||
if pullables == "?" || pullables == "0" {
|
||||
return gui.pushWithForceFlag(v, false)
|
||||
}
|
||||
|
@ -72,6 +72,9 @@ type Gui struct {
|
||||
statusManager *statusManager
|
||||
}
|
||||
|
||||
// for now the staging panel state, unlike the other panel states, is going to be
|
||||
// non-mutative, so that we don't accidentally end up
|
||||
// with mismatches of data. We might change this in the future
|
||||
type stagingPanelState struct {
|
||||
SelectedLine int
|
||||
StageableLines []int
|
||||
@ -233,7 +236,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
g.SetViewOnBottom("limit")
|
||||
_, _ = g.SetViewOnBottom("limit")
|
||||
}
|
||||
|
||||
g.DeleteView("limit")
|
||||
@ -364,14 +367,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.g.SetCurrentView(filesView.Name())
|
||||
|
||||
gui.refreshSidePanels(gui.g)
|
||||
if gui.g.CurrentView().Name() != "menu" {
|
||||
if err := gui.renderGlobalOptions(g); err != nil {
|
||||
if _, err := gui.g.SetCurrentView(filesView.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.refreshSidePanels(gui.g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -394,8 +397,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: comment-out
|
||||
gui.Log.Info(utils.AsJson(gui.State))
|
||||
// here is a good place log some stuff
|
||||
// if you download humanlog and do tail -f development.log | humanlog
|
||||
// this will let you see these branches as prettified json
|
||||
// gui.Log.Info(utils.AsJson(gui.State.Branches[0:4]))
|
||||
|
||||
return gui.resizeCurrentPopupPanel(g)
|
||||
}
|
||||
@ -435,8 +440,8 @@ func (gui *Gui) renderAppStatus(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderGlobalOptions(g *gocui.Gui) error {
|
||||
return gui.renderOptionsMap(g, map[string]string{
|
||||
func (gui *Gui) renderGlobalOptions() error {
|
||||
return gui.renderOptionsMap(map[string]string{
|
||||
"PgUp/PgDn": gui.Tr.SLocalize("scroll"),
|
||||
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
||||
"esc/q": gui.Tr.SLocalize("close"),
|
||||
@ -467,7 +472,7 @@ func (gui *Gui) Run() error {
|
||||
}
|
||||
|
||||
gui.goEvery(g, time.Second*60, gui.fetch)
|
||||
// gui.goEvery(g, time.Second*2, gui.refreshFiles) // TODO: comment back in
|
||||
gui.goEvery(g, time.Second*2, gui.refreshFiles)
|
||||
gui.goEvery(g, time.Millisecond*50, gui.updateLoader)
|
||||
gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus)
|
||||
|
||||
|
@ -219,12 +219,7 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Handler: gui.handleSwitchToStagingPanel,
|
||||
Description: gui.Tr.SLocalize("StageLines"),
|
||||
KeyReadable: "enter",
|
||||
},
|
||||
{ViewName: "files", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleFilesPrevLine},
|
||||
{ViewName: "files", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleFilesPrevLine},
|
||||
{ViewName: "files", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleFilesNextLine},
|
||||
{ViewName: "files", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleFilesNextLine},
|
||||
{
|
||||
}, {
|
||||
ViewName: "main",
|
||||
Key: gocui.KeyEsc,
|
||||
Modifier: gocui.ModNone,
|
||||
@ -327,12 +322,13 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleMerge,
|
||||
Description: gui.Tr.SLocalize("mergeIntoCurrentBranch"),
|
||||
},
|
||||
{ViewName: "branches", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleBranchesPrevLine},
|
||||
{ViewName: "branches", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleBranchesPrevLine},
|
||||
{ViewName: "branches", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleBranchesNextLine},
|
||||
{ViewName: "branches", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleBranchesNextLine},
|
||||
{
|
||||
}, {
|
||||
ViewName: "branches",
|
||||
Key: 'f',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleFastForward,
|
||||
Description: gui.Tr.SLocalize("FastForward"),
|
||||
}, {
|
||||
ViewName: "commits",
|
||||
Key: 's',
|
||||
Modifier: gocui.ModNone,
|
||||
@ -362,12 +358,7 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitFixup,
|
||||
Description: gui.Tr.SLocalize("fixupCommit"),
|
||||
},
|
||||
{ViewName: "commits", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleCommitsPrevLine},
|
||||
{ViewName: "commits", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleCommitsPrevLine},
|
||||
{ViewName: "commits", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleCommitsNextLine},
|
||||
{ViewName: "commits", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleCommitsNextLine},
|
||||
{
|
||||
}, {
|
||||
ViewName: "stash",
|
||||
Key: gocui.KeySpace,
|
||||
Modifier: gocui.ModNone,
|
||||
@ -386,12 +377,7 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleStashDrop,
|
||||
Description: gui.Tr.SLocalize("drop"),
|
||||
},
|
||||
{ViewName: "stash", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleStashPrevLine},
|
||||
{ViewName: "stash", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleStashPrevLine},
|
||||
{ViewName: "stash", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleStashNextLine},
|
||||
{ViewName: "stash", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleStashNextLine},
|
||||
{
|
||||
}, {
|
||||
ViewName: "commitMessage",
|
||||
Key: gocui.KeyEnter,
|
||||
Modifier: gocui.ModNone,
|
||||
@ -411,11 +397,7 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Key: 'q',
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleMenuClose,
|
||||
},
|
||||
{ViewName: "menu", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleMenuPrevLine},
|
||||
{ViewName: "menu", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleMenuPrevLine},
|
||||
{ViewName: "menu", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleMenuNextLine},
|
||||
{ViewName: "menu", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleMenuNextLine}, {
|
||||
}, {
|
||||
ViewName: "staging",
|
||||
Key: gocui.KeyEsc,
|
||||
Modifier: gocui.ModNone,
|
||||
@ -487,6 +469,26 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
}...)
|
||||
}
|
||||
|
||||
listPanelMap := map[string]struct {
|
||||
prevLine func(*gocui.Gui, *gocui.View) error
|
||||
nextLine func(*gocui.Gui, *gocui.View) error
|
||||
}{
|
||||
"menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine},
|
||||
"files": {prevLine: gui.handleFilesPrevLine, nextLine: gui.handleFilesNextLine},
|
||||
"branches": {prevLine: gui.handleBranchesPrevLine, nextLine: gui.handleBranchesNextLine},
|
||||
"commits": {prevLine: gui.handleCommitsPrevLine, nextLine: gui.handleCommitsNextLine},
|
||||
"stash": {prevLine: gui.handleStashPrevLine, nextLine: gui.handleStashNextLine},
|
||||
}
|
||||
|
||||
for viewName, functions := range listPanelMap {
|
||||
bindings = append(bindings, []*Binding{
|
||||
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: functions.prevLine},
|
||||
{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: functions.prevLine},
|
||||
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: functions.nextLine},
|
||||
{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: functions.nextLine},
|
||||
}...)
|
||||
}
|
||||
|
||||
return bindings
|
||||
}
|
||||
|
||||
|
@ -25,22 +25,18 @@ func (gui *Gui) handleMenuPrevLine(g *gocui.Gui, v *gocui.View) error {
|
||||
panelState := gui.State.Panels.Menu
|
||||
gui.changeSelectedLine(&panelState.SelectedLine, v.LinesHeight(), true)
|
||||
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.handleMenuSelect(g, v)
|
||||
}
|
||||
|
||||
// specific functions
|
||||
|
||||
func (gui *Gui) renderMenuOptions(g *gocui.Gui) error {
|
||||
func (gui *Gui) renderMenuOptions() error {
|
||||
optionsMap := map[string]string{
|
||||
"esc/q": gui.Tr.SLocalize("close"),
|
||||
"↑ ↓": gui.Tr.SLocalize("navigate"),
|
||||
"space": gui.Tr.SLocalize("execute"),
|
||||
}
|
||||
return gui.renderOptionsMap(g, optionsMap)
|
||||
return gui.renderOptionsMap(optionsMap)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
|
||||
@ -68,10 +64,6 @@ func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error
|
||||
fmt.Fprint(menuView, list)
|
||||
gui.State.Panels.Menu.SelectedLine = 0
|
||||
|
||||
if err := gui.renderMenuOptions(gui.g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
|
||||
selectedLine := gui.State.Panels.Menu.SelectedLine
|
||||
return handlePress(selectedLine)
|
||||
|
@ -194,9 +194,6 @@ func (gui *Gui) refreshMergePanel(g *gocui.Gui) error {
|
||||
gui.State.ConflictIndex = len(gui.State.Conflicts) - 1
|
||||
}
|
||||
hasFocus := gui.currentViewName(g) == "main"
|
||||
if hasFocus {
|
||||
gui.renderMergeOptions(g)
|
||||
}
|
||||
content, err := gui.coloredConflictFile(cat, gui.State.Conflicts, gui.State.ConflictIndex, gui.State.ConflictTop, hasFocus)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -233,8 +230,8 @@ func (gui *Gui) switchToMerging(g *gocui.Gui) error {
|
||||
return gui.refreshMergePanel(g)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderMergeOptions(g *gocui.Gui) error {
|
||||
return gui.renderOptionsMap(g, map[string]string{
|
||||
func (gui *Gui) renderMergeOptions() error {
|
||||
return gui.renderOptionsMap(map[string]string{
|
||||
"↑ ↓": gui.Tr.SLocalize("selectHunk"),
|
||||
"← →": gui.Tr.SLocalize("navigateConflicts"),
|
||||
"space": gui.Tr.SLocalize("pickHunk"),
|
||||
|
@ -30,7 +30,7 @@ func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
||||
go func() {
|
||||
// doing this asynchronously cos it can take time
|
||||
diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index)
|
||||
gui.renderString(g, "main", diff)
|
||||
_ = gui.renderString(g, "main", diff)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
||||
// contents end up cleared
|
||||
g.Update(func(*gocui.Gui) error {
|
||||
v.Clear()
|
||||
pushables, pullables := gui.GitCommand.UpstreamDifferenceCount()
|
||||
pushables, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
||||
fmt.Fprint(v, "↑"+pushables+"↓"+pullables)
|
||||
branches := gui.State.Branches
|
||||
if err := gui.updateHasMergeConflictStatus(); err != nil {
|
||||
@ -48,6 +48,8 @@ func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
blue := color.New(color.FgBlue)
|
||||
|
||||
dashboardString := strings.Join(
|
||||
[]string{
|
||||
lazygitTitle(),
|
||||
@ -56,7 +58,7 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
"Config Options: https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md",
|
||||
"Tutorial: https://youtu.be/VDXvbHZYeKY",
|
||||
"Raise an Issue: https://github.com/jesseduffield/lazygit/issues",
|
||||
"Buy Jesse a coffee: https://donorbox.org/lazygit",
|
||||
blue.Sprint("Buy Jesse a coffee: https://donorbox.org/lazygit"), // caffeine ain't free
|
||||
}, "\n\n")
|
||||
|
||||
return gui.renderString(g, "main", dashboardString)
|
||||
|
@ -13,11 +13,16 @@ import (
|
||||
var cyclableViews = []string{"status", "files", "branches", "commits", "stash"}
|
||||
|
||||
func (gui *Gui) refreshSidePanels(g *gocui.Gui) error {
|
||||
gui.refreshBranches(g)
|
||||
gui.refreshFiles(g)
|
||||
gui.refreshCommits(g)
|
||||
gui.refreshStashEntries(g)
|
||||
return nil
|
||||
if err := gui.refreshBranches(g); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshFiles(g); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.refreshCommits(g); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.refreshStashEntries(g)
|
||||
}
|
||||
|
||||
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
|
||||
@ -81,7 +86,7 @@ func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
|
||||
func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
||||
switch v.Name() {
|
||||
case "menu":
|
||||
return nil
|
||||
return gui.handleMenuSelect(g, v)
|
||||
case "status":
|
||||
return gui.handleStatusSelect(g, v)
|
||||
case "files":
|
||||
@ -160,6 +165,10 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
||||
|
||||
g.Cursor = newView.Editable
|
||||
|
||||
if err := gui.renderPanelOptions(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.newLineFocused(g, newView)
|
||||
}
|
||||
|
||||
@ -240,8 +249,8 @@ func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
|
||||
return strings.Join(optionsArray, ", ")
|
||||
}
|
||||
|
||||
func (gui *Gui) renderOptionsMap(g *gocui.Gui, optionsMap map[string]string) error {
|
||||
return gui.renderString(g, "options", gui.optionsMapToString(optionsMap))
|
||||
func (gui *Gui) renderOptionsMap(optionsMap map[string]string) error {
|
||||
return gui.renderString(gui.g, "options", gui.optionsMapToString(optionsMap))
|
||||
}
|
||||
|
||||
// TODO: refactor properly
|
||||
@ -312,22 +321,6 @@ func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// focusLine focuses and selects the given line
|
||||
func (gui *Gui) focusLine(lineNumber int, v *gocui.View) error {
|
||||
_, height := v.Size()
|
||||
overScroll := lineNumber - height + 1
|
||||
if overScroll < 0 {
|
||||
overScroll = 0
|
||||
}
|
||||
if err := v.SetOrigin(0, overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generalFocusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
|
||||
func (gui *Gui) generalFocusLine(lineNumber int, bottomLine int, v *gocui.View) error {
|
||||
_, height := v.Size()
|
||||
@ -367,3 +360,28 @@ func (gui *Gui) refreshSelectedLine(line *int, total int) {
|
||||
*line = total - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
list, err := utils.RenderList(items)
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
v.Clear()
|
||||
fmt.Fprint(v, list)
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderPanelOptions() error {
|
||||
currentView := gui.g.CurrentView()
|
||||
switch currentView.Name() {
|
||||
case "menu":
|
||||
return gui.renderMenuOptions()
|
||||
case "main":
|
||||
return gui.renderMergeOptions()
|
||||
default:
|
||||
return gui.renderGlobalOptions()
|
||||
}
|
||||
}
|
||||
|
@ -435,6 +435,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
||||
}, &i18n.Message{
|
||||
ID: "CantFindHunk",
|
||||
Other: `Could not find hunk`,
|
||||
}, &i18n.Message{
|
||||
ID: "FastForward",
|
||||
Other: `fast-forward this branch from its upstream`,
|
||||
}, &i18n.Message{
|
||||
ID: "Fetching",
|
||||
Other: "fetching and fast-forwarding {{.from}} -> {{.to}} ...",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -517,3 +517,14 @@ func TestPrevIndex(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsJson(t *testing.T) {
|
||||
type myStruct struct {
|
||||
a string
|
||||
}
|
||||
|
||||
output := AsJson(&myStruct{a: "foo"})
|
||||
|
||||
// no idea why this is returning empty hashes but it's works in the app ¯\_(ツ)_/¯
|
||||
assert.EqualValues(t, "{}", output)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user