Merge branch 'jc/pickaxe-ignore-case'
authorJunio C Hamano <gitster@pobox.com>
Wed, 7 Mar 2012 20:12:59 +0000 (12:12 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 7 Mar 2012 20:12:59 +0000 (12:12 -0800)
By Junio C Hamano (2) and Ramsay Jones (1)
* jc/pickaxe-ignore-case:
ctype.c: Fix a sparse warning
pickaxe: allow -i to search in patch case-insensitively
grep: use static trans-case table

1  2 
ctype.c
diff.h
git-compat-util.h
grep.c
revision.c
diff --combined ctype.c
index af722f957fa5a1899dc906196d9b0372acab946d,7c14d85c153d9647fff617768badd9a24e52e9b4..935327164b1066ad011d11fdcd3fd1dc1a936c58
+++ b/ctype.c
@@@ -3,7 -3,7 +3,7 @@@
   *
   * No surprises, and works with signed and unsigned chars.
   */
 -#include "cache.h"
 +#include "git-compat-util.h"
  
  enum {
        S = GIT_SPACE,
@@@ -25,3 -25,39 +25,39 @@@ unsigned char sane_ctype[256] = 
        A, A, A, A, A, A, A, A, A, A, A, R, R, 0, P, 0,         /* 112..127 */
        /* Nothing in the 128.. range */
  };
+ /* For case-insensitive kwset */
+ const char tolower_trans_tbl[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+        ' ',  '!',  '"',  '#',  '$',  '%',  '&', 0x27,
+        '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+        '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
+        '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+        '@',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+        'x',  'y',  'z',  '[', 0x5c,  ']',  '^',  '_',
+        '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
+        'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+        'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
+        'x',  'y',  'z',  '{',  '|',  '}',  '~', 0x7f,
+       0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+       0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+       0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+       0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+       0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+       0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+       0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+       0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+       0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+       0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+       0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+       0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+       0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+       0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+       0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ };
diff --combined diff.h
index e6a782c220865d764eb26cfbce471cad88b910ba,436b574a23e7808ff38d87d306d8437894478052..cb687436a0ddb9a08fc1a9e9cec569233284e01f
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -12,8 -12,6 +12,8 @@@ struct diff_queue_struct
  struct strbuf;
  struct diff_filespec;
  struct userdiff_driver;
 +struct sha1_array;
 +struct commit;
  
  typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
@@@ -82,6 -80,7 +82,7 @@@ typedef struct strbuf *(*diff_prefix_fn
  #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
  #define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)
  #define DIFF_OPT_FUNCCONTEXT         (1 << 29)
+ #define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
  
  #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
  #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
@@@ -129,7 -128,6 +130,7 @@@ struct diff_options 
  
        int stat_width;
        int stat_name_width;
 +      int stat_graph_width;
        int stat_count;
        const char *word_regex;
        enum diff_words_type word_diff;
@@@ -198,9 -196,9 +199,9 @@@ struct combine_diff_path 
  extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
                              int dense, struct rev_info *);
  
 -extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
 +extern void diff_tree_combined(const unsigned char *sha1, const struct sha1_array *parents, int dense, struct rev_info *rev);
  
 -extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
 +extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev);
  
  void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
  
@@@ -325,7 -323,4 +326,7 @@@ extern struct userdiff_driver *get_text
  
  extern int parse_rename_score(const char **cp_p);
  
 +extern int print_stat_summary(FILE *fp, int files,
 +                            int insertions, int deletions);
 +
  #endif /* DIFF_H */
