]> git.proxmox.com Git - libgit2.git/blame - src/revparse.c
fetch: declare variables at the top of the block
[libgit2.git] / src / revparse.c
CommitLineData
ac250c56
BS
1/*
2 * Copyright (C) 2009-2012 the libgit2 contributors
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include <assert.h>
9
10#include "common.h"
11#include "buffer.h"
244d2f6b 12#include "tree.h"
ac250c56 13
a346992f 14#include "git2.h"
ac250c56 15
cab65c2b 16static int revspec_error(const char *revspec)
023c6f69 17{
cab65c2b 18 giterr_set(GITERR_INVALID, "Failed to parse revision specifier - Invalid pattern '%s'", revspec);
19 return -1;
023c6f69
BS
20}
21
e7279381 22static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname)
ac250c56 23{
e7279381 24 int error, i;
25 bool fallbackmode = true;
26 git_reference *ref;
27 git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
28
e28dd29b 29 static const char* formatters[] = {
e7279381 30 "%s",
74a24005 31 GIT_REFS_DIR "%s",
32 GIT_REFS_TAGS_DIR "%s",
33 GIT_REFS_HEADS_DIR "%s",
34 GIT_REFS_REMOTES_DIR "%s",
35 GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE,
e28dd29b 36 NULL
37 };
e7279381 38
39 if (*refname)
40 git_buf_puts(&name, refname);
41 else {
b8748c12 42 git_buf_puts(&name, GIT_HEAD_FILE);
e7279381 43 fallbackmode = false;
44 }
45
46 for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {
47
48 git_buf_clear(&refnamebuf);
49
50 if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
51 goto cleanup;
52
77e06d7e 53 if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
54 error = GIT_ENOTFOUND;
55 continue;
56 }
57
e7279381 58 error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1);
59
60 if (!error) {
61 *out = ref;
62 error = 0;
63 goto cleanup;
64 }
65
66 if (error != GIT_ENOTFOUND)
67 goto cleanup;
68 }
69
70cleanup:
71 git_buf_free(&name);
72 git_buf_free(&refnamebuf);
73 return error;
74}
75
8f17ed80 76static int maybe_sha_or_abbrev(git_object**out, git_repository *repo, const char *spec)
77{
78 git_oid oid;
79 size_t speclen = strlen(spec);
80
81 if (git_oid_fromstrn(&oid, spec, speclen) < 0)
82 return GIT_ENOTFOUND;
83
84 return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJ_ANY);
85}
3e82d6c6 86
b8748c12 87static int build_regex(regex_t *regex, const char *pattern)
88{
89 int error;
90
91 if (*pattern == '\0') {
92 giterr_set(GITERR_REGEX, "Empty pattern");
93 return -1;
94 }
95
96 error = regcomp(regex, pattern, REG_EXTENDED);
97 if (!error)
98 return 0;
99
100 giterr_set_regex(regex, error);
101 regfree(regex);
102
103 return -1;
104}
105
3e82d6c6 106static int maybe_describe(git_object**out, git_repository *repo, const char *spec)
e7279381 107{
e28dd29b 108 const char *substr;
b8748c12 109 int error;
110 regex_t regex;
e28dd29b 111
e28dd29b 112 substr = strstr(spec, "-g");
e28dd29b 113
3e82d6c6 114 if (substr == NULL)
115 return GIT_ENOTFOUND;
116
b8748c12 117 if (build_regex(&regex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0)
118 return -1;
3e82d6c6 119
b8748c12 120 error = regexec(&regex, spec, 0, NULL, 0);
121 regfree(&regex);
122
123 if (error)
3e82d6c6 124 return GIT_ENOTFOUND;
125
8f17ed80 126 return maybe_sha_or_abbrev(out, repo, substr+2);
3e82d6c6 127}
128
8f17ed80 129static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
3e82d6c6 130{
131 int error;
132 git_reference *ref;
133
134 error = maybe_describe(out, repo, spec);
135 if (!error)
136 return 0;
137
138 if (error < 0 && error != GIT_ENOTFOUND)
139 return error;
140
e7279381 141 error = disambiguate_refname(&ref, repo, spec);
142 if (!error) {
143 error = git_object_lookup(out, repo, git_reference_oid(ref), GIT_OBJ_ANY);
144 git_reference_free(ref);
b5f90115 145 return error;
e28dd29b 146 }
147
d1b7921a 148 if (error < 0 && error != GIT_ENOTFOUND)
149 return error;
150
151 error = maybe_sha_or_abbrev(out, repo, spec);
152 if (!error)
153 return 0;
154
3e82d6c6 155 if (error < 0 && error != GIT_ENOTFOUND)
e7279381 156 return error;
e28dd29b 157
158 giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec);
4de89ce7 159 return GIT_ENOTFOUND;
ac250c56
BS
160}
161
b8748c12 162static int try_parse_numeric(int *n, const char *curly_braces_content)
a346992f 163{
b8748c12 164 int content;
165 const char *end_ptr;
166
167 if (git__strtol32(&content, curly_braces_content, &end_ptr, 10) < 0)
168 return -1;
29f72aa6 169
b8748c12 170 if (*end_ptr != '\0')
171 return -1;
29f72aa6 172
b8748c12 173 *n = content;
174 return 0;
a346992f
BS
175}
176
b8748c12 177static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *spec, const char *identifier, unsigned int position)
ac250c56 178{
b8748c12 179 git_reference *ref = NULL;
e28dd29b 180 git_reflog *reflog = NULL;
b8748c12 181 regex_t preg;
182 int numentries, i, cur, error = -1;
e28dd29b 183 const git_reflog_entry *entry;
b8748c12 184 const char *msg;
185 regmatch_t regexmatches[2];
e28dd29b 186 git_buf buf = GIT_BUF_INIT;
e28dd29b 187
b8748c12 188 cur = position;
e28dd29b 189
b8748c12 190 if (*identifier != '\0' || *base_ref != NULL)
191 return revspec_error(spec);
e28dd29b 192
b8748c12 193 if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0)
194 return -1;
cab65c2b 195
b8748c12 196 if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
197 goto cleanup;
e28dd29b 198
b8748c12 199 if (git_reflog_read(&reflog, ref) < 0)
200 goto cleanup;
201
202 numentries = git_reflog_entrycount(reflog);
203
204 for (i = numentries - 1; i >= 0; i--) {
205 entry = git_reflog_entry_byindex(reflog, i);
206 msg = git_reflog_entry_msg(entry);
207
208 if (regexec(&preg, msg, 2, regexmatches, 0))
209 continue;
210
211 cur--;
212
213 if (cur > 0)
214 continue;
215
216 git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so);
217
218 if ((error = disambiguate_refname(base_ref, repo, git_buf_cstr(&buf))) == 0)
219 goto cleanup;
220
221 if (error < 0 && error != GIT_ENOTFOUND)
222 goto cleanup;
223
224 error = maybe_sha_or_abbrev(out, repo, git_buf_cstr(&buf));
225
226 goto cleanup;
227 }
228
229 error = GIT_ENOTFOUND;
230
231cleanup:
232 git_reference_free(ref);
233 git_buf_free(&buf);
234 regfree(&preg);
235 git_reflog_free(reflog);
236 return error;
237}
238
239static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, unsigned int identifier)
240{
241 git_reflog *reflog;
242 int error = -1;
243 unsigned int numentries;
244 const git_reflog_entry *entry;
245 bool search_by_pos = (identifier <= 100000000);
246
247 if (git_reflog_read(&reflog, ref) < 0)
248 return -1;
249
250 numentries = git_reflog_entrycount(reflog);
251
252 if (search_by_pos) {
253 if (numentries < identifier + 1) {
254 giterr_set(
255 GITERR_REFERENCE,
256 "Reflog for '%s' has only %d entries, asked for %d",
257 git_reference_name(ref),
258 numentries,
259 identifier);
260
261 error = GIT_ENOTFOUND;
262 goto cleanup;
e28dd29b 263 }
b8748c12 264
265 entry = git_reflog_entry_byindex(reflog, identifier);
266 git_oid_cpy(oid, git_reflog_entry_oidold(entry));
267 error = 0;
268 goto cleanup;
269
e28dd29b 270 } else {
b8748c12 271 int i;
272 git_time commit_time;
e28dd29b 273
b8748c12 274 for (i = numentries - 1; i >= 0; i--) {
275 entry = git_reflog_entry_byindex(reflog, i);
276 commit_time = git_reflog_entry_committer(entry)->when;
277
278 if (commit_time.time - identifier > 0)
279 continue;
e7279381 280
b8748c12 281 git_oid_cpy(oid, git_reflog_entry_oidnew(entry));
282 error = 0;
e7279381 283 goto cleanup;
284 }
285
b8748c12 286 error = GIT_ENOTFOUND;
287 }
e28dd29b 288
b8748c12 289cleanup:
290 git_reflog_free(reflog);
291 return error;
292}
e28dd29b 293
b8748c12 294static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, unsigned int position)
295{
296 git_reference *ref;
297 git_oid oid;
298 int error = -1;
e28dd29b 299
b8748c12 300 if (*base_ref == NULL) {
301 if ((error = disambiguate_refname(&ref, repo, identifier)) < 0)
302 return error;
303 } else {
304 ref = *base_ref;
305 *base_ref = NULL;
306 }
e28dd29b 307
b8748c12 308 if (position == 0) {
309 error = git_object_lookup(out, repo, git_reference_oid(ref), GIT_OBJ_ANY);
310 goto cleanup;
311 }
e28dd29b 312
b8748c12 313 if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0)
314 goto cleanup;
e28dd29b 315
b8748c12 316 error = git_object_lookup(out, repo, &oid, GIT_OBJ_ANY);
317
318cleanup:
319 git_reference_free(ref);
320 return error;
321}
322
323static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo)
324{
325 git_reference *tracking, *ref;
326 int error = -1;
327
328 if (*base_ref == NULL) {
329 if ((error = disambiguate_refname(&ref, repo, identifier)) < 0)
330 return error;
331 } else {
332 ref = *base_ref;
333 *base_ref = NULL;
e28dd29b 334 }
335
fb910281 336 if ((error = git_branch_tracking(&tracking, ref)) < 0)
b8748c12 337 goto cleanup;
338
339 *base_ref = tracking;
340
e7279381 341cleanup:
b8748c12 342 git_reference_free(ref);
343 return error;
344}
345
b8457baa 346static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository* repo, const char *curly_braces_content)
b8748c12 347{
348 bool is_numeric;
7e48635d 349 int parsed = 0, error = -1;
b8748c12 350 git_buf identifier = GIT_BUF_INIT;
351 git_time_t timestamp;
352
353 assert(*out == NULL);
354
355 if (git_buf_put(&identifier, spec, identifier_len) < 0)
356 return -1;
357
358 is_numeric = !try_parse_numeric(&parsed, curly_braces_content);
359
360 if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) {
361 error = revspec_error(spec);
362 goto cleanup;
363 }
364
365 if (is_numeric) {
366 if (parsed < 0)
367 error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, spec, git_buf_cstr(&identifier), -parsed);
368 else
369 error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), parsed);
370
371 goto cleanup;
372 }
373
374 if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) {
375 error = retrieve_remote_tracking_reference(ref, git_buf_cstr(&identifier), repo);
376
377 goto cleanup;
378 }
379
380 if (git__date_parse(&timestamp, curly_braces_content) < 0)
381 goto cleanup;
382
383 error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), (unsigned int)timestamp);
384
385cleanup:
386 git_buf_free(&identifier);
387 return error;
023c6f69
BS
388}
389
9d7bdf71
BS
390static git_otype parse_obj_type(const char *str)
391{
b8748c12 392 if (!strcmp(str, "commit"))
393 return GIT_OBJ_COMMIT;
394
395 if (!strcmp(str, "tree"))
396 return GIT_OBJ_TREE;
397
398 if (!strcmp(str, "blob"))
399 return GIT_OBJ_BLOB;
400
401 if (!strcmp(str, "tag"))
402 return GIT_OBJ_TAG;
403
e28dd29b 404 return GIT_OBJ_BAD;
9d7bdf71
BS
405}
406
b8748c12 407static int dereference_to_non_tag(git_object **out, git_object *obj)
023c6f69 408{
b8748c12 409 if (git_object_type(obj) == GIT_OBJ_TAG)
410 return git_tag_peel(out, (git_tag *)obj);
e28dd29b 411
b8748c12 412 return git_object__dup(out, obj);
413}
e28dd29b 414
b8748c12 415static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n)
416{
417 git_object *temp_commit = NULL;
418 int error;
e28dd29b 419
e2c81fca 420 if (git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT) < 0)
b8748c12 421 return -1;
e28dd29b 422
e28dd29b 423 if (n == 0) {
b8748c12 424 *out = temp_commit;
e28dd29b 425 return 0;
426 }
427
b8748c12 428 error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1);
e28dd29b 429
b8748c12 430 git_object_free(temp_commit);
431 return error;
023c6f69 432}
ac250c56 433
b8748c12 434static int handle_linear_syntax(git_object **out, git_object *obj, int n)
38533d5a 435{
b8748c12 436 git_object *temp_commit = NULL;
437 int error;
e28dd29b 438
e2c81fca 439 if (git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT) < 0)
b8748c12 440 return -1;
e28dd29b 441
b8748c12 442 error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n);
e28dd29b 443
b8748c12 444 git_object_free(temp_commit);
445 return error;
38533d5a
BS
446}
447
b8748c12 448static int handle_colon_syntax(
449 git_object **out,
e28dd29b 450 git_object *obj,
451 const char *path)
244d2f6b 452{
b8748c12 453 git_object *tree;
bb89cf94 454 int error = -1;
455 git_tree_entry *entry = NULL;
244d2f6b 456
e2c81fca 457 if (git_object_peel(&tree, obj, GIT_OBJ_TREE) < 0)
b8748c12 458 return -1;
244d2f6b 459
b8748c12 460 if (*path == '\0') {
461 *out = tree;
462 return 0;
463 }
053b5096 464
bb89cf94 465 /*
466 * TODO: Handle the relative path syntax
467 * (:./relative/path and :../relative/path)
468 */
469 if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0)
470 goto cleanup;
471
b8748c12 472 error = git_tree_entry_to_object(out, git_object_owner(tree), entry);
053b5096 473
bb89cf94 474cleanup:
475 git_tree_entry_free(entry);
b8748c12 476 git_object_free(tree);
bb89cf94 477
478 return error;
244d2f6b
BS
479}
480
b8748c12 481static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex)
734efe4b 482{
b8748c12 483 int error;
484 git_oid oid;
485 git_object *obj;
486
487 while (!(error = git_revwalk_next(&oid, walk))) {
e28dd29b 488
b8748c12 489 if ((error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJ_COMMIT) < 0) &&
490 (error != GIT_ENOTFOUND))
491 return -1;
492
493 if (!regexec(regex, git_commit_message((git_commit*)obj), 0, NULL, 0)) {
494 *out = obj;
495 return 0;
496 }
497
498 git_object_free(obj);
e28dd29b 499 }
500
f335ecd6 501 if (error < 0 && error == GIT_ITEROVER)
b8748c12 502 error = GIT_ENOTFOUND;
503
504 return error;
505}
506
507static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern)
508{
509 regex_t preg;
510 git_revwalk *walk = NULL;
511 int error = -1;
512
513 if (build_regex(&preg, pattern) < 0)
514 return -1;
515
516 if (git_revwalk_new(&walk, repo) < 0)
517 goto cleanup;
518
519 git_revwalk_sorting(walk, GIT_SORT_TIME);
520
521 if (spec_oid == NULL) {
522 // TODO: @carlosmn: The glob should be refs/* but this makes git_revwalk_next() fails
74a24005 523 if (git_revwalk_push_glob(walk, GIT_REFS_HEADS_DIR "*") < 0)
b8748c12 524 goto cleanup;
525 } else if (git_revwalk_push(walk, spec_oid) < 0)
526 goto cleanup;
527
528 error = walk_and_search(out, walk, &preg);
529
530cleanup:
531 regfree(&preg);
532 git_revwalk_free(walk);
533
534 return error;
535}
536
537static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content)
538{
539 git_otype expected_type;
540
541 if (*curly_braces_content == '\0')
542 return dereference_to_non_tag(out, obj);
543
544 if (*curly_braces_content == '/')
545 return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1);
546
547 expected_type = parse_obj_type(curly_braces_content);
548
549 if (expected_type == GIT_OBJ_BAD)
550 return -1;
551
e2c81fca 552 return git_object_peel(out, obj, expected_type);
b8748c12 553}
554
b8457baa 555static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t *pos)
b8748c12 556{
557 git_buf_clear(buf);
558
559 assert(spec[*pos] == '^' || spec[*pos] == '@');
560
561 (*pos)++;
562
563 if (spec[*pos] == '\0' || spec[*pos] != '{')
564 return revspec_error(spec);
565
566 (*pos)++;
567
568 while (spec[*pos] != '}') {
569 if (spec[*pos] == '\0')
570 return revspec_error(spec);
571
572 git_buf_putc(buf, spec[(*pos)++]);
573 }
574
575 (*pos)++;
576
577 return 0;
578}
579
b8457baa 580static int extract_path(git_buf *buf, const char *spec, size_t *pos)
b8748c12 581{
582 git_buf_clear(buf);
583
584 assert(spec[*pos] == ':');
585
586 (*pos)++;
587
588 if (git_buf_puts(buf, spec + *pos) < 0)
589 return -1;
590
591 *pos += git_buf_len(buf);
592
593 return 0;
594}
595
b8457baa 596static int extract_how_many(int *n, const char *spec, size_t *pos)
b8748c12 597{
598 const char *end_ptr;
599 int parsed, accumulated;
600 char kind = spec[*pos];
601
602 assert(spec[*pos] == '^' || spec[*pos] == '~');
603
604 accumulated = 0;
605
606 do {
607 do {
608 (*pos)++;
609 accumulated++;
610 } while (spec[(*pos)] == kind && kind == '~');
611
612 if (git__isdigit(spec[*pos])) {
613 if ((git__strtol32(&parsed, spec + *pos, &end_ptr, 10) < 0) < 0)
614 return revspec_error(spec);
615
616 accumulated += (parsed - 1);
617 *pos = end_ptr - spec;
e28dd29b 618 }
b8748c12 619
620 } while (spec[(*pos)] == kind && kind == '~');
621
622 *n = accumulated;
623
624 return 0;
625}
626
627static int object_from_reference(git_object **object, git_reference *reference)
628{
629 git_reference *resolved = NULL;
630 int error;
631
632 if (git_reference_resolve(&resolved, reference) < 0)
633 return -1;
634
635 error = git_object_lookup(object, reference->owner, git_reference_oid(resolved), GIT_OBJ_ANY);
636 git_reference_free(resolved);
637
638 return error;
639}
640
b8457baa 641static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier)
b8748c12 642{
643 int error;
644 git_buf identifier = GIT_BUF_INIT;
645
646 if (*object != NULL)
647 return 0;
648
649 if (*reference != NULL) {
650 if ((error = object_from_reference(object, *reference)) < 0)
651 return error;
652
653 git_reference_free(*reference);
654 *reference = NULL;
655 return 0;
e28dd29b 656 }
657
b8748c12 658 if (!allow_empty_identifier && identifier_len == 0)
659 return revspec_error(spec);
660
661 if (git_buf_put(&identifier, spec, identifier_len) < 0)
662 return -1;
663
664 error = revparse_lookup_object(object, repo, git_buf_cstr(&identifier));
665 git_buf_free(&identifier);
666
667 return error;
668}
669
670static int ensure_base_rev_is_not_known_yet(git_object *object, const char *spec)
671{
672 if (object == NULL)
673 return 0;
674
675 return revspec_error(spec);
676}
677
b8457baa 678static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len)
b8748c12 679{
680 if (object != NULL)
681 return true;
682
683 if (reference != NULL)
684 return true;
685
686 if (identifier_len > 0)
687 return true;
688
689 return false;
690}
691
692static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference, const char *spec)
693{
694 if (!ensure_base_rev_is_not_known_yet(object, spec) && reference == NULL)
695 return 0;
696
697 return revspec_error(spec);
734efe4b
BS
698}
699
ac250c56
BS
700int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
701{
b8457baa 702 size_t pos = 0, identifier_len = 0;
b8748c12 703 int error = -1, n;
704 git_buf buf = GIT_BUF_INIT;
705
706 git_reference *reference = NULL;
707 git_object *base_rev = NULL;
e28dd29b 708
709 assert(out && repo && spec);
710
b8748c12 711 *out = NULL;
e28dd29b 712
279b45b0 713 while (spec[pos]) {
b8748c12 714 switch (spec[pos]) {
715 case '^':
716 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
717 goto cleanup;
e28dd29b 718
b8748c12 719 if (spec[pos+1] == '{') {
720 git_object *temp_object = NULL;
e28dd29b 721
b8748c12 722 if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
723 goto cleanup;
e28dd29b 724
b8748c12 725 if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
726 goto cleanup;
727
728 git_object_free(base_rev);
729 base_rev = temp_object;
e28dd29b 730 } else {
b8748c12 731 git_object *temp_object = NULL;
732
733 if ((error = extract_how_many(&n, spec, &pos)) < 0)
734 goto cleanup;
735
736 if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0)
737 goto cleanup;
738
739 git_object_free(base_rev);
740 base_rev = temp_object;
e28dd29b 741 }
e28dd29b 742 break;
743
b8748c12 744 case '~':
745 {
746 git_object *temp_object = NULL;
747
748 if ((error = extract_how_many(&n, spec, &pos)) < 0)
749 goto cleanup;
750
751 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
752 goto cleanup;
753
754 if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0)
755 goto cleanup;
756
757 git_object_free(base_rev);
758 base_rev = temp_object;
e28dd29b 759 break;
b8748c12 760 }
e28dd29b 761
b8748c12 762 case ':':
763 {
764 git_object *temp_object = NULL;
765
766 if ((error = extract_path(&buf, spec, &pos)) < 0)
767 goto cleanup;
768
769 if (any_left_hand_identifier(base_rev, reference, identifier_len)) {
770 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0)
771 goto cleanup;
772
773 if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
774 goto cleanup;
e28dd29b 775 } else {
b8748c12 776 if (*git_buf_cstr(&buf) == '/') {
777 if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_buf_cstr(&buf) + 1)) < 0)
778 goto cleanup;
779 } else {
780
781 /*
782 * TODO: support merge-stage path lookup (":2:Makefile")
783 * and plain index blob lookup (:i-am/a/blob)
784 */
785 giterr_set(GITERR_INVALID, "Unimplemented");
786 error = GIT_ERROR;
787 goto cleanup;
788 }
e28dd29b 789 }
b8748c12 790
791 git_object_free(base_rev);
792 base_rev = temp_object;
e28dd29b 793 break;
b8748c12 794 }
795
796 case '@':
797 {
798 git_object *temp_object = NULL;
e28dd29b 799
b8748c12 800 if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
801 goto cleanup;
802
803 if ((error = ensure_base_rev_is_not_known_yet(base_rev, spec)) < 0)
804 goto cleanup;
805
806 if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0)
807 goto cleanup;
808
809 if (temp_object != NULL)
810 base_rev = temp_object;
e28dd29b 811 break;
812 }
813
b8748c12 814 default:
815 if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference, spec)) < 0)
816 goto cleanup;
817
818 pos++;
819 identifier_len++;
e28dd29b 820 }
279b45b0 821 }
e28dd29b 822
279b45b0 823 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
b8748c12 824 goto cleanup;
825
826 *out = base_rev;
827 error = 0;
e28dd29b 828
b8748c12 829cleanup:
830 if (error)
831 git_object_free(base_rev);
832 git_reference_free(reference);
833 git_buf_free(&buf);
834 return error;
ac250c56 835}