Migrate deprecated AllBranchesLogCmd to AllBranchesLogCmds

This solves https://github.com/jesseduffield/lazygit/issues/3961 because
we no longer have a combination of the default and the user defined
list. We just have the user defined list.
This commit is contained in:
Chris McDonnell 2025-02-25 23:44:37 -05:00
parent c16c9f982f
commit 1028f8efb8
8 changed files with 144 additions and 22 deletions

View file

@ -354,7 +354,8 @@ git:
branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} -- branchLogCmd: git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --
# Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings) # Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings)
allBranchesLogCmds: [] allBranchesLogCmds:
- git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium
# If true, do not spawn a separate process when using GPG # If true, do not spawn a separate process when using GPG
overrideGpg: false overrideGpg: false

View file

@ -257,11 +257,7 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
func (self *BranchCommands) AllBranchesLogCmdObj() *oscommands.CmdObj { func (self *BranchCommands) AllBranchesLogCmdObj() *oscommands.CmdObj {
// Only choose between non-empty, non-identical commands // Only choose between non-empty, non-identical commands
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{ candidates := lo.Uniq(lo.WithoutEmpty(self.UserConfig().Git.AllBranchesLogCmds))
self.UserConfig().Git.AllBranchesLogCmd,
},
self.UserConfig().Git.AllBranchesLogCmds...,
)))
n := len(candidates) n := len(candidates)

View file

@ -1,6 +1,7 @@
package config package config
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -10,6 +11,7 @@ import (
"time" "time"
"github.com/adrg/xdg" "github.com/adrg/xdg"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils" "github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
"github.com/samber/lo" "github.com/samber/lo"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -286,6 +288,11 @@ func computeMigratedConfig(path string, content []byte) ([]byte, error) {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err) return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
} }
err = migrateAllBranchesLogCmd(&rootNode)
if err != nil {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
}
// Add more migrations here... // Add more migrations here...
if !reflect.DeepEqual(rootNode, originalCopy) { if !reflect.DeepEqual(rootNode, originalCopy) {
@ -386,6 +393,44 @@ func changeCustomCommandStreamAndOutputToOutputEnum(rootNode *yaml.Node) error {
}) })
} }
// This migration is special because users have already defined
// a single element at `allBranchesLogCmd` and the sequence at `allBranchesLogCmds`.
// Some users have explicitly set `allBranchesLogCmd` to be an empty string in order
// to remove it, so in that case we just delete the element, and add nothing to the list
func migrateAllBranchesLogCmd(rootNode *yaml.Node) error {
return yaml_utils.TransformNode(rootNode, []string{"git"}, func(gitNode *yaml.Node) error {
cmdKeyNode, cmdValueNode := yaml_utils.LookupKey(gitNode, "allBranchesLogCmd")
// Nothing to do if they do not have the deprecated item
if cmdKeyNode == nil {
return nil
}
cmdsKeyNode, cmdsValueNode := yaml_utils.LookupKey(gitNode, "allBranchesLogCmds")
if cmdsKeyNode == nil {
// Create empty sequence node and attach it onto the root git node
// We will later populate it with the individual allBranchesLogCmd record
cmdsKeyNode = &yaml.Node{Kind: yaml.ScalarNode, Value: "allBranchesLogCmds"}
cmdsValueNode = &yaml.Node{Kind: yaml.SequenceNode, Content: []*yaml.Node{}}
gitNode.Content = append(gitNode.Content,
cmdsKeyNode,
cmdsValueNode,
)
} else if cmdsValueNode.Kind != yaml.SequenceNode {
return errors.New("You should have an allBranchesLogCmds defined as a sequence!")
}
if cmdValueNode.Value != "" {
// Prepending the individual element to make it show up first in the list, which was prior behavior
cmdsValueNode.Content = utils.Prepend(cmdsValueNode.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: cmdValueNode.Value})
}
// Clear out the existing allBranchesLogCmd, now that we have migrated it into the list
_, _ = yaml_utils.RemoveKey(gitNode, "allBranchesLogCmd")
return nil
})
}
func (c *AppConfig) GetDebug() bool { func (c *AppConfig) GetDebug() bool {
return c.debug return c.debug
} }

View file

@ -781,3 +781,89 @@ func BenchmarkMigrationOnLargeConfiguration(b *testing.B) {
_, _ = computeMigratedConfig("path doesn't matter", largeConfiguration) _, _ = computeMigratedConfig("path doesn't matter", largeConfiguration)
} }
} }
func TestAllBranchesLogCmdMigrations(t *testing.T) {
scenarios := []struct {
name string
input string
expected string
}{
{
name: "Incomplete Configuration Passes uneventfully",
input: "git:",
expected: "git:",
}, {
name: "Single Cmd with no Cmds",
input: `git:
allBranchesLogCmd: git log --graph --oneline
`,
expected: `git:
allBranchesLogCmds:
- git log --graph --oneline
`,
}, {
name: "Cmd with one existing Cmds",
input: `git:
allBranchesLogCmd: git log --graph --oneline
allBranchesLogCmds:
- git log --graph --oneline --pretty
`,
expected: `git:
allBranchesLogCmds:
- git log --graph --oneline
- git log --graph --oneline --pretty
`,
}, {
name: "Only Cmds set have no changes",
input: `git:
allBranchesLogCmds:
- git log
`,
expected: `git:
allBranchesLogCmds:
- git log
`,
}, {
name: "Removes Empty Cmd When at end of yaml",
input: `git:
allBranchesLogCmds:
- git log --graph --oneline
allBranchesLogCmd:
`,
expected: `git:
allBranchesLogCmds:
- git log --graph --oneline
`,
}, {
name: "Migrates when sequence defined inline",
input: `git:
allBranchesLogCmds: [foo, bar]
allBranchesLogCmd: baz
`,
expected: `git:
allBranchesLogCmds: [baz, foo, bar]
`,
}, {
name: "Removes Empty Cmd With Keys Afterwards",
input: `git:
allBranchesLogCmds:
- git log --graph --oneline
allBranchesLogCmd:
foo: bar
`,
expected: `git:
allBranchesLogCmds:
- git log --graph --oneline
foo: bar
`,
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
actual, err := computeMigratedConfig("path doesn't matter", []byte(s.input))
assert.NoError(t, err)
assert.Equal(t, s.expected, string(actual))
})
}
}

