Line Flow Count Block(s) Source
1 - /*
2 - * Copyright (C) the libgit2 contributors. All rights reserved.
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 "common.h"
9 -
10 - #include "buffer.h"
11 - #include "tree.h"
12 - #include "refdb.h"
13 - #include "regexp.h"
14 -
15 - #include "git2.h"
16 -
17 1410 2 static int maybe_sha_or_abbrev(git_object** out, git_repository *repo, const char *spec, size_t speclen)
18 - {
19 - git_oid oid;
20 -
21 1410 2,3 if (git_oid_fromstrn(&oid, spec, speclen) < 0)
22 1103 4 return GIT_ENOTFOUND;
23 -
24 307 5 return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY);
25 - }
26 -
27 1668 2 static int maybe_sha(git_object** out, git_repository *repo, const char *spec)
28 - {
29 1668 2 size_t speclen = strlen(spec);
30 -
31 1668 2 if (speclen != GIT_OID_HEXSZ)
32 1609 3 return GIT_ENOTFOUND;
33 -
34 59 4 return maybe_sha_or_abbrev(out, repo, spec, speclen);
35 - }
36 -
37 1351 2 static int maybe_abbrev(git_object** out, git_repository *repo, const char *spec)
38 - {
39 1351 2 size_t speclen = strlen(spec);
40 -
41 1351 2 return maybe_sha_or_abbrev(out, repo, spec, speclen);
42 - }
43 -
44 58 2 static int build_regex(git_regexp *regex, const char *pattern)
45 - {
46 - int error;
47 -
48 58 2 if (*pattern == '\0') {
49 1 3 git_error_set(GIT_ERROR_REGEX, "empty pattern");
50 1 4 return GIT_EINVALIDSPEC;
51 - }
52 -
53 57 5 error = git_regexp_compile(regex, pattern, 0);
54 57 6 if (!error)
55 56 7 return 0;
56 -
57 1 8 git_regexp_dispose(regex);
58 -
59 1 9 return error;
60 - }
61 -
62 1107 2 static int maybe_describe(git_object**out, git_repository *repo, const char *spec)
63 - {
64 - const char *substr;
65 - int error;
66 - git_regexp regex;
67 -
68 1107 2 substr = strstr(spec, "-g");
69 -
70 1107 2 if (substr == NULL)
71 1070 3 return GIT_ENOTFOUND;
72 -
73 37 4,5 if (build_regex(&regex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0)
74 ##### 6 return -1;
75 -
76 37 7 error = git_regexp_match(&regex, spec);
77 37 8 git_regexp_dispose(&regex);
78 -
79 37 9 if (error)
80 35 10 return GIT_ENOTFOUND;
81 -
82 2 11 return maybe_abbrev(out, repo, substr+2);
83 - }
84 -
85 1668 2 static int revparse_lookup_object(
86 - git_object **object_out,
87 - git_reference **reference_out,
88 - git_repository *repo,
89 - const char *spec)
90 - {
91 - int error;
92 - git_reference *ref;
93 -
94 1668 2,3 if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND)
95 55 4 return error;
96 -
97 1613 5 error = git_reference_dwim(&ref, repo, spec);
98 1613 6 if (!error) {
99 -
100 258 7,8 error = git_object_lookup(
101 - object_out, repo, git_reference_target(ref), GIT_OBJECT_ANY);
102 -
103 258 9 if (!error)
104 258 10 *reference_out = ref;
105 -
106 258 11 return error;
107 - }
108 -
109 1355 12 if (error != GIT_ENOTFOUND)
110 3 13 return error;
111 -
112 1352 14-16 if ((strlen(spec) < GIT_OID_HEXSZ) &&
113 - ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
114 245 17 return error;
115 -
116 1107 18,19 if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
117 2 20 return error;
118 -
119 1105 21 git_error_set(GIT_ERROR_REFERENCE, "revspec '%s' not found", spec);
120 1105 22 return GIT_ENOTFOUND;
121 - }
122 -
123 86 2 static int try_parse_numeric(int *n, const char *curly_braces_content)
124 - {
125 - int32_t content;
126 - const char *end_ptr;
127 -
128 86 2,3 if (git__strntol32(&content, curly_braces_content, strlen(curly_braces_content),
129 - &end_ptr, 10) < 0)
130 12 4 return -1;
131 -
132 74 5 if (*end_ptr != '\0')
133 11 6 return -1;
134 -
135 63 7 *n = (int)content;
136 63 7 return 0;
137 - }
138 -
139 12 2 static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
140 - {
141 12 2 git_reference *ref = NULL;
142 12 2 git_reflog *reflog = NULL;
143 - git_regexp preg;
144 12 2 int error = -1;
145 - size_t i, numentries, cur;
146 - const git_reflog_entry *entry;
147 - const char *msg;
148 12 2 git_buf buf = GIT_BUF_INIT;
149 -
150 12 2 cur = position;
151 -
152 12 2,3 if (*identifier != '\0' || *base_ref != NULL)
153 3 4 return GIT_EINVALIDSPEC;
154 -
155 9 5,6 if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0)
156 ##### 7 return -1;
157 -
158 9 8,9 if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
159 ##### 10 goto cleanup;
160 -
161 9 11,12 if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
162 ##### 13 goto cleanup;
163 -
164 9 14 numentries = git_reflog_entrycount(reflog);
165 -
166 25 15,37,40 for (i = 0; i < numentries; i++) {
167 - git_regmatch regexmatches[2];
168 -
169 24 16 entry = git_reflog_entry_byindex(reflog, i);
170 24 17 msg = git_reflog_entry_message(entry);
171 24 18 if (!msg)
172 16 19,37 continue;
173 -
174 24 20,21 if (git_regexp_search(&preg, msg, 2, regexmatches) < 0)
175 6 22 continue;
176 -
177 18 23 cur--;
178 -
179 18 23 if (cur > 0)
180 10 24 continue;
181 -
182 8 25,26 if ((git_buf_put(&buf, msg+regexmatches[1].start, regexmatches[1].end - regexmatches[1].start)) < 0)
183 8 27,39 goto cleanup;
184 -
185 8 28-30 if ((error = git_reference_dwim(base_ref, repo, git_buf_cstr(&buf))) == 0)
186 7 31 goto cleanup;
187 -
188 1 32,33 if (error < 0 && error != GIT_ENOTFOUND)
189 ##### 34 goto cleanup;
190 -
191 1 35,36 error = maybe_abbrev(out, repo, git_buf_cstr(&buf));
192 -
193 1 38 goto cleanup;
194 - }
195 -
196 1 41 error = GIT_ENOTFOUND;
197 -
198 - cleanup:
199 9 42 git_reference_free(ref);
200 9 43 git_buf_dispose(&buf);
201 9 44 git_regexp_dispose(&preg);
202 9 45 git_reflog_free(reflog);
203 9 46 return error;
204 - }
205 -
206 32 2 static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier)
207 - {
208 - git_reflog *reflog;
209 - size_t numentries;
210 - const git_reflog_entry *entry;
211 32 2 bool search_by_pos = (identifier <= 100000000);
212 -
213 32 2-5 if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
214 ##### 6 return -1;
215 -
216 32 7 numentries = git_reflog_entrycount(reflog);
217 -
218 32 8 if (search_by_pos) {
219 20 9 if (numentries < identifier + 1)
220 4 10 goto notfound;
221 -
222 16 11 entry = git_reflog_entry_byindex(reflog, identifier);
223 16 12,13 git_oid_cpy(oid, git_reflog_entry_id_new(entry));
224 - } else {
225 - size_t i;
226 - git_time commit_time;
227 -
228 21 14,18,22 for (i = 0; i < numentries; i++) {
229 18 15 entry = git_reflog_entry_byindex(reflog, i);
230 18 16 commit_time = git_reflog_entry_committer(entry)->when;
231 -
232 18 17 if (commit_time.time > (git_time_t)identifier)
233 9 18 continue;
234 -
235 9 19,20 git_oid_cpy(oid, git_reflog_entry_id_new(entry));
236 9 21 break;
237 - }
238 -
239 12 23 if (i == numentries)
240 9 24,25 goto notfound;
241 - }
242 -
243 25 26 git_reflog_free(reflog);
244 25 27 return 0;
245 -
246 - notfound:
247 7 28,29 git_error_set(
248 - GIT_ERROR_REFERENCE,
249 - "reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
250 - git_reference_name(ref), numentries, identifier);
251 -
252 7 30 git_reflog_free(reflog);
253 7 31 return GIT_ENOTFOUND;
254 - }
255 -
256 60 2 static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
257 - {
258 - git_reference *ref;
259 - git_oid oid;
260 60 2 int error = -1;
261 -
262 60 2 if (*base_ref == NULL) {
263 56 3,4 if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
264 3 5 return error;
265 - } else {
266 4 6 ref = *base_ref;
267 4 6 *base_ref = NULL;
268 - }
269 -
270 57 7 if (position == 0) {
271 25 8,9 error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJECT_ANY);
272 25 10 goto cleanup;
273 - }
274 -
275 32 11,12 if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0)
276 7 13 goto cleanup;
277 -
278 25 14 error = git_object_lookup(out, repo, &oid, GIT_OBJECT_ANY);
279 -
280 - cleanup:
281 57 15 git_reference_free(ref);
282 57 16 return error;
283 - }
284 -
285 11 2 static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo)
286 - {
287 - git_reference *tracking, *ref;
288 11 2 int error = -1;
289 -
290 11 2 if (*base_ref == NULL) {
291 11 3,4 if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
292 1 5 return error;
293 - } else {
294 ##### 6 ref = *base_ref;
295 ##### 6 *base_ref = NULL;
296 - }
297 -
298 10 7,8 if (!git_reference_is_branch(ref)) {
299 2 9 error = GIT_EINVALIDSPEC;
300 2 9 goto cleanup;
301 - }
302 -
303 8 10,11 if ((error = git_branch_upstream(&tracking, ref)) < 0)
304 ##### 12 goto cleanup;
305 -
306 8 13 *base_ref = tracking;
307 -
308 - cleanup:
309 10 14 git_reference_free(ref);
310 10 15 return error;
311 - }
312 -
313 86 2 static 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)
314 - {
315 - bool is_numeric;
316 86 2 int parsed = 0, error = -1;
317 86 2 git_buf identifier = GIT_BUF_INIT;
318 - git_time_t timestamp;
319 -
320 86 2,3 assert(*out == NULL);
321 -
322 86 4,5 if (git_buf_put(&identifier, spec, identifier_len) < 0)
323 ##### 6 return -1;
324 -
325 86 7 is_numeric = !try_parse_numeric(&parsed, curly_braces_content);
326 -
327 86 8-10 if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) {
328 3 11 error = GIT_EINVALIDSPEC;
329 3 11 goto cleanup;
330 - }
331 -
332 83 12 if (is_numeric) {
333 62 13 if (parsed < 0)
334 12 14,15 error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_buf_cstr(&identifier), -parsed);
335 - else
336 50 16,17 error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), parsed);
337 -
338 62 18 goto cleanup;
339 - }
340 -
341 21 19,20 if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) {
342 11 21,22 error = retrieve_remote_tracking_reference(ref, git_buf_cstr(&identifier), repo);
343 -
344 11 23 goto cleanup;
345 - }
346 -
347 10 24,25 if (git__date_parse(&timestamp, curly_braces_content) < 0)
348 ##### 26 goto cleanup;
349 -
350 10 27,28 error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), (size_t)timestamp);
351 -
352 - cleanup:
353 86 29 git_buf_dispose(&identifier);
354 86 30 return error;
355 - }
356 -
357 67 2 static git_object_t parse_obj_type(const char *str)
358 - {
359 67 2 if (!strcmp(str, "commit"))
360 18 3 return GIT_OBJECT_COMMIT;
361 -
362 49 4 if (!strcmp(str, "tree"))
363 44 5 return GIT_OBJECT_TREE;
364 -
365 5 6 if (!strcmp(str, "blob"))
366 4 7 return GIT_OBJECT_BLOB;
367 -
368 1 8 if (!strcmp(str, "tag"))
369 ##### 9 return GIT_OBJECT_TAG;
370 -
371 1 10 return GIT_OBJECT_INVALID;
372 - }
373 -
374 7 2 static int dereference_to_non_tag(git_object **out, git_object *obj)
375 - {
376 7 2,3 if (git_object_type(obj) == GIT_OBJECT_TAG)
377 3 4 return git_tag_peel(out, (git_tag *)obj);
378 -
379 4 5 return git_object_dup(out, obj);
380 - }
381 -
382 116 2 static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n)
383 - {
384 116 2 git_object *temp_commit = NULL;
385 - int error;
386 -
387 116 2,3 if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0)
388 2 5,8 return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
389 2 4,6,7 GIT_EINVALIDSPEC : error;
390 -
391 114 9 if (n == 0) {
392 12 10 *out = temp_commit;
393 12 10 return 0;
394 - }
395 -
396 102 11 error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1);
397 -
398 102 12 git_object_free(temp_commit);
399 102 13 return error;
400 - }
401 -
402 38 2 static int handle_linear_syntax(git_object **out, git_object *obj, int n)
403 - {
404 38 2 git_object *temp_commit = NULL;
405 - int error;
406 -
407 38 2,3 if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0)
408 2 5,8 return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
409 2 4,6,7 GIT_EINVALIDSPEC : error;
410 -
411 36 9 error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n);
412 -
413 36 10 git_object_free(temp_commit);
414 36 11 return error;
415 - }
416 -
417 72 2 static int handle_colon_syntax(
418 - git_object **out,
419 - git_object *obj,
420 - const char *path)
421 - {
422 - git_object *tree;
423 72 2 int error = -1;
424 72 2 git_tree_entry *entry = NULL;
425 -
426 72 2,3 if ((error = git_object_peel(&tree, obj, GIT_OBJECT_TREE)) < 0)
427 1 4-7 return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error;
428 -
429 71 8 if (*path == '\0') {
430 2 9 *out = tree;
431 2 9 return 0;
432 - }
433 -
434 - /*
435 - * TODO: Handle the relative path syntax
436 - * (:./relative/path and :../relative/path)
437 - */
438 69 10,11 if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0)
439 20 12 goto cleanup;
440 -
441 49 13,14 error = git_tree_entry_to_object(out, git_object_owner(tree), entry);
442 -
443 - cleanup:
444 69 15 git_tree_entry_free(entry);
445 69 16 git_object_free(tree);
446 -
447 69 17 return error;
448 - }
449 -
450 10 2 static int walk_and_search(git_object **out, git_revwalk *walk, git_regexp *regex)
451 - {
452 - int error;
453 - git_oid oid;
454 - git_object *obj;
455 -
456 73 2,13,14 while (!(error = git_revwalk_next(&oid, walk))) {
457 -
458 70 3,4 error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJECT_COMMIT);
459 70 5,6 if ((error < 0) && (error != GIT_ENOTFOUND))
460 ##### 7 return -1;
461 -
462 70 8-10 if (!git_regexp_match(regex, git_commit_message((git_commit*)obj))) {
463 7 11 *out = obj;
464 7 11 return 0;
465 - }
466 -
467 63 12 git_object_free(obj);
468 - }
469 -
470 3 15,16 if (error < 0 && error == GIT_ITEROVER)
471 3 17 error = GIT_ENOTFOUND;
472 -
473 3 18 return error;
474 - }
475 -
476 12 2 static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern)
477 - {
478 - git_regexp preg;
479 12 2 git_revwalk *walk = NULL;
480 - int error;
481 -
482 12 2,3 if ((error = build_regex(&preg, pattern)) < 0)
483 2 4 return error;
484 -
485 10 5,6 if ((error = git_revwalk_new(&walk, repo)) < 0)
486 ##### 7 goto cleanup;
487 -
488 10 8 git_revwalk_sorting(walk, GIT_SORT_TIME);
489 -
490 10 9 if (spec_oid == NULL) {
491 4 10,11 if ((error = git_revwalk_push_glob(walk, "refs/*")) < 0)
492 ##### 12 goto cleanup;
493 6 13,14 } else if ((error = git_revwalk_push(walk, spec_oid)) < 0)
494 ##### 15 goto cleanup;
495 -
496 10 16 error = walk_and_search(out, walk, &preg);
497 -
498 - cleanup:
499 10 17 git_regexp_dispose(&preg);
500 10 18 git_revwalk_free(walk);
501 -
502 10 19 return error;
503 - }
504 -
505 81 2 static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content)
506 - {
507 - git_object_t expected_type;
508 -
509 81 2 if (*curly_braces_content == '\0')
510 7 3 return dereference_to_non_tag(out, obj);
511 -
512 74 4 if (*curly_braces_content == '/')
513 7 5-7 return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1);
514 -
515 67 8 expected_type = parse_obj_type(curly_braces_content);
516 -
517 67 9 if (expected_type == GIT_OBJECT_INVALID)
518 1 10 return GIT_EINVALIDSPEC;
519 -
520 66 11 return git_object_peel(out, obj, expected_type);
521 - }
522 -
523 170 2 static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t *pos)
524 - {
525 170 2 git_buf_clear(buf);
526 -
527 170 3-5 assert(spec[*pos] == '^' || spec[*pos] == '@');
528 -
529 170 6 (*pos)++;
530 -
531 170 6,7 if (spec[*pos] == '\0' || spec[*pos] != '{')
532 ##### 8 return GIT_EINVALIDSPEC;
533 -
534 170 9 (*pos)++;
535 -
536 846 9,15 while (spec[*pos] != '}') {
537 677 10 if (spec[*pos] == '\0')
538 1 11 return GIT_EINVALIDSPEC;
539 -
540 676 12,13 if (git_buf_putc(buf, spec[(*pos)++]) < 0)
541 ##### 14 return -1;
542 - }
543 -
544 169 16 (*pos)++;
545 -
546 169 16 return 0;
547 - }
548 -
549 78 2 static int extract_path(git_buf *buf, const char *spec, size_t *pos)
550 - {
551 78 2 git_buf_clear(buf);
552 -
553 78 3,4 assert(spec[*pos] == ':');
554 -
555 78 5 (*pos)++;
556 -
557 78 5,6 if (git_buf_puts(buf, spec + *pos) < 0)
558 ##### 7 return -1;
559 -
560 78 8 *pos += git_buf_len(buf);
561 -
562 78 9 return 0;
563 - }
564 -
565 158 2 static int extract_how_many(int *n, const char *spec, size_t *pos)
566 - {
567 - const char *end_ptr;
568 - int parsed, accumulated;
569 158 2 char kind = spec[*pos];
570 -
571 158 2-4 assert(spec[*pos] == '^' || spec[*pos] == '~');
572 -
573 158 5 accumulated = 0;
574 -
575 - do {
576 - do {
577 160 6 (*pos)++;
578 160 6 accumulated++;
579 160 6,7 } while (spec[(*pos)] == kind && kind == '~');
580 -
581 159 8,9 if (git__isdigit(spec[*pos])) {
582 109 10,11 if (git__strntol32(&parsed, spec + *pos, strlen(spec + *pos), &end_ptr, 10) < 0)
583 ##### 12 return GIT_EINVALIDSPEC;
584 -
585 109 13 accumulated += (parsed - 1);
586 109 13 *pos = end_ptr - spec;
587 - }
588 -
589 159 14,15 } while (spec[(*pos)] == kind && kind == '~');
590 -
591 158 16 *n = accumulated;
592 -
593 158 16 return 0;
594 - }
595 -
596 9 2 static int object_from_reference(git_object **object, git_reference *reference)
597 - {
598 9 2 git_reference *resolved = NULL;
599 - int error;
600 -
601 9 2,3 if (git_reference_resolve(&resolved, reference) < 0)
602 ##### 4 return -1;
603 -
604 9 5,6 error = git_object_lookup(object, reference->db->repo, git_reference_target(resolved), GIT_OBJECT_ANY);
605 9 7 git_reference_free(resolved);
606 -
607 9 8 return error;
608 - }
609 -
610 1998 2 static 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)
611 - {
612 - int error;
613 1998 2 git_buf identifier = GIT_BUF_INIT;
614 -
615 1998 2 if (*object != NULL)
616 318 3 return 0;
617 -
618 1680 4 if (*reference != NULL)
619 9 5 return object_from_reference(object, *reference);
620 -
621 1671 6,7 if (!allow_empty_identifier && identifier_len == 0)
622 3 8 return GIT_EINVALIDSPEC;
623 -
624 1668 9,10 if (git_buf_put(&identifier, spec, identifier_len) < 0)
625 ##### 11 return -1;
626 -
627 1668 12,13 error = revparse_lookup_object(object, reference, repo, git_buf_cstr(&identifier));
628 1668 14 git_buf_dispose(&identifier);
629 -
630 1668 15 return error;
631 - }
632 -
633 27630 2 static int ensure_base_rev_is_not_known_yet(git_object *object)
634 - {
635 27630 2 if (object == NULL)
636 27622 3 return 0;
637 -
638 8 4 return GIT_EINVALIDSPEC;
639 - }
640 -
641 78 2 static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len)
642 - {
643 78 2 if (object != NULL)
644 41 3 return true;
645 -
646 37 4 if (reference != NULL)
647 ##### 5 return true;
648 -
649 37 6 if (identifier_len > 0)
650 31 7 return true;
651 -
652 6 8 return false;
653 - }
654 -
655 27542 2 static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference)
656 - {
657 27542 2-4 if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL)
658 27536 5 return 0;
659 -
660 6 6 return GIT_EINVALIDSPEC;
661 - }
662 -
663 1758 2 static int revparse(
664 - git_object **object_out,
665 - git_reference **reference_out,
666 - size_t *identifier_len_out,
667 - git_repository *repo,
668 - const char *spec)
669 - {
670 1758 2 size_t pos = 0, identifier_len = 0;
671 1758 2 int error = -1, n;
672 1758 2 git_buf buf = GIT_BUF_INIT;
673 -
674 1758 2 git_reference *reference = NULL;
675 1758 2 git_object *base_rev = NULL;
676 -
677 1758 2 bool should_return_reference = true;
678 -
679 1758 2-6 assert(object_out && reference_out && repo && spec);
680 -
681 1758 7 *object_out = NULL;
682 1758 7 *reference_out = NULL;
683 -
684 29635 7,87 while (spec[pos]) {
685 28166 8 switch (spec[pos]) {
686 - case '^':
687 415 9 should_return_reference = false;
688 -
689 415 9,10 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
690 218 11 goto cleanup;
691 -
692 197 12 if (spec[pos+1] == '{') {
693 81 13 git_object *temp_object = NULL;
694 -
695 81 13,14 if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
696 6 15,22 goto cleanup;
697 -
698 81 16-18 if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
699 6 19 goto cleanup;
700 -
701 75 20 git_object_free(base_rev);
702 75 21 base_rev = temp_object;
703 - } else {
704 116 23 git_object *temp_object = NULL;
705 -
706 116 23,24 if ((error = extract_how_many(&n, spec, &pos)) < 0)
707 6 25,31 goto cleanup;
708 -
709 116 26,27 if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0)
710 6 28 goto cleanup;
711 -
712 110 29 git_object_free(base_rev);
713 110 30 base_rev = temp_object;
714 - }
715 185 32 break;
716 -
717 - case '~':
718 - {
719 42 33 git_object *temp_object = NULL;
720 -
721 42 33 should_return_reference = false;
722 -
723 42 33,34 if ((error = extract_how_many(&n, spec, &pos)) < 0)
724 6 35,44 goto cleanup;
725 -
726 42 36,37 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
727 4 38 goto cleanup;
728 -
729 38 39,40 if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0)
730 2 41 goto cleanup;
731 -
732 36 42 git_object_free(base_rev);
733 36 43 base_rev = temp_object;
734 36 43 break;
735 - }
736 -
737 - case ':':
738 - {
739 78 45 git_object *temp_object = NULL;
740 -
741 78 45 should_return_reference = false;
742 -
743 78 45,46 if ((error = extract_path(&buf, spec, &pos)) < 0)
744 24 47,67 goto cleanup;
745 -
746 78 48,49 if (any_left_hand_identifier(base_rev, reference, identifier_len)) {
747 72 50,51 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0)
748 ##### 52 goto cleanup;
749 -
750 72 53-55 if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
751 21 56 goto cleanup;
752 - } else {
753 6 57,58 if (*git_buf_cstr(&buf) == '/') {
754 5 59-61 if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_buf_cstr(&buf) + 1)) < 0)
755 2 62 goto cleanup;
756 - } else {
757 -
758 - /*
759 - * TODO: support merge-stage path lookup (":2:Makefile")
760 - * and plain index blob lookup (:i-am/a/blob)
761 - */
762 1 63 git_error_set(GIT_ERROR_INVALID, "unimplemented");
763 1 64 error = GIT_ERROR;
764 1 64 goto cleanup;
765 - }
766 - }
767 -
768 54 65 git_object_free(base_rev);
769 54 66 base_rev = temp_object;
770 54 66 break;
771 - }
772 -
773 - case '@':
774 93 68 if (spec[pos+1] == '{') {
775 89 69 git_object *temp_object = NULL;
776 -
777 89 69,70 if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
778 23 71,82 goto cleanup;
779 -
780 88 72,73 if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0)
781 2 74 goto cleanup;
782 -
783 86 75-77 if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0)
784 20 78 goto cleanup;
785 -
786 66 79 if (temp_object != NULL)
787 51 80 base_rev = temp_object;
788 66 81 break;
789 - }
790 - /* fall through */
791 -
792 - default:
793 27542 83,84 if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0)
794 6 85 goto cleanup;
795 -
796 27536 86 pos++;
797 27536 86 identifier_len++;
798 - }
799 - }
800 -
801 1469 88,89 if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
802 891 90 goto cleanup;
803 -
804 578 91 if (!should_return_reference) {
805 180 92 git_reference_free(reference);
806 180 93 reference = NULL;
807 - }
808 -
809 578 94 *object_out = base_rev;
810 578 94 *reference_out = reference;
811 578 94 *identifier_len_out = identifier_len;
812 578 94 error = 0;
813 -
814 - cleanup:
815 1758 95 if (error) {
816 1180 96 if (error == GIT_EINVALIDSPEC)
817 32 97 git_error_set(GIT_ERROR_INVALID,
818 - "failed to parse revision specifier - Invalid pattern '%s'", spec);
819 -
820 1180 98 git_object_free(base_rev);
821 1180 99 git_reference_free(reference);
822 - }
823 -
824 1758 100 git_buf_dispose(&buf);
825 1758 101 return error;
826 - }
827 -
828 1758 2 int git_revparse_ext(
829 - git_object **object_out,
830 - git_reference **reference_out,
831 - git_repository *repo,
832 - const char *spec)
833 - {
834 - int error;
835 - size_t identifier_len;
836 1758 2 git_object *obj = NULL;
837 1758 2 git_reference *ref = NULL;
838 -
839 1758 2,3 if ((error = revparse(&obj, &ref, &identifier_len, repo, spec)) < 0)
840 1180 4 goto cleanup;
841 -
842 578 5 *object_out = obj;
843 578 5 *reference_out = ref;
844 - GIT_UNUSED(identifier_len);
845 -
846 578 5 return 0;
847 -
848 - cleanup:
849 1180 6 git_object_free(obj);
850 1180 7 git_reference_free(ref);
851 1180 8 return error;
852 - }
853 -
854 1539 2 int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
855 - {
856 - int error;
857 1539 2 git_object *obj = NULL;
858 1539 2 git_reference *ref = NULL;
859 -
860 1539 2 *out = NULL;
861 -
862 1539 2,3 if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0)
863 1103 4 goto cleanup;
864 -
865 436 5 git_reference_free(ref);
866 -
867 436 6 *out = obj;
868 -
869 436 6 return 0;
870 -
871 - cleanup:
872 1103 7 git_object_free(obj);
873 1103 8 git_reference_free(ref);
874 1103 9 return error;
875 - }
876 -
877 15 2 int git_revparse(
878 - git_revspec *revspec,
879 - git_repository *repo,
880 - const char *spec)
881 - {
882 - const char *dotdot;
883 15 2 int error = 0;
884 -
885 15 2-5 assert(revspec && repo && spec);
886 -
887 15 6 memset(revspec, 0x0, sizeof(*revspec));
888 -
889 15 6 if ((dotdot = strstr(spec, "..")) != NULL) {
890 - char *lstr;
891 - const char *rstr;
892 12 7 revspec->flags = GIT_REVPARSE_RANGE;
893 -
894 - /*
895 - * Following git.git, don't allow '..' because it makes command line
896 - * arguments which can be either paths or revisions ambiguous when the
897 - * path is almost certainly intended. The empty range '...' is still
898 - * allowed.
899 - */
900 12 7 if (!git__strcmp(spec, "..")) {
901 1 8 git_error_set(GIT_ERROR_INVALID, "Invalid pattern '..'");
902 1 9 return GIT_EINVALIDSPEC;
903 - }
904 -
905 11 10 lstr = git__substrdup(spec, dotdot - spec);
906 11 11 rstr = dotdot + 2;
907 11 11 if (dotdot[2] == '.') {
908 6 12 revspec->flags |= GIT_REVPARSE_MERGE_BASE;
909 6 12 rstr++;
910 - }
911 -
912 11 13-16 error = git_revparse_single(
913 - &revspec->from,
914 - repo,
915 11 13 *lstr == '\0' ? "HEAD" : lstr);
916 -
917 11 17 if (!error) {
918 11 18-21 error = git_revparse_single(
919 - &revspec->to,
920 - repo,
921 11 18 *rstr == '\0' ? "HEAD" : rstr);
922 - }
923 -
924 11 22 git__free((void*)lstr);
925 - } else {
926 3 23 revspec->flags = GIT_REVPARSE_SINGLE;
927 3 23 error = git_revparse_single(&revspec->from, repo, spec);
928 - }
929 -
930 14 24 return error;
931 - }