diff options
Diffstat (limited to 'builtin/grep.c')
-rw-r--r-- | builtin/grep.c | 457 |
1 files changed, 121 insertions, 336 deletions
diff --git a/builtin/grep.c b/builtin/grep.c index 3cbee04dc4..ee753a403e 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -22,19 +22,14 @@ #include "pathspec.h" #include "submodule.h" #include "submodule-config.h" +#include "object-store.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; @@ -98,8 +93,7 @@ static pthread_cond_t cond_result; static int skip_first_line; -static void add_work(struct grep_opt *opt, enum grep_source_type type, - const char *name, const char *path, const void *id) +static void add_work(struct grep_opt *opt, const struct grep_source *gs) { grep_lock(); @@ -107,7 +101,7 @@ static void add_work(struct grep_opt *opt, enum grep_source_type type, pthread_cond_wait(&cond_write, &grep_mutex); } - grep_source_init(&todo[todo_end].source, type, name, path, id); + todo[todo_end].source = *gs; if (opt->binary != GREP_BINARY_TEXT) grep_source_load_driver(&todo[todo_end].source); todo[todo_end].done = 0; @@ -186,10 +180,7 @@ static void *run(void *arg) break; opt->output_priv = w; - if (w->source.type == GREP_SOURCE_SUBMODULE) - hit |= grep_submodule_launch(opt, &w->source); - else - hit |= grep_source(opt, &w->source); + hit |= grep_source(opt, &w->source); grep_source_clear_data(&w->source); work_done(w); } @@ -284,7 +275,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_default_config(var, value, cb) < 0) + if (git_color_default_config(var, value, cb) < 0) st = -1; if (!strcmp(var, "grep.threads")) { @@ -316,7 +307,7 @@ static void *lock_and_read_oid_file(const struct object_id *oid, enum object_typ void *data; grep_read_lock(); - data = read_sha1_file(oid->hash, type, size); + data = read_object_file(oid, type, size); grep_read_unlock(); return data; } @@ -326,35 +317,31 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, const char *path) { struct strbuf pathbuf = STRBUF_INIT; + struct grep_source gs; - if (super_prefix) { - strbuf_add(&pathbuf, filename, tree_name_len); - strbuf_addstr(&pathbuf, super_prefix); - strbuf_addstr(&pathbuf, filename + tree_name_len); + if (opt->relative && opt->prefix_length) { + quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf); + strbuf_insert(&pathbuf, 0, 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); - } + grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); + strbuf_release(&pathbuf); #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid); - strbuf_release(&pathbuf); + /* + * add_work() copies gs and thus assumes ownership of + * its fields, so do not call grep_source_clear() + */ + add_work(opt, &gs); return 0; } else #endif { - struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); - strbuf_release(&pathbuf); hit = grep_source(opt, &gs); grep_source_clear(&gs); @@ -365,30 +352,29 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid, static int grep_file(struct grep_opt *opt, const char *filename) { struct strbuf buf = STRBUF_INIT; + struct grep_source gs; - if (super_prefix) - strbuf_addstr(&buf, super_prefix); - strbuf_addstr(&buf, filename); + if (opt->relative && opt->prefix_length) + quote_path_relative(filename, opt->prefix, &buf); + else + 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); - } + grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); + strbuf_release(&buf); #ifndef NO_PTHREADS if (num_threads) { - add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename); - strbuf_release(&buf); + /* + * add_work() copies gs and thus assumes ownership of + * its fields, so do not call grep_source_clear() + */ + add_work(opt, &gs); return 0; } else #endif { - struct grep_source gs; int hit; - grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); - strbuf_release(&buf); hit = grep_source(opt, &gs); grep_source_clear(&gs); @@ -421,284 +407,92 @@ static void run_pager(struct grep_opt *opt, const char *prefix) exit(status); } -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); -} +static int grep_cache(struct grep_opt *opt, struct repository *repo, + const struct pathspec *pathspec, int cached); +static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, + struct tree_desc *tree, struct strbuf *base, int tn_len, + int check_attr, struct repository *repo); -/* - * Launch child process to grep contents of a submodule - */ -static int grep_submodule_launch(struct grep_opt *opt, - const struct grep_source *gs) +static int grep_submodule(struct grep_opt *opt, struct repository *superproject, + const struct pathspec *pathspec, + const struct object_id *oid, + const char *filename, const char *path) { - 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; + struct repository submodule; + int hit; - prepare_submodule_repo_env(&cp.env_array); - argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT); + if (!is_submodule_active(superproject, path)) + return 0; - if (opt->relative && opt->prefix_length) - argv_array_pushf(&cp.env_array, "%s=%s", - GIT_TOPLEVEL_PREFIX_ENVIRONMENT, - opt->prefix); + if (repo_submodule_init(&submodule, superproject, path)) + return 0; - /* Add super prefix */ - argv_array_pushf(&cp.args, "--super-prefix=%s%s/", - super_prefix ? super_prefix : "", - name); - argv_array_push(&cp.args, "grep"); + repo_read_gitmodules(&submodule); /* - * 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. + * NEEDSWORK: This adds the submodule's object directory to the list of + * alternates for the single in-memory object store. This has some bad + * consequences for memory (processed objects will never be freed) and + * performance (this increases the number of pack files git has to pay + * attention to, to the sum of the number of pack files in all the + * repositories processed so far). This can be removed once the object + * store is no longer global and instead is a member of the repository + * object. */ - if (gs->identifier && end_of_base) - argv_array_pushf(&cp.args, "--parent-basename=%.*s", - (int) (end_of_base - gs->name), - gs->name); + grep_read_lock(); + add_to_alternates_memory(submodule.objects->objectdir); + grep_read_unlock(); - /* 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)); - } + if (oid) { + struct object *object; + struct tree_desc tree; + void *data; + unsigned long size; + struct strbuf base = STRBUF_INIT; - argv_array_push(&cp.args, submodule_options.argv[i]); - } + object = parse_object_or_die(oid, oid_to_hex(oid)); - cp.git_cmd = 1; - cp.dir = gs->path; + grep_read_lock(); + data = read_object_with_reference(&object->oid, tree_type, + &size, NULL); + grep_read_unlock(); - /* - * 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); - } + if (!data) + die(_("unable to read tree (%s)"), oid_to_hex(&object->oid)); - 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; -} + strbuf_addstr(&base, filename); + strbuf_addch(&base, '/'); -/* - * 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_oid, path); - if (sub) - path = git_path("modules/%s", sub->name); - - if (!(is_directory(path) && is_git_directory(path))) - return 0; - } else { - return 0; - } + init_tree_desc(&tree, data, size); + hit = grep_tree(opt, pathspec, &tree, &base, base.len, + object->type == OBJ_COMMIT, &submodule); + strbuf_release(&base); + free(data); + } else { + hit = grep_cache(opt, &submodule, pathspec, 1); } -#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; - } + repo_clear(&submodule); + return hit; } -static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, - int cached) +static int grep_cache(struct grep_opt *opt, struct repository *repo, + 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); + if (repo->submodule_prefix) { + name_base_len = strlen(repo->submodule_prefix); + strbuf_addstr(&name, repo->submodule_prefix); } - read_cache(); + if (repo_read_index(repo) < 0) + die("index file corrupt"); - for (nr = 0; nr < active_nr; nr++) { - const struct cache_entry *ce = active_cache[nr]; + for (nr = 0; nr < repo->index->cache_nr; nr++) { + const struct cache_entry *ce = repo->index->cache[nr]; strbuf_setlen(&name, name_base_len); strbuf_addstr(&name, ce->name); @@ -715,14 +509,14 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, 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); + hit |= grep_oid(opt, &ce->oid, name.buf, + 0, name.buf); } else { - hit |= grep_file(opt, ce->name); + hit |= grep_file(opt, name.buf); } } 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); + hit |= grep_submodule(opt, repo, pathspec, NULL, ce->name, ce->name); } else { continue; } @@ -730,8 +524,8 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, if (ce_stage(ce)) { do { nr++; - } while (nr < active_nr && - !strcmp(ce->name, active_cache[nr]->name)); + } while (nr < repo->index->cache_nr && + !strcmp(ce->name, repo->index->cache[nr]->name)); nr--; /* compensate for loop control */ } if (hit && opt->status_only) @@ -744,7 +538,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, struct strbuf *base, int tn_len, - int check_attr) + int check_attr, struct repository *repo) { int hit = 0; enum interesting match = entry_not_interesting; @@ -752,8 +546,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, int old_baselen = base->len; struct strbuf name = STRBUF_INIT; int name_base_len = 0; - if (super_prefix) { - strbuf_addstr(&name, super_prefix); + if (repo->submodule_prefix) { + strbuf_addstr(&name, repo->submodule_prefix); name_base_len = name.len; } @@ -791,11 +585,11 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); hit |= grep_tree(opt, pathspec, &sub, base, tn_len, - check_attr); + check_attr, repo); free(data); } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { - hit |= grep_submodule(opt, entry.oid, base->buf, - base->buf + tn_len); + hit |= grep_submodule(opt, repo, pathspec, entry.oid, + base->buf, base->buf + tn_len); } strbuf_setlen(base, old_baselen); @@ -821,17 +615,13 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, int hit, len; grep_read_lock(); - data = read_object_with_reference(obj->oid.hash, tree_type, + data = read_object_with_reference(&obj->oid, tree_type, &size, NULL); grep_read_unlock(); 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) { @@ -840,12 +630,12 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, } init_tree_desc(&tree, data, size); hit = grep_tree(opt, pathspec, &tree, &base, base.len, - obj->type == OBJ_COMMIT); + obj->type == OBJ_COMMIT, the_repository); strbuf_release(&base); free(data); return hit; } - die(_("unable to grep from object of type %s"), typename(obj->type)); + die(_("unable to grep from object of type %s"), type_name(obj->type)); } static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, @@ -861,10 +651,11 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, /* load the gitmodules file for this rev */ if (recurse_submodules) { - submodule_free(); + submodule_free(the_repository); gitmodules_config_oid(&real_obj->oid); } - if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path)) { + if (grep_object(opt, pathspec, real_obj, list->objects[i].name, + list->objects[i].path)) { hit = 1; if (opt->status_only) break; @@ -1005,9 +796,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) 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")), @@ -1051,8 +839,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_BOOL('L', "files-without-match", &opt.unmatch_name_only, N_("show only the names of files without match")), - OPT_BOOL('z', "null", &opt.null_following_name, - N_("print NUL after filenames")), + OPT_BOOL_F('z', "null", &opt.null_following_name, + N_("print NUL after filenames"), + PARSE_OPT_NOCOMPLETE), OPT_BOOL('c', "count", &opt.count, N_("show the number of matches instead of matching lines")), OPT__COLOR(&opt.color, N_("highlight matches")), @@ -1097,22 +886,23 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("indicate hit with exit status without output")), OPT_BOOL(0, "all-match", &opt.all_match, N_("show only matches from files that match all patterns")), - { OPTION_SET_INT, 0, "debug", &opt.debug, NULL, - N_("show parse tree for grep expression"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 }, + OPT_SET_INT_F(0, "debug", &opt.debug, + N_("show parse tree for grep expression"), + 1, PARSE_OPT_HIDDEN), OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), - PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, - OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored, - N_("allow calling of grep(1) (ignored by this build)")), + PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE, + NULL, (intptr_t)default_pager }, + OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored, + N_("allow calling of grep(1) (ignored by this build)"), + PARSE_OPT_NOCOMPLETE), OPT_END() }; 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 @@ -1235,6 +1025,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) prefix, argv + i); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; + pathspec.recurse_submodules = !!recurse_submodules; #ifndef NO_PTHREADS if (list.nr || cached || show_in_pager) @@ -1270,13 +1061,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } #endif - if (recurse_submodules) { - gitmodules_config(); - compile_submodule_options(&opt, argv + i, cached, untracked, - opt_exclude, use_index, - pattern_type_arg); - } - if (show_in_pager && (cached || list.nr)) die(_("--open-files-in-pager only works on the worktree")); @@ -1318,10 +1102,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!cached) setup_work_tree(); - hit = grep_cache(&opt, &pathspec, cached); + hit = grep_cache(&opt, the_repository, &pathspec, cached); } else { if (cached) die(_("both --cached and trees are given.")); + hit = grep_objects(&opt, &pathspec, &list); } |