From: Junio C Hamano Date: Mon, 24 Jan 2011 19:06:13 +0000 (-0800) Subject: Merge branch 'nd/struct-pathspec' into next X-Git-Tag: ko-next~4 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=08f177421047bc3124cf2cd917446de4a97062db;p=git.git Merge branch 'nd/struct-pathspec' into next * nd/struct-pathspec: (21 commits) t7810: overlapping pathspecs and depth limit grep: drop pathspec_matches() in favor of tree_entry_interesting() grep: use writable strbuf from caller for grep_tree() grep: use match_pathspec_depth() for cache/worktree grepping grep: convert to use struct pathspec Convert ce_path_match() to use match_pathspec_depth() Convert ce_path_match() to use struct pathspec struct rev_info: convert prune_data to struct pathspec pathspec: add match_pathspec_depth() tree_entry_interesting(): optimize wildcard matching when base is matched tree_entry_interesting(): support wildcard matching tree_entry_interesting(): fix depth limit with overlapping pathspecs tree_entry_interesting(): support depth limit tree_entry_interesting(): refactor into separate smaller functions diff-tree: convert base+baselen to writable strbuf glossary: define pathspec Move tree_entry_interesting() to tree-walk.c and export it tree_entry_interesting(): remove dependency on struct diff_options Convert struct diff_options to use struct pathspec diff-no-index: use diff_tree_setup_paths() ... Conflicts: tree-diff.c --- 08f177421047bc3124cf2cd917446de4a97062db diff --cc tree-diff.c index 12c9a8888,e27782c70..3954281f5 --- a/tree-diff.c +++ b/tree-diff.c @@@ -241,30 -106,35 +106,27 @@@ static void show_entry(struct diff_opti if (!tree || type != OBJ_TREE) die("corrupt tree sha %s", sha1_to_hex(sha1)); - if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) { - newbase[baselen + pathlen] = 0; - opt->add_remove(opt, *prefix, mode, sha1, newbase, 0); - newbase[baselen + pathlen] = '/'; - } + if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) + opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0); - init_tree_desc(&inner, tree, size); - show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen); + strbuf_addch(base, '/'); + init_tree_desc(&inner, tree, size); + show_tree(opt, prefix, &inner, base); free(tree); - free(newbase); - } else { - char *fullname = malloc_fullname(base, baselen, path, pathlen); - opt->add_remove(opt, prefix[0], mode, sha1, fullname, 0); - free(fullname); - } + } else + opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0); + + strbuf_setlen(base, old_baselen); } - static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt, int *all_interesting) + static void skip_uninteresting(struct tree_desc *t, struct strbuf *base, - struct diff_options *opt) ++ struct diff_options *opt, int *all_interesting) { - int all_interesting = 0; while (t->size) { - int show = tree_entry_interesting(t, base, baselen, opt); - int show; - - if (all_interesting) - show = 1; - else { - show = tree_entry_interesting(&t->entry, base, 0, - &opt->pathspec); - if (show == 2) - all_interesting = 1; - } ++ int show = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec); + if (show == 2) + *all_interesting = 1; if (!show) { update_tree_entry(t); continue; @@@ -276,23 -146,26 +138,30 @@@ } } - int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt) + int diff_tree(struct tree_desc *t1, struct tree_desc *t2, + const char *base_str, struct diff_options *opt) { - int baselen = strlen(base); + struct strbuf base; + int baselen = strlen(base_str); + int all_t1_interesting = 0; + int all_t2_interesting = 0; + /* Enable recursion indefinitely */ + opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE); + opt->pathspec.max_depth = -1; + + strbuf_init(&base, PATH_MAX); + strbuf_add(&base, base_str, baselen); + for (;;) { if (DIFF_OPT_TST(opt, QUICK) && DIFF_OPT_TST(opt, HAS_CHANGES)) break; - if (opt->nr_paths) { + if (opt->pathspec.nr) { - skip_uninteresting(t1, &base, opt); - skip_uninteresting(t2, &base, opt); + if (!all_t1_interesting) - skip_uninteresting(t1, base, baselen, opt, - &all_t1_interesting); ++ skip_uninteresting(t1, &base, opt, &all_t1_interesting); + if (!all_t2_interesting) - skip_uninteresting(t2, base, baselen, opt, - &all_t2_interesting); ++ skip_uninteresting(t2, &base, opt, &all_t2_interesting); } if (!t1->size) { if (!t2->size) diff --cc tree-walk.c index a9bbf4e23,9b43ad58b..ddcf50c30 --- a/tree-walk.c +++ b/tree-walk.c @@@ -455,3 -456,183 +456,185 @@@ int get_tree_entry(const unsigned char free(tree); return retval; } + + static int match_entry(const struct name_entry *entry, int pathlen, + const char *match, int matchlen, + int *never_interesting) + { + int m = -1; /* signals that we haven't called strncmp() */ + + if (*never_interesting) { + /* + * We have not seen any match that sorts later + * than the current path. + */ + + /* + * Does match sort strictly earlier than path + * with their common parts? + */ + m = strncmp(match, entry->path, + (matchlen < pathlen) ? matchlen : pathlen); + if (m < 0) + return 0; + + /* + * If we come here even once, that means there is at + * least one pathspec that would sort equal to or + * later than the path we are currently looking at. + * In other words, if we have never reached this point + * after iterating all pathspecs, it means all + * pathspecs are either outside of base, or inside the + * base but sorts strictly earlier than the current + * one. In either case, they will never match the + * subsequent entries. In such a case, we initialized + * the variable to -1 and that is what will be + * returned, allowing the caller to terminate early. + */ + *never_interesting = 0; + } + + if (pathlen > matchlen) + return 0; + + if (matchlen > pathlen) { + if (match[pathlen] != '/') + return 0; + if (!S_ISDIR(entry->mode)) + return 0; + } + + if (m == -1) + /* + * we cheated and did not do strncmp(), so we do + * that here. + */ + m = strncmp(match, entry->path, pathlen); + + /* + * If common part matched earlier then it is a hit, + * because we rejected the case where path is not a + * leading directory and is shorter than match. + */ + if (!m) + return 1; + + return 0; + } + + static int match_dir_prefix(const char *base, int baselen, + const char *match, int matchlen) + { + if (strncmp(base, match, matchlen)) + return 0; + + /* + * If the base is a subdirectory of a path which + * was specified, all of them are interesting. + */ + if (!matchlen || + base[matchlen] == '/' || + match[matchlen - 1] == '/') + return 1; + + /* Just a random prefix match */ + return 0; + } + + /* + * Is a tree entry interesting given the pathspec we have? + * ++ * Pre-condition: baselen == 0 || base[baselen-1] == '/' ++ * + * Return: + * - 2 for "yes, and all subsequent entries will be" + * - 1 for yes + * - zero for no + * - negative for "no, and no subsequent entries will be either" + */ + int tree_entry_interesting(const struct name_entry *entry, + struct strbuf *base, int base_offset, + const struct pathspec *ps) + { + int i; + int pathlen, baselen = base->len - base_offset; + int never_interesting = ps->has_wildcard ? 0 : -1; + + if (!ps->nr) { + if (!ps->recursive || ps->max_depth == -1) + return 1; + return !!within_depth(base->buf + base_offset, baselen, + !!S_ISDIR(entry->mode), + ps->max_depth); + } + + pathlen = tree_entry_len(entry->path, entry->sha1); + + 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; + + if (baselen >= matchlen) { + /* If it doesn't match, move along... */ + if (!match_dir_prefix(base_str, baselen, match, matchlen)) + goto match_wildcards; + + if (!ps->recursive || ps->max_depth == -1) + return 2; + + return !!within_depth(base_str + matchlen + 1, + baselen - matchlen - 1, + !!S_ISDIR(entry->mode), + ps->max_depth); + } + + /* Does the base match? */ + if (!strncmp(base_str, match, baselen)) { + if (match_entry(entry, pathlen, + match + baselen, matchlen - baselen, + &never_interesting)) + return 1; + + if (ps->items[i].has_wildcard) { + if (!fnmatch(match + baselen, entry->path, 0)) + return 1; + + /* + * Match all directories. We'll try to + * match files later on. + */ + if (ps->recursive && S_ISDIR(entry->mode)) + return 1; + } + + continue; + } + + match_wildcards: + if (!ps->items[i].has_wildcard) + continue; + + /* + * Concatenate base and entry->path into one and do + * fnmatch() on it. + */ + + strbuf_add(base, entry->path, pathlen); + + if (!fnmatch(match, base->buf + base_offset, 0)) { + strbuf_setlen(base, base_offset + baselen); + return 1; + } + strbuf_setlen(base, base_offset + baselen); + + /* + * Match all directories. We'll try to match files + * later on. + */ + if (ps->recursive && S_ISDIR(entry->mode)) + return 1; + } + return never_interesting; /* No matches */ + }