#include "tree-walk.h"
#include "builtin.h"
#include "parse-options.h"
+#include "userdiff.h"
#include "grep.h"
#ifndef NO_EXTERNAL_GREP
{
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;
return git_color_default_config(var, value, cb);
}
+/*
+ * Return non-zero if max_depth is negative or path has no more then max_depth
+ * slashes.
+ */
+static int accept_subdir(const char *path, int max_depth)
+{
+ if (max_depth < 0)
+ return 1;
+
+ while ((path = strchr(path, '/')) != NULL) {
+ max_depth--;
+ if (max_depth < 0)
+ return 0;
+ path++;
+ }
+ return 1;
+}
+
+/*
+ * Return non-zero if name is a subdirectory of match and is not too deep.
+ */
+static int is_subdir(const char *name, int namelen,
+ const char *match, int matchlen, int max_depth)
+{
+ if (matchlen > namelen || strncmp(name, match, matchlen))
+ return 0;
+
+ if (name[matchlen] == '\0') /* exact match */
+ return 1;
+
+ if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
+ return accept_subdir(name + matchlen + 1, max_depth);
+
+ return 0;
+}
+
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
* pathname wildcards are allowed.
*/
-static int pathspec_matches(const char **paths, const char *name)
+static int pathspec_matches(const char **paths, const char *name, int max_depth)
{
int namelen, i;
if (!paths || !*paths)
- return 1;
+ return accept_subdir(name, max_depth);
namelen = strlen(name);
for (i = 0; paths[i]; i++) {
const char *match = paths[i];
int matchlen = strlen(match);
const char *cp, *meta;
- if (!matchlen ||
- ((matchlen <= namelen) &&
- !strncmp(name, match, matchlen) &&
- (match[matchlen-1] == '/' ||
- name[matchlen] == '\0' || name[matchlen] == '/')))
+ if (is_subdir(name, namelen, match, matchlen, max_depth))
return 1;
if (!fnmatch(match, name, 0))
return 1;
int kept;
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
name = ce->name;
if (name[0] == '-') {
struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
/*
* If CE_VALID is on, we assume worktree file and its cache entry
strbuf_addch(&pathbuf, '/');
down = pathbuf.buf + tn_len;
- if (!pathspec_matches(paths, down))
+ if (!pathspec_matches(paths, down, opt->max_depth))
;
else if (S_ISREG(entry.mode))
hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
patterns = fopen(arg, "r");
if (!patterns)
- die("'%s': %s", arg, strerror(errno));
+ die_errno("cannot open '%s'", arg);
while (strbuf_getline(&sb, patterns, '\n') == 0) {
/* ignore empty line like grep does */
if (sb.len == 0)
OPT_SET_INT('I', NULL, &opt.binary,
"don't match patterns in binary files",
GREP_BINARY_NOMATCH),
+ { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
+ "descend at most <depth> levels", PARSE_OPT_NONEG,
+ NULL, 1 },
OPT_GROUP(""),
OPT_BIT('E', "extended-regexp", &opt.regflags,
"use extended POSIX regular expressions", REG_EXTENDED),
"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),
opt.pathname = 1;
opt.pattern_tail = &opt.pattern_list;
opt.regflags = REG_NEWLINE;
+ opt.max_depth = -1;
strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
opt.color = -1;
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.");