Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ keybinding:
moveDownCommit: '<c-j>' # move commit down one
moveUpCommit: '<c-k>' # move commit up one
amendToCommit: 'A'
amendAttributeMenu: 'a'
pickCommit: 'p' # pick commit (when mid-rebase)
revertCommit: 't'
cherryPickCopy: 'C'
Expand All @@ -276,6 +277,12 @@ keybinding:
init: 'i'
update: 'u'
bulkMenu: 'b'
commitMessage:
commitMenu: '<c-o>'
amendAttribute:
addCoAuthor: 'c'
resetAuthor: 'a'
setAuthor: 'A'
```

## Platform Defaults
Expand Down
23 changes: 21 additions & 2 deletions pkg/commands/git_commands/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ func (self *CommitCommands) SetAuthor(value string) error {
}

// Add a commit's coauthor using Github/Gitlab Co-authored-by metadata. Value is expected to be of the form 'Name <Email>'
func (self *CommitCommands) AddCoAuthor(sha string, value string) error {
func (self *CommitCommands) AddCoAuthor(sha string, author string) error {
message, err := self.GetCommitMessage(sha)
if err != nil {
return err
}

message = message + fmt.Sprintf("\nCo-authored-by: %s", value)
message = AddCoAuthorToMessage(message, author)

cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only", "-m", message).
Expand All @@ -54,6 +54,25 @@ func (self *CommitCommands) AddCoAuthor(sha string, value string) error {
return self.cmd.New(cmdArgs).Run()
}

func AddCoAuthorToMessage(message string, author string) string {
subject, body, _ := strings.Cut(message, "\n")

return strings.TrimSpace(subject) + "\n\n" + AddCoAuthorToDescription(strings.TrimSpace(body), author)
}

func AddCoAuthorToDescription(description string, author string) string {
if description != "" {
lines := strings.Split(description, "\n")
if strings.HasPrefix(lines[len(lines)-1], "Co-authored-by:") {
description += "\n"
} else {
description += "\n\n"
}
}

return description + fmt.Sprintf("Co-authored-by: %s", author)
}

// ResetToCommit reset to commit
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
Expand Down
67 changes: 67 additions & 0 deletions pkg/commands/git_commands/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,70 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
})
}
}

func TestAddCoAuthorToMessage(t *testing.T) {
scenarios := []struct {
name string
message string
expectedResult string
}{
{
// This never happens, I think it isn't possible to create a commit
// with an empty message. Just including it for completeness.
name: "Empty message",
message: "",
expectedResult: "\n\nCo-authored-by: John Doe <[email protected]>",
},
{
name: "Just a subject, no body",
message: "Subject",
expectedResult: "Subject\n\nCo-authored-by: John Doe <[email protected]>",
},
{
name: "Subject and body",
message: "Subject\n\nBody",
expectedResult: "Subject\n\nBody\n\nCo-authored-by: John Doe <[email protected]>",
},
{
name: "Body already ending with a Co-authored-by line",
message: "Subject\n\nBody\n\nCo-authored-by: Jane Smith <[email protected]>",
expectedResult: "Subject\n\nBody\n\nCo-authored-by: Jane Smith <[email protected]>\nCo-authored-by: John Doe <[email protected]>",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
result := AddCoAuthorToMessage(s.message, "John Doe <[email protected]>")
assert.Equal(t, s.expectedResult, result)
})
}
}

func TestAddCoAuthorToDescription(t *testing.T) {
scenarios := []struct {
name string
description string
expectedResult string
}{
{
name: "Empty description",
description: "",
expectedResult: "Co-authored-by: John Doe <[email protected]>",
},
{
name: "Non-empty description",
description: "Body",
expectedResult: "Body\n\nCo-authored-by: John Doe <[email protected]>",
},
{
name: "Description already ending with a Co-authored-by line",
description: "Body\n\nCo-authored-by: Jane Smith <[email protected]>",
expectedResult: "Body\n\nCo-authored-by: Jane Smith <[email protected]>\nCo-authored-by: John Doe <[email protected]>",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
result := AddCoAuthorToDescription(s.description, "John Doe <[email protected]>")
assert.Equal(t, s.expectedResult, result)
})
}
}
38 changes: 25 additions & 13 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,17 +281,18 @@ type UpdateConfig struct {
}

type KeybindingConfig struct {
Universal KeybindingUniversalConfig `yaml:"universal"`
Status KeybindingStatusConfig `yaml:"status"`
Files KeybindingFilesConfig `yaml:"files"`
Branches KeybindingBranchesConfig `yaml:"branches"`
Worktrees KeybindingWorktreesConfig `yaml:"worktrees"`
Commits KeybindingCommitsConfig `yaml:"commits"`
Stash KeybindingStashConfig `yaml:"stash"`
CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"`
Main KeybindingMainConfig `yaml:"main"`
Submodules KeybindingSubmodulesConfig `yaml:"submodules"`
CommitMessage KeybindingCommitMessageConfig `yaml:"commitMessage"`
Universal KeybindingUniversalConfig `yaml:"universal"`
Status KeybindingStatusConfig `yaml:"status"`
Files KeybindingFilesConfig `yaml:"files"`
Branches KeybindingBranchesConfig `yaml:"branches"`
Worktrees KeybindingWorktreesConfig `yaml:"worktrees"`
Commits KeybindingCommitsConfig `yaml:"commits"`
AmendAttribute KeybindingAmendAttributeConfig `yaml:"amendAttribute"`
Stash KeybindingStashConfig `yaml:"stash"`
CommitFiles KeybindingCommitFilesConfig `yaml:"commitFiles"`
Main KeybindingMainConfig `yaml:"main"`
Submodules KeybindingSubmodulesConfig `yaml:"submodules"`
CommitMessage KeybindingCommitMessageConfig `yaml:"commitMessage"`
}

