github.com/x-oss-byte/git-lfs@v2.5.2+incompatible/git/gitattr/tree.go (about) 1 package gitattr 2 3 import ( 4 "strings" 5 6 "github.com/git-lfs/gitobj" 7 ) 8 9 // Tree represents the .gitattributes file at one layer of the tree in a Git 10 // repository. 11 type Tree struct { 12 // Lines are the lines of the .gitattributes at this level of the tree. 13 Lines []*Line 14 // Children are the named child directories in the repository. 15 Children map[string]*Tree 16 } 17 18 // New constructs a *Tree starting at the given tree "t" and reading objects 19 // from the given ObjectDatabase. If a tree was not able to be read, an error 20 // will be propagated up accordingly. 21 func New(db *gitobj.ObjectDatabase, t *gitobj.Tree) (*Tree, error) { 22 children := make(map[string]*Tree) 23 lines, err := linesInTree(db, t) 24 if err != nil { 25 return nil, err 26 } 27 28 for _, entry := range t.Entries { 29 if entry.Type() != gitobj.TreeObjectType { 30 continue 31 } 32 33 // For every entry in the current tree, parse its sub-trees to 34 // see if they might contain a .gitattributes. 35 t, err := db.Tree(entry.Oid) 36 if err != nil { 37 return nil, err 38 } 39 40 at, err := New(db, t) 41 if err != nil { 42 return nil, err 43 } 44 45 if len(at.Children) > 0 || len(at.Lines) > 0 { 46 // Only include entries that have either (1) a 47 // .gitattributes in their tree, or (2) a .gitattributes 48 // in a sub-tree. 49 children[entry.Name] = at 50 } 51 } 52 53 return &Tree{ 54 Lines: lines, 55 Children: children, 56 }, nil 57 } 58 59 // linesInTree parses a given tree's .gitattributes and returns a slice of lines 60 // in that .gitattributes, or an error. If no .gitattributes blob was found, 61 // return nil. 62 func linesInTree(db *gitobj.ObjectDatabase, t *gitobj.Tree) ([]*Line, error) { 63 var at int = -1 64 for i, e := range t.Entries { 65 if e.Name == ".gitattributes" { 66 at = i 67 break 68 } 69 } 70 71 if at < 0 { 72 return nil, nil 73 } 74 75 blob, err := db.Blob(t.Entries[at].Oid) 76 if err != nil { 77 return nil, err 78 } 79 defer blob.Close() 80 81 return ParseLines(blob.Contents) 82 } 83 84 // Applied returns a slice of attributes applied to the given path, relative to 85 // the receiving tree. It traverse through sub-trees in a topological ordering, 86 // if there are relevant .gitattributes matching that path. 87 func (t *Tree) Applied(to string) []*Attr { 88 var attrs []*Attr 89 for _, line := range t.Lines { 90 if line.Pattern.Match(to) { 91 attrs = append(attrs, line.Attrs...) 92 } 93 } 94 95 splits := strings.SplitN(to, "/", 2) 96 if len(splits) == 2 { 97 car, cdr := splits[0], splits[1] 98 if child, ok := t.Children[car]; ok { 99 attrs = append(attrs, child.Applied(cdr)...) 100 } 101 } 102 103 return attrs 104 }