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