+static int grep_cache(struct grep_opt *opt,
+ 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);
+
+static int grep_submodule(struct grep_opt *opt,
+ const struct pathspec *pathspec,
+ const struct object_id *oid,
+ const char *filename, const char *path, int cached)
+{
+ struct repository subrepo;
+ struct repository *superproject = opt->repo;
+ const struct submodule *sub = submodule_from_path(superproject,
+ &null_oid, path);
+ struct grep_opt subopt;
+ int hit;
+
+ /*
+ * NEEDSWORK: submodules functions need to be protected because they
+ * access the object store via config_from_gitmodules(): the latter
+ * uses get_oid() which, for now, relies on the global the_repository
+ * object.
+ */
+ grep_read_lock();
+
+ if (!is_submodule_active(superproject, path)) {
+ grep_read_unlock();
+ return 0;
+ }
+
+ if (repo_submodule_init(&subrepo, superproject, sub)) {
+ grep_read_unlock();
+ return 0;
+ }
+
+ repo_read_gitmodules(&subrepo);
+
+ /*
+ * 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.
+ */
+ add_to_alternates_memory(subrepo.objects->odb->path);
+ grep_read_unlock();
+
+ memcpy(&subopt, opt, sizeof(subopt));
+ subopt.repo = &subrepo;
+
+ if (oid) {
+ struct object *object;
+ struct tree_desc tree;
+ void *data;
+ unsigned long size;
+ struct strbuf base = STRBUF_INIT;
+
+ object = parse_object_or_die(oid, oid_to_hex(oid));
+
+ grep_read_lock();
+ data = read_object_with_reference(&subrepo,
+ &object->oid, tree_type,
+ &size, NULL);
+ grep_read_unlock();
+
+ if (!data)
+ die(_("unable to read tree (%s)"), oid_to_hex(&object->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);
+ strbuf_release(&base);
+ free(data);
+ } else {
+ hit = grep_cache(&subopt, pathspec, cached);
+ }
+
+ repo_clear(&subrepo);
+ return hit;
+}
+
+static int grep_cache(struct grep_opt *opt,
+ const struct pathspec *pathspec, int cached)