github.com/x-oss-byte/git-lfs@v2.5.2+incompatible/git/gitattr/attr.go (about)

     1  package gitattr
     2  
     3  import (
     4  	"bufio"
     5  	"io"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/git-lfs/git-lfs/errors"
    10  	"github.com/git-lfs/wildmatch"
    11  )
    12  
    13  // Line carries a single line from a repository's .gitattributes file, affecting
    14  // a single pattern and applying zero or more attributes.
    15  type Line struct {
    16  	// Pattern is a wildmatch pattern that, when matched, indicates that all
    17  	// of the below attributes (Attrs) should be applied to that tree entry.
    18  	//
    19  	// Pattern is relative to the tree in which the .gitattributes was read
    20  	// from. For example, /.gitattributes affects all blobs in the
    21  	// repository, while /path/to/.gitattributes affects all blobs that are
    22  	// direct or indirect children of /path/to.
    23  	Pattern *wildmatch.Wildmatch
    24  	// Attrs is the list of attributes to be applied when the above pattern
    25  	// matches a given filename.
    26  	//
    27  	// It is populated in-order as it was written in the .gitattributes file
    28  	// being read, from left to right.
    29  	Attrs []*Attr
    30  }
    31  
    32  // Attr is a single attribute that may be applied to a file.
    33  type Attr struct {
    34  	// K is the name of the attribute. It is commonly, "filter", "diff",
    35  	// "merge", or "text".
    36  	//
    37  	// It will never contain the special "false" shorthand ("-"), or the
    38  	// unspecify declarative ("!").
    39  	K string
    40  	// V is the value held by that attribute. It is commonly "lfs", or
    41  	// "false", indicating the special value given by a "-"-prefixed name.
    42  	V string
    43  	// Unspecified indicates whether or not this attribute was explicitly
    44  	// unset by prefixing the keyname with "!".
    45  	Unspecified bool
    46  }
    47  
    48  // ParseLines parses the given io.Reader "r" line-wise as if it were the
    49  // contents of a .gitattributes file.
    50  //
    51  // If an error was encountered, it will be returned and the []*Line should be
    52  // considered unusable.
    53  func ParseLines(r io.Reader) ([]*Line, error) {
    54  	var lines []*Line
    55  
    56  	scanner := bufio.NewScanner(r)
    57  	for scanner.Scan() {
    58  
    59  		text := strings.TrimSpace(scanner.Text())
    60  		if len(text) == 0 {
    61  			continue
    62  		}
    63  
    64  		var pattern string
    65  		var applied string
    66  
    67  		switch text[0] {
    68  		case '#':
    69  			continue
    70  		case '"':
    71  			var err error
    72  			last := strings.LastIndex(text, "\"")
    73  			if last == 0 {
    74  				return nil, errors.Errorf("git/gitattr: unbalanced quote: %s", text)
    75  			}
    76  			pattern, err = strconv.Unquote(text[:last+1])
    77  			if err != nil {
    78  				return nil, errors.Wrapf(err, "git/gitattr")
    79  			}
    80  			applied = strings.TrimSpace(text[last+1:])
    81  		default:
    82  			splits := strings.SplitN(text, " ", 2)
    83  
    84  			pattern = splits[0]
    85  			if len(splits) == 2 {
    86  				applied = splits[1]
    87  			}
    88  		}
    89  
    90  		var attrs []*Attr
    91  
    92  		for _, s := range strings.Split(applied, " ") {
    93  			if s == "" {
    94  				continue
    95  			}
    96  
    97  			var attr Attr
    98  
    99  			if strings.HasPrefix(s, "-") {
   100  				attr.K = strings.TrimPrefix(s, "-")
   101  				attr.V = "false"
   102  			} else if strings.HasPrefix(s, "!") {
   103  				attr.K = strings.TrimPrefix(s, "!")
   104  				attr.Unspecified = true
   105  			} else {
   106  				splits := strings.SplitN(s, "=", 2)
   107  				if len(splits) != 2 {
   108  					return nil, errors.Errorf("git/gitattr: malformed attribute: %s", s)
   109  				}
   110  				attr.K = splits[0]
   111  				attr.V = splits[1]
   112  			}
   113  
   114  			attrs = append(attrs, &attr)
   115  		}
   116  
   117  		lines = append(lines, &Line{
   118  			Pattern: wildmatch.NewWildmatch(pattern,
   119  				wildmatch.Basename, wildmatch.SystemCase,
   120  			),
   121  			Attrs: attrs,
   122  		})
   123  	}
   124  
   125  	if err := scanner.Err(); err != nil {
   126  		return nil, err
   127  	}
   128  	return lines, nil
   129  }