diff options
Diffstat (limited to 'builtin/grep.c')
-rw-r--r-- | builtin/grep.c | 548 |
1 files changed, 478 insertions, 70 deletions
diff --git a/builtin/grep.c b/builtin/grep.c index ae738312aa..42ff87065a 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -4,6 +4,8 @@ * Copyright (c) 2006 Junio C Hamano */ #include "cache.h" +#include "repository.h" +#include "config.h" #include "blob.h" #include "tree.h" #include "commit.h" @@ -18,12 +20,22 @@ #include "quote.h" #include "dir.h" #include "pathspec.h" +#include "submodule.h" +#include "submodule-config.h" static char const * const grep_usage[] = { N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"), NULL }; +static const char *super_prefix; +static int recurse_submodules; +static struct argv_array submodule_options = ARGV_ARRAY_INIT; +static const char *parent_basename; + +static int grep_submodule_launch(struct grep_opt *opt, + const struct grep_source *gs); + #define GREP_NUM_THREADS_DEFAULT 8 static int num_threads; @@ -63,14 +75,14 @@ static pthread_mutex_t grep_mutex; static inline void grep_lock(void) { - if (num_threads) - pthread_mutex_lock(&grep_mutex); + assert(num_threads); + pthread_mutex_lock(&grep_mutex); } static inline void grep_unlock(void) { - if (num_threads) - pthread_mutex_unlock(&grep_mutex); + assert(num_threads); + pthread_mutex_unlock(&grep_mutex); } /* Signalled when a new work_item is added to todo. */ @@ -174,7 +186,10 @@ static void *run(void *arg) break; opt->output_priv = w; - hit |= grep_source(opt, &w->source); + if (w->source.type == GREP_SOURCE_SUBMODULE) + hit |= grep_submodule_launch(opt, &w->source); + else + hit |= grep_source(opt, &w->source); grep_source_clear_data(&w->source); work_done(w); } @@ -211,7 +226,8 @@ static void start_threads(struct grep_opt *opt) int err; struct grep_opt *o = grep_opt_dup(opt); o->output = strbuf_out; - o->debug = 0; + if (i) + o->debug = 0; compile_grep_patterns(o); err = pthread_create(&threads[i], NULL, run, o); @@ -268,7 +284,7 @@ static int wait_all(void) static int grep_cmd_config(const char *var, const char *value, void *cb) { int st = grep_config(var, value, cb); - if (git_color_default_config(var, value, cb) < 0) + if (git_default_config(var, value, cb) < 0) st = -1; if (!strcmp(var, "grep.threads")) { @@ -276,37 +292,59 @@ static int grep_cmd_config(const char *var, const char *value, void *cb) if (num_threads < 0) die(_("invalid number of threads specified (%d) for %s"), num_threads, var); +#ifdef NO_PTHREADS + else if (num_threads && num_threads != 1) { + /* + * TRANSLATORS: %s is the configuration + * variable for tweaking threads, currently + * grep.threads + */ + warning(_("no threads support, ignoring %s"), var); + num_threads = 0; + } +#endif } + if (!strcmp(var, "submodule.recurse")) + recurse_submodules = git_config_bool(var, value); + return st; } -static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) +static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size) { void *data; grep_read_lock(); - data = read_sha1_file(sha1, type, size); + data = read_sha1_file(oid->hash, type, size); grep_read_unlock(); return data; } -static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, +static int grep_oid(struct grep_opt *opt, const struct object_id *oid, const char *filename, int tree_name_len, const char *path) { struct strbuf pathbuf = STRBUF_INIT; - if (opt->relative && opt->prefix_length) { - quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf); - strbuf_insert(&pathbuf, 0, filename, tree_name_len); + if (super_prefix) { + strbuf_add(&pathbuf, filename, tree_name_len); + strbuf_addstr(&pathbuf, super_prefix); + strbuf_addstr(&pathbuf, filename + tree_name_len); } else { strbuf_addstr(&pathbuf, filename); } + if (opt->relative && opt->prefix_length) { + char *name = strbuf_detach(&pathbuf, NULL); + quote_path_relative(name + tree_name_len, opt->prefix, &pathbuf); + strbuf_insert(&pathbuf, 0, name, tree_name_len); + free(name); + } + #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); + add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid); strbuf_release(&pathbuf); return 0; } else @@ -315,7 +353,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); + grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); strbuf_release(&pathbuf); hit = grep_source(opt, &gs); @@ -328,10 +366,15 @@ static int grep_file(struct grep_opt *opt, const char *filename) { struct strbuf buf = STRBUF_INIT; - if (opt->relative && opt->prefix_length) - quote_path_relative(filename, opt->prefix, &buf); - else - strbuf_addstr(&buf, filename); + if (super_prefix) + strbuf_addstr(&buf, super_prefix); + strbuf_addstr(&buf, filename); + + if (opt->relative && opt->prefix_length) { + char *name = strbuf_detach(&buf, NULL); + quote_path_relative(name, opt->prefix, &buf); + free(name); + } #ifndef NO_PTHREADS if (num_threads) { @@ -378,30 +421,312 @@ static void run_pager(struct grep_opt *opt, const char *prefix) exit(status); } -static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached) +static void compile_submodule_options(const struct grep_opt *opt, + const char **argv, + int cached, int untracked, + int opt_exclude, int use_index, + int pattern_type_arg) +{ + struct grep_pat *pattern; + + if (recurse_submodules) + argv_array_push(&submodule_options, "--recurse-submodules"); + + if (cached) + argv_array_push(&submodule_options, "--cached"); + if (!use_index) + argv_array_push(&submodule_options, "--no-index"); + if (untracked) + argv_array_push(&submodule_options, "--untracked"); + if (opt_exclude > 0) + argv_array_push(&submodule_options, "--exclude-standard"); + + if (opt->invert) + argv_array_push(&submodule_options, "-v"); + if (opt->ignore_case) + argv_array_push(&submodule_options, "-i"); + if (opt->word_regexp) + argv_array_push(&submodule_options, "-w"); + switch (opt->binary) { + case GREP_BINARY_NOMATCH: + argv_array_push(&submodule_options, "-I"); + break; + case GREP_BINARY_TEXT: + argv_array_push(&submodule_options, "-a"); + break; + default: + break; + } + if (opt->allow_textconv) + argv_array_push(&submodule_options, "--textconv"); + if (opt->max_depth != -1) + argv_array_pushf(&submodule_options, "--max-depth=%d", + opt->max_depth); + if (opt->linenum) + argv_array_push(&submodule_options, "-n"); + if (!opt->pathname) + argv_array_push(&submodule_options, "-h"); + if (!opt->relative) + argv_array_push(&submodule_options, "--full-name"); + if (opt->name_only) + argv_array_push(&submodule_options, "-l"); + if (opt->unmatch_name_only) + argv_array_push(&submodule_options, "-L"); + if (opt->null_following_name) + argv_array_push(&submodule_options, "-z"); + if (opt->count) + argv_array_push(&submodule_options, "-c"); + if (opt->file_break) + argv_array_push(&submodule_options, "--break"); + if (opt->heading) + argv_array_push(&submodule_options, "--heading"); + if (opt->pre_context) + argv_array_pushf(&submodule_options, "--before-context=%d", + opt->pre_context); + if (opt->post_context) + argv_array_pushf(&submodule_options, "--after-context=%d", + opt->post_context); + if (opt->funcname) + argv_array_push(&submodule_options, "-p"); + if (opt->funcbody) + argv_array_push(&submodule_options, "-W"); + if (opt->all_match) + argv_array_push(&submodule_options, "--all-match"); + if (opt->debug) + argv_array_push(&submodule_options, "--debug"); + if (opt->status_only) + argv_array_push(&submodule_options, "-q"); + + switch (pattern_type_arg) { + case GREP_PATTERN_TYPE_BRE: + argv_array_push(&submodule_options, "-G"); + break; + case GREP_PATTERN_TYPE_ERE: + argv_array_push(&submodule_options, "-E"); + break; + case GREP_PATTERN_TYPE_FIXED: + argv_array_push(&submodule_options, "-F"); + break; + case GREP_PATTERN_TYPE_PCRE: + argv_array_push(&submodule_options, "-P"); + break; + case GREP_PATTERN_TYPE_UNSPECIFIED: + break; + default: + die("BUG: Added a new grep pattern type without updating switch statement"); + } + + for (pattern = opt->pattern_list; pattern != NULL; + pattern = pattern->next) { + switch (pattern->token) { + case GREP_PATTERN: + argv_array_pushf(&submodule_options, "-e%s", + pattern->pattern); + break; + case GREP_AND: + case GREP_OPEN_PAREN: + case GREP_CLOSE_PAREN: + case GREP_NOT: + case GREP_OR: + argv_array_push(&submodule_options, pattern->pattern); + break; + /* BODY and HEAD are not used by git-grep */ + case GREP_PATTERN_BODY: + case GREP_PATTERN_HEAD: + break; + } + } + + /* + * Limit number of threads for child process to use. + * This is to prevent potential fork-bomb behavior of git-grep as each + * submodule process has its own thread pool. + */ + argv_array_pushf(&submodule_options, "--threads=%d", + DIV_ROUND_UP(num_threads, 2)); + + /* Add Pathspecs */ + argv_array_push(&submodule_options, "--"); + for (; *argv; argv++) + argv_array_push(&submodule_options, *argv); +} + +/* + * Launch child process to grep contents of a submodule + */ +static int grep_submodule_launch(struct grep_opt *opt, + const struct grep_source *gs) +{ + struct child_process cp = CHILD_PROCESS_INIT; + int status, i; + const char *end_of_base; + const char *name; + struct strbuf child_output = STRBUF_INIT; + + end_of_base = strchr(gs->name, ':'); + if (gs->identifier && end_of_base) + name = end_of_base + 1; + else + name = gs->name; + + prepare_submodule_repo_env(&cp.env_array); + argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT); + + if (opt->relative && opt->prefix_length) + argv_array_pushf(&cp.env_array, "%s=%s", + GIT_TOPLEVEL_PREFIX_ENVIRONMENT, + opt->prefix); + + /* Add super prefix */ + argv_array_pushf(&cp.args, "--super-prefix=%s%s/", + super_prefix ? super_prefix : "", + name); + argv_array_push(&cp.args, "grep"); + + /* + * Add basename of parent project + * When performing grep on a tree object the filename is prefixed + * with the object's name: 'tree-name:filename'. In order to + * provide uniformity of output we want to pass the name of the + * parent project's object name to the submodule so the submodule can + * prefix its output with the parent's name and not its own OID. + */ + if (gs->identifier && end_of_base) + argv_array_pushf(&cp.args, "--parent-basename=%.*s", + (int) (end_of_base - gs->name), + gs->name); + + /* Add options */ + for (i = 0; i < submodule_options.argc; i++) { + /* + * If there is a tree identifier for the submodule, add the + * rev after adding the submodule options but before the + * pathspecs. To do this we listen for the '--' and insert the + * oid before pushing the '--' onto the child process argv + * array. + */ + if (gs->identifier && + !strcmp("--", submodule_options.argv[i])) { + argv_array_push(&cp.args, oid_to_hex(gs->identifier)); + } + + argv_array_push(&cp.args, submodule_options.argv[i]); + } + + cp.git_cmd = 1; + cp.dir = gs->path; + + /* + * Capture output to output buffer and check the return code from the + * child process. A '0' indicates a hit, a '1' indicates no hit and + * anything else is an error. + */ + status = capture_command(&cp, &child_output, 0); + if (status && (status != 1)) { + /* flush the buffer */ + write_or_die(1, child_output.buf, child_output.len); + die("process for submodule '%s' failed with exit code: %d", + gs->name, status); + } + + opt->output(opt, child_output.buf, child_output.len); + strbuf_release(&child_output); + /* invert the return code to make a hit equal to 1 */ + return !status; +} + +/* + * Prep grep structures for a submodule grep + * oid: the oid of the submodule or NULL if using the working tree + * filename: name of the submodule including tree name of parent + * path: location of the submodule + */ +static int grep_submodule(struct grep_opt *opt, const struct object_id *oid, + const char *filename, const char *path) +{ + if (!is_submodule_active(the_repository, path)) + return 0; + if (!is_submodule_populated_gently(path, NULL)) { + /* + * If searching history, check for the presence of the + * submodule's gitdir before skipping the submodule. + */ + if (oid) { + const struct submodule *sub = + submodule_from_path(null_sha1, path); + if (sub) + path = git_path("modules/%s", sub->name); + + if (!(is_directory(path) && is_git_directory(path))) + return 0; + } else { + return 0; + } + } + +#ifndef NO_PTHREADS + if (num_threads) { + add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, oid); + return 0; + } else +#endif + { + struct grep_source gs; + int hit; + + grep_source_init(&gs, GREP_SOURCE_SUBMODULE, + filename, path, oid); + hit = grep_submodule_launch(opt, &gs); + + grep_source_clear(&gs); + return hit; + } +} + +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, + int cached) { int hit = 0; int nr; + struct strbuf name = STRBUF_INIT; + int name_base_len = 0; + if (super_prefix) { + name_base_len = strlen(super_prefix); + strbuf_addstr(&name, super_prefix); + } + read_cache(); for (nr = 0; nr < active_nr; nr++) { const struct cache_entry *ce = active_cache[nr]; - if (!S_ISREG(ce->ce_mode)) - continue; - if (!ce_path_match(ce, pathspec, NULL)) + strbuf_setlen(&name, name_base_len); + strbuf_addstr(&name, ce->name); + + if (S_ISREG(ce->ce_mode) && + match_pathspec(pathspec, name.buf, name.len, 0, NULL, + S_ISDIR(ce->ce_mode) || + S_ISGITLINK(ce->ce_mode))) { + /* + * If CE_VALID is on, we assume worktree file and its + * cache entry are identical, even if worktree file has + * been modified, so use cache version instead + */ + if (cached || (ce->ce_flags & CE_VALID) || + ce_skip_worktree(ce)) { + if (ce_stage(ce) || ce_intent_to_add(ce)) + continue; + hit |= grep_oid(opt, &ce->oid, ce->name, + 0, ce->name); + } else { + hit |= grep_file(opt, ce->name); + } + } else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && + submodule_path_match(pathspec, name.buf, NULL)) { + hit |= grep_submodule(opt, NULL, ce->name, ce->name); + } else { continue; - /* - * If CE_VALID is on, we assume worktree file and its cache entry - * are identical, even if worktree file has been modified, so use - * cache version instead - */ - if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) { - if (ce_stage(ce) || ce_intent_to_add(ce)) - continue; - hit |= grep_sha1(opt, ce->sha1, ce->name, 0, ce->name); } - else - hit |= grep_file(opt, ce->name); + if (ce_stage(ce)) { do { nr++; @@ -412,6 +737,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int if (hit && opt->status_only) break; } + + strbuf_release(&name); return hit; } @@ -423,12 +750,22 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, enum interesting match = entry_not_interesting; struct name_entry entry; int old_baselen = base->len; + struct strbuf name = STRBUF_INIT; + int name_base_len = 0; + if (super_prefix) { + strbuf_addstr(&name, super_prefix); + name_base_len = name.len; + } while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(&entry); if (match != all_entries_interesting) { - match = tree_entry_interesting(&entry, base, tn_len, pathspec); + strbuf_addstr(&name, base->buf + tn_len); + match = tree_entry_interesting(&entry, &name, + 0, pathspec); + strbuf_setlen(&name, name_base_len); + if (match == all_entries_not_interesting) break; if (match == entry_not_interesting) @@ -438,16 +775,15 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_add(base, entry.path, te_len); if (S_ISREG(entry.mode)) { - hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len, + hit |= grep_oid(opt, entry.oid, base->buf, tn_len, check_attr ? base->buf + tn_len : NULL); - } - else if (S_ISDIR(entry.mode)) { + } else if (S_ISDIR(entry.mode)) { enum object_type type; struct tree_desc sub; void *data; unsigned long size; - data = lock_and_read_sha1_file(entry.oid->hash, &type, &size); + data = lock_and_read_oid_file(entry.oid, &type, &size); if (!data) die(_("unable to read tree (%s)"), oid_to_hex(entry.oid)); @@ -457,12 +793,18 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, hit |= grep_tree(opt, pathspec, &sub, base, tn_len, check_attr); free(data); + } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { + hit |= grep_submodule(opt, entry.oid, base->buf, + base->buf + tn_len); } + strbuf_setlen(base, old_baselen); if (hit && opt->status_only) break; } + + strbuf_release(&name); return hit; } @@ -470,7 +812,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct object *obj, const char *name, const char *path) { if (obj->type == OBJ_BLOB) - return grep_sha1(opt, obj->oid.hash, name, 0, path); + return grep_oid(opt, &obj->oid, name, 0, path); if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { struct tree_desc tree; void *data; @@ -486,6 +828,10 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, if (!data) die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid)); + /* Use parent's name as base when recursing submodules */ + if (recurse_submodules && parent_basename) + name = parent_basename; + len = name ? strlen(name) : 0; strbuf_init(&base, PATH_MAX + len + 1); if (len) { @@ -512,6 +858,12 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); + + /* load the gitmodules file for this rev */ + if (recurse_submodules) { + submodule_free(); + gitmodules_config_sha1(real_obj->oid.hash); + } if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) { hit = 1; if (opt->status_only) @@ -533,7 +885,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, if (exc_std) setup_standard_excludes(&dir); - fill_directory(&dir, pathspec); + fill_directory(&dir, &the_index, pathspec); for (i = 0; i < dir.nr; i++) { if (!dir_path_match(dir.entries[i], pathspec, 0, NULL)) continue; @@ -640,6 +992,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) int dummy; int use_index = 1; int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; + int allow_revs; struct option options[] = { OPT_BOOL(0, "cached", &cached, @@ -650,6 +1003,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, N_("ignore files specified via '.gitignore'"), 1), + OPT_BOOL(0, "recurse-submodules", &recurse_submodules, + N_("recursively search in each submodule")), + OPT_STRING(0, "parent-basename", &parent_basename, + N_("basename"), + N_("prepend parent project's basename to output")), OPT_GROUP(""), OPT_BOOL('v', "invert-match", &opt.invert, N_("show non-matching lines")), @@ -754,6 +1112,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) init_grep_defaults(); git_config(grep_cmd_config, NULL); grep_init(&opt, prefix); + super_prefix = get_super_prefix(); /* * If there is no -- then the paths must exist in the working @@ -811,31 +1170,72 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!opt.pattern_list) die(_("no pattern given.")); - if (!opt.fixed && opt.ignore_case) - opt.regflags |= REG_ICASE; - compile_grep_patterns(&opt); + /* + * We have to find "--" in a separate pass, because its presence + * influences how we will parse arguments that come before it. + */ + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + seen_dashdash = 1; + break; + } + } - /* Check revs and then paths */ + /* + * Resolve any rev arguments. If we have a dashdash, then everything up + * to it must resolve as a rev. If not, then we stop at the first + * non-rev and assume everything else is a path. + */ + allow_revs = use_index && !untracked; for (i = 0; i < argc; i++) { const char *arg = argv[i]; - unsigned char sha1[20]; + struct object_id oid; struct object_context oc; - /* Is it a rev? */ - if (!get_sha1_with_context(arg, 0, sha1, &oc)) { - struct object *object = parse_object_or_die(sha1, arg); - if (!seen_dashdash) - verify_non_filename(prefix, arg); - add_object_array_with_path(object, arg, &list, oc.mode, oc.path); - continue; - } + struct object *object; + if (!strcmp(arg, "--")) { i++; - seen_dashdash = 1; + break; } - break; + + if (!allow_revs) { + if (seen_dashdash) + die(_("--no-index or --untracked cannot be used with revs")); + break; + } + + if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH, + oid.hash, &oc)) { + if (seen_dashdash) + die(_("unable to resolve revision: %s"), arg); + break; + } + + object = parse_object_or_die(&oid, arg); + if (!seen_dashdash) + verify_non_filename(prefix, arg); + add_object_array_with_path(object, arg, &list, oc.mode, oc.path); + free(oc.path); } + /* + * Anything left over is presumed to be a path. But in the non-dashdash + * "do what I mean" case, we verify and complain when that isn't true. + */ + if (!seen_dashdash) { + int j; + for (j = i; j < argc; j++) + verify_filename(prefix, argv[j], j == i && allow_revs); + } + + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_CWD | + (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), + prefix, argv + i); + pathspec.max_depth = opt.max_depth; + pathspec.recursive = 1; + #ifndef NO_PTHREADS if (list.nr || cached || show_in_pager) num_threads = 0; @@ -843,10 +1243,23 @@ int cmd_grep(int argc, const char **argv, const char *prefix) num_threads = GREP_NUM_THREADS_DEFAULT; else if (num_threads < 0) die(_("invalid number of threads specified (%d)"), num_threads); + if (num_threads == 1) + num_threads = 0; #else + if (num_threads) + warning(_("no threads support, ignoring --threads")); num_threads = 0; #endif + if (!num_threads) + /* + * The compiled patterns on the main path are only + * used when not using threading. Otherwise + * start_threads() below calls compile_grep_patterns() + * for each thread. + */ + compile_grep_patterns(&opt); + #ifndef NO_PTHREADS if (num_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count) @@ -857,20 +1270,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } #endif - /* The rest are paths */ - if (!seen_dashdash) { - int j; - for (j = i; j < argc; j++) - verify_filename(prefix, argv[j], j == i); + if (recurse_submodules) { + gitmodules_config(); + compile_submodule_options(&opt, argv + i, cached, untracked, + opt_exclude, use_index, + pattern_type_arg); } - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD | - (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), - prefix, argv + i); - pathspec.max_depth = opt.max_depth; - pathspec.recursive = 1; - if (show_in_pager && (cached || list.nr)) die(_("--open-files-in-pager only works on the worktree")); @@ -894,6 +1300,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } } + if (recurse_submodules && (!use_index || untracked)) + die(_("option not supported with --recurse-submodules.")); + if (!show_in_pager && !opt.status_only) setup_pager(); @@ -902,8 +1311,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!use_index || untracked) { int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; - if (list.nr) - die(_("--no-index or --untracked cannot be used with revs.")); hit = grep_directory(&opt, &pathspec, use_exclude, use_index); } else if (0 <= opt_exclude) { die(_("--[no-]exclude-standard cannot be used for tracked contents.")); @@ -922,6 +1329,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); + clear_pathspec(&pathspec); free_grep_patterns(&opt); return !hit; } |