mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 20:36:03 +02:00
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:
parent
1037371a44
commit
e27bc15bbd
15 changed files with 119 additions and 74 deletions
|
@ -66,6 +66,7 @@ type GetCommitsOptions struct {
|
||||||
// If non-empty, show divergence from this ref (left-right log)
|
// If non-empty, show divergence from this ref (left-right log)
|
||||||
RefToShowDivergenceFrom string
|
RefToShowDivergenceFrom string
|
||||||
MainBranches *MainBranches
|
MainBranches *MainBranches
|
||||||
|
HashPool *utils.StringPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommits obtains the commits of the current branch
|
// GetCommits obtains the commits of the current branch
|
||||||
|
@ -74,7 +75,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||||
|
|
||||||
if opts.IncludeRebaseCommits && opts.FilterPath == "" {
|
if opts.IncludeRebaseCommits && opts.FilterPath == "" {
|
||||||
var err error
|
var err error
|
||||||
commits, err = self.MergeRebasingCommits(commits)
|
commits, err = self.MergeRebasingCommits(opts.HashPool, commits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -89,7 +90,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
|
logErr = self.getLogCmd(opts).RunAndProcessLines(func(line string) (bool, error) {
|
||||||
commit := self.extractCommitFromLine(line, opts.RefToShowDivergenceFrom != "")
|
commit := self.extractCommitFromLine(opts.HashPool, line, opts.RefToShowDivergenceFrom != "")
|
||||||
commits = append(commits, commit)
|
commits = append(commits, commit)
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
|
@ -159,7 +160,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||||
return commits, nil
|
return commits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*models.Commit, error) {
|
func (self *CommitLoader) MergeRebasingCommits(hashPool *utils.StringPool, commits []*models.Commit) ([]*models.Commit, error) {
|
||||||
// chances are we have as many commits as last time so we'll set the capacity to be the old length
|
// chances are we have as many commits as last time so we'll set the capacity to be the old length
|
||||||
result := make([]*models.Commit, 0, len(commits))
|
result := make([]*models.Commit, 0, len(commits))
|
||||||
for i, commit := range commits {
|
for i, commit := range commits {
|
||||||
|
@ -172,7 +173,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
|
||||||
workingTreeState := self.getWorkingTreeState()
|
workingTreeState := self.getWorkingTreeState()
|
||||||
addConflictedRebasingCommit := true
|
addConflictedRebasingCommit := true
|
||||||
if workingTreeState.CherryPicking || workingTreeState.Reverting {
|
if workingTreeState.CherryPicking || workingTreeState.Reverting {
|
||||||
sequencerCommits, err := self.getHydratedSequencerCommits(workingTreeState)
|
sequencerCommits, err := self.getHydratedSequencerCommits(hashPool, workingTreeState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -181,7 +182,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
|
||||||
}
|
}
|
||||||
|
|
||||||
if workingTreeState.Rebasing {
|
if workingTreeState.Rebasing {
|
||||||
rebasingCommits, err := self.getHydratedRebasingCommits(addConflictedRebasingCommit)
|
rebasingCommits, err := self.getHydratedRebasingCommits(hashPool, addConflictedRebasingCommit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -196,7 +197,7 @@ func (self *CommitLoader) MergeRebasingCommits(commits []*models.Commit) ([]*mod
|
||||||
// then puts them into a commit object
|
// then puts them into a commit object
|
||||||
// example input:
|
// example input:
|
||||||
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
|
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
|
||||||
func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool) *models.Commit {
|
func (self *CommitLoader) extractCommitFromLine(hashPool *utils.StringPool, line string, showDivergence bool) *models.Commit {
|
||||||
split := strings.SplitN(line, "\x00", 8)
|
split := strings.SplitN(line, "\x00", 8)
|
||||||
|
|
||||||
hash := split[0]
|
hash := split[0]
|
||||||
|
@ -234,7 +235,7 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
|
||||||
parents = strings.Split(parentHashes, " ")
|
parents = strings.Split(parentHashes, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.NewCommit(models.NewCommitOpts{
|
return models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
Name: message,
|
Name: message,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
|
@ -247,13 +248,13 @@ func (self *CommitLoader) extractCommitFromLine(line string, showDivergence bool
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getHydratedRebasingCommits(addConflictingCommit bool) ([]*models.Commit, error) {
|
func (self *CommitLoader) getHydratedRebasingCommits(hashPool *utils.StringPool, addConflictingCommit bool) ([]*models.Commit, error) {
|
||||||
todoFileHasShortHashes := self.version.IsOlderThan(2, 25, 2)
|
todoFileHasShortHashes := self.version.IsOlderThan(2, 25, 2)
|
||||||
return self.getHydratedTodoCommits(self.getRebasingCommits(addConflictingCommit), todoFileHasShortHashes)
|
return self.getHydratedTodoCommits(hashPool, self.getRebasingCommits(hashPool, addConflictingCommit), todoFileHasShortHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getHydratedSequencerCommits(workingTreeState models.WorkingTreeState) ([]*models.Commit, error) {
|
func (self *CommitLoader) getHydratedSequencerCommits(hashPool *utils.StringPool, workingTreeState models.WorkingTreeState) ([]*models.Commit, error) {
|
||||||
commits := self.getSequencerCommits()
|
commits := self.getSequencerCommits(hashPool)
|
||||||
if len(commits) > 0 {
|
if len(commits) > 0 {
|
||||||
// If we have any commits in .git/sequencer/todo, then the last one of
|
// If we have any commits in .git/sequencer/todo, then the last one of
|
||||||
// those is the conflicting one.
|
// those is the conflicting one.
|
||||||
|
@ -262,16 +263,16 @@ func (self *CommitLoader) getHydratedSequencerCommits(workingTreeState models.Wo
|
||||||
// For single-commit cherry-picks and reverts, git apparently doesn't
|
// For single-commit cherry-picks and reverts, git apparently doesn't
|
||||||
// use the sequencer; in that case, CHERRY_PICK_HEAD or REVERT_HEAD is
|
// use the sequencer; in that case, CHERRY_PICK_HEAD or REVERT_HEAD is
|
||||||
// our conflicting commit, so synthesize it here.
|
// our conflicting commit, so synthesize it here.
|
||||||
conflicedCommit := self.getConflictedSequencerCommit(workingTreeState)
|
conflicedCommit := self.getConflictedSequencerCommit(hashPool, workingTreeState)
|
||||||
if conflicedCommit != nil {
|
if conflicedCommit != nil {
|
||||||
commits = append(commits, conflicedCommit)
|
commits = append(commits, conflicedCommit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.getHydratedTodoCommits(commits, true)
|
return self.getHydratedTodoCommits(hashPool, commits, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getHydratedTodoCommits(todoCommits []*models.Commit, todoFileHasShortHashes bool) ([]*models.Commit, error) {
|
func (self *CommitLoader) getHydratedTodoCommits(hashPool *utils.StringPool, todoCommits []*models.Commit, todoFileHasShortHashes bool) ([]*models.Commit, error) {
|
||||||
if len(todoCommits) == 0 {
|
if len(todoCommits) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -292,7 +293,7 @@ func (self *CommitLoader) getHydratedTodoCommits(todoCommits []*models.Commit, t
|
||||||
|
|
||||||
fullCommits := map[string]*models.Commit{}
|
fullCommits := map[string]*models.Commit{}
|
||||||
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||||
commit := self.extractCommitFromLine(line, false)
|
commit := self.extractCommitFromLine(hashPool, line, false)
|
||||||
fullCommits[commit.Hash()] = commit
|
fullCommits[commit.Hash()] = commit
|
||||||
return false, nil
|
return false, nil
|
||||||
})
|
})
|
||||||
|
@ -331,7 +332,7 @@ func (self *CommitLoader) getHydratedTodoCommits(todoCommits []*models.Commit, t
|
||||||
// git-rebase-todo example:
|
// git-rebase-todo example:
|
||||||
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
|
// pick ac446ae94ee560bdb8d1d057278657b251aaef17 ac446ae
|
||||||
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
|
// pick afb893148791a2fbd8091aeb81deba4930c73031 afb8931
|
||||||
func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*models.Commit {
|
func (self *CommitLoader) getRebasingCommits(hashPool *utils.StringPool, addConflictingCommit bool) []*models.Commit {
|
||||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
|
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error()))
|
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
|
||||||
// See if the current commit couldn't be applied because it conflicted; if
|
// See if the current commit couldn't be applied because it conflicted; if
|
||||||
// so, add a fake entry for it
|
// so, add a fake entry for it
|
||||||
if addConflictingCommit {
|
if addConflictingCommit {
|
||||||
if conflictedCommit := self.getConflictedCommit(todos); conflictedCommit != nil {
|
if conflictedCommit := self.getConflictedCommit(hashPool, todos); conflictedCommit != nil {
|
||||||
commits = append(commits, conflictedCommit)
|
commits = append(commits, conflictedCommit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +365,7 @@ func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*model
|
||||||
// Command does not have a commit associated, skip
|
// Command does not have a commit associated, skip
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
commits = utils.Prepend(commits, models.NewCommit(models.NewCommitOpts{
|
commits = utils.Prepend(commits, models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: t.Commit,
|
Hash: t.Commit,
|
||||||
Name: t.Msg,
|
Name: t.Msg,
|
||||||
Status: models.StatusRebasing,
|
Status: models.StatusRebasing,
|
||||||
|
@ -375,7 +376,7 @@ func (self *CommitLoader) getRebasingCommits(addConflictingCommit bool) []*model
|
||||||
return commits
|
return commits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) *models.Commit {
|
func (self *CommitLoader) getConflictedCommit(hashPool *utils.StringPool, todos []todo.Todo) *models.Commit {
|
||||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/done"))
|
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/done"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Log.Error(fmt.Sprintf("error occurred reading rebase-merge/done: %s", err.Error()))
|
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
|
||||||
amendFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/amend"))
|
amendFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/amend"))
|
||||||
messageFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/message"))
|
messageFileExists, _ := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/message"))
|
||||||
|
|
||||||
return self.getConflictedCommitImpl(todos, doneTodos, amendFileExists, messageFileExists)
|
return self.getConflictedCommitImpl(hashPool, todos, doneTodos, amendFileExists, messageFileExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos []todo.Todo, amendFileExists bool, messageFileExists bool) *models.Commit {
|
func (self *CommitLoader) getConflictedCommitImpl(hashPool *utils.StringPool, todos []todo.Todo, doneTodos []todo.Todo, amendFileExists bool, messageFileExists bool) *models.Commit {
|
||||||
// Should never be possible, but just to be safe:
|
// Should never be possible, but just to be safe:
|
||||||
if len(doneTodos) == 0 {
|
if len(doneTodos) == 0 {
|
||||||
self.Log.Error("no done entries in rebase-merge/done file")
|
self.Log.Error("no done entries in rebase-merge/done file")
|
||||||
|
@ -465,14 +466,14 @@ func (self *CommitLoader) getConflictedCommitImpl(todos []todo.Todo, doneTodos [
|
||||||
|
|
||||||
// Any other todo that has a commit associated with it must have failed with
|
// Any other todo that has a commit associated with it must have failed with
|
||||||
// a conflict, otherwise we wouldn't have stopped the rebase:
|
// a conflict, otherwise we wouldn't have stopped the rebase:
|
||||||
return models.NewCommit(models.NewCommitOpts{
|
return models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: lastTodo.Commit,
|
Hash: lastTodo.Commit,
|
||||||
Action: lastTodo.Command,
|
Action: lastTodo.Command,
|
||||||
Status: models.StatusConflicted,
|
Status: models.StatusConflicted,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getSequencerCommits() []*models.Commit {
|
func (self *CommitLoader) getSequencerCommits(hashPool *utils.StringPool) []*models.Commit {
|
||||||
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "sequencer/todo"))
|
bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "sequencer/todo"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Log.Error(fmt.Sprintf("error occurred reading sequencer/todo: %s", err.Error()))
|
self.Log.Error(fmt.Sprintf("error occurred reading sequencer/todo: %s", err.Error()))
|
||||||
|
@ -493,7 +494,7 @@ func (self *CommitLoader) getSequencerCommits() []*models.Commit {
|
||||||
// Command does not have a commit associated, skip
|
// Command does not have a commit associated, skip
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
commits = utils.Prepend(commits, models.NewCommit(models.NewCommitOpts{
|
commits = utils.Prepend(commits, models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: t.Commit,
|
Hash: t.Commit,
|
||||||
Name: t.Msg,
|
Name: t.Msg,
|
||||||
Status: models.StatusCherryPickingOrReverting,
|
Status: models.StatusCherryPickingOrReverting,
|
||||||
|
@ -504,7 +505,7 @@ func (self *CommitLoader) getSequencerCommits() []*models.Commit {
|
||||||
return commits
|
return commits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitLoader) getConflictedSequencerCommit(workingTreeState models.WorkingTreeState) *models.Commit {
|
func (self *CommitLoader) getConflictedSequencerCommit(hashPool *utils.StringPool, workingTreeState models.WorkingTreeState) *models.Commit {
|
||||||
var shaFile string
|
var shaFile string
|
||||||
var action todo.TodoCommand
|
var action todo.TodoCommand
|
||||||
if workingTreeState.CherryPicking {
|
if workingTreeState.CherryPicking {
|
||||||
|
@ -526,7 +527,7 @@ func (self *CommitLoader) getConflictedSequencerCommit(workingTreeState models.W
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return models.NewCommit(models.NewCommitOpts{
|
return models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: lines[0],
|
Hash: lines[0],
|
||||||
Status: models.StatusConflicted,
|
Status: models.StatusConflicted,
|
||||||
Action: action,
|
Action: action,
|
||||||
|
|
|
@ -314,13 +314,16 @@ func TestGetCommits(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
common.UserConfig().Git.MainBranches = scenario.mainBranches
|
common.UserConfig().Git.MainBranches = scenario.mainBranches
|
||||||
opts := scenario.opts
|
opts := scenario.opts
|
||||||
opts.MainBranches = NewMainBranches(common, cmd)
|
opts.MainBranches = NewMainBranches(common, cmd)
|
||||||
|
opts.HashPool = hashPool
|
||||||
commits, err := builder.GetCommits(opts)
|
commits, err := builder.GetCommits(opts)
|
||||||
|
|
||||||
expectedCommits := lo.Map(scenario.expectedCommitOpts,
|
expectedCommits := lo.Map(scenario.expectedCommitOpts,
|
||||||
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
|
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
|
||||||
assert.Equal(t, expectedCommits, commits)
|
assert.Equal(t, expectedCommits, commits)
|
||||||
assert.Equal(t, scenario.expectedError, err)
|
assert.Equal(t, scenario.expectedError, err)
|
||||||
|
|
||||||
|
@ -330,6 +333,8 @@ func TestGetCommits(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
testName string
|
testName string
|
||||||
todos []todo.Todo
|
todos []todo.Todo
|
||||||
|
@ -359,7 +364,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
amendFileExists: false,
|
amendFileExists: false,
|
||||||
expectedResult: models.NewCommit(models.NewCommitOpts{
|
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "fa1afe1",
|
Hash: "fa1afe1",
|
||||||
Action: todo.Pick,
|
Action: todo.Pick,
|
||||||
Status: models.StatusConflicted,
|
Status: models.StatusConflicted,
|
||||||
|
@ -460,7 +465,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
amendFileExists: false,
|
amendFileExists: false,
|
||||||
expectedResult: models.NewCommit(models.NewCommitOpts{
|
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "fa1afe1",
|
Hash: "fa1afe1",
|
||||||
Action: todo.Pick,
|
Action: todo.Pick,
|
||||||
Status: models.StatusConflicted,
|
Status: models.StatusConflicted,
|
||||||
|
@ -489,7 +494,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||||
},
|
},
|
||||||
amendFileExists: false,
|
amendFileExists: false,
|
||||||
messageFileExists: true,
|
messageFileExists: true,
|
||||||
expectedResult: models.NewCommit(models.NewCommitOpts{
|
expectedResult: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "fa1afe1",
|
Hash: "fa1afe1",
|
||||||
Action: todo.Edit,
|
Action: todo.Edit,
|
||||||
Status: models.StatusConflicted,
|
Status: models.StatusConflicted,
|
||||||
|
@ -526,7 +531,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := builder.getConflictedCommitImpl(scenario.todos, scenario.doneTodos, scenario.amendFileExists, scenario.messageFileExists)
|
hash := builder.getConflictedCommitImpl(hashPool, scenario.todos, scenario.doneTodos, scenario.amendFileExists, scenario.messageFileExists)
|
||||||
assert.Equal(t, scenario.expectedResult, hash)
|
assert.Equal(t, scenario.expectedResult, hash)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -573,11 +578,13 @@ func TestCommitLoader_setCommitMergedStatuses(t *testing.T) {
|
||||||
|
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
t.Run(scenario.testName, func(t *testing.T) {
|
t.Run(scenario.testName, func(t *testing.T) {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
commits := lo.Map(scenario.commitOpts,
|
commits := lo.Map(scenario.commitOpts,
|
||||||
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
|
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
|
||||||
setCommitMergedStatuses(scenario.ancestor, commits)
|
setCommitMergedStatuses(scenario.ancestor, commits)
|
||||||
expectedCommits := lo.Map(scenario.expectedCommitOpts,
|
expectedCommits := lo.Map(scenario.expectedCommitOpts,
|
||||||
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
|
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
|
||||||
assert.Equal(t, expectedCommits, commits)
|
assert.Equal(t, expectedCommits, commits)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -158,8 +159,9 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||||
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
commits := lo.Map(s.commitOpts,
|
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) })
|
||||||
|
|
||||||
s.test(instance.DiscardOldFileChanges(commits, s.commitIndex, s.fileName))
|
s.test(instance.DiscardOldFileChanges(commits, s.commitIndex, s.fileName))
|
||||||
s.runner.CheckForMissingCalls()
|
s.runner.CheckForMissingCalls()
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ReflogCommitLoader struct {
|
type ReflogCommitLoader struct {
|
||||||
|
@ -23,7 +24,7 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
|
||||||
|
|
||||||
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
// GetReflogCommits only returns the new reflog commits since the given lastReflogCommit
|
||||||
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
|
// if none is passed (i.e. it's value is nil) then we get all the reflog commits
|
||||||
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
|
func (self *ReflogCommitLoader) GetReflogCommits(hashPool *utils.StringPool, lastReflogCommit *models.Commit, filterPath string, filterAuthor string) ([]*models.Commit, bool, error) {
|
||||||
commits := make([]*models.Commit, 0)
|
commits := make([]*models.Commit, 0)
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("log").
|
cmdArgs := NewGitCmd("log").
|
||||||
|
@ -39,7 +40,7 @@ func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit
|
||||||
|
|
||||||
onlyObtainedNewReflogCommits := false
|
onlyObtainedNewReflogCommits := false
|
||||||
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
|
||||||
commit, ok := self.parseLine(line)
|
commit, ok := self.parseLine(hashPool, line)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -68,7 +69,7 @@ func (self *ReflogCommitLoader) sameReflogCommit(a *models.Commit, b *models.Com
|
||||||
return a.Hash() == b.Hash() && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
|
return a.Hash() == b.Hash() && a.UnixTimestamp == b.UnixTimestamp && a.Name == b.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
|
func (self *ReflogCommitLoader) parseLine(hashPool *utils.StringPool, line string) (*models.Commit, bool) {
|
||||||
fields := strings.SplitN(line, "\x00", 4)
|
fields := strings.SplitN(line, "\x00", 4)
|
||||||
if len(fields) <= 3 {
|
if len(fields) <= 3 {
|
||||||
return nil, false
|
return nil, false
|
||||||
|
@ -82,7 +83,7 @@ func (self *ReflogCommitLoader) parseLine(line string) (*models.Commit, bool) {
|
||||||
parents = strings.Split(parentHashes, " ")
|
parents = strings.Split(parentHashes, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.NewCommit(models.NewCommitOpts{
|
return models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: fields[0],
|
Hash: fields[0],
|
||||||
Name: fields[2],
|
Name: fields[2],
|
||||||
UnixTimestamp: int64(unixTimestamp),
|
UnixTimestamp: int64(unixTimestamp),
|
||||||
|
|
|
@ -32,6 +32,8 @@ func TestGetReflogCommits(t *testing.T) {
|
||||||
expectedError error
|
expectedError error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "no reflog entries",
|
testName: "no reflog entries",
|
||||||
|
@ -94,7 +96,7 @@ func TestGetReflogCommits(t *testing.T) {
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: models.NewCommit(models.NewCommitOpts{
|
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "c3c4b66b64c97ffeecde",
|
Hash: "c3c4b66b64c97ffeecde",
|
||||||
Name: "checkout: moving from B to A",
|
Name: "checkout: moving from B to A",
|
||||||
Status: models.StatusReflog,
|
Status: models.StatusReflog,
|
||||||
|
@ -118,7 +120,7 @@ func TestGetReflogCommits(t *testing.T) {
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: models.NewCommit(models.NewCommitOpts{
|
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "c3c4b66b64c97ffeecde",
|
Hash: "c3c4b66b64c97ffeecde",
|
||||||
Name: "checkout: moving from B to A",
|
Name: "checkout: moving from B to A",
|
||||||
Status: models.StatusReflog,
|
Status: models.StatusReflog,
|
||||||
|
@ -143,7 +145,7 @@ func TestGetReflogCommits(t *testing.T) {
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
|
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--author=John Doe <john@doe.com>"}, reflogOutput, nil),
|
||||||
|
|
||||||
lastReflogCommit: models.NewCommit(models.NewCommitOpts{
|
lastReflogCommit: models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: "c3c4b66b64c97ffeecde",
|
Hash: "c3c4b66b64c97ffeecde",
|
||||||
Name: "checkout: moving from B to A",
|
Name: "checkout: moving from B to A",
|
||||||
Status: models.StatusReflog,
|
Status: models.StatusReflog,
|
||||||
|
@ -183,14 +185,14 @@ func TestGetReflogCommits(t *testing.T) {
|
||||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||||
}
|
}
|
||||||
|
|
||||||
commits, onlyObtainednew, err := builder.GetReflogCommits(scenario.lastReflogCommit, scenario.filterPath, scenario.filterAuthor)
|
commits, onlyObtainednew, err := builder.GetReflogCommits(hashPool, scenario.lastReflogCommit, scenario.filterPath, scenario.filterAuthor)
|
||||||
assert.Equal(t, scenario.expectedOnlyObtainedNew, onlyObtainednew)
|
assert.Equal(t, scenario.expectedOnlyObtainedNew, onlyObtainednew)
|
||||||
assert.Equal(t, scenario.expectedError, err)
|
assert.Equal(t, scenario.expectedError, err)
|
||||||
t.Logf("actual commits: \n%s", litter.Sdump(commits))
|
t.Logf("actual commits: \n%s", litter.Sdump(commits))
|
||||||
var expectedCommits []*models.Commit
|
var expectedCommits []*models.Commit
|
||||||
if scenario.expectedCommitOpts != nil {
|
if scenario.expectedCommitOpts != nil {
|
||||||
expectedCommits = lo.Map(scenario.expectedCommitOpts,
|
expectedCommits = lo.Map(scenario.expectedCommitOpts,
|
||||||
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(opts) })
|
func(opts models.NewCommitOpts, _ int) *models.Commit { return models.NewCommit(hashPool, opts) })
|
||||||
}
|
}
|
||||||
assert.Equal(t, expectedCommits, commits)
|
assert.Equal(t, expectedCommits, commits)
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ const (
|
||||||
|
|
||||||
// Commit : A git commit
|
// Commit : A git commit
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
hash string
|
hash *string
|
||||||
Name string
|
Name string
|
||||||
Status CommitStatus
|
Status CommitStatus
|
||||||
Action todo.TodoCommand
|
Action todo.TodoCommand
|
||||||
|
@ -71,9 +71,9 @@ type NewCommitOpts struct {
|
||||||
Parents []string
|
Parents []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommit(opts NewCommitOpts) *Commit {
|
func NewCommit(hashPool *utils.StringPool, opts NewCommitOpts) *Commit {
|
||||||
return &Commit{
|
return &Commit{
|
||||||
hash: opts.Hash,
|
hash: hashPool.Add(opts.Hash),
|
||||||
Name: opts.Name,
|
Name: opts.Name,
|
||||||
Status: opts.Status,
|
Status: opts.Status,
|
||||||
Action: opts.Action,
|
Action: opts.Action,
|
||||||
|
@ -88,7 +88,7 @@ func NewCommit(opts NewCommitOpts) *Commit {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commit) Hash() string {
|
func (c *Commit) Hash() string {
|
||||||
return c.hash
|
return *c.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commit) ShortHash() string {
|
func (c *Commit) ShortHash() string {
|
||||||
|
|
|
@ -332,6 +332,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
|
||||||
RefForPushedStatus: checkedOutBranchName,
|
RefForPushedStatus: checkedOutBranchName,
|
||||||
All: self.c.Contexts().LocalCommits.GetShowWholeGitGraph(),
|
All: self.c.Contexts().LocalCommits.GetShowWholeGitGraph(),
|
||||||
MainBranches: self.c.Model().MainBranches,
|
MainBranches: self.c.Model().MainBranches,
|
||||||
|
HashPool: self.c.Model().HashPool,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -360,6 +361,7 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error {
|
||||||
RefToShowDivergenceFrom: self.c.Contexts().SubCommits.GetRefToShowDivergenceFrom(),
|
RefToShowDivergenceFrom: self.c.Contexts().SubCommits.GetRefToShowDivergenceFrom(),
|
||||||
RefForPushedStatus: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
RefForPushedStatus: self.c.Contexts().SubCommits.GetRef().FullRefName(),
|
||||||
MainBranches: self.c.Model().MainBranches,
|
MainBranches: self.c.Model().MainBranches,
|
||||||
|
HashPool: self.c.Model().HashPool,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -406,7 +408,7 @@ func (self *RefreshHelper) refreshRebaseCommits() error {
|
||||||
self.c.Mutexes().LocalCommitsMutex.Lock()
|
self.c.Mutexes().LocalCommitsMutex.Lock()
|
||||||
defer self.c.Mutexes().LocalCommitsMutex.Unlock()
|
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 {
|
if err != nil {
|
||||||
return err
|
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
|
// which allows us to order them correctly. So if we're filtering we'll just
|
||||||
// manually load all the reflog commits here
|
// manually load all the reflog commits here
|
||||||
var err error
|
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 {
|
if err != nil {
|
||||||
self.c.Log.Error(err)
|
self.c.Log.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -624,7 +626,7 @@ func (self *RefreshHelper) refreshReflogCommits() error {
|
||||||
|
|
||||||
refresh := func(stateCommits *[]*models.Commit, filterPath string, filterAuthor string) error {
|
refresh := func(stateCommits *[]*models.Commit, filterPath string, filterAuthor string) error {
|
||||||
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
|
commits, onlyObtainedNewReflogCommits, err := self.c.Git().Loaders.ReflogCommitLoader.
|
||||||
GetReflogCommits(lastReflogCommit, filterPath, filterAuthor)
|
GetReflogCommits(self.c.Model().HashPool, lastReflogCommit, filterPath, filterAuthor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
|
||||||
RefForPushedStatus: opts.Ref.FullRefName(),
|
RefForPushedStatus: opts.Ref.FullRefName(),
|
||||||
RefToShowDivergenceFrom: opts.RefToShowDivergenceFrom,
|
RefToShowDivergenceFrom: opts.RefToShowDivergenceFrom,
|
||||||
MainBranches: self.c.Model().MainBranches,
|
MainBranches: self.c.Model().MainBranches,
|
||||||
|
HashPool: self.c.Model().HashPool,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -564,7 +564,7 @@ func (self *LocalCommitsController) startInteractiveRebaseWithEdit(
|
||||||
for _, c := range commitsToEdit[:len(commitsToEdit)-1] {
|
for _, c := range commitsToEdit[:len(commitsToEdit)-1] {
|
||||||
// Merge commits can't be set to "edit", so just skip them
|
// Merge commits can't be set to "edit", so just skip them
|
||||||
if !c.IsMerge() {
|
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 {
|
if len(todos) > 0 {
|
||||||
|
|
|
@ -558,6 +558,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs) types.Context {
|
||||||
FilesTrie: patricia.NewTrie(),
|
FilesTrie: patricia.NewTrie(),
|
||||||
Authors: map[string]*models.Author{},
|
Authors: map[string]*models.Author{},
|
||||||
MainBranches: git_commands.NewMainBranches(gui.c.Common, gui.os.Cmd),
|
MainBranches: git_commands.NewMainBranches(gui.c.Common, gui.os.Cmd),
|
||||||
|
HashPool: &utils.StringPool{},
|
||||||
},
|
},
|
||||||
Modes: &types.Modes{
|
Modes: &types.Modes{
|
||||||
Filtering: filtering.New(startArgs.FilterPath, ""),
|
Filtering: filtering.New(startArgs.FilterPath, ""),
|
||||||
|
|
|
@ -544,8 +544,10 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
if !focusing || s.focus {
|
if !focusing || s.focus {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
commits := lo.Map(s.commitOpts,
|
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(
|
result := GetCommitListDisplayStrings(
|
||||||
common,
|
common,
|
||||||
|
|
|
@ -149,8 +149,10 @@ func TestRenderCommitFileTree(t *testing.T) {
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
viewModel := filetree.NewCommitFileTreeViewModel(func() []*models.CommitFile { return s.files }, utils.NewDummyLog(), true)
|
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()
|
viewModel.SetTree()
|
||||||
for _, path := range s.collapsedPaths {
|
for _, path := range s.collapsedPaths {
|
||||||
viewModel.ToggleCollapsed(path)
|
viewModel.ToggleCollapsed(path)
|
||||||
|
|
|
@ -219,9 +219,11 @@ func TestRenderCommitGraph(t *testing.T) {
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
getStyle := func(c *models.Commit) style.TextStyle { return style.FgDefault }
|
getStyle := func(c *models.Commit) style.TextStyle { return style.FgDefault }
|
||||||
commits := lo.Map(test.commitOpts,
|
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)
|
lines := RenderCommitGraph(commits, "blah", getStyle)
|
||||||
|
|
||||||
trimmedExpectedOutput := ""
|
trimmedExpectedOutput := ""
|
||||||
|
@ -254,6 +256,8 @@ func TestRenderPipeSet(t *testing.T) {
|
||||||
magenta := style.FgMagenta
|
magenta := style.FgMagenta
|
||||||
nothing := style.Nothing
|
nothing := style.Nothing
|
||||||
|
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pipes []*Pipe
|
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: "a", toHash: "b", kind: TERMINATES, style: cyan},
|
||||||
{fromPos: 0, toPos: 0, fromHash: "b", toHash: "c", kind: STARTS, style: green},
|
{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: "◯",
|
expectedStr: "◯",
|
||||||
expectedStyles: []style.TextStyle{green},
|
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: "a", toHash: "selected", kind: TERMINATES, style: cyan},
|
||||||
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "c", kind: STARTS, style: green},
|
{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: "◯",
|
expectedStr: "◯",
|
||||||
expectedStyles: []style.TextStyle{highlightStyle},
|
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: 0, fromHash: "selected", toHash: "d", kind: STARTS, style: green},
|
||||||
{fromPos: 0, toPos: 1, fromHash: "selected", toHash: "e", 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: "⏣─╮",
|
expectedStr: "⏣─╮",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
highlightStyle, highlightStyle, highlightStyle,
|
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: 0, fromHash: "b", toHash: "d", kind: STARTS, style: green},
|
||||||
{fromPos: 0, toPos: 1, fromHash: "b", toHash: "e", 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: "⏣─│",
|
expectedStr: "⏣─│",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
green, green, magenta,
|
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: 3, toPos: 0, fromHash: "e1", toHash: "a2", kind: TERMINATES, style: green},
|
||||||
{fromPos: 0, toPos: 2, fromHash: "a2", toHash: "c3", kind: STARTS, style: yellow},
|
{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: "⏣─│─┬─╯",
|
expectedStr: "⏣─│─┬─╯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
yellow, yellow, magenta, yellow, yellow, green, green,
|
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: 3, toPos: 0, fromHash: "e1", toHash: "selected", kind: TERMINATES, style: green},
|
||||||
{fromPos: 0, toPos: 2, fromHash: "selected", toHash: "c3", kind: STARTS, style: yellow},
|
{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: "⏣───╮ ╯",
|
expectedStr: "⏣───╮ ╯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
highlightStyle, highlightStyle, highlightStyle, highlightStyle, highlightStyle, nothing, green,
|
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: 1, toPos: 0, fromHash: "b1", toHash: "a2", kind: TERMINATES, style: magenta},
|
||||||
{fromPos: 2, toPos: 0, fromHash: "c1", toHash: "a2", kind: TERMINATES, style: green},
|
{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: "◯─┴─╯",
|
expectedStr: "◯─┴─╯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
yellow, magenta, magenta, green, green,
|
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: 1, toPos: 1, fromHash: "b1", toHash: "b3", kind: CONTINUES, style: magenta},
|
||||||
{fromPos: 2, toPos: 2, fromHash: "c1", toHash: "c3", kind: CONTINUES, style: green},
|
{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: "⏣─│─│─╮",
|
expectedStr: "⏣─│─│─╮",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
yellow, yellow, magenta, yellow, green, yellow, yellow,
|
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: 1, toPos: 1, fromHash: "b1", toHash: "a2", kind: CONTINUES, style: green},
|
||||||
{fromPos: 2, toPos: 0, fromHash: "c1", toHash: "a2", kind: TERMINATES, style: magenta},
|
{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: "⏣─│─╯",
|
expectedStr: "⏣─│─╯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
yellow, yellow, green, magenta, magenta,
|
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: 2, toPos: 2, fromHash: "c1", toHash: "c3", kind: CONTINUES, style: green},
|
||||||
{fromPos: 3, toPos: 0, fromHash: "d1", toHash: "a2", kind: TERMINATES, style: magenta},
|
{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: "⏣─┬─│─╯",
|
expectedStr: "⏣─┬─│─╯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
yellow, yellow, yellow, magenta, green, magenta, magenta,
|
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: "selected", toHash: "a2", kind: TERMINATES, style: red},
|
||||||
{fromPos: 0, toPos: 0, fromHash: "a2", toHash: "a3", kind: STARTS, style: yellow},
|
{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: "◯",
|
expectedStr: "◯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
yellow,
|
yellow,
|
||||||
|
@ -417,7 +421,7 @@ func TestRenderPipeSet(t *testing.T) {
|
||||||
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "a2", kind: TERMINATES, style: red},
|
{fromPos: 0, toPos: 0, fromHash: "selected", toHash: "a2", kind: TERMINATES, style: red},
|
||||||
{fromPos: 1, toPos: 1, fromHash: "selected", toHash: "b3", kind: CONTINUES, 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: "◯ │",
|
expectedStr: "◯ │",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
highlightStyle, nothing, highlightStyle,
|
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: 1, toPos: 1, fromHash: "z1", toHash: "z3", kind: CONTINUES, style: green},
|
||||||
{fromPos: 2, toPos: 2, fromHash: "selected", toHash: "b3", kind: CONTINUES, style: red},
|
{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: "◯ │ │",
|
expectedStr: "◯ │ │",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
highlightStyle, nothing, green, nothing, highlightStyle,
|
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: 0, toPos: 1, fromHash: "a2", toHash: "b3", kind: STARTS, style: green},
|
||||||
{fromPos: 1, toPos: 0, fromHash: "selected", toHash: "a2", kind: TERMINATES, style: yellow},
|
{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: "⏣─╯",
|
expectedStr: "⏣─╯",
|
||||||
expectedStyles: []style.TextStyle{
|
expectedStyles: []style.TextStyle{
|
||||||
highlightStyle, highlightStyle, highlightStyle,
|
highlightStyle, highlightStyle, highlightStyle,
|
||||||
|
@ -477,6 +481,8 @@ func TestRenderPipeSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetNextPipes(t *testing.T) {
|
func TestGetNextPipes(t *testing.T) {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
prevPipes []*Pipe
|
prevPipes []*Pipe
|
||||||
commit *models.Commit
|
commit *models.Commit
|
||||||
|
@ -486,7 +492,7 @@ func TestGetNextPipes(t *testing.T) {
|
||||||
prevPipes: []*Pipe{
|
prevPipes: []*Pipe{
|
||||||
{fromPos: 0, toPos: 0, fromHash: "a", toHash: "b", kind: STARTS, style: style.FgDefault},
|
{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",
|
Hash: "b",
|
||||||
Parents: []string{"c"},
|
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: 0, fromHash: "b", toHash: "c", kind: STARTS, style: style.FgDefault},
|
||||||
{fromPos: 0, toPos: 1, fromHash: "b", toHash: "d", 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",
|
Hash: "d",
|
||||||
Parents: []string{"e"},
|
Parents: []string{"e"},
|
||||||
}),
|
}),
|
||||||
|
@ -515,7 +521,7 @@ func TestGetNextPipes(t *testing.T) {
|
||||||
prevPipes: []*Pipe{
|
prevPipes: []*Pipe{
|
||||||
{fromPos: 0, toPos: 0, fromHash: "a", toHash: "root", kind: TERMINATES, style: style.FgDefault},
|
{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",
|
Hash: "root",
|
||||||
Parents: []string{},
|
Parents: []string{},
|
||||||
}),
|
}),
|
||||||
|
@ -557,8 +563,10 @@ func BenchmarkRenderCommitGraph(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCommits(count int) []*models.Commit {
|
func generateCommits(count int) []*models.Commit {
|
||||||
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
rnd := rand.New(rand.NewSource(1234))
|
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)
|
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"}
|
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 {
|
for len(commits) < count {
|
||||||
|
@ -575,7 +583,7 @@ func generateCommits(count int) []*models.Commit {
|
||||||
if reuseParent {
|
if reuseParent {
|
||||||
newParent = pool[j]
|
newParent = pool[j]
|
||||||
} else {
|
} else {
|
||||||
newParent = models.NewCommit(models.NewCommitOpts{
|
newParent = models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: fmt.Sprintf("%s%d", currentCommit.Hash(), j),
|
Hash: fmt.Sprintf("%s%d", currentCommit.Hash(), j),
|
||||||
AuthorName: authorPool[rnd.Intn(len(authorPool))],
|
AuthorName: authorPool[rnd.Intn(len(authorPool))],
|
||||||
})
|
})
|
||||||
|
@ -584,7 +592,7 @@ func generateCommits(count int) []*models.Commit {
|
||||||
parentHashes = append(parentHashes, newParent.Hash())
|
parentHashes = append(parentHashes, newParent.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
changedCommit := models.NewCommit(models.NewCommitOpts{
|
changedCommit := models.NewCommit(hashPool, models.NewCommitOpts{
|
||||||
Hash: currentCommit.Hash(),
|
Hash: currentCommit.Hash(),
|
||||||
AuthorName: currentCommit.AuthorName,
|
AuthorName: currentCommit.AuthorName,
|
||||||
Parents: parentHashes,
|
Parents: parentHashes,
|
||||||
|
|
|
@ -305,6 +305,8 @@ type Model struct {
|
||||||
FilesTrie *patricia.Trie
|
FilesTrie *patricia.Trie
|
||||||
|
|
||||||
Authors map[string]*models.Author
|
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
|
// if you add a new mutex here be sure to instantiate it. We're using pointers to
|
||||||
|
|
14
pkg/utils/string_pool.go
Normal file
14
pkg/utils/string_pool.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// A simple string pool implementation that can help reduce memory usage for
|
||||||
|
// cases where the same string is used multiple times.
|
||||||
|
type StringPool struct {
|
||||||
|
sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StringPool) Add(s string) *string {
|
||||||
|
poolEntry, _ := self.LoadOrStore(s, &s)
|
||||||
|
return poolEntry.(*string)
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue