1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-06 23:46:13 +02:00

Expose {{.SelectedCommitRange}} to custom commands (#4204)

- **PR Description**

Expose `{{.SelectedCommitRange}}` to custom commands. It has fields .To
and .From (the hashes of the last and the first selected commits,
respectively), and it is useful for creating git commands that act on a
range of commits.

Fixes #4184.

- **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)
* [ ] 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))
* [x] Docs have been updated if necessary
* [x] You've read through your own file changes for silly mistakes etc
This commit is contained in:
Stefan Haller 2025-01-27 09:03:47 +01:00 committed by GitHub
commit 55236802c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 72 additions and 0 deletions

View File

@ -297,6 +297,7 @@ Your commands can contain placeholder strings using Go's [template syntax](https
``` ```
SelectedCommit SelectedCommit
SelectedCommitRange
SelectedFile SelectedFile
SelectedPath SelectedPath
SelectedLocalBranch SelectedLocalBranch
@ -314,6 +315,11 @@ CheckedOutBranch
To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file). To see what fields are available on e.g. the `SelectedFile`, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/gui/services/custom_commands/models.go) (all the modelling lives in the same file).
We don't support accessing all elements of a range selection yet. We might add this in the future, but as a special case you can access the range of selected commits by using `SelectedCommitRange`, which has two properties `.To` and `.From` which are the hashes of the bottom and top selected commits, respectively. This is useful for passing them to a git command that operates on a range of commits. For example, to create patches for all selected commits, you might use
```yml
command: "git format-patch {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}}"
```
## Keybinding collisions ## Keybinding collisions
If your custom keybinding collides with an inbuilt keybinding that is defined for the same context, only the custom keybinding will be executed. This also applies to the global context. However, one caveat is that if you have a custom keybinding defined on the global context for some key, and there is an in-built keybinding defined for the same key and for a specific context (say the 'files' context), then the in-built keybinding will take precedence. See how to change in-built keybindings [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#keybindings) If your custom keybinding collides with an inbuilt keybinding that is defined for the same context, only the custom keybinding will be executed. This also applies to the global context. However, one caveat is that if you have a custom keybinding defined on the global context for some key, and there is an in-built keybinding defined for the same key and for a specific context (say the 'files' context), then the in-built keybinding will take precedence. See how to change in-built keybindings [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#keybindings)

View File

@ -162,12 +162,29 @@ func worktreeShimFromModelRemote(worktree *models.Worktree) *Worktree {
} }
} }
type CommitRange struct {
From string
To string
}
func makeCommitRange(commits []*models.Commit, _ int, _ int) *CommitRange {
if len(commits) == 0 {
return nil
}
return &CommitRange{
From: commits[len(commits)-1].Hash,
To: commits[0].Hash,
}
}
// SessionState captures the current state of the application for use in custom commands // SessionState captures the current state of the application for use in custom commands
type SessionState struct { type SessionState struct {
SelectedLocalCommit *Commit // deprecated, use SelectedCommit SelectedLocalCommit *Commit // deprecated, use SelectedCommit
SelectedReflogCommit *Commit // deprecated, use SelectedCommit SelectedReflogCommit *Commit // deprecated, use SelectedCommit
SelectedSubCommit *Commit // deprecated, use SelectedCommit SelectedSubCommit *Commit // deprecated, use SelectedCommit
SelectedCommit *Commit SelectedCommit *Commit
SelectedCommitRange *CommitRange
SelectedFile *File SelectedFile *File
SelectedPath string SelectedPath string
SelectedLocalBranch *Branch SelectedLocalBranch *Branch
@ -183,14 +200,20 @@ type SessionState struct {
func (self *SessionStateLoader) call() *SessionState { func (self *SessionStateLoader) call() *SessionState {
selectedLocalCommit := commitShimFromModelCommit(self.c.Contexts().LocalCommits.GetSelected()) selectedLocalCommit := commitShimFromModelCommit(self.c.Contexts().LocalCommits.GetSelected())
selectedLocalCommitRange := makeCommitRange(self.c.Contexts().LocalCommits.GetSelectedItems())
selectedReflogCommit := commitShimFromModelCommit(self.c.Contexts().ReflogCommits.GetSelected()) selectedReflogCommit := commitShimFromModelCommit(self.c.Contexts().ReflogCommits.GetSelected())
selectedReflogCommitRange := makeCommitRange(self.c.Contexts().ReflogCommits.GetSelectedItems())
selectedSubCommit := commitShimFromModelCommit(self.c.Contexts().SubCommits.GetSelected()) selectedSubCommit := commitShimFromModelCommit(self.c.Contexts().SubCommits.GetSelected())
selectedSubCommitRange := makeCommitRange(self.c.Contexts().SubCommits.GetSelectedItems())
selectedCommit := selectedLocalCommit selectedCommit := selectedLocalCommit
selectedCommitRange := selectedLocalCommitRange
if self.c.Context().IsCurrentOrParent(self.c.Contexts().ReflogCommits) { if self.c.Context().IsCurrentOrParent(self.c.Contexts().ReflogCommits) {
selectedCommit = selectedReflogCommit selectedCommit = selectedReflogCommit
selectedCommitRange = selectedReflogCommitRange
} else if self.c.Context().IsCurrentOrParent(self.c.Contexts().SubCommits) { } else if self.c.Context().IsCurrentOrParent(self.c.Contexts().SubCommits) {
selectedCommit = selectedSubCommit selectedCommit = selectedSubCommit
selectedCommitRange = selectedSubCommitRange
} }
selectedPath := self.c.Contexts().Files.GetSelectedPath() selectedPath := self.c.Contexts().Files.GetSelectedPath()
@ -207,6 +230,7 @@ func (self *SessionStateLoader) call() *SessionState {
SelectedReflogCommit: selectedReflogCommit, SelectedReflogCommit: selectedReflogCommit,
SelectedSubCommit: selectedSubCommit, SelectedSubCommit: selectedSubCommit,
SelectedCommit: selectedCommit, SelectedCommit: selectedCommit,
SelectedCommitRange: selectedCommitRange,
SelectedLocalBranch: branchShimFromModelBranch(self.c.Contexts().Branches.GetSelected()), SelectedLocalBranch: branchShimFromModelBranch(self.c.Contexts().Branches.GetSelected()),
SelectedRemoteBranch: remoteBranchShimFromModelRemoteBranch(self.c.Contexts().RemoteBranches.GetSelected()), SelectedRemoteBranch: remoteBranchShimFromModelRemoteBranch(self.c.Contexts().RemoteBranches.GetSelected()),
SelectedRemote: remoteShimFromModelRemote(self.c.Contexts().Remotes.GetSelected()), SelectedRemote: remoteShimFromModelRemote(self.c.Contexts().Remotes.GetSelected()),

View File

@ -0,0 +1,41 @@
package custom_commands
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var SelectedCommitRange = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Use the {{ .SelectedCommitRange }} template variable",
ExtraCmdArgs: []string{},
Skip: false,
SetupRepo: func(shell *Shell) {
shell.CreateNCommits(3)
},
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
{
Key: "X",
Context: "global",
Command: `git log --format="%s" {{.SelectedCommitRange.From}}^..{{.SelectedCommitRange.To}} > file.txt`,
},
}
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().Focus().
Lines(
Contains("commit 03").IsSelected(),
Contains("commit 02"),
Contains("commit 01"),
)
t.GlobalPress("X")
t.FileSystem().FileContent("file.txt", Equals("commit 03\n"))
t.Views().Commits().Focus().
Press(keys.Universal.RangeSelectDown)
t.GlobalPress("X")
t.FileSystem().FileContent("file.txt", Equals("commit 03\ncommit 02\n"))
},
})

View File

@ -139,6 +139,7 @@ var tests = []*components.IntegrationTest{
custom_commands.MultipleContexts, custom_commands.MultipleContexts,
custom_commands.MultiplePrompts, custom_commands.MultiplePrompts,
custom_commands.SelectedCommit, custom_commands.SelectedCommit,
custom_commands.SelectedCommitRange,
custom_commands.SelectedPath, custom_commands.SelectedPath,
custom_commands.ShowOutputInPanel, custom_commands.ShowOutputInPanel,
custom_commands.SuggestionsCommand, custom_commands.SuggestionsCommand,