Skip to content

Migrate deprecated AllBranchesLogCmd to AllBranchesLogCmds #4345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ git:
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)
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
overrideGpg: false
Expand Down
6 changes: 1 addition & 5 deletions pkg/commands/git_commands/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,7 @@ func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {

func (self *BranchCommands) AllBranchesLogCmdObj() *oscommands.CmdObj {
// Only choose between non-empty, non-identical commands
candidates := lo.Uniq(lo.WithoutEmpty(append([]string{
self.UserConfig().Git.AllBranchesLogCmd,
},
self.UserConfig().Git.AllBranchesLogCmds...,
)))
candidates := lo.Uniq(lo.WithoutEmpty(self.UserConfig().Git.AllBranchesLogCmds))

n := len(candidates)

Expand Down
45 changes: 45 additions & 0 deletions pkg/config/app_config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"errors"
"fmt"
"log"
"os"
Expand All @@ -10,6 +11,7 @@ import (
"time"

"github.com/adrg/xdg"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
"github.com/samber/lo"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -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)
}

err = migrateAllBranchesLogCmd(&rootNode)
if err != nil {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
}

// Add more migrations here...

if !reflect.DeepEqual(rootNode, originalCopy) {
Expand Down Expand Up @@ -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 {
return c.debug
}
Expand Down
86 changes: 86 additions & 0 deletions pkg/config/app_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,3 +781,89 @@ func BenchmarkMigrationOnLargeConfiguration(b *testing.B) {
_, _ = 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))
})
}
}
5 changes: 1 addition & 4 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,6 @@ type GitConfig struct {
AutoStageResolvedConflicts bool `yaml:"autoStageResolvedConflicts"`
// Command used when displaying the current branch git log in the main window
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)
AllBranchesLogCmds []string `yaml:"allBranchesLogCmds"`
// If true, do not spawn a separate process when using GPG
Expand Down Expand Up @@ -823,7 +820,7 @@ func GetDefaultConfig() *UserConfig {
FetchAll: true,
AutoStageResolvedConflicts: true,
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,
CommitPrefixes: map[string][]CommitPrefixConfig(nil),
BranchPrefix: "",
Expand Down
3 changes: 1 addition & 2 deletions pkg/integration/tests/status/log_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ var LogCmd = NewIntegrationTest(NewIntegrationTestArgs{
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {
config.GetUserConfig().Git.AllBranchesLogCmd = `echo "view1"`
config.GetUserConfig().Git.AllBranchesLogCmds = []string{`echo "view2"`}
config.GetUserConfig().Git.AllBranchesLogCmds = []string{`echo "view1"`, `echo "view2"`}
},
SetupRepo: func(shell *Shell) {},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
Expand Down
8 changes: 4 additions & 4 deletions pkg/utils/yaml_utils/yaml_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"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 {
if node.Content[i].Value == key {
return node.Content[i], node.Content[i+1]
Expand Down Expand Up @@ -55,7 +55,7 @@ func transformNode(node *yaml.Node, path []string, transform func(node *yaml.Nod
return transform(node)
}

keyNode, valueNode := lookupKey(node, path[0])
keyNode, valueNode := LookupKey(node, path[0])
if keyNode == nil {
return nil
}
Expand Down Expand Up @@ -86,15 +86,15 @@ func renameYamlKey(node *yaml.Node, path []string, newKey string) error {
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 {
return nil
}

// end of path reached: rename key
if len(path) == 1 {
// 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)
}

Expand Down
10 changes: 4 additions & 6 deletions schema/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,17 +349,15 @@
"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}} --"
},
"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": {
"items": {
"type": "string"
},
"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": {
"type": "boolean",
Expand Down