Store Commit.Hash by pointer (kept in a pool of hashes)

This in itself is not an improvement, because hashes are unique (they are shared
between real commits and rebase todos, but there are so few of those that it
doesn't matter). However, it becomes an improvement once we also store parent
hashes in the same pool; but the real motivation for this change is to also
reuse the hash pointers in Pipe objects later in the branch. This will be a big
win because in a merge-heavy git repo there are many more Pipe instances than
commits.
This commit is contained in:
Stefan Haller
2025-04-29 14:57:15 +02:00
parent 1037371a44
commit e27bc15bbd
15 changed files with 119 additions and 74 deletions
@@ -332,6 +332,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
RefForPushedStatus: checkedOutBranchName,
All: self.c.Contexts().LocalCommits.GetShowWholeGitGraph(),
MainBranches: self.c.Model().MainBranches,
HashPool: self.c.Model().HashPool,
},
)
if err != nil {
@@ -360,6 +361,7 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
RefToShowDivergenceFrom: self.c.Contexts().SubCommits.GetRefToShowDivergenceFrom(),
RefForPushedStatus: self.c.Contexts().SubCommits.GetRef().FullRefName(),
MainBranches: self.c.Model().MainBranches,
HashPool: self.c.Model().HashPool,
},
)
if err != nil {
@@ -406,7 +408,7 @@ func (self *RefreshHelper) refreshRebaseCommits() error {
self.c.Mutexes().LocalCommitsMutex.Lock()
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
updatedCommits, err := self.c.Git().Loaders.CommitLoader.MergeRebasingCommits(self.c.Model().Commits)
updatedCommits, err := self.c.Git().Loaders.CommitLoader.MergeRebasingCommits(self.c.Model().HashPool, self.c.Model().Commits)
if err != nil {
return err
}
@@ -455,7 +457,7 @@ func (self *RefreshHelper) refreshBranches(refreshWorktrees bool, keepBranchSele
// which allows us to order them correctly. So if we're filtering we'll just
// manually load all the reflog commits here
var err error
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(nil, "", "")
reflogCommits, _, err = self.c.Git().Loaders.ReflogCommitLoader.GetReflogCommits(self.c.Model().HashPool, nil, "", "")
if err != nil {
self.c.Log.Error(err)
}
@@ -624,7 +626,7 @@ func (self *RefreshHelper) refreshReflogCommits() error {
refresh := func(stateCommits *[]*models.Commit, filterPath string, filterAuthor string) error {
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
GetReflogCommits(lastReflogCommit, filterPath, filterAuthor)
GetReflogCommits(self.c.Model().HashPool, lastReflogCommit, filterPath, filterAuthor)
if err != nil {
return err
}
@@ -45,6 +45,7 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
RefForPushedStatus: opts.Ref.FullRefName(),
RefToShowDivergenceFrom: opts.RefToShowDivergenceFrom,
MainBranches: self.c.Model().MainBranches,
HashPool: self.c.Model().HashPool,
},
)
if err != nil {
@@ -564,7 +564,7 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit(
for _, c := range commitsToEdit[:len(commitsToEdit)-1] {
// Merge commits can't be set to "edit", so just skip them
if !c.IsMerge() {
todos = append(todos, models.NewCommit(models.NewCommitOpts{Hash: c.Hash(), Action: todo.Pick}))
todos = append(todos, models.NewCommit(self.c.Model().HashPool, models.NewCommitOpts{Hash: c.Hash(), Action: todo.Pick}))
}
}
if len(todos) > 0 {
+1
View File
@@ -558,6 +558,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
FilesTrie: patricia.NewTrie(),
Authors: map[string]*models.Author{},
MainBranches: git_commands.NewMainBranches(gui.c.Common, gui.os.Cmd),
HashPool: &utils.StringPool{},
},
Modes: &types.Modes{
Filtering: filtering.New(startArgs.FilterPath, ""),
+3 -1
View File
@@ -544,8 +544,10 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
for _, s := range scenarios {
if !focusing || s.focus {
t.Run(s.testName, func(t *testing.T) {
hashPool := &utils.StringPool{}
commits := lo.Map(s.commitOpts,
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
result := GetCommitListDisplayStrings(
common,
+3 -1
View File
@@ -149,8 +149,10 @@ func TestRenderCommitFileTree(t *testing.T) {
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
hashPool := &utils.StringPool{}
viewModel := filetree.NewCommitFileTreeViewModel(func() []*models.CommitFile { return s.files }, utils.NewDummyLog(), true)
viewModel.SetRef(&models.Commit{})
viewModel.SetRef(models.NewCommit(hashPool, models.NewCommitOpts{Hash: "1234"}))
viewModel.SetTree()
for _, path := range s.collapsedPaths {
viewModel.ToggleCollapsed(path)
+29 -21
View File
@@ -219,9 +219,11 @@ func TestRenderCommitGraph(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
hashPool := &utils.StringPool{}
getStyle := func(c *models.Commit) style.TextStyle { return style.FgDefault }
commits := lo.Map(test.commitOpts,
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
lines := RenderCommitGraph(commits, "blah", getStyle)
trimmedExpectedOutput := ""
@@ -254,6 +256,8 @@ func TestRenderPipeSet(t *testing.T) {
magenta := style.FgMagenta
nothing := style.Nothing
hashPool := &utils.StringPool{}
tests := []struct {
name string
pipes []*Pipe
@@ -268,7 +272,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "a", toHash: "b", kind: TERMINATES, style: cyan},
{fromPos: 0, toPos: 0, fromHash: "b", toHash: "c", kind: STARTS, style: green},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a"}),
expectedStr: "◯",
expectedStyles: []style.TextStyle{green},
},
@@ -278,7 +282,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "a", toHash: "selected", kind: TERMINATES, style: cyan},
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "c", kind: STARTS, style: green},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a"}),
expectedStr: "◯",
expectedStyles: []style.TextStyle{highlightStyle},
},
@@ -290,7 +294,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "d", kind: STARTS, style: green},
{fromPos: 0, toPos: 1, fromHash: "selected", toHash: "e", kind: STARTS, style: green},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a"}),
expectedStr: "⏣─╮",
expectedStyles: []style.TextStyle{
highlightStyle, highlightStyle, highlightStyle,
@@ -304,7 +308,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "b", toHash: "d", kind: STARTS, style: green},
{fromPos: 0, toPos: 1, fromHash: "b", toHash: "e", kind: STARTS, style: green},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a"}),
expectedStr: "⏣─│",
expectedStyles: []style.TextStyle{
green, green, magenta,
@@ -319,7 +323,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 3, toPos: 0, fromHash: "e1", toHash: "a2", kind: TERMINATES, style: green},
{fromPos: 0, toPos: 2, fromHash: "a2", toHash: "c3", kind: STARTS, style: yellow},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a1"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a1"}),
expectedStr: "⏣─│─┬─╯",
expectedStyles: []style.TextStyle{
yellow, yellow, magenta, yellow, yellow, green, green,
@@ -334,7 +338,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 3, toPos: 0, fromHash: "e1", toHash: "selected", kind: TERMINATES, style: green},
{fromPos: 0, toPos: 2, fromHash: "selected", toHash: "c3", kind: STARTS, style: yellow},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a1"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a1"}),
expectedStr: "⏣───╮ ╯",
expectedStyles: []style.TextStyle{
highlightStyle, highlightStyle, highlightStyle, highlightStyle, highlightStyle, nothing, green,
@@ -348,7 +352,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 1, toPos: 0, fromHash: "b1", toHash: "a2", kind: TERMINATES, style: magenta},
{fromPos: 2, toPos: 0, fromHash: "c1", toHash: "a2", kind: TERMINATES, style: green},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a1"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a1"}),
expectedStr: "◯─┴─╯",
expectedStyles: []style.TextStyle{
yellow, magenta, magenta, green, green,
@@ -363,7 +367,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 1, toPos: 1, fromHash: "b1", toHash: "b3", kind: CONTINUES, style: magenta},
{fromPos: 2, toPos: 2, fromHash: "c1", toHash: "c3", kind: CONTINUES, style: green},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a1"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a1"}),
expectedStr: "⏣─│─│─╮",
expectedStyles: []style.TextStyle{
yellow, yellow, magenta, yellow, green, yellow, yellow,
@@ -378,7 +382,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 1, toPos: 1, fromHash: "b1", toHash: "a2", kind: CONTINUES, style: green},
{fromPos: 2, toPos: 0, fromHash: "c1", toHash: "a2", kind: TERMINATES, style: magenta},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a1"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a1"}),
expectedStr: "⏣─│─╯",
expectedStyles: []style.TextStyle{
yellow, yellow, green, magenta, magenta,
@@ -393,7 +397,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 2, toPos: 2, fromHash: "c1", toHash: "c3", kind: CONTINUES, style: green},
{fromPos: 3, toPos: 0, fromHash: "d1", toHash: "a2", kind: TERMINATES, style: magenta},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "a1"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a1"}),
expectedStr: "⏣─┬─│─╯",
expectedStyles: []style.TextStyle{
yellow, yellow, yellow, magenta, green, magenta, magenta,
@@ -405,7 +409,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "a2", kind: TERMINATES, style: red},
{fromPos: 0, toPos: 0, fromHash: "a2", toHash: "a3", kind: STARTS, style: yellow},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "selected"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "selected"}),
expectedStr: "◯",
expectedStyles: []style.TextStyle{
yellow,
@@ -417,7 +421,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "a2", kind: TERMINATES, style: red},
{fromPos: 1, toPos: 1, fromHash: "selected", toHash: "b3", kind: CONTINUES, style: red},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "selected"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "selected"}),
expectedStr: "◯ │",
expectedStyles: []style.TextStyle{
highlightStyle, nothing, highlightStyle,
@@ -430,7 +434,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 1, toPos: 1, fromHash: "z1", toHash: "z3", kind: CONTINUES, style: green},
{fromPos: 2, toPos: 2, fromHash: "selected", toHash: "b3", kind: CONTINUES, style: red},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "selected"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "selected"}),
expectedStr: "◯ │ │",
expectedStyles: []style.TextStyle{
highlightStyle, nothing, green, nothing, highlightStyle,
@@ -444,7 +448,7 @@ func TestRenderPipeSet(t *testing.T) {
{fromPos: 0, toPos: 1, fromHash: "a2", toHash: "b3", kind: STARTS, style: green},
{fromPos: 1, toPos: 0, fromHash: "selected", toHash: "a2", kind: TERMINATES, style: yellow},
},
prevCommit: models.NewCommit(models.NewCommitOpts{Hash: "selected"}),
prevCommit: models.NewCommit(hashPool, models.NewCommitOpts{Hash: "selected"}),
expectedStr: "⏣─╯",
expectedStyles: []style.TextStyle{
highlightStyle, highlightStyle, highlightStyle,
@@ -477,6 +481,8 @@ func TestRenderPipeSet(t *testing.T) {
}
func TestGetNextPipes(t *testing.T) {
hashPool := &utils.StringPool{}
tests := []struct {
prevPipes []*Pipe
commit *models.Commit
@@ -486,7 +492,7 @@ func TestGetNextPipes(t *testing.T) {
prevPipes: []*Pipe{
{fromPos: 0, toPos: 0, fromHash: "a", toHash: "b", kind: STARTS, style: style.FgDefault},
},
commit: models.NewCommit(models.NewCommitOpts{
commit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "b",
Parents: []string{"c"},
}),
@@ -501,7 +507,7 @@ func TestGetNextPipes(t *testing.T) {
{fromPos: 0, toPos: 0, fromHash: "b", toHash: "c", kind: STARTS, style: style.FgDefault},
{fromPos: 0, toPos: 1, fromHash: "b", toHash: "d", kind: STARTS, style: style.FgDefault},
},
commit: models.NewCommit(models.NewCommitOpts{
commit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "d",
Parents: []string{"e"},
}),
@@ -515,7 +521,7 @@ func TestGetNextPipes(t *testing.T) {
prevPipes: []*Pipe{
{fromPos: 0, toPos: 0, fromHash: "a", toHash: "root", kind: TERMINATES, style: style.FgDefault},
},
commit: models.NewCommit(models.NewCommitOpts{
commit: models.NewCommit(hashPool, models.NewCommitOpts{
Hash: "root",
Parents: []string{},
}),
@@ -557,8 +563,10 @@ func BenchmarkRenderCommitGraph(b *testing.B) {
}
func generateCommits(count int) []*models.Commit {
hashPool := &utils.StringPool{}
rnd := rand.New(rand.NewSource(1234))
pool := []*models.Commit{models.NewCommit(models.NewCommitOpts{Hash: "a", AuthorName: "A"})}
pool := []*models.Commit{models.NewCommit(hashPool, models.NewCommitOpts{Hash: "a", AuthorName: "A"})}
commits := make([]*models.Commit, 0, count)
authorPool := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
for len(commits) < count {
@@ -575,7 +583,7 @@ func generateCommits(count int) []*models.Commit {
if reuseParent {
newParent = pool[j]
} else {
newParent = models.NewCommit(models.NewCommitOpts{
newParent = models.NewCommit(hashPool, models.NewCommitOpts{
Hash: fmt.Sprintf("%s%d", currentCommit.Hash(), j),
AuthorName: authorPool[rnd.Intn(len(authorPool))],
})
@@ -584,7 +592,7 @@ func generateCommits(count int) []*models.Commit {
parentHashes = append(parentHashes, newParent.Hash())
}
changedCommit := models.NewCommit(models.NewCommitOpts{
changedCommit := models.NewCommit(hashPool, models.NewCommitOpts{
Hash: currentCommit.Hash(),
AuthorName: currentCommit.AuthorName,
Parents: parentHashes,
+2
View File
@@ -305,6 +305,8 @@ type Model struct {
FilesTrie *patricia.Trie
Authors map[string]*models.Author
HashPool *utils.StringPool
}
// if you add a new mutex here be sure to instantiate it. We're using pointers to