diff --combined git-compat-util.h
index 426ae43be9cd7aa4230888ff0aa28ed2fd165ce0,ac0a87bdb9c77eb7a8321e5c63af7e73d726d62e..ed11ad8119bb22db8a5428aed5525c40fbb72217
  #else
  #include <poll.h>
  #endif
 -#ifndef __MINGW32__
 +#if defined(__MINGW32__)
 +/* pull in Windows compatibility stuff */
 +#include "compat/mingw.h"
 +#elif defined(_MSC_VER)
 +#include "compat/msvc.h"
 +#else
  #include <sys/wait.h>
  #include <sys/resource.h>
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <netdb.h>
  #include <pwd.h>
 +#include <sys/un.h>
  #ifndef NO_INTTYPES_H
  #include <inttypes.h>
  #else
  #include <grp.h>
  #define _ALL_SOURCE 1
  #endif
 -#else         /* __MINGW32__ */
 -/* pull in Windows compatibility stuff */
 -#include "compat/mingw.h"
 -#endif        /* __MINGW32__ */
 -#ifdef _MSC_VER
 -#include "compat/msvc.h"
  #endif
  
  #ifndef NO_LIBGEN_H
@@@ -457,14 -457,15 +457,17 @@@ static inline int has_extension(const c
        return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
  }
  
+ /* in ctype.c, for kwset users */
+ extern const char tolower_trans_tbl[256];
  /* Sane ctype - no locale, and works with signed chars */
  #undef isascii
  #undef isspace
  #undef isdigit
  #undef isalpha
  #undef isalnum
 +#undef islower
 +#undef isupper
  #undef tolower
  #undef toupper
  extern unsigned char sane_ctype[256];
  #define isdigit(x) sane_istest(x,GIT_DIGIT)
  #define isalpha(x) sane_istest(x,GIT_ALPHA)
  #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
 +#define islower(x) sane_iscase(x, 1)
 +#define isupper(x) sane_iscase(x, 0)
  #define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
  #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
  #define tolower(x) sane_case((unsigned char)(x), 0x20)
