File Coverage

deps/libgit2/src/libgit2/describe.c
Criterion Covered Total %
statement 0 400 0.0
branch 0 298 0.0
condition n/a
subroutine n/a
pod n/a
total 0 698 0.0


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