mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 15:46:26 -04:00
Add amend menu entry "Sign off"
This builds on the native 'git commit --signoff' feature to append the committer's Signed-off-by trailer to commit messages. Such a trailer is mandatory in certain projects like the Linux kernel and can now be easily applied to ranges of commits in hindsight with lazygit. The change is implemented as new CommitCommands/RebaseCommands methods that hook into GenericAmend to operate on range selections. It supports a configurable keybinding (default "s") and integrates with the command log.
This commit is contained in:
parent
e0b2a5081d
commit
e27f7bdf9a
|
|
@ -809,6 +809,7 @@ keybinding:
|
|||
resetAuthor: a
|
||||
setAuthor: A
|
||||
addCoAuthor: c
|
||||
signOff: s
|
||||
stash:
|
||||
popStash: g
|
||||
renameStash: r
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
| `` <ctrl+l> `` | View log options | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
| `` <ctrl+l> `` | 로그 메뉴 열기 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
| `` <ctrl+l> `` | Log opties weergeven | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
| `` <ctrl+l> `` | Открыть меню журнала | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
| `` <ctrl+l> `` | 開啟記錄選單 | View options for commit log e.g. changing sort order, hiding the git graph, showing the whole git graph. |
|
||||
|
|
|
|||
|
|
@ -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 <Email>'
|
||||
func (self *CommitCommands) SetAuthor(value string) error {
|
||||
cmdArgs := NewGitCmd("commit").
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 <Email>')",
|
||||
AddCoAuthorPromptTitle: "Add co-author (must look like 'Name <Email>')",
|
||||
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",
|
||||
|
|
|
|||
36
pkg/integration/tests/commit/sign_off.go
Normal file
36
pkg/integration/tests/commit/sign_off.go
Normal file
|
|
@ -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 <CI@example.com>"),
|
||||
)
|
||||
},
|
||||
})
|
||||
83
pkg/integration/tests/commit/sign_off_range.go
Normal file
83
pkg/integration/tests/commit/sign_off_range.go
Normal file
|
|
@ -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 <CI@example.com>"),
|
||||
)
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
SelectPreviousItem().
|
||||
SelectedLine(Contains("third commit"))
|
||||
|
||||
t.Views().Main().ContainsLines(
|
||||
Equals(" third commit"),
|
||||
Equals(" "),
|
||||
Equals(" Signed-off-by: CI <CI@example.com>"),
|
||||
)
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
SelectPreviousItem().
|
||||
SelectedLine(Contains("second commit"))
|
||||
|
||||
t.Views().Main().ContainsLines(
|
||||
Equals(" second commit"),
|
||||
Equals(" "),
|
||||
Equals(" Signed-off-by: CI <CI@example.com>"),
|
||||
)
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
SelectPreviousItem().
|
||||
SelectedLine(Contains("first commit"))
|
||||
|
||||
t.Views().Main().Content(
|
||||
Contains("first commit").
|
||||
DoesNotContain("Signed-off-by: CI <CI@example.com>"),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
|
@ -158,6 +158,8 @@ var tests = []*components.IntegrationTest{
|
|||
commit.Search,
|
||||
commit.SetAuthor,
|
||||
commit.SetAuthorRange,
|
||||
commit.SignOff,
|
||||
commit.SignOffRange,
|
||||
commit.StageRangeOfLines,
|
||||
commit.Staged,
|
||||
commit.StagedWithoutHooks,
|
||||
|
|
|
|||
|
|
@ -986,6 +986,20 @@
|
|||
}
|
||||
],
|
||||
"default": "c"
|
||||
},
|
||||
"signOff": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
],
|
||||
"default": "s"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
|
|
|||
Loading…
Reference in a new issue