summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAzraelSec <me@azraelsec.sh>2024-05-22 21:17:56 +0200
committerAzraelSec <me@azraelsec.sh>2024-06-07 23:09:52 +0200
commit6b9cf72e793f7cad3d277b7bae438d20d68a76de (patch)
tree6053755809ff9ec189ffab9b7aeb3bea88c0d181 /pkg
parent92f13fc56e4d2a6d1ffeec97d0844907dbf3e27e (diff)
feat: support range selection for commit attributes amend
Diffstat (limited to 'pkg')
-rw-r--r--pkg/commands/git_commands/rebase.go58
-rw-r--r--pkg/gui/controllers/local_commits_controller.go49
-rw-r--r--pkg/integration/tests/commit/add_co_author_range.go105
-rw-r--r--pkg/integration/tests/commit/reset_author_range.go52
-rw-r--r--pkg/integration/tests/commit/set_author_range.go57
-rw-r--r--pkg/integration/tests/test_list.go3
6 files changed, 282 insertions, 42 deletions
diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go
index 040df0070..59c2c6833 100644
--- a/pkg/commands/git_commands/rebase.go
+++ b/pkg/commands/git_commands/rebase.go
@@ -67,42 +67,47 @@ func (self *RebaseCommands) RewordCommitInEditor(commits []*models.Commit, index
}), nil
}
-func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, index int) error {
- return self.GenericAmend(commits, index, func() error {
+func (self *RebaseCommands) ResetCommitAuthor(commits []*models.Commit, start, end int) error {
+ return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
return self.commit.ResetAuthor()
})
}
-func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, index int, value string) error {
- return self.GenericAmend(commits, index, func() error {
+func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, start, end int, value string) error {
+ return self.GenericAmend(commits, start, end, func(_ *models.Commit) error {
return self.commit.SetAuthor(value)
})
}
-func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, index int, value string) error {
- return self.GenericAmend(commits, index, func() error {
- return self.commit.AddCoAuthor(commits[index].Hash, value)
+func (self *RebaseCommands) AddCommitCoAuthor(commits []*models.Commit, start, end int, value string) error {
+ return self.GenericAmend(commits, start, end, func(commit *models.Commit) error {
+ return self.commit.AddCoAuthor(commit.Hash, value)
})
}
-func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f func() error) error {
- if models.IsHeadCommit(commits, index) {
+func (self *RebaseCommands) GenericAmend(commits []*models.Commit, start, end int, f func(commit *models.Commit) error) error {
+ if start == end && models.IsHeadCommit(commits, start) {
// we've selected the top commit so no rebase is required
- return f()
+ return f(commits[start])
}
- err := self.BeginInteractiveRebaseForCommit(commits, index, false)
+ err := self.BeginInteractiveRebaseForCommitRange(commits, start, end, false)
if err != nil {
return err
}
- // now the selected commit should be our head so we'll amend it
- err = f()
- if err != nil {
- return err
+ for commitIndex := end; commitIndex >= start; commitIndex-- {
+ err = f(commits[commitIndex])
+ if err != nil {
+ return err
+ }
+
+ if err := self.ContinueRebase(); err != nil {
+ return err
+ }
}
- return self.ContinueRebase()
+ return nil
}
func (self *RebaseCommands) MoveCommitsDown(commits []*models.Commit, startIdx int, endIdx int) error {
@@ -381,7 +386,13 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool,
) error {
- if len(commits)-1 < commitIndex {
+ return self.BeginInteractiveRebaseForCommitRange(commits, commitIndex, commitIndex, keepCommitsThatBecomeEmpty)
+}
+
+func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
+ commits []*models.Commit, start, end int, keepCommitsThatBecomeEmpty bool,
+) error {
+ if len(commits)-1 < end {
return errors.New("index outside of range of commits")
}
@@ -392,14 +403,17 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
return errors.New(self.Tr.DisabledForGPG)
}
- changes := []daemon.ChangeTodoAction{{
- Hash: commits[commitIndex].Hash,
- NewAction: todo.Edit,
- }}
+ changes := make([]daemon.ChangeTodoAction, 0, end-start)
+ for commitIndex := end; commitIndex >= start; commitIndex-- {
+ changes = append(changes, daemon.ChangeTodoAction{
+ Hash: commits[commitIndex].Hash,
+ NewAction: todo.Edit,
+ })
+ }
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
- baseHashOrRoot: getBaseHashOrRoot(commits, commitIndex+1),
+ baseHashOrRoot: getBaseHashOrRoot(commits, end+1),
overrideEditor: true,
keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go
index 53a6b205f..f8717f413 100644
--- a/pkg/gui/controllers/local_commits_controller.go
+++ b/pkg/gui/controllers/local_commits_controller.go
@@ -238,8 +238,8 @@ func (self *LocalCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
},
{
Key: opts.GetKey(opts.Config.Commits.ResetCommitAuthor),
- Handler: self.withItem(self.amendAttribute),
- GetDisabledReason: self.require(self.singleItemSelected(self.canAmend)),
+ Handler: self.withItemsRange(self.amendAttribute),
+ GetDisabledReason: self.require(self.itemRangeSelected(self.canAmendRange)),
Description: self.c.Tr.AmendCommitAttribute,
Tooltip: self.c.Tr.AmendCommitAttributeTooltip,
OpensMenu: true,
@@ -371,7 +371,7 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
}
func (self *LocalCommitsController) switchFromCommitMessagePanelToEditor(filepath string) error {
- if self.isHeadCommit() {
+ if self.isSelectedHeadCommit() {
return self.c.RunSubprocessAndRefresh(
self.c.Git().Commit.RewordLastCommitInEditorWithMessageFileCmdObj(filepath))
}
@@ -408,7 +408,7 @@ func (self *LocalCommitsController) handleReword(summary string, description str
func (self *LocalCommitsController) doRewordEditor() error {
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
- if self.isHeadCommit() {
+ if self.isSelectedHeadCommit() {
return self.c.RunSubprocessAndRefresh(self.c.Git().Commit.RewordLastCommitInEditorCmdObj())
}
@@ -607,7 +607,7 @@ func (self *LocalCommitsController) rewordEnabled(commit *models.Commit) *types.
// If we are in a rebase, the only action that is allowed for
// non-todo commits is rewording the current head commit
- if self.isRebasing() && !self.isHeadCommit() {
+ if self.isRebasing() && !self.isSelectedHeadCommit() {
return &types.DisabledReason{Text: self.c.Tr.AlreadyRebasing}
}
@@ -665,7 +665,7 @@ func (self *LocalCommitsController) moveUp(selectedCommits []*models.Commit, sta
}
func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
- if self.isHeadCommit() {
+ if self.isSelectedHeadCommit() {
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.AmendCommitTitle,
Prompt: self.c.Tr.AmendCommitPrompt,
@@ -695,34 +695,39 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error {
})
}
-func (self *LocalCommitsController) canAmend(commit *models.Commit) *types.DisabledReason {
- if !self.isHeadCommit() && self.isRebasing() {
+func (self *LocalCommitsController) canAmendRange(commits []*models.Commit, start, end int) *types.DisabledReason {
+ if (start != end || !self.isHeadCommit(start)) && self.isRebasing() {
return &types.DisabledReason{Text: self.c.Tr.AlreadyRebasing}
}
return nil
}
-func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error {
+func (self *LocalCommitsController) canAmend(_ *models.Commit) *types.DisabledReason {
+ idx := self.context().GetSelectedLineIdx()
+ return self.canAmendRange(self.c.Model().Commits, idx, idx)
+}
+
+func (self *LocalCommitsController) amendAttribute(commits []*models.Commit, start, end int) error {
opts := self.c.KeybindingsOpts()
return self.c.Menu(types.CreateMenuOptions{
Title: "Amend commit attribute",
Items: []*types.MenuItem{
{
Label: self.c.Tr.ResetAuthor,
- OnPress: self.resetAuthor,
+ OnPress: func() error { return self.resetAuthor(start, end) },
Key: opts.GetKey(opts.Config.AmendAttribute.ResetAuthor),
Tooltip: self.c.Tr.ResetAuthorTooltip,
},
{
Label: self.c.Tr.SetAuthor,
- OnPress: self.setAuthor,
+ OnPress: func() error { return self.setAuthor(start, end) },
Key: opts.GetKey(opts.Config.AmendAttribute.SetAuthor),
Tooltip: self.c.Tr.SetAuthorTooltip,
},
{
Label: self.c.Tr.AddCoAuthor,
- OnPress: self.addCoAuthor,
+ OnPress: func() error { return self.addCoAuthor(start, end) },
Key: opts.GetKey(opts.Config.AmendAttribute.AddCoAuthor),
Tooltip: self.c.Tr.AddCoAuthorTooltip,
},
@@ -730,10 +735,10 @@ func (self *LocalCommitsController) amendAttribute(commit *models.Commit) error
})
}
-func (self *LocalCommitsController) resetAuthor() error {
+func (self *LocalCommitsController) resetAuthor(start, end int) error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.ResetCommitAuthor)
- if err := self.c.Git().Rebase.ResetCommitAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx()); err != nil {
+ if err := self.c.Git().Rebase.ResetCommitAuthor(self.c.Model().Commits, start, end); err != nil {
return err
}
@@ -741,14 +746,14 @@ func (self *LocalCommitsController) resetAuthor() error {
})
}
-func (self *LocalCommitsController) setAuthor() error {
+func (self *LocalCommitsController) setAuthor(start, end int) error {
return self.c.Prompt(types.PromptOpts{
Title: self.c.Tr.SetAuthorPromptTitle,
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(),
HandleConfirm: func(value string) error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.SetCommitAuthor)
- if err := self.c.Git().Rebase.SetCommitAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx(), value); err != nil {
+ if err := self.c.Git().Rebase.SetCommitAuthor(self.c.Model().Commits, start, end, value); err != nil {
return err
}
@@ -758,14 +763,14 @@ func (self *LocalCommitsController) setAuthor() error {
})
}
-func (self *LocalCommitsController) addCoAuthor() error {
+func (self *LocalCommitsController) addCoAuthor(start, end int) error {
return self.c.Prompt(types.PromptOpts{
Title: self.c.Tr.AddCoAuthorPromptTitle,
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(),
HandleConfirm: func(value string) error {
return self.c.WithWaitingStatus(self.c.Tr.AmendingStatus, func(gocui.Task) error {
self.c.LogAction(self.c.Tr.Actions.AddCommitCoAuthor)
- if err := self.c.Git().Rebase.AddCommitCoAuthor(self.c.Model().Commits, self.context().GetSelectedLineIdx(), value); err != nil {
+ if err := self.c.Git().Rebase.AddCommitCoAuthor(self.c.Model().Commits, start, end, value); err != nil {
return err
}
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
@@ -1188,8 +1193,12 @@ func (self *LocalCommitsController) markAsBaseCommit(commit *models.Commit) erro
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
}
-func (self *LocalCommitsController) isHeadCommit() bool {
- return models.IsHeadCommit(self.c.Model().Commits, self.context().GetSelectedLineIdx())
+func (self *LocalCommitsController) isHeadCommit(idx int) bool {
+ return models.IsHeadCommit(self.c.Model().Commits, idx)
+}
+
+func (self *LocalCommitsController) isSelectedHeadCommit() bool {
+ return self.isHeadCommit(self.context().GetSelectedLineIdx())
}
func (self *LocalCommitsController) notMidRebase(message string) func() *types.DisabledReason {
diff --git a/pkg/integration/tests/commit/add_co_author_range.go b/pkg/integration/tests/commit/add_co_author_range.go
new file mode 100644
index 000000000..9452c1ded
--- /dev/null
+++ b/pkg/integration/tests/commit/add_co_author_range.go
@@ -0,0 +1,105 @@
+package commit
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var AddCoAuthorRange = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Add co-author on a range of commits",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.EmptyCommit("fourth commit")
+ shell.EmptyCommit("third commit")
+ shell.EmptyCommit("second commit")
+ shell.EmptyCommit("first commit")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("first commit").IsSelected(),
+ Contains("second commit"),
+ Contains("third commit"),
+ Contains("fourth commit"),
+ ).
+ SelectNextItem().
+ Press(keys.Universal.ToggleRangeSelect).
+ SelectNextItem().
+ Lines(
+ Contains("first commit"),
+ Contains("second commit").IsSelected(),
+ Contains("third commit").IsSelected(),
+ Contains("fourth commit"),
+ ).
+ Press(keys.Commits.ResetCommitAuthor).
+ Tap(func() {
+ t.ExpectPopup().Menu().
+ Title(Equals("Amend commit attribute")).
+ Select(Contains("Add co-author")).
+ Confirm()
+
+ t.ExpectPopup().Prompt().
+ Title(Contains("Add co-author")).
+ Type("John Smith <jsmith@gmail.com>").
+ Confirm()
+ }).
+ // exit range selection mode
+ PressEscape().
+ SelectNextItem()
+
+ t.Views().Main().Content(
+ Contains("fourth commit").
+ DoesNotContain("Co-authored-by: John Smith <jsmith@gmail.com>"),
+ )
+
+ t.Views().Commits().
+ IsFocused().
+ SelectPreviousItem().
+ Lines(
+ Contains("first commit"),
+ Contains("second commit"),
+ Contains("third commit").IsSelected(),
+ Contains("fourth commit"),
+ )
+
+ t.Views().Main().ContainsLines(
+ Equals(" third commit"),
+ Equals(" "),
+ Equals(" Co-authored-by: John Smith <jsmith@gmail.com>"),
+ )
+
+ t.Views().Commits().
+ IsFocused().
+ SelectPreviousItem().
+ Lines(
+ Contains("first commit"),
+ Contains("second commit").IsSelected(),
+ Contains("third commit"),
+ Contains("fourth commit"),
+ )
+
+ t.Views().Main().ContainsLines(
+ Equals(" second commit"),
+ Equals(" "),
+ Equals(" Co-authored-by: John Smith <jsmith@gmail.com>"),
+ )
+
+ t.Views().Commits().
+ IsFocused().
+ SelectPreviousItem().
+ Lines(
+ Contains("first commit").IsSelected(),
+ Contains("second commit"),
+ Contains("third commit"),
+ Contains("fourth commit"),
+ )
+
+ t.Views().Main().Content(
+ Contains("first commit").
+ DoesNotContain("Co-authored-by: John Smith <jsmith@gmail.com>"),
+ )
+ },
+})
diff --git a/pkg/integration/tests/commit/reset_author_range.go b/pkg/integration/tests/commit/reset_author_range.go
new file mode 100644
index 000000000..9d7c764cf
--- /dev/null
+++ b/pkg/integration/tests/commit/reset_author_range.go
@@ -0,0 +1,52 @@
+package commit
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var ResetAuthorRange = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Reset author on a range of commits",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.SetConfig("user.email", "Bill@example.com")
+ shell.SetConfig("user.name", "Bill Smith")
+
+ shell.EmptyCommit("fourth")
+ shell.EmptyCommit("third")
+ shell.EmptyCommit("second")
+ shell.EmptyCommit("first")
+
+ shell.SetConfig("user.email", "John@example.com")
+ shell.SetConfig("user.name", "John Smith")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("BS").Contains("first").IsSelected(),
+ Contains("BS").Contains("second"),
+ Contains("BS").Contains("third"),
+ Contains("BS").Contains("fourth"),
+ ).
+ SelectNextItem().
+ Press(keys.Universal.ToggleRangeSelect).
+ SelectNextItem().
+ Press(keys.Commits.ResetCommitAuthor).
+ Tap(func() {
+ t.ExpectPopup().Menu().
+ Title(Equals("Amend commit attribute")).
+ Select(Contains("Reset author")).
+ Confirm()
+ }).
+ PressEscape().
+ Lines(
+ Contains("BS").Contains("first"),
+ Contains("JS").Contains("second"),
+ Contains("JS").Contains("third").IsSelected(),
+ Contains("BS").Contains("fourth"),
+ )
+ },
+})
diff --git a/pkg/integration/tests/commit/set_author_range.go b/pkg/integration/tests/commit/set_author_range.go
new file mode 100644
index 000000000..e366ba05e
--- /dev/null
+++ b/pkg/integration/tests/commit/set_author_range.go
@@ -0,0 +1,57 @@
+package commit
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var SetAuthorRange = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "Set author on a range of commits",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.SetConfig("user.email", "Bill@example.com")
+ shell.SetConfig("user.name", "Bill Smith")
+
+ shell.EmptyCommit("fourth")
+ shell.EmptyCommit("third")
+ shell.EmptyCommit("second")
+ shell.EmptyCommit("first")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("BS").Contains("first").IsSelected(),
+ Contains("BS").Contains("second"),
+ Contains("BS").Contains("third"),
+ Contains("BS").Contains("fourth"),
+ )
+
+ t.Views().Commits().
+ Focus().
+ SelectNextItem().
+ Press(keys.Universal.ToggleRangeSelect).
+ SelectNextItem().
+ Press(keys.Commits.ResetCommitAuthor).
+ Tap(func() {
+ t.ExpectPopup().Menu().
+ Title(Equals("Amend commit attribute")).
+ Select(Contains(" Set author")). // adding space at start to distinguish from 'reset author'
+ Confirm()
+
+ t.ExpectPopup().Prompt().
+ Title(Contains("Set author")).
+ Type("John Smith <John@example.com>").
+ Confirm()
+ }).
+ PressEscape().
+ Lines(
+ Contains("BS").Contains("first"),
+ Contains("JS").Contains("second"),
+ Contains("JS").Contains("third").IsSelected(),
+ Contains("BS").Contains("fourth"),
+ )
+ },
+})
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index d4ebd4026..6386146ba 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -68,6 +68,7 @@ var tests = []*components.IntegrationTest{
cherry_pick.CherryPickDuringRebase,
cherry_pick.CherryPickRange,
commit.AddCoAuthor,
+ commit.AddCoAuthorRange,
commit.AddCoAuthorWhileCommitting,
commit.Amend,
commit.AutoWrapMessage,
@@ -89,11 +90,13 @@ var tests = []*components.IntegrationTest{
commit.NewBranch,
commit.PreserveCommitMessage,
commit.ResetAuthor,
+ commit.ResetAuthorRange,
commit.Revert,
commit.RevertMerge,
commit.Reword,
commit.Search,
commit.SetAuthor,
+ commit.SetAuthorRange,
commit.StageRangeOfLines,
commit.Staged,
commit.StagedWithoutHooks,