Merge branch 'km/delete-ref-reflog-message'
[gitweb.git] / builtin / grep.c
index dca0be6475cb4e51bffce97757b80f5109cefa6c..9304c33e75051baa65ee02d1daa03c361e25339e 100644 (file)
@@ -19,6 +19,7 @@
 #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>...]"),
@@ -28,6 +29,7 @@ static char const * const grep_usage[] = {
 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);
@@ -534,19 +536,54 @@ static int grep_submodule_launch(struct grep_opt *opt,
 {
        struct child_process cp = CHILD_PROCESS_INIT;
        int status, i;
+       const char *end_of_base;
+       const char *name;
        struct work_item *w = opt->output_priv;
 
+       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);
 
        /* Add super prefix */
        argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
                         super_prefix ? super_prefix : "",
-                        gs->name);
+                        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 SHA1.
+        */
+       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++)
+       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
+                * sha1 before pushing the '--' onto the child process argv
+                * array.
+                */
+               if (gs->identifier &&
+                   !strcmp("--", submodule_options.argv[i])) {
+                       argv_array_push(&cp.args, sha1_to_hex(gs->identifier));
+               }
+
                argv_array_push(&cp.args, submodule_options.argv[i]);
+       }
 
        cp.git_cmd = 1;
        cp.dir = gs->path;
@@ -579,8 +616,23 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
 {
        if (!is_submodule_initialized(path))
                return 0;
-       if (!is_submodule_populated(path))
-               return 0;
+       if (!is_submodule_populated(path)) {
+               /*
+                * If searching history, check for the presense of the
+                * submodule's gitdir before skipping the submodule.
+                */
+               if (sha1) {
+                       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) {
@@ -673,12 +725,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)
@@ -690,8 +752,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                if (S_ISREG(entry.mode)) {
                        hit |= grep_sha1(opt, entry.oid->hash, 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;
@@ -707,12 +768,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->hash, base->buf,
+                                             base->buf + tn_len);
                }
+
                strbuf_setlen(base, old_baselen);
 
                if (hit && opt->status_only)
                        break;
        }
+
+       strbuf_release(&name);
        return hit;
 }
 
@@ -736,6 +803,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) {
@@ -762,6 +833,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)
@@ -890,6 +967,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,
@@ -902,6 +980,9 @@ 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_("recursivley 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")),
@@ -1069,26 +1150,69 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 
        compile_grep_patterns(&opt);
 
-       /* Check revs and then paths */
+       /*
+        * 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;
+               }
+       }
+
+       /*
+        * 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_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, 0, sha1, &oc)) {
+                       if (seen_dashdash)
+                               die(_("unable to resolve revision: %s"), arg);
+                       break;
+               }
+
+               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);
        }
 
+       /*
+        * 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;
@@ -1110,20 +1234,6 @@ 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);
-       }
-
-       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 (recurse_submodules) {
                gitmodules_config();
                compile_submodule_options(&opt, &pathspec, cached, untracked,
@@ -1154,7 +1264,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (recurse_submodules && (!use_index || untracked || list.nr))
+       if (recurse_submodules && (!use_index || untracked))
                die(_("option not supported with --recurse-submodules."));
 
        if (!show_in_pager && !opt.status_only)
@@ -1165,8 +1275,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."));