mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-11-12 15:53:33 -04:00
Add "Copy commit message to clipboard" to the commit menu (#6209)
The commit menu (`ctrl+o` in the commit message panel) can paste a message from the clipboard, but it can't copy the message to it. Copying is useful for keeping a message around while committing something else first. For example, if a pre-commit hook fails, users may want to commit a fix for it before they come back to the original change. Copying the preserved commit message gives them a chance to park it in an untitled editor buffer and reuse it later. Also, add "Clear commit message" to the menu; this is for those users who don't know that you can quickly clear the message by holding down `ctrl+u` with auto-repeat. I've seen users hold down backspace with auto-repeat instead, which takes forever. This changes the keybinding for the "Paste commit message from clipboard" menu item from `p` to `V`, so that we can use `C` for copying. I hope this isn't a problem for anybody's muscle memory, but the new keys are consistent with copying and pasting commits in the commits panel.
This commit is contained in:
@@ -5,11 +5,11 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type CommitsHelper struct {
|
||||
@@ -48,6 +48,15 @@ func (self *CommitsHelper) SplitCommitMessageAndDescription(message string) (str
|
||||
return summary, description
|
||||
}
|
||||
|
||||
// joinCommitMessageAndDescription is the inverse of
|
||||
// SplitCommitMessageAndDescription.
|
||||
func (self *CommitsHelper) joinCommitMessageAndDescription(summary string, description string) string {
|
||||
if len(description) == 0 {
|
||||
return summary
|
||||
}
|
||||
return summary + "\n\n" + description
|
||||
}
|
||||
|
||||
// SplitPreservedCommitMessage splits a message in our preservation format
|
||||
// (summary and description joined by a single "\n") into summary and description.
|
||||
// It is lossless: round-tripping through JoinCommitMessageAndUnwrappedDescription
|
||||
@@ -103,9 +112,7 @@ func TryRemoveHardLineBreaks(message string, autoWrapWidth int) string {
|
||||
}
|
||||
|
||||
func (self *CommitsHelper) SwitchToEditor() error {
|
||||
message := lo.Ternary(len(self.getCommitDescription()) == 0,
|
||||
self.getCommitSummary(),
|
||||
self.getCommitSummary()+"\n\n"+self.getCommitDescription())
|
||||
message := self.joinCommitMessageAndDescription(self.getCommitSummary(), self.getCommitDescription())
|
||||
filepath := filepath.Join(self.c.OS().GetTempDir(), self.c.Git().RepoPaths.RepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".msg")
|
||||
err := self.c.OS().CreateFileWithContent(filepath, message)
|
||||
if err != nil {
|
||||
@@ -242,12 +249,27 @@ func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.S
|
||||
},
|
||||
Keys: menuKey('c'),
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.CopyCommitMessageToClipboard,
|
||||
OnPress: func() error {
|
||||
return self.copyCommitMessageToClipboard()
|
||||
},
|
||||
Keys: menuKey('C'),
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.PasteCommitMessageFromClipboard,
|
||||
OnPress: func() error {
|
||||
return self.pasteCommitMessageFromClipboard()
|
||||
},
|
||||
Keys: menuKey('p'),
|
||||
Keys: menuKey('V'),
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.ClearCommitMessage,
|
||||
OnPress: func() error {
|
||||
self.setSummaryAndDescriptionInView("", "")
|
||||
return nil
|
||||
},
|
||||
Keys: menuKey('d'),
|
||||
},
|
||||
}
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
@@ -271,6 +293,30 @@ func (self *CommitsHelper) addCoAuthor(suggestionFunc func(string) []*types.Sugg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitsHelper) copyCommitMessageToClipboard() error {
|
||||
// The panel keeps the space at the end of each line that it wraps. Git
|
||||
// strips trailing whitespace when it commits, so do the same here; this
|
||||
// makes the copied message look like the committed one.
|
||||
message := stripTrailingWhitespace(
|
||||
self.joinCommitMessageAndDescription(self.getCommitSummary(), self.getCommitDescription()))
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitMessageToClipboard)
|
||||
if err := self.c.OS().CopyToClipboard(message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.Toast(self.c.Tr.CommitMessageCopiedToClipboard)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stripTrailingWhitespace(message string) string {
|
||||
lines := strings.Split(message, "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (self *CommitsHelper) pasteCommitMessageFromClipboard() error {
|
||||
message, err := self.c.OS().PasteFromClipboard()
|
||||
if err != nil {
|
||||
|
||||
@@ -765,8 +765,10 @@ type TranslationSet struct {
|
||||
CopyCommitHashToClipboard string
|
||||
CommitHash string
|
||||
CommitURL string
|
||||
CopyCommitMessageToClipboard string
|
||||
PasteCommitMessageFromClipboard string
|
||||
SurePasteCommitMessage string
|
||||
ClearCommitMessage string
|
||||
CommitMessage string
|
||||
CommitMessageBody string
|
||||
CommitSubject string
|
||||
@@ -2002,8 +2004,10 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
CopyCommitHashToClipboard: "Copy abbreviated commit hash to clipboard",
|
||||
CommitHash: "Commit hash",
|
||||
CommitURL: "Commit URL",
|
||||
CopyCommitMessageToClipboard: "Copy commit message to clipboard",
|
||||
PasteCommitMessageFromClipboard: "Paste commit message from clipboard",
|
||||
SurePasteCommitMessage: "Pasting will overwrite the current commit message, continue?",
|
||||
ClearCommitMessage: "Clear commit message",
|
||||
CommitMessage: "Commit message (subject and body)",
|
||||
CommitMessageBody: "Commit message body",
|
||||
CommitSubject: "Commit subject",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var ClearCommitMessage = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Clear the commit message in the commit message panel",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file", "file content")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
Press(keys.Files.CommitChanges)
|
||||
|
||||
t.ExpectPopup().CommitMessagePanel().
|
||||
Type("subject").
|
||||
SwitchToDescription().
|
||||
Type("first line").
|
||||
AddNewline().
|
||||
Type("second line").
|
||||
SwitchToSummary().
|
||||
OpenCommitMenu()
|
||||
|
||||
t.ExpectPopup().Menu().Title(Equals("Commit Menu")).
|
||||
Select(Contains("Clear commit message")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectPopup().CommitMessagePanel().
|
||||
Content(Equals("")).
|
||||
SwitchToDescription().
|
||||
Content(Equals(""))
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
package commit
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CopyCommitMessageFromPanel = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Copy the commit message from the commit message panel to the clipboard",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().OS.CopyToClipboardCmd = "printf '%s' {{text}} > ../clipboard"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateFileAndAdd("file", "file content")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
Press(keys.Files.CommitChanges)
|
||||
|
||||
t.ExpectPopup().CommitMessagePanel().
|
||||
Type("subject").
|
||||
SwitchToDescription().
|
||||
Type("This line is longer than the auto-wrap width, so the clipboard gets it with a line break").
|
||||
AddNewline().
|
||||
Type("second line").
|
||||
SwitchToSummary().
|
||||
OpenCommitMenu()
|
||||
|
||||
t.ExpectPopup().Menu().Title(Equals("Commit Menu")).
|
||||
Select(Contains("Copy commit message to clipboard")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectToast(Equals("Commit message copied to clipboard"))
|
||||
|
||||
t.FileSystem().FileContent("../clipboard", Equals(
|
||||
"subject\n\nThis line is longer than the auto-wrap width, so the clipboard gets it\nwith a line break\nsecond line"))
|
||||
|
||||
t.ExpectPopup().CommitMessagePanel().
|
||||
Content(Equals("subject"))
|
||||
},
|
||||
})
|
||||
@@ -142,6 +142,7 @@ var tests = []*components.IntegrationTest{
|
||||
commit.CheckoutFileFromCommit,
|
||||
commit.CheckoutFileFromRangeSelectionOfCommits,
|
||||
commit.CheckoutFileWithLocalModifications,
|
||||
commit.ClearCommitMessage,
|
||||
commit.Commit,
|
||||
commit.CommitAllWithNestedRepository,
|
||||
commit.CommitDuringSquashMerge,
|
||||
@@ -156,6 +157,7 @@ var tests = []*components.IntegrationTest{
|
||||
commit.CommitWithNonMatchingBranchName,
|
||||
commit.CommitWithPrefix,
|
||||
commit.CopyAuthorToClipboard,
|
||||
commit.CopyCommitMessageFromPanel,
|
||||
commit.CopyMessageBodyToClipboard,
|
||||
commit.CopyTagToClipboard,
|
||||
commit.CreateAmendCommit,
|
||||
|
||||
Reference in New Issue
Block a user