{"thread":{"id":"17197","subject":"fatal: git grep: cannot generate relative filenames containing '..'","startedAt":"2009-01-15T22:29:05Z","lastAt":"2009-01-18T16:58:23Z","messageCount":11,"participants":["George Spelvin","Markus Heidelberg","Junio C Hamano","Johannes Schindelin","SZEDER Gábor","Jon Loeliger","Hannu Koivisto"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"100667","messageId":"20090115222905.8157.qmail@science.horizon.com","threadId":"17197","inReplyTo":null,"subject":"fatal: git grep: cannot generate relative filenames containing '..'","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2009-01-15T22:29:05Z","receivedAt":"2009-01-15T22:29:05Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"Could someone fix this some day?  \"git grep <pattern> ../include\"\nis something I find myself wanting quite frequently, and it's a fresh\nannoyance every time I type it to discover that it still doesn't work.\n\nWhile you're at it, an option to search the entire git tree rather than\nthe current subdirectory would also be useful.  I was thinking about\na flag like -r (for \"root\"), but a second idea dawned on me: interpret\nabsolute pathnames as relative to the root of the repository.  So I could\n\"git grep <pattern> /\" or \"git grep <pattern> /include\" from any subdirectory.\n\nAs it is currently, absolute pathnames don't work very well either...\n[1]$ git grep xyzzy $PWD\nfatal: '/home/linux/project/src' is outside repository\n[2]$ cd /usr/src/linux\n[3]$ git grep xyzzy $PWD\nCREDITS:E: rjd@xyzzy.clara.co.uk\ndrivers/hwmon/hwmon-vid.c: * With assistance from Trent Piepho <xyzzy@speakeasy.\ndrivers/infiniband/hw/ipath/ipath_debug.h: * if(infinipath_debug & _IPATH_xyzzy)\ndrivers/media/dvb/frontends/or51132.c: *    Copyright (C) 2007 Trent Piepho <xyz\ndrivers/media/video/cx88/cx88-alsa.c: *    (c) 2007 Trent Piepho <xyzzy@speakeas\ndrivers/video/intelfb/intelfbhw.c:      /* do some funky magic - xyzzy */\ninclude/linux/byteorder/swab.h: * Trent Piepho <xyzzy@speakeasy.org> 2007114\ninclude/linux/hwmon-vid.h:    With assistance from Trent Piepho <xyzzy@speakeasy\n[4]$ cd include\n[5]$ git grep xyzzy $PWD\nfatal: git grep: cannot generate relative filenames containing '..'\n\nI don't quite understand that last error message.\n\nThank you.\n"},{"id":"100671","messageId":"200901160004.27956.markus.heidelberg@web.de","threadId":"17197","inReplyTo":"20090115222905.8157.qmail@science.horizon.com","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"Markus Heidelberg","fromEmail":"markus.heidelberg@web.de","sentAt":"2009-01-15T23:04:27Z","receivedAt":"2009-01-15T23:04:27Z","isPatch":false,"sender":{"key":"markus.heidelberg@web.de","avatar":"https://avatars.githubusercontent.com/u/6334512?v=4"},"body":"George Spelvin, 15.01.2009:\n> While you're at it, an option to search the entire git tree rather than\n> the current subdirectory would also be useful.  I was thinking about\n> a flag like -r (for \"root\"), but a second idea dawned on me: interpret\n> absolute pathnames as relative to the root of the repository.  So I could\n> \"git grep <pattern> /\" or \"git grep <pattern> /include\" from any subdirectory.\n\nI have never used submodules execpt for trying out, but I think it would\nambigous when calling this command from inside a submodule. It's not\nclear, whether the main repo or the submodule should be used.\n\nMarkus\n"},{"id":"100681","messageId":"7vr634ow3r.fsf@gitster.siamese.dyndns.org","threadId":"17197","inReplyTo":"20090115222905.8157.qmail@science.horizon.com","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-16T01:52:56Z","receivedAt":"2009-01-16T01:52:56Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"George Spelvin\" <linux@horizon.com> writes:\n\n> Could someone fix this some day?\n\nAs far as I know you are the first to ask for this, so perhaps people never\nconsidered this was something to \"fix\"....\n\n> \"git grep <pattern> ../include\"\n> is something I find myself wanting quite frequently, and it's a fresh\n> annoyance every time I type it to discover that it still doesn't work.\n>\n> While you're at it, an option to search the entire git tree rather than\n> the current subdirectory would also be useful.  I was thinking about\n> a flag like -r (for \"root\"),...\n\n... but\n\n    $ grep -r foo ..\n\nwill look for foo anywhere below one level up from your current directory,\nand I think it may be a reasonable enhancement to teach:\n\n    $ git grep foo ..\n\nto do the same.  Patches welcome ;-).\n\nBy the way, congratulations for getting a name.  Can we fix one entry in\nour .mailmap file?\n"},{"id":"100685","messageId":"alpine.DEB.1.00.0901160307290.3586@pacific.mpi-cbg.de","threadId":"17197","inReplyTo":"20090115222905.8157.qmail@science.horizon.com","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-16T02:07:59Z","receivedAt":"2009-01-16T02:07:59Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 15 Jan 2009, George Spelvin wrote:\n\n> Could someone fix this some day?\n\nYes, someone could.\n\nCiao,\nDscho\n"},{"id":"100686","messageId":"7vhc40ov78.fsf@gitster.siamese.dyndns.org","threadId":"17197","inReplyTo":"alpine.DEB.1.00.0901160307290.3586@pacific.mpi-cbg.de","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-01-16T02:12:27Z","receivedAt":"2009-01-16T02:12:27Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Thu, 15 Jan 2009, George Spelvin wrote:\n>\n>> Could someone fix this some day?\n>\n> Yes, someone could.\n\nOr perhaps someone did more than two years ago with --full-name?\n"},{"id":"100695","messageId":"20090116032708.21156.qmail@science.horizon.com","threadId":"17197","inReplyTo":"7vhc40ov78.fsf@gitster.siamese.dyndns.org","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2009-01-16T03:27:08Z","receivedAt":"2009-01-16T03:27:08Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> wrote:\n> Or perhaps someone did more than two years ago with --full-name?\n\nThank you for pointing that out!  It's a bit of a handful to type,\nbut at least it makes what I want to do possible without having to\nwrite a wrapper script.  And I can reduce the typing with an alias.\n\nI'm still trying to figure out why the basic form doesn't work,\nthough.  Is there something more subtle than that fact that the\nfilename simplification in grep_sha1 and grep_file might do the\nwrong thing?  If I comment out the check in cmd_grep(), it seems\nto work, although it prints out some funky filenames.\n\nThat seems like a straightforward thing to fix.  Basically, strip\noff the common part of the prefix and name, and for every remaining\ncomponent in the prefix, prepend a ../ to the name.\n\nI don't mind doing the coding, but can someone who groks the\ncode more fully tell me if I'm missing something major?\n"},{"id":"100698","messageId":"20090116042432.11434.qmail@science.horizon.com","threadId":"17197","inReplyTo":"20090116032708.21156.qmail@science.horizon.com","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2009-01-16T04:24:32Z","receivedAt":"2009-01-16T04:24:32Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"> I don't mind doing the coding, but can someone who groks the\n> code more fully tell me if I'm missing something major?\n\nHere's a first draft, that Works For Me(tm).  Does anyone see anything\nbroken about it?\n\n(This code released into the public domain, copyright abandoned.  You may\ndo anything you like with it, including evil things, as long as you\ndon't bother me asking for additional permissions.)\n\ndiff --git a/builtin-grep.c b/builtin-grep.c\nindex bebf15c..5727a8b 100644\n--- a/builtin-grep.c\n+++ b/builtin-grep.c\n@@ -90,12 +90,74 @@ static int pathspec_matches(const char **paths, const char *name)\n \treturn 0;\n }\n \n+/*\n+ * Write a filename like \"/usr/src/linux/include/linux/zlib.h\" as\n+ * a path relative to some prefix like \"/usr/src/linux/kernel/\"\n+ * (The prefix always includes a trailing slash.)\n+ * label_len, if non-zero, describes a leading portion on the name\n+ * (typically of the form \"HEAD^^:\") which should not be stripped off.\n+ * \n+ * The result is in one of three places:\n+ * - It may be a pointer into the supplied name in the simplest case,\n+ * - It may be returned in a static buffer if it is of reaonable size, or\n+ * - It may be in a malloced buffer if it is large.\n+ * A pointer to be passed to free() is returned in *to_free, which\n+ * is set to NULL if the return value is not to be freed, or is equal\n+ * to the return value if it is.  (It could be simply a boolean, but doing\n+ * it this way eliminates a test in the caller.)\n+ */\n+static const char *make_relative(unsigned label_len, const char *prefix, unsigned prefix_len, const char *name, void **to_free)\n+{\n+\tstatic char name_buf[PATH_MAX];\n+\tchar *cp;\n+\tunsigned n, match_len = 0, slashes = 0;\n+\tunsigned name_len;\n+\n+\tfor (n = 0; n < prefix_len; n++) {\n+\t\tif (prefix[n] != name[label_len+n]) {\n+\t\t\tfor (; n < prefix_len; n++)\n+\t\t\t\tslashes += (prefix[n] == '/');\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (prefix[n] == '/')\n+\t\t\tmatch_len = n+1;\n+\t}\n+\n+\t/* Can we return a substring of the input string? */\n+\tif (!slashes && (!label_len || !match_len)) {\n+\t\t*to_free = NULL;\n+\t\treturn name + match_len;\n+\t}\n+\n+\t/* Nope, assemble the full response */\n+\n+\t/* Output buffer will be tag + \"../\"*slashes + name + '\\0' */\n+\tname_len = strlen(name + label_len + match_len) + 1;\n+\tn = label_len + 3*slashes + name_len + 1;\n+\tif (n <= ARRAY_SIZE(name_buf)) {\n+\t\tcp = name_buf;\n+\t\t*to_free = NULL;\n+\t} else {\n+\t\t*to_free = cp = xmalloc(n);\n+\t}\n+\n+\t/* Now fill in the buffer */\n+\tmemcpy(cp, name, label_len);\n+\tn = label_len;\n+\twhile (slashes--) {\n+\t\tmemcpy(cp+n, \"../\", 3);\n+\t\tn += 3;\n+\t}\n+\tmemcpy(cp+n, name+label_len+match_len, name_len);\n+\treturn cp;\n+}\n+\n static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name, int tree_name_len)\n {\n \tunsigned long size;\n \tchar *data;\n \tenum object_type type;\n-\tchar *to_free = NULL;\n+\tvoid *to_free = NULL;\n \tint hit;\n \n \tdata = read_sha1_file(sha1, &type, &size);\n@@ -103,24 +165,9 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char\n \t\terror(\"'%s': unable to read %s\", name, sha1_to_hex(sha1));\n \t\treturn 0;\n \t}\n-\tif (opt->relative && opt->prefix_length) {\n-\t\tstatic char name_buf[PATH_MAX];\n-\t\tchar *cp;\n-\t\tint name_len = strlen(name) - opt->prefix_length + 1;\n-\n-\t\tif (!tree_name_len)\n-\t\t\tname += opt->prefix_length;\n-\t\telse {\n-\t\t\tif (ARRAY_SIZE(name_buf) <= name_len)\n-\t\t\t\tcp = to_free = xmalloc(name_len);\n-\t\t\telse\n-\t\t\t\tcp = name_buf;\n-\t\t\tmemcpy(cp, name, tree_name_len);\n-\t\t\tstrcpy(cp + tree_name_len,\n-\t\t\t       name + tree_name_len + opt->prefix_length);\n-\t\t\tname = cp;\n-\t\t}\n-\t}\n+\tif (opt->relative)\n+\t\tname = make_relative(tree_name_len, opt->prefix,\n+\t\t                     opt->prefix_length, name, &to_free);\n \thit = grep_buffer(opt, name, data, size);\n \tfree(data);\n \tfree(to_free);\n@@ -132,6 +179,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)\n \tstruct stat st;\n \tint i;\n \tchar *data;\n+\tvoid *to_free = NULL;\n \tsize_t sz;\n \n \tif (lstat(filename, &st) < 0) {\n@@ -156,10 +204,12 @@ static int grep_file(struct grep_opt *opt, const char *filename)\n \t\treturn 0;\n \t}\n \tclose(i);\n-\tif (opt->relative && opt->prefix_length)\n-\t\tfilename += opt->prefix_length;\n+\tif (opt->relative)\n+\t\tfilename = make_relative(0, opt->prefix, opt->prefix_length,\n+\t\t                         filename, &to_free);\n \ti = grep_buffer(opt, filename, data, sz);\n \tfree(data);\n+\tfree(to_free);\n \treturn i;\n }\n \n@@ -528,7 +578,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \tint i;\n \n \tmemset(&opt, 0, sizeof(opt));\n-\topt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;\n+\topt.prefix = prefix;\n+\topt.prefix_length = prefix ? strlen(prefix) : 0;\n \topt.relative = 1;\n \topt.pathname = 1;\n \topt.pattern_tail = &opt.pattern_list;\n@@ -787,17 +838,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\t\tverify_filename(prefix, argv[j]);\n \t}\n \n-\tif (i < argc) {\n+\tif (i < argc)\n \t\tpaths = get_pathspec(prefix, argv + i);\n-\t\tif (opt.prefix_length && opt.relative) {\n-\t\t\t/* Make sure we do not get outside of paths */\n-\t\t\tfor (i = 0; paths[i]; i++)\n-\t\t\t\tif (strncmp(prefix, paths[i], opt.prefix_length))\n-\t\t\t\t\tdie(\"git grep: cannot generate relative filenames containing '..'\");\n-\t\t}\n-\t}\n \telse if (prefix) {\n-\t\tpaths = xcalloc(2, sizeof(const char *));\n+\t\tpaths = xmalloc(2 * sizeof *paths);\n \t\tpaths[0] = prefix;\n \t\tpaths[1] = NULL;\n \t}\ndiff --git a/grep.h b/grep.h\nindex 45a222d..a02dccf 100644\n--- a/grep.h\n+++ b/grep.h\n@@ -56,6 +56,7 @@ struct grep_opt {\n \tstruct grep_pat *pattern_list;\n \tstruct grep_pat **pattern_tail;\n \tstruct grep_expr *pattern_expression;\n+\tconst char *prefix;\n \tint prefix_length;\n \tregex_t regexp;\n \tunsigned linenum:1;\n"},{"id":"100719","messageId":"20090116114144.GA29537@neumann","threadId":"17197","inReplyTo":"20090116032708.21156.qmail@science.horizon.com","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"SZEDER Gábor","fromEmail":"szeder@fzi.de","sentAt":"2009-01-16T11:41:44Z","receivedAt":"2009-01-16T11:41:44Z","isPatch":false,"sender":{"key":"szeder@fzi.de","avatar":null},"body":"On Thu, Jan 15, 2009 at 10:27:08PM -0500, George Spelvin wrote:\n> Junio C Hamano <gitster@pobox.com> wrote:\n> > Or perhaps someone did more than two years ago with --full-name?\n> \n> Thank you for pointing that out!  It's a bit of a handful to type,\n\nWell, if you happen to use bash, you will find our excellent ;) bash\ncompletion script under contrib/completion.\n\nhth,\nGábor\n"},{"id":"100777","messageId":"1232141442.5843.18.camel@ld0161-tx32","threadId":"17197","inReplyTo":"7vr634ow3r.fsf@gitster.siamese.dyndns.org","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"Jon Loeliger","fromEmail":"jdl@freescale.com","sentAt":"2009-01-16T21:30:42Z","receivedAt":"2009-01-16T21:30:42Z","isPatch":false,"sender":{"key":"jdl@jdl.com","avatar":"https://gravatar.com/avatar/75ce9a10b151acd2c28ec4ab2136dba7b2ff1634530bd04b155981a749d08a64?d=mp&s=160"},"body":"On Thu, 2009-01-15 at 17:52 -0800, Junio C Hamano wrote:\n> \"George Spelvin\" <linux@horizon.com> writes:\n> \n\n> By the way, congratulations for getting a name.  Can we fix one entry in\n> our .mailmap file?\n\n\nHmmm....\n\nhttp://en.wikipedia.org/wiki/George_Spelvin\n\n    George Spelvin, Georgette Spelvin, and Georgina Spelvin are the\n    traditional pseudonyms used in programs in American theater by\n    actors who don't want to be credited or whose names would\n    otherwise appear twice because they are playing more than one\n    role in a production.\n\njdl\n"},{"id":"100789","messageId":"83vdsefz9j.fsf@kalahari.s2.org","threadId":"17197","inReplyTo":"7vr634ow3r.fsf@gitster.siamese.dyndns.org","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"Hannu Koivisto","fromEmail":"azure@iki.fi","sentAt":"2009-01-17T02:21:44Z","receivedAt":"2009-01-17T02:21:44Z","isPatch":false,"sender":{"key":"azure@iki.fi","avatar":null},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> \"George Spelvin\" <linux@horizon.com> writes:\n>\n>> Could someone fix this some day?\n>\n> As far as I know you are the first to ask for this, so perhaps people never\n> considered this was something to \"fix\"....\n\nI for one have faced that problem and considered it something that\nwould be nice to have fixed.  Then again, I consider the default\nbehaviour (when you pass no path) a bigger usability problem.\n\n>> While you're at it, an option to search the entire git tree rather than\n>> the current subdirectory would also be useful.  I was thinking about\n>> a flag like -r (for \"root\"),...\n\nIt turns out that this \"entire git tree\" is practically my only use\ncase when using normal (find and) grep and when I first tried git\ngrep, I actually expected it to do just that with no path specified.\n\nWhy?  Because of the way at least git diff and git log work.  I\nthought that just like with them, \"git grep foo\" would search the\nentire git tree and I would have to say \"git grep foo .\" to limit to\nthe current directory and its subdirectories.  git-grep(1)'s \"Look\nfor specified patterns in the working tree files...\" at least\ndidn't seem to disagree with my expectation so I was a bit puzzled.\n\nSo I'd rather see git grep behave in a way consistent with git log\nand git diff (I realize that would change current behaviour instead\nof extending it).\n\n-- \nHannu\n"},{"id":"101019","messageId":"20090118165823.24501.qmail@science.horizon.com","threadId":"17197","inReplyTo":"83vdsefz9j.fsf@kalahari.s2.org","subject":"Re: fatal: git grep: cannot generate relative filenames containing '..'","fromName":"George Spelvin","fromEmail":"linux@horizon.com","sentAt":"2009-01-18T16:58:23Z","receivedAt":"2009-01-18T16:58:23Z","isPatch":false,"sender":{"key":"linux@horizon.com","avatar":null},"body":"Hannu Koivisto <azure@iki.fi> wrote:\n> It turns out that this \"entire git tree\" is practically my only use\n> case when using normal (find and) grep and when I first tried git\n> grep, I actually expected it to do just that with no path specified.\n>\n> Why?  Because of the way at least git diff and git log work.  I\n> thought that just like with them, \"git grep foo\" would search the\n> entire git tree and I would have to say \"git grep foo .\" to limit to\n> the current directory and its subdirectories.  git-grep(1)'s \"Look\n> for specified patterns in the working tree files...\" at least\n> didn't seem to disagree with my expectation so I was a bit puzzled.\n>\n> So I'd rather see git grep behave in a way consistent with git log\n> and git diff (I realize that would change current behaviour instead\n> of extending it).\n\nD'oh.  You're completely right.  Space-dot is trivial to type if you\nwant the current directory only, and that is more consistent.\n\nCould that be considered for 1.7?\n"}]}