Add the following function (name not final) that takes a mapping from basedirs to gitignore patterns and produces an object that takes the gitignore patterns for multiple directories into account at once as appropriate:
compile_tree(
patterns: Mapping[
AnyStr | os.PathLike[AnyStr],
Iterable[AnyStr] | Gitignore[AnyStr]
],
ignorecase: bool = False,
) -> GitignoreTree[AnyStr]
-
Basedirs must be normalized and relative, possibly with trailing slashes
-
To refer to the root directory, use os.curdir (or an empty string?)
-
Raises an exception if the same basedir is given more than once
-
If there's an entry for a given basedir, and the patterns for a parent directory match that basedir, should the basedir's patterns be honored (as though the basedir were tracked) or not?
-
Possible alternative API: The user constructs an empty GitignoreTree (with optional ignorecase option) and then adds basedir+patterns pairs one at a time (via __setitem__ or some other method?)
- If the same basedir is given more than once (even in different forms), the patterns for the old entry are replaced (unless you use some
extend_dir_patterns(basedir, patterns) method)
-
Regardless of which API is used, GitignoreTrees should be mutable, i.e., it should be possible to add more Gitignores to them after construction. This will be useful when progressively walking a directory tree, e.g., with iterpath.
Notes on how Git combines multiple .gitignore files:
- If
/.gitignore contains bar or foo/bar, and foo/.gitignore contains !bar, foo/bar will not be ignored.
- If
/.gitignore contains !bar, and foo/.gitignore contains bar,
foo/bar will be ignored.
- If
/.gitignore contains !foo, and foo/.gitignore contains bar, foo/bar will be ignored.
- If
/.gitignore contains foo and foo/.gitignore contains !bar, foo/bar will be ignored.
- If
foo/.gitignore is forcibly added to Git, foo/bar will still be ignored.
- If
/.gitignore contains both bar & foo/.gitignore and foo/.gitignore contains !bar, foo/bar will not be ignored, even if foo/.gitignore is forcibly added to Git.
- If
foo/.gitignore contains foo, then foo/bar will not be ignored.
- If
foo/bar/.gitignore contains foo, then foo/bar/baz will not be ignored.
- If
.gitignore contains /bar, then foo/bar will not be ignored.
Conclusions:
-
Matching a path in a tree is done by concatenating all .gitignores from the root of the tree up through the directory containing the path (ordered from the root upwards) and using that as the pattern set to test against, except that a pattern from foo/.gitignore will be tested against paths relative to foo/.
- Note that the rule about testing parent directories first still applies, and each parent directory is only tested against
.gitignores up through the parent's parent directory.
-
Ignoring a .gitignore but not its containing directory has no effect on whether anything in the directory is ignored
Add the following function (name not final) that takes a mapping from basedirs to
gitignorepatterns and produces an object that takes thegitignorepatterns for multiple directories into account at once as appropriate:Basedirs must be normalized and relative, possibly with trailing slashes
To refer to the root directory, use
os.curdir(or an empty string?)Raises an exception if the same basedir is given more than once
If there's an entry for a given basedir, and the patterns for a parent directory match that basedir, should the basedir's patterns be honored (as though the basedir were tracked) or not?
Possible alternative API: The user constructs an empty
GitignoreTree(with optionalignorecaseoption) and then adds basedir+patterns pairs one at a time (via__setitem__or some other method?)extend_dir_patterns(basedir, patterns)method)Regardless of which API is used,
GitignoreTrees should be mutable, i.e., it should be possible to add moreGitignores to them after construction. This will be useful when progressively walking a directory tree, e.g., withiterpath.Notes on how Git combines multiple
.gitignorefiles:/.gitignorecontainsbarorfoo/bar, andfoo/.gitignorecontains!bar,foo/barwill not be ignored./.gitignorecontains!bar, andfoo/.gitignorecontainsbar,foo/barwill be ignored./.gitignorecontains!foo, andfoo/.gitignorecontainsbar,foo/barwill be ignored./.gitignorecontainsfooandfoo/.gitignorecontains!bar,foo/barwill be ignored.foo/.gitignoreis forcibly added to Git,foo/barwill still be ignored./.gitignorecontains bothbar&foo/.gitignoreandfoo/.gitignorecontains!bar,foo/barwill not be ignored, even iffoo/.gitignoreis forcibly added to Git.foo/.gitignorecontainsfoo, thenfoo/barwill not be ignored.foo/bar/.gitignorecontainsfoo, thenfoo/bar/bazwill not be ignored..gitignorecontains/bar, thenfoo/barwill not be ignored.Conclusions:
Matching a path in a tree is done by concatenating all
.gitignoresfrom the root of the tree up through the directory containing the path (ordered from the root upwards) and using that as the pattern set to test against, except that a pattern fromfoo/.gitignorewill be tested against paths relative tofoo/..gitignores up through the parent's parent directory.Ignoring a
.gitignorebut not its containing directory has no effect on whether anything in the directory is ignored