summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/Config.md4
-rw-r--r--pkg/commands/git_commands/branch_loader.go74
-rw-r--r--pkg/commands/models/branch.go10
-rw-r--r--pkg/config/user_config.go46
-rw-r--r--pkg/config/user_config_validation.go7
-rw-r--r--pkg/gui/controllers/helpers/refresh_helper.go28
-rw-r--r--pkg/gui/presentation/branches.go58
-rw-r--r--pkg/gui/presentation/branches_test.go73
-rw-r--r--pkg/integration/tests/status/show_divergence_from_base_branch.go27
-rw-r--r--pkg/integration/tests/test_list.go1
-rw-r--r--schema/config.json10
11 files changed, 283 insertions, 55 deletions
diff --git a/docs/Config.md b/docs/Config.md
index 5662a4d73..7cc602c0b 100644
--- a/docs/Config.md
+++ b/docs/Config.md
@@ -187,6 +187,10 @@ gui:
# If true, show commit hashes alongside branch names in the branches view.
showBranchCommitHash: false
+ # Whether to show the divergence from the base branch in the branches view.
+ # One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
+ showDivergenceFromBaseBranch: none
+
# Height of the command log view
commandLogSize: 8
diff --git a/pkg/commands/git_commands/branch_loader.go b/pkg/commands/git_commands/branch_loader.go
index 1052626a9..929d5964d 100644
--- a/pkg/commands/git_commands/branch_loader.go
+++ b/pkg/commands/git_commands/branch_loader.go
@@ -5,6 +5,7 @@ import (
"regexp"
"strconv"
"strings"
+ "time"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/go-git/v5/config"
@@ -14,6 +15,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"golang.org/x/exp/slices"
+ "golang.org/x/sync/errgroup"
)
// context:
@@ -63,7 +65,13 @@ func NewBranchLoader(
}
// Load the list of branches for the current repo
-func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch, error) {
+func (self *BranchLoader) Load(reflogCommits []*models.Commit,
+ mainBranches *MainBranches,
+ oldBranches []*models.Branch,
+ loadBehindCounts bool,
+ onWorker func(func() error),
+ renderFunc func(),
+) ([]*models.Branch, error) {
branches := self.obtainBranches(self.version.IsAtLeast(2, 22, 0))
if self.AppState.LocalBranchSortOrder == "recency" {
@@ -122,11 +130,75 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch
branch.UpstreamRemote = match.Remote
branch.UpstreamBranch = match.Merge.Short()
}
+
+ // If the branch already existed, take over its BehindBaseBranch value
+ // to reduce flicker
+ if oldBranch, found := lo.Find(oldBranches, func(b *models.Branch) bool {
+ return b.Name == branch.Name
+ }); found {
+ branch.BehindBaseBranch.Store(oldBranch.BehindBaseBranch.Load())
+ }
+ }
+
+ if loadBehindCounts && self.UserConfig.Gui.ShowDivergenceFromBaseBranch != "none" {
+ onWorker(func() error {
+ return self.GetBehindBaseBranchValuesForAllBranches(branches, mainBranches, renderFunc)
+ })
}
return branches, nil
}
+func (self *BranchLoader) GetBehindBaseBranchValuesForAllBranches(
+ branches []*models.Branch,
+ mainBranches *MainBranches,
+ renderFunc func(),
+) error {
+ mainBranchRefs := mainBranches.Get()
+ if len(mainBranchRefs) == 0 {
+ return nil
+ }
+
+ t := time.Now()
+ errg := errgroup.Group{}
+
+ for _, branch := range branches {
+ errg.Go(func() error {
+ baseBranch, err := self.GetBaseBranch(branch, mainBranches)
+ if err != nil {
+ return err
+ }
+ behind := 0 // prime it in case something below fails
+ if baseBranch != "" {
+ output, err := self.cmd.New(
+ NewGitCmd("rev-list").
+ Arg("--left-right").
+ Arg("--count").
+ Arg(fmt.Sprintf("%s...%s", branch.FullRefName(), baseBranch)).
+ ToArgv(),
+ ).DontLog().RunWithOutput()
+ if err != nil {
+ return err
+ }
+ // The format of the output is "<ahead>\t<behind>"
+ aheadBehindStr := strings.Split(strings.TrimSpace(output), "\t")
+ if len(aheadBehindStr) == 2 {
+ if value, err := strconv.Atoi(aheadBehindStr[1]); err == nil {
+ behind = value
+ }
+ }
+ }
+ branch.BehindBaseBranch.Store(int32(behind))
+ return nil
+ })
+ }
+
+ err := errg.Wait()
+ self.Log.Debugf("time to get behind base branch values for all branches: %s", time.Since(t))
+ renderFunc()
+ return err
+}
+
// Find the base branch for the given branch (i.e. the main branch that the
// given branch was forked off of)
//
diff --git a/pkg/commands/models/branch.go b/pkg/commands/models/branch.go
index 5a34ba5e8..04f869ebd 100644
--- a/pkg/commands/models/branch.go
+++ b/pkg/commands/models/branch.go
@@ -1,6 +1,9 @@
package models
-import "fmt"
+import (
+ "fmt"
+ "sync/atomic"
+)
// Branch : A git branch
// duplicating this for now
@@ -32,6 +35,11 @@ type Branch struct {
Subject string
// commit hash
CommitHash string
+
+ // How far we have fallen behind our base branch. 0 means either not
+ // determined yet, or up to date with base branch. (We don't need to
+ // distinguish the two, as we don't draw anything in both cases.)
+ BehindBaseBranch atomic.Int32
}
func (b *Branch) FullRefName() string {
diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go
index 41d3dfe10..c8895710e 100644
--- a/pkg/config/user_config.go
+++ b/pkg/config/user_config.go
@@ -129,6 +129,9 @@ type GuiConfig struct {
CommitHashLength int `yaml:"commitHashLength" jsonschema:"minimum=0"`
// If true, show commit hashes alongside branch names in the branches view.
ShowBranchCommitHash bool `yaml:"showBranchCommitHash"`
+ // Whether to show the divergence from the base branch in the branches view.
+ // One of: 'none' | 'onlyArrow' | 'arrowAndNumber'
+ ShowDivergenceFromBaseBranch string `yaml:"showDivergenceFromBaseBranch" jsonschema:"enum=none,enum=onlyArrow,enum=arrowAndNumber"`
// Height of the command log view
CommandLogSize int `yaml:"commandLogSize" jsonschema:"minimum=0"`
// Whether to split the main window when viewing file changes.
@@ -673,27 +676,28 @@ func GetDefaultConfig() *UserConfig {
UnstagedChangesColor: []string{"red"},
DefaultFgColor: []string{"default"},
},
- CommitLength: CommitLengthConfig{Show: true},
- SkipNoStagedFilesWarning: false,
- ShowListFooter: true,
- ShowCommandLog: true,
- ShowBottomLine: true,
- ShowPanelJumps: true,
- ShowFileTree: true,
- ShowRandomTip: true,
- ShowIcons: false,
- NerdFontsVersion: "",
- ShowFileIcons: true,
- CommitHashLength: 8,
- ShowBranchCommitHash: false,
- CommandLogSize: 8,
- SplitDiff: "auto",
- SkipRewordInEditorWarning: false,
- WindowSize: "normal",
- Border: "rounded",
- AnimateExplosion: true,
- PortraitMode: "auto",
- FilterMode: "substring",
+ CommitLength: CommitLengthConfig{Show: true},
+ SkipNoStagedFilesWarning: false,
+ ShowListFooter: true,
+ ShowCommandLog: true,
+ ShowBottomLine: true,
+ ShowPanelJumps: true,
+ ShowFileTree: true,
+ ShowRandomTip: true,
+ ShowIcons: false,
+ NerdFontsVersion: "",
+ ShowFileIcons: true,
+ CommitHashLength: 8,
+ ShowBranchCommitHash: false,
+ ShowDivergenceFromBaseBranch: "none",
+ CommandLogSize: 8,
+ SplitDiff: "auto",
+ SkipRewordInEditorWarning: false,
+ WindowSize: "normal",
+ Border: "rounded",
+ AnimateExplosion: true,
+ PortraitMode: "auto",
+ FilterMode: "substring",
Spinner: SpinnerConfig{
Frames: []string{"|", "/", "-", "\\"},
Rate: 50,
diff --git a/pkg/config/user_config_validation.go b/pkg/config/user_config_validation.go
index 945979db9..403119ada 100644
--- a/pkg/config/user_config_validation.go
+++ b/pkg/config/user_config_validation.go
@@ -7,7 +7,12 @@ import (
)
func (config *UserConfig) Validate() error {
- if err := validateEnum("gui.statusPanelView", config.Gui.StatusPanelView, []string{"dashboard", "allBranchesLog"}); err != nil {
+ if err := validateEnum("gui.statusPanelView", config.Gui.StatusPanelView,
+ []string{"dashboard", "allBranchesLog"}); err != nil {
+ return err
+ }
+ if err := validateEnum("gui.showDivergenceFromBaseBranch", config.Gui.ShowDivergenceFromBaseBranch,
+ []string{"none", "onlyArrow", "arrowAndNumber"}); err != nil {
return err
}
return nil
diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go
index 02a26ded9..8b4192272 100644
--- a/pkg/gui/controllers/helpers/refresh_helper.go
+++ b/pkg/gui/controllers/helpers/refresh_helper.go
@@ -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
})
@@ -269,7 +269,7 @@ func (self *RefreshHelper) refreshReflogCommitsConsideringStartup() {
func (self *RefreshHelper) refreshReflogAndBranches(refreshWorktrees bool, keepBranchSelectionIndex bool) {
self.refreshReflogCommitsConsideringStartup()
- self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex)
+ self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, false)
}
func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
@@ -438,7 +438,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 +457,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)
}
diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go
index 0e48a3935..aab51fe61 100644
--- a/pkg/gui/presentation/branches.go
+++ b/pkg/gui/presentation/branches.go
@@ -155,32 +155,38 @@ func BranchStatus(
return style.FgCyan.Sprintf("%s %s", itemOperationStr, utils.Loader(now, userConfig.Gui.Spinner))
}
- if !branch.IsTrackingRemote() {
- return ""
- }
-
- if branch.UpstreamGone {
- return style.FgRed.Sprint(tr.UpstreamGone)
- }
-
- 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 ""
+ 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 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("↓")
+ }
+ }
+ }
+
+ return result
}
func SetCustomBranches(customBranchColors map[string]string) {
diff --git a/pkg/gui/presentation/branches_test.go b/pkg/gui/presentation/branches_test.go
index db4868970..ba79f16ce 100644
--- a/pkg/gui/presentation/branches_test.go
+++ b/pkg/gui/presentation/branches_test.go
@@ -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,15 +92,65 @@ 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,
fullDescription: false,
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 {
diff --git a/pkg/integration/tests/status/show_divergence_from_base_branch.go b/pkg/integration/tests/status/show_divergence_from_base_branch.go
new file mode 100644
index 000000000..53ab0ab2f
--- /dev/null
+++ b/pkg/integration/tests/status/show_divergence_from_base_branch.go
@@ -0,0 +1,27 @@
+package status
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var ShowDivergenceFromBaseBranch = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Show divergence from base branch in the status panel",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {
+ config.UserConfig.Gui.ShowDivergenceFromBaseBranch = "arrowAndNumber"
+ },
+ SetupRepo: func(shell *Shell) {
+ shell.CreateNCommits(2)
+ shell.CloneIntoRemote("origin")
+ shell.NewBranch("feature")
+ shell.HardReset("HEAD^")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.GlobalPress(keys.Universal.NextBlock)
+
+ t.Views().Status().
+ Content(Equals("↓1 repo → feature"))
+ },
+})
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index d4a093de8..0ab0e4331 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -266,6 +266,7 @@ var tests = []*components.IntegrationTest{
status.ClickRepoNameToOpenReposMenu,
status.ClickToFocus,
status.ClickWorkingTreeStateToOpenRebaseOptionsMenu,
+ status.ShowDivergenceFromBaseBranch,
submodule.Add,
submodule.Enter,
submodule.EnterNested,
diff --git a/schema/config.json b/schema/config.json
index f5f7bab86..cf25ce007 100644
--- a/schema/config.json
+++ b/schema/config.json
@@ -331,6 +331,16 @@
"description": "If true, show commit hashes alongside branch names in the branches view.",
"default": false
},
+ "showDivergenceFromBaseBranch": {
+ "type": "string",
+ "enum": [
+ "none",
+ "onlyArrow",
+ "arrowAndNumber"
+ ],
+ "description": "Whether to show the divergence from the base branch in the branches view.\nOne of: 'none' | 'onlyArrow' | 'arrowAndNumber'",
+ "default": "none"
+ },
"commandLogSize": {
"type": "integer",
"minimum": 0,