mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-12 00:26:24 -04:00
Show divergence from base branch in branches list
This commit is contained in:
parent
316ea99d67
commit
6eaece3696
|
|
@ -1,6 +1,7 @@
|
|||
package git_commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
|
@ -60,7 +61,7 @@ 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, existingMainBranches *ExistingMainBranches, oldBranches []*models.Branch, onWorker func(func() error), renderFunc func()) ([]*models.Branch, error) {
|
||||
branches := self.obtainBranches()
|
||||
|
||||
if self.AppState.LocalBranchSortOrder == "recency" {
|
||||
|
|
@ -119,8 +120,49 @@ func (self *BranchLoader) Load(reflogCommits []*models.Commit) ([]*models.Branch
|
|||
branch.UpstreamRemote = match.Remote
|
||||
branch.UpstreamBranch = match.Merge.Short()
|
||||
}
|
||||
|
||||
if oldBranch, found := lo.Find(oldBranches, func(b *models.Branch) bool {
|
||||
return b.Name == branch.Name
|
||||
}); found {
|
||||
branch.BehindBaseBranch.Store(oldBranch.BehindBaseBranch.Load())
|
||||
}
|
||||
}
|
||||
|
||||
onWorker(func() error {
|
||||
mainBranches := existingMainBranches.Get()
|
||||
if len(mainBranches) > 0 {
|
||||
for _, branch := range branches {
|
||||
baseBranch, err := self.GetBaseBranch(branch, existingMainBranches)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if baseBranch == "" {
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
aheadBehindStr := strings.Split(strings.TrimSpace(output), "\t")
|
||||
if len(aheadBehindStr) != 2 {
|
||||
return errors.New("unexpected output from git rev-list")
|
||||
}
|
||||
if behind, err := strconv.Atoi(aheadBehindStr[1]); err == nil {
|
||||
branch.BehindBaseBranch.Store(int32(behind))
|
||||
renderFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package models
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Branch : A git branch
|
||||
// duplicating this for now
|
||||
|
|
@ -28,6 +31,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 {
|
||||
|
|
|
|||
|
|
@ -452,7 +452,20 @@ 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().ExistingMainBranches, self.c.Model().Branches,
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,32 +155,31 @@ 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.HasCommitsToPull() && branch.HasCommitsToPush() {
|
||||
result = style.FgYellow.Sprintf("↓%s↑%s", branch.Pullables, branch.Pushables)
|
||||
} else if branch.HasCommitsToPull() {
|
||||
result = style.FgYellow.Sprintf("↓%s", branch.Pullables)
|
||||
} else if branch.HasCommitsToPush() {
|
||||
result = style.FgYellow.Sprintf("↑%s", branch.Pushables)
|
||||
}
|
||||
}
|
||||
|
||||
if branch.UpstreamGone {
|
||||
return style.FgRed.Sprint(tr.UpstreamGone)
|
||||
if v := branch.BehindBaseBranch.Load(); v != 0 {
|
||||
if result != "" {
|
||||
result += " "
|
||||
}
|
||||
result += style.FgCyan.Sprintf("↓%d", v)
|
||||
}
|
||||
|
||||
if branch.MatchesUpstream() {
|
||||
return style.FgGreen.Sprint("✓")
|
||||
}
|
||||
if branch.RemoteBranchNotStoredLocally() {
|
||||
return style.FgMagenta.Sprint("?")
|
||||
}
|
||||
|
||||
if branch.HasCommitsToPull() && branch.HasCommitsToPush() {
|
||||
return style.FgYellow.Sprintf("↓%s↑%s", branch.Pullables, branch.Pushables)
|
||||
}
|
||||
if branch.HasCommitsToPull() {
|
||||
return style.FgYellow.Sprintf("↓%s", branch.Pullables)
|
||||
}
|
||||
if branch.HasCommitsToPush() {
|
||||
return style.FgYellow.Sprintf("↑%s", branch.Pushables)
|
||||
}
|
||||
|
||||
return ""
|
||||
return result
|
||||
}
|
||||
|
||||
func SetCustomBranches(customBranchColors map[string]string) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ var ForcePush = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
Contains("one"),
|
||||
)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
t.Views().Status().Content(Equals("✓ ↓1 repo → master"))
|
||||
|
||||
t.Views().Remotes().Focus().
|
||||
Lines(Contains("origin")).
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ var ForcePushMultipleMatching = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
Contains("one"),
|
||||
)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
t.Views().Status().Content(Equals("✓ ↓1 repo → master"))
|
||||
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ var ForcePushMultipleUpstream = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
Contains("one"),
|
||||
)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
t.Views().Status().Content(Equals("✓ ↓1 repo → master"))
|
||||
|
||||
t.Views().Branches().
|
||||
Lines(
|
||||
|
|
|
|||
|
|
@ -36,6 +36,6 @@ var Pull = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
Contains("one"),
|
||||
)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
t.Views().Status().Content(Equals("✓ ↓1 repo → master"))
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -40,6 +40,6 @@ var PullAndSetUpstream = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
Contains("one"),
|
||||
)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
t.Views().Status().Content(Equals("✓ ↓1 repo → master"))
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ var PullMerge = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
IsFocused().
|
||||
Press(keys.Universal.Pull)
|
||||
|
||||
t.Views().Status().Content(Equals("↑2 repo → master"))
|
||||
t.Views().Status().Content(Equals("↑2 ↓2 repo → master"))
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ var PullMergeConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Equals("↑2 repo → master"))
|
||||
t.Views().Status().Content(Equals("↑2 ↓2 repo → master"))
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ var PullRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
IsFocused().
|
||||
Press(keys.Universal.Pull)
|
||||
|
||||
t.Views().Status().Content(Equals("↑1 repo → master"))
|
||||
t.Views().Status().Content(Equals("↑1 ↓2 repo → master"))
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ var PullRebaseConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Equals("↑1 repo → master"))
|
||||
t.Views().Status().Content(Equals("↑1 ↓2 repo → master"))
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ var PullRebaseInteractiveConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Equals("↑2 repo → master"))
|
||||
t.Views().Status().Content(Equals("↑2 ↓2 repo → master"))
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ var PullRebaseInteractiveConflictDrop = NewIntegrationTest(NewIntegrationTestArg
|
|||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Equals("↑1 repo → master"))
|
||||
t.Views().Status().Content(Equals("↑1 ↓2 repo → master"))
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
|
|
|
|||
Loading…
Reference in a new issue