prow/gitattributes/gitattributes.go
func (g *Group) load(r io.Reader) error {
	s := bufio.NewScanner(r)
	for s.Scan() {
		// Leading and trailing whitespaces are ignored.
		l := strings.TrimSpace(s.Text())
		// Lines that begin with # are ignored.
		if l == "" || l[0] == '#' {
			continue
		}

		fs := strings.Fields(l)
		if len(fs) < 2 {
			continue
		}

		// When the pattern matches the path in question, the attributes listed on the line are given to the path.
		attributes := sets.NewString(fs[1:]...)
		if attributes.Has("linguist-generated=true") {
			p, err := parsePattern(fs[0])
			if err != nil {
				return fmt.Errorf("error parsing pattern: %v", err)
			}
			g.LinguistGeneratedPatterns = append(g.LinguistGeneratedPatterns, p)
		}
	}

	if err := s.Err(); err != nil {
		return fmt.Errorf("scan error: %v", err)
	}

	return nil
}
func (g *Group) load(r io.Reader) error {
	s := bufio.NewScanner(r)
	for s.Scan() {
		// Leading and trailing whitespaces are ignored.
		l := strings.TrimSpace(s.Text())
		// Lines that begin with # are ignored.
		if l == "" || l[0] == '#' {
			continue
		}

		fs := strings.Fields(l)
		if len(fs) < 2 {
			continue
		}

		// When the pattern matches the path in question, the attributes listed on the line are given to the path.
		attributes := sets.NewString(fs[1:]...)
		if attributes.Has("linguist-generated=true") {
			p, err := parsePattern(fs[0])
			if err != nil {
				return fmt.Errorf("error parsing pattern: %v", err)
			}
			g.LinguistGeneratedPatterns = append(g.LinguistGeneratedPatterns, p)
		}
	}

	if err := s.Err(); err != nil {
		return fmt.Errorf("scan error: %v", err)
	}

	return nil
}
// Use load to read a .gitattributes file, and populate g with the commands.
// Each line in gitattributes file is of form:
//   pattern	attr1 attr2 ...
// That is, a pattern followed by an attributes list, separated by whitespaces.