/
niceSOFT
/
git-lfs
Обзор
Документация
Войти
/
niceSOFT
/
git-lfs
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
git/gitattr/tree.go
225 строк
5 KB
Julien Marzal
feat[tree]: method to get special attributes
31 янв 2026, 19:46
31 янв 2026, 19:46
69e0804
Код
Авторство
О чём код?
package gitattr import ( "io" "os" "strings" "github.com/git-lfs/git-lfs/v3/errors" "github.com/git-lfs/git-lfs/v3/tr" "github.com/git-lfs/gitobj/v2" ) // Environment is a restricted version of config.Environment that only provides // a single method. type Environment interface { // Get is shorthand for calling `e.Fetcher.Get(key)`. Get(key string) (val string, ok bool) } // Tree represents the .gitattributes file at one layer of the tree in a Git // repository. type Tree struct { mp *MacroProcessor systemAttributes *Tree userAttributes *Tree // lines are the lines of the .gitattributes at this level of the tree. lines []Line // children are the named child directories in the repository. children map[string]*Tree repoAttributes *Tree } // New constructs a *Tree starting at the given tree "t" and reading objects // from the given ObjectDatabase. If a tree was not able to be read, an error // will be propagated up accordingly. func New(db *gitobj.ObjectDatabase, t *gitobj.Tree) (*Tree, error) { processor := NewMacroProcessor() return newFromGitTree(db, t, processor) } func newFromGitTree(db *gitobj.ObjectDatabase, t *gitobj.Tree, mp *MacroProcessor) (*Tree, error) { children := make(map[string]*Tree) tree, err := linesInTree(db, t, mp) if err != nil { return nil, err } for _, entry := range t.Entries { if entry.Type() != gitobj.TreeObjectType { continue } // For every entry in the current tree, parse its sub-trees to // see if they might contain a .gitattributes. subGitTree, err := db.Tree(entry.Oid) if err != nil { return nil, err } subTree, err := newFromGitTree(db, subGitTree, mp) if err != nil { return nil, err } if len(subTree.children) > 0 || len(subTree.lines) > 0 { // Only include entries that have either (1) a // .gitattributes in their tree, or (2) a .gitattributes // in a sub-tree. children[entry.Name] = subTree } } tree.children = children return tree, nil } func NewFromReader(mp *MacroProcessor, rdr io.Reader) (*Tree, error) { lines, _, err := ParseLines(rdr) if err != nil { return nil, err } return &Tree{ mp: mp, lines: lines, }, nil } func (t *Tree) FindSpecialAttributes(gitEnv, osEnv Environment, gitDir string) error { systemPath, err := GetSystemAttributeFilePath(osEnv) if err != nil { return err } if len(systemPath) != 0 { systemReader, err := os.Open(systemPath) if err != nil { return err } defer systemReader.Close() t.systemAttributes, err = NewFromReader(t.mp, systemReader) if err != nil { return err } } userPath, err := GetUserAttributeFilePath(gitEnv) if err != nil { return err } if len(userPath) != 0 { userReader, err := os.Open(userPath) if err != nil { return err } defer userReader.Close() if t.userAttributes, err = NewFromReader(t.mp, userReader); err != nil { return err } } repoPath, err := GetRepoAttributeFilePath(gitDir) if err != nil { return err } if len(repoPath) != 0 { repoReader, err := os.Open(repoPath) if err != nil { return err } defer repoReader.Close() t.repoAttributes, err = NewFromReader(t.mp, repoReader) } return err } // linesInTree parses a given tree's .gitattributes and returns a slice of lines // in that .gitattributes, or an error. If no .gitattributes blob was found, // return nil. func linesInTree(db *gitobj.ObjectDatabase, t *gitobj.Tree, mp *MacroProcessor) (*Tree, error) { var at int = -1 for i, e := range t.Entries { if e.Name == ".gitattributes" { if e.IsLink() { return nil, errors.New(tr.Tr.Get("expected '.gitattributes' to be a file, got a symbolic link")) } at = i break } } if at < 0 { return &Tree{mp: mp}, nil } blob, err := db.Blob(t.Entries[at].Oid) if err != nil { return nil, err } defer blob.Close() return NewFromReader(mp, blob.Contents) } // Applied returns a slice of attributes applied to the given path, relative to // the receiving tree. It traverse through sub-trees in a topological ordering, // if there are relevant .gitattributes matching that path. func (t *Tree) Applied(to string) []*Attr { var attrs []*Attr if !t.mp.didReadMacros { if t.systemAttributes != nil { t.mp.ProcessMacros(t.systemAttributes.lines) } if t.userAttributes != nil { t.mp.ProcessMacros(t.userAttributes.lines) } t.mp.ProcessMacros(t.lines) if t.repoAttributes != nil { t.mp.ProcessMacros(t.repoAttributes.lines) } } if t.systemAttributes != nil { attrs = append(attrs, t.systemAttributes.applied(to)...) } if t.userAttributes != nil { attrs = append(attrs, t.userAttributes.applied(to)...) } attrs = append(attrs, t.applied(to)...) if t.repoAttributes != nil { attrs = append(attrs, t.repoAttributes.applied(to)...) } return attrs } func (t *Tree) applied(to string) []*Attr { var attrs []*Attr for _, line := range t.mp.ProcessLines(t.lines, false) { if line.Pattern().Match(to) { attrs = append(attrs, line.Attrs()...) } } dirPath, remainingPath, found := strings.Cut(to, "/") if found { if child, ok := t.children[dirPath]; ok { attrs = append(attrs, child.applied(remainingPath)...) } } return attrs }