{"thread":{"id":"19996","subject":"[PATCH 0/6] grep: add option -p/--show-function, similar to diff's","startedAt":"2009-07-01T22:00:05Z","lastAt":"2009-07-02T15:42:43Z","messageCount":15,"participants":["René Scharfe","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"117321","messageId":"4A4BDC65.80504@lsrfire.ath.cx","threadId":"19996","inReplyTo":null,"subject":"[PATCH 0/6] grep: add option -p/--show-function, similar to diff's","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:00:05Z","receivedAt":"2009-07-01T22:00:05Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"This patch series retrofits git grep with functionality similar to the diff\noption -p, which shows the function definitions for diff hunks.  git grep is\nenhanced to show them as context lines before matches, if called with -p.  It\ncan be combined with the existing context options (-A, -B, -C).\n\nThe first patch is a cleanup.  The userdiff API is enhanced to allow freeing\nof allocated memory, before it is used outside of git diff code, later in the\nseries:\n\n  [PATCH 1/6] userdiff: add xdiff_clear_find_func()\n\nThe next three patches massage the context handling code to make it easier to\nextend:\n\n  [PATCH 2/6] grep: move context hunk mark handling into show_line()\n  [PATCH 3/6] grep: print context hunk marks between files\n  [PATCH 4/6] grep: handle pre context lines on demand\n\nFinally, handling for the option -p is added and then enhanced to support\nuserdiff configuration settings:\n\n  [PATCH 5/6] grep: add option -p/--show-function\n  [PATCH 6/6] grep -p: support user defined regular expressions\n\n\n Documentation/git-grep.txt |    8 ++\n builtin-grep.c             |   22 ++++++-\n diff.c                     |    1 +\n grep.c                     |  154 ++++++++++++++++++++++++++++++++------------\n grep.h                     |    4 +\n t/t7002-grep.sh            |   71 ++++++++++++++++++++-\n xdiff-interface.c          |   15 ++++\n xdiff-interface.h          |    1 +\n 8 files changed, 232 insertions(+), 44 deletions(-)\n"},{"id":"117322","messageId":"4A4BDCC7.1090405@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4BDC65.80504@lsrfire.ath.cx","subject":"[PATCH 1/6] userdiff: add xdiff_clear_find_func()","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:01:43Z","receivedAt":"2009-07-01T22:01:43Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"xdiff_set_find_func() is used to set user defined regular expressions\nfor finding function signatures.  Add xdiff_clear_find_func(), which\nfrees the memory allocated by the former, making the API complete.\n\nAlso, use the new function in diff.c (the only call site of\nxdiff_set_find_func()) to clean up after ourselves.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n diff.c            |    1 +\n xdiff-interface.c |   15 +++++++++++++++\n xdiff-interface.h |    1 +\n 3 files changed, 17 insertions(+), 0 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 43835d7..892921c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1603,6 +1603,7 @@ static void builtin_diff(const char *name_a,\n \t\t\tfree(mf1.ptr);\n \t\tif (textconv_two)\n \t\t\tfree(mf2.ptr);\n+\t\txdiff_clear_find_func(&xecfg);\n \t}\n \n  free_ab_and_return:\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex b9b0db8..01f14fb 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -309,6 +309,21 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)\n \t}\n }\n \n+void xdiff_clear_find_func(xdemitconf_t *xecfg)\n+{\n+\tif (xecfg->find_func) {\n+\t\tint i;\n+\t\tstruct ff_regs *regs = xecfg->find_func_priv;\n+\n+\t\tfor (i = 0; i < regs->nr; i++)\n+\t\t\tregfree(&regs->array[i].re);\n+\t\tfree(regs->array);\n+\t\tfree(regs);\n+\t\txecfg->find_func = NULL;\n+\t\txecfg->find_func_priv = NULL;\n+\t}\n+}\n+\n int git_xmerge_style = -1;\n \n int git_xmerge_config(const char *var, const char *value, void *cb)\ndiff --git a/xdiff-interface.h b/xdiff-interface.h\nindex 7352b9a..55572c3 100644\n--- a/xdiff-interface.h\n+++ b/xdiff-interface.h\n@@ -21,6 +21,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename);\n int buffer_is_binary(const char *ptr, unsigned long size);\n \n extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);\n+extern void xdiff_clear_find_func(xdemitconf_t *xecfg);\n extern int git_xmerge_config(const char *var, const char *value, void *cb);\n extern int git_xmerge_style;\n \n-- \n1.6.3.3\n"},{"id":"117323","messageId":"4A4BDCFE.3050808@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4BDC65.80504@lsrfire.ath.cx","subject":"[PATCH 2/6] grep: move context hunk mark handling into show_line()","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:02:38Z","receivedAt":"2009-07-01T22:02:38Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Move last_shown into struct grep_opt, to make it available in\nshow_line(), and then make the function handle the printing of hunk\nmarks for context lines in a central place.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n grep.c |   26 +++++++++++---------------\n grep.h |    1 +\n 2 files changed, 12 insertions(+), 15 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex 92a47c7..6ee80f7 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -490,6 +490,12 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n {\n \tint rest = eol - bol;\n \n+\tif (opt->pre_context || opt->post_context) {\n+\t\tif (opt->last_shown && lno > opt->last_shown + 1)\n+\t\t\tfputs(\"--\\n\", stdout);\n+\t}\n+\topt->last_shown = lno;\n+\n \tif (opt->null_following_name)\n \t\tsign = '\\0';\n \tif (opt->pathname)\n@@ -531,12 +537,12 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\tchar *eol;\n \t} *prev = NULL, *pcl;\n \tunsigned last_hit = 0;\n-\tunsigned last_shown = 0;\n \tint binary_match_only = 0;\n-\tconst char *hunk_mark = \"\";\n \tunsigned count = 0;\n \tenum grep_context ctx = GREP_CONTEXT_HEAD;\n \n+\topt->last_shown = 0;\n+\n \tif (buffer_is_binary(buf, size)) {\n \t\tswitch (opt->binary) {\n \t\tcase GREP_BINARY_DEFAULT:\n@@ -552,8 +558,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \n \tif (opt->pre_context)\n \t\tprev = xcalloc(opt->pre_context, sizeof(*prev));\n-\tif (opt->pre_context || opt->post_context)\n-\t\thunk_mark = \"--\\n\";\n \n \twhile (left) {\n \t\tchar *eol, ch;\n@@ -607,33 +611,25 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t\t\tfrom = lno - opt->pre_context;\n \t\t\t\telse\n \t\t\t\t\tfrom = 1;\n-\t\t\t\tif (from <= last_shown)\n-\t\t\t\t\tfrom = last_shown + 1;\n-\t\t\t\tif (last_shown && from != last_shown + 1)\n-\t\t\t\t\tfputs(hunk_mark, stdout);\n+\t\t\t\tif (from <= opt->last_shown)\n+\t\t\t\t\tfrom = opt->last_shown + 1;\n \t\t\t\twhile (from < lno) {\n \t\t\t\t\tpcl = &prev[lno-from-1];\n \t\t\t\t\tshow_line(opt, pcl->bol, pcl->eol,\n \t\t\t\t\t\t  name, from, '-');\n \t\t\t\t\tfrom++;\n \t\t\t\t}\n-\t\t\t\tlast_shown = lno-1;\n \t\t\t}\n-\t\t\tif (last_shown && lno != last_shown + 1)\n-\t\t\t\tfputs(hunk_mark, stdout);\n \t\t\tif (!opt->count)\n \t\t\t\tshow_line(opt, bol, eol, name, lno, ':');\n-\t\t\tlast_shown = last_hit = lno;\n+\t\t\tlast_hit = lno;\n \t\t}\n \t\telse if (last_hit &&\n \t\t\t lno <= last_hit + opt->post_context) {\n \t\t\t/* If the last hit is within the post context,\n \t\t\t * we need to show this line.\n \t\t\t */\n-\t\t\tif (last_shown && lno != last_shown + 1)\n-\t\t\t\tfputs(hunk_mark, stdout);\n \t\t\tshow_line(opt, bol, eol, name, lno, '-');\n-\t\t\tlast_shown = lno;\n \t\t}\n \t\tif (opt->pre_context) {\n \t\t\tmemmove(prev+1, prev,\ndiff --git a/grep.h b/grep.h\nindex 464e272..0883214 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -84,6 +84,7 @@ struct grep_opt {\n \tint regflags;\n \tunsigned pre_context;\n \tunsigned post_context;\n+\tunsigned last_shown;\n };\n \n extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);\n-- \n1.6.3.3\n"},{"id":"117324","messageId":"4A4BDD40.5040302@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4BDC65.80504@lsrfire.ath.cx","subject":"[PATCH 3/6] grep: print context hunk marks between files","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:03:44Z","receivedAt":"2009-07-01T22:03:44Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Print a hunk mark before matches from a new file are shown, in addition\nto the current behaviour of printing them if lines have been skipped.\n\nThe result is easier to read, as (presumably unrelated) matches from\ndifferent files are separated by a hunk mark.  GNU grep does the same.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n builtin-grep.c  |   11 +++++++++++\n grep.c          |    7 ++++++-\n grep.h          |    1 +\n t/t7002-grep.sh |   22 ++++++++++++++++++++++\n 4 files changed, 40 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 73fc922..48998af 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -278,6 +278,17 @@ static int flush_grep(struct grep_opt *opt,\n \t\targc -= 2;\n \t}\n \n+\tif (opt->pre_context || opt->post_context) {\n+\t\t/*\n+\t\t * grep handles hunk marks between files, but we need to\n+\t\t * do that ourselves between multiple calls.\n+\t\t */\n+\t\tif (opt->show_hunk_mark)\n+\t\t\twrite_or_die(1, \"--\\n\", 3);\n+\t\telse\n+\t\t\topt->show_hunk_mark = 1;\n+\t}\n+\n \tstatus = exec_grep(argc, argv);\n \n \tif (kept_0) {\ndiff --git a/grep.c b/grep.c\nindex 6ee80f7..4bca759 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -491,7 +491,12 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \tint rest = eol - bol;\n \n \tif (opt->pre_context || opt->post_context) {\n-\t\tif (opt->last_shown && lno > opt->last_shown + 1)\n+\t\tif (opt->last_shown == 0) {\n+\t\t\tif (opt->show_hunk_mark)\n+\t\t\t\tfputs(\"--\\n\", stdout);\n+\t\t\telse\n+\t\t\t\topt->show_hunk_mark = 1;\n+\t\t} else if (lno > opt->last_shown + 1)\n \t\t\tfputs(\"--\\n\", stdout);\n \t}\n \topt->last_shown = lno;\ndiff --git a/grep.h b/grep.h\nindex 0883214..730ffd6 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -85,6 +85,7 @@ struct grep_opt {\n \tunsigned pre_context;\n \tunsigned post_context;\n \tunsigned last_shown;\n+\tint show_hunk_mark;\n };\n \n extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);\ndiff --git a/t/t7002-grep.sh b/t/t7002-grep.sh\nindex 7868af8..155bfdb 100755\n--- a/t/t7002-grep.sh\n+++ b/t/t7002-grep.sh\n@@ -155,6 +155,28 @@ test_expect_success 'grep -e A --and --not -e B' '\n \ttest_cmp expected actual\n '\n \n+cat >expected <<EOF\n+y:y yy\n+--\n+z:zzz\n+EOF\n+\n+# Create 1024 file names that sort between \"y\" and \"z\" to make sure\n+# the two files are handled by different calls to an external grep.\n+# This depends on MAXARGS in builtin-grep.c being 1024 or less.\n+c32=\"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\"\n+test_expect_success 'grep -C1, hunk mark between files' '\n+\tfor a in $c32; do for b in $c32; do : >y-$a$b; done; done &&\n+\tgit add y-?? &&\n+\tgit grep -C1 \"^[yz]\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '\n+\tgit grep -C1 --no-ext-grep \"^[yz]\" >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_expect_success 'log grep setup' '\n \techo a >>file &&\n \ttest_tick &&\n-- \n1.6.3.3\n"},{"id":"117325","messageId":"4A4BDD9D.8090407@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4BDC65.80504@lsrfire.ath.cx","subject":"[PATCH 4/6] grep: handle pre context lines on demand","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:05:17Z","receivedAt":"2009-07-01T22:05:17Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Factor out pre context line handling into the new function\nshow_pre_context() and change the algorithm to rewind by looking for\nnewline characters and roll forward again, instead of maintaining an\narray of line beginnings and ends.\n\nThis is slower for hits, but the cost for non-matching lines becomes\nzero.  Normally, there are far more non-matching lines, so the time\nspent in total decreases.\n\nBefore this patch (current Linux kernel repo, best of five runs):\n\n\t$ time git grep --no-ext-grep -B1 memset >/dev/null\n\n\treal\t0m2.134s\n\tuser\t0m1.932s\n\tsys\t0m0.196s\n\n\t$ time git grep --no-ext-grep -B1000 memset >/dev/null\n\n\treal\t0m12.059s\n\tuser\t0m11.837s\n\tsys\t0m0.224s\n\nThe same with this patch:\n\n\t$ time git grep --no-ext-grep -B1 memset >/dev/null\n\n\treal\t0m2.117s\n\tuser\t0m1.892s\n\tsys\t0m0.228s\n\n\t$ time git grep --no-ext-grep -B1000 memset >/dev/null\n\n\treal\t0m2.986s\n\tuser\t0m2.696s\n\tsys\t0m0.288s\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n grep.c |   61 ++++++++++++++++++++++++++++++++-----------------------------\n 1 files changed, 32 insertions(+), 29 deletions(-)\n\ndiff --git a/grep.c b/grep.c\nindex 4bca759..9b9d2e3 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -531,16 +531,42 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \tprintf(\"%.*s\\n\", rest, bol);\n }\n \n+static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,\n+\t\t\t     char *bol, unsigned lno)\n+{\n+\tunsigned cur = lno, from = 1;\n+\n+\tif (opt->pre_context < lno)\n+\t\tfrom = lno - opt->pre_context;\n+\tif (from <= opt->last_shown)\n+\t\tfrom = opt->last_shown + 1;\n+\n+\t/* Rewind. */\n+\twhile (bol > buf && cur > from) {\n+\t\tbol--;\n+\t\twhile (bol > buf && bol[-1] != '\\n')\n+\t\t\tbol--;\n+\t\tcur--;\n+\t}\n+\n+\t/* Back forward. */\n+\twhile (cur < lno) {\n+\t\tchar *eol = bol;\n+\n+\t\twhile (*eol != '\\n')\n+\t\t\teol++;\n+\t\tshow_line(opt, bol, eol, name, cur, '-');\n+\t\tbol = eol + 1;\n+\t\tcur++;\n+\t}\n+}\n+\n static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t char *buf, unsigned long size, int collect_hits)\n {\n \tchar *bol = buf;\n \tunsigned long left = size;\n \tunsigned lno = 1;\n-\tstruct pre_context_line {\n-\t\tchar *bol;\n-\t\tchar *eol;\n-\t} *prev = NULL, *pcl;\n \tunsigned last_hit = 0;\n \tint binary_match_only = 0;\n \tunsigned count = 0;\n@@ -561,9 +587,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t}\n \t}\n \n-\tif (opt->pre_context)\n-\t\tprev = xcalloc(opt->pre_context, sizeof(*prev));\n-\n \twhile (left) {\n \t\tchar *eol, ch;\n \t\tint hit;\n@@ -610,21 +633,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t * the context which is nonsense, but the user\n \t\t\t * deserves to get that ;-).\n \t\t\t */\n-\t\t\tif (opt->pre_context) {\n-\t\t\t\tunsigned from;\n-\t\t\t\tif (opt->pre_context < lno)\n-\t\t\t\t\tfrom = lno - opt->pre_context;\n-\t\t\t\telse\n-\t\t\t\t\tfrom = 1;\n-\t\t\t\tif (from <= opt->last_shown)\n-\t\t\t\t\tfrom = opt->last_shown + 1;\n-\t\t\t\twhile (from < lno) {\n-\t\t\t\t\tpcl = &prev[lno-from-1];\n-\t\t\t\t\tshow_line(opt, pcl->bol, pcl->eol,\n-\t\t\t\t\t\t  name, from, '-');\n-\t\t\t\t\tfrom++;\n-\t\t\t\t}\n-\t\t\t}\n+\t\t\tif (opt->pre_context)\n+\t\t\t\tshow_pre_context(opt, name, buf, bol, lno);\n \t\t\tif (!opt->count)\n \t\t\t\tshow_line(opt, bol, eol, name, lno, ':');\n \t\t\tlast_hit = lno;\n@@ -636,12 +646,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t */\n \t\t\tshow_line(opt, bol, eol, name, lno, '-');\n \t\t}\n-\t\tif (opt->pre_context) {\n-\t\t\tmemmove(prev+1, prev,\n-\t\t\t\t(opt->pre_context-1) * sizeof(*prev));\n-\t\t\tprev->bol = bol;\n-\t\t\tprev->eol = eol;\n-\t\t}\n \n \tnext_line:\n \t\tbol = eol + 1;\n@@ -651,7 +655,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\tlno++;\n \t}\n \n-\tfree(prev);\n \tif (collect_hits)\n \t\treturn 0;\n \n-- \n1.6.3.3\n"},{"id":"117326","messageId":"4A4BDDEA.3030005@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4BDC65.80504@lsrfire.ath.cx","subject":"[PATCH 5/6] grep: add option -p/--show-function","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:06:34Z","receivedAt":"2009-07-01T22:06:34Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"The new option -p instructs git grep to print the previous function\ndefinition as a context line, similar to diff -p.  Such context lines\nare marked with an equal sign instead of a dash.  This option\ncomplements the existing context options -A, -B, -C.\n\nFunction definitions are detected using the same heuristic that diff\nuses.  User defined regular expressions are not supported, yet.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/git-grep.txt |    5 +++\n builtin-grep.c             |    8 +++--\n grep.c                     |   61 ++++++++++++++++++++++++++++++++++++++------\n grep.h                     |    1 +\n t/t7002-grep.sh            |   36 ++++++++++++++++++++++++-\n 5 files changed, 98 insertions(+), 13 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex fccb82d..b3bb283 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -122,6 +122,11 @@ OPTIONS\n -<num>::\n \tA shortcut for specifying -C<num>.\n \n+-p::\n+--show-function::\n+\tShow the preceding line that contains the function name of\n+\tthe match, unless the matching line is a function name itself.\n+\n -f <file>::\n \tRead patterns from <file>, one per line.\n \ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 48998af..037452e 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -278,13 +278,13 @@ static int flush_grep(struct grep_opt *opt,\n \t\targc -= 2;\n \t}\n \n-\tif (opt->pre_context || opt->post_context) {\n+\tif (opt->pre_context || opt->post_context || opt->funcname) {\n \t\t/*\n \t\t * grep handles hunk marks between files, but we need to\n \t\t * do that ourselves between multiple calls.\n \t\t */\n \t\tif (opt->show_hunk_mark)\n-\t\t\twrite_or_die(1, \"--\\n\", 3);\n+\t\t\twrite_or_die(1, opt->funcname ? \"==\\n\" : \"--\\n\", 3);\n \t\telse\n \t\t\topt->show_hunk_mark = 1;\n \t}\n@@ -721,6 +721,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\t\"show <n> context lines after matches\"),\n \t\tOPT_NUMBER_CALLBACK(&opt, \"shortcut for -C NUM\",\n \t\t\tcontext_callback),\n+\t\tOPT_BOOLEAN('p', \"show-function\", &opt.funcname,\n+\t\t\t\"show a line with the function name before matches\"),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_CALLBACK('f', NULL, &opt, \"file\",\n \t\t\t\"read patterns from file\", file_callback),\n@@ -789,7 +791,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\targc--;\n \t}\n \n-\tif (opt.color && !opt.color_external)\n+\tif ((opt.color && !opt.color_external) || opt.funcname)\n \t\texternal_grep_allowed = 0;\n \tif (!opt.pattern_list)\n \t\tdie(\"no pattern given.\");\ndiff --git a/grep.c b/grep.c\nindex 9b9d2e3..3a5c138 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -490,14 +490,18 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n {\n \tint rest = eol - bol;\n \n-\tif (opt->pre_context || opt->post_context) {\n+\tif (opt->pre_context || opt->post_context || opt->funcname) {\n \t\tif (opt->last_shown == 0) {\n \t\t\tif (opt->show_hunk_mark)\n-\t\t\t\tfputs(\"--\\n\", stdout);\n+\t\t\t\tfputs(opt->funcname ? \"==\\n\" : \"--\\n\", stdout);\n \t\t\telse\n \t\t\t\topt->show_hunk_mark = 1;\n-\t\t} else if (lno > opt->last_shown + 1)\n-\t\t\tfputs(\"--\\n\", stdout);\n+\t\t} else if (lno > opt->last_shown + 1) {\n+\t\t\tif (opt->pre_context || opt->post_context)\n+\t\t\t\tfputs((sign == '=') ? \"==\\n\" : \"--\\n\", stdout);\n+\t\t\telse if (sign == '=')\n+\t\t\t\tfputs(\"==\\n\", stdout);\n+\t\t}\n \t}\n \topt->last_shown = lno;\n \n@@ -531,10 +535,40 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \tprintf(\"%.*s\\n\", rest, bol);\n }\n \n+static int match_funcname(char *bol, char *eol)\n+{\n+\tif (bol == eol)\n+\t\treturn 0;\n+\tif (isalpha(*bol) || *bol == '_' || *bol == '$')\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static void show_funcname_line(struct grep_opt *opt, const char *name,\n+\t\t\t       char *buf, char *bol, unsigned lno)\n+{\n+\twhile (bol > buf) {\n+\t\tchar *eol = --bol;\n+\n+\t\twhile (bol > buf && bol[-1] != '\\n')\n+\t\t\tbol--;\n+\t\tlno--;\n+\n+\t\tif (lno <= opt->last_shown)\n+\t\t\tbreak;\n+\n+\t\tif (match_funcname(bol, eol)) {\n+\t\t\tshow_line(opt, bol, eol, name, lno, '=');\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,\n \t\t\t     char *bol, unsigned lno)\n {\n-\tunsigned cur = lno, from = 1;\n+\tunsigned cur = lno, from = 1, funcname_lno = 0;\n+\tint funcname_needed = opt->funcname;\n \n \tif (opt->pre_context < lno)\n \t\tfrom = lno - opt->pre_context;\n@@ -543,19 +577,28 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,\n \n \t/* Rewind. */\n \twhile (bol > buf && cur > from) {\n-\t\tbol--;\n+\t\tchar *eol = --bol;\n+\n \t\twhile (bol > buf && bol[-1] != '\\n')\n \t\t\tbol--;\n \t\tcur--;\n+\t\tif (funcname_needed && match_funcname(bol, eol)) {\n+\t\t\tfuncname_lno = cur;\n+\t\t\tfuncname_needed = 0;\n+\t\t}\n \t}\n \n+\t/* We need to look even further back to find a function signature. */\n+\tif (opt->funcname && funcname_needed)\n+\t\tshow_funcname_line(opt, name, buf, bol, cur);\n+\n \t/* Back forward. */\n \twhile (cur < lno) {\n-\t\tchar *eol = bol;\n+\t\tchar *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';\n \n \t\twhile (*eol != '\\n')\n \t\t\teol++;\n-\t\tshow_line(opt, bol, eol, name, cur, '-');\n+\t\tshow_line(opt, bol, eol, name, cur, sign);\n \t\tbol = eol + 1;\n \t\tcur++;\n \t}\n@@ -635,6 +678,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t */\n \t\t\tif (opt->pre_context)\n \t\t\t\tshow_pre_context(opt, name, buf, bol, lno);\n+\t\t\telse if (opt->funcname)\n+\t\t\t\tshow_funcname_line(opt, name, buf, bol, lno);\n \t\t\tif (!opt->count)\n \t\t\t\tshow_line(opt, bol, eol, name, lno, ':');\n \t\t\tlast_hit = lno;\ndiff --git a/grep.h b/grep.h\nindex 730ffd6..3f75e3a 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -79,6 +79,7 @@ struct grep_opt {\n \tint pathname;\n \tint null_following_name;\n \tint color;\n+\tint funcname;\n \tchar color_match[COLOR_MAXLEN];\n \tconst char *color_external;\n \tint regflags;\ndiff --git a/t/t7002-grep.sh b/t/t7002-grep.sh\nindex 155bfdb..ef59ab9 100755\n--- a/t/t7002-grep.sh\n+++ b/t/t7002-grep.sh\n@@ -8,6 +8,15 @@ test_description='git grep various.\n \n . ./test-lib.sh\n \n+cat >hello.c <<EOF\n+#include <stdio.h>\n+int main(int argc, const char **argv)\n+{\n+\tprintf(\"Hello world.\\n\");\n+\treturn 0;\n+}\n+EOF\n+\n test_expect_success setup '\n \t{\n \t\techo foo mmap bar\n@@ -22,7 +31,7 @@ test_expect_success setup '\n \techo zzz > z &&\n \tmkdir t &&\n \techo test >t/t &&\n-\tgit add file w x y z t/t &&\n+\tgit add file w x y z t/t hello.c &&\n \ttest_tick &&\n \tgit commit -m initial\n '\n@@ -229,9 +238,32 @@ test_expect_success 'log grep (6)' '\n test_expect_success 'grep with CE_VALID file' '\n \tgit update-index --assume-unchanged t/t &&\n \trm t/t &&\n-\ttest \"$(git grep --no-ext-grep t)\" = \"t/t:test\" &&\n+\ttest \"$(git grep --no-ext-grep test)\" = \"t/t:test\" &&\n \tgit update-index --no-assume-unchanged t/t &&\n \tgit checkout t/t\n '\n \n+cat >expected <<EOF\n+hello.c=int main(int argc, const char **argv)\n+hello.c:\treturn 0;\n+EOF\n+\n+test_expect_success 'grep -p' '\n+\tgit grep -p return >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+hello.c-#include <stdio.h>\n+hello.c=int main(int argc, const char **argv)\n+hello.c-{\n+hello.c-\tprintf(\"Hello world.\\n\");\n+hello.c:\treturn 0;\n+EOF\n+\n+test_expect_success 'grep -p -B5' '\n+\tgit grep -p -B5 return >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.6.3.3\n"},{"id":"117327","messageId":"4A4BDE1C.8070903@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4BDC65.80504@lsrfire.ath.cx","subject":"[PATCH 6/6] grep -p: support user defined regular expressions","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-01T22:07:24Z","receivedAt":"2009-07-01T22:07:24Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Respect the userdiff attributes and config settings when looking for\nlines with function definitions in git grep -p.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/git-grep.txt |    3 +++\n builtin-grep.c             |    7 +++++++\n grep.c                     |   29 ++++++++++++++++++++++++++---\n grep.h                     |    1 +\n t/t7002-grep.sh            |   13 +++++++++++++\n 5 files changed, 50 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex b3bb283..b753c9d 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -126,6 +126,9 @@ OPTIONS\n --show-function::\n \tShow the preceding line that contains the function name of\n \tthe match, unless the matching line is a function name itself.\n+\tThe name is determined in the same way as 'git diff' works out\n+\tpatch hunk headers (see 'Defining a custom hunk-header' in\n+\tlinkgit:gitattributes[5]).\n \n -f <file>::\n \tRead patterns from <file>, one per line.\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 037452e..9343cc5 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -11,6 +11,7 @@\n #include \"tree-walk.h\"\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"userdiff.h\"\n #include \"grep.h\"\n \n #ifndef NO_EXTERNAL_GREP\n@@ -30,6 +31,12 @@ static int grep_config(const char *var, const char *value, void *cb)\n {\n \tstruct grep_opt *opt = cb;\n \n+\tswitch (userdiff_config(var, value)) {\n+\tcase 0: break;\n+\tcase -1: return -1;\n+\tdefault: return 0;\n+\t}\n+\n \tif (!strcmp(var, \"color.grep\")) {\n \t\topt->color = git_config_colorbool(var, value, -1);\n \t\treturn 0;\ndiff --git a/grep.c b/grep.c\nindex 3a5c138..c47785a 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"grep.h\"\n+#include \"userdiff.h\"\n #include \"xdiff-interface.h\"\n \n void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)\n@@ -535,8 +536,15 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \tprintf(\"%.*s\\n\", rest, bol);\n }\n \n-static int match_funcname(char *bol, char *eol)\n+static int match_funcname(struct grep_opt *opt, char *bol, char *eol)\n {\n+\txdemitconf_t *xecfg = opt->priv;\n+\tif (xecfg && xecfg->find_func) {\n+\t\tchar buf[1];\n+\t\treturn xecfg->find_func(bol, eol - bol, buf, 1,\n+\t\t\t\t\txecfg->find_func_priv) >= 0;\n+\t}\n+\n \tif (bol == eol)\n \t\treturn 0;\n \tif (isalpha(*bol) || *bol == '_' || *bol == '$')\n@@ -557,7 +565,7 @@ static void show_funcname_line(struct grep_opt *opt, const char *name,\n \t\tif (lno <= opt->last_shown)\n \t\t\tbreak;\n \n-\t\tif (match_funcname(bol, eol)) {\n+\t\tif (match_funcname(opt, bol, eol)) {\n \t\t\tshow_line(opt, bol, eol, name, lno, '=');\n \t\t\tbreak;\n \t\t}\n@@ -582,7 +590,7 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,\n \t\twhile (bol > buf && bol[-1] != '\\n')\n \t\t\tbol--;\n \t\tcur--;\n-\t\tif (funcname_needed && match_funcname(bol, eol)) {\n+\t\tif (funcname_needed && match_funcname(opt, bol, eol)) {\n \t\t\tfuncname_lno = cur;\n \t\t\tfuncname_needed = 0;\n \t\t}\n@@ -614,6 +622,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \tint binary_match_only = 0;\n \tunsigned count = 0;\n \tenum grep_context ctx = GREP_CONTEXT_HEAD;\n+\txdemitconf_t xecfg;\n \n \topt->last_shown = 0;\n \n@@ -630,6 +639,17 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t}\n \t}\n \n+\tmemset(&xecfg, 0, sizeof(xecfg));\n+\tif (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&\n+\t    !opt->name_only && !binary_match_only && !collect_hits) {\n+\t\tstruct userdiff_driver *drv = userdiff_find_by_path(name);\n+\t\tif (drv && drv->funcname.pattern) {\n+\t\t\tconst struct userdiff_funcname *pe = &drv->funcname;\n+\t\t\txdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);\n+\t\t\topt->priv = &xecfg;\n+\t\t}\n+\t}\n+\n \twhile (left) {\n \t\tchar *eol, ch;\n \t\tint hit;\n@@ -711,6 +731,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\treturn 1;\n \t}\n \n+\txdiff_clear_find_func(&xecfg);\n+\topt->priv = NULL;\n+\n \t/* NEEDSWORK:\n \t * The real \"grep -c foo *.c\" gives many \"bar.c:0\" lines,\n \t * which feels mostly useless but sometimes useful.  Maybe\ndiff --git a/grep.h b/grep.h\nindex 3f75e3a..f00db0e 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -87,6 +87,7 @@ struct grep_opt {\n \tunsigned post_context;\n \tunsigned last_shown;\n \tint show_hunk_mark;\n+\tvoid *priv;\n };\n \n extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);\ndiff --git a/t/t7002-grep.sh b/t/t7002-grep.sh\nindex ef59ab9..b13aa7e 100755\n--- a/t/t7002-grep.sh\n+++ b/t/t7002-grep.sh\n@@ -244,11 +244,24 @@ test_expect_success 'grep with CE_VALID file' '\n '\n \n cat >expected <<EOF\n+hello.c=#include <stdio.h>\n+hello.c:\treturn 0;\n+EOF\n+\n+test_expect_success 'grep -p with userdiff' '\n+\tgit config diff.custom.funcname \"^#\" &&\n+\techo \"hello.c diff=custom\" >.gitattributes &&\n+\tgit grep -p return >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:\treturn 0;\n EOF\n \n test_expect_success 'grep -p' '\n+\trm -f .gitattributes &&\n \tgit grep -p return >actual &&\n \ttest_cmp expected actual\n '\n-- \n1.6.3.3\n"},{"id":"117331","messageId":"7vzlbodne0.fsf@alter.siamese.dyndns.org","threadId":"19996","inReplyTo":"4A4BDCFE.3050808@lsrfire.ath.cx","subject":"Re: [PATCH 2/6] grep: move context hunk mark handling into show_line()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-01T22:55:19Z","receivedAt":"2009-07-01T22:55:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> Move last_shown into struct grep_opt, to make it available in\n> show_line(), and then make the function handle the printing of hunk\n> marks for context lines in a central place.\n\nMakes sense.  But now \"are we showing from a separate block of text\" check\nis in show_line() and relieves the caller of show_line() from doing so,...\n\n>  \t\telse if (last_hit &&\n>  \t\t\t lno <= last_hit + opt->post_context) {\n>  \t\t\t/* If the last hit is within the post context,\n>  \t\t\t * we need to show this line.\n>  \t\t\t */\n> -\t\t\tif (last_shown && lno != last_shown + 1)\n> -\t\t\t\tfputs(hunk_mark, stdout);\n>  \t\t\tshow_line(opt, bol, eol, name, lno, '-');\n> -\t\t\tlast_shown = lno;\n\n... the comment in this context should go, no?\n"},{"id":"117335","messageId":"7vbpo3errd.fsf@alter.siamese.dyndns.org","threadId":"19996","inReplyTo":"4A4BDDEA.3030005@lsrfire.ath.cx","subject":"Re: [PATCH 5/6] grep: add option -p/--show-function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-02T02:35:34Z","receivedAt":"2009-07-02T02:35:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> The new option -p instructs git grep to print the previous function\n> definition as a context line, similar to diff -p.  Such context lines\n> are marked with an equal sign instead of a dash.  This option\n> complements the existing context options -A, -B, -C.\n\nI have to say that I am a bit confused with these '==' markers.\n\n    $ ./git-grep -p -n -e pre_context -- '*.c'\n    builtin-grep.c=238=static int flush_grep(struct grep_opt *opt,\n    builtin-grep.c:288:\tif (opt->pre_context || opt->post_context ||...\n    ==\n    builtin-grep.c=328=static int external_grep(struct grep_opt *opt, co...\n    builtin-grep.c:363:\tif (opt->post_context || opt->pre_context) {\n    builtin-grep.c:364:\t\tif (opt->post_context != opt->pre_co...\n    builtin-grep.c:365:\t\t\tif (opt->pre_context) {\n    builtin-grep.c:368:\t\t\t\t\t\t\"%u\"...\n    ==\n    builtin-grep.c=586=static int context_callback(const struct option *...\n\nAm I correct to say that '==' tells us that it has been showing the hits\nin the previous function shown on the =$lineno= line, but now it will show\nhits from a different function?\n\nWhen used with -[ABC], it gets even confusing.\n\n    $ ./git-grep -C1 -p -n -e pre_context -- '*.c'\n    builtin-grep.c=238=static int flush_grep(struct grep_opt *opt,\n    --\n    builtin-grep.c-287-\n    builtin-grep.c:288:\tif (opt->pre_context || opt->post_context ||...\n    builtin-grep.c-289-\t\t/*\n    ==\n    builtin-grep.c=328=static int external_grep(struct grep_opt *opt, co...\n    --\n    builtin-grep.c-362-\t\tpush_arg(\"-c\");\n    builtin-grep.c:363:\tif (opt->post_context || opt->pre_context) {\n    builtin-grep.c:364:\t\tif (opt->post_context != opt->pre_co...\n    builtin-grep.c:365:\t\t\tif (opt->pre_context) {\n    builtin-grep.c-366-\t\t\t\tpush_arg(\"-B\");\n    builtin-grep.c-367-\t\t\t\tlen += snprintf(argp...\n    builtin-grep.c:368:\t\t\t\t\t\t\"%u\"...\n    builtin-grep.c-369-\t\t\t\tif (sizeof(randarg) ...\n    ==\n    builtin-grep.c=586=static int context_callback(const struct option *...\n    --\n\nWhat do \"--\" lines tell us in these cases?  For example, does the first\none tells us that the -p output shown, which was from line 238, is outside\nof the context of the hit it will show at line 288?\n\nI am in no way complaining.  My eyes are just not yet used to the output\nformat and are having hard time getting used to them and/or appreciating\nhow the separation these dividing lines give us is helpful.\n"},{"id":"117336","messageId":"4A4C2655.9000000@lsrfire.ath.cx","threadId":"19996","inReplyTo":"7vzlbodne0.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/6] grep: move context hunk mark handling into show_line()","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-02T03:15:33Z","receivedAt":"2009-07-02T03:15:33Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n> \n>> Move last_shown into struct grep_opt, to make it available in\n>> show_line(), and then make the function handle the printing of hunk\n>> marks for context lines in a central place.\n> \n> Makes sense.  But now \"are we showing from a separate block of text\" check\n> is in show_line() and relieves the caller of show_line() from doing so,...\n> \n>>  \t\telse if (last_hit &&\n>>  \t\t\t lno <= last_hit + opt->post_context) {\n>>  \t\t\t/* If the last hit is within the post context,\n>>  \t\t\t * we need to show this line.\n>>  \t\t\t */\n>> -\t\t\tif (last_shown && lno != last_shown + 1)\n>> -\t\t\t\tfputs(hunk_mark, stdout);\n>>  \t\t\tshow_line(opt, bol, eol, name, lno, '-');\n>> -\t\t\tlast_shown = lno;\n> \n> ... the comment in this context should go, no?\n\nHmm, I didn't read it to refer to the hunk_mark, but rather to the line\n (from bol to eol).  And it's still true that show_line needs to be\ncalled because we're not more than post_context lines away from the\nprevious hit, i.e. this line is a post context line.\n"},{"id":"117337","messageId":"4A4C39D3.1020401@lsrfire.ath.cx","threadId":"19996","inReplyTo":"7vbpo3errd.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 5/6] grep: add option -p/--show-function","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-02T04:38:43Z","receivedAt":"2009-07-02T04:38:43Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n> \n>> The new option -p instructs git grep to print the previous function\n>> definition as a context line, similar to diff -p.  Such context lines\n>> are marked with an equal sign instead of a dash.  This option\n>> complements the existing context options -A, -B, -C.\n> \n> I have to say that I am a bit confused with these '==' markers.\n\nYou may be glad to hear that initial versions of this patch followed\neven stranger rules for showing these markers. ;-)\n\n>     $ ./git-grep -p -n -e pre_context -- '*.c'\n>     builtin-grep.c=238=static int flush_grep(struct grep_opt *opt,\n>     builtin-grep.c:288:\tif (opt->pre_context || opt->post_context ||...\n>     ==\n>     builtin-grep.c=328=static int external_grep(struct grep_opt *opt, co...\n>     builtin-grep.c:363:\tif (opt->post_context || opt->pre_context) {\n>     builtin-grep.c:364:\t\tif (opt->post_context != opt->pre_co...\n>     builtin-grep.c:365:\t\t\tif (opt->pre_context) {\n>     builtin-grep.c:368:\t\t\t\t\t\t\"%u\"...\n>     ==\n>     builtin-grep.c=586=static int context_callback(const struct option *...\n> \n> Am I correct to say that '==' tells us that it has been showing the hits\n> in the previous function shown on the =$lineno= line, but now it will show\n> hits from a different function?\n\nWith -p, the previous function of a hit is marked with an \"=\" (or\n\"=$lineno=\" with -n).  \"==\" is printed immediately before such a line,\nto separate it from other functions, yes.\n\n\"--\" could be printed between lines 238 and 288, between lines 328 and\n363 (after the function lines), and between lines 365 and 368, but that\nwould bloat the output for no gain, IMHO.  It would mean \"here we've\nskipped over a few lines\", which is correct, but it's also common and\nexpected.  So I left out print of \"--\" markers completely if -p is used\nwithout -[ABC].\n\n> When used with -[ABC], it gets even confusing.\n> \n>     $ ./git-grep -C1 -p -n -e pre_context -- '*.c'\n>     builtin-grep.c=238=static int flush_grep(struct grep_opt *opt,\n>     --\n>     builtin-grep.c-287-\n>     builtin-grep.c:288:\tif (opt->pre_context || opt->post_context ||...\n>     builtin-grep.c-289-\t\t/*\n>     ==\n>     builtin-grep.c=328=static int external_grep(struct grep_opt *opt, co...\n>     --\n>     builtin-grep.c-362-\t\tpush_arg(\"-c\");\n>     builtin-grep.c:363:\tif (opt->post_context || opt->pre_context) {\n>     builtin-grep.c:364:\t\tif (opt->post_context != opt->pre_co...\n>     builtin-grep.c:365:\t\t\tif (opt->pre_context) {\n>     builtin-grep.c-366-\t\t\t\tpush_arg(\"-B\");\n>     builtin-grep.c-367-\t\t\t\tlen += snprintf(argp...\n>     builtin-grep.c:368:\t\t\t\t\t\t\"%u\"...\n>     builtin-grep.c-369-\t\t\t\tif (sizeof(randarg) ...\n>     ==\n>     builtin-grep.c=586=static int context_callback(const struct option *...\n>     --\n> \n> What do \"--\" lines tell us in these cases?  For example, does the first\n> one tells us that the -p output shown, which was from line 238, is outside\n> of the context of the hit it will show at line 288?\n>\n> I am in no way complaining.  My eyes are just not yet used to the output\n> format and are having hard time getting used to them and/or appreciating\n> how the separation these dividing lines give us is helpful.\n\nHere's another example, which is potentially even more confusing:\n\n\t$ git grep -p -n markers grep.c\n\tgrep.c=748=static void clr_hit_marker(struct grep_expr *x)\n\tgrep.c:750:\t/* All-hit markers are meaningful only at...\n\t==\n\tgrep.c=762=static int chk_hit_marker(struct grep_expr *x)\n\tgrep.c:764:\t/* Top level nodes have hit markers.  See...\n\t==\n\tgrep.c=774=int grep_buffer(struct grep_opt *opt, const ch...\n\tgrep.c:784:\t * We first clear hit markers from them.\n\n\n\t$ git grep -p -n -B3 markers grep.c\n\tgrep.c-747-\n\tgrep.c=748=static void clr_hit_marker(struct grep_expr *x)\n\tgrep.c-749-{\n\tgrep.c:750:\t/* All-hit markers are meaningful only at...\n\t--\n\tgrep.c-761-\n\tgrep.c=762=static int chk_hit_marker(struct grep_expr *x)\n\tgrep.c-763-{\n\tgrep.c:764:\t/* Top level nodes have hit markers.  See...\n\t==\n\tgrep.c=774=int grep_buffer(struct grep_opt *opt, const ch...\n\t--\n\tgrep.c-781-\t\treturn grep_buffer_1(opt, name, b...\n\tgrep.c-782-\n\tgrep.c-783-\t/* Otherwise the toplevel \"or\" terms hit ...\n\tgrep.c:784:\t * We first clear hit markers from them.\n\nThe \"==\" after line 750 is changed to a \"--\" with -B3 or more.\n\n\nI'm not so sure about the usefulness of these markers myself, but the\nrules to generate them are quite simple.  The \"==\" markers exist only\nbecause I wanted the output of -p alone to be as compact as possible,\nbut without changing the meaning of the \"--\" markers.\n\nSo the meaning of the \"--\" markers is the same as before: \"lines have\nbeen skipped\".  \"==\" means the same, but also \"the next line is a\nfunction line\".  And -p alone only shows these specialized markers, for\nbrevity.\n\nHmm, even shorter would be to leave out these \"==\" things entirely:\n\n\t# mock-up\n\t$ git grep -p -n markers grep.c\n\tgrep.c=746=static void clr_hit_marker(struct grep_expr *x)\n\tgrep.c:748:     /* All-hit markers are meaningful only at...\n\tgrep.c=760=static int chk_hit_marker(struct grep_expr *x)\n\tgrep.c:762:     /* Top level nodes have hit markers.  See...\n\tgrep.c=772=int grep_buffer(struct grep_opt *opt, const ch...\n\tgrep.c:782:      * We first clear hit markers from them.\n\nIs that still readable?\n"},{"id":"117338","messageId":"7v3a9fejyk.fsf@alter.siamese.dyndns.org","threadId":"19996","inReplyTo":"4A4C2655.9000000@lsrfire.ath.cx","subject":"Re: [PATCH 2/6] grep: move context hunk mark handling into show_line()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-02T05:24:03Z","receivedAt":"2009-07-02T05:24:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> Hmm, I didn't read it to refer to the hunk_mark, but rather to the line\n> (from bol to eol).  And it's still true that show_line needs to be\n> called because we're not more than post_context lines away from the\n> previous hit, i.e. this line is a post context line.\n\nYou are right; it was my thinko.\n"},{"id":"117339","messageId":"7vy6r7d583.fsf@alter.siamese.dyndns.org","threadId":"19996","inReplyTo":"4A4C39D3.1020401@lsrfire.ath.cx","subject":"Re: [PATCH 5/6] grep: add option -p/--show-function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-02T05:27:40Z","receivedAt":"2009-07-02T05:27:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n\n> Hmm, even shorter would be to leave out these \"==\" things entirely:\n>\n> \t# mock-up\n> \t$ git grep -p -n markers grep.c\n> \tgrep.c=746=static void clr_hit_marker(struct grep_expr *x)\n> \tgrep.c:748:     /* All-hit markers are meaningful only at...\n> \tgrep.c=760=static int chk_hit_marker(struct grep_expr *x)\n> \tgrep.c:762:     /* Top level nodes have hit markers.  See...\n> \tgrep.c=772=int grep_buffer(struct grep_opt *opt, const ch...\n> \tgrep.c:782:      * We first clear hit markers from them.\n>\n> Is that still readable?\n\nActually the above matches what I expected to see when I read your [0/6].\n\nIs the use of '=' your invention, or does somebody's grep have this\nfunction header feature already and use '=' the same way?\n"},{"id":"117342","messageId":"4A4C50C7.1050303@lsrfire.ath.cx","threadId":"19996","inReplyTo":"7vy6r7d583.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 5/6] grep: add option -p/--show-function","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-02T06:16:39Z","receivedAt":"2009-07-02T06:16:39Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"Junio C Hamano schrieb:\n> René Scharfe <rene.scharfe@lsrfire.ath.cx> writes:\n> \n>> Hmm, even shorter would be to leave out these \"==\" things entirely:\n>>\n>> \t# mock-up\n>> \t$ git grep -p -n markers grep.c\n>> \tgrep.c=746=static void clr_hit_marker(struct grep_expr *x)\n>> \tgrep.c:748:     /* All-hit markers are meaningful only at...\n>> \tgrep.c=760=static int chk_hit_marker(struct grep_expr *x)\n>> \tgrep.c:762:     /* Top level nodes have hit markers.  See...\n>> \tgrep.c=772=int grep_buffer(struct grep_opt *opt, const ch...\n>> \tgrep.c:782:      * We first clear hit markers from them.\n>>\n>> Is that still readable?\n> \n> Actually the above matches what I expected to see when I read your [0/6].\n\nOK. :)\n\n> Is the use of '=' your invention, or does somebody's grep have this\n> function header feature already and use '=' the same way?\n\nI didn't find a prior implementation, only this announcement:\n\n   http://thread.gmane.org/gmane.comp.gnu.grep.bugs/141\n\nInitially, I used '@' (similar to diff), but that looked strange.  The\nequal sign seemed to fit well ('-' means context, '=' means even more\ncontext! ;-), but I haven't put a lot of thought or research into it.\n\nHere's patch 5 again, with all traces of \"==\" markers removed.  Patch 6\nstill applies, albeit with a slight offset.\n\n--- snip! ---\nThe new option -p instructs git grep to print the previous function\ndefinition as a context line, similar to diff -p.  Such context lines\nare marked with an equal sign instead of a dash.  This option\ncomplements the existing context options -A, -B, -C.\n\nFunction definitions are detected using the same heuristic that diff\nuses.  User defined regular expressions are not supported, yet.\n\nSigned-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>\n---\n Documentation/git-grep.txt |    5 ++++\n builtin-grep.c             |    4 ++-\n grep.c                     |   49 ++++++++++++++++++++++++++++++++++++++++---\n grep.h                     |    1 +\n t/t7002-grep.sh            |   36 ++++++++++++++++++++++++++++++-\n 5 files changed, 88 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt\nindex fccb82d..b3bb283 100644\n--- a/Documentation/git-grep.txt\n+++ b/Documentation/git-grep.txt\n@@ -122,6 +122,11 @@ OPTIONS\n -<num>::\n \tA shortcut for specifying -C<num>.\n \n+-p::\n+--show-function::\n+\tShow the preceding line that contains the function name of\n+\tthe match, unless the matching line is a function name itself.\n+\n -f <file>::\n \tRead patterns from <file>, one per line.\n \ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 48998af..44fd485 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -721,6 +721,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\t\"show <n> context lines after matches\"),\n \t\tOPT_NUMBER_CALLBACK(&opt, \"shortcut for -C NUM\",\n \t\t\tcontext_callback),\n+\t\tOPT_BOOLEAN('p', \"show-function\", &opt.funcname,\n+\t\t\t\"show a line with the function name before matches\"),\n \t\tOPT_GROUP(\"\"),\n \t\tOPT_CALLBACK('f', NULL, &opt, \"file\",\n \t\t\t\"read patterns from file\", file_callback),\n@@ -789,7 +791,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\targc--;\n \t}\n \n-\tif (opt.color && !opt.color_external)\n+\tif ((opt.color && !opt.color_external) || opt.funcname)\n \t\texternal_grep_allowed = 0;\n \tif (!opt.pattern_list)\n \t\tdie(\"no pattern given.\");\ndiff --git a/grep.c b/grep.c\nindex 9b9d2e3..364c7c3 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -531,10 +531,40 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n \tprintf(\"%.*s\\n\", rest, bol);\n }\n \n+static int match_funcname(char *bol, char *eol)\n+{\n+\tif (bol == eol)\n+\t\treturn 0;\n+\tif (isalpha(*bol) || *bol == '_' || *bol == '$')\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static void show_funcname_line(struct grep_opt *opt, const char *name,\n+\t\t\t       char *buf, char *bol, unsigned lno)\n+{\n+\twhile (bol > buf) {\n+\t\tchar *eol = --bol;\n+\n+\t\twhile (bol > buf && bol[-1] != '\\n')\n+\t\t\tbol--;\n+\t\tlno--;\n+\n+\t\tif (lno <= opt->last_shown)\n+\t\t\tbreak;\n+\n+\t\tif (match_funcname(bol, eol)) {\n+\t\t\tshow_line(opt, bol, eol, name, lno, '=');\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,\n \t\t\t     char *bol, unsigned lno)\n {\n-\tunsigned cur = lno, from = 1;\n+\tunsigned cur = lno, from = 1, funcname_lno = 0;\n+\tint funcname_needed = opt->funcname;\n \n \tif (opt->pre_context < lno)\n \t\tfrom = lno - opt->pre_context;\n@@ -543,19 +573,28 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,\n \n \t/* Rewind. */\n \twhile (bol > buf && cur > from) {\n-\t\tbol--;\n+\t\tchar *eol = --bol;\n+\n \t\twhile (bol > buf && bol[-1] != '\\n')\n \t\t\tbol--;\n \t\tcur--;\n+\t\tif (funcname_needed && match_funcname(bol, eol)) {\n+\t\t\tfuncname_lno = cur;\n+\t\t\tfuncname_needed = 0;\n+\t\t}\n \t}\n \n+\t/* We need to look even further back to find a function signature. */\n+\tif (opt->funcname && funcname_needed)\n+\t\tshow_funcname_line(opt, name, buf, bol, cur);\n+\n \t/* Back forward. */\n \twhile (cur < lno) {\n-\t\tchar *eol = bol;\n+\t\tchar *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';\n \n \t\twhile (*eol != '\\n')\n \t\t\teol++;\n-\t\tshow_line(opt, bol, eol, name, cur, '-');\n+\t\tshow_line(opt, bol, eol, name, cur, sign);\n \t\tbol = eol + 1;\n \t\tcur++;\n \t}\n@@ -635,6 +674,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,\n \t\t\t */\n \t\t\tif (opt->pre_context)\n \t\t\t\tshow_pre_context(opt, name, buf, bol, lno);\n+\t\t\telse if (opt->funcname)\n+\t\t\t\tshow_funcname_line(opt, name, buf, bol, lno);\n \t\t\tif (!opt->count)\n \t\t\t\tshow_line(opt, bol, eol, name, lno, ':');\n \t\t\tlast_hit = lno;\ndiff --git a/grep.h b/grep.h\nindex 730ffd6..3f75e3a 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -79,6 +79,7 @@ struct grep_opt {\n \tint pathname;\n \tint null_following_name;\n \tint color;\n+\tint funcname;\n \tchar color_match[COLOR_MAXLEN];\n \tconst char *color_external;\n \tint regflags;\ndiff --git a/t/t7002-grep.sh b/t/t7002-grep.sh\nindex 155bfdb..ef59ab9 100755\n--- a/t/t7002-grep.sh\n+++ b/t/t7002-grep.sh\n@@ -8,6 +8,15 @@ test_description='git grep various.\n \n . ./test-lib.sh\n \n+cat >hello.c <<EOF\n+#include <stdio.h>\n+int main(int argc, const char **argv)\n+{\n+\tprintf(\"Hello world.\\n\");\n+\treturn 0;\n+}\n+EOF\n+\n test_expect_success setup '\n \t{\n \t\techo foo mmap bar\n@@ -22,7 +31,7 @@ test_expect_success setup '\n \techo zzz > z &&\n \tmkdir t &&\n \techo test >t/t &&\n-\tgit add file w x y z t/t &&\n+\tgit add file w x y z t/t hello.c &&\n \ttest_tick &&\n \tgit commit -m initial\n '\n@@ -229,9 +238,32 @@ test_expect_success 'log grep (6)' '\n test_expect_success 'grep with CE_VALID file' '\n \tgit update-index --assume-unchanged t/t &&\n \trm t/t &&\n-\ttest \"$(git grep --no-ext-grep t)\" = \"t/t:test\" &&\n+\ttest \"$(git grep --no-ext-grep test)\" = \"t/t:test\" &&\n \tgit update-index --no-assume-unchanged t/t &&\n \tgit checkout t/t\n '\n \n+cat >expected <<EOF\n+hello.c=int main(int argc, const char **argv)\n+hello.c:\treturn 0;\n+EOF\n+\n+test_expect_success 'grep -p' '\n+\tgit grep -p return >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+hello.c-#include <stdio.h>\n+hello.c=int main(int argc, const char **argv)\n+hello.c-{\n+hello.c-\tprintf(\"Hello world.\\n\");\n+hello.c:\treturn 0;\n+EOF\n+\n+test_expect_success 'grep -p -B5' '\n+\tgit grep -p -B5 return >actual &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n1.6.3.3\n"},{"id":"117371","messageId":"4A4CD573.80707@lsrfire.ath.cx","threadId":"19996","inReplyTo":"4A4C50C7.1050303@lsrfire.ath.cx","subject":"Re: [PATCH 5/6] grep: add option -p/--show-function","fromName":"René Scharfe","fromEmail":"rene.scharfe@lsrfire.ath.cx","sentAt":"2009-07-02T15:42:43Z","receivedAt":"2009-07-02T15:42:43Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"And here is the interdiff to remove the code to print == markers.\n\nI think it makes sense to start out without them.  We can add them\nback later (or something else, like colourization) if the simpler\noutput turns out to be difficult to read.\n\nComments?  Opinions?\n\n\n builtin-grep.c |    4 ++--\n grep.c         |   12 ++++--------\n 2 files changed, 6 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 9343cc5..ff8e51b 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -285,13 +285,13 @@ static int flush_grep(struct grep_opt *opt,\n \t\targc -= 2;\n \t}\n \n-\tif (opt->pre_context || opt->post_context || opt->funcname) {\n+\tif (opt->pre_context || opt->post_context) {\n \t\t/*\n \t\t * grep handles hunk marks between files, but we need to\n \t\t * do that ourselves between multiple calls.\n \t\t */\n \t\tif (opt->show_hunk_mark)\n-\t\t\twrite_or_die(1, opt->funcname ? \"==\\n\" : \"--\\n\", 3);\n+\t\t\twrite_or_die(1, \"--\\n\", 3);\n \t\telse\n \t\t\topt->show_hunk_mark = 1;\n \t}\ndiff --git a/grep.c b/grep.c\nindex c47785a..5d162da 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -491,18 +491,14 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,\n {\n \tint rest = eol - bol;\n \n-\tif (opt->pre_context || opt->post_context || opt->funcname) {\n+\tif (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\tfputs(opt->funcname ? \"==\\n\" : \"--\\n\", stdout);\n+\t\t\t\tfputs(\"--\\n\", stdout);\n \t\t\telse\n \t\t\t\topt->show_hunk_mark = 1;\n-\t\t} else if (lno > opt->last_shown + 1) {\n-\t\t\tif (opt->pre_context || opt->post_context)\n-\t\t\t\tfputs((sign == '=') ? \"==\\n\" : \"--\\n\", stdout);\n-\t\t\telse if (sign == '=')\n-\t\t\t\tfputs(\"==\\n\", stdout);\n-\t\t}\n+\t\t} else if (lno > opt->last_shown + 1)\n+\t\t\tfputs(\"--\\n\", stdout);\n \t}\n \topt->last_shown = lno;\n \n"}]}