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