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()
This commit is contained in:
commit
128a9d86da
@ -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.
|
||||
|
||||
|
@ -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.");
|
||||
|
1
diff.c
1
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:
|
||||
|
150
grep.c
150
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
|
||||
|
4
grep.h
4
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);
|
||||
|
@ -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
|
||||
|
@ -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(®s->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)
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user