diff --git a/modules/git/commit.go b/modules/git/commit.go index e7af72a013809..a55a398d7c043 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -9,7 +9,6 @@ import ( "bytes" "context" "errors" - "fmt" "io" "os/exec" "strconv" @@ -17,8 +16,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" - - "github.com/go-git/go-git/v5/config" ) // Commit represents a git commit. @@ -373,34 +370,20 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) { return nil, err } - content, err := entry.Blob().GetBlobContent(10 * 1024) + rd, err := entry.Blob().DataAsync() if err != nil { return nil, err } + defer rd.Close() - c.submoduleCache, err = parseSubmoduleContent([]byte(content)) + r := io.LimitReader(rd, 100*1024) // limit to 100KB + c.submoduleCache, err = parseSubmoduleContent(r) if err != nil { return nil, err } return c.submoduleCache, nil } -func parseSubmoduleContent(bs []byte) (*ObjectCache, error) { - cfg := config.NewModules() - if err := cfg.Unmarshal(bs); err != nil { - return nil, err - } - submoduleCache := newObjectCache() - if len(cfg.Submodules) == 0 { - return nil, fmt.Errorf("no submodules found") - } - for _, subModule := range cfg.Submodules { - submoduleCache.Set(subModule.Path, subModule.URL) - } - - return submoduleCache, nil -} - // GetSubModule get the sub module according entryname func (c *Commit) GetSubModule(entryname string) (*SubModule, error) { modules, err := c.GetSubModules() diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index badec18b1ba72..60165cfa08a97 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -365,30 +365,45 @@ func Test_GetCommitBranchStart(t *testing.T) { func Test_parseSubmoduleContent(t *testing.T) { submoduleFiles := []struct { - fileContent string - expectedPath string - expectedURL string + fileContent string + expectedPath string + expectedURL string + expectedBranch string }{ { fileContent: `[submodule "jakarta-servlet"] url = ../../ALP-pool/jakarta-servlet path = jakarta-servlet`, - expectedPath: "jakarta-servlet", - expectedURL: "../../ALP-pool/jakarta-servlet", + expectedPath: "jakarta-servlet", + expectedURL: "../../ALP-pool/jakarta-servlet", + expectedBranch: "", }, { fileContent: `[submodule "jakarta-servlet"] path = jakarta-servlet url = ../../ALP-pool/jakarta-servlet`, - expectedPath: "jakarta-servlet", - expectedURL: "../../ALP-pool/jakarta-servlet", + expectedPath: "jakarta-servlet", + expectedURL: "../../ALP-pool/jakarta-servlet", + expectedBranch: "", + }, + { + fileContent: `[submodule "jakarta-servlet"] +path = jakarta-servlet +url = ../../ALP-pool/jakarta-servlet +branch = stable`, + expectedPath: "jakarta-servlet", + expectedURL: "../../ALP-pool/jakarta-servlet", + expectedBranch: "stable", }, } for _, kase := range submoduleFiles { - submodule, err := parseSubmoduleContent([]byte(kase.fileContent)) + submodule, err := parseSubmoduleContent(strings.NewReader(kase.fileContent)) assert.NoError(t, err) v, ok := submodule.Get(kase.expectedPath) assert.True(t, ok) - assert.Equal(t, kase.expectedURL, v) + subModule := v.(*SubModule) + assert.Equal(t, kase.expectedPath, subModule.Path) + assert.Equal(t, kase.expectedURL, subModule.URL) + assert.Equal(t, kase.expectedBranch, subModule.Branch) } } diff --git a/modules/git/submodule.go b/modules/git/submodule.go index b99c81582b70e..74d8460637fc5 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -5,7 +5,9 @@ package git import ( + "bufio" "fmt" + "io" "net" "net/url" "path" @@ -17,8 +19,72 @@ var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`) // SubModule submodule is a reference on git repository type SubModule struct { - Name string - URL string + Path string + URL string + Branch string +} + +// parseSubmoduleContent this is not a complete parse for gitmodules file, it only +// parses the url and path of submodules +func parseSubmoduleContent(r io.Reader) (*ObjectCache, error) { + var path, url, branch string + var state int // 0: find section, 1: find path and url + subModules := newObjectCache() + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + // Skip empty lines and comments + if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") { + continue + } + + // Section header [section] + if strings.HasPrefix(line, "[submodule") && strings.HasSuffix(line, "]") { + subModules.Set(path, &SubModule{ + Path: path, + URL: url, + Branch: branch, + }) + state = 1 + path = "" + url = "" + branch = "" + continue + } + + if state != 1 { + continue + } + + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + continue + } + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + switch key { + case "path": + path = value + case "url": + url = value + case "branch": + branch = value + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading file: %w", err) + } + if path != "" && url != "" { + subModules.Set(path, &SubModule{ + Path: path, + URL: url, + Branch: branch, + }) + } + + return subModules, nil } // SubModuleFile represents a file with submodule type.