git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[RFC/PATCHv2 5/5] grep: add support for grepping in submodules

From
Chris Packham <judge.packham@gmail.com>
Date
Oct 15, 2010, 23:26 UTC
Message-ID
<1287185204-843-6-git-send-email-judge.packham@gmail.com>
In-Reply-To
<1287185204-843-1-git-send-email-judge.packham@gmail.com>

When the --recurse-submodules option is given git grep will search in submodules as they are encountered.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
The refspec from the super project is passed as an environment variable
along with the GIT_DIR and GIT_WORK_TREE. We re-build a command line for
the sub-process grep which works for the basic cases but I need to add
some more complex tests to ensure everything gets passed through
correctly.
    
At the moment I don't alter the pathspec or max-depth option but I'll do
so in a future re-roll.
 builtin/grep.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 grep.h         |    1 +
 2 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/builtin/grep.c b/builtin/grep.c
index 251c4e7..7bcdf05 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -587,6 +587,91 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
 	free(argv);
 }
 
+static int pattern_list_to_argv(struct grep_opt *opt, const char **argv, int len)
+{
+	int i = 0;
+	struct grep_pat *p = opt->pattern_list;
+	while(p) {
+		if (i > len)
+			die("grep: not enough space for subprocess args");
+		if (p->token == GREP_PATTERN)
+			argv[i++] = "-e";
+		argv[i++] = p->pattern;
+		p = p->next;
+	}
+	return i;
+}
+
+static const char **create_sub_grep_argv(struct grep_opt *opt,
+		const char *path, const char *sha1, const char *tree_name)
+{
+	#define NUM_ARGS 10
+	struct strbuf buf = STRBUF_INIT;
+	const char **argv;
+	int i = 0;
+
+	argv = xcalloc(NUM_ARGS, sizeof(const char *));
+	argv[i++] = "grep";
+
+	if (opt->linenum)
+		argv[i++] = "-n";
+	if (opt->invert)
+		argv[i++] = "-v";
+	if (opt->ignore_case)
+		argv[i++] = "-i";
+	if (opt->count)
+		argv[i++] = "-c";
+	if (opt->name_only)
+		argv[i++] = "-l";
+	if (opt->recurse_submodules)
+		argv[i++] = "--recursive";
+
+	i += pattern_list_to_argv(opt, &argv[i], NUM_ARGS-(i+1));
+	if (sha1) {
+		argv[i++] = sha1;
+	}
+	argv[i++] = NULL;
+
+	strbuf_release(&buf);
+	return argv;
+}
+
+static int grep_submodule(struct grep_opt *opt, const char *path,
+			  const char *sha1, const char *tree_name)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf pre_buf = STRBUF_INIT;
+	struct child_process cp;
+	const char **argv = create_sub_grep_argv(opt, path, sha1, tree_name);
+	const char *git_dir;
+	int hit = 0;
+	memset(&cp, 0, sizeof(cp));
+
+	strbuf_addf(&buf, "%s/.git", path);
+	git_dir = read_gitfile_gently(buf.buf);
+	if (!git_dir)
+		git_dir = buf.buf;
+	if (!is_directory(git_dir))
+		goto out_free;
+
+	setenv("GIT_SUPER_REFNAME", tree_name, 1);
+	setenv(GIT_DIR_ENVIRONMENT, git_dir, 1);
+	setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
+	cp.argv = argv;
+	cp.git_cmd = 1;
+	cp.no_stdin = 1;
+	if (run_command(&cp) == 0)
+		hit = 1;
+out_free:
+	unsetenv("GIT_SUPER_REFNAME");
+	unsetenv(GIT_DIR_ENVIRONMENT);
+	unsetenv(GIT_WORK_TREE_ENVIRONMENT);
+	free(argv);
+	strbuf_release(&buf);
+	strbuf_release(&pre_buf);
+	return hit;
+}
+
 static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
 {
 	int hit = 0;
@@ -597,6 +682,10 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
 		struct cache_entry *ce = active_cache[nr];
 		if (!pathspec_matches(paths, ce->name, opt->max_depth))
 			continue;
+		if (S_ISGITLINK(ce->ce_mode) && opt->recurse_submodules) {
+			hit |= grep_submodule(opt, ce->name, NULL, NULL);
+			continue;
+		}
 		if (!S_ISREG(ce->ce_mode))
 			continue;
 		/*
@@ -634,11 +723,16 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
 	char *down;
 	int tn_len = strlen(tree_name);
 	struct strbuf pathbuf;
+	const char *refname = getenv("GIT_SUPER_REFNAME");
+	int rn_len = refname ? strlen(refname) : 0;
 
-	strbuf_init(&pathbuf, PATH_MAX + tn_len);
+	strbuf_init(&pathbuf, PATH_MAX + MAX(tn_len, rn_len));
 
 	if (tn_len) {
-		strbuf_add(&pathbuf, tree_name, tn_len);
+		if (refname)
+			strbuf_add(&pathbuf, refname, rn_len);
+		else
+			strbuf_add(&pathbuf, tree_name, tn_len);
 		strbuf_addch(&pathbuf, ':');
 		tn_len = pathbuf.len;
 	}
@@ -664,6 +758,9 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
 			;
 		else if (S_ISREG(entry.mode))
 			hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
+		else if (S_ISGITLINK(entry.mode) && opt->recurse_submodules)
+			hit |= grep_submodule(opt, entry.path,
+				sha1_to_hex(entry.sha1), tree_name);
 		else if (S_ISDIR(entry.mode)) {
 			enum object_type type;
 			struct tree_desc sub;
@@ -931,6 +1028,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			    "allow calling of grep(1) (ignored by this build)"),
 		{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
 		  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
+		OPT_BOOLEAN(0, "recursive", &opt.recurse_submodules,
+			"recurse into submodules"),
 		OPT_END()
 	};
 
diff --git a/grep.h b/grep.h
index 06621fe..d5e2e11 100644
--- a/grep.h
+++ b/grep.h
@@ -101,6 +101,7 @@ struct grep_opt {
 	unsigned post_context;
 	unsigned last_shown;
 	int show_hunk_mark;
+	int recurse_submodules;
 	void *priv;
 
 	void (*output)(struct grep_opt *opt, const void *data, size_t size);
-- 
1.7.3.1
Previous: Chris PackhamNext: Nguyen Thai Ngoc Duy
Message 10 of 15 in “[RGC/PATCHv2] grep: submodule support”
  1. Chris PackhamOct 15, 2010
  2. 1/5 worktree: provide better prefix to go back to original cwdChris Packham, Oct 15, 2010
  3. Jonathan NiederOct 16, 2010
  4. Chris PackhamOct 17, 2010
  5. Nguyen Thai Ngoc DuyOct 17, 2010
  6. Chris PackhamOct 18, 2010
  7. 2/5 grep: output the path from cwd to worktreeChris Packham, Oct 15, 2010
  8. 3/5 grep_cache: check pathspec firstChris Packham, Oct 15, 2010
  9. 4/5 add test for git grep --recursiveChris Packham, Oct 15, 2010
  10. 5/5 grep: add support for grepping in submodulesChris Packham, Oct 15, 2010
  11. Nguyen Thai Ngoc DuyOct 17, 2010
  12. Chris PackhamOct 18, 2010
  13. Nguyen Thai Ngoc DuyOct 18, 2010
  14. Jens LehmannOct 16, 2010
  15. Chris PackhamOct 17, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.