builtin-grep: wildcard pathspec fixes

This tweaks the pathspec wildcard used in builtin-grep to match
that of ls-files.  With this:

	git grep -e DEBUG -- '*/Kconfig*'

would work like the shell script version, and you could even do:

	git grep -e DEBUG --cached -- '*/Kconfig*' ;# from index
	git grep -e DEBUG v2.6.12 -- '*/Kconfig*' ;# from rev

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2006-05-01 12:27:56 -07:00
parent 5010cb5fcc
commit e0eb889f8e

View file

@ -12,33 +12,66 @@
#include "revision.h" #include "revision.h"
#include "builtin.h" #include "builtin.h"
#include <regex.h> #include <regex.h>
#include <fnmatch.h>
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
* pathname wildcards are allowed.
*/
static int pathspec_matches(struct diff_options *opt, const char *name) static int pathspec_matches(struct diff_options *opt, const char *name)
{ {
int i, j; int namelen, i;
int namelen;
if (!opt->nr_paths) if (!opt->nr_paths)
return 1; return 1;
namelen = strlen(name); namelen = strlen(name);
for (i = 0; i < opt->nr_paths; i++) { for (i = 0; i < opt->nr_paths; i++) {
const char *match = opt->paths[i]; const char *match = opt->paths[i];
int matchlen = opt->pathlens[i]; int matchlen = opt->pathlens[i];
if (matchlen <= namelen) { const char *slash, *cp;
if (!strncmp(name, match, matchlen))
return 1; if ((matchlen <= namelen) &&
continue; !strncmp(name, match, matchlen) &&
} (match[matchlen-1] == '/' ||
/* If name is "Documentation" and pathspec is name[matchlen] == '\0' || name[matchlen] == '/'))
* "Documentation/", they should match. Maybe
* we would want to strip it in get_pathspec()???
*/
if (strncmp(name, match, namelen))
continue;
for (j = namelen; j < matchlen; j++)
if (match[j] != '/')
break;
if (matchlen <= j)
return 1; return 1;
if (!fnmatch(match, name, 0))
return 1;
if (name[namelen-1] != '/')
continue;
/* We are being asked if the name directory is worth
* descending into.
*
* Find the longest leading directory name that does
* not have metacharacter in the pathspec; the name
* we are looking at must overlap with that directory.
*/
for (cp = match, slash = NULL; cp - match < matchlen; cp++) {
char ch = *cp;
if (ch == '/')
slash = cp;
if (ch == '*' || ch == '[')
break;
}
if (!slash)
slash = match; /* toplevel */
else
slash++;
if (namelen <= slash - match) {
/* Looking at "Documentation/" and
* the pattern says "Documentation/howto/", or
* "Documentation/diff*.txt".
*/
if (!memcmp(match, name, namelen))
return 1;
}
else {
/* Looking at "Documentation/howto/" and
* the pattern says "Documentation/h*".
*/
if (!memcmp(match, name, slash - match))
return 1;
}
} }
return 0; return 0;
} }
@ -232,17 +265,17 @@ static int grep_tree(struct grep_opt *opt, struct rev_info *revs,
int hit = 0; int hit = 0;
const char *path; const char *path;
const unsigned char *sha1; const unsigned char *sha1;
char *down_base; char *down;
char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100); char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
if (tree_name[0]) { if (tree_name[0]) {
int offset = sprintf(path_buf, "%s:", tree_name); int offset = sprintf(path_buf, "%s:", tree_name);
down_base = path_buf + offset; down = path_buf + offset;
strcat(down_base, base); strcat(down, base);
} }
else { else {
down_base = path_buf; down = path_buf;
strcpy(down_base, base); strcpy(down, base);
} }
len = strlen(path_buf); len = strlen(path_buf);
@ -252,7 +285,14 @@ static int grep_tree(struct grep_opt *opt, struct rev_info *revs,
pathlen = strlen(path); pathlen = strlen(path);
strcpy(path_buf + len, path); strcpy(path_buf + len, path);
if (!pathspec_matches(&revs->diffopt, down_base)) if (S_ISDIR(mode))
/* Match "abc/" against pathspec to
* decide if we want to descend into "abc"
* directory.
*/
strcpy(path_buf + len + pathlen, "/");
if (!pathspec_matches(&revs->diffopt, down))
; ;
else if (S_ISREG(mode)) else if (S_ISREG(mode))
hit |= grep_sha1(opt, sha1, path_buf); hit |= grep_sha1(opt, sha1, path_buf);
@ -264,9 +304,8 @@ static int grep_tree(struct grep_opt *opt, struct rev_info *revs,
if (!data) if (!data)
die("unable to read tree (%s)", die("unable to read tree (%s)",
sha1_to_hex(sha1)); sha1_to_hex(sha1));
strcpy(path_buf + len + pathlen, "/");
sub.buf = data; sub.buf = data;
hit = grep_tree(opt, revs, &sub, tree_name, down_base); hit |= grep_tree(opt, revs, &sub, tree_name, down);
free(data); free(data);
} }
update_tree_entry(tree); update_tree_entry(tree);