{"thread":{"id":"27550","subject":"[PATCH 0/3] grep: add result grouping, ack style","startedAt":"2011-06-05T15:21:43Z","lastAt":"2011-06-06T12:26:17Z","messageCount":5,"participants":["René Scharfe","Michał Kiedrowicz"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"169319","messageId":"4DEB9F07.1070006@lsrfire.ath.cx","threadId":"27550","inReplyTo":null,"subject":"[PATCH 0/3] grep: add result grouping, ack style","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2011-06-05T15:21:43Z","receivedAt":"2011-06-05T15:21:43Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"ack (http://betterthangrep.com/) has a nice way to display results from\nmultiple files by writing the file name once at the top instead of at\nthe start of each matching line.  This short series starts with a fix\nthat is needed anyway and then adds the basic options that enable this\ndisplay mode.\n\n[PATCH 1/3] grep: fix coloring of hunk marks between files\n[PATCH 2/3] grep: add --break\n[PATCH 3/3] grep: add --heading\n\n Documentation/git-grep.txt |    7 +++\n builtin/grep.c             |   29 ++++++++++----\n grep.c                     |   26 ++++++++++--\n grep.h                     |    2 +\n t/t7810-grep.sh            |   95 ++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 146 insertions(+), 13 deletions(-)\n"},{"id":"169320","messageId":"4DEB9F9F.7040704@lsrfire.ath.cx","threadId":"27550","inReplyTo":"4DEB9F07.1070006@lsrfire.ath.cx","subject":"[PATCH 1/3] grep: fix coloring of hunk marks between files","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2011-06-05T15:24:15Z","receivedAt":"2011-06-05T15:24:15Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Commit 431d6e7b (grep: enable threading for context line printing)\nsplit the printing of the \"--\\n\" mark between results from different\nfiles out into two places: show_line() in grep.c for the non-threaded\ncase and work_done() in builtin/grep.c for the threaded case.  Commit\n55f638bd (grep: Colorize filename, line number, and separator) updated\nthe former, but not the latter, so the separators between files are\nnot colored if threads are used.\n\nThis patch merges the two.  In the threaded case, hunk marks are now\nprinted by show_line() for every file, including the first one, and the\nvery first mark is simply skipped in work_done().  This ensures that the\noutput is properly colored and works just as well.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n builtin/grep.c  |   23 ++++++++++++++++-------\n grep.c          |   15 ++++++++++++---\n t/t7810-grep.sh |   30 ++++++++++++++++++++++++++++++\n 3 files changed, 58 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 871afaa..0d5a90b 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -93,8 +93,7 @@ static pthread_cond_t cond_write;\n /* Signalled when we are finished with everything. */\n static pthread_cond_t cond_result;\n \n-static int print_hunk_marks_between_files;\n-static int printed_something;\n+static int skip_first_line;\n \n static void add_work(enum work_type type, char *name, void *id)\n {\n@@ -160,10 +159,20 @@ static void work_done(struct work_item *w)\n \t    todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {\n \t\tw = &todo[todo_done];\n \t\tif (w->out.len) {\n-\t\t\tif (print_hunk_marks_between_files && printed_something)\n-\t\t\t\twrite_or_die(1, \"--\\n\", 3);\n-\t\t\twrite_or_die(1, w->out.buf, w->out.len);\n-\t\t\tprinted_something = 1;\n+\t\t\tconst char *p = w->out.buf;\n+\t\t\tsize_t len = w->out.len;\n+\n+\t\t\t/* Skip the leading hunk mark of the first file. */\n+\t\t\tif (skip_first_line) {\n+\t\t\t\twhile (len) {\n+\t\t\t\t\tlen--;\n+\t\t\t\t\tif (*p++ == '\\n')\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tskip_first_line = 0;\n+\t\t\t}\n+\n+\t\t\twrite_or_die(1, p, len);\n \t\t}\n \t\tfree(w->name);\n \t\tfree(w->identifier);\n@@ -968,7 +977,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \n \tif (use_threads) {\n \t\tif (opt.pre_context || opt.post_context)\n-\t\t\tprint_hunk_marks_between_files = 1;\n+\t\t\tskip_first_line = 1;\n \t\tstart_threads(&opt);\n \t}\n #else\ndiff --git a/grep.c b/grep.c\nindex d03d9e2..3f15085 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -941,9 +941,18 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \tif (!opt->output)\n \t\topt->output = std_output;\n \n-\tif (opt->last_shown && (opt->pre_context || opt->post_context) &&\n-\t    opt->output == std_output)\n-\t\topt->show_hunk_mark = 1;\n+\tif (opt->pre_context || opt->post_context) {\n+\t\t/* Show hunk marks, except for the first file. */\n+\t\tif (opt->last_shown)\n+\t\t\topt->show_hunk_mark = 1;\n+\t\t/*\n+\t\t * If we're using threads then we can't easily identify\n+\t\t * the first file.  Always put hunk marks in that case\n+\t\t * and skip the very first one later in work_done().\n+\t\t */\n+\t\tif (opt->output != std_output)\n+\t\t\topt->show_hunk_mark = 1;\n+\t}\n \topt->last_shown = 0;\n \n \tswitch (opt->binary) {\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 69bd576..539a8fe 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -716,4 +716,34 @@ test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '\n \ttest_cmp expected actual\n '\n \n+test_config() {\n+\tgit config \"$1\" \"$2\" &&\n+\ttest_when_finished \"git config --unset $1\"\n+}\n+\n+cat >expected <<EOF\n+hello.c<RED>:<RESET>int main(int argc, const char **argv)\n+hello.c<RED>-<RESET>{\n+<RED>--<RESET>\n+hello.c<RED>:<RESET>\t/* char ?? */\n+hello.c<RED>-<RESET>}\n+<RED>--<RESET>\n+hello_world<RED>:<RESET>Hello_world\n+hello_world<RED>-<RESET>HeLLo_world\n+EOF\n+\n+test_expect_success 'grep --color, separator' '\n+\ttest_config color.grep.context\t\tnormal &&\n+\ttest_config color.grep.filename\t\tnormal &&\n+\ttest_config color.grep.function\t\tnormal &&\n+\ttest_config color.grep.linenumber\tnormal &&\n+\ttest_config color.grep.match\t\tnormal &&\n+\ttest_config color.grep.selected\t\tnormal &&\n+\ttest_config color.grep.separator\tred &&\n+\n+\tgit grep --color=always -A1 -e char -e lo_w hello.c hello_world |\n+\ttest_decode_color >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.6.rc0.3.ga805ac\n"},{"id":"169321","messageId":"4DEB9FA9.1050602@lsrfire.ath.cx","threadId":"27550","inReplyTo":"4DEB9F07.1070006@lsrfire.ath.cx","subject":"[PATCH 2/3] grep: add --break","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2011-06-05T15:24:25Z","receivedAt":"2011-06-05T15:24:25Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"With --break, an empty line is printed between matches from different\nfiles, increasing readability.  This option is taken from ack\n(http://betterthangrep.com/).\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/git-grep.txt |    3 +++\n builtin/grep.c             |    4 +++-\n grep.c                     |    7 +++++--\n grep.h                     |    1 +\n t/t7810-grep.sh            |   28 ++++++++++++++++++++++++++++\n 5 files changed, 40 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex e150c77..dea7cad 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -148,6 +148,9 @@ OPTIONS\n \tgives the default to color output.\n \tSame as `--color=never`.\n \n+--break::\n+\tPrint an empty line between matches from different files.\n+\n -[ABC] <context>::\n \tShow `context` trailing (`A` -- after), or leading (`B`\n \t-- before), or both (`C` -- context) lines, and place a\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 0d5a90b..42bb87f 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -822,6 +822,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN('c', \"count\", &opt.count,\n \t\t\t\"show the number of matches instead of matching lines\"),\n \t\tOPT__COLOR(&opt.color, \"highlight matches\"),\n+\t\tOPT_BOOLEAN(0, \"break\", &opt.file_break,\n+\t\t\t\"print empty line between matches from different files\"),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_CALLBACK('C', NULL, &opt, \"n\",\n \t\t\t\"show <n> context lines before and after matches\",\n@@ -976,7 +978,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\tuse_threads = 0;\n \n \tif (use_threads) {\n-\t\tif (opt.pre_context || opt.post_context)\n+\t\tif (opt.pre_context || opt.post_context || opt.file_break)\n \t\t\tskip_first_line = 1;\n \t\tstart_threads(&opt);\n \t}\ndiff --git a/grep.c b/grep.c\nindex 3f15085..b0b860a 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -721,7 +721,10 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \tint rest = eol - bol;\n \tchar *line_color = NULL;\n \n-\tif (opt->pre_context || opt->post_context) {\n+\tif (opt->file_break && opt->last_shown == 0) {\n+\t\tif (opt->show_hunk_mark)\n+\t\t\topt->output(opt, \"\\n\", 1);\n+\t} else if (opt->pre_context || opt->post_context) {\n \t\tif (opt->last_shown == 0) {\n \t\t\tif (opt->show_hunk_mark) {\n \t\t\t\toutput_color(opt, \"--\", 2, opt->color_sep);\n@@ -941,7 +944,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \tif (!opt->output)\n \t\topt->output = std_output;\n \n-\tif (opt->pre_context || opt->post_context) {\n+\tif (opt->pre_context || opt->post_context || opt->file_break) {\n \t\t/* Show hunk marks, except for the first file. */\n \t\tif (opt->last_shown)\n \t\t\topt->show_hunk_mark = 1;\ndiff --git a/grep.h b/grep.h\nindex cd055cd..638bee8 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -110,6 +110,7 @@ struct grep_opt {\n \tunsigned post_context;\n \tunsigned last_shown;\n \tint show_hunk_mark;\n+\tint file_break;\n \tvoid *priv;\n \n \tvoid (*output)(struct grep_opt *opt, const void *data, size_t size);\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex 539a8fe..f55793e 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -746,4 +746,32 @@ test_expect_success 'grep --color, separator' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+hello.c:int main(int argc, const char **argv)\n+hello.c:\t/* char ?? */\n+\n+hello_world:Hello_world\n+EOF\n+\n+test_expect_success 'grep --break' '\n+\tgit grep --break -e char -e lo_w hello.c hello_world >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+hello.c:int main(int argc, const char **argv)\n+hello.c-{\n+--\n+hello.c:\t/* char ?? */\n+hello.c-}\n+\n+hello_world:Hello_world\n+hello_world-HeLLo_world\n+EOF\n+\n+test_expect_success 'grep --break with context' '\n+\tgit grep --break -A1 -e char -e lo_w hello.c hello_world >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.6.rc0.3.ga805ac\n"},{"id":"169322","messageId":"4DEB9FB4.8020904@lsrfire.ath.cx","threadId":"27550","inReplyTo":"4DEB9F07.1070006@lsrfire.ath.cx","subject":"[PATCH 3/3] grep: add --heading","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2011-06-05T15:24:36Z","receivedAt":"2011-06-05T15:24:36Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"With --heading, the filename is printed once before matches from that\nfile instead of at the start of each line, giving more screen space to\nthe actual search results.\n\nThis option is taken from ack (http://betterthangrep.com/).  And now\ngit grep can dress up like it:\n\n\t$ git config alias.ack \"grep --break --heading --line-number\"\n\n\t$ git ack -e --heading\n\tDocumentation/git-grep.txt\n\t154:--heading::\n\t\n\tt/t7810-grep.sh\n\t785:test_expect_success 'grep --heading' '\n\t786:    git grep --heading -e char -e lo_w hello.c hello_world >actual &&\n\t808:    git grep --break --heading -n --color \\\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/git-grep.txt |    4 ++++\n builtin/grep.c             |    2 ++\n grep.c                     |    6 +++++-\n grep.h                     |    1 +\n t/t7810-grep.sh            |   37 +++++++++++++++++++++++++++++++++++++\n 5 files changed, 49 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex dea7cad..07b3c6a 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -151,6 +151,10 @@ OPTIONS\n --break::\n \tPrint an empty line between matches from different files.\n \n+--heading::\n+\tShow the filename above the matches in that file instead of\n+\tat the start of each shown line.\n+\n -[ABC] <context>::\n \tShow `context` trailing (`A` -- after), or leading (`B`\n \t-- before), or both (`C` -- context) lines, and place a\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 42bb87f..cccf8da 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -824,6 +824,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\tOPT__COLOR(&opt.color, \"highlight matches\"),\n \t\tOPT_BOOLEAN(0, \"break\", &opt.file_break,\n \t\t\t\"print empty line between matches from different files\"),\n+\t\tOPT_BOOLEAN(0, \"heading\", &opt.heading,\n+\t\t\t\"show filename only once above matches from same file\"),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_CALLBACK('C', NULL, &opt, \"n\",\n \t\t\t\"show <n> context lines before and after matches\",\ndiff --git a/grep.c b/grep.c\nindex b0b860a..04e9ba4 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -735,9 +735,13 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \t\t\topt->output(opt, \"\\n\", 1);\n \t\t}\n \t}\n+\tif (opt->heading && opt->last_shown == 0) {\n+\t\toutput_color(opt, name, strlen(name), opt->color_filename);\n+\t\topt->output(opt, \"\\n\", 1);\n+\t}\n \topt->last_shown = lno;\n \n-\tif (opt->pathname) {\n+\tif (!opt->heading && opt->pathname) {\n \t\toutput_color(opt, name, strlen(name), opt->color_filename);\n \t\toutput_sep(opt, sign);\n \t}\ndiff --git a/grep.h b/grep.h\nindex 638bee8..c568297 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -111,6 +111,7 @@ struct grep_opt {\n \tunsigned last_shown;\n \tint show_hunk_mark;\n \tint file_break;\n+\tint heading;\n \tvoid *priv;\n \n \tvoid (*output)(struct grep_opt *opt, const void *data, size_t size);\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex f55793e..1227fa6 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -774,4 +774,41 @@ test_expect_success 'grep --break with context' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+hello.c\n+int main(int argc, const char **argv)\n+\t/* char ?? */\n+hello_world\n+Hello_world\n+EOF\n+\n+test_expect_success 'grep --heading' '\n+\tgit grep --heading -e char -e lo_w hello.c hello_world >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+<BOLD;GREEN>hello.c<RESET>\n+2:int main(int argc, const <BLACK;BYELLOW>char<RESET> **argv)\n+6:\t/* <BLACK;BYELLOW>char<RESET> ?? */\n+\n+<BOLD;GREEN>hello_world<RESET>\n+3:Hel<BLACK;BYELLOW>lo_w<RESET>orld\n+EOF\n+\n+test_expect_success 'mimic ack-grep --group' '\n+\ttest_config color.grep.context\t\tnormal &&\n+\ttest_config color.grep.filename\t\t\"bold green\" &&\n+\ttest_config color.grep.function\t\tnormal &&\n+\ttest_config color.grep.linenumber\tnormal &&\n+\ttest_config color.grep.match\t\t\"black yellow\" &&\n+\ttest_config color.grep.selected\t\tnormal &&\n+\ttest_config color.grep.separator\tnormal &&\n+\n+\tgit grep --break --heading -n --color \\\n+\t\t-e char -e lo_w hello.c hello_world |\n+\ttest_decode_color >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.7.6.rc0.3.ga805ac\n"},{"id":"169361","messageId":"loom.20110606T141954-541@post.gmane.org","threadId":"27550","inReplyTo":"4DEB9FB4.8020904@lsrfire.ath.cx","subject":"Re: [PATCH 3/3] grep: add --heading","fromName":"Michał Kiedrowicz","fromEmail":"michal.kiedrowicz@gmail.com","sentAt":"2011-06-06T12:26:17Z","receivedAt":"2011-06-06T12:26:17Z","isPatch":true,"sender":{"key":"michal.kiedrowicz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/14072847?v=4"},"body":"René Scharfe <rene.scharfe <at> lsrfire.ath.cx> writes:\n> \n> This option is taken from ack (http://betterthangrep.com/).  And now\n> git grep can dress up like it:\n> \n> \t$ git config alias.ack \"grep --break --heading --line-number\"\n> \n> \t$ git ack -e --heading\n> \tDocumentation/git-grep.txt\n> \t154:--heading::\n> \t\n> \tt/t7810-grep.sh\n> \t785:test_expect_success 'grep --heading' '\n> \t786:    git grep --heading -e char -e lo_w hello.c hello_world >actual &&\n> \t808:    git grep --break --heading -n --color \\\n> \n\nNice! I really missed that.\n\nYou can also add --perl-regexp (since 1.7.6-rc0) to alias.ack to achive more\ncompability with ack (although it uses Perl regexes, not PCRE).\n"}]}