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 "git2/describe.h"
11 - #include "git2/strarray.h"
12 - #include "git2/diff.h"
13 - #include "git2/status.h"
14 -
15 - #include "commit.h"
16 - #include "commit_list.h"
17 - #include "oidmap.h"
18 - #include "refs.h"
19 - #include "repository.h"
20 - #include "revwalk.h"
21 - #include "tag.h"
22 - #include "vector.h"
23 - #include "wildmatch.h"
24 -
25 - /* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
26 -
27 - struct commit_name {
28 - git_tag *tag;
29 - unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
30 - unsigned name_checked:1;
31 - git_oid sha1;
32 - char *path;
33 -
34 - /* Khash workaround. They original key has to still be reachable */
35 - git_oid peeled;
36 - };
37 -
38 356 2 static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
39 - {
40 356 2 return git_oidmap_get(map, key);
41 - }
42 -
43 356 2 static struct commit_name *find_commit_name(
44 - git_oidmap *names,
45 - const git_oid *peeled)
46 - {
47 356 2 return (struct commit_name *)(oidmap_value_bykey(names, peeled));
48 - }
49 -
50 153 2 static int replace_name(
51 - git_tag **tag,
52 - git_repository *repo,
53 - struct commit_name *e,
54 - unsigned int prio,
55 - const git_oid *sha1)
56 - {
57 153 2 git_time_t e_time = 0, t_time = 0;
58 -
59 153 2,3 if (!e || e->prio < prio)
60 123 4 return 1;
61 -
62 30 5,6 if (e->prio == 2 && prio == 2) {
63 - /* Multiple annotated tags point to the same commit.
64 - * Select one to keep based upon their tagger date.
65 - */
66 28 7 git_tag *t = NULL;
67 -
68 28 7 if (!e->tag) {
69 26 8,9 if (git_tag_lookup(&t, repo, &e->sha1) < 0)
70 24 10,22 return 1;
71 26 11 e->tag = t;
72 - }
73 -
74 28 12,13 if (git_tag_lookup(&t, repo, sha1) < 0)
75 ##### 14 return 0;
76 -
77 28 15 *tag = t;
78 -
79 28 15 if (e->tag->tagger)
80 28 16 e_time = e->tag->tagger->when.time;
81 -
82 28 17 if (t->tagger)
83 26 18 t_time = t->tagger->when.time;
84 -
85 28 19 if (e_time < t_time)
86 24 20,21 return 1;
87 - }
88 -
89 6 23 return 0;
90 - }
91 -
92 153 2 static int add_to_known_names(
93 - git_repository *repo,
94 - git_oidmap *names,
95 - const char *path,
96 - const git_oid *peeled,
97 - unsigned int prio,
98 - const git_oid *sha1)
99 - {
100 153 2 struct commit_name *e = find_commit_name(names, peeled);
101 153 3 bool found = (e != NULL);
102 -
103 153 3 git_tag *tag = NULL;
104 153 3,4 if (replace_name(&tag, repo, e, prio, sha1)) {
105 147 5 if (!found) {
106 123 6 e = git__malloc(sizeof(struct commit_name));
107 123 7,8 GIT_ERROR_CHECK_ALLOC(e);
108 -
109 123 9 e->path = NULL;
110 123 9 e->tag = NULL;
111 - }
112 -
113 147 10 if (e->tag)
114 24 11 git_tag_free(e->tag);
115 147 12 e->tag = tag;
116 147 12 e->prio = prio;
117 147 12 e->name_checked = 0;
118 147 12 git_oid_cpy(&e->sha1, sha1);
119 147 13 git__free(e->path);
120 147 14 e->path = git__strdup(path);
121 147 15 git_oid_cpy(&e->peeled, peeled);
122 -
123 147 16-18 if (!found && git_oidmap_set(names, &e->peeled, e) < 0)
124 ##### 19 return -1;
125 - }
126 - else
127 6 20 git_tag_free(tag);
128 -
129 153 21 return 0;
130 - }
131 -
132 153 2 static int retrieve_peeled_tag_or_object_oid(
133 - git_oid *peeled_out,
134 - git_oid *ref_target_out,
135 - git_repository *repo,
136 - const char *refname)
137 - {
138 - git_reference *ref;
139 153 2 git_object *peeled = NULL;
140 - int error;
141 -
142 153 2,3 if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
143 ##### 4 return error;
144 -
145 153 5,6 if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_ANY)) < 0)
146 ##### 7 goto cleanup;
147 -
148 153 8,9 git_oid_cpy(ref_target_out, git_reference_target(ref));
149 153 10,11 git_oid_cpy(peeled_out, git_object_id(peeled));
150 -
151 153 12,13 if (git_oid_cmp(ref_target_out, peeled_out) != 0)
152 101 14 error = 1; /* The reference was pointing to a annotated tag */
153 - else
154 52 15 error = 0; /* Any other object */
155 -
156 - cleanup:
157 153 16 git_reference_free(ref);
158 153 17 git_object_free(peeled);
159 153 18 return error;
160 - }
161 -
162 - struct git_describe_result {
163 - int dirty;
164 - int exact_match;
165 - int fallback_to_id;
166 - git_oid commit_id;
167 - git_repository *repo;
168 - struct commit_name *name;
169 - struct possible_tag *tag;
170 - };
171 -
172 - struct get_name_data
173 - {
174 - git_describe_options *opts;
175 - git_repository *repo;
176 - git_oidmap *names;
177 - git_describe_result *result;
178 - };
179 -
180 27 2 static int commit_name_dup(struct commit_name **out, struct commit_name *in)
181 - {
182 - struct commit_name *name;
183 -
184 27 2 name = git__malloc(sizeof(struct commit_name));
185 27 3,4 GIT_ERROR_CHECK_ALLOC(name);
186 -
187 27 5 memcpy(name, in, sizeof(struct commit_name));
188 27 5 name->tag = NULL;
189 27 5 name->path = NULL;
190 -
191 27 5-7 if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
192 ##### 8 return -1;
193 -
194 27 9 name->path = git__strdup(in->path);
195 27 10,11 GIT_ERROR_CHECK_ALLOC(name->path);
196 -
197 27 12 *out = name;
198 27 12 return 0;
199 - }
200 -
201 238 2 static int get_name(const char *refname, void *payload)
202 - {
203 - struct get_name_data *data;
204 - bool is_tag, is_annotated, all;
205 - git_oid peeled, sha1;
206 - unsigned int prio;
207 238 2 int error = 0;
208 -
209 238 2 data = (struct get_name_data *)payload;
210 238 2 is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
211 238 3 all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
212 -
213 - /* Reject anything outside refs/tags/ unless --all */
214 238 3,4 if (!all && !is_tag)
215 53 5 return 0;
216 -
217 - /* Accept only tags that match the pattern, if given */
218 185 6-9 if (data->opts->pattern && (!is_tag || wildmatch(data->opts->pattern,
219 - refname + strlen(GIT_REFS_TAGS_DIR), 0)))
220 32 10 return 0;
221 -
222 - /* Is it annotated? */
223 153 11,12 if ((error = retrieve_peeled_tag_or_object_oid(
224 - &peeled, &sha1, data->repo, refname)) < 0)
225 ##### 13 return error;
226 -
227 153 14 is_annotated = error;
228 -
229 - /*
230 - * By default, we only use annotated tags, but with --tags
231 - * we fall back to lightweight ones (even without --tags,
232 - * we still remember lightweight ones, only to give hints
233 - * in an error message). --all allows any refs to be used.
234 - */
235 153 14 if (is_annotated)
236 101 15 prio = 2;
237 52 16 else if (is_tag)
238 49 17 prio = 1;
239 - else
240 3 18 prio = 0;
241 -
242 153 19-22 add_to_known_names(data->repo, data->names,
243 - all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR),
244 - &peeled, prio, &sha1);
245 153 23 return 0;
246 - }
247 -
248 - struct possible_tag {
249 - struct commit_name *name;
250 - int depth;
251 - int found_order;
252 - unsigned flag_within;
253 - };
254 -
255 19 2 static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
256 - {
257 - struct possible_tag *tag;
258 - int error;
259 -
260 19 2 tag = git__malloc(sizeof(struct possible_tag));
261 19 3,4 GIT_ERROR_CHECK_ALLOC(tag);
262 -
263 19 5 memcpy(tag, in, sizeof(struct possible_tag));
264 19 5 tag->name = NULL;
265 -
266 19 5,6 if ((error = commit_name_dup(&tag->name, in->name)) < 0) {
267 ##### 7 git__free(tag);
268 ##### 8 *out = NULL;
269 ##### 8 return error;
270 - }
271 -
272 19 9 *out = tag;
273 19 9 return 0;
274 - }
275 -
276 43 2 static int compare_pt(const void *a_, const void *b_)
277 - {
278 43 2 struct possible_tag *a = (struct possible_tag *)a_;
279 43 2 struct possible_tag *b = (struct possible_tag *)b_;
280 43 2 if (a->depth != b->depth)
281 30 3 return a->depth - b->depth;
282 13 4 if (a->found_order != b->found_order)
283 13 5 return a->found_order - b->found_order;
284 ##### 6 return 0;
285 - }
286 -
287 - #define SEEN (1u << 0)
288 -
289 19 2 static unsigned long finish_depth_computation(
290 - git_pqueue *list,
291 - git_revwalk *walk,
292 - struct possible_tag *best)
293 - {
294 19 2 unsigned long seen_commits = 0;
295 - int error, i;
296 -
297 19 2,27,28 while (git_pqueue_size(list) > 0) {
298 ##### 3 git_commit_list_node *c = git_pqueue_pop(list);
299 ##### 4 seen_commits++;
300 ##### 4 if (c->flags & best->flag_within) {
301 ##### 5 size_t index = 0;
302 ##### 5,10,11 while (git_pqueue_size(list) > index) {
303 ##### 6 git_commit_list_node *i = git_pqueue_get(list, index);
304 ##### 7 if (!(i->flags & best->flag_within))
305 ##### 8 break;
306 ##### 9 index++;
307 - }
308 ##### 12,13 if (index > git_pqueue_size(list))
309 ##### 14,15 break;
310 - } else
311 ##### 16 best->depth++;
312 ##### 17,25,26 for (i = 0; i < c->out_degree; i++) {
313 ##### 18 git_commit_list_node *p = c->parents[i];
314 ##### 18,19 if ((error = git_commit_list_parse(walk, p)) < 0)
315 ##### 20 return error;
316 ##### 21 if (!(p->flags & SEEN))
317 ##### 22,23 if ((error = git_pqueue_insert(list, p)) < 0)
318 ##### 24 return error;
319 ##### 25 p->flags |= c->flags;
320 - }
321 - }
322 19 29 return seen_commits;
323 - }
324 -
325 27 2 static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n)
326 - {
327 27 2,3 if (n->prio == 2 && !n->tag) {
328 11 4,5 if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
329 ##### 6 git_error_set(GIT_ERROR_TAG, "annotated tag '%s' not available", n->path);
330 ##### 7 return -1;
331 - }
332 - }
333 -
334 27 8,9 if (n->tag && !n->name_checked) {
335 14 10,11 if (!git_tag_name(n->tag)) {
336 ##### 12 git_error_set(GIT_ERROR_TAG, "annotated tag '%s' has no embedded name", n->path);
337 ##### 13 return -1;
338 - }
339 -
340 - /* TODO: Cope with warnings
341 - if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
342 - warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
343 - */
344 -
345 14 14 n->name_checked = 1;
346 - }
347 -
348 27 15 if (n->tag)
349 14 16,17 git_buf_printf(buf, "%s", git_tag_name(n->tag));
350 - else
351 13 18 git_buf_printf(buf, "%s", n->path);
352 -
353 27 19 return 0;
354 - }
355 -
356 22 2 static int find_unique_abbrev_size(
357 - int *out,
358 - git_repository *repo,
359 - const git_oid *oid_in,
360 - unsigned int abbreviated_size)
361 - {
362 22 2 size_t size = abbreviated_size;
363 - git_odb *odb;
364 - git_oid dummy;
365 - int error;
366 -
367 22 2,3 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
368 ##### 4 return error;
369 -
370 22 5,12 while (size < GIT_OID_HEXSZ) {
371 22 6,7 if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
372 22 8 *out = (int) size;
373 22 8 return 0;
374 - }
375 -
376 - /* If the error wasn't that it's not unique, then it's a proper error */
377 ##### 9 if (error != GIT_EAMBIGUOUS)
378 ##### 10 return error;
379 -
380 - /* Try again with a larger size */
381 ##### 11 size++;
382 - }
383 -
384 - /* If we didn't find any shorter prefix, we have to do the whole thing */
385 ##### 13 *out = GIT_OID_HEXSZ;
386 -
387 ##### 13 return 0;
388 - }
389 -
390 21 2 static int show_suffix(
391 - git_buf *buf,
392 - int depth,
393 - git_repository *repo,
394 - const git_oid* id,
395 - unsigned int abbrev_size)
396 - {
397 21 2 int error, size = 0;
398 -
399 - char hex_oid[GIT_OID_HEXSZ];
400 -
401 21 2,3 if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
402 ##### 4 return error;
403 -
404 21 5 git_oid_fmt(hex_oid, id);
405 -
406 21 6 git_buf_printf(buf, "-%d-g", depth);
407 -
408 21 7 git_buf_put(buf, hex_oid, size);
409 -
410 21 8 return git_buf_oom(buf) ? -1 : 0;
411 - }
412 -
413 - #define MAX_CANDIDATES_TAGS FLAG_BITS - 1
414 -
415 ##### 2 static int describe_not_found(const git_oid *oid, const char *message_format) {
416 - char oid_str[GIT_OID_HEXSZ + 1];
417 ##### 2 git_oid_tostr(oid_str, sizeof(oid_str), oid);
418 -
419 ##### 3 git_error_set(GIT_ERROR_DESCRIBE, message_format, oid_str);
420 ##### 4 return GIT_ENOTFOUND;
421 - }
422 -
423 29 2 static int describe(
424 - struct get_name_data *data,
425 - git_commit *commit)
426 - {
427 - struct commit_name *n;
428 - struct possible_tag *best;
429 - bool all, tags;
430 29 2 git_revwalk *walk = NULL;
431 - git_pqueue list;
432 29 2 git_commit_list_node *cmit, *gave_up_on = NULL;
433 29 2 git_vector all_matches = GIT_VECTOR_INIT;
434 29 2 unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
435 29 2 unsigned long seen_commits = 0; /* TODO: Check long */
436 29 2 unsigned int unannotated_cnt = 0;
437 - int error;
438 -
439 29 2,3 if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
440 ##### 4 return -1;
441 -
442 29 5,6 if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
443 ##### 7 goto cleanup;
444 -
445 29 8 all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
446 29 8 tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;
447 -
448 29 8,9 git_oid_cpy(&data->result->commit_id, git_commit_id(commit));
449 -
450 29 10,11 n = find_commit_name(data->names, git_commit_id(commit));
451 29 12-15 if (n && (tags || all || n->prio == 2)) {
452 - /*
453 - * Exact match to an existing ref.
454 - */
455 8 16 data->result->exact_match = 1;
456 8 16,17 if ((error = commit_name_dup(&data->result->name, n)) < 0)
457 ##### 18 goto cleanup;
458 -
459 8 19 goto cleanup;
460 - }
461 -
462 21 20 if (!data->opts->max_candidates_tags) {
463 ##### 21,22 error = describe_not_found(
464 - git_commit_id(commit),
465 - "cannot describe - no tag exactly matches '%s'");
466 -
467 ##### 23 goto cleanup;
468 - }
469 -
470 21 24-26 if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
471 ##### 27 goto cleanup;
472 -
473 21 28-30 if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
474 ##### 31 goto cleanup;
475 -
476 21 32,33 if ((error = git_commit_list_parse(walk, cmit)) < 0)
477 ##### 34 goto cleanup;
478 -
479 21 35 cmit->flags = SEEN;
480 -
481 21 35,36 if ((error = git_pqueue_insert(&list, cmit)) < 0)
482 ##### 37 goto cleanup;
483 -
484 178 38,79,80 while (git_pqueue_size(&list) > 0)
485 - {
486 - int i;
487 -
488 174 39 git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
489 174 40 seen_commits++;
490 -
491 174 40 n = find_commit_name(data->names, &c->oid);
492 -
493 174 41 if (n) {
494 65 42-44 if (!tags && !all && n->prio < 2) {
495 14 45 unannotated_cnt++;
496 51 46 } else if (match_cnt < data->opts->max_candidates_tags) {
497 51 47 struct possible_tag *t = git__malloc(sizeof(struct commit_name));
498 51 48,49 GIT_ERROR_CHECK_ALLOC(t);
499 51 50,51 if ((error = git_vector_insert(&all_matches, t)) < 0)
500 ##### 52 goto cleanup;
501 -
502 51 53 match_cnt++;
503 -
504 51 53 t->name = n;
505 51 53 t->depth = seen_commits - 1;
506 51 53 t->flag_within = 1u << match_cnt;
507 51 53 t->found_order = match_cnt;
508 51 53 c->flags |= t->flag_within;
509 51 53 if (n->prio == 2)
510 51 54,55 annotated_cnt++;
511 - }
512 - else {
513 ##### 56 gave_up_on = c;
514 ##### 56 break;
515 - }
516 - }
517 -
518 421 57,61,62 for (cur_match = 0; cur_match < match_cnt; cur_match++) {
519 247 58 struct possible_tag *t = git_vector_get(&all_matches, cur_match);
520 247 59 if (!(c->flags & t->flag_within))
521 129 60 t->depth++;
522 - }
523 -
524 174 63-65 if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
525 - /*
526 - if (debug) {
527 - char oid_str[GIT_OID_HEXSZ + 1];
528 - git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);
529 -
530 - fprintf(stderr, "finished search at %s\n", oid_str);
531 - }
532 - */
533 17 66 break;
534 - }
535 333 67,77,78 for (i = 0; i < c->out_degree; i++) {
536 180 68 git_commit_list_node *p = c->parents[i];
537 180 68,69 if ((error = git_commit_list_parse(walk, p)) < 0)
538 ##### 70 goto cleanup;
539 180 71 if (!(p->flags & SEEN))
540 153 72,73 if ((error = git_pqueue_insert(&list, p)) < 0)
541 ##### 74 goto cleanup;
542 180 75 p->flags |= c->flags;
543 -
544 180 75 if (data->opts->only_follow_first_parent)
545 4 76 break;
546 - }
547 - }
548 -
549 21 81 if (!match_cnt) {
550 2 82 if (data->opts->show_commit_oid_as_fallback) {
551 2 83 data->result->fallback_to_id = 1;
552 2 83 git_oid_cpy(&data->result->commit_id, &cmit->oid);
553 -
554 2 84 goto cleanup;
555 - }
556 ##### 85 if (unannotated_cnt) {
557 ##### 86,87 error = describe_not_found(git_commit_id(commit),
558 - "cannot describe - "
559 - "no annotated tags can describe '%s'; "
560 - "however, there were unannotated tags.");
561 ##### 88 goto cleanup;
562 - }
563 - else {
564 ##### 89,90 error = describe_not_found(git_commit_id(commit),
565 - "cannot describe - "
566 - "no tags can describe '%s'.");
567 ##### 91 goto cleanup;
568 - }
569 - }
570 -
571 19 92 git_vector_sort(&all_matches);
572 -
573 19 93 best = (struct possible_tag *)git_vector_get(&all_matches, 0);
574 -
575 19 94 if (gave_up_on) {
576 ##### 95,96 if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
577 ##### 97 goto cleanup;
578 ##### 98 seen_commits--;
579 - }
580 19 99,100 if ((error = finish_depth_computation(
581 - &list, walk, best)) < 0)
582 ##### 101 goto cleanup;
583 -
584 19 102 seen_commits += error;
585 19 102,103 if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
586 ##### 104 goto cleanup;
587 -
588 - /*
589 - {
590 - static const char *prio_names[] = {
591 - "head", "lightweight", "annotated",
592 - };
593 -
594 - char oid_str[GIT_OID_HEXSZ + 1];
595 -
596 - if (debug) {
597 - for (cur_match = 0; cur_match < match_cnt; cur_match++) {
598 - struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match);
599 - fprintf(stderr, " %-11s %8d %s\n",
600 - prio_names[t->name->prio],
601 - t->depth, t->name->path);
602 - }
603 - fprintf(stderr, "traversed %lu commits\n", seen_commits);
604 - if (gave_up_on) {
605 - git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid);
606 - fprintf(stderr,
607 - "more than %i tags found; listed %i most recent\n"
608 - "gave up search at %s\n",
609 - data->opts->max_candidates_tags, data->opts->max_candidates_tags,
610 - oid_str);
611 - }
612 - }
613 - }
614 - */
615 -
616 19 105 git_oid_cpy(&data->result->commit_id, &cmit->oid);
617 -
618 - cleanup:
619 - {
620 - size_t i;
621 - struct possible_tag *match;
622 80 106,108-110 git_vector_foreach(&all_matches, i, match) {
623 51 107 git__free(match);
624 - }
625 - }
626 29 111 git_vector_free(&all_matches);
627 29 112 git_pqueue_free(&list);
628 29 113 git_revwalk_free(walk);
629 29 114 return error;
630 - }
631 -
632 30 2 static int normalize_options(
633 - git_describe_options *dst,
634 - const git_describe_options *src)
635 - {
636 30 2 git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
637 30 2,3 if (!src) src = &default_options;
638 -
639 30 4 *dst = *src;
640 -
641 30 4 if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
642 ##### 5 dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
643 -
644 30 6 return 0;
645 - }
646 -
647 30 2 int git_describe_commit(
648 - git_describe_result **result,
649 - git_object *committish,
650 - git_describe_options *opts)
651 - {
652 - struct get_name_data data;
653 - struct commit_name *name;
654 - git_commit *commit;
655 30 2 int error = -1;
656 - git_describe_options normalized;
657 -
658 30 2,3 assert(committish);
659 -
660 30 4 data.result = git__calloc(1, sizeof(git_describe_result));
661 30 5,6 GIT_ERROR_CHECK_ALLOC(data.result);
662 30 7 data.result->repo = git_object_owner(committish);
663 -
664 30 8 data.repo = git_object_owner(committish);
665 -
666 30 9,10 if ((error = normalize_options(&normalized, opts)) < 0)
667 ##### 11 return error;
668 -
669 30 12-14 GIT_ERROR_CHECK_VERSION(
670 - &normalized,
671 - GIT_DESCRIBE_OPTIONS_VERSION,
672 - "git_describe_options");
673 30 15 data.opts = &normalized;
674 -
675 30 15,16 if ((error = git_oidmap_new(&data.names)) < 0)
676 ##### 17 return error;
677 -
678 - /** TODO: contains to be implemented */
679 -
680 30 18,19 if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJECT_COMMIT)) < 0)
681 ##### 20 goto cleanup;
682 -
683 30 21-23 if ((error = git_reference_foreach_name(
684 - git_object_owner(committish),
685 - get_name, &data)) < 0)
686 ##### 24 goto cleanup;
687 -
688 30 25-27 if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) {
689 1 28 git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - "
690 - "no reference found, cannot describe anything.");
691 1 29 error = -1;
692 1 29 goto cleanup;
693 - }
694 -
695 29 30,31 if ((error = describe(&data, commit)) < 0)
696 ##### 32 goto cleanup;
697 -
698 - cleanup:
699 30 33 git_commit_free(commit);
700 -
701 153 34-39 git_oidmap_foreach_value(data.names, name, {
702 - git_tag_free(name->tag);
703 - git__free(name->path);
704 - git__free(name);
705 - });
706 -
707 30 40 git_oidmap_free(data.names);
708 -
709 30 41 if (error < 0)
710 1 42 git_describe_result_free(data.result);
711 - else
712 29 43 *result = data.result;
713 -
714 30 44 return error;
715 - }
716 -
717 3 2 int git_describe_workdir(
718 - git_describe_result **out,
719 - git_repository *repo,
720 - git_describe_options *opts)
721 - {
722 - int error;
723 - git_oid current_id;
724 3 2 git_status_list *status = NULL;
725 3 2 git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
726 3 2 git_describe_result *result = NULL;
727 - git_object *commit;
728 -
729 3 2,3 if ((error = git_reference_name_to_id(&current_id, repo, GIT_HEAD_FILE)) < 0)
730 ##### 4 return error;
731 -
732 3 5,6 if ((error = git_object_lookup(&commit, repo, &current_id, GIT_OBJECT_COMMIT)) < 0)
733 ##### 7 return error;
734 -
735 - /* The first step is to perform a describe of HEAD, so we can leverage this */
736 3 8,9 if ((error = git_describe_commit(&result, commit, opts)) < 0)
737 ##### 10 goto out;
738 -
739 3 11,12 if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
740 ##### 13 goto out;
741 -
742 -
743 3 14,15 if (git_status_list_entrycount(status) > 0)
744 3 16 result->dirty = 1;
745 -
746 - out:
747 3 17 git_object_free(commit);
748 3 18 git_status_list_free(status);
749 -
750 3 19 if (error < 0)
751 ##### 20 git_describe_result_free(result);
752 - else
753 3 21 *out = result;
754 -
755 3 22 return error;
756 - }
757 -
758 28 2 static int normalize_format_options(
759 - git_describe_format_options *dst,
760 - const git_describe_format_options *src)
761 - {
762 28 2 if (!src) {
763 ##### 3 git_describe_format_options_init(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
764 ##### 4 return 0;
765 - }
766 -
767 28 5 memcpy(dst, src, sizeof(git_describe_format_options));
768 28 5 return 0;
769 - }
770 -
771 28 2 int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *given)
772 - {
773 - int error;
774 - git_repository *repo;
775 - struct commit_name *name;
776 - git_describe_format_options opts;
777 -
778 28 2-4 assert(out && result);
779 -
780 28 5-7 GIT_ERROR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
781 28 8 normalize_format_options(&opts, given);
782 -
783 28 9 git_buf_sanitize(out);
784 -
785 -
786 28 10,11 if (opts.always_use_long_format && opts.abbreviated_size == 0) {
787 ##### 12 git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - "
788 - "'always_use_long_format' is incompatible with a zero"
789 - "'abbreviated_size'");
790 ##### 13 return -1;
791 - }
792 -
793 -
794 28 14 repo = result->repo;
795 -
796 - /* If we did find an exact match, then it's the easier method */
797 28 14 if (result->exact_match) {
798 8 15 name = result->name;
799 8 15,16 if ((error = display_name(out, repo, name)) < 0)
800 ##### 17 return error;
801 -
802 8 18 if (opts.always_use_long_format) {
803 2 19-21 const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
804 2 22,23 if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0)
805 ##### 24 return error;
806 - }
807 -
808 8 25,26 if (result->dirty && opts.dirty_suffix)
809 ##### 27 git_buf_puts(out, opts.dirty_suffix);
810 -
811 8 28-32 return git_buf_oom(out) ? -1 : 0;
812 - }
813 -
814 - /* If we didn't find *any* tags, we fall back to the commit's id */
815 20 33 if (result->fallback_to_id) {
816 1 34 char hex_oid[GIT_OID_HEXSZ + 1] = {0};
817 1 34 int size = 0;
818 -
819 1 34,35 if ((error = find_unique_abbrev_size(
820 - &size, repo, &result->commit_id, opts.abbreviated_size)) < 0)
821 ##### 36 return -1;
822 -
823 1 37 git_oid_fmt(hex_oid, &result->commit_id);
824 1 38 git_buf_put(out, hex_oid, size);
825 -
826 1 39,40 if (result->dirty && opts.dirty_suffix)
827 ##### 41 git_buf_puts(out, opts.dirty_suffix);
828 -
829 1 42-47 return git_buf_oom(out) ? -1 : 0;
830 - }
831 -
832 - /* Lastly, if we found a matching tag, we show that */
833 19 48 name = result->tag->name;
834 -
835 19 48,49 if ((error = display_name(out, repo, name)) < 0)
836 ##### 50 return error;
837 -
838 19 51 if (opts.abbreviated_size) {
839 19 52,53 if ((error = show_suffix(out, result->tag->depth, repo,
840 - &result->commit_id, opts.abbreviated_size)) < 0)
841 ##### 54 return error;
842 - }
843 -
844 19 55,56 if (result->dirty && opts.dirty_suffix) {
845 2 57 git_buf_puts(out, opts.dirty_suffix);
846 - }
847 -
848 19 58 return git_buf_oom(out) ? -1 : 0;
849 - }
850 -
851 30 2 void git_describe_result_free(git_describe_result *result)
852 - {
853 30 2 if (result == NULL)
854 30 3,14 return;
855 -
856 30 4 if (result->name) {
857 8 5 git_tag_free(result->name->tag);
858 8 6 git__free(result->name->path);
859 8 7 git__free(result->name);
860 - }
861 -
862 30 8 if (result->tag) {
863 19 9 git_tag_free(result->tag->name->tag);
864 19 10 git__free(result->tag->name->path);
865 19 11 git__free(result->tag->name);
866 19 12 git__free(result->tag);
867 - }
868 -
869 30 13 git__free(result);
870 - }
871 -
872 ##### 2 int git_describe_options_init(git_describe_options *opts, unsigned int version)
873 - {
874 ##### 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
875 - opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
876 ##### 5 return 0;
877 - }
878 -
879 - #ifndef GIT_DEPRECATE_HARD
880 ##### 2 int git_describe_init_options(git_describe_options *opts, unsigned int version)
881 - {
882 ##### 2 return git_describe_options_init(opts, version);
883 - }
884 - #endif
885 -
886 ##### 2 int git_describe_format_options_init(git_describe_format_options *opts, unsigned int version)
887 - {
888 ##### 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
889 - opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
890 ##### 5 return 0;
891 - }
892 -
893 - #ifndef GIT_DEPRECATE_HARD
894 ##### 2 int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
895 - {
896 ##### 2 return git_describe_format_options_init(opts, version);
897 - }
898 - #endif