Skip to content

Commit e27bc15

Browse files
committed
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.
1 parent 1037371 commit e27bc15

File tree

15 files changed

+119
-74
lines changed

15 files changed

+119
-74
lines changed

pkg/commands/git_commands/commit_loader.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type GetCommitsOptions struct {
6666
// If non-empty, show divergence from this ref (left-right log)
6767
RefToShowDivergenceFrom string
6868
MainBranches *MainBranches
69+
HashPool *utils.StringPool
6970
}
7071

7172
// GetCommits obtains the commits of the current branch
@@ -74,7 +75,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
7475

7576
if opts.IncludeRebaseCommits && opts.FilterPath == "" {
7677
var err error
77-
commits, err = self.MergeRebasingCommits(commits)
78+
commits, err = self.MergeRebasingCommits(opts.HashPool, commits)
7879
if err != nil {
7980
return nil, err
8081
}
@@ -89,7 +90,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
8990
defer wg.Done()
9091

9192
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
92-
commit := self.extractCommitFromLine(line, opts.RefToShowDivergenceFrom != "")
93+
commit := self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != "")
9394
commits = append(commits, commit)
9495
return false, nil
9596
})
@@ -159,7 +160,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
159160
return commits, nil
160161
}
161162

162-
func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*models.Commit, error) {
163+
func (self *CommitLoader) MergeRebasingCommits(hashPool *utils.StringPool, commits []*models.Commit) ([]*models.Commit, error) {
163164
// chances are we have as many commits as last time so we'll set the capacity to be the old length
164165
result := make([]*models.Commit, 0, len(commits))
165166
for i, commit := range commits {
@@ -172,7 +173,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
172173
workingTreeState := self.getWorkingTreeState()
173174
addConflictedRebasingCommit := true
174175
if workingTreeState.CherryPicking || workingTreeState.Reverting {
175-
sequencerCommits, err := self.getHydratedSequencerCommits(workingTreeState)
176+
sequencerCommits, err := self.getHydratedSequencerCommits(hashPool, workingTreeState)
176177
if err != nil {
177178
return nil, err
178179
}
@@ -181,7 +182,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
181182
}
182183

183184
if workingTreeState.Rebasing {
184-
rebasingCommits, err := self.getHydratedRebasingCommits(addConflictedRebasingCommit)
185+
rebasingCommits, err := self.getHydratedRebasingCommits(hashPool, addConflictedRebasingCommit)
185186
if err != nil {
186187
return nil, err
187188
}
@@ -196,7 +197,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
196197
// then puts them into a commit object
197198
// example input:
198199
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
199-
func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool) *models.Commit {
200+
func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line string, showDivergence bool) *models.Commit {
200201
split := strings.SplitN(line, "\x00", 8)
201202

202203
hash := split[0]
@@ -234,7 +235,7 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
234235
parents = strings.Split(parentHashes, " ")
235236
}
236237

237-
return models.NewCommit(models.NewCommitOpts{
238+
return models.NewCommit(hashPool, models.NewCommitOpts{
238239
Hash: hash,
239240
Name: message,
240241
Tags: tags,
@@ -247,13 +248,13 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
247248
})
248249
}
249250

250-
func (self *CommitLoader) getHydratedRebasingCommits(addConflictingCommit bool) ([]*models.Commit, error) {
251+
func (self *CommitLoader) getHydratedRebasingCommits(hashPool *utils.StringPool, addConflictingCommit bool) ([]*models.Commit, error) {
251252
todoFileHasShortHashes := self.version.IsOlderThan(2, 25, 2)
252-
return self.getHydratedTodoCommits(self.getRebasingCommits(addConflictingCommit), todoFileHasShortHashes)
253+
return self.getHydratedTodoCommits(hashPool, self.getRebasingCommits(hashPool, addConflictingCommit), todoFileHasShortHashes)
253254
}
254255

255-
func (self *CommitLoader) getHydratedSequencerCommits(workingTreeState models.WorkingTreeState) ([]*models.Commit, error) {
256-
commits := self.getSequencerCommits()
256+
func (self *CommitLoader) getHydratedSequencerCommits(hashPool *utils.StringPool, workingTreeState models.WorkingTreeState) ([]*models.Commit, error) {
257+
commits := self.getSequencerCommits(hashPool)
257258
if len(commits) > 0 {
258259
// If we have any commits in .git/sequencer/todo, then the last one of
259260
// those is the conflicting one.
@@ -262,16 +263,16 @@ func (self *CommitLoader) getHydratedSequencerCommits(workingTreeState models.Wo
262263
// For single-commit cherry-picks and reverts, git apparently doesn't
263264
// use the sequencer; in that case, CHERRY_PICK_HEAD or REVERT_HEAD is
264265
// our conflicting commit, so synthesize it here.
265-
conflicedCommit := self.getConflictedSequencerCommit(workingTreeState)
266+
conflicedCommit := self.getConflictedSequencerCommit(hashPool, workingTreeState)
266267
if conflicedCommit != nil {
267268
commits = append(commits, conflicedCommit)
268269
}
269270
}
270271

271-
return self.getHydratedTodoCommits(commits, true)
272+
return self.getHydratedTodoCommits(hashPool, commits, true)
272273
}
273274

274-
func (self *CommitLoader) getHydratedTodoCommits(todoCommits []*models.Commit, todoFileHasShortHashes bool) ([]*models.Commit, error) {
275+
func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, todoCommits []*models.Commit, todoFileHasShortHashes bool) ([]*models.Commit, error) {
275276
if len(todoCommits) == 0 {
276277
return nil, nil
277278
}
@@ -292,7 +293,7 @@ func (self *CommitLoader) getHydratedTodoCommits(todoCommits []*models.Commit, t
292293

293294
fullCommits := map[string]*models.Commit{}
294295
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
295-
commit := self.extractCommitFromLine(line, false)
296+
commit := self.extractCommitFromLine(hashPool, line, false)
296297
fullCommits[commit.Hash()] = commit
297298
return false, nil
298299
})
@@ -331,7 +332,7 @@ func (self *CommitLoader) getHydratedTodoCommits(todoCommits []*models.Commit, t
331332
// git-rebase-todo example:
332333
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
333334
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
334-
func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*models.Commit {
335+
func (self *CommitLoader) getRebasingCommits(hashPool *utils.StringPool, addConflictingCommit bool) []*models.Commit {
335336
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
336337
if err != nil {
337338
self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
@@ -350,7 +351,7 @@ func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*model
350351
// See if the current commit couldn't be applied because it conflicted; if
351352
// so, add a fake entry for it
352353
if addConflictingCommit {
353-
if conflictedCommit := self.getConflictedCommit(todos); conflictedCommit != nil {
354+
if conflictedCommit := self.getConflictedCommit(hashPool, todos); conflictedCommit != nil {
354355
commits = append(commits, conflictedCommit)
355356
}
356357
}
@@ -364,7 +365,7 @@ func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*model
364365
// Command does not have a commit associated, skip
365366
continue
366367
}
367-
commits = utils.Prepend(commits, models.NewCommit(models.NewCommitOpts{
368+
commits = utils.Prepend(commits, models.NewCommit(hashPool, models.NewCommitOpts{
368369
Hash: t.Commit,
369370
Name: t.Msg,
370371
Status: models.StatusRebasing,
@@ -375,7 +376,7 @@ func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*model
375376
return commits
376377
}
377378

378-
func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) *models.Commit {
379+
func (self *CommitLoader) getConflictedCommit(hashPool *utils.StringPool, todos []todo.Todo) *models.Commit {
379380
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/done"))
380381
if err != nil {
381382
self.Log.Error(fmt.Sprintf("error occurred reading rebase-merge/done: %s", err.Error()))
@@ -391,10 +392,10 @@ func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) *models.Commit
391392
amendFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/amend"))
392393
messageFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/message"))
393394

394-
return self.getConflictedCommitImpl(todos, doneTodos, amendFileExists, messageFileExists)
395+
return self.getConflictedCommitImpl(hashPool, todos, doneTodos, amendFileExists, messageFileExists)
395396
}
396397

397-
func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos []todo.Todo, amendFileExists bool, messageFileExists bool) *models.Commit {
398+
func (self *CommitLoader) getConflictedCommitImpl(hashPool *utils.StringPool, todos []todo.Todo, doneTodos []todo.Todo, amendFileExists bool, messageFileExists bool) *models.Commit {
398399
// Should never be possible, but just to be safe:
399400
if len(doneTodos) == 0 {
400401
self.Log.Error("no done entries in rebase-merge/done file")
@@ -465,14 +466,14 @@ func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos [
465466

466467
// Any other todo that has a commit associated with it must have failed with
467468
// a conflict, otherwise we wouldn't have stopped the rebase:
468-
return models.NewCommit(models.NewCommitOpts{
469+
return models.NewCommit(hashPool, models.NewCommitOpts{
469470
Hash: lastTodo.Commit,
470471
Action: lastTodo.Command,
471472
Status: models.StatusConflicted,
472473
})
473474
}
474475

475-
func (self *CommitLoader) getSequencerCommits() []*models.Commit {
476+
func (self *CommitLoader) getSequencerCommits(hashPool *utils.StringPool) []*models.Commit {
476477
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "sequencer/todo"))
477478
if err != nil {
478479
self.Log.Error(fmt.Sprintf("error occurred reading sequencer/todo: %s", err.Error()))
@@ -493,7 +494,7 @@ func (self *CommitLoader) getSequencerCommits() []*models.Commit {
493494
// Command does not have a commit associated, skip
494495
continue
495496
}
496-
commits = utils.Prepend(commits, models.NewCommit(models.NewCommitOpts{
497+
commits = utils.Prepend(commits, models.NewCommit(hashPool, models.NewCommitOpts{
497498
Hash: t.Commit,
498499
Name: t.Msg,
499500
Status: models.StatusCherryPickingOrReverting,
@@ -504,7 +505,7 @@ func (self *CommitLoader) getSequencerCommits() []*models.Commit {
504505
return commits
505506
}
506507

507-
func (self *CommitLoader) getConflictedSequencerCommit(workingTreeState models.WorkingTreeState) *models.Commit {
508+
func (self *CommitLoader) getConflictedSequencerCommit(hashPool *utils.StringPool, workingTreeState models.WorkingTreeState) *models.Commit {
508509
var shaFile string
509510
var action todo.TodoCommand
510511
if workingTreeState.CherryPicking {
@@ -526,7 +527,7 @@ func (self *CommitLoader) getConflictedSequencerCommit(workingTreeState models.W
526527
if len(lines) == 0 {
527528
return nil
528529
}
529-
return models.NewCommit(models.NewCommitOpts{
530+
return models.NewCommit(hashPool, models.NewCommitOpts{
530531
Hash: lines[0],
531532
Status: models.StatusConflicted,
532533
Action: action,

pkg/commands/git_commands/commit_loader_test.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,16 @@ func TestGetCommits(t *testing.T) {
314314
},
315315
}
316316

317+
hashPool := &utils.StringPool{}
318+
317319
common.UserConfig().Git.MainBranches = scenario.mainBranches
318320
opts := scenario.opts
319321
opts.MainBranches = NewMainBranches(common, cmd)
322+
opts.HashPool = hashPool
320323
commits, err := builder.GetCommits(opts)
321324

322325
expectedCommits := lo.Map(scenario.expectedCommitOpts,
323-
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
326+
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
324327
assert.Equal(t, expectedCommits, commits)
325328
assert.Equal(t, scenario.expectedError, err)
326329

@@ -330,6 +333,8 @@ func TestGetCommits(t *testing.T) {
330333
}
331334

332335
func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
336+
hashPool := &utils.StringPool{}
337+
333338
scenarios := []struct {
334339
testName string
335340
todos []todo.Todo
@@ -359,7 +364,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
359364
},
360365
},
361366
amendFileExists: false,
362-
expectedResult: models.NewCommit(models.NewCommitOpts{
367+
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
363368
Hash: "fa1afe1",
364369
Action: todo.Pick,
365370
Status: models.StatusConflicted,
@@ -460,7 +465,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
460465
},
461466
},
462467
amendFileExists: false,
463-
expectedResult: models.NewCommit(models.NewCommitOpts{
468+
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
464469
Hash: "fa1afe1",
465470
Action: todo.Pick,
466471
Status: models.StatusConflicted,
@@ -489,7 +494,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
489494
},
490495
amendFileExists: false,
491496
messageFileExists: true,
492-
expectedResult: models.NewCommit(models.NewCommitOpts{
497+
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
493498
Hash: "fa1afe1",
494499
Action: todo.Edit,
495500
Status: models.StatusConflicted,
@@ -526,7 +531,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
526531
},
527532
}
528533

529-
hash := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists, scenario.messageFileExists)
534+
hash := builder.getConflictedCommitImpl(hashPool, scenario.todos, scenario.doneTodos, scenario.amendFileExists, scenario.messageFileExists)
530535
assert.Equal(t, scenario.expectedResult, hash)
531536
})
532537
}
@@ -573,11 +578,13 @@ func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
573578

574579
for _, scenario := range scenarios {
575580
t.Run(scenario.testName, func(t *testing.T) {
581+
hashPool := &utils.StringPool{}
582+
576583
commits := lo.Map(scenario.commitOpts,
577-
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
584+
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
578585
setCommitMergedStatuses(scenario.ancestor, commits)
579586
expectedCommits := lo.Map(scenario.expectedCommitOpts,
580-
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
587+
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
581588
assert.Equal(t, expectedCommits, commits)
582589
})
583590
}

pkg/commands/git_commands/rebase_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
1111
"github.com/jesseduffield/lazygit/pkg/commands/models"
1212
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
13+
"github.com/jesseduffield/lazygit/pkg/utils"
1314
"github.com/samber/lo"
1415
"github.com/stretchr/testify/assert"
1516
)
@@ -158,8 +159,9 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
158159
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
159160
})
160161

162+
hashPool := &utils.StringPool{}
161163
commits := lo.Map(s.commitOpts,
162-
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
164+
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
163165

164166
s.test(instance.DiscardOldFileChanges(commits, s.commitIndex, s.fileName))
165167
s.runner.CheckForMissingCalls()

pkg/commands/git_commands/reflog_commit_loader.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/jesseduffield/lazygit/pkg/commands/models"
88
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
99
"github.com/jesseduffield/lazygit/pkg/common"
10+
"github.com/jesseduffield/lazygit/pkg/utils"
1011
)
1112

1213
type ReflogCommitLoader struct {
@@ -23,7 +24,7 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
2324

2425
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
2526
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
26-
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
27+
func (self *ReflogCommitLoader) GetReflogCommits(hashPool *utils.StringPool, lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
2728
commits := make([]*models.Commit, 0)
2829

2930
cmdArgs := NewGitCmd("log").
@@ -39,7 +40,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
3940

4041
onlyObtainedNewReflogCommits := false
4142
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
42-
commit, ok := self.parseLine(line)
43+
commit, ok := self.parseLine(hashPool, line)
4344
if !ok {
4445
return false, nil
4546
}
@@ -68,7 +69,7 @@ func (self *ReflogCommitLoader) sameReflogCommit(a *models.Commit, b *models.Com
6869
return a.Hash() == b.Hash() && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
6970
}
7071

71-
func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
72+
func (self *ReflogCommitLoader) parseLine(hashPool *utils.StringPool, line string) (*models.Commit, bool) {
7273
fields := strings.SplitN(line, "\x00", 4)
7374
if len(fields) <= 3 {
7475
return nil, false
@@ -82,7 +83,7 @@ func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
8283
parents = strings.Split(parentHashes, " ")
8384
}
8485

85-
return models.NewCommit(models.NewCommitOpts{
86+
return models.NewCommit(hashPool, models.NewCommitOpts{
8687
Hash: fields[0],
8788
Name: fields[2],
8889
UnixTimestamp: int64(unixTimestamp),

0 commit comments

Comments
 (0)