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

From: Chris Packham <[email-masked]>
Date: Sun, 17 Oct 2010 19:01:51 -0700

On 17/10/10 03:28, Nguyen Thai Ngoc Duy wrote:
> On Sat, Oct 16, 2010 at 6:26 AM, Chris Packham <[email-masked]> wrote:
>> +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);
> 
> Can we just save sha1 in a env variable and drop this argv rewrite?
> 
The tree_name is a hangover from when I was passing it as a command line
arg, I'll remove it. For now we still need to rewrite argv to modify the
pathspec and max-depth, eventually I'd like to ditch that approach in
favour of using fork() and modifying opts.

>> +       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.env can be used to set these variables
> 
I was struggling a little with how to do this. The run_command
invocation in submodules.c uses local_repo_env which is protected
against modification. I also don't understand how the actual values are
passed. If someone could point me in the right direction it'd be
appreciated.

>> +       cp.argv = argv;
>> +       cp.git_cmd = 1;
>> +       cp.no_stdin = 1;
> 
> I think you stll need "cp.dir = path;" here because the setup routines
> won't do that for you. But I need to check/test that.
> 
Originally I did set cp.dir but that is used by run_command (via
start_command) to chdir. Which foils the cwd to worktree detection (at
least in V1 of that patch).

>> +       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
>>
>>
> 
> 
>