From 3fe491fcb25a31268a1dd428d9a00bdcd027d155 Mon Sep 17 00:00:00 2001 From: Alex March Date: Wed, 20 Dec 2023 15:38:05 +0900 Subject: [PATCH 1/2] Implement a sort order menu for remote branches --- docs/keybindings/Keybindings_en.md | 1 + docs/keybindings/Keybindings_ja.md | 1 + docs/keybindings/Keybindings_ko.md | 1 + docs/keybindings/Keybindings_nl.md | 1 + docs/keybindings/Keybindings_pl.md | 1 + docs/keybindings/Keybindings_ru.md | 1 + docs/keybindings/Keybindings_zh-CN.md | 1 + docs/keybindings/Keybindings_zh-TW.md | 1 + pkg/commands/git_commands/remote_loader.go | 24 +++++++++----- pkg/config/app_config.go | 10 +++--- pkg/config/user_config.go | 2 ++ pkg/gui/controllers/helpers/refs_helper.go | 31 +++++++++++++++++++ .../controllers/remote_branches_controller.go | 18 +++++++++++ pkg/i18n/english.go | 6 ++++ pkg/i18n/japanese.go | 7 +++-- pkg/i18n/russian.go | 3 ++ schema/config.json | 4 +++ 17 files changed, 100 insertions(+), 13 deletions(-) diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md index 5c7d2f1f97d..e4f6fd8be03 100644 --- a/docs/keybindings/Keybindings_en.md +++ b/docs/keybindings/Keybindings_en.md @@ -258,6 +258,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Rebase checked-out branch onto this branch d: Delete remote tag u: Set as upstream of checked-out branch + s: Sort order g: View reset options w: View worktree options <enter>: View commits diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md index 2acbd3777cb..16e2d8a4dfc 100644 --- a/docs/keybindings/Keybindings_ja.md +++ b/docs/keybindings/Keybindings_ja.md @@ -323,6 +323,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Rebase checked-out branch onto this branch d: Delete remote tag u: Set as upstream of checked-out branch + s: 並び替え g: View reset options w: View worktree options <enter>: コミットを閲覧 diff --git a/docs/keybindings/Keybindings_ko.md b/docs/keybindings/Keybindings_ko.md index 68e3a25ed5a..35dd0ebc9bd 100644 --- a/docs/keybindings/Keybindings_ko.md +++ b/docs/keybindings/Keybindings_ko.md @@ -241,6 +241,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: 체크아웃된 브랜치를 이 브랜치에 리베이스 d: Delete remote tag u: Set as upstream of checked-out branch + s: Sort order g: View reset options w: View worktree options <enter>: 커밋 보기 diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md index a53ad13a875..c5fe3f3b3d1 100644 --- a/docs/keybindings/Keybindings_nl.md +++ b/docs/keybindings/Keybindings_nl.md @@ -236,6 +236,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Rebase branch d: Delete remote tag u: Stel in als upstream van uitgecheckte branch + s: Sort order g: Bekijk reset opties w: View worktree options <enter>: Bekijk commits diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index 9ffffade50a..558e3b50f4d 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -235,6 +235,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: Zmiana bazy gałęzi d: Delete remote tag u: Set as upstream of checked-out branch + s: Sort order g: Wyświetl opcje resetu w: View worktree options <enter>: View commits diff --git a/docs/keybindings/Keybindings_ru.md b/docs/keybindings/Keybindings_ru.md index 3296c8c1615..22600aea788 100644 --- a/docs/keybindings/Keybindings_ru.md +++ b/docs/keybindings/Keybindings_ru.md @@ -296,6 +296,7 @@ _Связки клавиш_ r: Перебазировать переключённую ветку на эту ветку d: Delete remote tag u: Установить как upstream-ветку переключённую ветку + s: Порядок сортировки g: Просмотреть параметры сброса w: View worktree options <enter>: Просмотреть коммиты diff --git a/docs/keybindings/Keybindings_zh-CN.md b/docs/keybindings/Keybindings_zh-CN.md index 3f9d060ca45..354ef952a36 100644 --- a/docs/keybindings/Keybindings_zh-CN.md +++ b/docs/keybindings/Keybindings_zh-CN.md @@ -337,6 +337,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ r: 将已检出的分支变基到该分支 d: Delete remote tag u: 设置为检出分支的上游 + s: Sort order g: 查看重置选项 w: View worktree options <enter>: 查看提交 diff --git a/docs/keybindings/Keybindings_zh-TW.md b/docs/keybindings/Keybindings_zh-TW.md index 78fd756872f..6e3094afd9c 100644 --- a/docs/keybindings/Keybindings_zh-TW.md +++ b/docs/keybindings/Keybindings_zh-TW.md @@ -347,6 +347,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ r: 將已檢出的分支變基至此分支 d: Delete remote tag u: 將此分支設為當前分支之上游 + s: Sort order g: 檢視重設選項 w: View worktree options <enter>: 檢視提交 diff --git a/pkg/commands/git_commands/remote_loader.go b/pkg/commands/git_commands/remote_loader.go index 6551ecb25cf..9d48374c52a 100644 --- a/pkg/commands/git_commands/remote_loader.go +++ b/pkg/commands/git_commands/remote_loader.go @@ -1,6 +1,7 @@ package git_commands import ( + "fmt" "strings" "sync" @@ -83,14 +84,23 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) { func (self *RemoteLoader) getRemoteBranchesByRemoteName() (map[string][]*models.RemoteBranch, error) { remoteBranchesByRemoteName := make(map[string][]*models.RemoteBranch) - cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv() - err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) { - // excluding lines like 'origin/HEAD -> origin/master' (there will be a separate - // line for 'origin/master') - if strings.Contains(line, "->") { - return false, nil - } + var sortOrder string + switch strings.ToLower(self.AppState.RemoteBranchSortOrder) { + case "alphabetical": + sortOrder = "refname" + case "date": + sortOrder = "-committerdate" + default: + sortOrder = "refname" + } + + cmdArgs := NewGitCmd("for-each-ref"). + Arg(fmt.Sprintf("--sort=%s", sortOrder)). + Arg("--format=%(refname:short)"). + Arg("refs/remotes"). + ToArgv() + err := self.cmd.New(cmdArgs).DontLog().RunAndProcessLines(func(line string) (bool, error) { line = strings.TrimSpace(line) split := strings.SplitN(line, "/", 2) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 54d41b695e2..927f9f30973 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -323,14 +323,16 @@ type AppState struct { HideCommandLog bool IgnoreWhitespaceInDiffView bool DiffContextSize int + RemoteBranchSortOrder string } func getDefaultAppState() *AppState { return &AppState{ - LastUpdateCheck: 0, - RecentRepos: []string{}, - StartupPopupVersion: 0, - DiffContextSize: 3, + LastUpdateCheck: 0, + RecentRepos: []string{}, + StartupPopupVersion: 0, + DiffContextSize: 3, + RemoteBranchSortOrder: "alphabetical", } } diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 3d772e778d4..749b4182240 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -389,6 +389,7 @@ type KeybindingBranchesConfig struct { PushTag string `yaml:"pushTag"` SetUpstream string `yaml:"setUpstream"` FetchRemote string `yaml:"fetchRemote"` + SortOrder string `yaml:"sortOrder"` } type KeybindingWorktreesConfig struct { @@ -781,6 +782,7 @@ func GetDefaultConfig() *UserConfig { PushTag: "P", SetUpstream: "u", FetchRemote: "f", + SortOrder: "s", }, Worktrees: KeybindingWorktreesConfig{ ViewWorktreeOptions: "w", diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index a6de0d39fe8..b4617c8a033 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -119,6 +119,37 @@ func (self *RefsHelper) ResetToRef(ref string, strength string, envVars []string return nil } +func (self *RefsHelper) CreateSortOrderMenu(onSelected func(sortOrder string) error) error { + type sortOrderWithKey struct { + key types.Key + label string + sortKey string + sortOrder string + } + sortKeys := []sortOrderWithKey{ + {label: self.c.Tr.SortAlphabetical, sortKey: "refname", sortOrder: "alphabetical", key: 'a'}, + {label: self.c.Tr.SortByDate, sortKey: "-committerdate", sortOrder: "date", key: 'd'}, + } + + menuItems := lo.Map(sortKeys, func(row sortOrderWithKey, _ int) *types.MenuItem { + return &types.MenuItem{ + LabelColumns: []string{ + row.label, + style.FgYellow.Sprintf("--sort=%s", row.sortKey), + }, + OnPress: func() error { + return onSelected(row.sortOrder) + }, + Key: row.key, + } + }) + + return self.c.Menu(types.CreateMenuOptions{ + Title: self.c.Tr.SortOrder, + Items: menuItems, + }) +} + func (self *RefsHelper) CreateGitResetMenu(ref string) error { type strengthWithKey struct { strength string diff --git a/pkg/gui/controllers/remote_branches_controller.go b/pkg/gui/controllers/remote_branches_controller.go index ffb55d5cad2..6d224474631 100644 --- a/pkg/gui/controllers/remote_branches_controller.go +++ b/pkg/gui/controllers/remote_branches_controller.go @@ -58,6 +58,12 @@ func (self *RemoteBranchesController) GetKeybindings(opts types.KeybindingsOpts) Handler: self.checkSelected(self.setAsUpstream), Description: self.c.Tr.SetAsUpstream, }, + { + Key: opts.GetKey(opts.Config.Branches.SortOrder), + Handler: self.createSortMenu, + Description: self.c.Tr.SortOrder, + OpensMenu: true, + }, { Key: opts.GetKey(opts.Config.Commits.ViewResetOptions), Handler: self.checkSelected(self.createResetMenu), @@ -121,6 +127,18 @@ func (self *RemoteBranchesController) rebase(selectedBranch *models.RemoteBranch return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranch.FullName()) } +func (self *RemoteBranchesController) createSortMenu() error { + return self.c.Helpers().Refs.CreateSortOrderMenu(func(sortOrder string) error { + if self.c.GetAppState().RemoteBranchSortOrder != sortOrder { + self.c.GetAppState().RemoteBranchSortOrder = sortOrder + self.c.SaveAppStateAndLogError() + self.c.Contexts().RemoteBranches.SetSelectedLineIdx(0) + return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.REMOTES}}) + } + return nil + }) +} + func (self *RemoteBranchesController) createResetMenu(selectedBranch *models.RemoteBranch) error { return self.c.Helpers().Refs.CreateGitResetMenu(selectedBranch.FullName()) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 32392cee7a4..260976d649e 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -555,6 +555,9 @@ type TranslationSet struct { LogMenuTitle string ToggleShowGitGraphAll string ShowGitGraph string + SortOrder string + SortAlphabetical string + SortByDate string SortCommits string CantChangeContextSizeError string OpenCommitInBrowser string @@ -1364,6 +1367,9 @@ func EnglishTranslationSet() TranslationSet { LogMenuTitle: "Commit Log Options", ToggleShowGitGraphAll: "Toggle show whole git graph (pass the `--all` flag to `git log`)", ShowGitGraph: "Show git graph", + SortOrder: "Sort order", + SortAlphabetical: "Alphabetical", + SortByDate: "Date", SortCommits: "Commit sort order", CantChangeContextSizeError: "Cannot change context while in patch building mode because we were too lazy to support it when releasing the feature. If you really want it, please let us know!", OpenCommitInBrowser: "Open commit in browser", diff --git a/pkg/i18n/japanese.go b/pkg/i18n/japanese.go index 03e4ff6f48d..089c95ed680 100644 --- a/pkg/i18n/japanese.go +++ b/pkg/i18n/japanese.go @@ -461,8 +461,11 @@ func japaneseTranslationSet() TranslationSet { OpenLogMenu: "ログメニューを開く", LogMenuTitle: "コミットログオプション", // ToggleShowGitGraphAll: "Toggle show whole git graph (pass the `--all` flag to `git log`)", - ShowGitGraph: "コミットグラフの表示", - SortCommits: "コミットの表示順", + ShowGitGraph: "コミットグラフの表示", + SortOrder: "並び替え", + SortAlphabetical: "アルファベット順", + SortByDate: "日付順", + SortCommits: "コミットの表示順", // CantChangeContextSizeError: "Cannot change context while in patch building mode because we were too lazy to support it when releasing the feature. If you really want it, please let us know!", OpenCommitInBrowser: "ブラウザでコミットを開く", // LcViewBisectOptions: "View bisect options", diff --git a/pkg/i18n/russian.go b/pkg/i18n/russian.go index 3332f72cdb2..e4b67a77ecf 100644 --- a/pkg/i18n/russian.go +++ b/pkg/i18n/russian.go @@ -525,6 +525,9 @@ func RussianTranslationSet() TranslationSet { LogMenuTitle: "Параметры журнала коммитов", ToggleShowGitGraphAll: "Переключить отображение всего git графа (передать флаг --all в git log )", ShowGitGraph: "Показать git граф", + SortOrder: "Порядок сортировки", + SortAlphabetical: "По алфавиту", + SortByDate: "По дате", SortCommits: "Упорядочить коммиты", CantChangeContextSizeError: "Невозможно изменить контекст в режиме создания патча, потому что мы были слишком ленивы, чтобы поддерживать его при выпуске функции. Если вы действительно этого хотите, пожалуйста, дайте нам знать!", OpenCommitInBrowser: "Открыть коммит в браузере", diff --git a/schema/config.json b/schema/config.json index 186ca25760b..54f6d69612b 100644 --- a/schema/config.json +++ b/schema/config.json @@ -1014,6 +1014,10 @@ "fetchRemote": { "type": "string", "default": "f" + }, + "sortOrder": { + "type": "string", + "default": "s" } }, "additionalProperties": false, From 1e3935cbafedb0666a6193858e9a3f45191fd82c Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 20 Dec 2023 11:54:20 +0100 Subject: [PATCH 2/2] Add integration test for remote branch sort order --- pkg/integration/components/shell.go | 20 ++++++- .../tests/branch/sort_remote_branches.go | 55 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 pkg/integration/tests/branch/sort_remote_branches.go diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index 33b82fc58d5..5a3bbaa29a8 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -31,7 +31,11 @@ func NewShell(dir string, fail func(string)) *Shell { } func (self *Shell) RunCommand(args []string) *Shell { - output, err := self.runCommandWithOutput(args) + return self.RunCommandWithEnv(args, []string{}) +} + +func (self *Shell) RunCommandWithEnv(args []string, env []string) *Shell { + output, err := self.runCommandWithOutputAndEnv(args, env) if err != nil { self.fail(fmt.Sprintf("error running command: %v\n%s", args, output)) } @@ -49,8 +53,12 @@ func (self *Shell) RunCommandExpectError(args []string) *Shell { } func (self *Shell) runCommandWithOutput(args []string) (string, error) { + return self.runCommandWithOutputAndEnv(args, []string{}) +} + +func (self *Shell) runCommandWithOutputAndEnv(args []string, env []string) (string, error) { cmd := exec.Command(args[0], args[1:]...) - cmd.Env = os.Environ() + cmd.Env = append(os.Environ(), env...) cmd.Dir = self.dir output, err := cmd.CombinedOutput() @@ -164,6 +172,14 @@ func (self *Shell) EmptyCommitDaysAgo(message string, daysAgo int) *Shell { return self.RunCommand([]string{"git", "commit", "--allow-empty", "--date", fmt.Sprintf("%d days ago", daysAgo), "-m", message}) } +func (self *Shell) EmptyCommitWithDate(message string, date string) *Shell { + env := []string{ + "GIT_AUTHOR_DATE=" + date, + "GIT_COMMITTER_DATE=" + date, + } + return self.RunCommandWithEnv([]string{"git", "commit", "--allow-empty", "-m", message}, env) +} + func (self *Shell) Revert(ref string) *Shell { return self.RunCommand([]string{"git", "revert", ref}) } diff --git a/pkg/integration/tests/branch/sort_remote_branches.go b/pkg/integration/tests/branch/sort_remote_branches.go new file mode 100644 index 00000000000..35e2f700a07 --- /dev/null +++ b/pkg/integration/tests/branch/sort_remote_branches.go @@ -0,0 +1,55 @@ +package branch + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SortRemoteBranches = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Sort remote branches alphabetically or by date", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.NewBranch("first") + shell.EmptyCommitWithDate("commit", "2023-04-07 10:00:00") + shell.NewBranch("second") + shell.EmptyCommitWithDate("commit", "2023-04-07 12:00:00") + shell.NewBranch("third") + shell.EmptyCommitWithDate("commit", "2023-04-07 11:00:00") + shell.CloneIntoRemote("origin") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Remotes(). + Focus(). + Lines( + Contains("origin").IsSelected(), + ). + PressEnter() + + // sorted alphabetically by default + t.Views().RemoteBranches(). + IsFocused(). + Lines( + Contains("first").IsSelected(), + Contains("second"), + Contains("third"), + ). + SelectNextItem() // to test that the selection jumps back to the first when sorting + + t.Views().RemoteBranches(). + Press(keys.Branches.SortOrder) + + t.ExpectPopup().Menu().Title(Equals("Sort order")). + Select(Contains("-committerdate")). + Confirm() + + t.Views().RemoteBranches(). + IsFocused(). + Lines( + Contains("second").IsSelected(), + Contains("third"), + Contains("first"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 0aa61b463bf..9246aa82a80 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -54,6 +54,7 @@ var tests = []*components.IntegrationTest{ branch.ResetToUpstream, branch.SetUpstream, branch.ShowDivergenceFromUpstream, + branch.SortRemoteBranches, branch.Suggestions, branch.UnsetUpstream, cherry_pick.CherryPick,