Merge branch 'as/grep-quiet-no-match-exit-code-fix' into next
authorJunio C Hamano <gitster@pobox.com>
Sun, 20 Aug 2017 06:07:20 +0000 (23:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 20 Aug 2017 06:07:20 +0000 (23:07 -0700)
"git grep -L" and "git grep --quiet -L" reported different exit
codes; this has been corrected.

* as/grep-quiet-no-match-exit-code-fix:
git-grep: correct exit code with --quiet and -L

1  2 
grep.c
t/t7810-grep.sh
diff --combined grep.c
index 45acd333b093d8e3bd77f367a19317010372ce99,3b29fbde2f4a6cd2f4afeb3888133018099ec46a..ce6a48e634105154ca4bb267b4eb744c74d4d803
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -1,5 -1,4 +1,5 @@@
  #include "cache.h"
 +#include "config.h"
  #include "grep.h"
  #include "userdiff.h"
  #include "xdiff-interface.h"
@@@ -13,11 -12,6 +13,11 @@@ static int grep_source_is_binary(struc
  
  static struct grep_opt grep_defaults;
  
 +static void std_output(struct grep_opt *opt, const void *buf, size_t size)
 +{
 +      fwrite(buf, size, 1, stdout);
 +}
 +
  /*
   * Initialize the grep_defaults template with hardcoded defaults.
   * We could let the compiler do this, but without C99 initializers
@@@ -35,8 -29,10 +35,8 @@@ void init_grep_defaults(void
        memset(opt, 0, sizeof(*opt));
        opt->relative = 1;
        opt->pathname = 1;
 -      opt->regflags = REG_NEWLINE;
        opt->max_depth = -1;
        opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
 -      opt->extended_regexp_option = 0;
        color_set(opt->color_context, "");
        color_set(opt->color_filename, "");
        color_set(opt->color_function, "");
@@@ -46,7 -42,6 +46,7 @@@
        color_set(opt->color_selected, "");
        color_set(opt->color_sep, GIT_COLOR_CYAN);
        opt->color = -1;
 +      opt->output = std_output;
  }
  
  static int parse_pattern_type_arg(const char *opt, const char *arg)
@@@ -77,7 -72,10 +77,7 @@@ int grep_config(const char *var, const 
                return -1;
  
        if (!strcmp(var, "grep.extendedregexp")) {
 -              if (git_config_bool(var, value))
 -                      opt->extended_regexp_option = 1;
 -              else
 -                      opt->extended_regexp_option = 0;
 +              opt->extended_regexp_option = git_config_bool(var, value);
                return 0;
        }
  
@@@ -152,8 -150,8 +152,8 @@@ void grep_init(struct grep_opt *opt, co
        opt->linenum = def->linenum;
        opt->max_depth = def->max_depth;
        opt->pathname = def->pathname;
 -      opt->regflags = def->regflags;
        opt->relative = def->relative;
 +      opt->output = def->output;
  
        color_set(opt->color_context, def->color_context);
        color_set(opt->color_filename, def->color_filename);
  
  static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
  {
 +      /*
 +       * When committing to the pattern type by setting the relevant
 +       * fields in grep_opt it's generally not necessary to zero out
 +       * the fields we're not choosing, since they won't have been
 +       * set by anything. The extended_regexp_option field is the
 +       * only exception to this.
 +       *
 +       * This is because in the process of parsing grep.patternType
 +       * & grep.extendedRegexp we set opt->pattern_type_option and
 +       * opt->extended_regexp_option, respectively. We then
 +       * internally use opt->extended_regexp_option to see if we're
 +       * compiling an ERE. It must be unset if that's not actually
 +       * the case.
 +       */
 +      if (pattern_type != GREP_PATTERN_TYPE_ERE &&
 +          opt->extended_regexp_option)
 +              opt->extended_regexp_option = 0;
 +
        switch (pattern_type) {
        case GREP_PATTERN_TYPE_UNSPECIFIED:
                /* fall through */
  
        case GREP_PATTERN_TYPE_BRE:
 -              opt->fixed = 0;
 -              opt->pcre = 0;
 -              opt->regflags &= ~REG_EXTENDED;
                break;
  
        case GREP_PATTERN_TYPE_ERE:
 -              opt->fixed = 0;
 -              opt->pcre = 0;
 -              opt->regflags |= REG_EXTENDED;
 +              opt->extended_regexp_option = 1;
                break;
  
        case GREP_PATTERN_TYPE_FIXED:
                opt->fixed = 1;
 -              opt->pcre = 0;
 -              opt->regflags &= ~REG_EXTENDED;
                break;
  
        case GREP_PATTERN_TYPE_PCRE:
 -              opt->fixed = 0;
 -              opt->pcre = 1;
 -              opt->regflags &= ~REG_EXTENDED;
 +#ifdef USE_LIBPCRE2
 +              opt->pcre2 = 1;
 +#else
 +              /*
 +               * It's important that pcre1 always be assigned to
 +               * even when there's no USE_LIBPCRE* defined. We still
 +               * call the PCRE stub function, it just dies with
 +               * "cannot use Perl-compatible regexes[...]".
 +               */
 +              opt->pcre1 = 1;
 +#endif
                break;
        }
  }
@@@ -223,11 -202,6 +223,11 @@@ void grep_commit_pattern_type(enum grep
        else if (opt->pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
                grep_set_pattern_type_option(opt->pattern_type_option, opt);
        else if (opt->extended_regexp_option)
 +              /*
 +               * This branch *must* happen after setting from the
 +               * opt->pattern_type_option above, we don't want
 +               * grep.extendedRegexp to override grep.patternType!
 +               */
                grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, opt);
  }
  
@@@ -343,32 -317,8 +343,32 @@@ static NORETURN void compile_regexp_fai
        die("%s'%s': %s", where, p->pattern, error);
  }
  
 -#ifdef USE_LIBPCRE
 -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
 +static int is_fixed(const char *s, size_t len)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < len; i++) {
 +              if (is_regex_special(s[i]))
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +static int has_null(const char *s, size_t len)
 +{
 +      /*
 +       * regcomp cannot accept patterns with NULs so when using it
 +       * we consider any pattern containing a NUL fixed.
 +       */
 +      if (memchr(s, 0, len))
 +              return 1;
 +
 +      return 0;
 +}
 +
 +#ifdef USE_LIBPCRE1
 +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
  {
        const char *error;
        int erroffset;
  
        if (opt->ignore_case) {
                if (has_non_ascii(p->pattern))
 -                      p->pcre_tables = pcre_maketables();
 +                      p->pcre1_tables = pcre_maketables();
                options |= PCRE_CASELESS;
        }
        if (is_utf8_locale() && has_non_ascii(p->pattern))
                options |= PCRE_UTF8;
  
 -      p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
 -                                    p->pcre_tables);
 -      if (!p->pcre_regexp)
 +      p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
 +                                    p->pcre1_tables);
 +      if (!p->pcre1_regexp)
                compile_regexp_failed(p, error);
  
 -      p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error);
 -      if (!p->pcre_extra_info && error)
 +      p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error);
 +      if (!p->pcre1_extra_info && error)
                die("%s", error);
 +
 +#ifdef GIT_PCRE1_USE_JIT
 +      pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on);
 +      if (p->pcre1_jit_on == 1) {
 +              p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024);
 +              if (!p->pcre1_jit_stack)
 +                      die("Couldn't allocate PCRE JIT stack");
 +              pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack);
 +      } else if (p->pcre1_jit_on != 0) {
 +              die("BUG: The pcre1_jit_on variable should be 0 or 1, not %d",
 +                  p->pcre1_jit_on);
 +      }
 +#endif
  }
  
 -static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
 +static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
                regmatch_t *match, int eflags)
  {
        int ovector[30], ret, flags = 0;
        if (eflags & REG_NOTBOL)
                flags |= PCRE_NOTBOL;
  
 -      ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line,
 -                      0, flags, ovector, ARRAY_SIZE(ovector));
 +#ifdef GIT_PCRE1_USE_JIT
 +      if (p->pcre1_jit_on) {
 +              ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
 +                                  eol - line, 0, flags, ovector,
 +                                  ARRAY_SIZE(ovector), p->pcre1_jit_stack);
 +      } else
 +#endif
 +      {
 +              ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
 +                              eol - line, 0, flags, ovector,
 +                              ARRAY_SIZE(ovector));
 +      }
 +
        if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
                die("pcre_exec failed with error code %d", ret);
        if (ret > 0) {
        return ret;
  }
  
 -static void free_pcre_regexp(struct grep_pat *p)
 +static void free_pcre1_regexp(struct grep_pat *p)
  {
 -      pcre_free(p->pcre_regexp);
 -      pcre_free(p->pcre_extra_info);
 -      pcre_free((void *)p->pcre_tables);
 +      pcre_free(p->pcre1_regexp);
 +#ifdef GIT_PCRE1_USE_JIT
 +      if (p->pcre1_jit_on) {
 +              pcre_free_study(p->pcre1_extra_info);
 +              pcre_jit_stack_free(p->pcre1_jit_stack);
 +      } else
 +#endif
 +      {
 +              pcre_free(p->pcre1_extra_info);
 +      }
 +      pcre_free((void *)p->pcre1_tables);
  }
 -#else /* !USE_LIBPCRE */
 -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
 +#else /* !USE_LIBPCRE1 */
 +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
  {
        die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
  }
  
 -static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
 +static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
                regmatch_t *match, int eflags)
  {
        return 1;
  }
  
 -static void free_pcre_regexp(struct grep_pat *p)
 +static void free_pcre1_regexp(struct grep_pat *p)
  {
  }
 -#endif /* !USE_LIBPCRE */
 +#endif /* !USE_LIBPCRE1 */
  
 -static int is_fixed(const char *s, size_t len)
 +#ifdef USE_LIBPCRE2
 +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
  {
 -      size_t i;
 +      int error;
 +      PCRE2_UCHAR errbuf[256];
 +      PCRE2_SIZE erroffset;
 +      int options = PCRE2_MULTILINE;
 +      const uint8_t *character_tables = NULL;
 +      int jitret;
  
 -      /* regcomp cannot accept patterns with NULs so we
 -       * consider any pattern containing a NUL fixed.
 -       */
 -      if (memchr(s, 0, len))
 -              return 1;
 +      assert(opt->pcre2);
  
 -      for (i = 0; i < len; i++) {
 -              if (is_regex_special(s[i]))
 -                      return 0;
 +      p->pcre2_compile_context = NULL;
 +
 +      if (opt->ignore_case) {
 +              if (has_non_ascii(p->pattern)) {
 +                      character_tables = pcre2_maketables(NULL);
 +                      p->pcre2_compile_context = pcre2_compile_context_create(NULL);
 +                      pcre2_set_character_tables(p->pcre2_compile_context, character_tables);
 +              }
 +              options |= PCRE2_CASELESS;
 +      }
 +      if (is_utf8_locale() && has_non_ascii(p->pattern))
 +              options |= PCRE2_UTF;
 +
 +      p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern,
 +                                       p->patternlen, options, &error, &erroffset,
 +                                       p->pcre2_compile_context);
 +
 +      if (p->pcre2_pattern) {
 +              p->pcre2_match_data = pcre2_match_data_create_from_pattern(p->pcre2_pattern, NULL);
 +              if (!p->pcre2_match_data)
 +                      die("Couldn't allocate PCRE2 match data");
 +      } else {
 +              pcre2_get_error_message(error, errbuf, sizeof(errbuf));
 +              compile_regexp_failed(p, (const char *)&errbuf);
 +      }
 +
 +      pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
 +      if (p->pcre2_jit_on == 1) {
 +              jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
 +              if (jitret)
 +                      die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
 +              p->pcre2_jit_stack = pcre2_jit_stack_create(1, 1024 * 1024, NULL);
 +              if (!p->pcre2_jit_stack)
 +                      die("Couldn't allocate PCRE2 JIT stack");
 +              p->pcre2_match_context = pcre2_match_context_create(NULL);
 +              if (!p->pcre2_match_context)
 +                      die("Couldn't allocate PCRE2 match context");
 +              pcre2_jit_stack_assign(p->pcre2_match_context, NULL, p->pcre2_jit_stack);
 +      } else if (p->pcre2_jit_on != 0) {
 +              die("BUG: The pcre2_jit_on variable should be 0 or 1, not %d",
 +                  p->pcre1_jit_on);
 +      }
 +}
 +
 +static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
 +              regmatch_t *match, int eflags)
 +{
 +      int ret, flags = 0;
 +      PCRE2_SIZE *ovector;
 +      PCRE2_UCHAR errbuf[256];
 +
 +      if (eflags & REG_NOTBOL)
 +              flags |= PCRE2_NOTBOL;
 +
 +      if (p->pcre2_jit_on)
 +              ret = pcre2_jit_match(p->pcre2_pattern, (unsigned char *)line,
 +                                    eol - line, 0, flags, p->pcre2_match_data,
 +                                    NULL);
 +      else
 +              ret = pcre2_match(p->pcre2_pattern, (unsigned char *)line,
 +                                eol - line, 0, flags, p->pcre2_match_data,
 +                                NULL);
 +
 +      if (ret < 0 && ret != PCRE2_ERROR_NOMATCH) {
 +              pcre2_get_error_message(ret, errbuf, sizeof(errbuf));
 +              die("%s failed with error code %d: %s",
 +                  (p->pcre2_jit_on ? "pcre2_jit_match" : "pcre2_match"), ret,
 +                  errbuf);
 +      }
 +      if (ret > 0) {
 +              ovector = pcre2_get_ovector_pointer(p->pcre2_match_data);
 +              ret = 0;
 +              match->rm_so = (int)ovector[0];
 +              match->rm_eo = (int)ovector[1];
        }
  
 +      return ret;
 +}
 +
 +static void free_pcre2_pattern(struct grep_pat *p)
 +{
 +      pcre2_compile_context_free(p->pcre2_compile_context);
 +      pcre2_code_free(p->pcre2_pattern);
 +      pcre2_match_data_free(p->pcre2_match_data);
 +      pcre2_jit_stack_free(p->pcre2_jit_stack);
 +      pcre2_match_context_free(p->pcre2_match_context);
 +}
 +#else /* !USE_LIBPCRE2 */
 +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
 +{
 +      /*
 +       * Unreachable until USE_LIBPCRE2 becomes synonymous with
 +       * USE_LIBPCRE. See the sibling comment in
 +       * grep_set_pattern_type_option().
 +       */
 +      die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
 +}
 +
 +static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
 +              regmatch_t *match, int eflags)
 +{
        return 1;
  }
  
 +static void free_pcre2_pattern(struct grep_pat *p)
 +{
 +}
 +#endif /* !USE_LIBPCRE2 */
 +
  static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
  {
        struct strbuf sb = STRBUF_INIT;
        int err;
 -      int regflags;
 +      int regflags = 0;
  
        basic_regex_quote_buf(&sb, p->pattern);
 -      regflags = opt->regflags & ~REG_EXTENDED;
        if (opt->ignore_case)
                regflags |= REG_ICASE;
        err = regcomp(&p->regexp, sb.buf, regflags);
  
  static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
  {
 -      int icase, ascii_only;
 +      int ascii_only;
        int err;
 +      int regflags = REG_NEWLINE;
  
        p->word_regexp = opt->word_regexp;
        p->ignore_case = opt->ignore_case;
 -      icase          = opt->regflags & REG_ICASE || p->ignore_case;
        ascii_only     = !has_non_ascii(p->pattern);
  
        /*
         * simple string match using kws.  p->fixed tells us if we
         * want to use kws.
         */
 -      if (opt->fixed || is_fixed(p->pattern, p->patternlen))
 -              p->fixed = !icase || ascii_only;
 -      else
 -              p->fixed = 0;
 +      if (opt->fixed ||
 +          has_null(p->pattern, p->patternlen) ||
 +          is_fixed(p->pattern, p->patternlen))
 +              p->fixed = !p->ignore_case || ascii_only;
  
        if (p->fixed) {
 -              p->kws = kwsalloc(icase ? tolower_trans_tbl : NULL);
 +              p->kws = kwsalloc(p->ignore_case ? tolower_trans_tbl : NULL);
                kwsincr(p->kws, p->pattern, p->patternlen);
                kwsprep(p->kws);
                return;
                return;
        }
  
 -      if (opt->pcre) {
 -              compile_pcre_regexp(p, opt);
 +      if (opt->pcre2) {
 +              compile_pcre2_pattern(p, opt);
                return;
        }
  
 -      err = regcomp(&p->regexp, p->pattern, opt->regflags);
 +      if (opt->pcre1) {
 +              compile_pcre1_regexp(p, opt);
 +              return;
 +      }
 +
 +      if (p->ignore_case)
 +              regflags |= REG_ICASE;
 +      if (opt->extended_regexp_option)
 +              regflags |= REG_EXTENDED;
 +      err = regcomp(&p->regexp, p->pattern, regflags);
        if (err) {
                char errbuf[1024];
                regerror(err, &p->regexp, errbuf, 1024);
@@@ -1018,10 -825,8 +1018,10 @@@ void free_grep_patterns(struct grep_op
                case GREP_PATTERN_BODY:
                        if (p->kws)
                                kwsfree(p->kws);
 -                      else if (p->pcre_regexp)
 -                              free_pcre_regexp(p);
 +                      else if (p->pcre1_regexp)
 +                              free_pcre1_regexp(p);
 +                      else if (p->pcre2_pattern)
 +                              free_pcre2_pattern(p);
                        else
                                regfree(&p->regexp);
                        free(p->pattern);
@@@ -1100,10 -905,8 +1100,10 @@@ static int patmatch(struct grep_pat *p
  
        if (p->fixed)
                hit = !fixmatch(p, line, eol, match);
 -      else if (p->pcre_regexp)
 -              hit = !pcrematch(p, line, eol, match, eflags);
 +      else if (p->pcre1_regexp)
 +              hit = !pcre1match(p, line, eol, match, eflags);
 +      else if (p->pcre2_pattern)
 +              hit = !pcre2match(p, line, eol, match, eflags);
        else
                hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
                                   eflags);
@@@ -1361,7 -1164,7 +1361,7 @@@ static void show_line(struct grep_opt *
        }
        if (opt->linenum) {
                char buf[32];
 -              snprintf(buf, sizeof(buf), "%d", lno);
 +              xsnprintf(buf, sizeof(buf), "%d", lno);
                output_color(opt, buf, strlen(buf), opt->color_lineno);
                output_sep(opt, sign);
        }
@@@ -1576,6 -1379,11 +1576,6 @@@ static int look_ahead(struct grep_opt *
        return 0;
  }
  
 -static void std_output(struct grep_opt *opt, const void *buf, size_t size)
 -{
 -      fwrite(buf, size, 1, stdout);
 -}
 -
  static int fill_textconv_grep(struct userdiff_driver *driver,
                              struct grep_source *gs)
  {
         */
        df = alloc_filespec(gs->path);
        switch (gs->type) {
 -      case GREP_SOURCE_SHA1:
 +      case GREP_SOURCE_OID:
                fill_filespec(df, gs->identifier, 1, 0100644);
                break;
        case GREP_SOURCE_FILE:
 -              fill_filespec(df, null_sha1, 0, 0100644);
 +              fill_filespec(df, &null_oid, 0, 0100644);
                break;
        default:
                die("BUG: attempt to textconv something without a path?");
@@@ -1821,7 -1629,7 +1821,7 @@@ static int grep_source_1(struct grep_op
                return 0;
  
        if (opt->status_only)
-               return 0;
+               return opt->unmatch_name_only;
        if (opt->unmatch_name_only) {
                /* We did not see any hit, so we want to show this */
                show_name(opt, gs->name);
                                     opt->color_filename);
                        output_sep(opt, ':');
                }
 -              snprintf(buf, sizeof(buf), "%u\n", count);
 +              xsnprintf(buf, sizeof(buf), "%u\n", count);
                opt->output(opt, buf, strlen(buf));
                return 1;
        }
@@@ -1927,8 -1735,19 +1927,8 @@@ void grep_source_init(struct grep_sourc
        case GREP_SOURCE_FILE:
                gs->identifier = xstrdup(identifier);
                break;
 -      case GREP_SOURCE_SUBMODULE:
 -              if (!identifier) {
 -                      gs->identifier = NULL;
 -                      break;
 -              }
 -              /*
 -               * FALL THROUGH
 -               * If the identifier is non-NULL (in the submodule case) it
 -               * will be a SHA1 that needs to be copied.
 -               */
 -      case GREP_SOURCE_SHA1:
 -              gs->identifier = xmalloc(20);
 -              hashcpy(gs->identifier, identifier);
 +      case GREP_SOURCE_OID:
 +              gs->identifier = oiddup(identifier);
                break;
        case GREP_SOURCE_BUF:
                gs->identifier = NULL;
  
  void grep_source_clear(struct grep_source *gs)
  {
 -      free(gs->name);
 -      gs->name = NULL;
 -      free(gs->path);
 -      gs->path = NULL;
 -      free(gs->identifier);
 -      gs->identifier = NULL;
 +      FREE_AND_NULL(gs->name);
 +      FREE_AND_NULL(gs->path);
 +      FREE_AND_NULL(gs->identifier);
        grep_source_clear_data(gs);
  }
  
@@@ -1948,8 -1770,10 +1948,8 @@@ void grep_source_clear_data(struct grep
  {
        switch (gs->type) {
        case GREP_SOURCE_FILE:
 -      case GREP_SOURCE_SHA1:
 -      case GREP_SOURCE_SUBMODULE:
 -              free(gs->buf);
 -              gs->buf = NULL;
 +      case GREP_SOURCE_OID:
 +              FREE_AND_NULL(gs->buf);
                gs->size = 0;
                break;
        case GREP_SOURCE_BUF:
        }
  }
  
 -static int grep_source_load_sha1(struct grep_source *gs)
 +static int grep_source_load_oid(struct grep_source *gs)
  {
        enum object_type type;
  
        if (!gs->buf)
                return error(_("'%s': unable to read %s"),
                             gs->name,
 -                           sha1_to_hex(gs->identifier));
 +                           oid_to_hex(gs->identifier));
        return 0;
  }
  
@@@ -2015,10 -1839,12 +2015,10 @@@ static int grep_source_load(struct grep
        switch (gs->type) {
        case GREP_SOURCE_FILE:
                return grep_source_load_file(gs);
 -      case GREP_SOURCE_SHA1:
 -              return grep_source_load_sha1(gs);
 +      case GREP_SOURCE_OID:
 +              return grep_source_load_oid(gs);
        case GREP_SOURCE_BUF:
                return gs->buf ? 0 : -1;
 -      case GREP_SOURCE_SUBMODULE:
 -              break;
        }
        die("BUG: invalid grep_source type to load");
  }
diff --combined t/t7810-grep.sh
index f1063878205cfb9b0af8e54d997040ddb65bc27e,7395038f7e42b9b2683ca5bf91e471bcc9e55de0..2a6679c2f596fb13a34786a4aa4b954e61c61113
                test_cmp expected actual
        '
  
 -      test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" '
 +      test_expect_success PCRE "grep $L with grep.patterntype=perl" '
                echo "${HC}ab:a+b*c" >expected &&
                git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" $H ab >actual &&
                test_cmp expected actual
        '
  
 +      test_expect_success !PCRE "grep $L with grep.patterntype=perl errors without PCRE" '
 +              test_must_fail git -c grep.patterntype=perl grep "foo.*bar"
 +      '
 +
        test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" '
                echo "${HC}ab:abc" >expected &&
                git \
@@@ -374,6 -370,11 +374,11 @@@ test_expect_success 'grep -L -C' 
        test_cmp expected actual
  '
  
+ test_expect_success 'grep --files-without-match --quiet' '
+       git grep --files-without-match --quiet nonexistent_string >actual &&
+       test_cmp /dev/null actual
+ '
  cat >expected <<EOF
  file:foo mmap bar_mmap
  EOF
@@@ -775,40 -776,6 +780,40 @@@ test_expect_success 'grep -W with userd
        test_cmp expected actual
  '
  
 +for threads in $(test_seq 0 10)
 +do
 +      test_expect_success "grep --threads=$threads & -c grep.threads=$threads" "
 +              git grep --threads=$threads . >actual.$threads &&
 +              if test $threads -ge 1
 +              then
 +                      test_cmp actual.\$(($threads - 1)) actual.$threads
 +              fi &&
 +              git -c grep.threads=$threads grep . >actual.$threads &&
 +              if test $threads -ge 1
 +              then
 +                      test_cmp actual.\$(($threads - 1)) actual.$threads
 +              fi
 +      "
 +done
 +
 +test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' '
 +      git grep --threads=2 Hello hello_world 2>err &&
 +      grep ^warning: err >warnings &&
 +      test_line_count = 1 warnings &&
 +      grep -F "no threads support, ignoring --threads" err &&
 +      git -c grep.threads=2 grep Hello hello_world 2>err &&
 +      grep ^warning: err >warnings &&
 +      test_line_count = 1 warnings &&
 +      grep -F "no threads support, ignoring grep.threads" err &&
 +      git -c grep.threads=2 grep --threads=4 Hello hello_world 2>err &&
 +      grep ^warning: err >warnings &&
 +      test_line_count = 2 warnings &&
 +      grep -F "no threads support, ignoring --threads" err &&
 +      grep -F "no threads support, ignoring grep.threads" err &&
 +      git -c grep.threads=0 grep --threads=0 Hello hello_world 2>err &&
 +      test_line_count = 0 err
 +'
 +
  test_expect_success 'grep from a subdirectory to search wider area (1)' '
        mkdir -p s &&
        (
@@@ -1091,24 -1058,16 +1096,24 @@@ hello.c:int main(int argc, const char *
  hello.c:      printf("Hello world.\n");
  EOF
  
 -test_expect_success LIBPCRE 'grep --perl-regexp pattern' '
 +test_expect_success PCRE 'grep --perl-regexp pattern' '
        git grep --perl-regexp "\p{Ps}.*?\p{Pe}" hello.c >actual &&
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -P pattern' '
 +test_expect_success !PCRE 'grep --perl-regexp pattern errors without PCRE' '
 +      test_must_fail git grep --perl-regexp "foo.*bar"
 +'
 +
 +test_expect_success PCRE 'grep -P pattern' '
        git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
        test_cmp expected actual
  '
  
 +test_expect_success !PCRE 'grep -P pattern errors without PCRE' '
 +      test_must_fail git grep -P "foo.*bar"
 +'
 +
  test_expect_success 'grep pattern with grep.extendedRegexp=true' '
        >empty &&
        test_must_fail git -c grep.extendedregexp=true \
        test_cmp empty actual
  '
  
 -test_expect_success LIBPCRE 'grep -P pattern with grep.extendedRegexp=true' '
 +test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' '
        git -c grep.extendedregexp=true \
                grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -P -v pattern' '
 +test_expect_success PCRE 'grep -P -v pattern' '
        {
                echo "ab:a+b*c"
                echo "ab:a+bc"
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -P -i pattern' '
 +test_expect_success PCRE 'grep -P -i pattern' '
        cat >expected <<-EOF &&
        hello.c:        printf("Hello world.\n");
        EOF
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -P -w pattern' '
 +test_expect_success PCRE 'grep -P -w pattern' '
        {
                echo "hello_world:Hello world"
                echo "hello_world:HeLLo world"
        test_cmp expected actual
  '
  
 +test_expect_success PCRE 'grep -P backreferences work (the PCRE NO_AUTO_CAPTURE flag is not set)' '
 +      git grep -P -h "(?P<one>.)(?P=one)" hello_world >actual &&
 +      test_cmp hello_world actual &&
 +      git grep -P -h "(.)\1" hello_world >actual &&
 +      test_cmp hello_world actual
 +'
 +
  test_expect_success 'grep -G invalidpattern properly dies ' '
        test_must_fail git grep -G "a["
  '
@@@ -1171,11 -1123,11 +1176,11 @@@ test_expect_success 'grep invalidpatter
        test_must_fail git -c grep.patterntype=extended grep "a["
  '
  
 -test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' '
 +test_expect_success PCRE 'grep -P invalidpattern properly dies ' '
        test_must_fail git grep -P "a["
  '
  
 -test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' '
 +test_expect_success PCRE 'grep invalidpattern properly dies with grep.patternType=perl' '
        test_must_fail git -c grep.patterntype=perl grep "a["
  '
  
@@@ -1244,13 -1196,13 +1249,13 @@@ test_expect_success 'grep pattern with 
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
 +test_expect_success PCRE 'grep -G -F -E -P pattern' '
        echo "d0:0" >expected &&
        git grep -G -F -E -P "[\d]" d0 >actual &&
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
 +test_expect_success PCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
        echo "d0:0" >expected &&
        git \
                -c grep.patterntype=fixed \
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' '
 +test_expect_success PCRE 'grep -P pattern with grep.patternType=fixed' '
        echo "ab:a+b*c" >expected &&
        git \
                -c grep.patterntype=fixed \
@@@ -1396,12 -1348,12 +1401,12 @@@ space: line with leading space
  space: line with leading space3
  EOF
  
 -test_expect_success LIBPCRE 'grep -E "^ "' '
 +test_expect_success PCRE 'grep -E "^ "' '
        git grep -E "^ " space >actual &&
        test_cmp expected actual
  '
  
 -test_expect_success LIBPCRE 'grep -P "^ "' '
 +test_expect_success PCRE 'grep -P "^ "' '
        git grep -P "^ " space >actual &&
        test_cmp expected actual
  '