Merge branch 'jc/grep-author-all-match-implicit' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 8 Mar 2010 08:35:59 +0000 (00:35 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 8 Mar 2010 08:35:59 +0000 (00:35 -0800)
* jc/grep-author-all-match-implicit:
"log --author=me --grep=it" should find intersection, not union

1  2 
builtin-grep.c
builtin-rev-list.c
grep.c
grep.h
revision.c
t/t7002-grep.sh
diff --combined builtin-grep.c
index 26979577d3f753cdc8b3358954249708d4cb53ef,d57c4d9bed1b3c4f64bbafa8cf0a8d9bb0040bb5..371db0aa9e6572c5b25ea679a510bc8784a25759
  #include "grep.h"
  #include "quote.h"
  
 -#ifndef NO_EXTERNAL_GREP
 -#ifdef __unix__
 -#define NO_EXTERNAL_GREP 0
 -#else
 -#define NO_EXTERNAL_GREP 1
 -#endif
 +#ifndef NO_PTHREADS
 +#include "thread-utils.h"
 +#include <pthread.h>
  #endif
  
  static char const * const grep_usage[] = {
        NULL
  };
  
 +static int use_threads = 1;
 +
 +#ifndef NO_PTHREADS
 +#define THREADS 8
 +static pthread_t threads[THREADS];
 +
 +static void *load_sha1(const unsigned char *sha1, unsigned long *size,
 +                     const char *name);
 +static void *load_file(const char *filename, size_t *sz);
 +
 +enum work_type {WORK_SHA1, WORK_FILE};
 +
 +/* We use one producer thread and THREADS consumer
 + * threads. The producer adds struct work_items to 'todo' and the
 + * consumers pick work items from the same array.
 + */
 +struct work_item
 +{
 +      enum work_type type;
 +      char *name;
 +
 +      /* if type == WORK_SHA1, then 'identifier' is a SHA1,
 +       * otherwise type == WORK_FILE, and 'identifier' is a NUL
 +       * terminated filename.
 +       */
 +      void *identifier;
 +      char done;
 +      struct strbuf out;
 +};
 +
 +/* In the range [todo_done, todo_start) in 'todo' we have work_items
 + * that have been or are processed by a consumer thread. We haven't
 + * written the result for these to stdout yet.
 + *
 + * The work_items in [todo_start, todo_end) are waiting to be picked
 + * up by a consumer thread.
 + *
 + * The ranges are modulo TODO_SIZE.
 + */
 +#define TODO_SIZE 128
 +static struct work_item todo[TODO_SIZE];
 +static int todo_start;
 +static int todo_end;
 +static int todo_done;
 +
 +/* Has all work items been added? */
 +static int all_work_added;
 +
 +/* This lock protects all the variables above. */
 +static pthread_mutex_t grep_mutex;
 +
 +/* Used to serialize calls to read_sha1_file. */
 +static pthread_mutex_t read_sha1_mutex;
 +
 +#define grep_lock() pthread_mutex_lock(&grep_mutex)
 +#define grep_unlock() pthread_mutex_unlock(&grep_mutex)
 +#define read_sha1_lock() pthread_mutex_lock(&read_sha1_mutex)
 +#define read_sha1_unlock() pthread_mutex_unlock(&read_sha1_mutex)
 +
 +/* Signalled when a new work_item is added to todo. */
 +static pthread_cond_t cond_add;
 +
 +/* Signalled when the result from one work_item is written to
 + * stdout.
 + */
 +static pthread_cond_t cond_write;
 +
 +/* Signalled when we are finished with everything. */
 +static pthread_cond_t cond_result;
 +
 +static void add_work(enum work_type type, char *name, void *id)
 +{
 +      grep_lock();
 +
 +      while ((todo_end+1) % ARRAY_SIZE(todo) == todo_done) {
 +              pthread_cond_wait(&cond_write, &grep_mutex);
 +      }
 +
 +      todo[todo_end].type = type;
 +      todo[todo_end].name = name;
 +      todo[todo_end].identifier = id;
 +      todo[todo_end].done = 0;
 +      strbuf_reset(&todo[todo_end].out);
 +      todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
 +
 +      pthread_cond_signal(&cond_add);
 +      grep_unlock();
 +}
 +
 +static struct work_item *get_work(void)
 +{
 +      struct work_item *ret;
 +
 +      grep_lock();
 +      while (todo_start == todo_end && !all_work_added) {
 +              pthread_cond_wait(&cond_add, &grep_mutex);
 +      }
 +
 +      if (todo_start == todo_end && all_work_added) {
 +              ret = NULL;
 +      } else {
 +              ret = &todo[todo_start];
 +              todo_start = (todo_start + 1) % ARRAY_SIZE(todo);
 +      }
 +      grep_unlock();
 +      return ret;
 +}
 +
 +static void grep_sha1_async(struct grep_opt *opt, char *name,
 +                          const unsigned char *sha1)
 +{
 +      unsigned char *s;
 +      s = xmalloc(20);
 +      memcpy(s, sha1, 20);
 +      add_work(WORK_SHA1, name, s);
 +}
 +
 +static void grep_file_async(struct grep_opt *opt, char *name,
 +                          const char *filename)
 +{
 +      add_work(WORK_FILE, name, xstrdup(filename));
 +}
 +
 +static void work_done(struct work_item *w)
 +{
 +      int old_done;
 +
 +      grep_lock();
 +      w->done = 1;
 +      old_done = todo_done;
 +      for(; todo[todo_done].done && todo_done != todo_start;
 +          todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
 +              w = &todo[todo_done];
 +              write_or_die(1, w->out.buf, w->out.len);
 +              free(w->name);
 +              free(w->identifier);
 +      }
 +
 +      if (old_done != todo_done)
 +              pthread_cond_signal(&cond_write);
 +
 +      if (all_work_added && todo_done == todo_end)
 +              pthread_cond_signal(&cond_result);
 +
 +      grep_unlock();
 +}
 +
 +static void *run(void *arg)
 +{
 +      int hit = 0;
 +      struct grep_opt *opt = arg;
 +
 +      while (1) {
 +              struct work_item *w = get_work();
 +              if (!w)
 +                      break;
 +
 +              opt->output_priv = w;
 +              if (w->type == WORK_SHA1) {
 +                      unsigned long sz;
 +                      void* data = load_sha1(w->identifier, &sz, w->name);
 +
 +                      if (data) {
 +                              hit |= grep_buffer(opt, w->name, data, sz);
 +                              free(data);
 +                      }
 +              } else if (w->type == WORK_FILE) {
 +                      size_t sz;
 +                      void* data = load_file(w->identifier, &sz);
 +                      if (data) {
 +                              hit |= grep_buffer(opt, w->name, data, sz);
 +                              free(data);
 +                      }
 +              } else {
 +                      assert(0);
 +              }
 +
 +              work_done(w);
 +      }
 +      free_grep_patterns(arg);
 +      free(arg);
 +
 +      return (void*) (intptr_t) hit;
 +}
 +
 +static void strbuf_out(struct grep_opt *opt, const void *buf, size_t size)
 +{
 +      struct work_item *w = opt->output_priv;
 +      strbuf_add(&w->out, buf, size);
 +}
 +
 +static void start_threads(struct grep_opt *opt)
 +{
 +      int i;
 +
 +      pthread_mutex_init(&grep_mutex, NULL);
 +      pthread_mutex_init(&read_sha1_mutex, NULL);
 +      pthread_cond_init(&cond_add, NULL);
 +      pthread_cond_init(&cond_write, NULL);
 +      pthread_cond_init(&cond_result, NULL);
 +
 +      for (i = 0; i < ARRAY_SIZE(todo); i++) {
 +              strbuf_init(&todo[i].out, 0);
 +      }
 +
 +      for (i = 0; i < ARRAY_SIZE(threads); i++) {
 +              int err;
 +              struct grep_opt *o = grep_opt_dup(opt);
 +              o->output = strbuf_out;
 +              compile_grep_patterns(o);
 +              err = pthread_create(&threads[i], NULL, run, o);
 +
 +              if (err)
 +                      die("grep: failed to create thread: %s",
 +                          strerror(err));
 +      }
 +}
 +
 +static int wait_all(void)
 +{
 +      int hit = 0;
 +      int i;
 +
 +      grep_lock();
 +      all_work_added = 1;
 +
 +      /* Wait until all work is done. */
 +      while (todo_done != todo_end)
 +              pthread_cond_wait(&cond_result, &grep_mutex);
 +
 +      /* Wake up all the consumer threads so they can see that there
 +       * is no more work to do.
 +       */
 +      pthread_cond_broadcast(&cond_add);
 +      grep_unlock();
 +
 +      for (i = 0; i < ARRAY_SIZE(threads); i++) {
 +              void *h;
 +              pthread_join(threads[i], &h);
 +              hit |= (int) (intptr_t) h;
 +      }
 +
 +      pthread_mutex_destroy(&grep_mutex);
 +      pthread_mutex_destroy(&read_sha1_mutex);
 +      pthread_cond_destroy(&cond_add);
 +      pthread_cond_destroy(&cond_write);
 +      pthread_cond_destroy(&cond_result);
 +
 +      return hit;
 +}
 +#else /* !NO_PTHREADS */
 +#define read_sha1_lock()
 +#define read_sha1_unlock()
 +
 +static int wait_all(void)
 +{
 +      return 0;
 +}
 +#endif
 +
  static int grep_config(const char *var, const char *value, void *cb)
  {
        struct grep_opt *opt = cb;
                opt->color = git_config_colorbool(var, value, -1);
                return 0;
        }
 -      if (!strcmp(var, "color.grep.external"))
 -              return git_config_string(&(opt->color_external), var, value);
        if (!strcmp(var, "color.grep.match")) {
                if (!value)
                        return config_error_nonbool(var);
@@@ -408,74 -153,37 +408,74 @@@ static int pathspec_matches(const char 
        return 0;
  }
  
 -static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)
 +static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 +{
 +      void *data;
 +
 +      if (use_threads) {
 +              read_sha1_lock();
 +              data = read_sha1_file(sha1, type, size);
 +              read_sha1_unlock();
 +      } else {
 +              data = read_sha1_file(sha1, type, size);
 +      }
 +      return data;
 +}
 +
 +static void *load_sha1(const unsigned char *sha1, unsigned long *size,
 +                     const char *name)
  {
 -      unsigned long size;
 -      char *data;
        enum object_type type;
 -      int hit;
 -      struct strbuf pathbuf = STRBUF_INIT;
 +      void *data = lock_and_read_sha1_file(sha1, &type, size);
  
 -      data = read_sha1_file(sha1, &type, &size);
 -      if (!data) {
 +      if (!data)
                error("'%s': unable to read %s", name, sha1_to_hex(sha1));
 -              return 0;
 -      }
 +
 +      return data;
 +}
 +
 +static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
 +                   const char *filename, int tree_name_len)
 +{
 +      struct strbuf pathbuf = STRBUF_INIT;
 +      char *name;
 +
        if (opt->relative && opt->prefix_length) {
 -              quote_path_relative(name + tree_name_len, -1, &pathbuf, opt->prefix);
 -              strbuf_insert(&pathbuf, 0, name, tree_name_len);
 -              name = pathbuf.buf;
 +              quote_path_relative(filename + tree_name_len, -1, &pathbuf,
 +                                  opt->prefix);
 +              strbuf_insert(&pathbuf, 0, filename, tree_name_len);
 +      } else {
 +              strbuf_addstr(&pathbuf, filename);
 +      }
 +
 +      name = strbuf_detach(&pathbuf, NULL);
 +
 +#ifndef NO_PTHREADS
 +      if (use_threads) {
 +              grep_sha1_async(opt, name, sha1);
 +              return 0;
 +      } else
 +#endif
 +      {
 +              int hit;
 +              unsigned long sz;
 +              void *data = load_sha1(sha1, &sz, name);
 +              if (!data)
 +                      hit = 0;
 +              else
 +                      hit = grep_buffer(opt, name, data, sz);
 +
 +              free(data);
 +              free(name);
 +              return hit;
        }
 -      hit = grep_buffer(opt, name, data, size);
 -      strbuf_release(&pathbuf);
 -      free(data);
 -      return hit;
  }
  
 -static int grep_file(struct grep_opt *opt, const char *filename)
 +static void *load_file(const char *filename, size_t *sz)
  {
        struct stat st;
 -      int i;
        char *data;
 -      size_t sz;
 -      struct strbuf buf = STRBUF_INIT;
 +      int i;
  
        if (lstat(filename, &st) < 0) {
        err_ret:
        }
        if (!S_ISREG(st.st_mode))
                return 0;
 -      sz = xsize_t(st.st_size);
 +      *sz = xsize_t(st.st_size);
        i = open(filename, O_RDONLY);
        if (i < 0)
                goto err_ret;
 -      data = xmalloc(sz + 1);
 -      if (st.st_size != read_in_full(i, data, sz)) {
 +      data = xmalloc(*sz + 1);
 +      if (st.st_size != read_in_full(i, data, *sz)) {
                error("'%s': short read %s", filename, strerror(errno));
                close(i);
                free(data);
                return 0;
        }
        close(i);
 -      if (opt->relative && opt->prefix_length)
 -              filename = quote_path_relative(filename, -1, &buf, opt->prefix);
 -      i = grep_buffer(opt, filename, data, sz);
 -      strbuf_release(&buf);
 -      free(data);
 -      return i;
 +      data[*sz] = 0;
 +      return data;
  }
  
 -#if !NO_EXTERNAL_GREP
 -static int exec_grep(int argc, const char **argv)
 +static int grep_file(struct grep_opt *opt, const char *filename)
  {
 -      pid_t pid;
 -      int status;
 +      struct strbuf buf = STRBUF_INIT;
 +      char *name;
  
 -      argv[argc] = NULL;
 -      trace_argv_printf(argv, "trace: grep:");
 -      pid = fork();
 -      if (pid < 0)
 -              return pid;
 -      if (!pid) {
 -              execvp("grep", (char **) argv);
 -              exit(255);
 -      }
 -      while (waitpid(pid, &status, 0) < 0) {
 -              if (errno == EINTR)
 -                      continue;
 -              return -1;
 -      }
 -      if (WIFEXITED(status)) {
 -              if (!WEXITSTATUS(status))
 -                      return 1;
 +      if (opt->relative && opt->prefix_length)
 +              quote_path_relative(filename, -1, &buf, opt->prefix);
 +      else
 +              strbuf_addstr(&buf, filename);
 +      name = strbuf_detach(&buf, NULL);
 +
 +#ifndef NO_PTHREADS
 +      if (use_threads) {
 +              grep_file_async(opt, name, filename);
                return 0;
 -      }
 -      return -1;
 -}
 -
 -#define MAXARGS 1000
 -#define ARGBUF 4096
 -#define push_arg(a) do { \
 -      if (nr < MAXARGS) argv[nr++] = (a); \
 -      else die("maximum number of args exceeded"); \
 -      } while (0)
 -
 -/*
 - * If you send a singleton filename to grep, it does not give
 - * the name of the file.  GNU grep has "-H" but we would want
 - * that behaviour in a portable way.
 - *
 - * So we keep two pathnames in argv buffer unsent to grep in
 - * the main loop if we need to do more than one grep.
 - */
 -static int flush_grep(struct grep_opt *opt,
 -                    int argc, int arg0, const char **argv, int *kept)
 -{
 -      int status;
 -      int count = argc - arg0;
 -      const char *kept_0 = NULL;
 -
 -      if (count <= 2) {
 -              /*
 -               * Because we keep at least 2 paths in the call from
 -               * the main loop (i.e. kept != NULL), and MAXARGS is
 -               * far greater than 2, this usually is a call to
 -               * conclude the grep.  However, the user could attempt
 -               * to overflow the argv buffer by giving too many
 -               * options to leave very small number of real
 -               * arguments even for the call in the main loop.
 -               */
 -              if (kept)
 -                      die("insanely many options to grep");
 -
 -              /*
 -               * If we have two or more paths, we do not have to do
 -               * anything special, but we need to push /dev/null to
 -               * get "-H" behaviour of GNU grep portably but when we
 -               * are not doing "-l" nor "-L" nor "-c".
 -               */
 -              if (count == 1 &&
 -                  !opt->name_only &&
 -                  !opt->unmatch_name_only &&
 -                  !opt->count) {
 -                      argv[argc++] = "/dev/null";
 -                      argv[argc] = NULL;
 -              }
 -      }
 -
 -      else if (kept) {
 -              /*
 -               * Called because we found many paths and haven't finished
 -               * iterating over the cache yet.  We keep two paths
 -               * for the concluding call.  argv[argc-2] and argv[argc-1]
 -               * has the last two paths, so save the first one away,
 -               * replace it with NULL while sending the list to grep,
 -               * and recover them after we are done.
 -               */
 -              *kept = 2;
 -              kept_0 = argv[argc-2];
 -              argv[argc-2] = NULL;
 -              argc -= 2;
 -      }
 -
 -      if (opt->pre_context || opt->post_context) {
 -              /*
 -               * grep handles hunk marks between files, but we need to
 -               * do that ourselves between multiple calls.
 -               */
 -              if (opt->show_hunk_mark)
 -                      write_or_die(1, "--\n", 3);
 +      } else
 +#endif
 +      {
 +              int hit;
 +              size_t sz;
 +              void *data = load_file(filename, &sz);
 +              if (!data)
 +                      hit = 0;
                else
 -                      opt->show_hunk_mark = 1;
 -      }
 +                      hit = grep_buffer(opt, name, data, sz);
  
 -      status = exec_grep(argc, argv);
 -
 -      if (kept_0) {
 -              /*
 -               * Then recover them.  Now the last arg is beyond the
 -               * terminating NULL which is at argc, and the second
 -               * from the last is what we saved away in kept_0
 -               */
 -              argv[arg0++] = kept_0;
 -              argv[arg0] = argv[argc+1];
 -      }
 -      return status;
 -}
 -
 -static void grep_add_color(struct strbuf *sb, const char *escape_seq)
 -{
 -      size_t orig_len = sb->len;
 -
 -      while (*escape_seq) {
 -              if (*escape_seq == 'm')
 -                      strbuf_addch(sb, ';');
 -              else if (*escape_seq != '\033' && *escape_seq  != '[')
 -                      strbuf_addch(sb, *escape_seq);
 -              escape_seq++;
 -      }
 -      if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
 -              strbuf_setlen(sb, sb->len - 1);
 -}
 -
 -static int has_skip_worktree_entry(struct grep_opt *opt, const char **paths)
 -{
 -      int nr;
 -      for (nr = 0; nr < active_nr; nr++) {
 -              struct cache_entry *ce = active_cache[nr];
 -              if (!S_ISREG(ce->ce_mode))
 -                      continue;
 -              if (!pathspec_matches(paths, ce->name, opt->max_depth))
 -                      continue;
 -              if (ce_skip_worktree(ce))
 -                      return 1;
 -      }
 -      return 0;
 -}
 -
 -static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 -{
 -      int i, nr, argc, hit, len, status;
 -      const char *argv[MAXARGS+1];
 -      char randarg[ARGBUF];
 -      char *argptr = randarg;
 -      struct grep_pat *p;
 -
 -      if (opt->extended || (opt->relative && opt->prefix_length)
 -          || has_skip_worktree_entry(opt, paths))
 -              return -1;
 -      len = nr = 0;
 -      push_arg("grep");
 -      if (opt->fixed)
 -              push_arg("-F");
 -      if (opt->linenum)
 -              push_arg("-n");
 -      if (!opt->pathname)
 -              push_arg("-h");
 -      if (opt->regflags & REG_EXTENDED)
 -              push_arg("-E");
 -      if (opt->ignore_case)
 -              push_arg("-i");
 -      if (opt->binary == GREP_BINARY_NOMATCH)
 -              push_arg("-I");
 -      if (opt->word_regexp)
 -              push_arg("-w");
 -      if (opt->name_only)
 -              push_arg("-l");
 -      if (opt->unmatch_name_only)
 -              push_arg("-L");
 -      if (opt->null_following_name)
 -              /* in GNU grep git's "-z" translates to "-Z" */
 -              push_arg("-Z");
 -      if (opt->count)
 -              push_arg("-c");
 -      if (opt->post_context || opt->pre_context) {
 -              if (opt->post_context != opt->pre_context) {
 -                      if (opt->pre_context) {
 -                              push_arg("-B");
 -                              len += snprintf(argptr, sizeof(randarg)-len,
 -                                              "%u", opt->pre_context) + 1;
 -                              if (sizeof(randarg) <= len)
 -                                      die("maximum length of args exceeded");
 -                              push_arg(argptr);
 -                              argptr += len;
 -                      }
 -                      if (opt->post_context) {
 -                              push_arg("-A");
 -                              len += snprintf(argptr, sizeof(randarg)-len,
 -                                              "%u", opt->post_context) + 1;
 -                              if (sizeof(randarg) <= len)
 -                                      die("maximum length of args exceeded");
 -                              push_arg(argptr);
 -                              argptr += len;
 -                      }
 -              }
 -              else {
 -                      push_arg("-C");
 -                      len += snprintf(argptr, sizeof(randarg)-len,
 -                                      "%u", opt->post_context) + 1;
 -                      if (sizeof(randarg) <= len)
 -                              die("maximum length of args exceeded");
 -                      push_arg(argptr);
 -                      argptr += len;
 -              }
 -      }
 -      for (p = opt->pattern_list; p; p = p->next) {
 -              push_arg("-e");
 -              push_arg(p->pattern);
 -      }
 -      if (opt->color) {
 -              struct strbuf sb = STRBUF_INIT;
 -
 -              grep_add_color(&sb, opt->color_match);
 -              setenv("GREP_COLOR", sb.buf, 1);
 -
 -              strbuf_reset(&sb);
 -              strbuf_addstr(&sb, "mt=");
 -              grep_add_color(&sb, opt->color_match);
 -              strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
 -              setenv("GREP_COLORS", sb.buf, 1);
 -
 -              strbuf_release(&sb);
 -
 -              if (opt->color_external && strlen(opt->color_external) > 0)
 -                      push_arg(opt->color_external);
 -      } else {
 -              unsetenv("GREP_COLOR");
 -              unsetenv("GREP_COLORS");
 -      }
 -      unsetenv("GREP_OPTIONS");
 -
 -      hit = 0;
 -      argc = nr;
 -      for (i = 0; i < active_nr; i++) {
 -              struct cache_entry *ce = active_cache[i];
 -              char *name;
 -              int kept;
 -              if (!S_ISREG(ce->ce_mode))
 -                      continue;
 -              if (!pathspec_matches(paths, ce->name, opt->max_depth))
 -                      continue;
 -              name = ce->name;
 -              if (name[0] == '-') {
 -                      int len = ce_namelen(ce);
 -                      name = xmalloc(len + 3);
 -                      memcpy(name, "./", 2);
 -                      memcpy(name + 2, ce->name, len + 1);
 -              }
 -              argv[argc++] = name;
 -              if (MAXARGS <= argc) {
 -                      status = flush_grep(opt, argc, nr, argv, &kept);
 -                      if (0 < status)
 -                              hit = 1;
 -                      argc = nr + kept;
 -              }
 -              if (ce_stage(ce)) {
 -                      do {
 -                              i++;
 -                      } while (i < active_nr &&
 -                               !strcmp(ce->name, active_cache[i]->name));
 -                      i--; /* compensate for loop control */
 -              }
 -      }
 -      if (argc > nr) {
 -              status = flush_grep(opt, argc, nr, argv, NULL);
 -              if (0 < status)
 -                      hit = 1;
 +              free(data);
 +              free(name);
 +              return hit;
        }
 -      return hit;
  }
 -#endif
  
 -static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
 -                    int external_grep_allowed)
 +static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
  {
        int hit = 0;
        int nr;
        read_cache();
  
 -#if !NO_EXTERNAL_GREP
 -      /*
 -       * Use the external "grep" command for the case where
 -       * we grep through the checked-out files. It tends to
 -       * be a lot more optimized
 -       */
 -      if (!cached && external_grep_allowed) {
 -              hit = external_grep(opt, paths, cached);
 -              if (hit >= 0)
 -                      return hit;
 -              hit = 0;
 -      }
 -#endif
 -
        for (nr = 0; nr < active_nr; nr++) {
                struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                                 !strcmp(ce->name, active_cache[nr]->name));
                        nr--; /* compensate for loop control */
                }
 +              if (hit && opt->status_only)
 +                      break;
        }
        free_grep_patterns(opt);
        return hit;
