1package git23import (4 "bufio"5 "bytes"6 "context"7 "strings"8)910// Submodule contains information of a Git submodule.11type Submodule struct {12 // The name of the submodule.13 Name string14 // The URL of the submodule.15 URL string16 // The commit ID of the subproject.17 Commit string18}1920// Submodules contains information of submodules.21type Submodules = *objectCache2223// Submodules returns submodules found in this commit. Successful results are24// cached; failed attempts are not cached, allowing retries with a fresh context.25func (c *Commit) Submodules(ctx context.Context) (Submodules, error) {26 c.submodulesMu.Lock()27 defer c.submodulesMu.Unlock()2829 if c.submodulesSet {30 return c.submodules, nil31 }3233 e, err := c.TreeEntry(ctx, ".gitmodules")34 if err != nil {35 return nil, err36 }3738 p, err := e.Blob().Bytes(ctx)39 if err != nil {40 return nil, err41 }4243 scanner := bufio.NewScanner(bytes.NewReader(p))44 submodules := newObjectCache()45 var inSection bool46 var path string47 var url string48 for scanner.Scan() {49 if strings.HasPrefix(scanner.Text(), "[submodule") {50 inSection = true51 path = ""52 url = ""53 continue54 } else if !inSection {55 continue56 }5758 key, value, ok := strings.Cut(scanner.Text(), "=")59 if !ok {60 continue61 }6263 switch strings.TrimSpace(key) {64 case "path":65 path = strings.TrimSpace(value)66 case "url":67 url = strings.TrimSpace(value)68 }6970 if len(path) > 0 && len(url) > 0 {71 mod := &Submodule{72 Name: path,73 URL: url,74 }7576 mod.Commit, err = c.repo.RevParse(ctx, c.id.String()+":"+mod.Name)77 if err != nil {78 return nil, err79 }8081 submodules.Set(path, mod)82 inSection = false83 }84 }8586 if err := scanner.Err(); err != nil {87 return nil, err88 }8990 c.submodules = submodules91 c.submodulesSet = true92 return c.submodules, nil93}9495// Submodule returns submodule by given name. It returns an ErrSubmoduleNotExist96// if the path does not exist as a submodule.97func (c *Commit) Submodule(ctx context.Context, path string) (*Submodule, error) {98 mods, err := c.Submodules(ctx)99 if err != nil {100 return nil, err101 }102103 m, has := mods.Get(path)104 if has {105 return m.(*Submodule), nil106 }107 return nil, ErrSubmoduleNotExist108}