// damn looks like we have some inconsistencies here with -alt and -alt1
Expand Down Expand Up @@ -440,6 +441,12 @@ type KeybindingCommitsConfig struct {
StartInteractiveRebase string `yaml:"startInteractiveRebase"`
}

type KeybindingAmendAttributeConfig struct {
ResetAuthor string `yaml:"resetAuthor"`
SetAuthor string `yaml:"setAuthor"`
AddCoAuthor string `yaml:"addCoAuthor"`
}

type KeybindingStashConfig struct {
PopStash string `yaml:"popStash"`
RenameStash string `yaml:"renameStash"`
Expand All @@ -462,7 +469,7 @@ type KeybindingSubmodulesConfig struct {
}

type KeybindingCommitMessageConfig struct {
SwitchToEditor string `yaml:"switchToEditor"`
CommitMenu string `yaml:"commitMenu"`
}

// OSConfig contains config on the level of the os
Expand Down Expand Up @@ -836,6 +843,11 @@ func GetDefaultConfig() *UserConfig {
ViewBisectOptions: "b",
StartInteractiveRebase: "i",
},
AmendAttribute: KeybindingAmendAttributeConfig{
ResetAuthor: "a",
SetAuthor: "A",
AddCoAuthor: "c",
},
Stash: KeybindingStashConfig{
PopStash: "g",
RenameStash: "r",
Expand All @@ -854,7 +866,7 @@ func GetDefaultConfig() *UserConfig {
BulkMenu: "b",
},
CommitMessage: KeybindingCommitMessageConfig{
SwitchToEditor: "<c-o>",
CommitMenu: "<c-o>",
},
},
OS: OSConfig{},
Expand Down
4 changes: 2 additions & 2 deletions pkg/gui/context/commit_message_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ func (self *CommitMessageContext) SetPanelState(
subtitleTemplate := lo.Ternary(onSwitchToEditor != nil, self.c.Tr.CommitDescriptionSubTitle, self.c.Tr.CommitDescriptionSubTitleNoSwitch)
self.c.Views().CommitDescription.Subtitle = utils.ResolvePlaceholderString(subtitleTemplate,
map[string]string{
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
"switchToEditorKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.SwitchToEditor),
"togglePanelKeyBinding": keybindings.Label(self.c.UserConfig.Keybinding.Universal.TogglePanel),
"commitMenuKeybinding": keybindings.Label(self.c.UserConfig.Keybinding.CommitMessage.CommitMenu),
})
}

Expand Down
9 changes: 5 additions & 4 deletions pkg/gui/controllers/commit_description_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ func (self *CommitDescriptionController) GetKeybindings(opts types.KeybindingsOp
Handler: self.confirm,
},
{
Key: opts.GetKey(opts.Config.CommitMessage.SwitchToEditor),
Handler: self.switchToEditor,
Key: opts.GetKey(opts.Config.CommitMessage.CommitMenu),
Handler: self.openCommitMenu,
},
}

Expand All @@ -64,6 +64,7 @@ func (self *CommitDescriptionController) confirm() error {
return self.c.Helpers().Commits.HandleCommitConfirm()
}

func (self *CommitDescriptionController) switchToEditor() error {
return self.c.Helpers().Commits.SwitchToEditor()
func (self *CommitDescriptionController) openCommitMenu() error {
authorSuggestion := self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc()
return self.c.Helpers().Commits.OpenCommitMenu(authorSuggestion)
}
13 changes: 7 additions & 6 deletions pkg/gui/controllers/commit_message_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
Handler: self.switchToCommitDescription,
},
{
Key: opts.GetKey(opts.Config.CommitMessage.SwitchToEditor),
Handler: self.switchToEditor,
Key: opts.GetKey(opts.Config.CommitMessage.CommitMenu),
Handler: self.openCommitMenu,
},
}

Expand Down Expand Up @@ -89,10 +89,6 @@ func (self *CommitMessageController) switchToCommitDescription() error {
return nil
}

func (self *CommitMessageController) switchToEditor() error {
return self.c.Helpers().Commits.SwitchToEditor()
}

