1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-10-30 23:57:43 +02:00

Show divergence from base branch in branches list

This commit is contained in:
Stefan Haller
2024-04-30 12:34:05 +02:00
parent 5b613f5bc7
commit 373b1970ca
11 changed files with 281 additions and 51 deletions

View File

@@ -130,7 +130,7 @@ func (self *RefreshHelper) Refresh(options types.RefreshOptions) error {
if self.c.AppState.LocalBranchSortOrder == "recency" {
refresh("reflog and branches", func() { self.refreshReflogAndBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex) })
} else {
refresh("branches", func() { self.refreshBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex) })
refresh("branches", func() { self.refreshBranches(includeWorktreesWithBranches, options.KeepBranchSelectionIndex, true) })
refresh("reflog", func() { _ = self.refreshReflogCommits() })
}
} else if scopeSet.Includes(types.REBASE_COMMITS) {
@@ -256,7 +256,7 @@ func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
case types.INITIAL:
self.c.OnWorker(func(_ gocui.Task) error {
_ = self.refreshReflogCommits()
self.refreshBranches(false, true)
self.refreshBranches(false, true, true)
self.c.State().GetRepoState().SetStartupStage(types.COMPLETE)
return nil
})
@@ -267,9 +267,11 @@ func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
}
func (self *RefreshHelper) refreshReflogAndBranches(refreshWorktrees bool, keepBranchSelectionIndex bool) {
loadBehindCounts := self.c.State().GetRepoState().GetStartupStage() == types.COMPLETE
self.refreshReflogCommitsConsideringStartup()
self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex)
self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, loadBehindCounts)
}
func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
@@ -438,7 +440,7 @@ func (self *RefreshHelper) refreshStateSubmoduleConfigs() error {
// self.refreshStatus is called at the end of this because that's when we can
// be sure there is a State.Model.Branches array to pick the current branch from
func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSelectionIndex bool) {
func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSelectionIndex bool, loadBehindCounts bool) {
self.c.Mutexes().RefreshingBranchesMutex.Lock()
defer self.c.Mutexes().RefreshingBranchesMutex.Unlock()
@@ -457,7 +459,25 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
}
}
branches, err := self.c.Git().Loaders.BranchLoader.Load(reflogCommits)
branches, err := self.c.Git().Loaders.BranchLoader.Load(
reflogCommits,
self.c.Model().MainBranches,
self.c.Model().Branches,
loadBehindCounts,
func(f func() error) {
self.c.OnWorker(func(_ gocui.Task) error {
return f()
})
},
func() {
self.c.OnUIThread(func() error {
if err := self.c.Contexts().Branches.HandleRender(); err != nil {
self.c.Log.Error(err)
}
self.refreshStatus()
return nil
})
})
if err != nil {
self.c.Log.Error(err)
}

View File