View file

@ -256,9 +256,6 @@ type GitConfig struct {
AutoStageResolvedConflicts bool `yaml:"autoStageResolvedConflicts"` AutoStageResolvedConflicts bool `yaml:"autoStageResolvedConflicts"`
// Command used when displaying the current branch git log in the main window // Command used when displaying the current branch git log in the main window
BranchLogCmd string `yaml:"branchLogCmd"` BranchLogCmd string `yaml:"branchLogCmd"`
// Command used to display git log of all branches in the main window.
// Deprecated: Use `allBranchesLogCmds` instead.
AllBranchesLogCmd string `yaml:"allBranchesLogCmd" jsonschema:"deprecated"`
// Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings) // Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings)
AllBranchesLogCmds []string `yaml:"allBranchesLogCmds"` AllBranchesLogCmds []string `yaml:"allBranchesLogCmds"`
// If true, do not spawn a separate process when using GPG // If true, do not spawn a separate process when using GPG
@ -823,7 +820,7 @@ func GetDefaultConfig() *UserConfig {
FetchAll: true, FetchAll: true,
AutoStageResolvedConflicts: true, AutoStageResolvedConflicts: true,
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --", BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium", AllBranchesLogCmds: []string{"git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium"},
DisableForcePushing: false, DisableForcePushing: false,
CommitPrefixes: map[string][]CommitPrefixConfig(nil), CommitPrefixes: map[string][]CommitPrefixConfig(nil),
BranchPrefix: "", BranchPrefix: "",

View file

@ -10,8 +10,7 @@ var LogCmd = NewIntegrationTest(NewIntegrationTestArgs{
ExtraCmdArgs: []string{}, ExtraCmdArgs: []string{},
Skip: false, Skip: false,
SetupConfig: func(config *config.AppConfig) { SetupConfig: func(config *config.AppConfig) {
config.GetUserConfig().Git.AllBranchesLogCmd = `echo "view1"` config.GetUserConfig().Git.AllBranchesLogCmds = []string{`echo "view1"`, `echo "view2"`}
config.GetUserConfig().Git.AllBranchesLogCmds = []string{`echo "view2"`}
}, },
SetupRepo: func(shell *Shell) {}, SetupRepo: func(shell *Shell) {},
Run: func(t *TestDriver, keys config.KeybindingConfig) { Run: func(t *TestDriver, keys config.KeybindingConfig) {

View file

@ -9,7 +9,7 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
func lookupKey(node *yaml.Node, key string) (*yaml.Node, *yaml.Node) { func LookupKey(node *yaml.Node, key string) (*yaml.Node, *yaml.Node) {
for i := 0; i < len(node.Content)-1; i += 2 { for i := 0; i < len(node.Content)-1; i += 2 {
if node.Content[i].Value == key { if node.Content[i].Value == key {
return node.Content[i], node.Content[i+1] return node.Content[i], node.Content[i+1]
@ -55,7 +55,7 @@ func transformNode(node *yaml.Node, path []string, transform func(node *yaml.Nod
return transform(node) return transform(node)
} }
keyNode, valueNode := lookupKey(node, path[0]) keyNode, valueNode := LookupKey(node, path[0])
if keyNode == nil { if keyNode == nil {
return nil return nil
} }
@ -86,7 +86,7 @@ func renameYamlKey(node *yaml.Node, path []string, newKey string) error {
return errors.New("yaml node in path is not a dictionary") return errors.New("yaml node in path is not a dictionary")
} }
keyNode, valueNode := lookupKey(node, path[0]) keyNode, valueNode := LookupKey(node, path[0])
if keyNode == nil { if keyNode == nil {
return nil return nil
} }
@ -94,7 +94,7 @@ func renameYamlKey(node *yaml.Node, path []string, newKey string) error {
// end of path reached: rename key // end of path reached: rename key
if len(path) == 1 { if len(path) == 1 {
// Check that new key doesn't exist yet // Check that new key doesn't exist yet
if newKeyNode, _ := lookupKey(node, newKey); newKeyNode != nil { if newKeyNode, _ := LookupKey(node, newKey); newKeyNode != nil {
return fmt.Errorf("new key `%s' already exists", newKey) return fmt.Errorf("new key `%s' already exists", newKey)
} }

View file

@ -349,17 +349,15 @@
"description": "Command used when displaying the current branch git log in the main window", "description": "Command used when displaying the current branch git log in the main window",
"default": "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --" "default": "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --"
}, },
"allBranchesLogCmd": {
"type": "string",
"description": "Command used to display git log of all branches in the main window.\nDeprecated: Use `allBranchesLogCmds` instead.",
"default": "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium"
},
"allBranchesLogCmds": { "allBranchesLogCmds": {
"items": { "items": {
"type": "string" "type": "string"
}, },
"type": "array", "type": "array",
"description": "Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings)" "description": "Commands used to display git log of all branches in the main window, they will be cycled in order of appearance (array of strings)",
"default": [
"git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium"
]
}, },
"overrideGpg": { "overrideGpg": {
"type": "boolean", "type": "boolean",