@@@ -615,7 -590,7 +615,7 @@@ static int grep_tree(struct grep_opt *o
                        void *data;
                        unsigned long size;
  
 -                      data = read_sha1_file(entry.sha1, &type, &size);
 +                      data = lock_and_read_sha1_file(entry.sha1, &type, &size);
                        if (!data)
                                die("unable to read tree (%s)",
                                    sha1_to_hex(entry.sha1));
                        hit |= grep_tree(opt, paths, &sub, tree_name, down);
                        free(data);
                }
 +              if (hit && opt->status_only)
 +                      break;
        }
        strbuf_release(&pathbuf);
        return hit;
@@@ -739,8 -712,8 +739,8 @@@ int cmd_grep(int argc, const char **arg
  {
        int hit = 0;
        int cached = 0;
 -      int external_grep_allowed = 1;
        int seen_dashdash = 0;
 +      int external_grep_allowed__ignored;
        struct grep_opt opt;
        struct object_array list = { 0, 0, NULL };
        const char **paths = NULL;
                { OPTION_CALLBACK, ')', NULL, &opt, NULL, "",
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
                  close_callback },
 +              OPT_BOOLEAN('q', "quiet", &opt.status_only,
 +                          "indicate hit with exit status without output"),
                OPT_BOOLEAN(0, "all-match", &opt.all_match,
                        "show only matches from files that match all patterns"),
                OPT_GROUP(""),
 -#if NO_EXTERNAL_GREP
 -              OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
 -                      "allow calling of grep(1) (ignored by this build)"),
 -#else
 -              OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
 -                      "allow calling of grep(1) (default)"),
 -#endif
 +              OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
 +                          "allow calling of grep(1) (ignored by this build)"),
                { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
                  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
                OPT_END()
        opt.relative = 1;
        opt.pathname = 1;
        opt.pattern_tail = &opt.pattern_list;
+       opt.header_tail = &opt.header_list;
        opt.regflags = REG_NEWLINE;
        opt.max_depth = -1;
  
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
  
 +      /*
 +       * skip a -- separator; we know it cannot be
 +       * separating revisions from pathnames if
 +       * we haven't even had any patterns yet
 +       */
 +      if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) {
 +              argv++;
 +              argc--;
 +      }
 +
        /* First unrecognized non-option token */
        if (argc > 0 && !opt.pattern_list) {
                append_grep_pattern(&opt, argv[0], "command line", 0,
                argc--;
        }
  
 -      if ((opt.color && !opt.color_external) || opt.funcname)
 -              external_grep_allowed = 0;
        if (!opt.pattern_list)
                die("no pattern given.");
        if (!opt.fixed && opt.ignore_case)
                opt.regflags |= REG_ICASE;
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
                die("cannot mix --fixed-strings and regexp");
 +
 +#ifndef NO_PTHREADS
 +      if (online_cpus() == 1 || !grep_threads_ok(&opt))
 +              use_threads = 0;
 +
 +      if (use_threads)
 +              start_threads(&opt);
 +#else
 +      use_threads = 0;
 +#endif
 +
        compile_grep_patterns(&opt);
  
        /* Check revs and then paths */
        }
  
        if (!list.nr) {
 +              int hit;
                if (!cached)
                        setup_work_tree();
 -              return !grep_cache(&opt, paths, cached, external_grep_allowed);
 +
 +              hit = grep_cache(&opt, paths, cached);
 +              if (use_threads)
 +                      hit |= wait_all();
 +              return !hit;
        }
  
        if (cached)
        for (i = 0; i < list.nr; i++) {
                struct object *real_obj;
                real_obj = deref_tag(list.objects[i].item, NULL, 0);
 -              if (grep_object(&opt, paths, real_obj, list.objects[i].name))
 +              if (grep_object(&opt, paths, real_obj, list.objects[i].name)) {
                        hit = 1;
 +                      if (opt.status_only)
 +                              break;
 +              }
        }
 +
 +      if (use_threads)
 +              hit |= wait_all();
        free_grep_patterns(&opt);
        return !hit;
  }