@@@ -495,17 -494,6 +498,17 @@@ static inline int sane_case(int x, int 
        return x;
  }
  
 +static inline int sane_iscase(int x, int is_lower)
 +{
 +      if (!sane_istest(x, GIT_ALPHA))
 +              return 0;
 +
 +      if (is_lower)
 +              return (x & 0x20) != 0;
 +      else
 +              return (x & 0x20) == 0;
 +}
 +
  static inline int strtoul_ui(char const *s, int base, unsigned int *result)
  {
        unsigned long ul;
diff --combined grep.c
index f492d267cc157d46f899c70d01ea707d737d27ae,1030f38f53f2cf3e3efcd70049d4c2efa575c14b..190139cfcd0d61f364a88010fe1dd6e71e26f9e2
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -79,7 -79,7 +79,7 @@@ static void compile_pcre_regexp(struct 
  {
        const char *error;
        int erroffset;
 -      int options = 0;
 +      int options = PCRE_MULTILINE;
  
        if (opt->ignore_case)
                options |= PCRE_CASELESS;
@@@ -168,15 -168,10 +168,10 @@@ static void compile_regexp(struct grep_
                p->fixed = 0;
  
        if (p->fixed) {
-               if (opt->regflags & REG_ICASE || p->ignore_case) {
-                       static char trans[256];
-                       int i;
-                       for (i = 0; i < 256; i++)
-                               trans[i] = tolower(i);
-                       p->kws = kwsalloc(trans);
-               } else {
+               if (opt->regflags & REG_ICASE || p->ignore_case)
+                       p->kws = kwsalloc(tolower_trans_tbl);
+               else
                        p->kws = kwsalloc(NULL);
-               }
                kwsincr(p->kws, p->pattern, p->patternlen);
                kwsprep(p->kws);
                return;
@@@ -806,51 -801,10 +801,51 @@@ static void show_line(struct grep_opt *
        opt->output(opt, "\n", 1);
  }
  
 -static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
 +#ifndef NO_PTHREADS
 +int grep_use_locks;
 +
 +/*
 + * This lock protects access to the gitattributes machinery, which is
 + * not thread-safe.
 + */
 +pthread_mutex_t grep_attr_mutex;
 +
 +static inline void grep_attr_lock(void)
 +{
 +      if (grep_use_locks)
 +              pthread_mutex_lock(&grep_attr_mutex);
 +}
 +
 +static inline void grep_attr_unlock(void)
 +{
 +      if (grep_use_locks)
 +              pthread_mutex_unlock(&grep_attr_mutex);
 +}
 +
 +/*
 + * Same as git_attr_mutex, but protecting the thread-unsafe object db access.
 + */
 +pthread_mutex_t grep_read_mutex;
 +
 +#else
 +#define grep_attr_lock()
 +#define grep_attr_unlock()
 +#endif
 +
 +static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol)
  {
        xdemitconf_t *xecfg = opt->priv;
 -      if (xecfg && xecfg->find_func) {
 +      if (xecfg && !xecfg->find_func) {
 +              grep_source_load_driver(gs);
 +              if (gs->driver->funcname.pattern) {
 +                      const struct userdiff_funcname *pe = &gs->driver->funcname;
 +                      xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
 +              } else {
 +                      xecfg = opt->priv = NULL;
 +              }
 +      }
 +
 +      if (xecfg) {
                char buf[1];
                return xecfg->find_func(bol, eol - bol, buf, 1,
                                        xecfg->find_func_priv) >= 0;
        return 0;
  }
  
 -static void show_funcname_line(struct grep_opt *opt, const char *name,
 -                             char *buf, char *bol, unsigned lno)
 +static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
 +                             char *bol, unsigned lno)
  {
 -      while (bol > buf) {
 +      while (bol > gs->buf) {
                char *eol = --bol;
  
 -              while (bol > buf && bol[-1] != '\n')
 +              while (bol > gs->buf && bol[-1] != '\n')
                        bol--;
                lno--;
  
                if (lno <= opt->last_shown)
                        break;
  
 -              if (match_funcname(opt, bol, eol)) {
 -                      show_line(opt, bol, eol, name, lno, '=');
 +              if (match_funcname(opt, gs, bol, eol)) {
 +                      show_line(opt, bol, eol, gs->name, lno, '=');
                        break;
                }
        }
  }
  
 -static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
 +static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
                             char *bol, char *end, unsigned lno)
  {
        unsigned cur = lno, from = 1, funcname_lno = 0;
        int funcname_needed = !!opt->funcname;
  
 -      if (opt->funcbody && !match_funcname(opt, bol, end))
 +      if (opt->funcbody && !match_funcname(opt, gs, bol, end))
                funcname_needed = 2;
  
        if (opt->pre_context < lno)
                from = opt->last_shown + 1;
  
        /* Rewind. */
 -      while (bol > buf &&
 +      while (bol > gs->buf &&
               cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) {
                char *eol = --bol;
  
 -              while (bol > buf && bol[-1] != '\n')
 +              while (bol > gs->buf && bol[-1] != '\n')
                        bol--;
                cur--;
 -              if (funcname_needed && match_funcname(opt, bol, eol)) {
 +              if (funcname_needed && match_funcname(opt, gs, bol, eol)) {
                        funcname_lno = cur;
                        funcname_needed = 0;
                }
  
        /* We need to look even further back to find a function signature. */
        if (opt->funcname && funcname_needed)
 -              show_funcname_line(opt, name, buf, bol, cur);
 +              show_funcname_line(opt, gs, bol, cur);
  
        /* Back forward. */
        while (cur < lno) {
  
                while (*eol != '\n')
                        eol++;
 -              show_line(opt, bol, eol, name, cur, sign);
 +              show_line(opt, bol, eol, gs->name, cur, sign);
                bol = eol + 1;
                cur++;
        }
@@@ -983,15 -937,29 +978,15 @@@ static int look_ahead(struct grep_opt *
        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;
 -
 -      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)
 +static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits)
  {
 -      char *bol = buf;
 -      unsigned long left = size;
 +      char *bol;
 +      unsigned long left;
        unsigned lno = 1;
        unsigned last_hit = 0;
        int binary_match_only = 0;
  
        switch (opt->binary) {
        case GREP_BINARY_DEFAULT:
 -              if (buffer_is_binary(buf, size))
 +              if (grep_source_is_binary(gs))
                        binary_match_only = 1;
                break;
        case GREP_BINARY_NOMATCH:
 -              if (buffer_is_binary(buf, size))
 +              if (grep_source_is_binary(gs))
                        return 0; /* Assume unmatch */
                break;
        case GREP_BINARY_TEXT:
        }
  
        memset(&xecfg, 0, sizeof(xecfg));
 -      if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
 -          !opt->name_only && !binary_match_only && !collect_hits) {
 -              struct userdiff_driver *drv = userdiff_find_by_path(name);
 -              if (drv && drv->funcname.pattern) {
 -                      const struct userdiff_funcname *pe = &drv->funcname;
 -                      xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
 -                      opt->priv = &xecfg;
 -              }
 -      }
 +      opt->priv = &xecfg;
 +
        try_lookahead = should_lookahead(opt);
  
 +      if (grep_source_load(gs) < 0)
 +              return 0;
 +
 +      bol = gs->buf;
 +      left = gs->size;
        while (left) {
                char *eol, ch;
                int hit;
                        if (opt->status_only)
                                return 1;
                        if (opt->name_only) {
 -                              show_name(opt, name);
 +                              show_name(opt, gs->name);
                                return 1;
                        }
                        if (opt->count)
                                goto next_line;
                        if (binary_match_only) {
                                opt->output(opt, "Binary file ", 12);
 -                              output_color(opt, name, strlen(name),
 +                              output_color(opt, gs->name, strlen(gs->name),
                                             opt->color_filename);
                                opt->output(opt, " matches\n", 9);
                                return 1;
                         * pre-context lines, we would need to show them.
                         */
                        if (opt->pre_context || opt->funcbody)
 -                              show_pre_context(opt, name, buf, bol, eol, lno);
 +                              show_pre_context(opt, gs, bol, eol, lno);
                        else if (opt->funcname)
 -                              show_funcname_line(opt, name, buf, bol, lno);
 -                      show_line(opt, bol, eol, name, lno, ':');
 +                              show_funcname_line(opt, gs, bol, lno);
 +                      show_line(opt, bol, eol, gs->name, lno, ':');
                        last_hit = lno;
                        if (opt->funcbody)
                                show_function = 1;
                        goto next_line;
                }
 -              if (show_function && match_funcname(opt, bol, eol))
 +              if (show_function && match_funcname(opt, gs, bol, eol))
                        show_function = 0;
                if (show_function ||
                    (last_hit && lno <= last_hit + opt->post_context)) {
                        /* If the last hit is within the post context,
                         * we need to show this line.
                         */
 -                      show_line(opt, bol, eol, name, lno, '-');
 +                      show_line(opt, bol, eol, gs->name, lno, '-');
                }
  
        next_line:
                return 0;
        if (opt->unmatch_name_only) {
                /* We did not see any hit, so we want to show this */
 -              show_name(opt, name);
 +              show_name(opt, gs->name);
                return 1;
        }
  
         */
        if (opt->count && count) {
                char buf[32];
 -              output_color(opt, name, strlen(name), opt->color_filename);
 +              output_color(opt, gs->name, strlen(gs->name), opt->color_filename);
                output_sep(opt, ':');
                snprintf(buf, sizeof(buf), "%u\n", count);
                opt->output(opt, buf, strlen(buf));
@@@ -1191,174 -1161,23 +1186,174 @@@ static int chk_hit_marker(struct grep_e
        }
  }
  
 -int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
 +int grep_source(struct grep_opt *opt, struct grep_source *gs)
  {
        /*
         * we do not have to do the two-pass grep when we do not check
         * buffer-wide "all-match".
         */
        if (!opt->all_match)
 -              return grep_buffer_1(opt, name, buf, size, 0);
 +              return grep_source_1(opt, gs, 0);
  
        /* Otherwise the toplevel "or" terms hit a bit differently.
         * We first clear hit markers from them.
         */
        clr_hit_marker(opt->pattern_expression);
 -      grep_buffer_1(opt, name, buf, size, 1);
 +      grep_source_1(opt, gs, 1);
  
        if (!chk_hit_marker(opt->pattern_expression))
                return 0;
  
 -      return grep_buffer_1(opt, name, buf, size, 0);
 +      return grep_source_1(opt, gs, 0);
 +}
 +
 +int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size)
 +{
 +      struct grep_source gs;
 +      int r;
 +
 +      grep_source_init(&gs, GREP_SOURCE_BUF, NULL, NULL);
 +      gs.buf = buf;
 +      gs.size = size;
 +
 +      r = grep_source(opt, &gs);
 +
 +      grep_source_clear(&gs);
 +      return r;
 +}
 +
 +void grep_source_init(struct grep_source *gs, enum grep_source_type type,
 +                    const char *name, const void *identifier)
 +{
 +      gs->type = type;
 +      gs->name = name ? xstrdup(name) : NULL;
 +      gs->buf = NULL;
 +      gs->size = 0;
 +      gs->driver = NULL;
 +
 +      switch (type) {
 +      case GREP_SOURCE_FILE:
 +              gs->identifier = xstrdup(identifier);
 +              break;
 +      case GREP_SOURCE_SHA1:
 +              gs->identifier = xmalloc(20);
 +              memcpy(gs->identifier, identifier, 20);
 +              break;
 +      case GREP_SOURCE_BUF:
 +              gs->identifier = NULL;
 +      }
 +}
 +
 +void grep_source_clear(struct grep_source *gs)
 +{
 +      free(gs->name);
 +      gs->name = NULL;
 +      free(gs->identifier);
 +      gs->identifier = NULL;
 +      grep_source_clear_data(gs);
 +}
 +
 +void grep_source_clear_data(struct grep_source *gs)
 +{
 +      switch (gs->type) {
 +      case GREP_SOURCE_FILE:
 +      case GREP_SOURCE_SHA1:
 +              free(gs->buf);
 +              gs->buf = NULL;
 +              gs->size = 0;
 +              break;
 +      case GREP_SOURCE_BUF:
 +              /* leave user-provided buf intact */
 +              break;
 +      }
 +}
 +
 +static int grep_source_load_sha1(struct grep_source *gs)
 +{
 +      enum object_type type;
 +
 +      grep_read_lock();
 +      gs->buf = read_sha1_file(gs->identifier, &type, &gs->size);
 +      grep_read_unlock();
 +
 +      if (!gs->buf)
 +              return error(_("'%s': unable to read %s"),
 +                           gs->name,
 +                           sha1_to_hex(gs->identifier));
 +      return 0;
 +}
 +
 +static int grep_source_load_file(struct grep_source *gs)
 +{
 +      const char *filename = gs->identifier;
 +      struct stat st;
 +      char *data;
 +      size_t size;
 +      int i;
 +
 +      if (lstat(filename, &st) < 0) {
 +      err_ret:
 +              if (errno != ENOENT)
 +                      error(_("'%s': %s"), filename, strerror(errno));
 +              return -1;
 +      }
 +      if (!S_ISREG(st.st_mode))
 +              return -1;
 +      size = xsize_t(st.st_size);
 +      i = open(filename, O_RDONLY);
 +      if (i < 0)
 +              goto err_ret;
 +      data = xmalloc(size + 1);
 +      if (st.st_size != read_in_full(i, data, size)) {
 +              error(_("'%s': short read %s"), filename, strerror(errno));
 +              close(i);
 +              free(data);
 +              return -1;
 +      }
 +      close(i);
 +      data[size] = 0;
 +
 +      gs->buf = data;
 +      gs->size = size;
 +      return 0;
 +}
 +
 +int grep_source_load(struct grep_source *gs)
 +{
 +      if (gs->buf)
 +              return 0;
 +
 +      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_BUF:
 +              return gs->buf ? 0 : -1;
 +      }
 +      die("BUG: invalid grep_source type");
 +}
 +
 +void grep_source_load_driver(struct grep_source *gs)
 +{
 +      if (gs->driver)
 +              return;
 +
 +      grep_attr_lock();
 +      gs->driver = userdiff_find_by_path(gs->name);
 +      if (!gs->driver)
 +              gs->driver = userdiff_find_by_name("default");
 +      grep_attr_unlock();
 +}
 +
 +int grep_source_is_binary(struct grep_source *gs)
 +{
 +      grep_source_load_driver(gs);
 +      if (gs->driver->binary != -1)
 +              return gs->driver->binary;
 +
 +      if (!grep_source_load(gs))
 +              return buffer_is_binary(gs->buf, gs->size);
 +
 +      return 0;
  }
diff --combined revision.c
index 819ff012ff046fee0da1fee16f965b3059ebc31b,971b7dc98da9edcd7d320de38838046e747f9666..b3554ed11b5c1a987c1d22b7851c91641bbf7f56
@@@ -139,32 -139,11 +139,32 @@@ void mark_tree_uninteresting(struct tre
  
  void mark_parents_uninteresting(struct commit *commit)
  {
 -      struct commit_list *parents = commit->parents;
 +      struct commit_list *parents = NULL, *l;
 +
 +      for (l = commit->parents; l; l = l->next)
 +              commit_list_insert(l->item, &parents);
  
        while (parents) {
                struct commit *commit = parents->item;
 -              if (!(commit->object.flags & UNINTERESTING)) {
 +              l = parents;
 +              parents = parents->next;
 +              free(l);
 +
 +              while (commit) {
 +                      /*
 +                       * A missing commit is ok iff its parent is marked
 +                       * uninteresting.
 +                       *
 +                       * We just mark such a thing parsed, so that when
 +                       * it is popped next time around, we won't be trying
 +                       * to parse it and get an error.
 +                       */
 +                      if (!has_sha1_file(commit->object.sha1))
 +                              commit->object.parsed = 1;
 +
 +                      if (commit->object.flags & UNINTERESTING)
 +                              break;
 +
                        commit->object.flags |= UNINTERESTING;
  
                        /*
                         * wasn't uninteresting), in which case we need
                         * to mark its parents recursively too..
                         */
 -                      if (commit->parents)
 -                              mark_parents_uninteresting(commit);
 -              }
 +                      if (!commit->parents)
 +                              break;
  
 -              /*
 -               * A missing commit is ok iff its parent is marked
 -               * uninteresting.
 -               *
 -               * We just mark such a thing parsed, so that when
 -               * it is popped next time around, we won't be trying
 -               * to parse it and get an error.
 -               */
 -              if (!has_sha1_file(commit->object.sha1))
 -                      commit->object.parsed = 1;
 -              parents = parents->next;
 +                      for (l = commit->parents->next; l; l = l->next)
 +                              commit_list_insert(l->item, &parents);
 +                      commit = commit->parents->item;
 +              }
        }
  }
  
@@@ -429,7 -416,7 +429,7 @@@ static int rev_same_tree_as_empty(struc
  static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
  {
        struct commit_list **pp, *parent;
 -      int tree_changed = 0, tree_same = 0;
 +      int tree_changed = 0, tree_same = 0, nth_parent = 0;
  
        /*
         * If we don't do pruning, everything is interesting
        while ((parent = *pp) != NULL) {
                struct commit *p = parent->item;
  
 +              /*
 +               * Do not compare with later parents when we care only about
 +               * the first parent chain, in order to avoid derailing the
 +               * traversal to follow a side branch that brought everything
 +               * in the path we are limited to by the pathspec.
 +               */
 +              if (revs->first_parent_only && nth_parent++)
 +                      break;
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
@@@ -1490,8 -1469,6 +1490,8 @@@ static int handle_revision_opt(struct r
                revs->show_notes = 1;
                revs->show_notes_given = 1;
                revs->notes_opt.use_default_notes = 1;
 +      } else if (!strcmp(arg, "--show-signature")) {
 +              revs->show_signature = 1;
        } else if (!prefixcmp(arg, "--show-notes=") ||
                   !prefixcmp(arg, "--notes=")) {
                struct strbuf buf = STRBUF_INIT;
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
+               DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
@@@ -2149,6 -2127,7 +2150,6 @@@ static int commit_match(struct commit *
        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 */
                           commit->buffer, strlen(commit->buffer));
  }