Add "Copy commit message to clipboard" to the commit menu

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. If
they commit the fix through the commit message panel, lazygit deletes
the preserved message of the original change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-11-12 08:36:14 +01:00
co-authored by Claude Opus 5.5
parent f11e1dca4c
commit 5a5e3ac696
4 changed files with 79 additions and 0 deletions
@@ -5,6 +5,7 @@ import (
"path/filepath"
"strings"
"time"
"unicode"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/gocui"
@@ -248,6 +249,13 @@ 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 {
@@ -277,6 +285,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 {
+2
View File
@@ -765,6 +765,7 @@ type TranslationSet struct {
CopyCommitHashToClipboard string
CommitHash string
CommitURL string
CopyCommitMessageToClipboard string
PasteCommitMessageFromClipboard string
SurePasteCommitMessage string
CommitMessage string
@@ -2002,6 +2003,7 @@ 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?",
CommitMessage: "Commit message (subject and body)",
@@ -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"))
},
})
+1
View File
@@ -156,6 +156,7 @@ var tests = []*components.IntegrationTest{
commit.CommitWithNonMatchingBranchName,
commit.CommitWithPrefix,
commit.CopyAuthorToClipboard,
commit.CopyCommitMessageFromPanel,
commit.CopyMessageBodyToClipboard,
commit.CopyTagToClipboard,
commit.CreateAmendCommit,