diff --combined builtin-rev-list.c
index c924b3a2c76c1f9a7f5531504825ed1b5456d41a,c53ad9fa08a24ffcf89fd91b2a22ef36cbd7f2e4..5679170e82ed644d4c3eb4f71f26aa0ac9acce24
@@@ -253,7 -253,7 +253,7 @@@ static void print_var_int(const char *v
        printf("%s=%d\n", var, val);
  }
  
 -int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 +static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
  {
        int cnt, flags = info->bisect_show_flags;
        char hex[41] = "";
@@@ -371,8 -371,9 +371,9 @@@ int cmd_rev_list(int argc, const char *
            revs.diff)
                usage(rev_list_usage);
  
-       save_commit_buffer = revs.verbose_header ||
-               revs.grep_filter.pattern_list;
+       save_commit_buffer = (revs.verbose_header ||
+                             revs.grep_filter.pattern_list ||
+                             revs.grep_filter.header_list);
        if (bisect_list)
                revs.limited = 1;
  
diff --combined grep.c
index a0864f1cbbe5fcc6f28eb57a08ebfd9a76c87da6,eee99f31f1ac65d55b09fbeae2ecf7a4a99843bd..90a063a985098976f831c37227496d612fae37c0
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -11,8 -11,8 +11,8 @@@ void append_header_grep_pattern(struct 
        p->no = 0;
        p->token = GREP_PATTERN_HEAD;
        p->field = field;
-       *opt->pattern_tail = p;
-       opt->pattern_tail = &p->next;
+       *opt->header_tail = p;
+       opt->header_tail = &p->next;
        p->next = NULL;
  }
  
@@@ -29,26 -29,11 +29,26 @@@ void append_grep_pattern(struct grep_op
        p->next = NULL;
  }
  
 -static int is_fixed(const char *s)
 +struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
  {
 -      while (*s && !is_regex_special(*s))
 -              s++;
 -      return !*s;
 +      struct grep_pat *pat;
 +      struct grep_opt *ret = xmalloc(sizeof(struct grep_opt));
 +      *ret = *opt;
 +
 +      ret->pattern_list = NULL;
 +      ret->pattern_tail = &ret->pattern_list;
 +
 +      for(pat = opt->pattern_list; pat != NULL; pat = pat->next)
 +      {
 +              if(pat->token == GREP_PATTERN_HEAD)
 +                      append_header_grep_pattern(ret, pat->field,
 +                                                 pat->pattern);
 +              else
 +                      append_grep_pattern(ret, pat->pattern, pat->origin,
 +                                          pat->no, pat->token);
 +      }
 +
 +      return ret;
  }
  
  static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
  
        p->word_regexp = opt->word_regexp;
        p->ignore_case = opt->ignore_case;
 +      p->fixed = opt->fixed;
  
 -      if (opt->fixed || is_fixed(p->pattern))
 -              p->fixed = 1;
 -      if (opt->regflags & REG_ICASE)
 -              p->fixed = 0;
        if (p->fixed)
                return;
  
@@@ -184,9 -172,26 +184,26 @@@ static struct grep_expr *compile_patter
  void compile_grep_patterns(struct grep_opt *opt)
  {
        struct grep_pat *p;
+       struct grep_expr *header_expr = NULL;
  
-       if (opt->all_match)
-               opt->extended = 1;
+       if (opt->header_list) {
+               p = opt->header_list;
+               header_expr = compile_pattern_expr(&p);
+               if (p)
+                       die("incomplete pattern expression: %s", p->pattern);
+               for (p = opt->header_list; p; p = p->next) {
+                       switch (p->token) {
+                       case GREP_PATTERN: /* atom */
+                       case GREP_PATTERN_HEAD:
+                       case GREP_PATTERN_BODY:
+                               compile_regexp(p, opt);
+                               break;
+                       default:
+                               opt->extended = 1;
+                               break;
+                       }
+               }
+       }
  
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
                }
        }
  
