github.com/catandhorse/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  }