func (self *CommitMessageController) handleCommitIndexChange(value int) error {
currentIndex := self.context().GetSelectedIndex()
newIndex := currentIndex + value
Expand Down Expand Up @@ -134,3 +130,8 @@ func (self *CommitMessageController) confirm() error {
func (self *CommitMessageController) close() error {
return self.c.Helpers().Commits.CloseCommitMessagePanel()
}

func (self *CommitMessageController) openCommitMenu() error {
authorSuggestion := self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc()
return self.c.Helpers().Commits.OpenCommitMenu(authorSuggestion)
}
37 changes: 37 additions & 0 deletions pkg/gui/controllers/helpers/commits_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo"
)
Expand Down Expand Up @@ -215,3 +216,39 @@ func (self *CommitsHelper) commitMessageContexts() []types.Context {
self.c.Contexts().CommitMessage,
}
}

func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.Suggestion) error {
menuItems := []*types.MenuItem{
{
Label: self.c.Tr.OpenInEditor,
OnPress: func() error {
return self.SwitchToEditor()
},
Key: 'e',
},
{
Label: self.c.Tr.AddCoAuthor,
OnPress: func() error {
return self.addCoAuthor(suggestionFunc)
},
Key: 'c',
},
}
return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.CommitMenuTitle,
Items: menuItems,
})
}

func (self *CommitsHelper) addCoAuthor(suggestionFunc func(string) []*types.Suggestion) error {
return self.c.Prompt(types.PromptOpts{
Title: self.c.Tr.AddCoAuthorPromptTitle,
FindSuggestionsFunc: suggestionFunc,
HandleConfirm: func(value string) error {
commitDescription := self.getCommitDescription()
commitDescription = git_commands.AddCoAuthorToDescription(commitDescription, value)
self.setCommitDescription(commitDescription)
return nil
},
})
}
11 changes: 6 additions & 5 deletions pkg/gui/controllers/local_commits_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,25 +673,26 @@ func (self *LocalCommitsController) canAmend(commit *models.Commit) *types.Disab
}

func (self *LocalCommitsController) amendAttribute(commit *models.Commit) 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,
Key: 'a',
Tooltip: "Reset the commit's author to the currently configured user. This will also renew the author timestamp",
Key: opts.GetKey(opts.Config.AmendAttribute.ResetAuthor),
Tooltip: self.c.Tr.ResetAuthorTooltip,
},
{
Label: self.c.Tr.SetAuthor,
OnPress: self.setAuthor,
Key: 'A',
Tooltip: "Set the author based on a prompt",
Key: opts.GetKey(opts.Config.AmendAttribute.SetAuthor),
Tooltip: self.c.Tr.SetAuthorTooltip,
},
{
Label: self.c.Tr.AddCoAuthor,
OnPress: self.addCoAuthor,
Key: 'c',
Key: opts.GetKey(opts.Config.AmendAttribute.AddCoAuthor),
Tooltip: self.c.Tr.AddCoAuthorTooltip,
},
},
Expand Down
8 changes: 7 additions & 1 deletion pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ type TranslationSet struct {
AmendCommitTooltip string
Amend string
ResetAuthor string
ResetAuthorTooltip string
SetAuthor string
SetAuthorTooltip string
AddCoAuthor string
AmendCommitAttribute string
AmendCommitAttributeTooltip string
Expand Down Expand Up @@ -270,6 +272,7 @@ type TranslationSet struct {
SearchTitle string
TagsTitle string
MenuTitle string
CommitMenuTitle string
RemotesTitle string
RemoteBranchesTitle string
PatchBuildingTitle string
Expand Down Expand Up @@ -1093,7 +1096,9 @@ func EnglishTranslationSet() TranslationSet {
AmendCommitTooltip: "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.",
Amend: "Amend",
ResetAuthor: "Reset author",
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",
AddCoAuthor: "Add co-author",
AmendCommitAttribute: "Amend commit attribute",
AmendCommitAttributeTooltip: "Set/Reset commit author or set co-author.",
Expand Down Expand Up @@ -1209,12 +1214,13 @@ func EnglishTranslationSet() TranslationSet {
RebaseOptionsTitle: "Rebase options",
CommitSummaryTitle: "Commit summary",
CommitDescriptionTitle: "Commit description",
CommitDescriptionSubTitle: "Press {{.togglePanelKeyBinding}} to toggle focus, {{.switchToEditorKeyBinding}} to switch to editor",
CommitDescriptionSubTitle: "Press {{.togglePanelKeyBinding}} to toggle focus, {{.commitMenuKeybinding}} to open menu",
CommitDescriptionSubTitleNoSwitch: "Press {{.togglePanelKeyBinding}} to toggle focus",
LocalBranchesTitle: "Local branches",
SearchTitle: "Search",
TagsTitle: "Tags",
MenuTitle: "Menu",
CommitMenuTitle: "Commit Menu",
RemotesTitle: "Remotes",
RemoteBranchesTitle: "Remote branches",
PatchBuildingTitle: "Main panel (patch building)",
Expand Down
Loading