Imported Upstream version 2.5.0
[scm/test.git] / git / gitattr / tree.go
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 }