File Coverage

deps/libgit2/src/describe.c
Criterion Covered Total %
statement 0 397 0.0
branch 0 292 0.0
condition n/a
subroutine n/a
pod n/a
total 0 689 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 "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 0           static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
39             {
40 0           return git_oidmap_get(map, key);
41             }
42              
43 0           static struct commit_name *find_commit_name(
44             git_oidmap *names,
45             const git_oid *peeled)
46             {
47 0           return (struct commit_name *)(oidmap_value_bykey(names, peeled));
48             }
49              
50 0           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 0           git_time_t e_time = 0, t_time = 0;
58              
59 0 0         if (!e || e->prio < prio)
    0          
60 0           return 1;
61              
62 0 0         if (e->prio == 2 && prio == 2) {
    0          
63             /* Multiple annotated tags point to the same commit.
64             * Select one to keep based upon their tagger date.
65             */
66 0           git_tag *t = NULL;
67              
68 0 0         if (!e->tag) {
69 0 0         if (git_tag_lookup(&t, repo, &e->sha1) < 0)
70 0           return 1;
71 0           e->tag = t;
72             }
73              
74 0 0         if (git_tag_lookup(&t, repo, sha1) < 0)
75 0           return 0;
76              
77 0           *tag = t;
78              
79 0 0         if (e->tag->tagger)
80 0           e_time = e->tag->tagger->when.time;
81              
82 0 0         if (t->tagger)
83 0           t_time = t->tagger->when.time;
84              
85 0 0         if (e_time < t_time)
86 0           return 1;
87             }
88              
89 0           return 0;
90             }
91              
92 0           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 0           struct commit_name *e = find_commit_name(names, peeled);
101 0           bool found = (e != NULL);
102              
103 0           git_tag *tag = NULL;
104 0 0         if (replace_name(&tag, repo, e, prio, sha1)) {
105 0 0         if (!found) {
106 0           e = git__malloc(sizeof(struct commit_name));
107 0 0         GIT_ERROR_CHECK_ALLOC(e);
108              
109 0           e->path = NULL;
110 0           e->tag = NULL;
111             }
112              
113 0 0         if (e->tag)
114 0           git_tag_free(e->tag);
115 0           e->tag = tag;
116 0           e->prio = prio;
117 0           e->name_checked = 0;
118 0           git_oid_cpy(&e->sha1, sha1);
119 0           git__free(e->path);
120 0           e->path = git__strdup(path);
121 0           git_oid_cpy(&e->peeled, peeled);
122              
123 0 0         if (!found && git_oidmap_set(names, &e->peeled, e) < 0)
    0          
124 0           return -1;
125             }
126             else
127 0           git_tag_free(tag);
128              
129 0           return 0;
130             }
131              
132 0           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 0           git_object *peeled = NULL;
140             int error;
141              
142 0 0         if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
143 0           return error;
144              
145 0 0         if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_ANY)) < 0)
146 0           goto cleanup;
147              
148 0           git_oid_cpy(ref_target_out, git_reference_target(ref));
149 0           git_oid_cpy(peeled_out, git_object_id(peeled));
150              
151 0 0         if (git_oid_cmp(ref_target_out, peeled_out) != 0)
152 0           error = 1; /* The reference was pointing to a annotated tag */
153             else
154 0           error = 0; /* Any other object */
155              
156             cleanup:
157 0           git_reference_free(ref);
158 0           git_object_free(peeled);
159 0           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 0           static int commit_name_dup(struct commit_name **out, struct commit_name *in)
181             {
182             struct commit_name *name;
183              
184 0           name = git__malloc(sizeof(struct commit_name));
185 0 0         GIT_ERROR_CHECK_ALLOC(name);
186              
187 0           memcpy(name, in, sizeof(struct commit_name));
188 0           name->tag = NULL;
189 0           name->path = NULL;
190              
191 0 0         if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
    0          
192 0           return -1;
193              
194 0           name->path = git__strdup(in->path);
195 0 0         GIT_ERROR_CHECK_ALLOC(name->path);
196              
197 0           *out = name;
198 0           return 0;
199             }
200              
201 0           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 0           int error = 0;
208              
209 0           data = (struct get_name_data *)payload;
210 0           is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
211 0           all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
212              
213             /* Reject anything outside refs/tags/ unless --all */
214 0 0         if (!all && !is_tag)
    0          
