Merge branch 'tb/grep-only-matching'
authorJunio C Hamano <gitster@pobox.com>
Thu, 2 Aug 2018 22:30:44 +0000 (15:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 2 Aug 2018 22:30:44 +0000 (15:30 -0700)
"git grep" learned the "--only-matching" option.

* tb/grep-only-matching:
grep.c: teach 'git grep --only-matching'
grep.c: extract show_line_header()

1  2 
builtin/grep.c
grep.c
grep.h
t/t7810-grep.sh
diff --combined builtin/grep.c
index f9678f19e4aff52ae82ee1ff5d9842950b943f43,228b83990f990e8cd6f2a6bcbe05df0955921eda..056161f0f88596a6074ef6edddeaa6c894d8b801
@@@ -647,8 -647,7 +647,8 @@@ static int grep_objects(struct grep_op
  
        for (i = 0; i < nr; i++) {
                struct object *real_obj;
 -              real_obj = deref_tag(list->objects[i].item, NULL, 0);
 +              real_obj = deref_tag(the_repository, list->objects[i].item,
 +                                   NULL, 0);
  
                /* load the gitmodules file for this rev */
                if (recurse_submodules) {
@@@ -844,6 -843,8 +844,8 @@@ int cmd_grep(int argc, const char **arg
                OPT_BOOL_F('z', "null", &opt.null_following_name,
                           N_("print NUL after filenames"),
                           PARSE_OPT_NOCOMPLETE),
+               OPT_BOOL('o', "only-matching", &opt.only_matching,
+                       N_("show only matching parts of a line")),
                OPT_BOOL('c', "count", &opt.count,
                        N_("show the number of matches instead of matching lines")),
                OPT__COLOR(&opt.color, N_("highlight matches")),
        if (!opt.pattern_list)
                die(_("no pattern given."));
  
+       /* --only-matching has no effect with --invert. */
+       if (opt.invert)
+               opt.only_matching = 0;
        /*
         * We have to find "--" in a separate pass, because its presence
         * influences how we will parse arguments that come before it.
diff --combined grep.c
index cd7fc6f66cadfcb38ad73f171b97a419610aad37,49a744f96b373c6b90cde4d2d090eea2aa18cbc3..2b26cee08d559ceba6dd5a83ae90aaa0155ebca8
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -1,32 -1,18 +1,32 @@@
  #include "cache.h"
  #include "config.h"
  #include "grep.h"
 +#include "object-store.h"
  #include "userdiff.h"
  #include "xdiff-interface.h"
  #include "diff.h"
  #include "diffcore.h"
  #include "commit.h"
  #include "quote.h"
 +#include "help.h"
  
  static int grep_source_load(struct grep_source *gs);
  static int grep_source_is_binary(struct grep_source *gs);
  
  static struct grep_opt grep_defaults;
  
 +static const char *color_grep_slots[] = {
 +      [GREP_COLOR_CONTEXT]        = "context",
 +      [GREP_COLOR_FILENAME]       = "filename",
 +      [GREP_COLOR_FUNCTION]       = "function",
 +      [GREP_COLOR_LINENO]         = "lineNumber",
 +      [GREP_COLOR_COLUMNNO]       = "column",
 +      [GREP_COLOR_MATCH_CONTEXT]  = "matchContext",
 +      [GREP_COLOR_MATCH_SELECTED] = "matchSelected",
 +      [GREP_COLOR_SELECTED]       = "selected",
 +      [GREP_COLOR_SEP]            = "separator",
 +};
 +
  static void std_output(struct grep_opt *opt, const void *buf, size_t size)
  {
        fwrite(buf, size, 1, stdout);
@@@ -56,15 -42,16 +56,16 @@@ void init_grep_defaults(void
        opt->pathname = 1;
        opt->max_depth = -1;
        opt->pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
 -      color_set(opt->color_context, "");
 -      color_set(opt->color_filename, "");
 -      color_set(opt->color_function, "");
 -      color_set(opt->color_lineno, "");
 -      color_set(opt->color_columnno, "");
 -      color_set(opt->color_match_context, GIT_COLOR_BOLD_RED);
 -      color_set(opt->color_match_selected, GIT_COLOR_BOLD_RED);
 -      color_set(opt->color_selected, "");
 -      color_set(opt->color_sep, GIT_COLOR_CYAN);
 +      color_set(opt->colors[GREP_COLOR_CONTEXT], "");
 +      color_set(opt->colors[GREP_COLOR_FILENAME], "");
 +      color_set(opt->colors[GREP_COLOR_FUNCTION], "");
 +      color_set(opt->colors[GREP_COLOR_LINENO], "");
 +      color_set(opt->colors[GREP_COLOR_COLUMNNO], "");
 +      color_set(opt->colors[GREP_COLOR_MATCH_CONTEXT], GIT_COLOR_BOLD_RED);
 +      color_set(opt->colors[GREP_COLOR_MATCH_SELECTED], GIT_COLOR_BOLD_RED);
 +      color_set(opt->colors[GREP_COLOR_SELECTED], "");
 +      color_set(opt->colors[GREP_COLOR_SEP], GIT_COLOR_CYAN);
+       opt->only_matching = 0;
        opt->color = -1;
        opt->output = std_output;
  }
@@@ -84,8 -71,6 +85,8 @@@ static int parse_pattern_type_arg(cons
        die("bad %s argument: %s", opt, arg);
  }
  
 +define_list_config_array_extra(color_grep_slots, {"match"});
 +
  /*
   * Read the configuration file once and store it in
   * the grep_defaults template.
@@@ -93,7 -78,7 +94,7 @@@
  int grep_config(const char *var, const char *value, void *cb)
  {
        struct grep_opt *opt = &grep_defaults;
 -      char *color = NULL;
 +      const char *slot;
  
        if (userdiff_config(var, value) < 0)
                return -1;
  
        if (!strcmp(var, "color.grep"))
                opt->color = git_config_colorbool(var, value);
 -      else if (!strcmp(var, "color.grep.context"))
 -              color = opt->color_context;
 -      else if (!strcmp(var, "color.grep.filename"))
 -              color = opt->color_filename;
 -      else if (!strcmp(var, "color.grep.function"))
 -              color = opt->color_function;
 -      else if (!strcmp(var, "color.grep.linenumber"))
 -              color = opt->color_lineno;
 -      else if (!strcmp(var, "color.grep.column"))
 -              color = opt->color_columnno;
 -      else if (!strcmp(var, "color.grep.matchcontext"))
 -              color = opt->color_match_context;
 -      else if (!strcmp(var, "color.grep.matchselected"))
 -              color = opt->color_match_selected;
 -      else if (!strcmp(var, "color.grep.selected"))
 -              color = opt->color_selected;
 -      else if (!strcmp(var, "color.grep.separator"))
 -              color = opt->color_sep;
 -      else if (!strcmp(var, "color.grep.match")) {
 -              int rc = 0;
 -              if (!value)
 -                      return config_error_nonbool(var);
 -              rc |= color_parse(value, opt->color_match_context);
 -              rc |= color_parse(value, opt->color_match_selected);
 -              return rc;
 -      }
 -
 -      if (color) {
 +      if (!strcmp(var, "color.grep.match")) {
 +              if (grep_config("color.grep.matchcontext", value, cb) < 0)
 +                      return -1;
 +              if (grep_config("color.grep.matchselected", value, cb) < 0)
 +                      return -1;
 +      } else if (skip_prefix(var, "color.grep.", &slot)) {
 +              int i = LOOKUP_CONFIG(color_grep_slots, slot);
 +              char *color;
 +
 +              if (i < 0)
 +                      return -1;
 +              color = opt->colors[i];
                if (!value)
                        return config_error_nonbool(var);
                return color_parse(value, color);
  void grep_init(struct grep_opt *opt, const char *prefix)
  {
        struct grep_opt *def = &grep_defaults;
 +      int i;
  
        memset(opt, 0, sizeof(*opt));
        opt->prefix = prefix;
        opt->pattern_tail = &opt->pattern_list;
        opt->header_tail = &opt->header_list;
  
+       opt->only_matching = def->only_matching;
        opt->color = def->color;
        opt->extended_regexp_option = def->extended_regexp_option;
        opt->pattern_type_option = def->pattern_type_option;
        opt->relative = def->relative;
        opt->output = def->output;
  
 -      color_set(opt->color_context, def->color_context);
 -      color_set(opt->color_filename, def->color_filename);
 -      color_set(opt->color_function, def->color_function);
 -      color_set(opt->color_lineno, def->color_lineno);
 -      color_set(opt->color_columnno, def->color_columnno);
 -      color_set(opt->color_match_context, def->color_match_context);
 -      color_set(opt->color_match_selected, def->color_match_selected);
 -      color_set(opt->color_selected, def->color_selected);
 -      color_set(opt->color_sep, def->color_sep);
 +      for (i = 0; i < NR_GREP_COLORS; i++)
 +              color_set(opt->colors[i], def->colors[i]);
  }
  
  static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, struct grep_opt *opt)
@@@ -1101,12 -1109,12 +1103,12 @@@ static void output_sep(struct grep_opt 
        if (opt->null_following_name)
                opt->output(opt, "\0", 1);
        else
 -              output_color(opt, &sign, 1, opt->color_sep);
 +              output_color(opt, &sign, 1, opt->colors[GREP_COLOR_SEP]);
  }
  
  static void show_name(struct grep_opt *opt, const char *name)
  {
 -      output_color(opt, name, strlen(name), opt->color_filename);
 +      output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]);
        opt->output(opt, opt->null_following_name ? "\0" : "\n", 1);
  }
  
@@@ -1404,40 -1412,23 +1406,23 @@@ static int next_match(struct grep_opt *
        return hit;
  }
  
- static void show_line(struct grep_opt *opt, char *bol, char *eol,
-                     const char *name, unsigned lno, ssize_t cno, char sign)
+ static void show_line_header(struct grep_opt *opt, const char *name,
+                            unsigned lno, ssize_t cno, char sign)
  {
-       int rest = eol - bol;
-       const char *match_color, *line_color = NULL;
-       if (opt->file_break && opt->last_shown == 0) {
-               if (opt->show_hunk_mark)
-                       opt->output(opt, "\n", 1);
-       } else if (opt->pre_context || opt->post_context || opt->funcbody) {
-               if (opt->last_shown == 0) {
-                       if (opt->show_hunk_mark) {
-                               output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]);
-                               opt->output(opt, "\n", 1);
-                       }
-               } else if (lno > opt->last_shown + 1) {
-                       output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]);
-                       opt->output(opt, "\n", 1);
-               }
-       }
        if (opt->heading && opt->last_shown == 0) {
 -              output_color(opt, name, strlen(name), opt->color_filename);
 +              output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]);
                opt->output(opt, "\n", 1);
        }
        opt->last_shown = lno;
  
        if (!opt->heading && opt->pathname) {
 -              output_color(opt, name, strlen(name), opt->color_filename);
 +              output_color(opt, name, strlen(name), opt->colors[GREP_COLOR_FILENAME]);
                output_sep(opt, sign);
        }
        if (opt->linenum) {
                char buf[32];
                xsnprintf(buf, sizeof(buf), "%d", lno);
 -              output_color(opt, buf, strlen(buf), opt->color_lineno);
 +              output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_LINENO]);
                output_sep(opt, sign);
        }
        /*
        if (opt->columnnum && cno) {
                char buf[32];
                xsnprintf(buf, sizeof(buf), "%"PRIuMAX, (uintmax_t)cno);
 -              output_color(opt, buf, strlen(buf), opt->color_columnno);
 +              output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_COLUMNNO]);
                output_sep(opt, sign);
        }
-       if (opt->color) {
+ }
+ static void show_line(struct grep_opt *opt, char *bol, char *eol,
+                     const char *name, unsigned lno, ssize_t cno, char sign)
+ {
+       int rest = eol - bol;
+       const char *match_color = NULL;
+       const char *line_color = NULL;
+       if (opt->file_break && opt->last_shown == 0) {
+               if (opt->show_hunk_mark)
+                       opt->output(opt, "\n", 1);
+       } else if (opt->pre_context || opt->post_context || opt->funcbody) {
+               if (opt->last_shown == 0) {
+                       if (opt->show_hunk_mark) {
 -                              output_color(opt, "--", 2, opt->color_sep);
++                              output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]);
+                               opt->output(opt, "\n", 1);
+                       }
+               } else if (lno > opt->last_shown + 1) {
 -                      output_color(opt, "--", 2, opt->color_sep);
++                      output_color(opt, "--", 2, opt->colors[GREP_COLOR_SEP]);
+                       opt->output(opt, "\n", 1);
+               }
+       }
+       if (!opt->only_matching) {
+               /*
+                * In case the line we're being called with contains more than
+                * one match, leave printing each header to the loop below.
+                */
+               show_line_header(opt, name, lno, cno, sign);
+       }
+       if (opt->color || opt->only_matching) {
                regmatch_t match;
                enum grep_context ctx = GREP_CONTEXT_BODY;
                int ch = *eol;
                int eflags = 0;
  
-               if (sign == ':')
-                       match_color = opt->colors[GREP_COLOR_MATCH_SELECTED];
-               else
-                       match_color = opt->colors[GREP_COLOR_MATCH_CONTEXT];
-               if (sign == ':')
-                       line_color = opt->colors[GREP_COLOR_SELECTED];
-               else if (sign == '-')
-                       line_color = opt->colors[GREP_COLOR_CONTEXT];
-               else if (sign == '=')
-                       line_color = opt->colors[GREP_COLOR_FUNCTION];
+               if (opt->color) {
+                       if (sign == ':')
 -                              match_color = opt->color_match_selected;
++                              match_color = opt->colors[GREP_COLOR_MATCH_SELECTED];
+                       else
 -                              match_color = opt->color_match_context;
++                              match_color = opt->colors[GREP_COLOR_MATCH_CONTEXT];
+                       if (sign == ':')
 -                              line_color = opt->color_selected;
++                              line_color = opt->colors[GREP_COLOR_SELECTED];
+                       else if (sign == '-')
 -                              line_color = opt->color_context;
++                              line_color = opt->colors[GREP_COLOR_CONTEXT];
+                       else if (sign == '=')
 -                              line_color = opt->color_function;
++                              line_color = opt->colors[GREP_COLOR_FUNCTION];
+               }
                *eol = '\0';
                while (next_match(opt, bol, eol, ctx, &match, eflags)) {
                        if (match.rm_so == match.rm_eo)
                                break;
  
-                       output_color(opt, bol, match.rm_so, line_color);
+                       if (opt->only_matching)
+                               show_line_header(opt, name, lno, cno, sign);
+                       else
+                               output_color(opt, bol, match.rm_so, line_color);
                        output_color(opt, bol + match.rm_so,
                                     match.rm_eo - match.rm_so, match_color);
+                       if (opt->only_matching)
+                               opt->output(opt, "\n", 1);
                        bol += match.rm_eo;
+                       cno += match.rm_eo;
                        rest -= match.rm_eo;
                        eflags = REG_NOTBOL;
                }
                *eol = ch;
        }
-       output_color(opt, bol, rest, line_color);
-       opt->output(opt, "\n", 1);
+       if (!opt->only_matching) {
+               output_color(opt, bol, rest, line_color);
+               opt->output(opt, "\n", 1);
+       }
  }
  
  #ifndef NO_PTHREADS
@@@ -1875,7 -1906,7 +1900,7 @@@ static int grep_source_1(struct grep_op
                        if (binary_match_only) {
                                opt->output(opt, "Binary file ", 12);
                                output_color(opt, gs->name, strlen(gs->name),
 -                                           opt->color_filename);
 +                                           opt->colors[GREP_COLOR_FILENAME]);
                                opt->output(opt, " matches\n", 9);
                                return 1;
                        }
                char buf[32];
                if (opt->pathname) {
                        output_color(opt, gs->name, strlen(gs->name),
 -                                   opt->color_filename);
 +                                   opt->colors[GREP_COLOR_FILENAME]);
                        output_sep(opt, ':');
                }
                xsnprintf(buf, sizeof(buf), "%u\n", count);
diff --combined grep.h
index 01d2cba6f80921538d0269aefcad6f11218392b0,4d474d8ec439fa0256c548055b17cf1ce64505da..0ba62a11c5cc2767b15b52aca20a0ea5b00870b8
--- 1/grep.h
--- 2/grep.h
+++ b/grep.h
@@@ -62,19 -62,6 +62,19 @@@ enum grep_header_field 
        GREP_HEADER_FIELD_MAX
  };
  
 +enum grep_color {
 +      GREP_COLOR_CONTEXT,
 +      GREP_COLOR_FILENAME,
 +      GREP_COLOR_FUNCTION,
 +      GREP_COLOR_LINENO,
 +      GREP_COLOR_COLUMNNO,
 +      GREP_COLOR_MATCH_CONTEXT,
 +      GREP_COLOR_MATCH_SELECTED,
 +      GREP_COLOR_SELECTED,
 +      GREP_COLOR_SEP,
 +      NR_GREP_COLORS
 +};
 +
  struct grep_pat {
        struct grep_pat *next;
        const char *origin;
@@@ -163,13 -150,22 +163,14 @@@ struct grep_opt 
        int relative;
        int pathname;
        int null_following_name;
+       int only_matching;
        int color;
        int max_depth;
        int funcname;
        int funcbody;
        int extended_regexp_option;
        int pattern_type_option;
 -      char color_context[COLOR_MAXLEN];
 -      char color_filename[COLOR_MAXLEN];
 -      char color_function[COLOR_MAXLEN];
 -      char color_lineno[COLOR_MAXLEN];
 -      char color_columnno[COLOR_MAXLEN];
 -      char color_match_context[COLOR_MAXLEN];
 -      char color_match_selected[COLOR_MAXLEN];
 -      char color_selected[COLOR_MAXLEN];
 -      char color_sep[COLOR_MAXLEN];
 +      char colors[NR_GREP_COLORS][COLOR_MAXLEN];
        unsigned pre_context;
        unsigned post_context;
        unsigned last_shown;
diff --combined t/t7810-grep.sh
index 90bba4fcefd5f32d9200188cef77913b5d5fd40f,d8c232dbf4d894a199b2db9df493fd4a810dc58c..dcaab1557b4082adbd2507d1ca4beba629723022
                fi
        '
  
+       test_expect_success "grep $L (with --column, --only-matching)" '
+               {
+                       echo ${HC}file:1:5:mmap
+                       echo ${HC}file:2:5:mmap
+                       echo ${HC}file:3:5:mmap
+                       echo ${HC}file:3:13:mmap
+                       echo ${HC}file:4:5:mmap
+                       echo ${HC}file:4:13:mmap
+                       echo ${HC}file:5:5:mmap
+                       echo ${HC}file:5:13:mmap
+               } >expected &&
+               git grep --column -n -o -e mmap $H >actual &&
+               test_cmp expected actual
+       '
        test_expect_success "grep $L (t-1)" '
                echo "${HC}t/t:1:test" >expected &&
                git grep -n -e test $H >actual &&
@@@ -940,9 -955,10 +955,9 @@@ test_expect_success 'grep from a subdir
  test_expect_success 'grep from a subdirectory to search wider area (2)' '
        mkdir -p s &&
        (
 -              cd s || exit 1
 -              ( git grep xxyyzz .. >out ; echo $? >status )
 -              ! test -s out &&
 -              test 1 = $(cat status)
 +              cd s &&
 +              test_expect_code 1 git grep xxyyzz .. >out &&
 +              ! test -s out
        )
  '