{"thread":{"id":"2259","subject":"Be more careful about reference parsing","startedAt":"2005-10-28T19:41:49Z","lastAt":"2005-10-28T19:41:49Z","messageCount":1,"participants":["Linus Torvalds"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"10765","messageId":"Pine.LNX.4.64.0510281220290.4664@g5.osdl.org","threadId":"2259","inReplyTo":null,"subject":"Be more careful about reference parsing","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2005-10-28T19:41:49Z","receivedAt":"2005-10-28T19:41:49Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis does two things:\n\n - we don't allow \".\" and \"..\" as components of a refname. Thus get_sha1() \n   will not accept \"./refname\" as being the same as \"refname\" any more.\n\n - git-rev-parse stops doing revision translation after seeing a pathname, \n   to match the brhaviour of all the tools (once we see a pathname, \n   everything else will also be parsed as a pathname).\n\nBasically, if you did\n\n\tgit log *\n\nand \"gitk\" was somewhere in the \"*\", we don't want to replace the filename \n\"gitk\" with the SHA1 of the branch with the same name.\n\nOf course, if there is any change of ambiguity, you should always use \"--\" \nto make it explicit what are filenames and what are revisions, but this \nmakes the normal cases sane. The refname rule also means that instead of \nthe \"--\", you can do the same thing we're used to doing with filenames \nthat start with a slash: use \"./filename\" instead, and now it's a \nfilename, not an option (and not a revision).\n\nSo \"git log ./*.c\" is now actually a perfectly valid thing to do, even if \nthe first C-file might have the same name as a branch.\n\nTrivial test:\n\n\tgit-rev-parse gitk ./gitk gitk\n\nshould output something like\n\n\t9843c3074dfbf57117565f6b7c93e3e6812857ee\n\t./gitk\n\tgitk\n\nwhere the \"./gitk\" isn't seen as a revision, and the second \"gitk\" is a \nfilename simply because we've seen filenames already, and thus stopped \ndoing revision parsing.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n----\n\nI like my \"ambiguous_path()\" function. Now, admittedly it thinks that \n\".../\" is also an ambigious path since it doesn't count the dots. But \nsince we already don't like \"..\" inside refnames, we don't much care.\n\ndiff --git a/rev-parse.c b/rev-parse.c\nindex adfc68c..169d0cc 100644\n--- a/rev-parse.c\n+++ b/rev-parse.c\n@@ -292,6 +292,7 @@ int main(int argc, char **argv)\n \t\t}\n \t\tif (verify)\n \t\t\tdie(\"Needed a single revision\");\n+\t\tas_is = 1;\n \t\tshow_file(arg);\n \t}\n \tshow_default();\ndiff --git a/sha1_name.c b/sha1_name.c\nindex cc320d3..fe409fb 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -203,6 +203,29 @@ const char *find_unique_abbrev(const uns\n \treturn NULL;\n }\n \n+static int ambiguous_path(const char *path)\n+{\n+\tint slash = 1;\n+\n+\tfor (;;) {\n+\t\tswitch (*path++) {\n+\t\tcase '\\0':\n+\t\t\tbreak;\n+\t\tcase '/':\n+\t\t\tif (slash)\n+\t\t\t\tbreak;\n+\t\t\tslash = 1;\n+\t\t\tcontinue;\n+\t\tcase '.':\n+\t\t\tcontinue;\n+\t\tdefault:\n+\t\t\tslash = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\treturn slash;\n+\t}\n+}\n+\n static int get_sha1_basic(const char *str, int len, unsigned char *sha1)\n {\n \tstatic const char *prefix[] = {\n@@ -217,6 +240,10 @@ static int get_sha1_basic(const char *st\n \tif (len == 40 && !get_sha1_hex(str, sha1))\n \t\treturn 0;\n \n+\t/* Accept only unambiguous ref paths. */\n+\tif (ambiguous_path(str))\n+\t\treturn -1;\n+\n \tfor (p = prefix; *p; p++) {\n \t\tchar *pathname = git_path(\"%s/%.*s\", *p, len, str);\n \t\tif (!read_ref(pathname, sha1))\n"}]}