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 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-11-07 23:59:32 +01:00
co-authored by Claude Opus 5.5
parent 2f18399841
commit aa034b1b5e
+27 -10
View File
@@ -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
}
}
}