{"thread":{"id":"4014","subject":"[PATCH] built-in \"git grep\" (git grip).","startedAt":"2006-05-01T06:32:36Z","lastAt":"2006-05-03T00:01:40Z","messageCount":21,"participants":["Junio C Hamano","Jakub Narebski","Sam Ravnborg","Linus Torvalds","Andreas Ericsson"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"19318","messageId":"7v1wvetfuj.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":null,"subject":"[PATCH] built-in \"git grep\" (git grip).","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-01T06:32:36Z","receivedAt":"2006-05-01T06:32:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This attempts to set up built-in \"git grep\" to further reduce\nour dependence on the shell, while at the same time optionally\nallowing to run grep against object database.  You could do\nfunky things like these:\n\n\tgit grep --cached -e pattern\t;# grep from index\n\tgit grep -e pattern master\t;# or in a rev\n\tgit grep -e pattern master next ;# or in multiple revs\n\tgit grep -e pattern pu^@\t;# even like this with an\n\t\t\t\t\t;# extension from another topic ;-)\n\tgit grep -e pattern master..next ;# or even from rev ranges\n\tgit grep -e pattern master~20:Documentation\n\t\t\t\t\t;# or an arbitrary tree\n\tgit grep -e pattern next:git-commit.sh\n        \t\t\t\t;# or an arbitrary blob\n\nRight now, it does not understand and/or obey many options grep\nshould accept, and the pattern matcher using POSIX.2 regex seems\nto be excruciatingly slow (I lifted it from Pasky's regexp\npickaxe code almost verbatim without thinking -- I was too\ntired).  Help to improve things in the grep_buffer() function is\nvery much appreciated.\n\nBut this is going in the right direction.  The shell script\nversion is one of the worst Portability offender in the git\nbarebone Porcelainish; it uses xargs -0 to pass paths around and\nshell arrays to sift flags and parameters.\n\nIn order to stay out of the way of real work people want to get\ndone with the real \"git grep\", for now this implementation is\ncalled \"git grip\".\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Makefile       |    2 \n builtin-grep.c |  377 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h      |    1 \n git.c          |    1 \n 4 files changed, 380 insertions(+), 1 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 8ce27a6..8d5122b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -214,7 +214,7 @@ LIB_OBJS = \\\n \t$(DIFF_OBJS)\n \n BUILTIN_OBJS = \\\n-\tbuiltin-log.o builtin-help.o\n+\tbuiltin-log.o builtin-help.o builtin-grep.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-grep.c b/builtin-grep.c\nnew file mode 100644\nindex 0000000..adcdbaa\n--- /dev/null\n+++ b/builtin-grep.c\n@@ -0,0 +1,377 @@\n+/*\n+ * Builtin \"git grep\"\n+ *\n+ * Copyright (c) 2006 Junio C Hamano\n+ */\n+#include \"cache.h\"\n+#include \"blob.h\"\n+#include \"tree.h\"\n+#include \"commit.h\"\n+#include \"tag.h\"\n+#include \"diff.h\"\n+#include \"revision.h\"\n+#include \"builtin.h\"\n+#include <regex.h>\n+\n+struct grep_opt {\n+\tconst char *pattern;\n+\tregex_t regexp;\n+\tunsigned linenum:1;\n+\tunsigned pre_context;\n+\tunsigned post_context;\n+};\n+\n+static char *end_of_line(char *cp, unsigned long *left)\n+{\n+\tunsigned long l = *left;\n+\twhile (l && *cp != '\\n') {\n+\t\tl--;\n+\t\tcp++;\n+\t}\n+\t*left = l;\n+\treturn cp;\n+}\n+\n+static int grep_buffer(struct grep_opt *opt, const char *name,\n+\t\t       char *buf, unsigned long size)\n+{\n+\tchar *bol = buf;\n+\tunsigned long left = size;\n+\tunsigned lno = 1;\n+\tint hit = 0;\n+\n+\twhile (left) {\n+\t\tregmatch_t pmatch[10];\n+\t\tint flags = 0;\n+\t\tchar *eol, *cp, ch;\n+\t\teol = end_of_line(bol, &left);\n+\t\tch = *eol;\n+\t\t*eol = 0;\n+\t\tfor (cp = bol; cp < eol; cp++) { \n+\t\t\tint status = regexec(&opt->regexp, cp,\n+\t\t\t\t\t     ARRAY_SIZE(pmatch), pmatch,\n+\t\t\t\t\t     flags);\n+\t\t\tif (status == REG_NOMATCH)\n+\t\t\t\tflags |= REG_NOTBOL;\n+\t\t\telse if (status == 0) {\n+\t\t\t\t/* Hit at this line */\n+\t\t\t\tprintf(\"%s:\", name);\n+\t\t\t\tif (opt->linenum)\n+\t\t\t\t\tprintf(\"%d:\", lno);\n+\t\t\t\tprintf(\"%.*s\\n\", eol-bol, bol);\n+\t\t\t\thit = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\t*eol = ch;\n+\t\tbol = eol + 1;\n+\t\tleft--;\n+\t\tlno++;\n+\t}\n+\treturn hit;\n+}\n+\n+static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)\n+{\n+\tunsigned long size;\n+\tchar *data;\n+\tchar type[20];\n+\tint hit;\n+\tdata = read_sha1_file(sha1, type, &size);\n+\tif (!data) {\n+\t\terror(\"'%s': unable to read %s\", name, sha1_to_hex(sha1));\n+\t\treturn 0;\n+\t}\n+\thit = grep_buffer(opt, name, data, size);\n+\tfree(data);\n+\treturn hit;\n+}\n+\n+static int grep_file(struct grep_opt *opt, const char *filename)\n+{\n+\tstruct stat st;\n+\tint i;\n+\tchar *data;\n+\tif (lstat(filename, &st) < 0) {\n+\terr_ret:\n+\t\tif (errno != ENOENT)\n+\t\t\terror(\"'%s': %s\", filename, strerror(errno));\n+\t\treturn 0;\n+\t}\n+\tif (!st.st_size)\n+\t\treturn 0; /* empty file -- no grep hit */\n+\tif (!S_ISREG(st.st_mode))\n+\t\treturn 0;\n+\ti = open(filename, O_RDONLY);\n+\tif (i < 0)\n+\t\tgoto err_ret;\n+\tdata = xmalloc(st.st_size + 1);\n+\tif (st.st_size != xread(i, data, st.st_size)) {\n+\t\terror(\"'%s': short read %s\", filename, strerror(errno));\n+\t\tclose(i);\n+\t\tfree(data);\n+\t\treturn 0;\n+\t}\n+\tclose(i);\n+\ti = grep_buffer(opt, filename, data, st.st_size);\n+\tfree(data);\n+\treturn i;\n+}\n+\n+static int grep_cache(struct grep_opt *opt, struct rev_info *revs, int cached)\n+{\n+\tint hit = 0;\n+\tint nr;\n+\tread_cache();\n+\n+\tfor (nr = 0; nr < active_nr; nr++) {\n+\t\tstruct cache_entry *ce = active_cache[nr];\n+\t\tif (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))\n+\t\t\tcontinue;\n+\t\tif (revs->diffopt.nr_paths) {\n+\t\t\tint i;\n+\t\t\tint namelen = ce_namelen(ce);\n+\t\t\tconst char *name = ce->name;\n+\t\t\tfor (i = 0; i < revs->diffopt.nr_paths; i++) {\n+\t\t\t\tconst char *match = revs->diffopt.paths[i];\n+\t\t\t\tint matchlen = revs->diffopt.pathlens[i];\n+\t\t\t\tif (matchlen <= namelen)\n+\t\t\t\t\tif (!strncmp(name, match, matchlen))\n+\t\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (revs->diffopt.nr_paths <= i)\n+\t\t\t\tcontinue;\n+\t\t}\n+\t\tif (cached)\n+\t\t\thit |= grep_sha1(opt, ce->sha1, ce->name);\n+\t\telse\n+\t\t\thit |= grep_file(opt, ce->name);\n+\t}\n+\treturn hit;\n+}\n+\n+static int pathspec_matches(struct diff_options *opt, const char *name)\n+{\n+\tint i;\n+\tint namelen;\n+\tif (!opt->nr_paths)\n+\t\treturn 1;\n+\tnamelen = strlen(name);\n+\tfor (i = 0; i < opt->nr_paths; i++) {\n+\t\tconst char *match = opt->paths[i];\n+\t\tint matchlen = opt->pathlens[i];\n+\t\tif (matchlen <= namelen)\n+\t\t\tif (!strncmp(name, match, matchlen))\n+\t\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int grep_tree(struct grep_opt *opt, struct rev_info *revs,\n+\t\t     struct tree_desc *tree,\n+\t\t     const char *tree_name, const char *base)\n+{\n+\tunsigned mode;\n+\tint len;\n+\tint hit = 0;\n+\tconst char *path;\n+\tconst unsigned char *sha1;\n+\tchar *down_base;\n+\tchar *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);\n+\n+\tif (tree_name[0]) {\n+\t\tint offset = sprintf(path_buf, \"%s:\", tree_name);\n+\t\tdown_base = path_buf + offset;\n+\t\tstrcat(down_base, base);\n+\t}\n+\telse {\n+\t\tdown_base = path_buf;\n+\t\tstrcpy(down_base, base);\n+\t}\n+\tlen = strlen(path_buf);\n+\n+\twhile (tree->size) {\n+\t\tint pathlen;\n+\t\tsha1 = tree_entry_extract(tree, &path, &mode);\n+\t\tpathlen = strlen(path);\n+\t\tstrcpy(path_buf + len, path);\n+\n+\t\tif (!pathspec_matches(&revs->diffopt, down_base))\n+\t\t\t;\n+\t\telse if (S_ISREG(mode))\n+\t\t\thit |= grep_sha1(opt, sha1, path_buf);\n+\t\telse if (S_ISDIR(mode)) {\n+\t\t\tchar type[20];\n+\t\t\tstruct tree_desc sub;\n+\t\t\tvoid *data;\n+\t\t\tdata = read_sha1_file(sha1, type, &sub.size);\n+\t\t\tif (!data)\n+\t\t\t\tdie(\"unable to read tree (%s)\",\n+\t\t\t\t    sha1_to_hex(sha1));\n+\t\t\tstrcpy(path_buf + len + pathlen, \"/\");\n+\t\t\tsub.buf = data;\n+\t\t\thit = grep_tree(opt, revs, &sub, tree_name, down_base);\n+\t\t\tfree(data);\n+\t\t}\n+\t\tupdate_tree_entry(tree);\n+\t}\n+\treturn hit;\n+}\n+\n+static int grep_object(struct grep_opt *opt, struct rev_info *revs,\n+\t\t       struct object *obj, const char *name)\n+{\n+\tif (!strcmp(obj->type, blob_type))\n+\t\treturn grep_sha1(opt, obj->sha1, name);\n+\tif (!strcmp(obj->type, commit_type) ||\n+\t    !strcmp(obj->type, tree_type)) {\n+\t\tstruct tree_desc tree;\n+\t\tvoid *data;\n+\t\tint hit;\n+\t\tdata = read_object_with_reference(obj->sha1, tree_type,\n+\t\t\t\t\t\t  &tree.size, NULL);\n+\t\tif (!data)\n+\t\t\tdie(\"unable to read tree (%s)\", sha1_to_hex(obj->sha1));\n+\t\ttree.buf = data;\n+\t\thit = grep_tree(opt, revs, &tree, name, \"\");\n+\t\tfree(data);\n+\t\treturn hit;\n+\t}\n+\tdie(\"unable to grep from object of type %s\", obj->type);\n+}\n+\n+static const char builtin_grep_usage[] =\n+\"git-grep <option>* <rev>* [-e] <pattern> [<path>...]\";\n+\n+int cmd_grep(int argc, const char **argv, char **envp)\n+{\n+\tstruct rev_info rev;\n+\tconst char **dst, **src;\n+\tint err;\n+\tint hit = 0;\n+\tint no_more_arg = 0;\n+\tint seen_range = 0;\n+\tint seen_noncommit = 0;\n+\tint cached = 0;\n+\tstruct grep_opt opt;\n+\tstruct object_list *list;\n+\n+\tmemset(&opt, 0, sizeof(opt));\n+\n+\t/*\n+\t * Interpret and remove the grep options upfront.  Sigh...\n+\t */\n+\tfor (dst = src = &argv[1]; src < argc + argv; ) {\n+\t\tconst char *arg = *src++;\n+\t\tif (!no_more_arg) {\n+\t\t\tif (!strcmp(\"--\", arg)) {\n+\t\t\t\tno_more_arg = 1;\n+\t\t\t\t*dst++ = arg;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(\"--cached\", arg)) {\n+\t\t\t\tcached = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(\"-e\", arg)) {\n+\t\t\t\tif (src < argc + argv) {\n+\t\t\t\t\topt.pattern = *src++;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tusage(builtin_grep_usage);\n+\t\t\t}\n+\t\t\tif (!strcmp(\"-n\", arg)) {\n+\t\t\t\topt.linenum = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(\"-H\", arg)) {\n+\t\t\t\t/* We always show the pathname, so this\n+\t\t\t\t * is a noop.\n+\t\t\t\t */\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(\"-A\", arg) ||\n+\t\t\t    !strcmp(\"-B\", arg) ||\n+\t\t\t    !strcmp(\"-C\", arg)) {\n+\t\t\t\tunsigned num;\n+\t\t\t\tif ((argc + argv <= src) ||\n+\t\t\t\t    sscanf(*src++, \"%u\", &num) != 1)\n+\t\t\t\t\tusage(builtin_grep_usage);\n+\t\t\t\tswitch (arg[1]) {\n+\t\t\t\tcase 'A':\n+\t\t\t\t\topt.post_context = num;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'C':\n+\t\t\t\t\topt.post_context = num;\n+\t\t\t\tcase 'B':\n+\t\t\t\t\topt.pre_context = num;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n+\t\t*dst++ = arg;\n+\t}\n+\tif (!opt.pattern)\n+\t\tdie(\"no pattern given.\");\n+\n+\terr = regcomp(&opt.regexp, opt.pattern, REG_NEWLINE);\n+\tif (err) {\n+\t\tchar errbuf[1024];\n+\t\tregerror(err, &opt.regexp, errbuf, 1024);\n+\t\tregfree(&opt.regexp);\n+\t\tdie(\"'%s': %s\", opt.pattern, errbuf);\n+\t}\n+\n+\tinit_revisions(&rev);\n+\t*dst = NULL;\n+\targc = setup_revisions(dst - argv, argv, &rev, NULL);\n+\n+\t/*\n+\t * Do not walk \"grep -e foo master next pu -- Documentation/\"\n+\t * but do walk \"grep -e foo master..next -- Documentation/\".\n+\t * Ranged request mixed with a blob or tree object, like\n+\t * \"grep -e foo v1.0.0:Documentation/ master..next\"\n+\t * so detect that and complain.\n+\t */\n+\tfor (list = rev.pending_objects; list; list = list->next) {\n+\t\tstruct object *real_obj;\n+\t\tif (list->item->flags & UNINTERESTING)\n+\t\t\tseen_range = 1;\n+\t\treal_obj = deref_tag(list->item, NULL, 0);\n+\t\tif (strcmp(real_obj->type, commit_type))\n+\t\t\tseen_noncommit = 1;\n+\t}\n+\tif (!rev.pending_objects)\n+\t\treturn !grep_cache(&opt, &rev, cached);\n+\tif (cached)\n+\t\tdie(\"both --cached and revisions given.\");\n+\n+\tif (seen_range && seen_noncommit)\n+\t\tdie(\"both A..B and non commit are given.\");\n+\tif (seen_range) {\n+\t\tstruct commit *commit;\n+\t\tprepare_revision_walk(&rev);\n+\t\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\t\tunsigned char *sha1 = commit->object.sha1;\n+\t\t\tchar *n = find_unique_abbrev(sha1, DEFAULT_ABBREV);\n+\t\t\tchar rev_name[41];\n+\t\t\tstrcpy(rev_name, n);\n+\t\t\tif (grep_object(&opt, &rev, &commit->object, rev_name))\n+\t\t\t\thit = 1;\n+\t\t\tcommit->buffer = NULL;\n+\t\t}\n+\t\treturn !hit;\n+\t}\n+\n+\t/* all of them are non-commit; do not walk, and\n+\t * do not lose their names.\n+\t */\n+\tfor (list = rev.pending_objects; list; list = list->next) {\n+\t\tstruct object *real_obj;\n+\t\treal_obj = deref_tag(list->item, NULL, 0);\n+\t\tif (grep_object(&opt, &rev, real_obj, list->name))\n+\t\t\thit = 1;\n+\t}\n+\treturn !hit;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 47408a0..cf5de3b 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -19,5 +19,6 @@ extern int cmd_version(int argc, const c\n extern int cmd_whatchanged(int argc, const char **argv, char **envp);\n extern int cmd_show(int argc, const char **argv, char **envp);\n extern int cmd_log(int argc, const char **argv, char **envp);\n+extern int cmd_grep(int argc, const char **argv, char **envp);\n \n #endif\ndiff --git a/git.c b/git.c\nindex 01b7e28..18e857d 100644\n--- a/git.c\n+++ b/git.c\n@@ -46,6 +46,7 @@ static void handle_internal_command(int \n \t\t{ \"log\", cmd_log },\n \t\t{ \"whatchanged\", cmd_whatchanged },\n \t\t{ \"show\", cmd_show },\n+\t\t{ \"grip\", cmd_grep },\n \t};\n \tint i;\n \n-- \n1.3.1.gd233\n"},{"id":"19319","messageId":"e34bdf$ho4$1@sea.gmane.org","threadId":"4014","inReplyTo":"7v1wvetfuj.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-05-01T06:56:36Z","receivedAt":"2006-05-01T06:56:36Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> This attempts to set up built-in \"git grep\" to further reduce\n> our dependence on the shell, while at the same time optionally\n> allowing to run grep against object database.\n[...]\n> In order to stay out of the way of real work people want to get\n> done with the real \"git grep\", for now this implementation is\n> called \"git grip\".\n\nWouldn't \"git ggrep\" (from git-aware grep) or \"git bgrep\" (from built-in\ngrep), similar to the egrep and fgrep from the grep package?\n\n-- \nJakub Narebski\nWarsaw, Poland\n"},{"id":"19321","messageId":"7vhd4as00i.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"e34bdf$ho4$1@sea.gmane.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-01T06:59:57Z","receivedAt":"2006-05-01T06:59:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Wouldn't \"git ggrep\" (from git-aware grep) or \"git bgrep\" (from built-in\n> grep), similar to the egrep and fgrep from the grep package?\n\nThe eventual goal is to rename it to \"git grep\" and remove the\nshell based one, so the interim name does not matter.\n"},{"id":"19322","messageId":"e34cb4$is1$1@sea.gmane.org","threadId":"4014","inReplyTo":"7vhd4as00i.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-05-01T07:12:25Z","receivedAt":"2006-05-01T07:12:25Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\nJakub Narebski <jnareb@gmail.com> writes:\n\n> Wouldn't \"git ggrep\" (from git-aware grep) or \"git bgrep\" (from built-in\n> grep), similar to the egrep and fgrep from the grep package?\n\nYes, I understand, but I just don't like using 'grip'. And it would be nice\nto have some convention for further not-ready-yet built-in replacements for\nscript versions of commands, for example adding letter 'b' as 'built-in' at\nthe beginning of command name: 'bgrep', 'bdiff'. Or use postfix 'n' or\n'-ng' to denote transitionary not-ready-yet new version of command:\n'grepn', 'diffn' or 'grep-ng', 'diff-ng'.\n\n\nBy the way, [my] grep is linked against libpcre - would it mean that git\nwould also need to use pcre library, or at least have an option to use it?\n\nI also wonder if anyone would be interested to _force_ using external grep\n(probably enhanced)... just a thought.\n\n-- \nJakub Narebski\nWarsaw, Poland\n"},{"id":"19323","messageId":"7vbquirymf.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"7v1wvetfuj.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] built-in \"git grep\" (git grip) - quickfix","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-01T07:30:00Z","receivedAt":"2006-05-01T07:30:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> Right now, it does not understand and/or obey many options grep\n> should accept, and the pattern matcher using POSIX.2 regex seems\n> to be excruciatingly slow...\n\nI forgot to say that unlike the shell script version you need to\ngive -e in front of the pattern with this version because of the\nway the option parser is structured.  Obviously this needs to be\nfixed for usability's sake.\n\nBut I seem to have managed to fix the \"excruciatingly slow\" part\ntrivially.  regexec() is not re.match() but re.search(), and\nthere is no point looking at each character on the line.  Here\nis a patch.\n\n-- >8 --\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex adcdbaa..6230f44 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -42,26 +42,18 @@ static int grep_buffer(struct grep_opt *\n \n \twhile (left) {\n \t\tregmatch_t pmatch[10];\n-\t\tint flags = 0;\n-\t\tchar *eol, *cp, ch;\n+\t\tchar *eol, ch;\n \t\teol = end_of_line(bol, &left);\n \t\tch = *eol;\n \t\t*eol = 0;\n-\t\tfor (cp = bol; cp < eol; cp++) { \n-\t\t\tint status = regexec(&opt->regexp, cp,\n-\t\t\t\t\t     ARRAY_SIZE(pmatch), pmatch,\n-\t\t\t\t\t     flags);\n-\t\t\tif (status == REG_NOMATCH)\n-\t\t\t\tflags |= REG_NOTBOL;\n-\t\t\telse if (status == 0) {\n-\t\t\t\t/* Hit at this line */\n-\t\t\t\tprintf(\"%s:\", name);\n-\t\t\t\tif (opt->linenum)\n-\t\t\t\t\tprintf(\"%d:\", lno);\n-\t\t\t\tprintf(\"%.*s\\n\", eol-bol, bol);\n-\t\t\t\thit = 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n+\t\tif (!regexec(&opt->regexp, bol,\n+\t\t\t     ARRAY_SIZE(pmatch), pmatch, 0)) {\n+\t\t\t/* Hit at this line */\n+\t\t\tprintf(\"%s:\", name);\n+\t\t\tif (opt->linenum)\n+\t\t\t\tprintf(\"%d:\", lno);\n+\t\t\tprintf(\"%.*s\\n\", eol-bol, bol);\n+\t\t\thit = 1;\n \t\t}\n \t\t*eol = ch;\n \t\tbol = eol + 1;\n"},{"id":"19325","messageId":"20060501140410.GA3505@mars.ravnborg.org","threadId":"4014","inReplyTo":"7v1wvetfuj.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Sam Ravnborg","fromEmail":"sam@ravnborg.org","sentAt":"2006-05-01T14:04:10Z","receivedAt":"2006-05-01T14:04:10Z","isPatch":true,"sender":{"key":"sam@ravnborg.org","avatar":"https://gravatar.com/avatar/168a912606ed0742d840bb365e3cc21db390c36531a58341dc7a069cc1f15f62?d=mp&s=160"},"body":"On Sun, Apr 30, 2006 at 11:32:36PM -0700, Junio C Hamano wrote:\n> This attempts to set up built-in \"git grep\" to further reduce\n> our dependence on the shell, while at the same time optionally\n> allowing to run grep against object database.  You could do\n> funky things like these:\n> \n> \tgit grep --cached -e pattern\t;# grep from index\n> \tgit grep -e pattern master\t;# or in a rev\n> \tgit grep -e pattern master next ;# or in multiple revs\n> \tgit grep -e pattern pu^@\t;# even like this with an\n> \t\t\t\t\t;# extension from another topic ;-)\n> \tgit grep -e pattern master..next ;# or even from rev ranges\n> \tgit grep -e pattern master~20:Documentation\n> \t\t\t\t\t;# or an arbitrary tree\n> \tgit grep -e pattern next:git-commit.sh\n>         \t\t\t\t;# or an arbitrary blob\n> \n\nA feature I have been missing often has been the possibility to limit\ngrep (and ls-files) to certain filenames.\nSay:\ngit grip -e DEBUG 'Kconfig*'\n\nI usually do something stupid like:\ngit ls-files | grep Kconfig | xargs grep DEBUG\n\nThought it may be trivial to extend git grip while you are there anyway.\nBut obviously only if this is useful for more than just me.\n\n\tSam\n"},{"id":"19326","messageId":"20060501140704.GA6096@mars.ravnborg.org","threadId":"4014","inReplyTo":"20060501140410.GA3505@mars.ravnborg.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Sam Ravnborg","fromEmail":"sam@ravnborg.org","sentAt":"2006-05-01T14:07:04Z","receivedAt":"2006-05-01T14:07:04Z","isPatch":true,"sender":{"key":"sam@ravnborg.org","avatar":"https://gravatar.com/avatar/168a912606ed0742d840bb365e3cc21db390c36531a58341dc7a069cc1f15f62?d=mp&s=160"},"body":"> \n> I usually do something stupid like:\n> git ls-files | grep Kconfig | xargs grep DEBUG\n\nWhich is indeed studip. I just learned I could say:\ngit ls-files '*/Kconfig*' | xargs grep DEBUG\n\n\tSam\n"},{"id":"19327","messageId":"20060501145328.GA14856@mars.ravnborg.org","threadId":"4014","inReplyTo":"20060501140704.GA6096@mars.ravnborg.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Sam Ravnborg","fromEmail":"sam@ravnborg.org","sentAt":"2006-05-01T14:53:28Z","receivedAt":"2006-05-01T14:53:28Z","isPatch":true,"sender":{"key":"sam@ravnborg.org","avatar":"https://gravatar.com/avatar/168a912606ed0742d840bb365e3cc21db390c36531a58341dc7a069cc1f15f62?d=mp&s=160"},"body":"On Mon, May 01, 2006 at 04:07:04PM +0200, Sam Ravnborg wrote:\n> > \n> > I usually do something stupid like:\n> > git ls-files | grep Kconfig | xargs grep DEBUG\n> \n> Which is indeed studip. I just learned I could say:\n> git ls-files '*/Kconfig*' | xargs grep DEBUG\n\nSeems I have confused myself.\ngit grep DEBUG '*/Kconfig*'\ndoes indeed work today. And browsing the git grip code that\nwill also support it.\n\nSorry for the noise.\n\n\tSam\n"},{"id":"19328","messageId":"Pine.LNX.4.64.0605010842130.21189@g5.osdl.org","threadId":"4014","inReplyTo":"20060501145328.GA14856@mars.ravnborg.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-05-01T15:48:44Z","receivedAt":"2006-05-01T15:48:44Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 1 May 2006, Sam Ravnborg wrote:\n> \n> Seems I have confused myself.\n>\n>\tgit grep DEBUG '*/Kconfig*'\n>\n> does indeed work today.\n\nIndeed. I was a bit confused about your report, since not only does it \nwork today, that's how it has always worked, and it was very much designed \nthat way. I use it all the time.\n\nIt takes the git-ls-files pathname syntax, which is a bit _different_ from \nthe normal \"limit to these paths\" syntax, in that it honors '*'. And it \nhonors that a bit differently than normal shell pathname expansion, \nbecause for git-ls-files a '*' pattern will match '/' as well.\n\nSo\n\n\tgit grep pattern 'net/*.c'\n\nwill match every single C file found _recursively_ inside the \"net/\" \nsubdirectory, not just in that single directory itself.\n\nSo \"*\" for git grep is a bit more like a \"**\" pattern in some shells.\n\n\t\tLinus\n"},{"id":"19332","messageId":"7v64kpsftv.fsf_-_@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"Pine.LNX.4.64.0605010842130.21189@g5.osdl.org","subject":"[PATCH] builtin-grep: wildcard pathspec fixes","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-01T19:30:36Z","receivedAt":"2006-05-01T19:30:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This tweaks the pathspec wildcard used in builtin-grep to match\nthat of ls-files.  With this:\n\n\tgit grep -e DEBUG -- '*/Kconfig*'\n\nwould work like the shell script version, and you could even do:\n\n\tgit grep -e DEBUG --cached -- '*/Kconfig*' ;# from index\n\tgit grep -e DEBUG v2.6.12 -- '*/Kconfig*' ;# from rev\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n * Still haven't improved the \"-e\" issue (and to a lesser extent\n   I think requiring -- is not right in this context either), but\n\n builtin-grep.c |   85 +++++++++++++++++++++++++++++++++++++++++---------------\n 1 files changed, 62 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex 36150bf..653b65e 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -12,33 +12,66 @@ #include \"diff.h\"\n #include \"revision.h\"\n #include \"builtin.h\"\n #include <regex.h>\n+#include <fnmatch.h>\n \n+/*\n+ * git grep pathspecs are somewhat different from diff-tree pathspecs;\n+ * pathname wildcards are allowed.\n+ */\n static int pathspec_matches(struct diff_options *opt, const char *name)\n {\n-\tint i, j;\n-\tint namelen;\n+\tint namelen, i;\n \tif (!opt->nr_paths)\n \t\treturn 1;\n \tnamelen = strlen(name);\n \tfor (i = 0; i < opt->nr_paths; i++) {\n \t\tconst char *match = opt->paths[i];\n \t\tint matchlen = opt->pathlens[i];\n-\t\tif (matchlen <= namelen) {\n-\t\t\tif (!strncmp(name, match, matchlen))\n-\t\t\t\treturn 1;\n+\t\tconst char *slash, *cp;\n+\n+\t\tif ((matchlen <= namelen) &&\n+\t\t    !strncmp(name, match, matchlen) &&\n+\t\t    (match[matchlen-1] == '/' ||\n+\t\t     name[matchlen] == '\\0' || name[matchlen] == '/'))\n+\t\t\treturn 1;\n+\t\tif (!fnmatch(match, name, 0))\n+\t\t\treturn 1;\n+\t\tif (name[namelen-1] != '/')\n \t\t\tcontinue;\n-\t\t}\n-\t\t/* If name is \"Documentation\" and pathspec is\n-\t\t * \"Documentation/\", they should match.  Maybe\n-\t\t * we would want to strip it in get_pathspec()???\n+\n+\t\t/* We are being asked if the name directory is worth\n+\t\t * descending into.\n+\t\t *\n+\t\t * Find the longest leading directory name that does\n+\t\t * not have metacharacter in the pathspec; the name\n+\t\t * we are looking at must overlap with that directory.\n \t\t */\n-\t\tif (strncmp(name, match, namelen))\n-\t\t\tcontinue;\n-\t\tfor (j = namelen; j < matchlen; j++)\n-\t\t\tif (match[j] != '/')\n+\t\tfor (cp = match, slash = NULL; cp - match < matchlen; cp++) {\n+\t\t\tchar ch = *cp;\n+\t\t\tif (ch == '/')\n+\t\t\t\tslash = cp;\n+\t\t\tif (ch == '*' || ch == '[')\n \t\t\t\tbreak;\n-\t\tif (matchlen <= j)\n-\t\t\treturn 1;\n+\t\t}\n+\t\tif (!slash)\n+\t\t\tslash = match; /* toplevel */\n+\t\telse\n+\t\t\tslash++;\n+\t\tif (namelen <= slash - match) {\n+\t\t\t/* Looking at \"Documentation/\" and\n+\t\t\t * the pattern says \"Documentation/howto/\", or\n+\t\t\t * \"Documentation/diff*.txt\".\n+\t\t\t */\n+\t\t\tif (!memcmp(match, name, namelen))\n+\t\t\t\treturn 1;\n+\t\t}\n+\t\telse {\n+\t\t\t/* Looking at \"Documentation/howto/\" and\n+\t\t\t * the pattern says \"Documentation/h*\".\n+\t\t\t */\n+\t\t\tif (!memcmp(match, name, slash - match))\n+\t\t\t\treturn 1;\n+\t\t}\n \t}\n \treturn 0;\n }\n@@ -232,17 +265,17 @@ static int grep_tree(struct grep_opt *op\n \tint hit = 0;\n \tconst char *path;\n \tconst unsigned char *sha1;\n-\tchar *down_base;\n+\tchar *down;\n \tchar *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);\n \n \tif (tree_name[0]) {\n \t\tint offset = sprintf(path_buf, \"%s:\", tree_name);\n-\t\tdown_base = path_buf + offset;\n-\t\tstrcat(down_base, base);\n+\t\tdown = path_buf + offset;\n+\t\tstrcat(down, base);\n \t}\n \telse {\n-\t\tdown_base = path_buf;\n-\t\tstrcpy(down_base, base);\n+\t\tdown = path_buf;\n+\t\tstrcpy(down, base);\n \t}\n \tlen = strlen(path_buf);\n \n@@ -252,7 +285,14 @@ static int grep_tree(struct grep_opt *op\n \t\tpathlen = strlen(path);\n \t\tstrcpy(path_buf + len, path);\n \n-\t\tif (!pathspec_matches(&revs->diffopt, down_base))\n+\t\tif (S_ISDIR(mode))\n+\t\t\t/* Match \"abc/\" against pathspec to\n+\t\t\t * decide if we want to descend into \"abc\"\n+\t\t\t * directory.\n+\t\t\t */\n+\t\t\tstrcpy(path_buf + len + pathlen, \"/\");\n+\n+\t\tif (!pathspec_matches(&revs->diffopt, down))\n \t\t\t;\n \t\telse if (S_ISREG(mode))\n \t\t\thit |= grep_sha1(opt, sha1, path_buf);\n@@ -264,9 +304,8 @@ static int grep_tree(struct grep_opt *op\n \t\t\tif (!data)\n \t\t\t\tdie(\"unable to read tree (%s)\",\n \t\t\t\t    sha1_to_hex(sha1));\n-\t\t\tstrcpy(path_buf + len + pathlen, \"/\");\n \t\t\tsub.buf = data;\n-\t\t\thit = grep_tree(opt, revs, &sub, tree_name, down_base);\n+\t\t\thit |= grep_tree(opt, revs, &sub, tree_name, down);\n \t\t\tfree(data);\n \t\t}\n \t\tupdate_tree_entry(tree);\n-- \n1.3.1.gd233\n"},{"id":"19341","messageId":"7vodyhqqfg.fsf_-_@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"7v64kpsftv.fsf_-_@assigned-by-dhcp.cox.net","subject":"[PATCH] builtin-grep: do not use setup_revisions()","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-01T23:24:35Z","receivedAt":"2006-05-01T23:24:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Grep may want to grok multiple revisions, but it does not make\nmuch sense to walk revisions while doing so.  This stops calling\nthe code to parse parameters for the revision walker.  The\nparameter parsing for the optional \"-e\" option becomes a lot\nsimpler with it as well.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n\n  Junio C Hamano <junkio@cox.net> writes:\n\n  >  * Still haven't improved the \"-e\" issue (and to a lesser extent\n  >    I think requiring -- is not right in this context either),...\n\n  So I did this.  This removes the ability to say:\n\n  \tgit grep master..next -e foobar -- Documentation\n\n  but if you want to walk the revision graph, you are interested\n  in commits that actually changes things to be subject to grep,\n  not just all commits in the range, so if that is what you are\n  doing, you are better off using the pickaxe.\n\n builtin-grep.c |  255 +++++++++++++++++++++++++++++---------------------------\n 1 files changed, 134 insertions(+), 121 deletions(-)\n\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex c3e6701..4be1514 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -8,8 +8,7 @@ #include \"blob.h\"\n #include \"tree.h\"\n #include \"commit.h\"\n #include \"tag.h\"\n-#include \"diff.h\"\n-#include \"revision.h\"\n+#include \"tree-walk.h\"\n #include \"builtin.h\"\n #include <regex.h>\n #include <fnmatch.h>\n@@ -18,15 +17,15 @@ #include <fnmatch.h>\n  * git grep pathspecs are somewhat different from diff-tree pathspecs;\n  * pathname wildcards are allowed.\n  */\n-static int pathspec_matches(struct diff_options *opt, const char *name)\n+static int pathspec_matches(const char **paths, const char *name)\n {\n \tint namelen, i;\n-\tif (!opt->nr_paths)\n+\tif (!paths || !*paths)\n \t\treturn 1;\n \tnamelen = strlen(name);\n-\tfor (i = 0; i < opt->nr_paths; i++) {\n-\t\tconst char *match = opt->paths[i];\n-\t\tint matchlen = opt->pathlens[i];\n+\tfor (i = 0; paths[i]; i++) {\n+\t\tconst char *match = paths[i];\n+\t\tint matchlen = strlen(match);\n \t\tconst char *slash, *cp;\n \n \t\tif ((matchlen <= namelen) &&\n@@ -241,7 +240,7 @@ static int grep_file(struct grep_opt *op\n \treturn i;\n }\n \n-static int grep_cache(struct grep_opt *opt, struct rev_info *revs, int cached)\n+static int grep_cache(struct grep_opt *opt, const char **paths, int cached)\n {\n \tint hit = 0;\n \tint nr;\n@@ -251,7 +250,7 @@ static int grep_cache(struct grep_opt *o\n \t\tstruct cache_entry *ce = active_cache[nr];\n \t\tif (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))\n \t\t\tcontinue;\n-\t\tif (!pathspec_matches(&revs->diffopt, ce->name))\n+\t\tif (!pathspec_matches(paths, ce->name))\n \t\t\tcontinue;\n \t\tif (cached)\n \t\t\thit |= grep_sha1(opt, ce->sha1, ce->name);\n@@ -261,7 +260,7 @@ static int grep_cache(struct grep_opt *o\n \treturn hit;\n }\n \n-static int grep_tree(struct grep_opt *opt, struct rev_info *revs,\n+static int grep_tree(struct grep_opt *opt, const char **paths,\n \t\t     struct tree_desc *tree,\n \t\t     const char *tree_name, const char *base)\n {\n@@ -297,7 +296,7 @@ static int grep_tree(struct grep_opt *op\n \t\t\t */\n \t\t\tstrcpy(path_buf + len + pathlen, \"/\");\n \n-\t\tif (!pathspec_matches(&revs->diffopt, down))\n+\t\tif (!pathspec_matches(paths, down))\n \t\t\t;\n \t\telse if (S_ISREG(mode))\n \t\t\thit |= grep_sha1(opt, sha1, path_buf);\n@@ -310,7 +309,7 @@ static int grep_tree(struct grep_opt *op\n \t\t\t\tdie(\"unable to read tree (%s)\",\n \t\t\t\t    sha1_to_hex(sha1));\n \t\t\tsub.buf = data;\n-\t\t\thit |= grep_tree(opt, revs, &sub, tree_name, down);\n+\t\t\thit |= grep_tree(opt, paths, &sub, tree_name, down);\n \t\t\tfree(data);\n \t\t}\n \t\tupdate_tree_entry(tree);\n@@ -318,7 +317,7 @@ static int grep_tree(struct grep_opt *op\n \treturn hit;\n }\n \n-static int grep_object(struct grep_opt *opt, struct rev_info *revs,\n+static int grep_object(struct grep_opt *opt, const char **paths,\n \t\t       struct object *obj, const char *name)\n {\n \tif (!strcmp(obj->type, blob_type))\n@@ -333,7 +332,7 @@ static int grep_object(struct grep_opt *\n \t\tif (!data)\n \t\t\tdie(\"unable to read tree (%s)\", sha1_to_hex(obj->sha1));\n \t\ttree.buf = data;\n-\t\thit = grep_tree(opt, revs, &tree, name, \"\");\n+\t\thit = grep_tree(opt, paths, &tree, name, \"\");\n \t\tfree(data);\n \t\treturn hit;\n \t}\n@@ -345,102 +344,119 @@ static const char builtin_grep_usage[] =\n \n int cmd_grep(int argc, const char **argv, char **envp)\n {\n-\tstruct rev_info rev;\n-\tconst char **dst, **src;\n \tint err;\n \tint hit = 0;\n-\tint no_more_arg = 0;\n-\tint seen_range = 0;\n+\tint no_more_flags = 0;\n \tint seen_noncommit = 0;\n \tint cached = 0;\n \tstruct grep_opt opt;\n-\tstruct object_list *list;\n+\tstruct object_list *list, **tail, *object_list = NULL;\n+\tconst char *prefix = setup_git_directory();\n+\tconst char **paths = NULL;\n \n \tmemset(&opt, 0, sizeof(opt));\n \topt.regflags = REG_NEWLINE;\n \n \t/*\n-\t * Interpret and remove the grep options upfront.  Sigh...\n+\t * No point using rev_info, really.\n \t */\n-\tfor (dst = src = &argv[1]; src < argc + argv; ) {\n-\t\tconst char *arg = *src++;\n-\t\tif (!no_more_arg) {\n-\t\t\tif (!strcmp(\"--\", arg)) {\n-\t\t\t\tno_more_arg = 1;\n-\t\t\t\t*dst++ = arg;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"--cached\", arg)) {\n-\t\t\t\tcached = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-i\", arg) ||\n-\t\t\t    !strcmp(\"--ignore-case\", arg)) {\n-\t\t\t\topt.regflags |= REG_ICASE;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-v\", arg) ||\n-\t\t\t    !strcmp(\"--invert-match\", arg)) {\n-\t\t\t\topt.invert = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-E\", arg) ||\n-\t\t\t    !strcmp(\"--extended-regexp\", arg)) {\n-\t\t\t\topt.regflags |= REG_EXTENDED;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-G\", arg) ||\n-\t\t\t    !strcmp(\"--basic-regexp\", arg)) {\n-\t\t\t\topt.regflags &= ~REG_EXTENDED;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-e\", arg)) {\n-\t\t\t\tif (src < argc + argv) {\n-\t\t\t\t\topt.pattern = *src++;\n-\t\t\t\t\tcontinue;\n-\t\t\t\t}\n+\twhile (1 < argc) {\n+\t\tconst char *arg = argv[1];\n+\t\targc--; argv++;\n+\t\tif (!strcmp(\"--cached\", arg)) {\n+\t\t\tcached = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-i\", arg) ||\n+\t\t    !strcmp(\"--ignore-case\", arg)) {\n+\t\t\topt.regflags |= REG_ICASE;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-v\", arg) ||\n+\t\t    !strcmp(\"--invert-match\", arg)) {\n+\t\t\topt.invert = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-E\", arg) ||\n+\t\t    !strcmp(\"--extended-regexp\", arg)) {\n+\t\t\topt.regflags |= REG_EXTENDED;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-G\", arg) ||\n+\t\t    !strcmp(\"--basic-regexp\", arg)) {\n+\t\t\topt.regflags &= ~REG_EXTENDED;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-n\", arg)) {\n+\t\t\topt.linenum = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-H\", arg)) {\n+\t\t\t/* We always show the pathname, so this\n+\t\t\t * is a noop.\n+\t\t\t */\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-l\", arg) ||\n+\t\t    !strcmp(\"--files-with-matches\", arg)) {\n+\t\t\topt.name_only = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-A\", arg) ||\n+\t\t    !strcmp(\"-B\", arg) ||\n+\t\t    !strcmp(\"-C\", arg)) {\n+\t\t\tunsigned num;\n+\t\t\tif (argc <= 1 ||\n+\t\t\t    sscanf(*++argv, \"%u\", &num) != 1)\n \t\t\t\tusage(builtin_grep_usage);\n+\t\t\targc--;\n+\t\t\tswitch (arg[1]) {\n+\t\t\tcase 'A':\n+\t\t\t\topt.post_context = num;\n+\t\t\t\tbreak;\n+\t\t\tcase 'C':\n+\t\t\t\topt.post_context = num;\n+\t\t\tcase 'B':\n+\t\t\t\topt.pre_context = num;\n+\t\t\t\tbreak;\n \t\t\t}\n-\t\t\tif (!strcmp(\"-n\", arg)) {\n-\t\t\t\topt.linenum = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-H\", arg)) {\n-\t\t\t\t/* We always show the pathname, so this\n-\t\t\t\t * is a noop.\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(\"-e\", arg)) {\n+\t\t\tif (1 < argc) {\n+\t\t\t\t/* We probably would want to do\n+\t\t\t\t * -e pat1 -e pat2 as well later...\n \t\t\t\t */\n+\t\t\t\tif (opt.pattern)\n+\t\t\t\t\tdie(\"more than one pattern?\");\n+\t\t\t\topt.pattern = *++argv;\n+\t\t\t\targc--;\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tif (!strcmp(\"-l\", arg) ||\n-\t\t\t    !strcmp(\"--files-with-matches\", arg)) {\n-\t\t\t\topt.name_only = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(\"-A\", arg) ||\n-\t\t\t    !strcmp(\"-B\", arg) ||\n-\t\t\t    !strcmp(\"-C\", arg)) {\n-\t\t\t\tunsigned num;\n-\t\t\t\tif ((argc + argv <= src) ||\n-\t\t\t\t    sscanf(*src++, \"%u\", &num) != 1)\n-\t\t\t\t\tusage(builtin_grep_usage);\n-\t\t\t\tswitch (arg[1]) {\n-\t\t\t\tcase 'A':\n-\t\t\t\t\topt.post_context = num;\n-\t\t\t\t\tbreak;\n-\t\t\t\tcase 'C':\n-\t\t\t\t\topt.post_context = num;\n-\t\t\t\tcase 'B':\n-\t\t\t\t\topt.pre_context = num;\n-\t\t\t\t\tbreak;\n-\t\t\t\t}\n-\t\t\t\tcontinue;\n-\t\t\t}\n+\t\t\tusage(builtin_grep_usage);\n+\t\t}\n+\t\tif (!strcmp(\"--\", arg)) {\n+\t\t\tno_more_flags = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\t/* Either unrecognized option or a single pattern */\n+\t\tif (!no_more_flags && *arg == '-')\n+\t\t\tusage(builtin_grep_usage);\n+\t\tif (!opt.pattern) {\n+\t\t\topt.pattern = arg;\n+\t\t\tbreak;\n+\t\t}\n+\t\telse {\n+\t\t\t/* We are looking at the first path or rev;\n+\t\t\t * it is found at argv[0] after leaving the\n+\t\t\t * loop.\n+\t\t\t */\n+\t\t\targc++; argv--;\n+\t\t\tbreak;\n \t\t}\n-\t\t*dst++ = arg;\n \t}\n \tif (!opt.pattern)\n \t\tdie(\"no pattern given.\");\n-\n \terr = regcomp(&opt.regexp, opt.pattern, opt.regflags);\n \tif (err) {\n \t\tchar errbuf[1024];\n@@ -448,11 +464,32 @@ int cmd_grep(int argc, const char **argv\n \t\tregfree(&opt.regexp);\n \t\tdie(\"'%s': %s\", opt.pattern, errbuf);\n \t}\n+\ttail = &object_list;\n+\twhile (1 < argc) {\n+\t\tstruct object *object;\n+\t\tstruct object_list *elem;\n+\t\tconst char *arg = argv[1];\n+\t\tunsigned char sha1[20];\n+\t\tif (get_sha1(arg, sha1) < 0)\n+\t\t\tbreak;\n+\t\tobject = parse_object(sha1);\n+\t\tif (!object)\n+\t\t\tdie(\"bad object %s\", arg);\n+\t\telem = object_list_insert(object, tail);\n+\t\telem->name = arg;\n+\t\ttail = &elem->next;\n+\t\targc--; argv++;\n+\t}\n+\tif (1 < argc)\n+\t\tpaths = get_pathspec(prefix, argv + 1);\n+\telse if (prefix) {\n+\t\tpaths = xcalloc(2, sizeof(const char *));\n+\t\tpaths[0] = prefix;\n+\t\tpaths[1] = NULL;\n+\t}\n \n-\tinit_revisions(&rev);\n-\t*dst = NULL;\n-\targc = setup_revisions(dst - argv, argv, &rev, NULL);\n-\n+\tif (!object_list)\n+\t\treturn !grep_cache(&opt, paths, cached);\n \t/*\n \t * Do not walk \"grep -e foo master next pu -- Documentation/\"\n \t * but do walk \"grep -e foo master..next -- Documentation/\".\n@@ -460,43 +497,19 @@ int cmd_grep(int argc, const char **argv\n \t * \"grep -e foo v1.0.0:Documentation/ master..next\"\n \t * so detect that and complain.\n \t */\n-\tfor (list = rev.pending_objects; list; list = list->next) {\n+\tfor (list = object_list; list; list = list->next) {\n \t\tstruct object *real_obj;\n-\t\tif (list->item->flags & UNINTERESTING)\n-\t\t\tseen_range = 1;\n \t\treal_obj = deref_tag(list->item, NULL, 0);\n \t\tif (strcmp(real_obj->type, commit_type))\n \t\t\tseen_noncommit = 1;\n \t}\n-\tif (!rev.pending_objects)\n-\t\treturn !grep_cache(&opt, &rev, cached);\n \tif (cached)\n \t\tdie(\"both --cached and revisions given.\");\n \n-\tif (seen_range && seen_noncommit)\n-\t\tdie(\"both A..B and non commit are given.\");\n-\tif (seen_range) {\n-\t\tstruct commit *commit;\n-\t\tprepare_revision_walk(&rev);\n-\t\twhile ((commit = get_revision(&rev)) != NULL) {\n-\t\t\tunsigned char *sha1 = commit->object.sha1;\n-\t\t\tconst char *n = find_unique_abbrev(sha1, rev.abbrev);\n-\t\t\tchar rev_name[41];\n-\t\t\tstrcpy(rev_name, n);\n-\t\t\tif (grep_object(&opt, &rev, &commit->object, rev_name))\n-\t\t\t\thit = 1;\n-\t\t\tcommit->buffer = NULL;\n-\t\t}\n-\t\treturn !hit;\n-\t}\n-\n-\t/* all of them are non-commit; do not walk, and\n-\t * do not lose their names.\n-\t */\n-\tfor (list = rev.pending_objects; list; list = list->next) {\n+\tfor (list = object_list; list; list = list->next) {\n \t\tstruct object *real_obj;\n \t\treal_obj = deref_tag(list->item, NULL, 0);\n-\t\tif (grep_object(&opt, &rev, real_obj, list->name))\n+\t\tif (grep_object(&opt, paths, real_obj, list->name))\n \t\t\thit = 1;\n \t}\n \treturn !hit;\n-- \n1.3.1.gd233\n"},{"id":"19355","messageId":"44571967.7080807@op5.se","threadId":"4014","inReplyTo":"e34cb4$is1$1@sea.gmane.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-05-02T08:33:43Z","receivedAt":"2006-05-02T08:33:43Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Jakub Narebski wrote:\n> Junio C Hamano wrote:\n> \n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n> \n>>Wouldn't \"git ggrep\" (from git-aware grep) or \"git bgrep\" (from built-in\n>>grep), similar to the egrep and fgrep from the grep package?\n> \n> \n> Yes, I understand, but I just don't like using 'grip'. And it would be nice\n> to have some convention for further not-ready-yet built-in replacements for\n> script versions of commands, for example adding letter 'b' as 'built-in' at\n> the beginning of command name: 'bgrep', 'bdiff'. Or use postfix 'n' or\n> '-ng' to denote transitionary not-ready-yet new version of command:\n> 'grepn', 'diffn' or 'grep-ng', 'diff-ng'.\n> \n\nForcing the user to remember what's implemented as built-ins is not a \ngood idea. It was for that exact reason the \"git-<command>-script\" were \nall renamed \"git-<command>\" once upon a time.\n\n\"git grip\" work just fine for me, since it's only intended for testing \nand performance improvements so far. I also think it's clearer for \nend-users looking for a grep command if they're not faced with \nfgrep/egrep/ggrep/bgrep alongside plain \"grep\".\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"19356","messageId":"e3764f$ea7$1@sea.gmane.org","threadId":"4014","inReplyTo":"44571967.7080807@op5.se","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-05-02T08:44:57Z","receivedAt":"2006-05-02T08:44:57Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Andreas Ericsson wrote:\n\n> Jakub Narebski wrote:\n\n>> Yes, I understand, but I just don't like using 'grip'. And it would be\n>> nice to have some convention for further not-ready-yet built-in\n>> replacements for script versions of commands, for example adding letter\n>> 'b' as 'built-in' at the beginning of command name: 'bgrep', 'bdiff'. Or\n>> use postfix 'n' or '-ng' to denote transitionary not-ready-yet new\n>> version of command: 'grepn', 'diffn' or 'grep-ng', 'diff-ng'.\n>> \n> \n> Forcing the user to remember what's implemented as built-ins is not a\n> good idea. It was for that exact reason the \"git-<command>-script\" were\n> all renamed \"git-<command>\" once upon a time.\n> \n> \"git grip\" work just fine for me, since it's only intended for testing\n> and performance improvements so far. I also think it's clearer for\n> end-users looking for a grep command if they're not faced with\n> fgrep/egrep/ggrep/bgrep alongside plain \"grep\".\n\nWell, scratch 'bgrep' idea, even if I had no intend for 'bgrep' to be\npersistent name; it was meant as transitionary name. Well, that doesn't\nmatter much because someone interested in testing new, not-ready-yet\nversions of commands (I like 'grepn' idea) usually would follow git\ndevelopment, and would know (or not) about new version of 'git grep' being\n'git grip' (and not 'git grepn').\n\n\nWhat about forcing using external grep, and the fact that grep is linked\nwith libpcre?\n\n-- \nJakub Narebski\nWarsaw, Poland\n"},{"id":"19358","messageId":"7vy7xkn6kd.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"44571967.7080807@op5.se","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-02T09:01:54Z","receivedAt":"2006-05-02T09:01:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> \"git grip\" work just fine for me, since it's only intended for testing\n> and performance improvements so far. I also think it's clearer for\n> end-users looking for a grep command if they're not faced with\n> fgrep/egrep/ggrep/bgrep alongside plain \"grep\".\n\nI renamed \"git grip\" to \"git grep\" and \"git diffn\" to \"git diff\"\nboth in \"next\" branch to avoid confusion.  Thanks Andreas,\nJakub, and others for input.\n"},{"id":"19359","messageId":"e378fs$lpc$1@sea.gmane.org","threadId":"4014","inReplyTo":"7vy7xkn6kd.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-05-02T09:25:10Z","receivedAt":"2006-05-02T09:25:10Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> Andreas Ericsson <ae@op5.se> writes:\n> \n>> \"git grip\" work just fine for me, since it's only intended for testing\n>> and performance improvements so far. I also think it's clearer for\n>> end-users looking for a grep command if they're not faced with\n>> fgrep/egrep/ggrep/bgrep alongside plain \"grep\".\n> \n> I renamed \"git grip\" to \"git grep\" and \"git diffn\" to \"git diff\"\n> both in \"next\" branch to avoid confusion.  Thanks Andreas,\n> Jakub, and others for input.\n\nSo, is there a way to use old, script version of those commands?\n\n-- \nJakub Narebski\nWarsaw, Poland\n"},{"id":"19360","messageId":"445728D4.60301@op5.se","threadId":"4014","inReplyTo":"e378fs$lpc$1@sea.gmane.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-05-02T09:39:32Z","receivedAt":"2006-05-02T09:39:32Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Jakub Narebski wrote:\n> Junio C Hamano wrote:\n> \n> \n>>Andreas Ericsson <ae@op5.se> writes:\n>>\n>>\n>>>\"git grip\" work just fine for me, since it's only intended for testing\n>>>and performance improvements so far. I also think it's clearer for\n>>>end-users looking for a grep command if they're not faced with\n>>>fgrep/egrep/ggrep/bgrep alongside plain \"grep\".\n>>\n>>I renamed \"git grip\" to \"git grep\" and \"git diffn\" to \"git diff\"\n>>both in \"next\" branch to avoid confusion.  Thanks Andreas,\n>>Jakub, and others for input.\n> \n> \n> So, is there a way to use old, script version of those commands?\n> \n\nUse \"master\" branch or \"git-grep\" syntax if you're trying \"next\" branch.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"19372","messageId":"7v1wvcmejr.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"e378fs$lpc$1@sea.gmane.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-02T19:07:04Z","receivedAt":"2006-05-02T19:07:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n>> I renamed \"git grip\" to \"git grep\" and \"git diffn\" to \"git diff\"\n>> both in \"next\" branch to avoid confusion.  Thanks Andreas,\n>> Jakub, and others for input.\n>\n> So, is there a way to use old, script version of those commands?\n\nI'd say that is probably not the real question you wanted to\nask, but let's pretend it is for a moment.\n\nThe \"master\" branch has not been updated to remove the script\none, so you can keep running \"master\" one (or 1.3.X series).  Or\nyou can fork your own private edition by tweaking git.c (prevent\nit from running the builtin one) and Makefile (resurrect the\nscript based one and prevent it from installing git-grep\nhardlinked with git itself).  One thing that I will not do in\nthe long run, however, is to keep the script based one and have\nbuiltin one.  It is like carrying all the earlier slightly\nincompatible versions as git-grep-1.1.sh, git-grep-1.2.sh, and\ngit-grep-1.3.sh in the source for fear of backward compatibility\nproblems -- it is crazy.\n\nSo the real question, is what are still missing in the built-in\nimplementation.  What will we lose if we remove the script based\none and replace it with today's built-in one, if we are ready to\ndo it today, and if not what we are going to do about them.  My\nanswer to the latter questions are \"not yet\" (obviously, that is\nwhy \"master\" does not have it yet), and \"will support what are\nreasonable\".\n\nHere are main differences that I am aware of:\n\n - The shell-script one, if you use GNU grep, accepts more\n   options to grep than what the current built-in one supports.\n   Notable ones that are missing: fixed strings (-F), patterns\n   from file (-f), count matches (-c), omit filenames (-h),\n   skipping binary files (-I, -U), files without match (-L),\n   pcre (-P), silent (-q), word expression (-w), NUL (-z).  They\n   should be easy to add if somebody cares enough, and I plan to\n   do a few myself before pushing it out to \"master\".\n\n - The shell-script one can be coaxed to use different \"grep\"\n   implementation from the standard one with an appropriate PATH\n   settings.\n\nAt the lowest level, buitlin-grep.c::grep_buffer() function is\ncalled with the set of parsed options, the \"filename\" used for\nreporting, and the text to grepped in-core.  The shell-script\none always worked on working tree files, but the built-in one\ncan work on working tree files and also alternatively on files\nfrom other versions.  Regardless of where the file comes from,\nthis function is called to look for the pattern the user is\nlooking for.\n\nYou can do two things.\n\nOne is to add support for commonly used but still missing\nfeatures to built-in one.  For this, you would need to extend\n\"struct grep_opt\" to hold new option parameters (e.g. if you\nwant to do \"-f\", you would need to hold all patterns you obtain\nfrom the named file so grep_buffer() can use them -- currently\nit supports only one pattern), and teach grep_buffer() how to do\nthe new feature.\n\nAnother thing you can do is to detect GIT_EXTERNAL_GREP (in the\nsame spirit as GIT_EXTERNAL_DIFF) environment variable at the\nfront of grep_buffer(), and when it is set, spawn the named\nexternal program with the original parameters the user supplied,\nprobably stashed away in \"struct grep_opt\" when cmd_grep() does\nits parameter parsing, and feed it the contents of the buffer.\n"},{"id":"19374","messageId":"Pine.LNX.4.64.0605021422200.4086@g5.osdl.org","threadId":"4014","inReplyTo":"7v1wvcmejr.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-05-02T21:23:05Z","receivedAt":"2006-05-02T21:23:05Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 2 May 2006, Junio C Hamano wrote:\n> \n>  - The shell-script one, if you use GNU grep, accepts more\n>    options to grep than what the current built-in one supports.\n>    Notable ones that are missing: fixed strings (-F), patterns\n>    from file (-f), count matches (-c), omit filenames (-h),\n>    skipping binary files (-I, -U), files without match (-L),\n>    pcre (-P), silent (-q), word expression (-w), NUL (-z).  They\n>    should be easy to add if somebody cares enough, and I plan to\n>    do a few myself before pushing it out to \"master\".\n\nI use \"-w\" all the time, along with -5 or similar to get context for the \ngrep.\n\n\t\tLinus\n"},{"id":"19378","messageId":"7vbqugks8j.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"Pine.LNX.4.64.0605021422200.4086@g5.osdl.org","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-02T21:54:20Z","receivedAt":"2006-05-02T21:54:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> On Tue, 2 May 2006, Junio C Hamano wrote:\n>> \n>>  - The shell-script one, if you use GNU grep, accepts more\n>>    options to grep than what the current built-in one supports.\n>>    Notable ones that are missing: fixed strings (-F), patterns\n>>    from file (-f), count matches (-c), omit filenames (-h),\n>>    skipping binary files (-I, -U), files without match (-L),\n>>    pcre (-P), silent (-q), word expression (-w), NUL (-z).  They\n>>    should be easy to add if somebody cares enough, and I plan to\n>>    do a few myself before pushing it out to \"master\".\n>\n> I use \"-w\" all the time, along with -5 or similar to get context for the \n> grep.\n\nNoted; -w is missing; -A/-B/-C are already there so you could\nsay -C 5 instead, and -<n> should be easy to add.\n\nOn a related tangent, ever since I started using the built-in\ngrep with ls-files like wildcard, I find myself typing something\nlike this by mistake (this is from my day-job work project that\nhas src/mx.js and src/mxstyle.css among other things):\n\n\tgit diff 268a94 -- 'src/mx*'\n\nI am tempted to suggest switching pathspecs used by diff and log\nfamily to do the same wildcarding, perhaps after tightening the\nwildcard vs directory prefix logic used in the builtin-grep of\nthe current \"next\" tip, which is a bit looser than necessary.\n"},{"id":"19383","messageId":"Pine.LNX.4.64.0605021603050.4086@g5.osdl.org","threadId":"4014","inReplyTo":"7vbqugks8j.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-05-02T23:07:45Z","receivedAt":"2006-05-02T23:07:45Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 2 May 2006, Junio C Hamano wrote:\n> \n> On a related tangent, ever since I started using the built-in\n> grep with ls-files like wildcard, I find myself typing something\n> like this by mistake (this is from my day-job work project that\n> has src/mx.js and src/mxstyle.css among other things):\n> \n> \tgit diff 268a94 -- 'src/mx*'\n> \n> I am tempted to suggest switching pathspecs used by diff and log\n> family to do the same wildcarding, perhaps after tightening the\n> wildcard vs directory prefix logic used in the builtin-grep of\n> the current \"next\" tip, which is a bit looser than necessary.\n\nYeah, the wildcarding is nice. You need to be very careful about it, \nthough, to make sure that you take full advantage of the path \ncomponent optimizations _before_ the wildcards, so that when you do \nsomething like the above ('src/mx*'), you do the \"src/\" part with the \ntree-level optimizations, and only the latter part with the pattern \nmatching (because you do _not_ want to expand the whole tree when you \ndon't want to).\n\nThat \"ls-files.c\" thing already does part of this (that whole \"prefix_len\" \nthing for the \"longest common prefix\").\n\n\t\tLinus\n"},{"id":"19393","messageId":"7vwtd4ht7f.fsf@assigned-by-dhcp.cox.net","threadId":"4014","inReplyTo":"7vbqugks8j.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] built-in \"git grep\" (git grip).","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-03T00:01:40Z","receivedAt":"2006-05-03T00:01:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> Linus Torvalds <torvalds@osdl.org> writes:\n>\n>> On Tue, 2 May 2006, Junio C Hamano wrote:\n>>> \n>>>  - The shell-script one, if you use GNU grep, accepts more\n>>>    options to grep than what the current built-in one supports.\n>>>    Notable ones that are missing: fixed strings (-F), patterns\n>>>    from file (-f), count matches (-c), omit filenames (-h),\n>>>    skipping binary files (-I, -U), files without match (-L),\n>>>    pcre (-P), silent (-q), word expression (-w), NUL (-z).  They\n>>>    should be easy to add if somebody cares enough, and I plan to\n>>>    do a few myself before pushing it out to \"master\".\n>>\n>> I use \"-w\" all the time, along with -5 or similar to get context for the \n>> grep.\n>\n> Noted; -w is missing; -A/-B/-C are already there so you could\n> say -C 5 instead, and -<n> should be easy to add.\n\nI did both -<n> and -w, and pushed it out in \"next\".\n\nWhat we have:\n\n        -<n>, -[ABC] <n> (and -[ABC]<n>)\n        -E\n        -G\n        -H (but it is an no-op -- we always show name)\n        -c\n        -e (you can do multiple patterns now)\n        -i\n        -n\n        -v\n        -w\n\t-l\n\nWhat are still missing:\n\n        -I (easy)\n        -L (probably a bit intrusive)\n        -P (code is easy -- deciding dependency on pcre is OK is harder)\n        -U (probably not so easy but may be useful)\n        -Z (probably easy but is it useful?)\n        -q (may not be worth doing)\n        -z (easy but pointless)\n\t-F (dunno)\n\t-f (with the enhancement to do multiple -e, trivial to add this)\n"}]}