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