/* * Does 'match' match the given name? * A match is found if * * (1) the 'match' string is leading directory of 'name', or * (2) the 'match' string is a wildcard and matches 'name', or * (3) the 'match' string is exactly the same as 'name'. * * and the return value tells which case it was. * * It returns 0 when there is no match. */ static int match_pathspec_item(const struct pathspec_item *item, int prefix, const char *name, int namelen) { /* name/namelen has prefix cut off by caller */ const char *match = item->match + prefix; int matchlen = item->len - prefix; /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; if (matchlen <= namelen && !strncmp(match, name, matchlen)) { if (matchlen == namelen) return MATCHED_EXACTLY; if (match[matchlen-1] == '/' || name[matchlen] == '/') return MATCHED_RECURSIVELY; } if (item->nowildcard_len < item->len && !git_fnmatch(match, name, item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0, item->nowildcard_len - prefix)) return MATCHED_FNMATCH; return 0; }
/* * Does 'match' match the given name? * A match is found if * * (1) the 'match' string is leading directory of 'name', or * (2) the 'match' string is a wildcard and matches 'name', or * (3) the 'match' string is exactly the same as 'name'. * * and the return value tells which case it was. * * It returns 0 when there is no match. */ static int match_pathspec_item(const struct pathspec_item *item, int prefix, const char *name, int namelen) { /* name/namelen has prefix cut off by caller */ const char *match = item->match + prefix; int matchlen = item->len - prefix; /* * The normal call pattern is: * 1. prefix = common_prefix_len(ps); * 2. prune something, or fill_directory * 3. match_pathspec_depth() * * 'prefix' at #1 may be shorter than the command's prefix and * it's ok for #2 to match extra files. Those extras will be * trimmed at #3. * * Suppose the pathspec is 'foo' and '../bar' running from * subdir 'xyz'. The common prefix at #1 will be empty, thanks * to "../". We may have xyz/foo _and_ XYZ/foo after #2. The * user does not want XYZ/foo, only the "foo" part should be * case-insensitive. We need to filter out XYZ/foo here. In * other words, we do not trust the caller on comparing the * prefix part when :(icase) is involved. We do exact * comparison ourselves. * * Normally the caller (common_prefix_len() in fact) does * _exact_ matching on name[-prefix+1..-1] and we do not need * to check that part. Be defensive and check it anyway, in * case common_prefix_len is changed, or a new caller is * introduced that does not use common_prefix_len. * * If the penalty turns out too high when prefix is really * long, maybe change it to * strncmp(match, name, item->prefix - prefix) */ if (item->prefix && (item->magic & PATHSPEC_ICASE) && strncmp(item->match, name - prefix, item->prefix)) return 0; /* If the match was just the prefix, we matched */ if (!*match) return MATCHED_RECURSIVELY; if (matchlen <= namelen && !ps_strncmp(item, match, name, matchlen)) { if (matchlen == namelen) return MATCHED_EXACTLY; if (match[matchlen-1] == '/' || name[matchlen] == '/') return MATCHED_RECURSIVELY; } if (item->nowildcard_len < item->len && !git_fnmatch(item, match, name, item->nowildcard_len - prefix)) return MATCHED_FNMATCH; return 0; }
/* * Is a tree entry interesting given the pathspec we have? * * Pre-condition: either baselen == base_offset (i.e. empty path) * or base[baselen-1] == '/' (i.e. with trailing slash). */ static enum interesting do_match(const struct name_entry *entry, struct strbuf *base, int base_offset, const struct pathspec *ps, int exclude) { int i; int pathlen, baselen = base->len - base_offset; enum interesting never_interesting = ps->has_wildcard ? entry_not_interesting : all_entries_not_interesting; GUARD_PATHSPEC(ps, PATHSPEC_FROMTOP | PATHSPEC_MAXDEPTH | PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE | PATHSPEC_EXCLUDE); if (!ps->nr) { if (!ps->recursive || !(ps->magic & PATHSPEC_MAXDEPTH) || ps->max_depth == -1) return all_entries_interesting; return within_depth(base->buf + base_offset, baselen, !!S_ISDIR(entry->mode), ps->max_depth) ? entry_interesting : entry_not_interesting; } pathlen = tree_entry_len(entry); for (i = ps->nr - 1; i >= 0; i--) { const struct pathspec_item *item = ps->items+i; const char *match = item->match; const char *base_str = base->buf + base_offset; int matchlen = item->len, matched = 0; if ((!exclude && item->magic & PATHSPEC_EXCLUDE) || ( exclude && !(item->magic & PATHSPEC_EXCLUDE))) continue; if (baselen >= matchlen) { /* If it doesn't match, move along... */ if (!match_dir_prefix(item, base_str, match, matchlen)) goto match_wildcards; if (!ps->recursive || !(ps->magic & PATHSPEC_MAXDEPTH) || ps->max_depth == -1) return all_entries_interesting; return within_depth(base_str + matchlen + 1, baselen - matchlen - 1, !!S_ISDIR(entry->mode), ps->max_depth) ? entry_interesting : entry_not_interesting; } /* Either there must be no base, or the base must match. */ if (baselen == 0 || !basecmp(item, base_str, match, baselen)) { if (match_entry(item, entry, pathlen, match + baselen, matchlen - baselen, &never_interesting)) return entry_interesting; if (item->nowildcard_len < item->len) { if (!git_fnmatch(item, match + baselen, entry->path, item->nowildcard_len - baselen)) return entry_interesting; /* * Match all directories. We'll try to * match files later on. */ if (ps->recursive && S_ISDIR(entry->mode)) return entry_interesting; } continue; } match_wildcards: if (item->nowildcard_len == item->len) continue; if (item->nowildcard_len && !match_wildcard_base(item, base_str, baselen, &matched)) continue; /* * Concatenate base and entry->path into one and do * fnmatch() on it. * * While we could avoid concatenation in certain cases * [1], which saves a memcpy and potentially a * realloc, it turns out not worth it. Measurement on * linux-2.6 does not show any clear improvements, * partly because of the nowildcard_len optimization * in git_fnmatch(). Avoid micro-optimizations here. * * [1] if match_wildcard_base() says the base * directory is already matched, we only need to match * the rest, which is shorter so _in theory_ faster. */ strbuf_add(base, entry->path, pathlen); if (!git_fnmatch(item, match, base->buf + base_offset, item->nowildcard_len)) { strbuf_setlen(base, base_offset + baselen); return entry_interesting; } strbuf_setlen(base, base_offset + baselen); /* * Match all directories. We'll try to match files * later on. * max_depth is ignored but we may consider support it * in future, see * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840 */ if (ps->recursive && S_ISDIR(entry->mode)) return entry_interesting; } return never_interesting; /* No matches */ }