-       if (!opt->extended)
+       if (opt->all_match || header_expr)
+               opt->extended = 1;
+       else if (!opt->extended)
                return;
  
        /* Then bundle them up in an expression.
                opt->pattern_expression = compile_pattern_expr(&p);
        if (p)
                die("incomplete pattern expression: %s", p->pattern);
+       if (!header_expr)
+               return;
+       if (opt->pattern_expression) {
+               struct grep_expr *z;
+               z = xcalloc(1, sizeof(*z));
+               z->node = GREP_NODE_OR;
+               z->u.binary.left = opt->pattern_expression;
+               z->u.binary.right = header_expr;
+               opt->pattern_expression = z;
+       } else {
+               opt->pattern_expression = header_expr;
+       }
+       opt->all_match = 1;
  }
  
  static void free_pattern_expr(struct grep_expr *x)
@@@ -272,8 -294,7 +306,8 @@@ static int word_char(char ch
  
  static void show_name(struct grep_opt *opt, const char *name)
  {
 -      printf("%s%c", name, opt->null_following_name ? '\0' : '\n');
 +      opt->output(opt, name, strlen(name));
 +      opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
  }
  
  
@@@ -510,32 -531,24 +544,32 @@@ static void show_line(struct grep_opt *
                      const char *name, unsigned lno, char sign)
  {
        int rest = eol - bol;
 +      char sign_str[1];
  
 +      sign_str[0] = sign;
        if (opt->pre_context || opt->post_context) {
                if (opt->last_shown == 0) {
                        if (opt->show_hunk_mark)
 -                              fputs("--\n", stdout);
 +                              opt->output(opt, "--\n", 3);
                        else
                                opt->show_hunk_mark = 1;
                } else if (lno > opt->last_shown + 1)
 -                      fputs("--\n", stdout);
 +                      opt->output(opt, "--\n", 3);
        }
        opt->last_shown = lno;
  
        if (opt->null_following_name)
 -              sign = '\0';
 -      if (opt->pathname)
 -              printf("%s%c", name, sign);
 -      if (opt->linenum)
 -              printf("%d%c", lno, sign);
 +              sign_str[0] = '\0';
 +      if (opt->pathname) {
 +              opt->output(opt, name, strlen(name));
 +              opt->output(opt, sign_str, 1);
 +      }
 +      if (opt->linenum) {
 +              char buf[32];
 +              snprintf(buf, sizeof(buf), "%d", lno);
 +              opt->output(opt, buf, strlen(buf));
 +              opt->output(opt, sign_str, 1);
 +      }
        if (opt->color) {
                regmatch_t match;
                enum grep_context ctx = GREP_CONTEXT_BODY;
                while (next_match(opt, bol, eol, ctx, &match, eflags)) {
                        if (match.rm_so == match.rm_eo)
                                break;
 -                      printf("%.*s%s%.*s%s",
 -                             (int)match.rm_so, bol,
 -                             opt->color_match,
 -                             (int)(match.rm_eo - match.rm_so), bol + match.rm_so,
 -                             GIT_COLOR_RESET);
 +
 +                      opt->output(opt, bol, match.rm_so);
 +                      opt->output(opt, opt->color_match,
 +                                  strlen(opt->color_match));
 +                      opt->output(opt, bol + match.rm_so,
 +                                  (int)(match.rm_eo - match.rm_so));
 +                      opt->output(opt, GIT_COLOR_RESET,
 +                                  strlen(GIT_COLOR_RESET));
                        bol += match.rm_eo;
                        rest -= match.rm_eo;
                        eflags = REG_NOTBOL;
                }
                *eol = ch;
        }
 -      printf("%.*s\n", rest, bol);
 +      opt->output(opt, bol, rest);
 +      opt->output(opt, "\n", 1);
  }
  
  static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
@@@ -640,98 -649,6 +674,98 @@@ static void show_pre_context(struct gre
        }
  }
  
 +static int should_lookahead(struct grep_opt *opt)
 +{
 +      struct grep_pat *p;
 +
 +      if (opt->extended)
 +              return 0; /* punt for too complex stuff */
 +      if (opt->invert)
 +              return 0;
 +      for (p = opt->pattern_list; p; p = p->next) {
 +              if (p->token != GREP_PATTERN)
 +                      return 0; /* punt for "header only" and stuff */
 +      }
 +      return 1;
 +}
 +
 +static int look_ahead(struct grep_opt *opt,
 +                    unsigned long *left_p,
 +                    unsigned *lno_p,
 +                    char **bol_p)
 +{
 +      unsigned lno = *lno_p;
 +      char *bol = *bol_p;
 +      struct grep_pat *p;
 +      char *sp, *last_bol;
 +      regoff_t earliest = -1;
 +
 +      for (p = opt->pattern_list; p; p = p->next) {
 +              int hit;
 +              regmatch_t m;
 +
 +              if (p->fixed)
 +                      hit = !fixmatch(p->pattern, bol, p->ignore_case, &m);
 +              else {
 +#ifdef REG_STARTEND
 +                      m.rm_so = 0;
 +                      m.rm_eo = *left_p;
 +                      hit = !regexec(&p->regexp, bol, 1, &m, REG_STARTEND);
 +#else
 +                      hit = !regexec(&p->regexp, bol, 1, &m, 0);
 +#endif
 +              }
 +              if (!hit || m.rm_so < 0 || m.rm_eo < 0)
 +                      continue;
 +              if (earliest < 0 || m.rm_so < earliest)
 +                      earliest = m.rm_so;
 +      }
 +
 +      if (earliest < 0) {
 +              *bol_p = bol + *left_p;
 +              *left_p = 0;
 +              return 1;
 +      }
 +      for (sp = bol + earliest; bol < sp && sp[-1] != '\n'; sp--)
 +              ; /* find the beginning of the line */
 +      last_bol = sp;
 +
 +      for (sp = bol; sp < last_bol; sp++) {
 +              if (*sp == '\n')
 +                      lno++;
 +      }
 +      *left_p -= last_bol - bol;
 +      *bol_p = last_bol;
 +      *lno_p = lno;
 +      return 0;
 +}
 +
 +int grep_threads_ok(const struct grep_opt *opt)
 +{
 +      /* If this condition is true, then we may use the attribute
 +       * machinery in grep_buffer_1. The attribute code is not
 +       * thread safe, so we disable the use of threads.
 +       */
 +      if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
 +          !opt->name_only)
 +              return 0;
 +
 +      /* If we are showing hunk marks, we should not do it for the
 +       * first match. The synchronization problem we get for this
 +       * constraint is not yet solved, so we disable threading in
 +       * this case.
 +       */
 +      if (opt->pre_context || opt->post_context)
 +              return 0;
 +
 +      return 1;
 +}
 +
 +static void std_output(struct grep_opt *opt, const void *buf, size_t size)
 +{
 +      fwrite(buf, size, 1, stdout);
 +}
 +
  static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         char *buf, unsigned long size, int collect_hits)
  {
        unsigned last_hit = 0;
        int binary_match_only = 0;
        unsigned count = 0;
 +      int try_lookahead = 0;
        enum grep_context ctx = GREP_CONTEXT_HEAD;
        xdemitconf_t xecfg;
  
        opt->last_shown = 0;
  
 +      if (!opt->output)
 +              opt->output = std_output;
 +
        if (buffer_is_binary(buf, size)) {
                switch (opt->binary) {
                case GREP_BINARY_DEFAULT:
                        opt->priv = &xecfg;
                }
        }
 +      try_lookahead = should_lookahead(opt);
  
        while (left) {
                char *eol, ch;
                int hit;
  
 +              /*
 +               * look_ahead() skips quicly to the line that possibly
 +               * has the next hit; don't call it if we need to do
 +               * something more than just skipping the current line
 +               * in response to an unmatch for the current line.  E.g.
 +               * inside a post-context window, we will show the current
 +               * line as a context around the previous hit when it
 +               * doesn't hit.
 +               */
 +              if (try_lookahead
 +                  && !(last_hit
 +                       && lno <= last_hit + opt->post_context)
 +                  && look_ahead(opt, &left, &lno, &bol))
 +                      break;
                eol = end_of_line(bol, &left);
                ch = *eol;
                *eol = 0;
                        if (opt->status_only)
                                return 1;
                        if (binary_match_only) {
 -                              printf("Binary file %s matches\n", name);
 +                              opt->output(opt, "Binary file ", 12);
 +                              opt->output(opt, name, strlen(name));
 +                              opt->output(opt, " matches\n", 9);
                                return 1;
                        }
                        if (opt->name_only) {
         * which feels mostly useless but sometimes useful.  Maybe
         * make it another option?  For now suppress them.
         */
 -      if (opt->count && count)
 -              printf("%s%c%u\n", name,
 -                     opt->null_following_name ? '\0' : ':', count);
 +      if (opt->count && count) {
 +              char buf[32];
 +              opt->output(opt, name, strlen(name));
 +              snprintf(buf, sizeof(buf), "%c%u\n",
 +                       opt->null_following_name ? '\0' : ':', count);
 +              opt->output(opt, buf, strlen(buf));
 +      }
        return !!last_hit;
  }
  
diff --combined grep.h
index 970308799664fe6a3871c0b8364c13e43cf96e1f,e39e5146d8701465f1d95a195de4183f28659f93..d35bc29bfd76f27c066f40dcb3f31078b4100059
--- 1/grep.h
--- 2/grep.h
+++ b/grep.h
@@@ -59,6 -59,8 +59,8 @@@ struct grep_expr 
  struct grep_opt {
        struct grep_pat *pattern_list;
        struct grep_pat **pattern_tail;
+       struct grep_pat *header_list;
+       struct grep_pat **header_tail;
        struct grep_expr *pattern_expression;
        const char *prefix;
        int prefix_length;
        int max_depth;
        int funcname;
        char color_match[COLOR_MAXLEN];
 -      const char *color_external;
        int regflags;
        unsigned pre_context;
        unsigned post_context;
        unsigned last_shown;
        int show_hunk_mark;
        void *priv;
 +
 +      void (*output)(struct grep_opt *opt, const void *data, size_t size);
 +      void *output_priv;
  };
  
  extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
@@@ -102,7 -102,4 +104,7 @@@ extern void compile_grep_patterns(struc
  extern void free_grep_patterns(struct grep_opt *opt);
  extern int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size);
  
 +extern struct grep_opt *grep_opt_dup(const struct grep_opt *opt);
 +extern int grep_threads_ok(const struct grep_opt *opt);
 +
  #endif
diff --combined revision.c
index a8f0aa4c676a882de7e5a2763d67f886340abcef,c84243d7bb5c1c4c718556f840f3acc152700274..29721ecf84316d01b18ffdee8bdefa8dbb3887db
@@@ -134,20 -134,10 +134,20 @@@ static void add_pending_object_with_mod
  {
        if (revs->no_walk && (obj->flags & UNINTERESTING))
                revs->no_walk = 0;
 -      if (revs->reflog_info && obj->type == OBJ_COMMIT &&
 -                      add_reflog_for_walk(revs->reflog_info,
 -                              (struct commit *)obj, name))
 -              return;
 +      if (revs->reflog_info && obj->type == OBJ_COMMIT) {
 +              struct strbuf buf = STRBUF_INIT;
 +              int len = interpret_branch_name(name, &buf);
 +              int st;
 +
 +              if (0 < len && name[len] && buf.len)
 +                      strbuf_addstr(&buf, name + len);
 +              st = add_reflog_for_walk(revs->reflog_info,
 +                                       (struct commit *)obj,
 +                                       buf.buf[0] ? buf.buf: name);
 +              strbuf_release(&buf);
 +              if (st)
 +                      return;
 +      }
        add_object_array_with_mode(obj, name, &revs->pending, mode);
  }
  
@@@ -278,7 -268,7 +278,7 @@@ static int tree_difference = REV_TREE_S
  static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
 -                  const char *fullpath)
 +                  const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
  
@@@ -291,8 -281,7 +291,8 @@@ static void file_change(struct diff_opt
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
 -               const char *fullpath)
 +               const char *fullpath,
 +               unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        tree_difference = REV_TREE_DIFFERENT;
        DIFF_OPT_SET(options, HAS_CHANGES);
@@@ -547,9 -536,6 +547,9 @@@ static void cherry_pick_list(struct com
                        right_count++;
        }
  
 +      if (!left_count || !right_count)
 +              return;
 +
        left_first = left_count < right_count;
        init_patch_ids(&ids);
        if (revs->diffopt.nr_paths) {
@@@ -713,18 -699,12 +713,18 @@@ static int handle_one_ref(const char *p
        return 0;
  }
  
 +static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
 +      unsigned flags)
 +{
 +      cb->all_revs = revs;
 +      cb->all_flags = flags;
 +}
 +
  static void handle_refs(struct rev_info *revs, unsigned flags,
                int (*for_each)(each_ref_fn, void *))
  {
        struct all_refs_cb cb;
 -      cb.all_revs = revs;
 -      cb.all_flags = flags;
 +      init_all_refs_cb(&cb, revs, flags);
        for_each(handle_one_ref, &cb);
  }
  
@@@ -826,6 -806,7 +826,7 @@@ void init_revisions(struct rev_info *re
  
        revs->grep_filter.status_only = 1;
        revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
+       revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
        revs->grep_filter.regflags = REG_NEWLINE;
  
        diff_setup(&revs->diffopt);
@@@ -1181,22 -1162,13 +1182,22 @@@ static int handle_revision_opt(struct r
                revs->verbose_header = 1;
        } else if (!strcmp(arg, "--pretty")) {
                revs->verbose_header = 1;
 +              revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
                revs->verbose_header = 1;
 +              revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
 +      } else if (!strcmp(arg, "--show-notes")) {
 +              revs->show_notes = 1;
 +              revs->show_notes_given = 1;
 +      } else if (!strcmp(arg, "--no-notes")) {
 +              revs->show_notes = 0;
 +              revs->show_notes_given = 1;
        } else if (!strcmp(arg, "--oneline")) {
                revs->verbose_header = 1;
                get_commit_format("oneline", revs);
 +              revs->pretty_given = 1;
                revs->abbrev_commit = 1;
        } else if (!strcmp(arg, "--graph")) {
                revs->topo_order = 1;
@@@ -1381,30 -1353,6 +1382,30 @@@ int setup_revisions(int argc, const cha
                                handle_refs(revs, flags, for_each_remote_ref);
                                continue;
                        }
 +                      if (!prefixcmp(arg, "--glob=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref(handle_one_ref, arg + 7, &cb);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--branches=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--tags=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
 +                              continue;
 +                      }
 +                      if (!prefixcmp(arg, "--remotes=")) {
 +                              struct all_refs_cb cb;
 +                              init_all_refs_cb(&cb, revs, flags);
 +                              for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--reflog")) {
                                handle_reflog(revs, flags);
                                continue;
@@@ -1804,7 -1752,7 +1805,7 @@@ static int rewrite_parents(struct rev_i
  
  static int commit_match(struct commit *commit, struct rev_info *opt)
  {
-       if (!opt->grep_filter.pattern_list)
+       if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                return 1;
        return grep_buffer(&opt->grep_filter,
                           NULL, /* we say nothing, not even filename */
diff --combined t/t7002-grep.sh
index 0b583cbfc15ae09c030c0d4e82635f244ffc5293,6baa4461f704a39807cee861071f075f09da03da..af63d6ec6dfdf34b7a8ab4c19547d1d84cc184f2
@@@ -8,6 -8,18 +8,6 @@@ test_description='git grep various
  
  . ./test-lib.sh
  
 -test_expect_success 'Check for external grep support' '
 -      case "$(git grep -h 2>&1|grep ext-grep)" in
 -      *"(default)"*)
 -              test_set_prereq EXTGREP
 -              true;;
 -      *"(ignored by this build)"*)
 -              true;;
 -      *)
 -              false;;
 -      esac
 -'
 -
  cat >hello.c <<EOF
  #include <stdio.h>
  int main(int argc, const char **argv)
@@@ -291,14 -303,6 +291,14 @@@ y:y y
  z:zzz
  EOF
  
 +test_expect_success 'grep -q, silently report matches' '
 +      >empty &&
 +      git grep -q mmap >actual &&
 +      test_cmp empty actual &&
 +      test_must_fail git grep -q qfwfq >actual &&
 +      test_cmp empty actual
 +'
 +
  # Create 1024 file names that sort between "y" and "z" to make sure
  # the two files are handled by different calls to an external grep.
  # This depends on MAXARGS in builtin-grep.c being 1024 or less.
@@@ -310,8 -314,8 +310,8 @@@ test_expect_success 'grep -C1, hunk mar
        test_cmp expected actual
  '
  
 -test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '
 -      git grep -C1 --no-ext-grep "^[yz]" >actual &&
 +test_expect_success 'grep -C1 hunk mark between files' '
 +      git grep -C1 "^[yz]" >actual &&
        test_cmp expected actual
  '
  
@@@ -353,7 -357,7 +353,7 @@@ test_expect_success 'log grep (4)' 
  '
  
  test_expect_success 'log grep (5)' '
-       git log --author=Thor -F --grep=Thu --pretty=tformat:%s >actual &&
+       git log --author=Thor -F --pretty=tformat:%s >actual &&
        ( echo third ; echo initial ) >expect &&
        test_cmp expect actual
  '
@@@ -364,10 -368,18 +364,18 @@@ test_expect_success 'log grep (6)' 
        test_cmp expect actual
  '
  
+ test_expect_success 'log --grep --author implicitly uses all-match' '
+       # grep matches initial and second but not third
+       # author matches only initial and third
+       git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
+       echo initial >expect &&
+       test_cmp expect actual
+ '
  test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
 -      test "$(git grep --no-ext-grep test)" = "t/t:test" &&
 +      test "$(git grep test)" = "t/t:test" &&
        git update-index --no-assume-unchanged t/t &&
        git checkout t/t
  '
@@@ -434,37 -446,16 +442,37 @@@ test_expect_success 'grep -Fi' 
        test_cmp expected actual
  '
  
 -test_expect_success EXTGREP 'external grep is called' '
 -      GIT_TRACE=2 git grep foo >/dev/null 2>actual &&
 -      grep "trace: grep:.*foo" actual >/dev/null
 +test_expect_success 'setup double-dash tests' '
 +cat >double-dash <<EOF &&
 +--
 +->
 +other
 +EOF
 +git add double-dash
 +'
 +
 +cat >expected <<EOF
 +double-dash:->
 +EOF
 +test_expect_success 'grep -- pattern' '
 +      git grep -- "->" >actual &&
 +      test_cmp expected actual
 +'
 +test_expect_success 'grep -- pattern -- pathspec' '
 +      git grep -- "->" -- double-dash >actual &&
 +      test_cmp expected actual
 +'
 +test_expect_success 'grep -e pattern -- path' '
 +      git grep -e "->" -- double-dash >actual &&
 +      test_cmp expected actual
  '
  
 -test_expect_success EXTGREP 'no external grep when skip-worktree entries exist' '
 -      git update-index --skip-worktree file &&
 -      GIT_TRACE=2 git grep foo >/dev/null 2>actual &&
 -      ! grep "trace: grep:" actual >/dev/null &&
 -      git update-index --no-skip-worktree file
 +cat >expected <<EOF
 +double-dash:--
 +EOF
 +test_expect_success 'grep -e -- -- path' '
 +      git grep -e -- -- double-dash >actual &&
 +      test_cmp expected actual
  '
  
  test_done