From 53a8bd2e3fbbc474bbd70564f38f6fe1e1d10079 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 12 Jan 2024 10:32:20 +1100 Subject: [PATCH 1/4] Add ability to start an interactive rebase onto an appropriate base A common issue I have is that I want to move a commit from the top of my branch all the way down to the first commit on the branch. To do that, I need to navigate down to the first commit on my branch, press 'e' to start an interactive rebase, then navigate back up to the top of the branch, then move my commit back down to the base. This is annoying. Similarly annoying is moving the commit one-by-one without explicitly starting an interactive rebase, because then each individual step is its own rebase which takes a while in aggregate. This PR allows you to press 'i' from the commits view to start an interactive rebase from an 'appropriate' base. By appropriate, we mean that we want to start from the HEAD and stop when we reach the first merge commit or commit on the main branch. This may end up including more commits than you need, but it doesn't make a difference. --- 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/config/user_config.go | 2 + .../controllers/local_commits_controller.go | 88 ++++++++++++- pkg/i18n/english.go | 6 + pkg/integration/components/common.go | 18 +++ .../tests/interactive_rebase/quick_start.go | 123 ++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + schema/config.json | 4 + 15 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 pkg/integration/tests/interactive_rebase/quick_start.go diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md index ef6299613..9ffd28746 100644 --- a/docs/keybindings/Keybindings_en.md +++ b/docs/keybindings/Keybindings_en.md @@ -79,6 +79,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: Reword commit with editor d: Delete commit e: Edit commit + i: Start interactive rebase p: Pick commit (when mid-rebase) F: Create fixup commit for this commit S: Squash all 'fixup!' commits above selected commit (autosquash) diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md index 36389ecc1..f2f26db5a 100644 --- a/docs/keybindings/Keybindings_ja.md +++ b/docs/keybindings/Keybindings_ja.md @@ -98,6 +98,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: エディタでコミットメッセージを編集 d: コミットを削除 e: コミットを編集 + i: Start interactive rebase p: Pick commit (when mid-rebase) F: このコミットに対するfixupコミットを作成 S: Squash all 'fixup!' commits above selected commit (autosquash) diff --git a/docs/keybindings/Keybindings_ko.md b/docs/keybindings/Keybindings_ko.md index d96283192..36c045ba1 100644 --- a/docs/keybindings/Keybindings_ko.md +++ b/docs/keybindings/Keybindings_ko.md @@ -263,6 +263,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: 에디터에서 커밋메시지 수정 d: 커밋 삭제 e: 커밋을 편집 + i: Start interactive rebase p: Pick commit (when mid-rebase) F: Create fixup commit for this commit S: Squash all 'fixup!' commits above selected commit (autosquash) diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md index 23bf0a477..bd78ff694 100644 --- a/docs/keybindings/Keybindings_nl.md +++ b/docs/keybindings/Keybindings_nl.md @@ -142,6 +142,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: Hernoem commit met editor d: Verwijder commit e: Wijzig commit + i: Start interactive rebase p: Kies commit (wanneer midden in rebase) F: Creëer fixup commit S: Squash bovenstaande commits diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index 601ec72f5..048dd490e 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -63,6 +63,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: Zmień nazwę commita w edytorze d: Usuń commit e: Edytuj commit + i: Start interactive rebase p: Wybierz commit (podczas zmiany bazy) F: Utwórz commit naprawczy dla tego commita S: Spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash) diff --git a/docs/keybindings/Keybindings_ru.md b/docs/keybindings/Keybindings_ru.md index 6277f3db8..ba7af912f 100644 --- a/docs/keybindings/Keybindings_ru.md +++ b/docs/keybindings/Keybindings_ru.md @@ -146,6 +146,7 @@ _Связки клавиш_ R: Переписать коммит с помощью редактора d: Удалить коммит e: Изменить коммит + i: Start interactive rebase p: Выбрать коммит (в середине перебазирования) F: Создать fixup коммит для этого коммита S: Объединить все 'fixup!' коммиты выше в выбранный коммит (автосохранение) diff --git a/docs/keybindings/Keybindings_zh-CN.md b/docs/keybindings/Keybindings_zh-CN.md index 4b4a8e028..eaa7c725f 100644 --- a/docs/keybindings/Keybindings_zh-CN.md +++ b/docs/keybindings/Keybindings_zh-CN.md @@ -144,6 +144,7 @@ _Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b_ R: 使用编辑器重命名提交 d: 删除提交 e: 编辑提交 + i: Start interactive rebase p: 选择提交(变基过程中) F: 创建修正提交 S: 压缩在所选提交之上的所有“fixup!”提交(自动压缩) diff --git a/docs/keybindings/Keybindings_zh-TW.md b/docs/keybindings/Keybindings_zh-TW.md index 8fd6a274b..9f623678b 100644 --- a/docs/keybindings/Keybindings_zh-TW.md +++ b/docs/keybindings/Keybindings_zh-TW.md @@ -187,6 +187,7 @@ _說明:`` 表示 Ctrl+B、`` 表示 Alt+B,`B`表示 Shift+B_ R: 使用編輯器改寫提交 d: 刪除提交 e: 編輯提交 + i: Start interactive rebase p: 挑選提交 (於變基過程中) F: 為此提交建立修復提交 S: 壓縮上方所有的“fixup!”提交 (自動壓縮) diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index d0d518455..602dc54bf 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -428,6 +428,7 @@ type KeybindingCommitsConfig struct { OpenLogMenu string `yaml:"openLogMenu"` OpenInBrowser string `yaml:"openInBrowser"` ViewBisectOptions string `yaml:"viewBisectOptions"` + StartInteractiveRebase string `yaml:"startInteractiveRebase"` } type KeybindingStashConfig struct { @@ -822,6 +823,7 @@ func GetDefaultConfig() *UserConfig { OpenLogMenu: "", OpenInBrowser: "o", ViewBisectOptions: "b", + StartInteractiveRebase: "i", }, Stash: KeybindingStashConfig{ PopStash: "g", diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index efb06fe71..f3637397b 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -4,11 +4,13 @@ import ( "fmt" "github.com/fsmiamoto/git-todo-parser/todo" + "github.com/go-errors/errors" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/types/enums" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" + "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" @@ -42,6 +44,8 @@ func NewLocalCommitsController( } func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding { + editCommitKey := opts.Config.Universal.Edit + outsideFilterModeBindings := []*types.Binding{ { Key: opts.GetKey(opts.Config.Commits.SquashDown), @@ -74,11 +78,23 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ Description: self.c.Tr.DeleteCommit, }, { - Key: opts.GetKey(opts.Config.Universal.Edit), + Key: opts.GetKey(editCommitKey), Handler: self.checkSelected(self.edit), GetDisabledReason: self.getDisabledReasonForRebaseCommandWithSelectedCommit(todo.Edit), Description: self.c.Tr.EditCommit, }, + { + // The user-facing description here is 'Start interactive rebase' but internally + // we're calling it 'quick-start interactive rebase' to differentiate it from + // when you manually select the base commit. + Key: opts.GetKey(opts.Config.Commits.StartInteractiveRebase), + Handler: self.checkSelected(self.quickStartInteractiveRebase), + GetDisabledReason: self.require(self.notMidRebase, self.canFindCommitForQuickStart), + Description: self.c.Tr.QuickStartInteractiveRebase, + Tooltip: utils.ResolvePlaceholderString(self.c.Tr.QuickStartInteractiveRebaseTooltip, map[string]string{ + "editKey": keybindings.Label(editCommitKey), + }), + }, { Key: opts.GetKey(opts.Config.Commits.PickCommit), Handler: self.checkSelected(self.pick), @@ -414,14 +430,33 @@ func (self *LocalCommitsController) edit(commit *models.Commit) error { return nil } + return self.startInteractiveRebaseWithEdit(commit, commit) +} + +func (self *LocalCommitsController) quickStartInteractiveRebase(selectedCommit *models.Commit) error { + commitToEdit, err := self.findCommitForQuickStartInteractiveRebase() + if err != nil { + return self.c.Error(err) + } + + return self.startInteractiveRebaseWithEdit(commitToEdit, selectedCommit) +} + +func (self *LocalCommitsController) startInteractiveRebaseWithEdit( + commitToEdit *models.Commit, + selectedCommit *models.Commit, +) error { return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.EditCommit) - err := self.c.Git().Rebase.EditRebase(commit.Sha) + err := self.c.Git().Rebase.EditRebase(commitToEdit.Sha) return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions( err, types.RefreshOptions{Mode: types.BLOCK_UI, Then: func() { + // We need to select the same commit again because after starting a rebase, + // new lines can be added for update-ref commands in the TODO file, due to + // stacked branches. So the commit may be in a different position in the list. _, index, ok := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { - return c.Sha == commit.Sha + return c.Sha == selectedCommit.Sha }) if ok { self.context().SetSelectedLineIdx(index) @@ -430,6 +465,22 @@ func (self *LocalCommitsController) edit(commit *models.Commit) error { }) } +func (self *LocalCommitsController) findCommitForQuickStartInteractiveRebase() (*models.Commit, error) { + commit, index, ok := lo.FindIndexOf(self.c.Model().Commits, func(c *models.Commit) bool { + return c.IsMerge() || c.Status == models.StatusMerged + }) + + if !ok || index == 0 { + errorMsg := utils.ResolvePlaceholderString(self.c.Tr.CannotQuickStartInteractiveRebase, map[string]string{ + "editKey": keybindings.Label(self.c.UserConfig.Keybinding.Universal.Edit), + }) + + return nil, errors.New(errorMsg) + } + + return commit, nil +} + func (self *LocalCommitsController) pick(commit *models.Commit) error { applied, err := self.handleMidRebaseCommand(todo.Pick, commit) if err != nil { @@ -827,6 +878,24 @@ func (self *LocalCommitsController) getDisabledReasonForSquashAllAboveFixupCommi return "" } +// For getting disabled reason +func (self *LocalCommitsController) notMidRebase() string { + if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + return self.c.Tr.AlreadyRebasing + } + + return "" +} + +// For getting disabled reason +func (self *LocalCommitsController) canFindCommitForQuickStart() string { + if _, err := self.findCommitForQuickStartInteractiveRebase(); err != nil { + return err.Error() + } + + return "" +} + func (self *LocalCommitsController) createTag(commit *models.Commit) error { return self.c.Helpers().Tags.OpenCreateTagPrompt(commit.Sha, func() {}) } @@ -1030,6 +1099,19 @@ func (self *LocalCommitsController) isHeadCommit() bool { return models.IsHeadCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx()) } +// Convenience function for composing multiple disabled reason functions +func (self *LocalCommitsController) require(callbacks ...func() string) func() string { + return func() string { + for _, callback := range callbacks { + if disabledReason := callback(); disabledReason != "" { + return disabledReason + } + } + + return "" + } +} + func isChangeOfRebaseTodoAllowed(action todo.TodoCommand) bool { allowedActions := []todo.TodoCommand{ todo.Pick, diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index b645291e7..8886217c3 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -648,6 +648,9 @@ type TranslationSet struct { DisabledMenuItemPrefix string NoCommitSelected string NoCopiedCommits string + QuickStartInteractiveRebase string + QuickStartInteractiveRebaseTooltip string + CannotQuickStartInteractiveRebase string Actions Actions Bisect Bisect Log Log @@ -1477,6 +1480,9 @@ func EnglishTranslationSet() TranslationSet { DisabledMenuItemPrefix: "Disabled: ", NoCommitSelected: "No commit selected", NoCopiedCommits: "No copied commits", + QuickStartInteractiveRebase: "Start interactive rebase", + QuickStartInteractiveRebaseTooltip: "Start an interactive rebase for the commits on your branch. This will include all commits from the HEAD commit down to the first merge commit or main branch commit.\nIf you would instead like to start an interactive rebase from the selected commit, press `{{.editKey}}`.", + CannotQuickStartInteractiveRebase: "Cannot start interactive rebase: the HEAD commit is a merge commit or is present on the main branch, so there is no appropriate base commit to start the rebase from. You can start an interactive rebase from a specific commit by selecting the commit and pressing `{{.editKey}}`.", Actions: Actions{ // TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) CheckoutCommit: "Checkout commit", diff --git a/pkg/integration/components/common.go b/pkg/integration/components/common.go index 12afaee2f..4f7cd9754 100644 --- a/pkg/integration/components/common.go +++ b/pkg/integration/components/common.go @@ -18,6 +18,24 @@ func (self *Common) ContinueRebase() { self.ContinueMerge() } +func (self *Common) AbortRebase() { + self.t.GlobalPress(self.t.keys.Universal.CreateRebaseOptionsMenu) + + self.t.ExpectPopup().Menu(). + Title(Equals("Rebase options")). + Select(Contains("abort")). + Confirm() +} + +func (self *Common) AbortMerge() { + self.t.GlobalPress(self.t.keys.Universal.CreateRebaseOptionsMenu) + + self.t.ExpectPopup().Menu(). + Title(Equals("Merge options")). + Select(Contains("abort")). + Confirm() +} + func (self *Common) AcknowledgeConflicts() { self.t.ExpectPopup().Menu(). Title(Equals("Conflicts!")). diff --git a/pkg/integration/tests/interactive_rebase/quick_start.go b/pkg/integration/tests/interactive_rebase/quick_start.go new file mode 100644 index 000000000..713e00cdb --- /dev/null +++ b/pkg/integration/tests/interactive_rebase/quick_start.go @@ -0,0 +1,123 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var QuickStart = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Quick-starts an interactive rebase in several contexts", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + // we're going to test the following: + // * quick start from main fails + // * quick start from feature branch starts from main + // * quick start from branch with merge commit starts from merge commit + + shell.NewBranch("main") + shell.EmptyCommit("initial commit") + shell.EmptyCommit("last main commit") + + shell.NewBranch("feature-branch") + shell.NewBranch("branch-to-merge") + shell.NewBranch("branch-with-merge-commit") + + shell.Checkout("feature-branch") + shell.EmptyCommit("feature-branch one") + shell.EmptyCommit("feature-branch two") + + shell.Checkout("branch-to-merge") + shell.EmptyCommit("branch-to-merge one") + shell.EmptyCommit("branch-to-merge two") + + shell.Checkout("branch-with-merge-commit") + shell.EmptyCommit("branch-with-merge one") + shell.EmptyCommit("branch-with-merge two") + + shell.Merge("branch-to-merge") + + shell.EmptyCommit("branch-with-merge three") + + shell.Checkout("main") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("last main commit"), + Contains("initial commit"), + ). + // Verify we can't quick start from main + Press(keys.Commits.StartInteractiveRebase). + Tap(func() { + t.ExpectPopup().Alert(). + Title(Equals("Error")). + Content(Contains("Cannot start interactive rebase: the HEAD commit is a merge commit or is present on the main branch, so there is no appropriate base commit to start the rebase from. You can start an interactive rebase from a specific commit by selecting the commit and pressing `e`.")). + Confirm() + }) + + t.Views().Branches(). + Focus(). + NavigateToLine(Contains("feature-branch")). + Press(keys.Universal.Select) + + t.Views().Commits(). + Focus(). + Lines( + Contains("feature-branch two").IsSelected(), + Contains("feature-branch one"), + Contains("last main commit"), + Contains("initial commit"), + ). + // Verify quick start picks the last commit on the main branch + Press(keys.Commits.StartInteractiveRebase). + Lines( + Contains("feature-branch two").IsSelected(), + Contains("feature-branch one"), + Contains("last main commit").Contains("YOU ARE HERE"), + Contains("initial commit"), + ). + // Try again, verify we fail because we're already rebasing + Press(keys.Commits.StartInteractiveRebase). + Tap(func() { + t.ExpectPopup().Alert(). + Title(Equals("Error")). + Content(Contains("Can't perform this action during a rebase")). + Confirm() + + t.Common().AbortRebase() + }) + + // Verify if a merge commit is present on the branch we start from there + t.Views().Branches(). + Focus(). + NavigateToLine(Contains("branch-with-merge-commit")). + Press(keys.Universal.Select) + + t.Views().Commits(). + Focus(). + Lines( + Contains("branch-with-merge three").IsSelected(), + Contains("Merge branch 'branch-to-merge'"), + Contains("branch-to-merge two"), + Contains("branch-to-merge one"), + Contains("branch-with-merge two"), + Contains("branch-with-merge one"), + Contains("last main commit"), + Contains("initial commit"), + ). + Press(keys.Commits.StartInteractiveRebase). + Lines( + Contains("branch-with-merge three").IsSelected(), + Contains("Merge branch 'branch-to-merge'").Contains("YOU ARE HERE"), + Contains("branch-to-merge two"), + Contains("branch-to-merge one"), + Contains("branch-with-merge two"), + Contains("branch-with-merge one"), + Contains("last main commit"), + Contains("initial commit"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 1b6ab75e9..a0e58d6fb 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -164,6 +164,7 @@ var tests = []*components.IntegrationTest{ interactive_rebase.MoveInRebase, interactive_rebase.MoveWithCustomCommentChar, interactive_rebase.PickRescheduled, + interactive_rebase.QuickStart, interactive_rebase.Rebase, interactive_rebase.RewordCommitWithEditorAndFail, interactive_rebase.RewordFirstCommit, diff --git a/schema/config.json b/schema/config.json index 1251240a1..3de7df17d 100644 --- a/schema/config.json +++ b/schema/config.json @@ -1143,6 +1143,10 @@ "viewBisectOptions": { "type": "string", "default": "b" + }, + "startInteractiveRebase": { + "type": "string", + "default": "i" } }, "additionalProperties": false, From a0b63090e03c42ddf10514851fbf7758299e0d59 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 12 Jan 2024 11:17:26 +1100 Subject: [PATCH 2/4] Update codebase guide I'm adding a couple more terms: Keybinding and Action, to keep things standardised --- docs/dev/Codebase_Guide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/dev/Codebase_Guide.md b/docs/dev/Codebase_Guide.md index 93a771274..307482e2a 100644 --- a/docs/dev/Codebase_Guide.md +++ b/docs/dev/Codebase_Guide.md @@ -75,6 +75,8 @@ In terms of dependencies, controllers sit at the highest level, so they can refe * **Tab**: Each tab in a window (e.g. Files, Worktrees, Submodules) actually has a corresponding view which we bring to the front upon changing tabs. * **Model**: Representation of a git object e.g. commits, branches, files. * **ViewModel**: Used by a context to maintain state related to the view. +* **Keybinding**: A keybinding associates a _key_ with an _action_. For example if you press the 'down' arrow, the action performed will be your cursor moving down a list by one. +* **Action**: An action is the thing that happens when you press a key. Often an action will invoke a git command, but not always: for example, navigation actions don't involve git. * **Common structs**: Most structs have a field named `c` which contains a 'common' struct: a struct containing a bag of dependencies that most structs of the same layer require. For example if you want to access a helper from a controller you can do so with `self.c.Helpers.MyHelper`. ## Event loop and threads From 23a98937b47abec81b5523f5e0cd5c1ba967948e Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 12 Jan 2024 11:21:28 +1100 Subject: [PATCH 3/4] Update README to add backticks to key Missed a spot when I originally wrote this --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 16a123032..bcebc8fec 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,9 @@ Press space on the selected line to stage it, or press `v` to start selecting a ### Interactive Rebase -Press `e` on a commit to start an interactive rebase on it: causing all above commits to become part of the TODO file. Then squash (`s`), fixup (`f`), drop (`d`), edit (`e`), move up (ctrl+i) or move down (ctrl+j) any of TODO commits, before continuing the rebase by bringing up the rebase options menu with `m` and then selecting `continue`. You can also perform any these actions as a once-off (e.g. pressing `s` on a commit to squash it) without explicitly starting a rebase. +Press `e` on a commit to start an interactive rebase on it: causing all above commits to become part of the TODO file. Then squash (`s`), fixup (`f`), drop (`d`), edit (`e`), move up (`ctrl+i`) or move down (`ctrl+j`) any of TODO commits, before continuing the rebase by bringing up the rebase options menu with `m` and then selecting `continue`. + +You can also perform any these actions as a once-off (e.g. pressing `s` on a commit to squash it) without explicitly starting a rebase. ![interactive_rebase](../assets/demo/interactive_rebase-compressed.gif) From ee106f9af8354867c7ce310a52c726e24891f596 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 13 Jan 2024 00:28:04 +1100 Subject: [PATCH 4/4] Do not perform IO work when getting disabled reason with local commits Because we obtain disabled reasons after every action, we need to keep the code for doing so super fast. As such, we should not be hitting the filesystem to get rebase state, instead we should just get the cached state. I feel like we should actually be using the cached state everywhere like we do with all our other models if only for the sake of consistency. --- pkg/gui/controllers/local_commits_controller.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index f3637397b..d3b92bc93 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -534,7 +534,7 @@ func (self *LocalCommitsController) rebaseCommandEnabled(action todo.TodoCommand } if !commit.IsTODO() { - if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + if self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE { // If we are in a rebase, the only action that is allowed for // non-todo commits is rewording the current head commit if !(action == todo.Reword && self.isHeadCommit()) { @@ -688,7 +688,7 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error { } func (self *LocalCommitsController) getDisabledReasonForAmendTo(commit *models.Commit) string { - if !self.isHeadCommit() && self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + if !self.isHeadCommit() && self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE { return self.c.Tr.AlreadyRebasing } @@ -871,7 +871,7 @@ func (self *LocalCommitsController) squashAllAboveFixupCommits(commit *models.Co } func (self *LocalCommitsController) getDisabledReasonForSquashAllAboveFixupCommits(commit *models.Commit) string { - if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + if self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE { return self.c.Tr.AlreadyRebasing } @@ -880,7 +880,7 @@ func (self *LocalCommitsController) getDisabledReasonForSquashAllAboveFixupCommi // For getting disabled reason func (self *LocalCommitsController) notMidRebase() string { - if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE { + if self.c.Model().WorkingTreeStateAtLastCommitRefresh != enums.REBASE_MODE_NONE { return self.c.Tr.AlreadyRebasing }