215 0           return 0;
216              
217             /* Accept only tags that match the pattern, if given */
218 0 0         if (data->opts->pattern && (!is_tag || wildmatch(data->opts->pattern,
    0          
    0          
219             refname + strlen(GIT_REFS_TAGS_DIR), 0)))
220 0           return 0;
221              
222             /* Is it annotated? */
223 0 0         if ((error = retrieve_peeled_tag_or_object_oid(
224             &peeled, &sha1, data->repo, refname)) < 0)
225 0           return error;
226              
227 0           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 0 0         if (is_annotated)
236 0           prio = 2;
237 0 0         else if (is_tag)
238 0           prio = 1;
239             else
240 0           prio = 0;
241              
242 0 0         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 0           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 0           static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
256             {
257             struct possible_tag *tag;
258             int error;
259              
260 0           tag = git__malloc(sizeof(struct possible_tag));
261 0 0         GIT_ERROR_CHECK_ALLOC(tag);
262              
263 0           memcpy(tag, in, sizeof(struct possible_tag));
264 0           tag->name = NULL;
265              
266 0 0         if ((error = commit_name_dup(&tag->name, in->name)) < 0) {
267 0           git__free(tag);
268 0           *out = NULL;
269 0           return error;
270             }
271              
272 0           *out = tag;
273 0           return 0;
274             }
275              
276 0           static int compare_pt(const void *a_, const void *b_)
277             {
278 0           struct possible_tag *a = (struct possible_tag *)a_;
279 0           struct possible_tag *b = (struct possible_tag *)b_;
280 0 0         if (a->depth != b->depth)
281 0           return a->depth - b->depth;
282 0 0         if (a->found_order != b->found_order)
283 0           return a->found_order - b->found_order;
284 0           return 0;
285             }
286              
287             #define SEEN (1u << 0)
288              
289 0           static unsigned long finish_depth_computation(
290             git_pqueue *list,
291             git_revwalk *walk,
292             struct possible_tag *best)
293             {
294 0           unsigned long seen_commits = 0;
295             int error, i;
296              
297 0 0         while (git_pqueue_size(list) > 0) {
298 0           git_commit_list_node *c = git_pqueue_pop(list);
299 0           seen_commits++;
300 0 0         if (c->flags & best->flag_within) {
301 0           size_t index = 0;
302 0 0         while (git_pqueue_size(list) > index) {
303 0           git_commit_list_node *i = git_pqueue_get(list, index);
304 0 0         if (!(i->flags & best->flag_within))
305 0           break;
306 0           index++;
307             }
308 0 0         if (index > git_pqueue_size(list))
309 0           break;
310             } else
311 0           best->depth++;
312 0 0         for (i = 0; i < c->out_degree; i++) {
313 0           git_commit_list_node *p = c->parents[i];
314 0 0         if ((error = git_commit_list_parse(walk, p)) < 0)
315 0           return error;
316 0 0         if (!(p->flags & SEEN))
317 0 0         if ((error = git_pqueue_insert(list, p)) < 0)
318 0           return error;
319 0           p->flags |= c->flags;
320             }
321             }
322 0           return seen_commits;
323             }
324              
325 0           static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n)
326             {
327 0 0         if (n->prio == 2 && !n->tag) {
    0          
328 0 0         if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
329 0           git_error_set(GIT_ERROR_TAG, "annotated tag '%s' not available", n->path);
330 0           return -1;
331             }
332             }
333              
334 0 0         if (n->tag && !n->name_checked) {
    0          
335 0 0         if (!git_tag_name(n->tag)) {
336 0           git_error_set(GIT_ERROR_TAG, "annotated tag '%s' has no embedded name", n->path);
337 0           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 0           n->name_checked = 1;
346             }
347              
348 0 0         if (n->tag)
349 0           git_buf_printf(buf, "%s", git_tag_name(n->tag));
350             else
351 0           git_buf_printf(buf, "%s", n->path);
352              
353 0           return 0;
354             }
355              
356 0           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 0           size_t size = abbreviated_size;
363             git_odb *odb;
364             git_oid dummy;
365             int error;
366              
367 0 0         if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
368 0           return error;
369              
370 0 0         while (size < GIT_OID_HEXSZ) {
371 0 0         if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
372 0           *out = (int) size;
373 0           return 0;
374             }
375              
376             /* If the error wasn't that it's not unique, then it's a proper error */
377 0 0         if (error != GIT_EAMBIGUOUS)
378 0           return error;
379              
380             /* Try again with a larger size */
381 0           size++;
382             }
383              
384             /* If we didn't find any shorter prefix, we have to do the whole thing */
385 0           *out = GIT_OID_HEXSZ;
386              
387 0           return 0;
388             }
389              
390 0           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 0           int error, size = 0;
398              
399             char hex_oid[GIT_OID_HEXSZ];
400              
401 0 0         if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
402 0           return error;
403              
404 0           git_oid_fmt(hex_oid, id);
405              
406 0           git_buf_printf(buf, "-%d-g", depth);
407              
408 0           git_buf_put(buf, hex_oid, size);
409              
410 0 0         return git_buf_oom(buf) ? -1 : 0;
411             }
412              
413             #define MAX_CANDIDATES_TAGS FLAG_BITS - 1
414              
415 0           static int describe_not_found(const git_oid *oid, const char *message_format) {
416             char oid_str[GIT_OID_HEXSZ + 1];
417 0           git_oid_tostr(oid_str, sizeof(oid_str), oid);
418              
419 0           git_error_set(GIT_ERROR_DESCRIBE, message_format, oid_str);
420 0           return GIT_ENOTFOUND;
421             }
422              
423 0           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 0           git_revwalk *walk = NULL;
431             git_pqueue list;
432 0           git_commit_list_node *cmit, *gave_up_on = NULL;
433 0           git_vector all_matches = GIT_VECTOR_INIT;
434 0           unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
435 0           unsigned long seen_commits = 0; /* TODO: Check long */
436 0           unsigned int unannotated_cnt = 0;
437             int error;
438              
439 0 0         if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
440 0           return -1;
441              
442 0 0         if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
443 0           goto cleanup;
444              
445 0           all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
446 0           tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;
447              
448 0           git_oid_cpy(&data->result->commit_id, git_commit_id(commit));
449              
450 0           n = find_commit_name(data->names, git_commit_id(commit));
451 0 0         if (n && (tags || all || n->prio == 2)) {
    0          
    0          
    0          
452             /*
453             * Exact match to an existing ref.
454             */
455 0           data->result->exact_match = 1;
456 0 0         if ((error = commit_name_dup(&data->result->name, n)) < 0)
457 0           goto cleanup;
458              
459 0           goto cleanup;
460             }
461              
462 0 0         if (!data->opts->max_candidates_tags) {
463 0           error = describe_not_found(
464             git_commit_id(commit),
465             "cannot describe - no tag exactly matches '%s'");
466              
467 0           goto cleanup;
468             }
469              
470 0 0         if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
471 0           goto cleanup;
472              
473 0 0         if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
474 0           goto cleanup;
475              
476 0 0         if ((error = git_commit_list_parse(walk, cmit)) < 0)
477 0           goto cleanup;
478              
479 0           cmit->flags = SEEN;
480              
481 0 0         if ((error = git_pqueue_insert(&list, cmit)) < 0)
482 0           goto cleanup;
483              
484 0 0         while (git_pqueue_size(&list) > 0)
485             {
486             int i;
487              
488 0           git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
489 0           seen_commits++;
490              
491 0           n = find_commit_name(data->names, &c->oid);
492              
493 0 0         if (n) {
494 0 0         if (!tags && !all && n->prio < 2) {
    0          
    0          
495 0           unannotated_cnt++;
496 0 0         } else if (match_cnt < data->opts->max_candidates_tags) {
497 0           struct possible_tag *t = git__malloc(sizeof(struct commit_name));
498 0 0         GIT_ERROR_CHECK_ALLOC(t);
499 0 0         if ((error = git_vector_insert(&all_matches, t)) < 0)
500 0           goto cleanup;
501              
502 0           match_cnt++;
503              
504 0           t->name = n;
505 0           t->depth = seen_commits - 1;
506 0           t->flag_within = 1u << match_cnt;
507 0           t->found_order = match_cnt;
508 0           c->flags |= t->flag_within;
509 0 0         if (n->prio == 2)
510 0           annotated_cnt++;
511             }
512             else {
513 0           gave_up_on = c;
514 0           break;
515             }
516             }
517              
518 0 0         for (cur_match = 0; cur_match < match_cnt; cur_match++) {
519 0           struct possible_tag *t = git_vector_get(&all_matches, cur_match);
520 0 0         if (!(c->flags & t->flag_within))
521 0           t->depth++;
522             }
523              
524 0 0         if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
    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 0           break;
534             }
535 0 0         for (i = 0; i < c->out_degree; i++) {
536 0           git_commit_list_node *p = c->parents[i];
537 0 0         if ((error = git_commit_list_parse(walk, p)) < 0)
538 0           goto cleanup;
539 0 0         if (!(p->flags & SEEN))
540 0 0         if ((error = git_pqueue_insert(&list, p)) < 0)
541 0           goto cleanup;
542 0           p->flags |= c->flags;
543              
544 0 0         if (data->opts->only_follow_first_parent)
545 0           break;
546             }
547             }
548              
549 0 0         if (!match_cnt) {
550 0 0         if (data->opts->show_commit_oid_as_fallback) {
551 0           data->result->fallback_to_id = 1;
552 0           git_oid_cpy(&data->result->commit_id, &cmit->oid);
553              
554 0           goto cleanup;
555             }
556 0 0         if (unannotated_cnt) {
557 0           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 0           goto cleanup;
562             }
563             else {
564 0           error = describe_not_found(git_commit_id(commit),
565             "cannot describe - "
566             "no tags can describe '%s'.");
567 0           goto cleanup;
568             }
569             }
570              
571 0           git_vector_sort(&all_matches);
572              
573 0           best = (struct possible_tag *)git_vector_get(&all_matches, 0);
574              
575 0 0         if (gave_up_on) {
576 0 0         if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
577 0           goto cleanup;
578 0           seen_commits--;
579             }
580 0 0         if ((error = finish_depth_computation(
581             &list, walk, best)) < 0)
582 0           goto cleanup;
583              
584 0           seen_commits += error;
585 0 0         if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
586 0           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 0           git_oid_cpy(&data->result->commit_id, &cmit->oid);
617              
618             cleanup:
619             {
620             size_t i;
621             struct possible_tag *match;
622 0 0         git_vector_foreach(&all_matches, i, match) {
623 0           git__free(match);
624             }
625             }
626 0           git_vector_free(&all_matches);
627 0           git_pqueue_free(&list);
628 0           git_revwalk_free(walk);
629 0           return error;
630             }
631              
632 0           static int normalize_options(
633             git_describe_options *dst,
634             const git_describe_options *src)
635             {
636 0           git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
637 0 0         if (!src) src = &default_options;
638              
639 0           *dst = *src;
640              
641 0 0         if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
642 0           dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
643              
644 0           return 0;
645             }
646              
647 0           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 0           int error = -1;
656             git_describe_options normalized;
657              
658 0 0         assert(committish);
659              
660 0           data.result = git__calloc(1, sizeof(git_describe_result));
661 0 0         GIT_ERROR_CHECK_ALLOC(data.result);
662 0           data.result->repo = git_object_owner(committish);
663              
664 0           data.repo = git_object_owner(committish);
665              
666 0 0         if ((error = normalize_options(&normalized, opts)) < 0)
667 0           return error;
668              
669 0 0         GIT_ERROR_CHECK_VERSION(
670             &normalized,
671             GIT_DESCRIBE_OPTIONS_VERSION,
672             "git_describe_options");
673 0           data.opts = &normalized;
674              
675 0 0         if ((error = git_oidmap_new(&data.names)) < 0)
676 0           return error;
677              
678             /** TODO: contains to be implemented */
679              
680 0 0         if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJECT_COMMIT)) < 0)
681 0           goto cleanup;
682              
683 0 0         if ((error = git_reference_foreach_name(
684             git_object_owner(committish),
685             get_name, &data)) < 0)
686 0           goto cleanup;
687              
688 0 0         if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) {
    0          
689 0           git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - "
690             "no reference found, cannot describe anything.");
691 0           error = -1;
692 0           goto cleanup;
693             }
694              
695 0 0         if ((error = describe(&data, commit)) < 0)
696 0           goto cleanup;
697              
698             cleanup:
699 0           git_commit_free(commit);
700              
701 0 0         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 0           git_oidmap_free(data.names);
708              
709 0 0         if (error < 0)
710 0           git_describe_result_free(data.result);
711             else
712 0           *result = data.result;
713              
714 0           return error;
715             }
716              
717 0           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 0           git_status_list *status = NULL;
725 0           git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
726 0           git_describe_result *result = NULL;
727             git_object *commit;
728              
729 0 0         if ((error = git_reference_name_to_id(¤t_id, repo, GIT_HEAD_FILE)) < 0)
730 0           return error;
731              
732 0 0         if ((error = git_object_lookup(&commit, repo, ¤t_id, GIT_OBJECT_COMMIT)) < 0)
733 0           return error;
734              
735             /* The first step is to perform a describe of HEAD, so we can leverage this */
736 0 0         if ((error = git_describe_commit(&result, commit, opts)) < 0)
737 0           goto out;
738              
739 0 0         if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
740 0           goto out;
741              
742              
743 0 0         if (git_status_list_entrycount(status) > 0)
744 0           result->dirty = 1;
745              
746             out:
747 0           git_object_free(commit);
748 0           git_status_list_free(status);
749              
750 0 0         if (error < 0)
751 0           git_describe_result_free(result);
752             else
753 0           *out = result;
754              
755 0           return error;
756             }
757              
758 0           static int normalize_format_options(
759             git_describe_format_options *dst,
760             const git_describe_format_options *src)
761             {
762 0 0         if (!src) {
763 0           git_describe_format_options_init(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
764 0           return 0;
765             }
766              
767 0           memcpy(dst, src, sizeof(git_describe_format_options));
768 0           return 0;
769             }
770              
771 0           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 0 0         assert(out && result);
    0          
779              
780 0 0         GIT_ERROR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
781 0           normalize_format_options(&opts, given);
782              
783 0           git_buf_sanitize(out);
784              
785              
786 0 0         if (opts.always_use_long_format && opts.abbreviated_size == 0) {
    0          
787 0           git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - "
788             "'always_use_long_format' is incompatible with a zero"
789             "'abbreviated_size'");
790 0           return -1;
791             }
792              
793              
794 0           repo = result->repo;
795              
796             /* If we did find an exact match, then it's the easier method */
797 0 0         if (result->exact_match) {
798 0           name = result->name;
799 0 0         if ((error = display_name(out, repo, name)) < 0)
800 0           return error;
801              
802 0 0         if (opts.always_use_long_format) {
803 0 0         const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
804 0 0         if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0)
805 0           return error;
806             }
807              
808 0 0         if (result->dirty && opts.dirty_suffix)
    0          
