aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2009-07-09 00:59:58 -0700
committerJunio C Hamano <gitster@pobox.com>2009-07-09 00:59:58 -0700
commit128a9d86da3c156c7d1bf25692eeb68a522af935 (patch)
treec741913f11f81167c34ab306c35c8ed95eb88640
parentce4f404c6f059abb4988f4549c6e46a3ba0d56ec (diff)
parented24e401e0e6ab860475b8575e28a2c6ea99cc69 (diff)
downloadgit-128a9d86da3c156c7d1bf25692eeb68a522af935.tar.gz
git-128a9d86da3c156c7d1bf25692eeb68a522af935.tar.xz
Merge branch 'rs/grep-p'
* rs/grep-p: grep: simplify -p output grep -p: support user defined regular expressions grep: add option -p/--show-function grep: handle pre context lines on demand grep: print context hunk marks between files grep: move context hunk mark handling into show_line() userdiff: add xdiff_clear_find_func()
-rw-r--r--Documentation/git-grep.txt8
-rw-r--r--builtin-grep.c22
-rw-r--r--diff.c1
-rw-r--r--grep.c150
-rw-r--r--grep.h4
-rwxr-xr-xt/t7002-grep.sh71
-rw-r--r--xdiff-interface.c15
-rw-r--r--xdiff-interface.h1
8 files changed, 228 insertions, 44 deletions
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index fccb82deb..b753c9d76 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -122,6 +122,14 @@ OPTIONS
-<num>::
A shortcut for specifying -C<num>.
+-p::
+--show-function::
+ Show the preceding line that contains the function name of
+ the match, unless the matching line is a function name itself.
+ The name is determined in the same way as 'git diff' works out
+ patch hunk headers (see 'Defining a custom hunk-header' in
+ linkgit:gitattributes[5]).
+
-f <file>::
Read patterns from <file>, one per line.
diff --git a/builtin-grep.c b/builtin-grep.c
index e5583686a..f47765910 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -11,6 +11,7 @@
#include "tree-walk.h"
#include "builtin.h"
#include "parse-options.h"
+#include "userdiff.h"
#include "grep.h"
#ifndef NO_EXTERNAL_GREP
@@ -30,6 +31,12 @@ static int grep_config(const char *var, const char *value, void *cb)
{
struct grep_opt *opt = cb;
+ switch (userdiff_config(var, value)) {
+ case 0: break;
+ case -1: return -1;
+ default: return 0;
+ }
+
if (!strcmp(var, "color.grep")) {
opt->color = git_config_colorbool(var, value, -1);
return 0;
@@ -278,6 +285,17 @@ static int flush_grep(struct grep_opt *opt,
argc -= 2;
}
+ if (opt->pre_context || opt->post_context) {
+ /*
+ * grep handles hunk marks between files, but we need to
+ * do that ourselves between multiple calls.
+ */
+ if (opt->show_hunk_mark)
+ write_or_die(1, "--\n", 3);
+ else
+ opt->show_hunk_mark = 1;
+ }
+
status = exec_grep(argc, argv);
if (kept_0) {
@@ -710,6 +728,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
"show <n> context lines after matches"),
OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
context_callback),
+ OPT_BOOLEAN('p', "show-function", &opt.funcname,
+ "show a line with the function name before matches"),
OPT_GROUP(""),
OPT_CALLBACK('f', NULL, &opt, "file",
"read patterns from file", file_callback),
@@ -778,7 +798,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
argc--;
}
- if (opt.color && !opt.color_external)
+ if ((opt.color && !opt.color_external) || opt.funcname)
external_grep_allowed = 0;
if (!opt.pattern_list)
die("no pattern given.");
diff --git a/diff.c b/diff.c
index 0a020ffab..cd35e0c2d 100644
--- a/diff.c
+++ b/diff.c
@@ -1603,6 +1603,7 @@ static void builtin_diff(const char *name_a,
free(mf1.ptr);
if (textconv_two)
free(mf2.ptr);
+ xdiff_clear_find_func(&xecfg);
}
free_ab_and_return:
diff --git a/grep.c b/grep.c
index 92a47c71e..5d162dae6 100644
--- a/grep.c
+++ b/grep.c
@@ -1,5 +1,6 @@
#include "cache.h"
#include "grep.h"
+#include "userdiff.h"
#include "xdiff-interface.h"
void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
@@ -490,6 +491,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
{
int rest = eol - bol;
+ if (opt->pre_context || opt->post_context) {
+ if (opt->last_shown == 0) {
+ if (opt->show_hunk_mark)
+ fputs("--\n", stdout);
+ else
+ opt->show_hunk_mark = 1;
+ } else if (lno > opt->last_shown + 1)
+ fputs("--\n", stdout);
+ }
+ opt->last_shown = lno;
+
if (opt->null_following_name)
sign = '\0';
if (opt->pathname)
@@ -520,22 +532,95 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
printf("%.*s\n", rest, bol);
}
+static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
+{
+ xdemitconf_t *xecfg = opt->priv;
+ if (xecfg && xecfg->find_func) {
+ char buf[1];
+ return xecfg->find_func(bol, eol - bol, buf, 1,
+ xecfg->find_func_priv) >= 0;
+ }
+
+ if (bol == eol)
+ return 0;
+ if (isalpha(*bol) || *bol == '_' || *bol == '$')
+ return 1;
+ return 0;
+}
+
+static void show_funcname_line(struct grep_opt *opt, const char *name,
+ char *buf, char *bol, unsigned lno)
+{
+ while (bol > buf) {
+ char *eol = --bol;
+
+ while (bol > 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, '=');
+ break;
+ }
+ }
+}
+
+static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
+ char *bol, unsigned lno)
+{
+ unsigned cur = lno, from = 1, funcname_lno = 0;
+ int funcname_needed = opt->funcname;
+
+ if (opt->pre_context < lno)
+ from = lno - opt->pre_context;
+ if (from <= opt->last_shown)
+ from = opt->last_shown + 1;
+
+ /* Rewind. */
+ while (bol > buf && cur > from) {
+ char *eol = --bol;
+
+ while (bol > buf && bol[-1] != '\n')
+ bol--;
+ cur--;
+ if (funcname_needed && match_funcname(opt, 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);
+
+ /* Back forward. */
+ while (cur < lno) {
+ char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
+
+ while (*eol != '\n')
+ eol++;
+ show_line(opt, bol, eol, name, cur, sign);
+ bol = eol + 1;
+ cur++;
+ }
+}
+
static int grep_buffer_1(struct grep_opt *opt, const char *name,
char *buf, unsigned long size, int collect_hits)
{
char *bol = buf;
unsigned long left = size;
unsigned lno = 1;
- struct pre_context_line {
- char *bol;
- char *eol;
- } *prev = NULL, *pcl;
unsigned last_hit = 0;
- unsigned last_shown = 0;
int binary_match_only = 0;
- const char *hunk_mark = "";
unsigned count = 0;
enum grep_context ctx = GREP_CONTEXT_HEAD;
+ xdemitconf_t xecfg;
+
+ opt->last_shown = 0;
if (buffer_is_binary(buf, size)) {
switch (opt->binary) {
@@ -550,10 +635,16 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
}
}
- if (opt->pre_context)
- prev = xcalloc(opt->pre_context, sizeof(*prev));
- if (opt->pre_context || opt->post_context)
- hunk_mark = "--\n";
+ 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;
+ }
+ }
while (left) {
char *eol, ch;
@@ -601,45 +692,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
* the context which is nonsense, but the user
* deserves to get that ;-).
*/
- if (opt->pre_context) {
- unsigned from;
- if (opt->pre_context < lno)
- from = lno - opt->pre_context;
- else
- from = 1;
- if (from <= last_shown)
- from = last_shown + 1;
- if (last_shown && from != last_shown + 1)
- fputs(hunk_mark, stdout);
- while (from < lno) {
- pcl = &prev[lno-from-1];
- show_line(opt, pcl->bol, pcl->eol,
- name, from, '-');
- from++;
- }
- last_shown = lno-1;
- }
- if (last_shown && lno != last_shown + 1)
- fputs(hunk_mark, stdout);
+ if (opt->pre_context)
+ show_pre_context(opt, name, buf, bol, lno);
+ else if (opt->funcname)
+ show_funcname_line(opt, name, buf, bol, lno);
if (!opt->count)
show_line(opt, bol, eol, name, lno, ':');
- last_shown = last_hit = lno;
+ last_hit = lno;
}
else if (last_hit &&
lno <= last_hit + opt->post_context) {
/* If the last hit is within the post context,
* we need to show this line.
*/
- if (last_shown && lno != last_shown + 1)
- fputs(hunk_mark, stdout);
show_line(opt, bol, eol, name, lno, '-');
- last_shown = lno;
- }
- if (opt->pre_context) {
- memmove(prev+1, prev,
- (opt->pre_context-1) * sizeof(*prev));
- prev->bol = bol;
- prev->eol = eol;
}
next_line:
@@ -650,7 +716,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
lno++;
}
- free(prev);
if (collect_hits)
return 0;
@@ -662,6 +727,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
return 1;
}
+ xdiff_clear_find_func(&xecfg);
+ opt->priv = NULL;
+
/* NEEDSWORK:
* The real "grep -c foo *.c" gives many "bar.c:0" lines,
* which feels mostly useless but sometimes useful. Maybe
diff --git a/grep.h b/grep.h
index 464e272ed..f00db0e40 100644
--- a/grep.h
+++ b/grep.h
@@ -79,11 +79,15 @@ struct grep_opt {
int pathname;
int null_following_name;
int color;
+ int funcname;
char color_match[COLOR_MAXLEN];
const char *color_external;
int regflags;
unsigned pre_context;
unsigned post_context;
+ unsigned last_shown;
+ int show_hunk_mark;
+ void *priv;
};
extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 7868af8f1..b13aa7e89 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -8,6 +8,15 @@ test_description='git grep various.
. ./test-lib.sh
+cat >hello.c <<EOF
+#include <stdio.h>
+int main(int argc, const char **argv)
+{
+ printf("Hello world.\n");
+ return 0;
+}
+EOF
+
test_expect_success setup '
{
echo foo mmap bar
@@ -22,7 +31,7 @@ test_expect_success setup '
echo zzz > z &&
mkdir t &&
echo test >t/t &&
- git add file w x y z t/t &&
+ git add file w x y z t/t hello.c &&
test_tick &&
git commit -m initial
'
@@ -155,6 +164,28 @@ test_expect_success 'grep -e A --and --not -e B' '
test_cmp expected actual
'
+cat >expected <<EOF
+y:y yy
+--
+z:zzz
+EOF
+
+# Create 1024 file names that sort between "y" and "z" to make sure
+# the two files are handled by different calls to an external grep.
+# This depends on MAXARGS in builtin-grep.c being 1024 or less.
+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"
+test_expect_success 'grep -C1, hunk mark between files' '
+ for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
+ git add y-?? &&
+ git grep -C1 "^[yz]" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '
+ git grep -C1 --no-ext-grep "^[yz]" >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'log grep setup' '
echo a >>file &&
test_tick &&
@@ -207,9 +238,45 @@ test_expect_success 'log grep (6)' '
test_expect_success 'grep with CE_VALID file' '
git update-index --assume-unchanged t/t &&
rm t/t &&
- test "$(git grep --no-ext-grep t)" = "t/t:test" &&
+ test "$(git grep --no-ext-grep test)" = "t/t:test" &&
git update-index --no-assume-unchanged t/t &&
git checkout t/t
'
+cat >expected <<EOF
+hello.c=#include <stdio.h>
+hello.c: return 0;
+EOF
+
+test_expect_success 'grep -p with userdiff' '
+ git config diff.custom.funcname "^#" &&
+ echo "hello.c diff=custom" >.gitattributes &&
+ git grep -p return >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c: return 0;
+EOF
+
+test_expect_success 'grep -p' '
+ rm -f .gitattributes &&
+ git grep -p return >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-#include <stdio.h>
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c- printf("Hello world.\n");
+hello.c: return 0;
+EOF
+
+test_expect_success 'grep -p -B5' '
+ git grep -p -B5 return >actual &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/xdiff-interface.c b/xdiff-interface.c
index b9b0db8d8..01f14fb50 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -309,6 +309,21 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)
}
}
+void xdiff_clear_find_func(xdemitconf_t *xecfg)
+{
+ if (xecfg->find_func) {
+ int i;
+ struct ff_regs *regs = xecfg->find_func_priv;
+
+ for (i = 0; i < regs->nr; i++)
+ regfree(&regs->array[i].re);
+ free(regs->array);
+ free(regs);
+ xecfg->find_func = NULL;
+ xecfg->find_func_priv = NULL;
+ }
+}
+
int git_xmerge_style = -1;
int git_xmerge_config(const char *var, const char *value, void *cb)
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 7352b9a9c..55572c39a 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -21,6 +21,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename);
int buffer_is_binary(const char *ptr, unsigned long size);
extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
+extern void xdiff_clear_find_func(xdemitconf_t *xecfg);
extern int git_xmerge_config(const char *var, const char *value, void *cb);
extern int git_xmerge_style;