- /* foo^[0-9] or foo^ (== foo^1); we do not do more than 9 parents. */
- if (len > 2 && name[len-2] == '^' &&
- name[len-1] >= '0' && name[len-1] <= '9') {
- parent = name[len-1] - '0';
- len -= 2;
+ /*
+ * "ref^{type}" dereferences ref repeatedly until you cannot
+ * dereference anymore, or you get an object of given type,
+ * whichever comes first. "ref^{}" means just dereference
+ * tags until you get a non-tag. "ref^0" is a shorthand for
+ * "ref^{commit}". "commit^{tree}" could be used to find the
+ * top-level tree of the given commit.
+ */
+ if (len < 4 || name[len-1] != '}')
+ return -1;
+
+ for (sp = name + len - 1; name <= sp; sp--) {
+ int ch = *sp;
+ if (ch == '{' && name < sp && sp[-1] == '^')
+ break;
+ }
+ if (sp <= name)
+ return -1;
+
+ sp++; /* beginning of type name, or closing brace for empty */
+ if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
+ type_string = commit_type;
+ else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
+ type_string = tree_type;
+ else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
+ type_string = blob_type;
+ else if (sp[0] == '}')
+ type_string = NULL;
+ else
+ return -1;
+
+ if (get_sha1_1(name, sp - name - 2, outer))
+ return -1;
+
+ o = parse_object(outer);
+ if (!o)
+ return -1;
+ if (!type_string) {
+ o = deref_tag(o, name, sp - name - 2);
+ if (!o || (!o->parsed && !parse_object(o->sha1)))
+ return -1;
+ memcpy(sha1, o->sha1, 20);