mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-15 18:16:25 -04:00
The existing git log logic fetched the first 300 commits of a repo and displayed them in the local and sub-commit views. Once a user selected a commit beyond a threshold of 200 commits the whole repository was loaded. This is problematic with large repos e.g. the linux kernel with currently ~138k commits as lazygit slows down substantially with such a large number of commits in memory. This commit replaces the current all or only the first 300 commits logic with an incremental fetching approach: 1. The first 400 commits of repo are loaded by default. 2. If the user selects a commit beyond a threshold (current limit-100) the git log limit is increased by 400 if there are more commits available. If there are more commits available is currently checked by comparing the previous log limit with the real commit count in the model, if the commit count is less then the limit it is assumed that we reached the end of the commit log. Ideally it would be better to call `git rev-list --count xyz` in the right places and compare with this result, but this requires more changes. Adding a "paginated implementation" by utilizing `git log --skip=x --max-count=y` and appending commits to the model instead of replacing the whole collection would be nice, but this requires deeper changes to keep everything consistent. Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de> Co-authored-by: DeepSeek V4 Flash
244 lines
6.6 KiB
Go
244 lines
6.6 KiB
Go
package context
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"github.com/jesseduffield/lazygit/pkg/gocui"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type SubCommitsContext struct {
|
|
c *ContextCommon
|
|
|
|
*SubCommitsViewModel
|
|
*ListContextTrait
|
|
*DynamicTitleBuilder
|
|
*SearchTrait
|
|
}
|
|
|
|
var (
|
|
_ types.IListContext = (*SubCommitsContext)(nil)
|
|
_ types.DiffableContext = (*SubCommitsContext)(nil)
|
|
_ types.ISearchableContext = (*SubCommitsContext)(nil)
|
|
)
|
|
|
|
func NewSubCommitsContext(
|
|
c *ContextCommon,
|
|
) *SubCommitsContext {
|
|
viewModel := &SubCommitsViewModel{
|
|
ListViewModel: NewListViewModel(
|
|
func() []*models.Commit { return c.Model().SubCommits },
|
|
),
|
|
ref: nil,
|
|
gitLogLimit: git_commands.DefaultGitLogLimit(),
|
|
}
|
|
|
|
getDisplayStrings := func(startIdx int, endIdx int) [][]string {
|
|
// This can happen if a sub-commits view is asked to be rerendered while
|
|
// it is invisible; for example when switching screen modes, which
|
|
// rerenders all views.
|
|
if viewModel.GetRef() == nil {
|
|
return [][]string{}
|
|
}
|
|
|
|
var selectedCommitHashPtr *string
|
|
if c.Context().Current().GetKey() == SUB_COMMITS_CONTEXT_KEY {
|
|
selectedCommit := viewModel.GetSelected()
|
|
if selectedCommit != nil {
|
|
selectedCommitHashPtr = selectedCommit.HashPtr()
|
|
}
|
|
}
|
|
branches := []*models.Branch{}
|
|
if viewModel.GetShowBranchHeads() {
|
|
branches = c.Model().Branches
|
|
}
|
|
hasRebaseUpdateRefsConfig := c.Git().Config.GetRebaseUpdateRefs()
|
|
return presentation.GetCommitListDisplayStrings(
|
|
c.Common,
|
|
c.Model().SubCommits,
|
|
branches,
|
|
viewModel.GetRef().RefName(),
|
|
hasRebaseUpdateRefsConfig,
|
|
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
|
c.Modes().CherryPicking.SelectedHashSet(),
|
|
c.Modes().Diffing.Ref,
|
|
"",
|
|
c.UserConfig().Gui.TimeFormat,
|
|
c.UserConfig().Gui.ShortTimeFormat,
|
|
time.Now(),
|
|
c.UserConfig().Git.ParseEmoji,
|
|
selectedCommitHashPtr,
|
|
startIdx,
|
|
endIdx,
|
|
shouldShowGraph(c),
|
|
git_commands.NewNullBisectInfo(),
|
|
)
|
|
}
|
|
|
|
getNonModelItems := func() []*NonModelItem {
|
|
result := []*NonModelItem{}
|
|
if viewModel.GetRefToShowDivergenceFrom() != "" {
|
|
_, upstreamIdx, found := lo.FindIndexOf(
|
|
c.Model().SubCommits, func(c *models.Commit) bool { return c.Divergence == models.DivergenceRight })
|
|
if !found {
|
|
upstreamIdx = 0
|
|
}
|
|
result = append(result, &NonModelItem{
|
|
Index: upstreamIdx,
|
|
Content: formatListSectionHeader(c.Tr.DivergenceSectionHeaderRemote),
|
|
})
|
|
|
|
_, localIdx, found := lo.FindIndexOf(
|
|
c.Model().SubCommits, func(c *models.Commit) bool { return c.Divergence == models.DivergenceLeft })
|
|
if !found {
|
|
localIdx = len(c.Model().SubCommits)
|
|
}
|
|
result = append(result, &NonModelItem{
|
|
Index: localIdx,
|
|
Content: formatListSectionHeader(c.Tr.DivergenceSectionHeaderLocal),
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
ctx := &SubCommitsContext{
|
|
c: c,
|
|
SubCommitsViewModel: viewModel,
|
|
SearchTrait: NewSearchTrait(c),
|
|
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle),
|
|
ListContextTrait: &ListContextTrait{
|
|
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
|
View: c.Views().SubCommits,
|
|
WindowName: "branches",
|
|
Key: SUB_COMMITS_CONTEXT_KEY,
|
|
Kind: types.SIDE_CONTEXT,
|
|
Focusable: true,
|
|
Transient: true,
|
|
NeedsRerenderOnWidthChange: types.NEEDS_RERENDER_ON_WIDTH_CHANGE_WHEN_SCREEN_MODE_CHANGES,
|
|
NeedsRerenderOnHeightChange: true,
|
|
})),
|
|
ListRenderer: ListRenderer{
|
|
list: viewModel,
|
|
getDisplayStrings: getDisplayStrings,
|
|
getNonModelItems: getNonModelItems,
|
|
},
|
|
c: c,
|
|
refreshViewportOnChange: true,
|
|
renderOnlyVisibleLines: true,
|
|
},
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
type SubCommitsViewModel struct {
|
|
// name of the ref that the sub-commits are shown for
|
|
ref models.Ref
|
|
refToShowDivergenceFrom string
|
|
*ListViewModel[*models.Commit]
|
|
|
|
gitLogLimit *git_commands.GitLogLimit
|
|
showBranchHeads bool
|
|
}
|
|
|
|
func (self *SubCommitsViewModel) SetRef(ref models.Ref) {
|
|
self.ref = ref
|
|
}
|
|
|
|
func (self *SubCommitsViewModel) GetRef() models.Ref {
|
|
return self.ref
|
|
}
|
|
|
|
func (self *SubCommitsViewModel) SetRefToShowDivergenceFrom(ref string) {
|
|
self.refToShowDivergenceFrom = ref
|
|
}
|
|
|
|
func (self *SubCommitsViewModel) GetRefToShowDivergenceFrom() string {
|
|
return self.refToShowDivergenceFrom
|
|
}
|
|
|
|
func (self *SubCommitsViewModel) SetShowBranchHeads(value bool) {
|
|
self.showBranchHeads = value
|
|
}
|
|
|
|
func (self *SubCommitsViewModel) GetShowBranchHeads() bool {
|
|
return self.showBranchHeads
|
|
}
|
|
|
|
func (self *SubCommitsContext) CanRebase() bool {
|
|
return false
|
|
}
|
|
|
|
func (self *SubCommitsContext) GetSelectedRef() models.Ref {
|
|
commit := self.GetSelected()
|
|
if commit == nil {
|
|
return nil
|
|
}
|
|
return commit
|
|
}
|
|
|
|
func (self *SubCommitsContext) GetSelectedRefRangeForDiffFiles() *types.RefRange {
|
|
commits, startIdx, endIdx := self.GetSelectedItems()
|
|
if commits == nil || startIdx == endIdx {
|
|
return nil
|
|
}
|
|
from := commits[len(commits)-1]
|
|
to := commits[0]
|
|
if from.Divergence != to.Divergence {
|
|
return nil
|
|
}
|
|
return &types.RefRange{From: from, To: to}
|
|
}
|
|
|
|
func (self *SubCommitsContext) GetCommits() []*models.Commit {
|
|
return self.getModel()
|
|
}
|
|
|
|
func (self *SubCommitsContext) SetGitLogLimit(limit *git_commands.GitLogLimit) {
|
|
self.gitLogLimit = limit
|
|
}
|
|
|
|
func (self *SubCommitsContext) GetGitLogLimit() *git_commands.GitLogLimit {
|
|
return self.gitLogLimit
|
|
}
|
|
|
|
func (self *SubCommitsContext) GetDiffTerminals() []string {
|
|
itemId := self.GetSelectedItemId()
|
|
|
|
return []string{itemId}
|
|
}
|
|
|
|
func (self *SubCommitsContext) RefForAdjustingLineNumberInDiff() string {
|
|
commits, _, _ := self.GetSelectedItems()
|
|
if commits == nil {
|
|
return ""
|
|
}
|
|
return commits[0].Hash()
|
|
}
|
|
|
|
func (self *SubCommitsContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
|
return searchModelCommits(caseSensitive, self.GetCommits(), self.ColumnPositions(), self.modelToViewIndexConverter(), searchStr)
|
|
}
|
|
|
|
func (self *SubCommitsContext) IndexForGotoBottom() int {
|
|
commits := self.GetCommits()
|
|
selectedIdx := self.GetSelectedLineIdx()
|
|
if selectedIdx >= 0 && selectedIdx < len(commits)-1 {
|
|
if commits[selectedIdx+1].Status != models.StatusMerged {
|
|
_, idx, found := lo.FindIndexOf(commits, func(c *models.Commit) bool {
|
|
return c.Status == models.StatusMerged
|
|
})
|
|
if found {
|
|
return idx - 1
|
|
}
|
|
}
|
|
}
|
|
|
|
return self.list.Len() - 1
|
|
}
|