809 0           git_buf_puts(out, opts.dirty_suffix);
810              
811 0 0         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 0 0         if (result->fallback_to_id) {
816 0           char hex_oid[GIT_OID_HEXSZ + 1] = {0};
817 0           int size = 0;
818              
819 0 0         if ((error = find_unique_abbrev_size(
820             &size, repo, &result->commit_id, opts.abbreviated_size)) < 0)
821 0           return -1;
822              
823 0           git_oid_fmt(hex_oid, &result->commit_id);
824 0           git_buf_put(out, hex_oid, size);
825              
826 0 0         if (result->dirty && opts.dirty_suffix)
    0          
827 0           git_buf_puts(out, opts.dirty_suffix);
828              
829 0 0         return git_buf_oom(out) ? -1 : 0;
830             }
831              
832             /* Lastly, if we found a matching tag, we show that */
833 0           name = result->tag->name;
834              
835 0 0         if ((error = display_name(out, repo, name)) < 0)
836 0           return error;
837              
838 0 0         if (opts.abbreviated_size) {
839 0 0         if ((error = show_suffix(out, result->tag->depth, repo,
840             &result->commit_id, opts.abbreviated_size)) < 0)
841 0           return error;
842             }
843              
844 0 0         if (result->dirty && opts.dirty_suffix) {
    0          
845 0           git_buf_puts(out, opts.dirty_suffix);
846             }
847              
848 0 0         return git_buf_oom(out) ? -1 : 0;
849             }
850              
851 0           void git_describe_result_free(git_describe_result *result)
852             {
853 0 0         if (result == NULL)
854 0           return;
855              
856 0 0         if (result->name) {
857 0           git_tag_free(result->name->tag);
858 0           git__free(result->name->path);
859 0           git__free(result->name);
860             }
861              
862 0 0         if (result->tag) {
863 0           git_tag_free(result->tag->name->tag);
864 0           git__free(result->tag->name->path);
865 0           git__free(result->tag->name);
866 0           git__free(result->tag);
867             }
868              
869 0           git__free(result);
870             }
871              
872 0           int git_describe_options_init(git_describe_options *opts, unsigned int version)
873             {
874 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
875             opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
876 0           return 0;
877             }
878              
879 0           int git_describe_init_options(git_describe_options *opts, unsigned int version)
880             {
881 0           return git_describe_options_init(opts, version);
882             }
883              
884 0           int git_describe_format_options_init(git_describe_format_options *opts, unsigned int version)
885             {
886 0 0         GIT_INIT_STRUCTURE_FROM_TEMPLATE(
887             opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
888 0           return 0;
889             }
890              
891 0           int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
892             {
893 0           return git_describe_format_options_init(opts, version);
894             }