Code

Merge branch 'nd/struct-pathspec' into jch
authorJunio C Hamano <gitster@pobox.com>
Mon, 31 Jan 2011 03:03:00 +0000 (19:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 31 Jan 2011 03:03:00 +0000 (19:03 -0800)
* 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

16 files changed:
1  2 
Documentation/glossary-content.txt
builtin/add.c
builtin/diff.c
builtin/grep.c
builtin/log.c
builtin/update-index.c
cache.h
diff.h
dir.c
dir.h
read-cache.c
revision.c
t/t7810-grep.sh
tree-diff.c
tree-walk.c
wt-status.c

Simple merge
diff --cc builtin/add.c
Simple merge
diff --cc builtin/diff.c
Simple merge
diff --cc builtin/grep.c
Simple merge
diff --cc builtin/log.c
Simple merge
Simple merge
diff --cc cache.h
Simple merge
diff --cc diff.h
Simple merge
diff --cc dir.c
Simple merge
diff --cc dir.h
Simple merge
diff --cc read-cache.c
Simple merge
diff --cc revision.c
Simple merge
diff --cc t/t7810-grep.sh
Simple merge
diff --cc tree-diff.c
index 12c9a88884ec3bb70a1744e44235c578a44e08e6,e27782c70e62d6011ec32c61c660084a001bd5c9..3954281f509bbed9a9b095ed92d24b67275fed82
@@@ -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;
        }
  }
  
- 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 a9bbf4e2354df5ce6b010873b419731343a12c7d,9b43ad58b55173f8a6535fb583e89ed004934ff7..ddcf50c30a25e5c3695b41363c050d1c1f6580bf
@@@ -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 */
+ }
diff --cc wt-status.c
Simple merge