diff --git a/docs/keybindings/Keybindings_en.md b/docs/keybindings/Keybindings_en.md index 449c4b6ec6d..f162014b700 100644 --- a/docs/keybindings/Keybindings_en.md +++ b/docs/keybindings/Keybindings_en.md @@ -352,6 +352,7 @@ If you would instead like to start an interactive rebase from the selected commi | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | Checkout | Checkout the selected tag as a detached HEAD. | | `` n `` | New tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. | | `` d `` | Delete | View delete options for local/remote tag. | diff --git a/docs/keybindings/Keybindings_ja.md b/docs/keybindings/Keybindings_ja.md index 0ccd6e7abe9..a1046c8dc17 100644 --- a/docs/keybindings/Keybindings_ja.md +++ b/docs/keybindings/Keybindings_ja.md @@ -182,6 +182,7 @@ If you would instead like to start an interactive rebase from the selected commi | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | チェックアウト | Checkout the selected tag as a detached HEAD. | | `` n `` | タグを作成 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. | | `` d `` | Delete | View delete options for local/remote tag. | diff --git a/docs/keybindings/Keybindings_ko.md b/docs/keybindings/Keybindings_ko.md index e5fb37782d3..50c71971961 100644 --- a/docs/keybindings/Keybindings_ko.md +++ b/docs/keybindings/Keybindings_ko.md @@ -323,6 +323,7 @@ If you would instead like to start an interactive rebase from the selected commi | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | 체크아웃 | Checkout the selected tag as a detached HEAD. | | `` n `` | 태그를 생성 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. | | `` d `` | Delete | View delete options for local/remote tag. | diff --git a/docs/keybindings/Keybindings_nl.md b/docs/keybindings/Keybindings_nl.md index 5951320c072..37bacb20f35 100644 --- a/docs/keybindings/Keybindings_nl.md +++ b/docs/keybindings/Keybindings_nl.md @@ -352,6 +352,7 @@ If you would instead like to start an interactive rebase from the selected commi | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | Uitchecken | Checkout the selected tag as a detached HEAD. | | `` n `` | Creëer tag | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. | | `` d `` | Delete | View delete options for local/remote tag. | diff --git a/docs/keybindings/Keybindings_pl.md b/docs/keybindings/Keybindings_pl.md index c22d151d3cf..8db7a0e73ee 100644 --- a/docs/keybindings/Keybindings_pl.md +++ b/docs/keybindings/Keybindings_pl.md @@ -333,6 +333,7 @@ Jeśli chcesz zamiast tego rozpocząć interaktywny rebase od wybranego commita, | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | Przełącz | Przełącz wybrany tag jako odłączoną głowę (detached HEAD). | | `` n `` | Nowy tag | Utwórz nowy tag z bieżącego commita. Zostaniesz poproszony o wprowadzenie nazwy tagu i opcjonalnego opisu. | | `` d `` | Usuń | Wyświetl opcje usuwania lokalnego/odległego tagu. | diff --git a/docs/keybindings/Keybindings_ru.md b/docs/keybindings/Keybindings_ru.md index a9d977fcb8c..9c17e4c6b92 100644 --- a/docs/keybindings/Keybindings_ru.md +++ b/docs/keybindings/Keybindings_ru.md @@ -288,6 +288,7 @@ If you would instead like to start an interactive rebase from the selected commi | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | Переключить | Checkout the selected tag as a detached HEAD. | | `` n `` | Создать тег | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. | | `` d `` | Delete | View delete options for local/remote tag. | diff --git a/docs/keybindings/Keybindings_zh-CN.md b/docs/keybindings/Keybindings_zh-CN.md index 6cabb0a3a13..6baea60d035 100644 --- a/docs/keybindings/Keybindings_zh-CN.md +++ b/docs/keybindings/Keybindings_zh-CN.md @@ -250,6 +250,7 @@ _图例:`` 意味着ctrl+b, `意味着Alt+b, `B` 意味着shift+b_ | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | 检出 | 检出选择的标签作为分离的HEAD | | `` n `` | 创建标签 | 基于当前提交创建一个新标签。你将在弹窗中输入标签名称和描述(可选)。 | | `` d `` | 删除 | 查看本地/远程标签的删除选项 | diff --git a/docs/keybindings/Keybindings_zh-TW.md b/docs/keybindings/Keybindings_zh-TW.md index c891f6bdcb2..895a81e79fd 100644 --- a/docs/keybindings/Keybindings_zh-TW.md +++ b/docs/keybindings/Keybindings_zh-TW.md @@ -284,6 +284,7 @@ If you would instead like to start an interactive rebase from the selected commi | Key | Action | Info | |-----|--------|-------------| +| `` `` | Copy tag to clipboard | | | `` `` | 檢出 | Checkout the selected tag as a detached HEAD. | | `` n `` | 建立標籤 | Create new tag from current commit. You'll be prompted to enter a tag name and optional description. | | `` d `` | 刪除 | View delete options for local/remote tag. | diff --git a/pkg/gui/controllers/basic_commits_controller.go b/pkg/gui/controllers/basic_commits_controller.go index 797215746c9..fb118b02483 100644 --- a/pkg/gui/controllers/basic_commits_controller.go +++ b/pkg/gui/controllers/basic_commits_controller.go @@ -3,6 +3,7 @@ package controllers import ( "errors" "fmt" + "strings" "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" @@ -122,51 +123,67 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [ } func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) error { - return self.c.Menu(types.CreateMenuOptions{ - Title: self.c.Tr.Actions.CopyCommitAttributeToClipboard, - Items: []*types.MenuItem{ - { - Label: self.c.Tr.CommitHash, - OnPress: func() error { - return self.copyCommitHashToClipboard(commit) - }, + items := []*types.MenuItem{ + { + Label: self.c.Tr.CommitHash, + OnPress: func() error { + return self.copyCommitHashToClipboard(commit) }, - { - Label: self.c.Tr.CommitSubject, - OnPress: func() error { - return self.copyCommitSubjectToClipboard(commit) - }, - Key: 's', + }, + { + Label: self.c.Tr.CommitSubject, + OnPress: func() error { + return self.copyCommitSubjectToClipboard(commit) }, - { - Label: self.c.Tr.CommitMessage, - OnPress: func() error { - return self.copyCommitMessageToClipboard(commit) - }, - Key: 'm', + Key: 's', + }, + { + Label: self.c.Tr.CommitMessage, + OnPress: func() error { + return self.copyCommitMessageToClipboard(commit) }, - { - Label: self.c.Tr.CommitURL, - OnPress: func() error { - return self.copyCommitURLToClipboard(commit) - }, - Key: 'u', + Key: 'm', + }, + { + Label: self.c.Tr.CommitURL, + OnPress: func() error { + return self.copyCommitURLToClipboard(commit) }, - { - Label: self.c.Tr.CommitDiff, - OnPress: func() error { - return self.copyCommitDiffToClipboard(commit) - }, - Key: 'd', + Key: 'u', + }, + { + Label: self.c.Tr.CommitDiff, + OnPress: func() error { + return self.copyCommitDiffToClipboard(commit) }, - { - Label: self.c.Tr.CommitAuthor, - OnPress: func() error { - return self.copyAuthorToClipboard(commit) - }, - Key: 'a', + Key: 'd', + }, + { + Label: self.c.Tr.CommitAuthor, + OnPress: func() error { + return self.copyAuthorToClipboard(commit) }, + Key: 'a', }, + } + + commitTagsItem := types.MenuItem{ + Label: self.c.Tr.CommitTags, + OnPress: func() error { + return self.copyCommitTagsToClipboard(commit) + }, + Key: 't', + } + + if len(commit.Tags) == 0 { + commitTagsItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.CommitHasNoTags} + } + + items = append(items, &commitTagsItem) + + return self.c.Menu(types.CreateMenuOptions{ + Title: self.c.Tr.Actions.CopyCommitAttributeToClipboard, + Items: items, }) } @@ -257,6 +274,18 @@ func (self *BasicCommitsController) copyCommitSubjectToClipboard(commit *models. return nil } +func (self *BasicCommitsController) copyCommitTagsToClipboard(commit *models.Commit) error { + message := strings.Join(commit.Tags, "\n") + + self.c.LogAction(self.c.Tr.Actions.CopyCommitTagsToClipboard) + if err := self.c.OS().CopyToClipboard(message); err != nil { + return err + } + + self.c.Toast(self.c.Tr.CommitTagsCopiedToClipboard) + return nil +} + func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error { url, err := self.c.Helpers().Host.GetCommitURL(commit.Hash) if err != nil { diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 371d039a662..72af2f9fdf1 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -145,6 +145,13 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi GetDisabledReason: self.getCopySelectedSideContextItemToClipboardDisabledReason, Description: self.c.Tr.CopyBranchNameToClipboard, }, + { + ViewName: "tags", + Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), + Handler: self.handleCopySelectedSideContextItemCommitHashToClipboard, + GetDisabledReason: self.getCopySelectedSideContextItemToClipboardDisabledReason, + Description: self.c.Tr.CopyTagToClipboard, + }, { ViewName: "commits", Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 3efe269b262..968aa5718fb 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -612,9 +612,11 @@ type TranslationSet struct { CommitMessage string CommitSubject string CommitAuthor string + CommitTags string CopyCommitAttributeToClipboard string CopyCommitAttributeToClipboardTooltip string CopyBranchNameToClipboard string + CopyTagToClipboard string CopyPathToClipboard string CommitPrefixPatternError string CopySelectedTextToClipboard string @@ -674,6 +676,8 @@ type TranslationSet struct { CommitMessageCopiedToClipboard string CommitSubjectCopiedToClipboard string CommitAuthorCopiedToClipboard string + CommitTagsCopiedToClipboard string + CommitHasNoTags string PatchCopiedToClipboard string CopiedToClipboard string ErrCannotEditDirectory string @@ -905,6 +909,7 @@ type Actions struct { CopyCommitURLToClipboard string CopyCommitAuthorToClipboard string CopyCommitAttributeToClipboard string + CopyCommitTagsToClipboard string CopyPatchToClipboard string CustomCommand string DiscardAllChangesInDirectory string @@ -1627,9 +1632,11 @@ func EnglishTranslationSet() *TranslationSet { CommitMessage: "Commit message", CommitSubject: "Commit subject", CommitAuthor: "Commit author", + CommitTags: "Commit tags", CopyCommitAttributeToClipboard: "Copy commit attribute to clipboard", CopyCommitAttributeToClipboardTooltip: "Copy commit attribute to clipboard (e.g. hash, URL, diff, message, author).", CopyBranchNameToClipboard: "Copy branch name to clipboard", + CopyTagToClipboard: "Copy tag to clipboard", CopyPathToClipboard: "Copy path to clipboard", CopySelectedTextToClipboard: "Copy selected text to clipboard", CommitPrefixPatternError: "Error in commitPrefix pattern", @@ -1688,6 +1695,8 @@ func EnglishTranslationSet() *TranslationSet { CommitMessageCopiedToClipboard: "Commit message copied to clipboard", CommitSubjectCopiedToClipboard: "Commit subject copied to clipboard", CommitAuthorCopiedToClipboard: "Commit author copied to clipboard", + CommitTagsCopiedToClipboard: "Commit tags copied to clipboard", + CommitHasNoTags: "Commit has no tags", PatchCopiedToClipboard: "Patch copied to clipboard", CopiedToClipboard: "copied to clipboard", ErrCannotEditDirectory: "Cannot edit directories: you can only edit individual files", @@ -1872,6 +1881,7 @@ func EnglishTranslationSet() *TranslationSet { CreateAnnotatedTag: "Create annotated tag", CopyCommitMessageToClipboard: "Copy commit message to clipboard", CopyCommitSubjectToClipboard: "Copy commit subject to clipboard", + CopyCommitTagsToClipboard: "Copy commit tags to clipboard", CopyCommitDiffToClipboard: "Copy commit diff to clipboard", CopyCommitHashToClipboard: "Copy full commit hash to clipboard", CopyCommitURLToClipboard: "Copy commit URL to clipboard", diff --git a/pkg/integration/tests/commit/copy_tag_to_clipboard.go b/pkg/integration/tests/commit/copy_tag_to_clipboard.go new file mode 100644 index 00000000000..a88148754ca --- /dev/null +++ b/pkg/integration/tests/commit/copy_tag_to_clipboard.go @@ -0,0 +1,51 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +// We're emulating the clipboard by writing to a file called clipboard + +var CopyTagToClipboard = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Copy a commit tag to the clipboard", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + // Include delimiters around the text so that we can assert on the entire content + config.GetUserConfig().OS.CopyToClipboardCmd = "echo _{{text}}_ > clipboard" + }, + + SetupRepo: func(shell *Shell) { + shell.SetAuthor("John Doe", "john@doe.com") + shell.EmptyCommit("commit") + shell.CreateLightweightTag("tag1", "HEAD") + shell.CreateLightweightTag("tag2", "HEAD") + }, + + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains("commit").IsSelected(), + ). + Press(keys.Commits.CopyCommitAttributeToClipboard) + + t.ExpectPopup().Menu(). + Title(Equals("Copy to clipboard")). + Select(Contains("Commit tags")). + Confirm() + + t.ExpectToast(Equals("Commit tags copied to clipboard")) + + t.Views().Files(). + Focus(). + Press(keys.Files.RefreshFiles). + Lines( + Contains("clipboard").IsSelected(), + ) + + t.Views().Main().Content(Contains("+_tag2")) + t.Views().Main().Content(Contains("+tag1_")) + }, +}) diff --git a/pkg/integration/tests/tag/copy_to_clipboard.go b/pkg/integration/tests/tag/copy_to_clipboard.go new file mode 100644 index 00000000000..f0176df9a92 --- /dev/null +++ b/pkg/integration/tests/tag/copy_to_clipboard.go @@ -0,0 +1,39 @@ +package tag + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var CopyToClipboard = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Copy the tag to the clipboard", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + // Include delimiters around the text so that we can assert on the entire content + config.GetUserConfig().OS.CopyToClipboardCmd = "echo _{{text}}_ > clipboard" + }, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("one") + shell.CreateLightweightTag("tag1", "HEAD") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Tags(). + Focus(). + Lines( + Contains("tag").IsSelected(), + ). + Press(keys.Universal.CopyToClipboard) + + t.ExpectToast(Equals("'tag1' copied to clipboard")) + + t.Views().Files(). + Focus(). + Press(keys.Files.RefreshFiles). + Lines( + Contains("clipboard").IsSelected(), + ) + + t.Views().Main().Content(Contains("_tag1_")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 0736677a80b..8b520e9e4ab 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -93,6 +93,7 @@ var tests = []*components.IntegrationTest{ commit.CommitWithNonMatchingBranchName, commit.CommitWithPrefix, commit.CopyAuthorToClipboard, + commit.CopyTagToClipboard, commit.CreateAmendCommit, commit.CreateFixupCommitInBranchStack, commit.CreateTag, @@ -351,6 +352,7 @@ var tests = []*components.IntegrationTest{ sync.RenameBranchAndPull, tag.Checkout, tag.CheckoutWhenBranchWithSameNameExists, + tag.CopyToClipboard, tag.CreateWhileCommitting, tag.CrudAnnotated, tag.CrudLightweight,