When an attribute does not start with '!', '-', or contain an '=', we
had previously treated it as malformed. Instead, assume that it is a
'set' attribute, which we interpret to mean as being set implicitly to
"true" (and therefore the dual of the '-' prefix).
From the relevant documentation [1]:
Set
The path has the attribute with special value "true"; this is
specified by listing only the name of the attribute in the attribute
list.
[1]: https://git-scm.com/docs/gitattributes
In ParseLines, we read the contents of an io.Reader producing the
contents of a '.gitattributes' file, and fill a slice with `*Line`'s,
one corresponding to each line in the file.
Currently, we handle cases for the following attribute types:
-foo set foo to "false" (unset)
foo=bar set foo to "bar" (set to a value)
!foo unspecify foo (unspecified)
We parse each line by first seeing if it is unset, then if it is
unspecified, and then if it is set to a value.
If we are in the "let's see if it set to a value" and fail to split the
string on the assignment operator, '=', we complain that the attribute
is ill-defined.
Let's harden this check to _only_ assume that it is set to a value when
there is an indicating '=' sign, and temporarily ignore attributes that
don't start with a '-', '!', or contain an '='.
When writing this test in 58ae7f7f (git/gitattr: introduce package
'gitattr', 2018-07-06), code was introduced to check each line of a
multi-line .gitattributes file.
Since each line was similar, there's a good chance that we did some
over-eager copy/pasting, and wound up checking the same thing twice.
To fix this, let's increment the index of the line that we're testing,
to ensure that all lines are tested correctly.
The TestNewDiscovers*ChildrenTrees tests ensure that child trees are
discovered and applied to given paths correctly.
To make sure that we aren't applying patterns from the wrong place,
let's ensure that the top-level Lines is empty, implying that any
applied attributes come from a child.
This is a safe thing to do, since Lines and Children are both part of
the public scope of a *gitattr.Tree instance, and therefore should be in
a correct state and use-able by any caller who wishes to do so.
This commit introduces package github.com/git-lfs/git-lfs/git/gitattr,
which manifests a tree-like representation of a repository's
.gitattribute file(s).
It introduces the following top-level and member functions:
- func ParseLines(r io.Reader) ([]*Line, error)
Parses the lines of a .gitattributes-like io.Reader into a set of
wildmatch to attribute-set pairings, or an error if one was
encountered.
- func New(db *gitobj.ObjectDatabase, t *gitobj.Tree) (*Tree, error)
Visits all trees reachable from "t" in a depth-first search to
reconstruct a tree-representation of all .gitattributes file(s) in the
repository.
- func (t *Tree) Applied(to string) []*Attr
Finds all attributes in-order that are applied to "to" relative to the
receiving *Tree's location in the repository's tree structure.
This will become useful in subsequent commits when this is used to scan
a repository's .gitattributes files before rewriting the blobs in that
repository during the forthcoming 'git lfs migrate import --fixup' mode.