aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin-grep.c85
1 files changed, 62 insertions, 23 deletions
diff --git a/builtin-grep.c b/builtin-grep.c
index 36150bf4e..653b65ea1 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -12,33 +12,66 @@
#include "revision.h"
#include "builtin.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)
{
- int i, j;
- int namelen;
+ int namelen, i;
if (!opt->nr_paths)
return 1;
namelen = strlen(name);
for (i = 0; i < opt->nr_paths; i++) {
const char *match = opt->paths[i];
int matchlen = opt->pathlens[i];
- if (matchlen <= namelen) {
- if (!strncmp(name, match, matchlen))
- return 1;
+ const char *slash, *cp;
+
+ if ((matchlen <= namelen) &&
+ !strncmp(name, match, matchlen) &&
+ (match[matchlen-1] == '/' ||
+ name[matchlen] == '\0' || name[matchlen] == '/'))
+ return 1;
+ if (!fnmatch(match, name, 0))
+ return 1;
+ if (name[namelen-1] != '/')
continue;
- }
- /* If name is "Documentation" and pathspec is
- * "Documentation/", they should match. Maybe
- * we would want to strip it in get_pathspec()???
+
+ /* 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.
*/
- if (strncmp(name, match, namelen))
- continue;
- for (j = namelen; j < matchlen; j++)
- if (match[j] != '/')
+ for (cp = match, slash = NULL; cp - match < matchlen; cp++) {
+ char ch = *cp;
+ if (ch == '/')
+ slash = cp;
+ if (ch == '*' || ch == '[')
break;
- if (matchlen <= j)
- return 1;
+ }
+ 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;
}
@@ -232,17 +265,17 @@ static int grep_tree(struct grep_opt *opt, struct rev_info *revs,
int hit = 0;
const char *path;
const unsigned char *sha1;
- char *down_base;
+ char *down;
char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
if (tree_name[0]) {
int offset = sprintf(path_buf, "%s:", tree_name);
- down_base = path_buf + offset;
- strcat(down_base, base);
+ down = path_buf + offset;
+ strcat(down, base);
}
else {
- down_base = path_buf;
- strcpy(down_base, base);
+ down = path_buf;
+ strcpy(down, base);
}
len = strlen(path_buf);
@@ -252,7 +285,14 @@ static int grep_tree(struct grep_opt *opt, struct rev_info *revs,
pathlen = strlen(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))
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)
die("unable to read tree (%s)",
sha1_to_hex(sha1));
- strcpy(path_buf + len + pathlen, "/");
sub.buf = data;
- hit = grep_tree(opt, revs, &sub, tree_name, down_base);
+ hit |= grep_tree(opt, revs, &sub, tree_name, down);
free(data);
}
update_tree_entry(tree);