From aa034b1b5e34b046a2d7f06b8fbe6d6d9cccb7e8 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 7 Nov 2026 23:14:48 +0100 Subject: [PATCH] Parse .gitmodules completely before loading nested submodules So far, GetConfigs loaded the nested submodules of an entry as soon as it saw the entry's path line. Parse the whole file first, and load the nested submodules in a second loop. This prepares for checking the name and path of each entry before using it. This only makes a difference for malformed entries. If the path value is empty, we no longer read the same .gitmodules file over and over. If an entry has several path lines, we no longer load its nested submodules several times. Co-Authored-By: Claude Opus 5.5 --- pkg/commands/git_commands/submodule.go | 37 +++++++++++++++++++------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/pkg/commands/git_commands/submodule.go b/pkg/commands/git_commands/submodule.go index 79d3e2318..80ccbfcdd 100644 --- a/pkg/commands/git_commands/submodule.go +++ b/pkg/commands/git_commands/submodule.go @@ -2,6 +2,7 @@ package git_commands import ( "bufio" + "io" "os" "path/filepath" "regexp" @@ -46,7 +47,28 @@ func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) } defer file.Close() - scanner := bufio.NewScanner(file) + parsedConfigs, err := parseGitmodules(file, parentModule) + if err != nil { + return nil, err + } + + configs := []*models.SubmoduleConfig{} + for _, config := range parsedConfigs { + configs = append(configs, config) + + if config.Path != "" { + nestedConfigs, err := self.GetConfigs(config) + if err == nil { + configs = append(configs, nestedConfigs...) + } + } + } + + return configs, nil +} + +func parseGitmodules(reader io.Reader, parentModule *models.SubmoduleConfig) ([]*models.SubmoduleConfig, error) { + scanner := bufio.NewScanner(reader) scanner.Split(bufio.ScanLines) firstMatch := func(str string, regex string) (string, bool) { @@ -60,7 +82,6 @@ func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) } configs := []*models.SubmoduleConfig{} - lastConfigIdx := -1 for scanner.Scan() { line := scanner.Text() @@ -68,19 +89,15 @@ func (self *SubmoduleCommands) GetConfigs(parentModule *models.SubmoduleConfig) configs = append(configs, &models.SubmoduleConfig{ Name: name, ParentModule: parentModule, }) - lastConfigIdx = len(configs) - 1 continue } - if lastConfigIdx != -1 { + if len(configs) > 0 { + config := configs[len(configs)-1] if path, ok := firstMatch(line, `\s*path\s*=\s*(.*)\s*`); ok { - configs[lastConfigIdx].Path = path - nestedConfigs, err := self.GetConfigs(configs[lastConfigIdx]) - if err == nil { - configs = append(configs, nestedConfigs...) - } + config.Path = path } else if url, ok := firstMatch(line, `\s*url\s*=\s*(.*)\s*`); ok { - configs[lastConfigIdx].Url = url + config.Url = url } } }