Make dotfile handling of GlobNode configurable

Summary:
To provide the ability to ignore dotfiles, we update `GlobNode()` to take a
`bool includeDotfiles` that is used to determine the options used to create
the `GlobMatcher` associated with the node. Whereas the patterns
`**` and `*` were assumed to always match, that is now true only when
`includeDotfiles` is true, as well.

Finally, this adds a special case when `**` is used as the `pattern` for a `GlobNode`
and `includeDotfiles` is `false`. Because `GlobMatcher` does not accept `**` as
input, we specify the pattern as `**/*` to `GlobMatcher`, which it accepts and is
functionally equivalent in our case.

This is safe because we only do this trick when `**` is specified as a leaf `GlobNode`.

Reviewed By: wez

Differential Revision: D7741508

fbshipit-source-id: 9e6a50cb4dab09be2497393c641f176c84316a07
This commit is contained in:
Michael Bolin 2018-05-02 14:41:32 -07:00 committed by Facebook Github Bot
parent bcfbc86081
commit d6fccefb49
3 changed files with 36 additions and 12 deletions

View File

@ -440,7 +440,7 @@ void EdenServiceHandler::glob(
auto rootInode = edenMount->getRootInode();
// Compile the list of globs into a tree
GlobNode globRoot;
GlobNode globRoot(/*includeDotfiles=*/true);
for (auto& globString : *globs) {
globRoot.parse(globString);
}

View File

@ -23,12 +23,16 @@ using std::vector;
namespace facebook {
namespace eden {
GlobNode::GlobNode(StringPiece pattern, bool hasSpecials)
: pattern_(pattern.str()), hasSpecials_(hasSpecials) {
if (pattern == "**" || pattern == "*") {
GlobNode::GlobNode(StringPiece pattern, bool includeDotfiles, bool hasSpecials)
: pattern_(pattern.str()),
includeDotfiles_(includeDotfiles),
hasSpecials_(hasSpecials) {
if (includeDotfiles && (pattern == "**" || pattern == "*")) {
alwaysMatch_ = true;
} else {
auto compiled = GlobMatcher::create(pattern, GlobOptions::DEFAULT);
auto options =
includeDotfiles ? GlobOptions::DEFAULT : GlobOptions::IGNORE_DOTFILES;
auto compiled = GlobMatcher::create(pattern, options);
if (compiled.hasError()) {
throw newEdenError(
EINVAL,
@ -42,6 +46,7 @@ GlobNode::GlobNode(StringPiece pattern, bool hasSpecials)
void GlobNode::parse(StringPiece pattern) {
GlobNode* parent = this;
string normalizedPattern;
while (!pattern.empty()) {
StringPiece token;
@ -51,7 +56,19 @@ void GlobNode::parse(StringPiece pattern) {
if (pattern.startsWith("**")) {
// Recursive match defeats most optimizations; we have to stop
// tokenizing here.
token = pattern;
// HACK: We special-case "**" if includeDotfiles=false. In this case, we
// need to create a GlobMatcher for this pattern, but GlobMatcher is
// designed to reject "**". As a workaround, we use "**/*", which is
// functionally equivalent in this case because there are no other
// "tokens" in the pattern following the "**" at this point.
if (pattern == "**" && !includeDotfiles_) {
normalizedPattern = "**/*";
token = normalizedPattern;
} else {
token = pattern;
}
pattern = StringPiece();
container = &parent->recursiveChildren_;
hasSpecials = true;
@ -61,7 +78,8 @@ void GlobNode::parse(StringPiece pattern) {
auto node = lookupToken(container, token);
if (!node) {
container->emplace_back(std::make_unique<GlobNode>(token, hasSpecials));
container->emplace_back(
std::make_unique<GlobNode>(token, includeDotfiles_, hasSpecials));
node = container->back().get();
}

View File

@ -27,11 +27,11 @@ namespace eden {
*/
class GlobNode {
public:
// Default constructor is intended to create the root of a set of globs
// that will be parsed into the overall glob tree.
GlobNode() = default;
// Single parameter constructor is intended to create the root of a set of
// globs that will be parsed into the overall glob tree.
explicit GlobNode(bool includeDotfiles) : includeDotfiles_(includeDotfiles) {}
GlobNode(folly::StringPiece pattern, bool hasSpecials);
GlobNode(folly::StringPiece pattern, bool includeDotfiles, bool hasSpecials);
// Compile and add a new glob pattern to the tree.
// Compilation splits the pattern into nodes, with one node for each
@ -82,13 +82,19 @@ class GlobNode {
// List of ** child rules
std::vector<std::unique_ptr<GlobNode>> recursiveChildren_;
// For a child GlobNode that is added to this GlobNode (presumably via
// parse()), the GlobMatcher pattern associated with the child node should use
// this value for its includeDotfiles parameter.
bool includeDotfiles_;
// If true, generate results for matches. Only applies
// to non-recursive glob patterns.
bool isLeaf_{false};
// If false we can try a name lookup of pattern rather
// than walking the children and applying the matcher
bool hasSpecials_{false};
// If true, this node is **
// true when both of the following hold:
// - this node is "**" or "*"
// - it was created with includeDotfiles=true.
bool alwaysMatch_{false};
};
} // namespace eden