diff --git a/docs-master/Config.md b/docs-master/Config.md index 857a4e359..10454cd4a 100644 --- a/docs-master/Config.md +++ b/docs-master/Config.md @@ -809,6 +809,7 @@ keybinding: resetAuthor: a setAuthor: A addCoAuthor: c + signOff: s stash: popStash: g renameStash: r diff --git a/docs-master/keybindings/Keybindings_en.md b/docs-master/keybindings/Keybindings_en.md index 3ec731bf2..0cc9759b0 100644 --- a/docs-master/keybindings/Keybindings_en.md +++ b/docs-master/keybindings/Keybindings_en.md @@ -102,7 +102,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` V `` | Paste (cherry-pick) | | | `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. | | `` A `` | Amend | Amend commit with staged changes. If the selected commit is the HEAD commit, this will perform `git commit --amend`. Otherwise the commit will be amended via a rebase. | -| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. | +| `` a `` | Amend commit attribute | Set/Reset commit author, set co-author, or sign off the commit. | | `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. | | `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. | | `` `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. | diff --git a/docs-master/keybindings/Keybindings_ko.md b/docs-master/keybindings/Keybindings_ko.md index a0e5d84dc..ca35d3f2a 100644 --- a/docs-master/keybindings/Keybindings_ko.md +++ b/docs-master/keybindings/Keybindings_ko.md @@ -313,7 +313,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` V `` | 커밋을 붙여넣기 (cherry-pick) | | | `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. | | `` A `` | Amend | Amend commit with staged changes | -| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. | +| `` a `` | Amend commit attribute | Set/Reset commit author, set co-author, or sign off the commit. | | `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. | | `` T `` | Tag commit | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. | | `` `` | 로그 메뉴 열기 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. | diff --git a/docs-master/keybindings/Keybindings_nl.md b/docs-master/keybindings/Keybindings_nl.md index 7d95e72dd..78baa081d 100644 --- a/docs-master/keybindings/Keybindings_nl.md +++ b/docs-master/keybindings/Keybindings_nl.md @@ -174,7 +174,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` V `` | Plak commits (cherry-pick) | | | `` B `` | Markeer als basiscommit voor rebase | Selecteer een basiscommit voor de volgende rebase. Als je rebased op een branch worden alleen commits boven de basiscommit meegenomen. Hiervoor wordt het `git rebase --onto` commando gebruikt. | | `` A `` | Amend | Wijzig commit met staged veranderingen | -| `` a `` | Amend commit attribute | Set/Reset commit author or set co-author. | +| `` a `` | Amend commit attribute | Set/Reset commit author, set co-author, or sign off the commit. | | `` t `` | Revert | Maak een revert commit voor de geselecteerde commit, die de wijzigingen in deze commit terugdraait. | | `` T `` | Tag commit | Maak een nieuwe tag die naar de geselecteerde commit wijst. Je wordt gevraagd om een tag naam en optionele omschrijving. | | `` `` | Log opties weergeven | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. | diff --git a/docs-master/keybindings/Keybindings_ru.md b/docs-master/keybindings/Keybindings_ru.md index 3d03f9ca6..f686d7420 100644 --- a/docs-master/keybindings/Keybindings_ru.md +++ b/docs-master/keybindings/Keybindings_ru.md @@ -184,7 +184,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` V `` | Вставить отобранные коммиты (cherry-pick) | | | `` B `` | Mark as base commit for rebase | Select a base commit for the next rebase. When you rebase onto a branch, only commits above the base commit will be brought across. This uses the `git rebase --onto` command. | | `` A `` | Amend | Править последний коммит с проиндексированными изменениями | -| `` a `` | Установить/убрать автора коммита | Set/Reset commit author or set co-author. | +| `` a `` | Установить/убрать автора коммита | Set/Reset commit author, set co-author, or sign off the commit. | | `` t `` | Revert | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. | | `` T `` | Пометить коммит тегом | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. | | `` `` | Открыть меню журнала | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. | diff --git a/docs-master/keybindings/Keybindings_zh-TW.md b/docs-master/keybindings/Keybindings_zh-TW.md index 1706a627e..40b95f765 100644 --- a/docs-master/keybindings/Keybindings_zh-TW.md +++ b/docs-master/keybindings/Keybindings_zh-TW.md @@ -198,7 +198,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` V `` | 貼上提交 (揀選) | | | `` B `` | 為了變基已標注提交為基準提交 | 請為了下一次變基選擇一項基準提交;此將執行 `git rebase --onto`。 | | `` A `` | 修改 | 使用已預存的更改修正提交 | -| `` a `` | 設定/重設提交作者 | Set/Reset commit author or set co-author. | +| `` a `` | 設定/重設提交作者 | Set/Reset commit author, set co-author, or sign off the commit. | | `` t `` | 還原 | Create a revert commit for the selected commit, which applies the selected commit's changes in reverse. | | `` T `` | 打標籤到提交 | Create a new tag pointing at the selected commit. You'll be prompted to enter a tag name and optional description. | | `` `` | 開啟記錄選單 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. | diff --git a/pkg/commands/git_commands/commit.go b/pkg/commands/git_commands/commit.go index d067e9831..271caea08 100644 --- a/pkg/commands/git_commands/commit.go +++ b/pkg/commands/git_commands/commit.go @@ -29,6 +29,15 @@ func (self *CommitCommands) ResetAuthor() error { return self.cmd.New(cmdArgs).Run() } +// Adds a Signed-off-by trailer to the topmost commit +func (self *CommitCommands) SignOff() error { + cmdArgs := NewGitCmd("commit"). + Arg("--allow-empty", "--allow-empty-message", "--only", "--no-edit", "--amend", "--signoff"). + ToArgv() + + return self.cmd.New(cmdArgs).Run() +} + // Sets the commit's author to the supplied value. Value is expected to be of the form 'Name ' func (self *CommitCommands) SetAuthor(value string) error { cmdArgs := NewGitCmd("commit"). diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index 74278b18d..d8363eeb9 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -80,6 +80,12 @@ func (self *RebaseCommands) SetCommitAuthor(commits []*models.Commit, start, end }) } +func (self *RebaseCommands) SignOffCommit(commits []*models.Commit, start, end int) error { + return self.GenericAmend(commits, start, end, func(_ *models.Commit) error { + return self.commit.SignOff() + }) +} + 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) diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 9738186d9..5361f8682 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -646,6 +646,7 @@ type KeybindingAmendAttributeConfig struct { ResetAuthor Keybinding `yaml:"resetAuthor"` SetAuthor Keybinding `yaml:"setAuthor"` AddCoAuthor Keybinding `yaml:"addCoAuthor"` + SignOff Keybinding `yaml:"signOff"` } type KeybindingStashConfig struct { @@ -1159,6 +1160,7 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig { ResetAuthor: Keybinding{"a"}, SetAuthor: Keybinding{"A"}, AddCoAuthor: Keybinding{"c"}, + SignOff: Keybinding{"s"}, }, Stash: KeybindingStashConfig{ PopStash: Keybinding{"g"}, diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 1e1a01427..328ebc869 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -1301,10 +1301,31 @@ func (self *LocalCommitsController) amendAttribute(_ []*models.Commit, start, en Keys: opts.GetKeys(opts.Config.AmendAttribute.AddCoAuthor), Tooltip: self.c.Tr.AddCoAuthorTooltip, }, + { + Label: self.c.Tr.SignOffCommit, + OnPress: func() error { return self.signOffCommit(commits, start, end) }, + Keys: opts.GetKeys(opts.Config.AmendAttribute.SignOff), + Tooltip: self.c.Tr.SignOffCommitTooltip, + }, }, }) } +func (self *LocalCommitsController) signOffCommit(commits []*models.Commit, start, end int) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.AmendingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.SignOffCommit) + if err := self.c.Git().Rebase.SignOffCommit(commits, start, end); err != nil { + return err + } + + self.c.RefreshFromWorker(types.RefreshOptions{}) + return nil + }) +} + func (self *LocalCommitsController) resetAuthor(commits []*models.Commit, start, end int) error { return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ Message: self.c.Tr.AmendingStatus, diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 9c72b53df..beb65c1eb 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -197,6 +197,8 @@ type TranslationSet struct { ResetAuthorTooltip string SetAuthor string SetAuthorTooltip string + SignOffCommit string + SignOffCommitTooltip string AddCoAuthor string AmendCommitAttribute string AmendCommitAttributeTooltip string @@ -1037,6 +1039,7 @@ type Actions struct { ResetCommitAuthor string SetCommitAuthor string AddCommitCoAuthor string + SignOffCommit string RevertCommit string CreateFixupCommit string SquashAllAboveFixupCommits string @@ -1355,9 +1358,11 @@ func EnglishTranslationSet() *TranslationSet { ResetAuthorTooltip: "Reset the commit's author to the currently configured user. This will also renew the author timestamp", SetAuthor: "Set author", SetAuthorTooltip: "Set the author based on a prompt", + SignOffCommit: "Sign off", + SignOffCommitTooltip: "Add a Signed-off-by trailer to the commit.", AddCoAuthor: "Add co-author", AmendCommitAttribute: "Amend commit attribute", - AmendCommitAttributeTooltip: "Set/Reset commit author or set co-author.", + AmendCommitAttributeTooltip: "Set/Reset commit author, set co-author, or sign off the commit.", SetAuthorPromptTitle: "Set author (must look like 'Name ')", AddCoAuthorPromptTitle: "Add co-author (must look like 'Name ')", AddCoAuthorTooltip: "Add co-author using the Github/Gitlab metadata Co-authored-by.", @@ -2154,6 +2159,7 @@ func EnglishTranslationSet() *TranslationSet { ResetCommitAuthor: "Reset commit author", SetCommitAuthor: "Set commit author", AddCommitCoAuthor: "Add commit co-author", + SignOffCommit: "Sign off commit", RevertCommit: "Revert commit", CreateFixupCommit: "Create fixup commit", SquashAllAboveFixupCommits: "Squash all above fixup commits", diff --git a/pkg/integration/tests/commit/sign_off.go b/pkg/integration/tests/commit/sign_off.go new file mode 100644 index 000000000..f19d0c99f --- /dev/null +++ b/pkg/integration/tests/commit/sign_off.go @@ -0,0 +1,36 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SignOff = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Sign off a commit", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("initial commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("initial commit").IsSelected(), + ). + Press(keys.Commits.ResetCommitAuthor). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Amend commit attribute")). + Select(Contains("Sign off")). + Confirm() + }) + + t.Views().Main().ContainsLines( + Equals(" initial commit"), + Equals(" "), + Equals(" Signed-off-by: CI "), + ) + }, +}) diff --git a/pkg/integration/tests/commit/sign_off_range.go b/pkg/integration/tests/commit/sign_off_range.go new file mode 100644 index 000000000..707c143a2 --- /dev/null +++ b/pkg/integration/tests/commit/sign_off_range.go @@ -0,0 +1,83 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SignOffRange = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Sign off 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(). + SelectedLines( + Contains("second commit"), + Contains("third commit"), + ). + Press(keys.Commits.ResetCommitAuthor). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Amend commit attribute")). + Select(Contains("Sign off")). + Confirm() + }). + PressEscape(). + SelectNextItem(). + SelectedLine(Contains("fourth commit")) + + t.Views().Main().Content( + Contains("fourth commit"). + DoesNotContain("Signed-off-by: CI "), + ) + + t.Views().Commits(). + IsFocused(). + SelectPreviousItem(). + SelectedLine(Contains("third commit")) + + t.Views().Main().ContainsLines( + Equals(" third commit"), + Equals(" "), + Equals(" Signed-off-by: CI "), + ) + + t.Views().Commits(). + IsFocused(). + SelectPreviousItem(). + SelectedLine(Contains("second commit")) + + t.Views().Main().ContainsLines( + Equals(" second commit"), + Equals(" "), + Equals(" Signed-off-by: CI "), + ) + + t.Views().Commits(). + IsFocused(). + SelectPreviousItem(). + SelectedLine(Contains("first commit")) + + t.Views().Main().Content( + Contains("first commit"). + DoesNotContain("Signed-off-by: CI "), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 0d25ddba6..686ec21fd 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -158,6 +158,8 @@ var tests = []*components.IntegrationTest{ commit.Search, commit.SetAuthor, commit.SetAuthorRange, + commit.SignOff, + commit.SignOffRange, commit.StageRangeOfLines, commit.Staged, commit.StagedWithoutHooks, diff --git a/schema-master/config.json b/schema-master/config.json index 45a2b9efe..7b79438e6 100644 --- a/schema-master/config.json +++ b/schema-master/config.json @@ -986,6 +986,20 @@ } ], "default": "c" + }, + "signOff": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "s" } }, "additionalProperties": false,