{"thread":{"id":"2102","subject":"[PATCH] Introduce notation \"ref^{type}\".","startedAt":"2005-10-14T06:03:49Z","lastAt":"2005-10-14T06:03:49Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"10092","messageId":"7vd5m8ws6y.fsf@assigned-by-dhcp.cox.net","threadId":"2102","inReplyTo":null,"subject":"[PATCH] Introduce notation \"ref^{type}\".","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-10-14T06:03:49Z","receivedAt":"2005-10-14T06:03:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Existing \"tagname^0\" notation means \"dereference tag zero or more\ntimes until you cannot dereference it anymore, and make sure it is a\ncommit -- otherwise barf\".  But tags do not necessarily reference\ncommit objects.\n\nThis commit introduces a bit more generalized notation, \"ref^{type}\".\nExisting \"ref^0\" is a shorthand for \"ref^{commit}\".  If the type\nis empty, it just dereferences tags until it hits a non-tag object.\n\nWith this, \"git-rev-parse --verify 'junio-gpg-pub^{}'\" shows the blob\nobject name -- there is no need to manually read the tag object and\nfind out the object name anymore.\n\n\"git-rev-parse --verify 'HEAD^{tree}'\" can be used to find out the\ntree object name of the HEAD commit.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n * This is another preparation, which is not strictly needed but\n   for notational consistency.  The next one is the gem in the\n   series -- upload-pack starts sending extra information on\n   refs it has using this notation, so that client side can find\n   out what each tag on the remote side refers to without first\n   getting them.\n\n sha1_name.c |   83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 83 insertions(+), 0 deletions(-)\n\napplies-to: 8edf2fed07b27673ab24e1bb40a0887f757d60b0\n24552507a1fd71c707199bca0a225f2715a6759b\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 4e9a052..75c688e 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1,5 +1,8 @@\n #include \"cache.h\"\n+#include \"tag.h\"\n #include \"commit.h\"\n+#include \"tree.h\"\n+#include \"blob.h\"\n \n static int find_short_object_filename(int len, const char *name, unsigned char *sha1)\n {\n@@ -274,6 +277,82 @@ static int get_nth_ancestor(const char *\n \treturn 0;\n }\n \n+static int peel_onion(const char *name, int len, unsigned char *sha1)\n+{\n+\tunsigned char outer[20];\n+\tconst char *sp;\n+\tconst char *type_string = NULL;\n+\tstruct object *o;\n+\n+\t/*\n+\t * \"ref^{type}\" dereferences ref repeatedly until you cannot\n+\t * dereference anymore, or you get an object of given type,\n+\t * whichever comes first.  \"ref^{}\" means just dereference\n+\t * tags until you get a non-tag.  \"ref^0\" is a shorthand for\n+\t * \"ref^{commit}\".  \"commit^{tree}\" could be used to find the\n+\t * top-level tree of the given commit.\n+\t */\n+\tif (len < 4 || name[len-1] != '}')\n+\t\treturn -1;\n+\n+\tfor (sp = name + len - 1; name <= sp; sp--) {\n+\t\tint ch = *sp;\n+\t\tif (ch == '{' && name < sp && sp[-1] == '^')\n+\t\t\tbreak;\n+\t}\n+\tif (sp <= name)\n+\t\treturn -1;\n+\n+\tsp++; /* beginning of type name, or closing brace for empty */\n+\tif (!strncmp(commit_type, sp, 6) && sp[6] == '}')\n+\t\ttype_string = commit_type;\n+\telse if (!strncmp(tree_type, sp, 4) && sp[4] == '}')\n+\t\ttype_string = tree_type;\n+\telse if (!strncmp(blob_type, sp, 4) && sp[4] == '}')\n+\t\ttype_string = blob_type;\n+\telse if (sp[0] == '}')\n+\t\ttype_string = NULL;\n+\telse\n+\t\treturn -1;\n+\n+\tif (get_sha1_1(name, sp - name - 2, outer))\n+\t\treturn -1;\n+\n+\to = parse_object(outer);\n+\tif (!o)\n+\t\treturn -1;\n+\tif (!type_string) {\n+\t\to = deref_tag(o);\n+\t\tmemcpy(sha1, o->sha1, 20);\n+\t}\n+\telse {\n+\t\t/* At this point, the syntax look correct, so\n+\t\t * if we do not get the needed object, we should\n+\t\t * barf.\n+\t\t */\n+\n+\t\twhile (1) {\n+\t\t\tif (!o)\n+\t\t\t\treturn -1;\n+\t\t\tif (o->type == type_string) {\n+\t\t\t\tmemcpy(sha1, o->sha1, 20);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tif (o->type == tag_type)\n+\t\t\t\to = ((struct tag*) o)->tagged;\n+\t\t\telse if (o->type == commit_type)\n+\t\t\t\to = &(((struct commit *) o)->tree->object);\n+\t\t\telse\n+\t\t\t\treturn error(\"%.*s: expected %s type, but the object dereferences to %s type\",\n+\t\t\t\t\t     len, name, type_string,\n+\t\t\t\t\t     o->type);\n+\t\t\tif (!o->parsed)\n+\t\t\t\tparse_object(o->sha1);\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n static int get_sha1_1(const char *name, int len, unsigned char *sha1)\n {\n \tint parent, ret;\n@@ -315,6 +394,10 @@ static int get_sha1_1(const char *name, \n \t\treturn get_nth_ancestor(name, len1, sha1, parent);\n \t}\n \n+\tret = peel_onion(name, len, sha1);\n+\tif (!ret)\n+\t\treturn 0;\n+\n \tret = get_sha1_basic(name, len, sha1);\n \tif (!ret)\n \t\treturn 0;\n---\n@@GIT_VERSION@@\n"}]}