]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/grep.c
object-name.h: move declarations for object-name.c functions from cache.h
[thirdparty/git.git] / builtin / grep.c
index ab8822e68f42dab702d096f28b88c492e6a3f4ef..f66e14389e145eb0b292d5a7ab45bf8137e8eb1a 100644 (file)
@@ -3,8 +3,10 @@
  *
  * Copyright (c) 2006 Junio C Hamano
  */
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "alloc.h"
+#include "gettext.h"
+#include "hex.h"
 #include "repository.h"
 #include "config.h"
 #include "blob.h"
 #include "quote.h"
 #include "dir.h"
 #include "pathspec.h"
+#include "setup.h"
 #include "submodule.h"
 #include "submodule-config.h"
+#include "object-name.h"
 #include "object-store.h"
 #include "packfile.h"
+#include "write-or-die.h"
+
+static const char *grep_prefix;
 
 static char const * const grep_usage[] = {
        N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"),
@@ -65,6 +72,9 @@ static int todo_done;
 /* Has all work items been added? */
 static int all_work_added;
 
+static struct repository **repos_to_free;
+static size_t repos_to_free_nr, repos_to_free_alloc;
+
 /* This lock protects all the variables above. */
 static pthread_mutex_t grep_mutex;
 
@@ -168,6 +178,19 @@ static void work_done(struct work_item *w)
        grep_unlock();
 }
 
+static void free_repos(void)
+{
+       int i;
+
+       for (i = 0; i < repos_to_free_nr; i++) {
+               repo_clear(repos_to_free[i]);
+               free(repos_to_free[i]);
+       }
+       FREE_AND_NULL(repos_to_free);
+       repos_to_free_nr = 0;
+       repos_to_free_alloc = 0;
+}
+
 static void *run(void *arg)
 {
        int hit = 0;
@@ -183,8 +206,8 @@ static void *run(void *arg)
                grep_source_clear_data(&w->source);
                work_done(w);
        }
-       free_grep_patterns(arg);
-       free(arg);
+       free_grep_patterns(opt);
+       free(opt);
 
        return (void*) (intptr_t) hit;
 }
