|
1 | 1 | #include "cache.h"
|
| 2 | +#include "tag.h" |
2 | 3 | #include "commit.h"
|
| 4 | +#include "tree.h" |
| 5 | +#include "blob.h" |
3 | 6 |
|
4 | 7 | static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
|
5 | 8 | {
|
@@ -274,6 +277,82 @@ static int get_nth_ancestor(const char *name, int len,
|
274 | 277 | return 0;
|
275 | 278 | }
|
276 | 279 |
|
| 280 | +static int peel_onion(const char *name, int len, unsigned char *sha1) |
| 281 | +{ |
| 282 | + unsigned char outer[20]; |
| 283 | + const char *sp; |
| 284 | + const char *type_string = NULL; |
| 285 | + struct object *o; |
| 286 | + |
| 287 | + /* |
| 288 | + * "ref^{type}" dereferences ref repeatedly until you cannot |
| 289 | + * dereference anymore, or you get an object of given type, |
| 290 | + * whichever comes first. "ref^{}" means just dereference |
| 291 | + * tags until you get a non-tag. "ref^0" is a shorthand for |
| 292 | + * "ref^{commit}". "commit^{tree}" could be used to find the |
| 293 | + * top-level tree of the given commit. |
| 294 | + */ |
| 295 | + if (len < 4 || name[len-1] != '}') |
| 296 | + return -1; |
| 297 | + |
| 298 | + for (sp = name + len - 1; name <= sp; sp--) { |
| 299 | + int ch = *sp; |
| 300 | + if (ch == '{' && name < sp && sp[-1] == '^') |
| 301 | + break; |
| 302 | + } |
| 303 | + if (sp <= name) |
| 304 | + return -1; |
| 305 | + |
| 306 | + sp++; /* beginning of type name, or closing brace for empty */ |
| 307 | + if (!strncmp(commit_type, sp, 6) && sp[6] == '}') |
| 308 | + type_string = commit_type; |
| 309 | + else if (!strncmp(tree_type, sp, 4) && sp[4] == '}') |
| 310 | + type_string = tree_type; |
| 311 | + else if (!strncmp(blob_type, sp, 4) && sp[4] == '}') |
| 312 | + type_string = blob_type; |
| 313 | + else if (sp[0] == '}') |
| 314 | + type_string = NULL; |
| 315 | + else |
| 316 | + return -1; |
| 317 | + |
| 318 | + if (get_sha1_1(name, sp - name - 2, outer)) |
| 319 | + return -1; |
| 320 | + |
| 321 | + o = parse_object(outer); |
| 322 | + if (!o) |
| 323 | + return -1; |
| 324 | + if (!type_string) { |
| 325 | + o = deref_tag(o); |
| 326 | + memcpy(sha1, o->sha1, 20); |
| 327 | + } |
| 328 | + else { |
| 329 | + /* At this point, the syntax look correct, so |
| 330 | + * if we do not get the needed object, we should |
| 331 | + * barf. |
| 332 | + */ |
| 333 | + |
| 334 | + while (1) { |
| 335 | + if (!o) |
| 336 | + return -1; |
| 337 | + if (o->type == type_string) { |
| 338 | + memcpy(sha1, o->sha1, 20); |
| 339 | + return 0; |
| 340 | + } |
| 341 | + if (o->type == tag_type) |
| 342 | + o = ((struct tag*) o)->tagged; |
| 343 | + else if (o->type == commit_type) |
| 344 | + o = &(((struct commit *) o)->tree->object); |
| 345 | + else |
| 346 | + return error("%.*s: expected %s type, but the object dereferences to %s type", |
| 347 | + len, name, type_string, |
| 348 | + o->type); |
| 349 | + if (!o->parsed) |
| 350 | + parse_object(o->sha1); |
| 351 | + } |
| 352 | + } |
| 353 | + return 0; |
| 354 | +} |
| 355 | + |
277 | 356 | static int get_sha1_1(const char *name, int len, unsigned char *sha1)
|
278 | 357 | {
|
279 | 358 | int parent, ret;
|
@@ -315,6 +394,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
|
315 | 394 | return get_nth_ancestor(name, len1, sha1, parent);
|
316 | 395 | }
|
317 | 396 |
|
| 397 | + ret = peel_onion(name, len, sha1); |
| 398 | + if (!ret) |
| 399 | + return 0; |
| 400 | + |
318 | 401 | ret = get_sha1_basic(name, len, sha1);
|
319 | 402 | if (!ret)
|
320 | 403 | return 0;
|
|
0 commit comments