@@ -155,32 +155,38 @@ func BranchStatus(
return style.FgCyan.Sprintf("%s %s", itemOperationStr, utils.Loader(now, userConfig.Gui.Spinner))
}
if !branch.IsTrackingRemote() {
return ""
result := ""
if branch.IsTrackingRemote() {
if branch.UpstreamGone {
result = style.FgRed.Sprint(tr.UpstreamGone)
} else if branch.MatchesUpstream() {
result = style.FgGreen.Sprint("✓")
} else if branch.RemoteBranchNotStoredLocally() {
result = style.FgMagenta.Sprint("?")
} else if branch.IsBehindForPull() && branch.IsAheadForPull() {
result = style.FgYellow.Sprintf("↓%s↑%s", branch.BehindForPull, branch.AheadForPull)
} else if branch.IsBehindForPull() {
result = style.FgYellow.Sprintf("↓%s", branch.BehindForPull)
} else if branch.IsAheadForPull() {
result = style.FgYellow.Sprintf("↑%s", branch.AheadForPull)
}
}
if branch.UpstreamGone {
return style.FgRed.Sprint(tr.UpstreamGone)
if userConfig.Gui.ShowDivergenceFromBaseBranch != "none" {
behind := branch.BehindBaseBranch.Load()
if behind != 0 {
if result != "" {
result += " "
}
if userConfig.Gui.ShowDivergenceFromBaseBranch == "arrowAndNumber" {
result += style.FgCyan.Sprintf("↓%d", behind)
} else {
result += style.FgCyan.Sprintf("↓")
}
}
}
if branch.MatchesUpstream() {
return style.FgGreen.Sprint("✓")
}
if branch.RemoteBranchNotStoredLocally() {
return style.FgMagenta.Sprint("?")
}
if branch.IsBehindForPull() && branch.IsAheadForPull() {
return style.FgYellow.Sprintf("↓%s↑%s", branch.BehindForPull, branch.AheadForPull)
}
if branch.IsBehindForPull() {
return style.FgYellow.Sprintf("↓%s", branch.BehindForPull)
}
if branch.IsAheadForPull() {
return style.FgYellow.Sprintf("↑%s", branch.AheadForPull)
}
return ""
return result
}
func SetCustomBranches(customBranchColors map[string]string) {

View File

@@ -2,6 +2,7 @@ package presentation
import (
"fmt"
"sync/atomic"
"testing"
"time"
@@ -15,6 +16,11 @@ import (
"github.com/xo/terminfo"
)
func makeAtomic(v int32) (result atomic.Int32) {
result.Store(v)
return //nolint: nakedret
}
func Test_getBranchDisplayStrings(t *testing.T) {
scenarios := []struct {
branch *models.Branch
@@ -23,6 +29,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth int
useIcons bool
checkedOutByWorktree bool
showDivergenceCfg string
expected []string
}{
// First some tests for when the view is wide enough so that everything fits:
@@ -33,6 +40,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_name"},
},
{
@@ -42,6 +50,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: true,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_name (worktree)"},
},
{
@@ -51,6 +60,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: true,
checkedOutByWorktree: true,
showDivergenceCfg: "none",
expected: []string{"1m", "󰘬", "branch_name 󰌹"},
},
{
@@ -66,6 +76,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_name ✓"},
},
{
@@ -81,8 +92,57 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: true,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_name (worktree) ↓5↑3"},
},
{
branch: &models.Branch{
Name: "branch_name",
Recency: "1m",
BehindBaseBranch: makeAtomic(2),
},
itemOperation: types.ItemOperationNone,
fullDescription: false,
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "onlyArrow",
expected: []string{"1m", "branch_name ↓"},
},
{
branch: &models.Branch{
Name: "branch_name",
Recency: "1m",
UpstreamRemote: "origin",
AheadForPull: "0",
BehindForPull: "0",
BehindBaseBranch: makeAtomic(2),
},
itemOperation: types.ItemOperationNone,
fullDescription: false,
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "arrowAndNumber",
expected: []string{"1m", "branch_name ✓ ↓2"},
},
{
branch: &models.Branch{
Name: "branch_name",
Recency: "1m",
UpstreamRemote: "origin",
AheadForPull: "3",
BehindForPull: "5",
BehindBaseBranch: makeAtomic(2),
},
itemOperation: types.ItemOperationNone,
fullDescription: false,
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "arrowAndNumber",
expected: []string{"1m", "branch_name ↓5↑3 ↓2"},
},
{
branch: &models.Branch{Name: "branch_name", Recency: "1m"},
itemOperation: types.ItemOperationPushing,
@@ -90,6 +150,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_name Pushing |"},
},
{
@@ -108,6 +169,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 100,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "12345678", "branch_name ✓", "origin branch_name", "commit title"},
},
@@ -119,6 +181,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 14,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_na…"},
},
{
@@ -128,6 +191,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 14,
useIcons: false,
checkedOutByWorktree: true,
showDivergenceCfg: "none",
expected: []string{"1m", "bra… (worktree)"},
},
{
@@ -137,6 +201,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 14,
useIcons: true,
checkedOutByWorktree: true,
showDivergenceCfg: "none",
expected: []string{"1m", "󰘬", "branc… 󰌹"},
},
{
@@ -152,6 +217,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 14,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_… ✓"},
},
{
@@ -167,6 +233,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 30,
useIcons: false,
checkedOutByWorktree: true,
showDivergenceCfg: "none",
expected: []string{"1m", "branch_na… (worktree) ↓5↑3"},
},
{
@@ -176,6 +243,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 20,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "branc… Pushing |"},
},
{
@@ -185,6 +253,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: -1,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "abc Pushing |"},
},
{
@@ -194,6 +263,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: -1,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "ab Pushing |"},
},
{
@@ -203,6 +273,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: -1,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "a Pushing |"},
},
{
@@ -221,6 +292,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
viewWidth: 20,
useIcons: false,
checkedOutByWorktree: false,
showDivergenceCfg: "none",
expected: []string{"1m", "12345678", "bran… ✓", "origin branch_name", "commit title"},
},
}
@@ -232,6 +304,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
for i, s := range scenarios {
icons.SetNerdFontsVersion(lo.Ternary(s.useIcons, "3", ""))
c.UserConfig.Gui.ShowDivergenceFromBaseBranch = s.showDivergenceCfg
worktrees := []*models.Worktree{}
if s.checkedOutByWorktree {