Merge branch 'maint-1.6.4' into maint
authorJunio C Hamano <gitster@pobox.com>
Sat, 17 Oct 2009 06:47:58 +0000 (23:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 17 Oct 2009 06:47:58 +0000 (23:47 -0700)
* maint-1.6.4:
grep: do not segfault when -f is used

1  2 
builtin-grep.c
t/t7002-grep.sh
diff --combined builtin-grep.c
index 761799d7d0afd62ecba99d703a8595664faa4723,e3b940b93334f99f610f84a86c9da11a435d1365..1df25b07b573301bba7fdcb3b13d1af9050f513e
@@@ -53,58 -53,26 +53,58 @@@ static int grep_config(const char *var
        return git_color_default_config(var, value, cb);
  }
  
 +/*
 + * Return non-zero if max_depth is negative or path has no more then max_depth
 + * slashes.
 + */
 +static int accept_subdir(const char *path, int max_depth)
 +{
 +      if (max_depth < 0)
 +              return 1;
 +
 +      while ((path = strchr(path, '/')) != NULL) {
 +              max_depth--;
 +              if (max_depth < 0)
 +                      return 0;
 +              path++;
 +      }
 +      return 1;
 +}
 +
 +/*
 + * Return non-zero if name is a subdirectory of match and is not too deep.
 + */
 +static int is_subdir(const char *name, int namelen,
 +              const char *match, int matchlen, int max_depth)
 +{
 +      if (matchlen > namelen || strncmp(name, match, matchlen))
 +              return 0;
 +
 +      if (name[matchlen] == '\0') /* exact match */
 +              return 1;
 +
 +      if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
 +              return accept_subdir(name + matchlen + 1, max_depth);
 +
 +      return 0;
 +}
 +
  /*
   * git grep pathspecs are somewhat different from diff-tree pathspecs;
   * pathname wildcards are allowed.
   */
 -static int pathspec_matches(const char **paths, const char *name)
 +static int pathspec_matches(const char **paths, const char *name, int max_depth)
  {
        int namelen, i;
        if (!paths || !*paths)
 -              return 1;
 +              return accept_subdir(name, max_depth);
        namelen = strlen(name);
        for (i = 0; paths[i]; i++) {
                const char *match = paths[i];
                int matchlen = strlen(match);
                const char *cp, *meta;
  
 -              if (!matchlen ||
 -                  ((matchlen <= namelen) &&
 -                   !strncmp(name, match, matchlen) &&
 -                   (match[matchlen-1] == '/' ||
 -                    name[matchlen] == '\0' || name[matchlen] == '/')))
 +              if (is_subdir(name, namelen, match, matchlen, max_depth))
                        return 1;
                if (!fnmatch(match, name, 0))
                        return 1;
@@@ -443,7 -411,7 +443,7 @@@ static int external_grep(struct grep_op
                int kept;
                if (!S_ISREG(ce->ce_mode))
                        continue;
 -              if (!pathspec_matches(paths, ce->name))
 +              if (!pathspec_matches(paths, ce->name, opt->max_depth))
                        continue;
                name = ce->name;
                if (name[0] == '-') {
@@@ -501,7 -469,7 +501,7 @@@ static int grep_cache(struct grep_opt *
                struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                        continue;
 -              if (!pathspec_matches(paths, ce->name))
 +              if (!pathspec_matches(paths, ce->name, opt->max_depth))
                        continue;
                /*
                 * If CE_VALID is on, we assume worktree file and its cache entry
@@@ -561,7 -529,7 +561,7 @@@ static int grep_tree(struct grep_opt *o
                        strbuf_addch(&pathbuf, '/');
  
                down = pathbuf.buf + tn_len;
 -              if (!pathspec_matches(paths, down))
 +              if (!pathspec_matches(paths, down, opt->max_depth))
                        ;
                else if (S_ISREG(entry.mode))
                        hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
@@@ -631,7 -599,7 +631,7 @@@ static int file_callback(const struct o
        struct grep_opt *grep_opt = opt->value;
        FILE *patterns;
        int lno = 0;
-       struct strbuf sb;
+       struct strbuf sb = STRBUF_INIT;
  
        patterns = fopen(arg, "r");
        if (!patterns)
@@@ -715,9 -683,6 +715,9 @@@ int cmd_grep(int argc, const char **arg
                OPT_SET_INT('I', NULL, &opt.binary,
                        "don't match patterns in binary files",
                        GREP_BINARY_NOMATCH),
 +              { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
 +                      "descend at most <depth> levels", PARSE_OPT_NONEG,
 +                      NULL, 1 },
                OPT_GROUP(""),
                OPT_BIT('E', "extended-regexp", &opt.regflags,
                        "use extended POSIX regular expressions", REG_EXTENDED),
        opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
        opt.regflags = REG_NEWLINE;
 +      opt.max_depth = -1;
  
        strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
        opt.color = -1;
diff --combined t/t7002-grep.sh
index ae56a36eacbc2d9a796742c974fa498705f71104,5f91d822971180aebbd9503466a3b14db79b8bc6..ae5290ab43e8b51b32331768371e2a67d700064d
@@@ -25,17 -25,13 +25,17 @@@ test_expect_success setup 
                echo foo mmap bar_mmap
                echo foo_mmap bar mmap baz
        } >file &&
 +      echo vvv >v &&
        echo ww w >w &&
        echo x x xx x >x &&
        echo y yy >y &&
        echo zzz > z &&
        mkdir t &&
        echo test >t/t &&
 -      git add file w x y z t/t hello.c &&
 +      echo vvv >t/v &&
 +      mkdir t/a &&
 +      echo vvv >t/a/v &&
 +      git add . &&
        test_tick &&
        git commit -m initial
  '
                ! git grep -c test $H | grep /dev/null
          '
  
 +      test_expect_success "grep --max-depth -1 $L" '
 +              {
 +                      echo ${HC}t/a/v:1:vvv
 +                      echo ${HC}t/v:1:vvv
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth -1 -n -e vvv $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 0 $L" '
 +              {
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 0 -n -e vvv $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 0 -- '*' $L" '
 +              {
 +                      echo ${HC}t/a/v:1:vvv
 +                      echo ${HC}t/v:1:vvv
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 1 $L" '
 +              {
 +                      echo ${HC}t/v:1:vvv
 +                      echo ${HC}v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 1 -n -e vvv $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep --max-depth 0 -- t $L" '
 +              {
 +                      echo ${HC}t/v:1:vvv
 +              } >expected &&
 +              git grep --max-depth 0 -n -e vvv $H -- t >actual &&
 +              test_cmp expected actual
 +      '
 +
  done
  
  cat >expected <<EOF
@@@ -213,6 -164,72 +213,72 @@@ test_expect_success 'grep -e A --and --
        test_cmp expected actual
  '
  
+ test_expect_success 'grep -f, non-existent file' '
+       test_must_fail git grep -f patterns
+ '
+ cat >expected <<EOF
+ file:foo mmap bar
+ file:foo_mmap bar
+ file:foo_mmap bar mmap
+ file:foo mmap bar_mmap
+ file:foo_mmap bar mmap baz
+ EOF
+ cat >pattern <<EOF
+ mmap
+ EOF
+ test_expect_success 'grep -f, one pattern' '
+       git grep -f pattern >actual &&
+       test_cmp expected actual
+ '
+ cat >expected <<EOF
+ file:foo mmap bar
+ file:foo_mmap bar
+ file:foo_mmap bar mmap
+ file:foo mmap bar_mmap
+ file:foo_mmap bar mmap baz
+ t/a/v:vvv
+ t/v:vvv
+ v:vvv
+ EOF
+ cat >patterns <<EOF
+ mmap
+ vvv
+ EOF
+ test_expect_success 'grep -f, multiple patterns' '
+       git grep -f patterns >actual &&
+       test_cmp expected actual
+ '
+ cat >expected <<EOF
+ file:foo mmap bar
+ file:foo_mmap bar
+ file:foo_mmap bar mmap
+ file:foo mmap bar_mmap
+ file:foo_mmap bar mmap baz
+ t/a/v:vvv
+ t/v:vvv
+ v:vvv
+ EOF
+ cat >patterns <<EOF
+ mmap
+ vvv
+ EOF
+ test_expect_success 'grep -f, ignore empty lines' '
+       git grep -f patterns >actual &&
+       test_cmp expected actual
+ '
  cat >expected <<EOF
  y:y yy
  --