From 5a5e3ac6961e99f59de69b27cdb4eeaa83f8cdb6 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 11 Nov 2026 09:12:29 +0100 Subject: [PATCH] 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 --- pkg/gui/controllers/helpers/commits_helper.go | 32 ++++++++++++++ pkg/i18n/english.go | 2 + .../commit/copy_commit_message_from_panel.go | 44 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 4 files changed, 79 insertions(+) create mode 100644 pkg/integration/tests/commit/copy_commit_message_from_panel.go diff --git a/pkg/gui/controllers/helpers/commits_helper.go b/pkg/gui/controllers/helpers/commits_helper.go index 6a9b0861f..601fc45a9 100644 --- a/pkg/gui/controllers/helpers/commits_helper.go +++ b/pkg/gui/controllers/helpers/commits_helper.go @@ -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 { diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 3d4689d8a..d923c6e3b 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -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)", diff --git a/pkg/integration/tests/commit/copy_commit_message_from_panel.go b/pkg/integration/tests/commit/copy_commit_message_from_panel.go new file mode 100644 index 000000000..12da37d46 --- /dev/null +++ b/pkg/integration/tests/commit/copy_commit_message_from_panel.go @@ -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")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 6d66a1080..6c8e83b6a 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -156,6 +156,7 @@ var tests = []*components.IntegrationTest{ commit.CommitWithNonMatchingBranchName, commit.CommitWithPrefix, commit.CopyAuthorToClipboard, + commit.CopyCommitMessageFromPanel, commit.CopyMessageBodyToClipboard, commit.CopyTagToClipboard, commit.CreateAmendCommit,