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