diff options
author | Linus Torvalds <torvalds@osdl.org> | 2006-05-14 20:49:15 -0700 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2006-05-14 22:33:24 -0700 |
commit | 1e2398d7fad308dcc6019709244b40303b51b54d (patch) | |
tree | 50ca170889b4b033782e076fce7539ea2f7cddbb /builtin-grep.c | |
parent | 07ea91d84f3c7bb075d4716ee40096e3b12a4c86 (diff) | |
download | git-1e2398d7fad308dcc6019709244b40303b51b54d.tar.gz |
builtin-grep: use external grep when we can take advantage of it
It's not perfect, but it gets the "git grep some-random-string" down to
the good old half-a-second range for the kernel.
It should convert more of the argument flags for "grep", that should be
trivial to expand (I did a few just as an example). It should also bother
to try to return the right "hit" value (which it doesn't, right now - the
code is kind of there, but I didn't actually bother to do it _right_).
Also, right now it _just_ limits by number of arguments, but it should
also strictly speaking limit by total argument size (ie add up the length
of the filenames, and do the "exec_grep()" flush call if it's bigger than
some random value like 32kB).
But I think that it's _conceptually_ doing all the right things, and it
seems to work. So maybe somebody else can do some of the final polish.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Diffstat (limited to 'builtin-grep.c')
-rw-r--r-- | builtin-grep.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/builtin-grep.c b/builtin-grep.c index fead356629..14471db7cb 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -12,6 +12,7 @@ #include "builtin.h" #include <regex.h> #include <fnmatch.h> +#include <sys/wait.h> /* * git grep pathspecs are somewhat different from diff-tree pathspecs; @@ -409,12 +410,90 @@ static int grep_file(struct grep_opt *opt, const char *filename) return i; } +static int exec_grep(int argc, const char **argv) +{ + pid_t pid; + int status; + + argv[argc] = NULL; + pid = fork(); + if (pid < 0) + return pid; + if (!pid) { + execvp("grep", (char **) argv); + exit(255); + } + while (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + return -1; + } + if (WIFEXITED(status)) { + if (!WEXITSTATUS(status)) + return 1; + return 0; + } + return -1; +} + +#define MAXARGS 1000 + +static int external_grep(struct grep_opt *opt, const char **paths, int cached) +{ + int i, nr, argc, hit; + const char *argv[MAXARGS+1]; + struct grep_pat *p; + + nr = 0; + argv[nr++] = "grep"; + if (opt->word_regexp) + argv[nr++] = "-w"; + if (opt->name_only) + argv[nr++] = "-l"; + for (p = opt->pattern_list; p; p = p->next) { + argv[nr++] = "-e"; + argv[nr++] = p->pattern; + } + argv[nr++] = "--"; + + hit = 0; + argc = nr; + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode))) + continue; + if (!pathspec_matches(paths, ce->name)) + continue; + argv[argc++] = ce->name; + if (argc < MAXARGS) + continue; + hit += exec_grep(argc, argv); + argc = nr; + } + if (argc > nr) + hit += exec_grep(argc, argv); + return 0; +} + static int grep_cache(struct grep_opt *opt, const char **paths, int cached) { int hit = 0; int nr; read_cache(); +#ifdef __unix__ + /* + * Use the external "grep" command for the case where + * we grep through the checked-out files. It tends to + * be a lot more optimized + */ + if (!cached) { + hit = external_grep(opt, paths, cached); + if (hit >= 0) + return hit; + } +#endif + for (nr = 0; nr < active_nr; nr++) { struct cache_entry *ce = active_cache[nr]; if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode))) |