From 3cee37388c8d1688b2c8ff0e5b5509663b096952 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 22 Jul 2023 10:33:40 +1000 Subject: [PATCH] Keep track of authors across local commits and branch commits for suggestions Previously, we would only show the authors based on local commits, but sometimes you want to set a commit author to that of a commit on another branch. Now, so long as you've viewed the branch's commits, the author will appear as a suggestion. --- pkg/commands/models/author.go | 13 +++++++ pkg/gui/controllers/helpers/refresh_helper.go | 17 +++++++++ .../controllers/helpers/suggestions_helper.go | 8 +++-- .../switch_to_sub_commits_controller.go | 1 + pkg/gui/gui.go | 2 ++ pkg/gui/types/common.go | 3 ++ pkg/integration/tests/commit/set_author.go | 36 ++++++++++++++++--- 7 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 pkg/commands/models/author.go diff --git a/pkg/commands/models/author.go b/pkg/commands/models/author.go new file mode 100644 index 000000000..ece9b7972 --- /dev/null +++ b/pkg/commands/models/author.go @@ -0,0 +1,13 @@ +package models + +import "fmt" + +// A commit author +type Author struct { + Name string + Email string +} + +func (self *Author) Combined() string { + return fmt.Sprintf("%s <%s>", self.Name, self.Email) +} diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 36b960c69..5a70414b6 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -266,6 +266,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error { return err } self.c.Model().Commits = commits + self.RefreshAuthors(commits) self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState() return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits) @@ -287,10 +288,26 @@ func (self *RefreshHelper) refreshSubCommitsWithLimit() error { return err } self.c.Model().SubCommits = commits + self.RefreshAuthors(commits) return self.c.PostRefreshUpdate(self.c.Contexts().SubCommits) } +func (self *RefreshHelper) RefreshAuthors(commits []*models.Commit) { + self.c.Mutexes().AuthorsMutex.Lock() + defer self.c.Mutexes().AuthorsMutex.Unlock() + + authors := self.c.Model().Authors + for _, commit := range commits { + if _, ok := authors[commit.AuthorEmail]; !ok { + authors[commit.AuthorEmail] = &models.Author{ + Email: commit.AuthorEmail, + Name: commit.AuthorName, + } + } + } +} + func (self *RefreshHelper) refreshCommitFilesContext() error { ref := self.c.Contexts().CommitFiles.GetRef() to := ref.RefName() diff --git a/pkg/gui/controllers/helpers/suggestions_helper.go b/pkg/gui/controllers/helpers/suggestions_helper.go index 783576617..65ddf70aa 100644 --- a/pkg/gui/controllers/helpers/suggestions_helper.go +++ b/pkg/gui/controllers/helpers/suggestions_helper.go @@ -176,9 +176,11 @@ func (self *SuggestionsHelper) GetRefsSuggestionsFunc() func(string) []*types.Su } func (self *SuggestionsHelper) GetAuthorsSuggestionsFunc() func(string) []*types.Suggestion { - authors := lo.Uniq(slices.Map(self.c.Model().Commits, func(commit *models.Commit) string { - return fmt.Sprintf("%s <%s>", commit.AuthorName, commit.AuthorEmail) - })) + authors := lo.Map(lo.Values(self.c.Model().Authors), func(author *models.Author, _ int) string { + return author.Combined() + }) + + slices.Sort(authors) return FuzzySearchFunc(authors) } diff --git a/pkg/gui/controllers/switch_to_sub_commits_controller.go b/pkg/gui/controllers/switch_to_sub_commits_controller.go index 8163181e5..8aa92f14b 100644 --- a/pkg/gui/controllers/switch_to_sub_commits_controller.go +++ b/pkg/gui/controllers/switch_to_sub_commits_controller.go @@ -70,6 +70,7 @@ func (self *SwitchToSubCommitsController) viewCommits() error { } self.setSubCommits(commits) + self.c.Helpers().Refresh.RefreshAuthors(commits) subCommitsContext := self.c.Contexts().SubCommits subCommitsContext.SetSelectedLineIdx(0) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 25ec769a5..9a8d96712 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -349,6 +349,7 @@ func (gui *Gui) resetState(startArgs appTypes.StartArgs, reuseState bool) types. ReflogCommits: make([]*models.Commit, 0), BisectInfo: git_commands.NewNullBisectInfo(), FilesTrie: patricia.NewTrie(), + Authors: map[string]*models.Author{}, }, Modes: &types.Modes{ Filtering: filtering.New(startArgs.FilterPath), @@ -456,6 +457,7 @@ func NewGui( SyncMutex: &deadlock.Mutex{}, LocalCommitsMutex: &deadlock.Mutex{}, SubCommitsMutex: &deadlock.Mutex{}, + AuthorsMutex: &deadlock.Mutex{}, SubprocessMutex: &deadlock.Mutex{}, PopupMutex: &deadlock.Mutex{}, PtyMutex: &deadlock.Mutex{}, diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 33b0f7a18..e485fbceb 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -217,6 +217,8 @@ type Model struct { // for displaying suggestions while typing in a file name FilesTrie *patricia.Trie + + Authors map[string]*models.Author } // if you add a new mutex here be sure to instantiate it. We're using pointers to @@ -228,6 +230,7 @@ type Mutexes struct { SyncMutex *deadlock.Mutex LocalCommitsMutex *deadlock.Mutex SubCommitsMutex *deadlock.Mutex + AuthorsMutex *deadlock.Mutex SubprocessMutex *deadlock.Mutex PopupMutex *deadlock.Mutex PtyMutex *deadlock.Mutex diff --git a/pkg/integration/tests/commit/set_author.go b/pkg/integration/tests/commit/set_author.go index ecfc201fd..e0cd722da 100644 --- a/pkg/integration/tests/commit/set_author.go +++ b/pkg/integration/tests/commit/set_author.go @@ -5,29 +5,58 @@ import ( . "github.com/jesseduffield/lazygit/pkg/integration/components" ) +// Originally we only suggested authors present in the current branch, but now +// we include authors from other branches whose commits you've looked at in the +// lazygit session. + var SetAuthor = NewIntegrationTest(NewIntegrationTestArgs{ Description: "Set author on a commit", ExtraCmdArgs: []string{}, Skip: false, SetupConfig: func(config *config.AppConfig) {}, SetupRepo: func(shell *Shell) { + shell.NewBranch("original") + shell.SetConfig("user.email", "Bill@example.com") shell.SetConfig("user.name", "Bill Smith") shell.EmptyCommit("one") + shell.NewBranch("other") + shell.SetConfig("user.email", "John@example.com") shell.SetConfig("user.name", "John Smith") shell.EmptyCommit("two") + + shell.Checkout("original") }, Run: func(t *TestDriver, keys config.KeybindingConfig) { t.Views().Commits(). Focus(). + Lines( + Contains("BS").Contains("one").IsSelected(), + ) + + t.Views().Branches(). + Focus(). + Lines( + Contains("original").IsSelected(), + Contains("other"), + ). + NavigateToLine(Contains("other")). + PressEnter() + + // ensuring we get these commit authors as suggestions + t.Views().SubCommits(). + IsFocused(). Lines( Contains("JS").Contains("two").IsSelected(), Contains("BS").Contains("one"), - ). + ) + + t.Views().Commits(). + Focus(). Press(keys.Commits.ResetCommitAuthor). Tap(func() { t.ExpectPopup().Menu(). @@ -38,14 +67,13 @@ var SetAuthor = NewIntegrationTest(NewIntegrationTestArgs{ t.ExpectPopup().Prompt(). Title(Contains("Set author")). SuggestionLines( - Contains("John Smith"), Contains("Bill Smith"), + Contains("John Smith"), ). ConfirmSuggestion(Contains("John Smith")) }). Lines( - Contains("JS").Contains("two").IsSelected(), - Contains("BS").Contains("one"), + Contains("JS").Contains("one").IsSelected(), ) }, })