Factor out CommitLoader.mainBranches into its own class, and store it in Model

This commit is contained in:
Stefan Haller 2024-04-29 18:37:34 +02:00
parent b2011dca35
commit f4d922bc80
7 changed files with 116 additions and 68 deletions

View file

@ -35,11 +35,6 @@ type CommitLoader struct {
readFile func(filename string) ([]byte, error)
walkFiles func(root string, fn filepath.WalkFunc) error
dotGitDir string
// List of main branches that exist in the repo.
// We use these to obtain the merge base of the branch.
// When nil, we're yet to obtain the list of existing main branches.
// When an empty slice, we've obtained the list and it's empty.
mainBranches []string
*GitCommon
}
@ -56,7 +51,6 @@ func NewCommitLoader(
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
mainBranches: nil,
GitCommon: gitCommon,
}
}
@ -72,6 +66,7 @@ type GetCommitsOptions struct {
All bool
// If non-empty, show divergence from this ref (left-right log)
RefToShowDivergenceFrom string
MainBranches *MainBranches
}
// GetCommits obtains the commits of the current branch
@ -108,9 +103,9 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
go utils.Safe(func() {
defer wg.Done()
ancestor = self.getMergeBase(opts.RefName)
ancestor = self.getMergeBase(opts.RefName, opts.MainBranches)
if opts.RefToShowDivergenceFrom != "" {
remoteAncestor = self.getMergeBase(opts.RefToShowDivergenceFrom)
remoteAncestor = self.getMergeBase(opts.RefToShowDivergenceFrom, opts.MainBranches)
}
})
@ -471,12 +466,9 @@ func setCommitMergedStatuses(ancestor string, commits []*models.Commit) {
}
}
func (self *CommitLoader) getMergeBase(refName string) string {
if self.mainBranches == nil {
self.mainBranches = self.getExistingMainBranches()
}
if len(self.mainBranches) == 0 {
func (self *CommitLoader) getMergeBase(refName string, existingMainBranches *ExistingMainBranches) string {
mainBranches := existingMainBranches.Get()
if len(mainBranches) == 0 {
return ""
}
@ -491,63 +483,12 @@ func (self *CommitLoader) getMergeBase(refName string) string {
// also not very common, but can totally happen and is not an error.
output, _ := self.cmd.New(
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
NewGitCmd("merge-base").Arg(refName).Arg(mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
return ignoringWarnings(output)
}
func (self *CommitLoader) getExistingMainBranches() []string {
var existingBranches []string
var wg sync.WaitGroup
mainBranches := self.UserConfig.Git.MainBranches
existingBranches = make([]string, len(mainBranches))
for i, branchName := range mainBranches {
wg.Add(1)
go utils.Safe(func() {
defer wg.Done()
// Try to determine upstream of local main branch
if ref, err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
).DontLog().RunWithOutput(); err == nil {
existingBranches[i] = strings.TrimSpace(ref)
return
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
return
}
// If this failed as well, try if we have the main branch as a local
// branch. This covers the case where somebody is using git locally
// for something, but never pushing anywhere.
ref = "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
}
})
}
wg.Wait()
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
return branch != ""
})
return existingBranches
}
func ignoringWarnings(commandOutput string) string {
trimmedOutput := strings.TrimSpace(commandOutput)
split := strings.Split(trimmedOutput, "\n")

View file

@ -307,10 +307,11 @@ func TestGetCommits(t *testing.T) {
common := utils.NewDummyCommon()
common.AppState = &config.AppState{}
common.AppState.GitLogOrder = scenario.logOrder
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
builder := &CommitLoader{
Common: common,
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
cmd: cmd,
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
dotGitDir: ".git",
readFile: func(filename string) ([]byte, error) {
@ -322,7 +323,9 @@ func TestGetCommits(t *testing.T) {
}
common.UserConfig.Git.MainBranches = scenario.mainBranches
commits, err := builder.GetCommits(scenario.opts)
opts := scenario.opts
opts.MainBranches = NewMainBranches(scenario.mainBranches, cmd)
commits, err := builder.GetCommits(opts)
assert.Equal(t, scenario.expectedCommits, commits)
assert.Equal(t, scenario.expectedError, err)

View file

@ -0,0 +1,98 @@
package git_commands
import (
"strings"
"sync"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
type MainBranches struct {
// List of main branches configured by the user. Just the bare names.
configuredMainBranches []string
// Which of these actually exist in the repository. Full ref names, and it
// could be either "refs/heads/..." or "refs/remotes/origin/..." depending
// on which one exists for a given bare name.
existingMainBranches []string
cmd oscommands.ICmdObjBuilder
mutex *deadlock.Mutex
}
func NewMainBranches(
configuredMainBranches []string,
cmd oscommands.ICmdObjBuilder,
) *MainBranches {
return &MainBranches{
configuredMainBranches: configuredMainBranches,
existingMainBranches: nil,
cmd: cmd,
mutex: &deadlock.Mutex{},
}
}
// Get the list of main branches that exist in the repository. This is a list of
// full ref names.
func (self *MainBranches) Get() []string {
self.mutex.Lock()
defer self.mutex.Unlock()
if self.existingMainBranches == nil {
self.existingMainBranches = self.determineMainBranches()
}
return self.existingMainBranches
}
func (self *MainBranches) determineMainBranches() []string {
var existingBranches []string
var wg sync.WaitGroup
existingBranches = make([]string, len(self.configuredMainBranches))
for i, branchName := range self.configuredMainBranches {
wg.Add(1)
go utils.Safe(func() {
defer wg.Done()
// Try to determine upstream of local main branch
if ref, err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--symbolic-full-name", branchName+"@{u}").ToArgv(),
).DontLog().RunWithOutput(); err == nil {
existingBranches[i] = strings.TrimSpace(ref)
return
}
// If this failed, a local branch for this main branch doesn't exist or it
// has no upstream configured. Try looking for one in the "origin" remote.
ref := "refs/remotes/origin/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
return
}
// If this failed as well, try if we have the main branch as a local
// branch. This covers the case where somebody is using git locally
// for something, but never pushing anywhere.
ref = "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err == nil {
existingBranches[i] = ref
}
})
}
wg.Wait()
existingBranches = lo.Filter(existingBranches, func(branch string, _ int) bool {
return branch != ""
})
return existingBranches
}

View file

@ -331,6 +331,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
RefName: self.refForLog(),
RefForPushedStatus: checkedOutBranchName,
All: self.c.Contexts().LocalCommits.GetShowWholeGitGraph(),
MainBranches: self.c.Model().MainBranches,
},
)
if err != nil {
@ -357,6 +358,7 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
RefName: self.c.Contexts().SubCommits.GetRef().FullRefName(),
RefToShowDivergenceFrom: self.c.Contexts().SubCommits.GetRefToShowDivergenceFrom(),
RefForPushedStatus: self.c.Contexts().SubCommits.GetRef().FullRefName(),
MainBranches: self.c.Model().MainBranches,
},
)
if err != nil {

View file

@ -44,6 +44,7 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
RefName: opts.Ref.FullRefName(),
RefForPushedStatus: opts.Ref.FullRefName(),
RefToShowDivergenceFrom: opts.RefToShowDivergenceFrom,
MainBranches: self.c.Model().MainBranches,
},
)
if err != nil {

View file

@ -379,6 +379,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
BisectInfo: git_commands.NewNullBisectInfo(),
FilesTrie: patricia.NewTrie(),
Authors: map[string]*models.Author{},
MainBranches: git_commands.NewMainBranches(gui.UserConfig.Git.MainBranches, gui.os.Cmd),
},
Modes: &types.Modes{
Filtering: filtering.New(startArgs.FilterPath, ""),

View file

@ -281,6 +281,8 @@ type Model struct {
// we're on a detached head because we're rebasing or bisecting.
CheckedOutBranch string
MainBranches *git_commands.MainBranches
// for displaying suggestions while typing in a file name
FilesTrie *patricia.Trie