@@ -268,7 +291,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_color_default_config(var, value, NULL) < 0)
                st = -1;
 
        if (!strcmp(var, "grep.threads")) {
@@ -299,11 +322,11 @@ static void grep_source_name(struct grep_opt *opt, const char *filename,
        strbuf_reset(out);
 
        if (opt->null_following_name) {
-               if (opt->relative && opt->prefix_length) {
+               if (opt->relative && grep_prefix) {
                        struct strbuf rel_buf = STRBUF_INIT;
                        const char *rel_name =
                                relative_path(filename + tree_name_len,
-                                             opt->prefix, &rel_buf);
+                                             grep_prefix, &rel_buf);
 
                        if (tree_name_len)
                                strbuf_add(out, filename, tree_name_len);
@@ -316,8 +339,8 @@ static void grep_source_name(struct grep_opt *opt, const char *filename,
                return;
        }
 
-       if (opt->relative && opt->prefix_length)
-               quote_path(filename + tree_name_len, opt->prefix, out, 0);
+       if (opt->relative && grep_prefix)
+               quote_path(filename + tree_name_len, grep_prefix, out, 0);
        else
                quote_c_style(filename + tree_name_len, out, NULL, 0);
 
@@ -333,7 +356,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
        struct grep_source gs;
 
        grep_source_name(opt, filename, tree_name_len, &pathbuf);
-       grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
+       grep_source_init_oid(&gs, pathbuf.buf, path, oid, opt->repo);
        strbuf_release(&pathbuf);
 
        if (num_threads > 1) {
@@ -359,7 +382,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
        struct grep_source gs;
 
        grep_source_name(opt, filename, 0, &buf);
-       grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
+       grep_source_init_file(&gs, buf.buf, filename);
        strbuf_release(&buf);
 
        if (num_threads > 1) {
@@ -385,7 +408,7 @@ static void append_path(struct grep_opt *opt, const void *data, size_t len)
 
        if (len == 1 && *(const char *)data == '\0')
                return;
-       string_list_append(path_list, xstrndup(data, len));
+       string_list_append_nodup(path_list, xstrndup(data, len));
 }
 
 static void run_pager(struct grep_opt *opt, const char *prefix)
@@ -415,19 +438,21 @@ static int grep_submodule(struct grep_opt *opt,
                          const struct object_id *oid,
                          const char *filename, const char *path, int cached)
 {
-       struct repository subrepo;
+       struct repository *subrepo;
        struct repository *superproject = opt->repo;
-       const struct submodule *sub;
        struct grep_opt subopt;
-       int hit;
-
-       sub = submodule_from_path(superproject, null_oid(), path);
+       int hit = 0;
 
        if (!is_submodule_active(superproject, path))
                return 0;
 
-       if (repo_submodule_init(&subrepo, superproject, sub))
+       subrepo = xmalloc(sizeof(*subrepo));
+       if (repo_submodule_init(subrepo, superproject, path, null_oid())) {
+               free(subrepo);
                return 0;
+       }
+       ALLOC_GROW(repos_to_free, repos_to_free_nr + 1, repos_to_free_alloc);
+       repos_to_free[repos_to_free_nr++] = subrepo;
 
        /*
         * NEEDSWORK: repo_read_gitmodules() might call
@@ -438,53 +463,76 @@ static int grep_submodule(struct grep_opt *opt,
         * subrepo's odbs to the in-memory alternates list.
         */
        obj_read_lock();
-       repo_read_gitmodules(&subrepo, 0);
 
        /*
-        * 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.
+        * NEEDSWORK: when reading a submodule, the sparsity settings in the
+        * superproject are incorrectly forgotten or misused. For example:
+        *
+        * 1. "command_requires_full_index"
+        *      When this setting is turned on for `grep`, only the superproject
+        *      knows it. All the submodules are read with their own configs
+        *      and get prepare_repo_settings()'d. Therefore, these submodules
+        *      "forget" the sparse-index feature switch. As a result, the index
+        *      of these submodules are expanded unexpectedly.
+        *
+        * 2. "core_apply_sparse_checkout"
+        *      When running `grep` in the superproject, this setting is
+        *      populated using the superproject's configs. However, once
+        *      initialized, this config is globally accessible and is read by
+        *      prepare_repo_settings() for the submodules. For instance, if a
+        *      submodule is using a sparse-checkout, however, the superproject
+        *      is not, the result is that the config from the superproject will
+        *      dictate the behavior for the submodule, making it "forget" its
+        *      sparse-checkout state.
+        *
+        * 3. "core_sparse_checkout_cone"
+        *      ditto.
+        *
+        * Note that this list is not exhaustive.
+        */
+       repo_read_gitmodules(subrepo, 0);
+
+       /*
+        * All code paths tested by test code no longer need submodule ODBs to
+        * be added as alternates, but add it to the list just in case.
+        * Submodule ODBs added through add_submodule_odb_by_path() will be
+        * lazily registered as alternates when needed (and except in an
+        * unexpected code interaction, it won't be needed).
         */
-       add_to_alternates_memory(subrepo.objects->odb->path);
+       add_submodule_odb_by_path(subrepo->objects->odb->path);
        obj_read_unlock();
 
        memcpy(&subopt, opt, sizeof(subopt));
-       subopt.repo = &subrepo;
+       subopt.repo = subrepo;
 
        if (oid) {
-               struct object *object;
+               enum object_type object_type;
                struct tree_desc tree;
                void *data;
                unsigned long size;
                struct strbuf base = STRBUF_INIT;
 
                obj_read_lock();
-               object = parse_object_or_die(oid, NULL);
+               object_type = oid_object_info(subrepo, oid, NULL);
                obj_read_unlock();
-               data = read_object_with_reference(&subrepo,
-                                                 &object->oid, tree_type,
+               data = read_object_with_reference(subrepo,
+                                                 oid, OBJ_TREE,
                                                  &size, NULL);
                if (!data)
-                       die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
+                       die(_("unable to read tree (%s)"), oid_to_hex(oid));
 
                strbuf_addstr(&base, filename);
                strbuf_addch(&base, '/');
 
                init_tree_desc(&tree, data, size);
                hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
-                               object->type == OBJ_COMMIT);
+                               object_type == OBJ_COMMIT);
                strbuf_release(&base);
                free(data);
        } else {
                hit = grep_cache(&subopt, pathspec, cached);
        }
 
-       repo_clear(&subrepo);
        return hit;
 }
 
@@ -504,8 +552,6 @@ static int grep_cache(struct grep_opt *opt,
        if (repo_read_index(repo) < 0)
                die(_("index file corrupt"));
 
-       /* TODO: audit for interaction with sparse-index. */
-       ensure_full_index(repo->index);
        for (nr = 0; nr < repo->index->cache_nr; nr++) {
                const struct cache_entry *ce = repo->index->cache[nr];
 
@@ -514,8 +560,21 @@ static int grep_cache(struct grep_opt *opt,
 
                strbuf_setlen(&name, name_base_len);
                strbuf_addstr(&name, ce->name);
+               if (S_ISSPARSEDIR(ce->ce_mode)) {
+                       enum object_type type;
+                       struct tree_desc tree;
+                       void *data;
+                       unsigned long size;
 
-               if (S_ISREG(ce->ce_mode) &&
+                       data = repo_read_object_file(the_repository, &ce->oid,
+                                                    &type, &size);
+                       init_tree_desc(&tree, data, size);
+
+                       hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
+                       strbuf_setlen(&name, name_base_len);
+                       strbuf_addstr(&name, ce->name);
+                       free(data);
+               } else if (S_ISREG(ce->ce_mode) &&
                    match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL,
                                   S_ISDIR(ce->ce_mode) ||
                                   S_ISGITLINK(ce->ce_mode))) {
@@ -598,7 +657,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = read_object_file(&entry.oid, &type, &size);
+                       data = repo_read_object_file(the_repository,
+                                                    &entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
                                    oid_to_hex(&entry.oid));
@@ -637,7 +697,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                int hit, len;
 
                data = read_object_with_reference(opt->repo,
-                                                 &obj->oid, tree_type,
+                                                 &obj->oid, OBJ_TREE,
                                                  &size, NULL);
                if (!data)
                        die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
@@ -704,10 +764,9 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
 static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
                          int exc_std, int use_index)
 {
-       struct dir_struct dir;
+       struct dir_struct dir = DIR_INIT;
        int i, hit = 0;
 
-       dir_init(&dir);
        if (!use_index)
                dir.flags |= DIR_NO_GITLINKS;
        if (exc_std)
@@ -826,11 +885,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        struct grep_opt opt;
        struct object_array list = OBJECT_ARRAY_INIT;
        struct pathspec pathspec;
-       struct string_list path_list = STRING_LIST_INIT_NODUP;
+       struct string_list path_list = STRING_LIST_INIT_DUP;
        int i;
        int dummy;
        int use_index = 1;
-       int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
        int allow_revs;
 
        struct option options[] = {
@@ -864,16 +922,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
                        NULL, 1 },
                OPT_GROUP(""),
-               OPT_SET_INT('E', "extended-regexp", &pattern_type_arg,
+               OPT_SET_INT('E', "extended-regexp", &opt.pattern_type_option,
                            N_("use extended POSIX regular expressions"),
                            GREP_PATTERN_TYPE_ERE),
-               OPT_SET_INT('G', "basic-regexp", &pattern_type_arg,
+               OPT_SET_INT('G', "basic-regexp", &opt.pattern_type_option,
                            N_("use basic POSIX regular expressions (default)"),
                            GREP_PATTERN_TYPE_BRE),
-               OPT_SET_INT('F', "fixed-strings", &pattern_type_arg,
+               OPT_SET_INT('F', "fixed-strings", &opt.pattern_type_option,
                            N_("interpret patterns as fixed strings"),
                            GREP_PATTERN_TYPE_FIXED),
-               OPT_SET_INT('P', "perl-regexp", &pattern_type_arg,
+               OPT_SET_INT('P', "perl-regexp", &opt.pattern_type_option,
                            N_("use Perl-compatible regular expressions"),
                            GREP_PATTERN_TYPE_PCRE),
                OPT_GROUP(""),
@@ -947,11 +1005,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
                           N_("allow calling of grep(1) (ignored by this build)"),
                           PARSE_OPT_NOCOMPLETE),
+               OPT_INTEGER('m', "max-count", &opt.max_count,
+                       N_("maximum number of results per file")),
                OPT_END()
        };
+       grep_prefix = prefix;
 
-       git_config(grep_cmd_config, NULL);
-       grep_init(&opt, the_repository, prefix);
+       grep_init(&opt, the_repository);
+       git_config(grep_cmd_config, &opt);
 
        /*
         * If there is no -- then the paths must exist in the working
@@ -966,7 +1027,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, grep_usage,
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION);
-       grep_commit_pattern_type(pattern_type_arg, &opt);
+
+       if (the_repository->gitdir) {
+               prepare_repo_settings(the_repository);
+               the_repository->settings.command_requires_full_index = 0;
+       }
 
        if (use_index && !startup_info->have_repository) {
                int fallback = 0;
@@ -1087,6 +1152,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        if (recurse_submodules && untracked)
                die(_("--untracked not supported with --recurse-submodules"));
 
+       /*
+        * Optimize out the case where the amount of matches is limited to zero.
+        * We do this to keep results consistent with GNU grep(1).
+        */
+       if (opt.max_count == 0)
+               return 1;
+
        if (show_in_pager) {
                if (num_threads > 1)
                        warning(_("invalid option combination, ignoring --threads"));
@@ -1146,19 +1218,17 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        strbuf_addf(&buf, "+/%s%s",
                                        strcmp("less", pager) ? "" : "*",
                                        opt.pattern_list->pattern);
-                       string_list_append(&path_list,
-                                          strbuf_detach(&buf, NULL));
+                       string_list_append_nodup(&path_list,
+                                                strbuf_detach(&buf, NULL));
                }
        }
 
        if (!show_in_pager && !opt.status_only)
                setup_pager();
 
-       if (!use_index && (untracked || cached))
-               die(_("--cached or --untracked cannot be used with --no-index"));
-
-       if (untracked && cached)
-               die(_("--untracked cannot be used with --cached"));
+       die_for_incompatible_opt3(!use_index, "--no-index",
+                                 untracked, "--untracked",
+                                 cached, "--cached");
 
        if (!use_index || untracked) {
                int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
@@ -1182,6 +1252,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        if (hit && show_in_pager)
                run_pager(&opt, prefix);
        clear_pathspec(&pathspec);
+       string_list_clear(&path_list, 0);
        free_grep_patterns(&opt);
+       object_array_clear(&list);
+       free_repos();
        return !hit;
 }