mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 12:25:47 +02:00
Add a "Content of selected file" entry to the copy menu for commit files (#4341)
- **PR Description** Some people have the need to get at the file content of a file as it was in an earlier commit. They expect to press `e` in the commit files view and get the file opened in the state it was in at that commit, somehow. I explained a few times (e.g. in #3902, #4109, and #4327) that this is not how `e` should work; so if we want this as a feature, we'd need it as a separate command. However, it's a bit tricky to implement; if we check out the selected file to a temp file, it's unclear when to remove the file again. Alternatively we could use EditAndWait to open the file, then we would block until the user closes the editor and delete the file then. This is probably not the best user experience though, e.g. it wouldn't allow opening two files from an older commit at once. Instead of finding solutions to all these questions, this PR takes a simpler route and adds a command to the "Copy to Clipboard" menu that allows copying the entire content of a file. Users can then open a new, empty editor buffer and paste it in, this is almost as good as opening a temp file, but without all the questions. This was suggested in #4327. - **Please check if the PR fulfills these requirements** * [x] Cheatsheets are up-to-date (run `go generate ./...`) * [x] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting)) * [x] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide) * [x] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation)) * [ ] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig)) * [ ] Docs have been updated if necessary * [x] You've read through your own file changes for silly mistakes etc
This commit is contained in:
commit
e8d3a7afec
4 changed files with 74 additions and 3 deletions
|
@ -279,6 +279,13 @@ func (self *CommitCommands) ShowCmdObj(hash string, filterPath string) oscommand
|
|||
return self.cmd.New(cmdArgs).DontLog()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) ShowFileContentCmdObj(hash string, filePath string) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("show").
|
||||
Arg(fmt.Sprintf("%s:%s", hash, filePath)).
|
||||
ToArgv()
|
||||
return self.cmd.New(cmdArgs).DontLog()
|
||||
}
|
||||
|
||||
// Revert reverts the selected commit by hash
|
||||
func (self *CommitCommands) Revert(hash string) error {
|
||||
cmdArgs := NewGitCmd("revert").Arg(hash).ToArgv()
|
||||
|
|
|
@ -203,6 +203,16 @@ func (self *CommitFilesController) copyDiffToClipboard(path string, toastMessage
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) copyFileContentToClipboard(path string) error {
|
||||
_, to := self.context().GetFromAndToForDiff()
|
||||
cmdObj := self.c.Git().Commit.ShowFileContentCmdObj(to, path)
|
||||
diff, err := cmdObj.RunWithOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return self.c.OS().CopyToClipboard(diff)
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) openCopyMenu() error {
|
||||
node := self.context().GetSelected()
|
||||
|
||||
|
@ -246,6 +256,27 @@ func (self *CommitFilesController) openCopyMenu() error {
|
|||
DisabledReason: self.require(self.itemsSelected())(),
|
||||
Key: 'a',
|
||||
}
|
||||
copyFileContentItem := &types.MenuItem{
|
||||
Label: self.c.Tr.CopyFileContent,
|
||||
OnPress: func() error {
|
||||
if err := self.copyFileContentToClipboard(node.GetPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
self.c.Toast(self.c.Tr.FileContentCopiedToast)
|
||||
return nil
|
||||
},
|
||||
DisabledReason: self.require(self.singleItemSelected(
|
||||
func(node *filetree.CommitFileNode) *types.DisabledReason {
|
||||
if !node.IsFile() {
|
||||
return &types.DisabledReason{
|
||||
Text: self.c.Tr.ErrCannotCopyContentOfDirectory,
|
||||
ShowErrorInPanel: true,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))(),
|
||||
Key: 'c',
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.CopyToClipboardMenu,
|
||||
|
@ -254,6 +285,7 @@ func (self *CommitFilesController) openCopyMenu() error {
|
|||
copyPathItem,
|
||||
copyFileDiffItem,
|
||||
copyAllDiff,
|
||||
copyFileContentItem,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -80,11 +80,13 @@ type TranslationSet struct {
|
|||
CopyFileDiffTooltip string
|
||||
CopySelectedDiff string
|
||||
CopyAllFilesDiff string
|
||||
CopyFileContent string
|
||||
NoContentToCopyError string
|
||||
FileNameCopiedToast string
|
||||
FilePathCopiedToast string
|
||||
FileDiffCopiedToast string
|
||||
AllFilesDiffCopiedToast string
|
||||
FileContentCopiedToast string
|
||||
FilterStagedFiles string
|
||||
FilterUnstagedFiles string
|
||||
FilterTrackedFiles string
|
||||
|
@ -696,6 +698,7 @@ type TranslationSet struct {
|
|||
PatchCopiedToClipboard string
|
||||
CopiedToClipboard string
|
||||
ErrCannotEditDirectory string
|
||||
ErrCannotCopyContentOfDirectory string
|
||||
ErrStageDirWithInlineMergeConflicts string
|
||||
ErrRepositoryMovedOrDeleted string
|
||||
ErrWorktreeMovedOrRemoved string
|
||||
|
@ -1120,11 +1123,13 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
CopyFileDiffTooltip: "If there are staged items, this command considers only them. Otherwise, it considers all the unstaged ones.",
|
||||
CopySelectedDiff: "Diff of selected file",
|
||||
CopyAllFilesDiff: "Diff of all files",
|
||||
CopyFileContent: "Content of selected file",
|
||||
NoContentToCopyError: "Nothing to copy",
|
||||
FileNameCopiedToast: "File name copied to clipboard",
|
||||
FilePathCopiedToast: "File path copied to clipboard",
|
||||
FileDiffCopiedToast: "File diff copied to clipboard",
|
||||
AllFilesDiffCopiedToast: "All files diff copied to clipboard",
|
||||
FileContentCopiedToast: "File content copied to clipboard",
|
||||
FilterStagedFiles: "Show only staged files",
|
||||
FilterUnstagedFiles: "Show only unstaged files",
|
||||
FilterTrackedFiles: "Show only tracked files",
|
||||
|
@ -1737,6 +1742,7 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
PatchCopiedToClipboard: "Patch copied to clipboard",
|
||||
CopiedToClipboard: "copied to clipboard",
|
||||
ErrCannotEditDirectory: "Cannot edit directories: you can only edit individual files",
|
||||
ErrCannotCopyContentOfDirectory: "Cannot copy content of directories: you can only copy content of individual files",
|
||||
ErrStageDirWithInlineMergeConflicts: "Cannot stage/unstage directory containing files with inline merge conflicts. Please fix up the merge conflicts first",
|
||||
ErrRepositoryMovedOrDeleted: "Cannot find repo. It might have been moved or deleted ¯\\_(ツ)_/¯",
|
||||
CommandLog: "Command log",
|
||||
|
|
|
@ -23,17 +23,21 @@ var CopyToClipboard = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
shell.CreateDir("dir")
|
||||
shell.CreateFileAndAdd("dir/file1", "1st line\n")
|
||||
shell.Commit("1")
|
||||
shell.CreateFileAndAdd("dir/file1", "1st line\n2nd line\n")
|
||||
shell.UpdateFileAndAdd("dir/file1", "1st line\n2nd line\n")
|
||||
shell.CreateFileAndAdd("dir/file2", "file2\n")
|
||||
shell.Commit("2")
|
||||
shell.UpdateFileAndAdd("dir/file1", "1st line\n2nd line\n3rd line\n")
|
||||
shell.Commit("3")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("2").IsSelected(),
|
||||
Contains("3").IsSelected(),
|
||||
Contains("2"),
|
||||
Contains("1"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
|
@ -91,11 +95,22 @@ var CopyToClipboard = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
Contains("diff --git a/dir/file1 b/dir/file1").Contains("+2nd line").DoesNotContain("+1st line").
|
||||
Contains("diff --git a/dir/file2 b/dir/file2").Contains("+file2"))
|
||||
})
|
||||
}).
|
||||
Press(keys.Files.CopyFileInfoToClipboard).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Copy to clipboard")).
|
||||
Select(Contains("Content of selected file")).
|
||||
Confirm().
|
||||
Tap(func() {
|
||||
t.ExpectToast(Equals("File content copied to clipboard"))
|
||||
expectClipboard(t, Equals("1st line\n2nd line\n"))
|
||||
})
|
||||
})
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
// Select both commits
|
||||
// Select commits 1 and 2
|
||||
Press(keys.Universal.RangeSelectDown).
|
||||
PressEnter()
|
||||
|
||||
|
@ -118,6 +133,17 @@ var CopyToClipboard = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
expectClipboard(t,
|
||||
Contains("diff --git a/dir/file1 b/dir/file1").Contains("+1st line").Contains("+2nd line"))
|
||||
})
|
||||
}).
|
||||
Press(keys.Files.CopyFileInfoToClipboard).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Copy to clipboard")).
|
||||
Select(Contains("Content of selected file")).
|
||||
Confirm().
|
||||
Tap(func() {
|
||||
t.ExpectToast(Equals("File content copied to clipboard"))
|
||||
expectClipboard(t, Equals("1st line\n2nd line\n"))
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue