File Coverage

deps/libgit2/src/libgit2/tag.c
Criterion Covered Total %
statement 125 267 46.8
branch 52 168 30.9
condition n/a
subroutine n/a
pod n/a
total 177 435 40.6


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 "tag.h"
9              
10             #include "commit.h"
11             #include "signature.h"
12             #include "wildmatch.h"
13             #include "git2/object.h"
14             #include "git2/repository.h"
15             #include "git2/signature.h"
16             #include "git2/odb_backend.h"
17              
18 0           void git_tag__free(void *_tag)
19             {
20 0           git_tag *tag = _tag;
21 0           git_signature_free(tag->tagger);
22 0           git__free(tag->message);
23 0           git__free(tag->tag_name);
24 0           git__free(tag);
25 0           }
26              
27 1           int git_tag_target(git_object **target, const git_tag *t)
28             {
29 1 50         GIT_ASSERT_ARG(t);
30 1           return git_object_lookup(target, t->object.repo, &t->target, t->type);
31             }
32              
33 0           const git_oid *git_tag_target_id(const git_tag *t)
34             {
35 0 0         GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
36 0           return &t->target;
37             }
38              
39 0           git_object_t git_tag_target_type(const git_tag *t)
40             {
41 0 0         GIT_ASSERT_ARG_WITH_RETVAL(t, GIT_OBJECT_INVALID);
42 0           return t->type;
43             }
44              
45 3           const char *git_tag_name(const git_tag *t)
46             {
47 3 50         GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
48 3           return t->tag_name;
49             }
50              
51 4           const git_signature *git_tag_tagger(const git_tag *t)
52             {
53 4           return t->tagger;
54             }
55              
56 2           const char *git_tag_message(const git_tag *t)
57             {
58 2 50         GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
59 2           return t->message;
60             }
61              
62 0           static int tag_error(const char *str)
63             {
64 0           git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str);
65 0           return GIT_EINVALID;
66             }
67              
68 1           static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
69             {
70             static const char *tag_types[] = {
71             NULL, "commit\n", "tree\n", "blob\n", "tag\n"
72             };
73             size_t text_len, alloc_len;
74             const char *search;
75             unsigned int i;
76             int error;
77              
78 1 50         if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
79 0           return tag_error("object field invalid");
80              
81 1 50         if (buffer + 5 >= buffer_end)
82 0           return tag_error("object too short");
83              
84 1 50         if (memcmp(buffer, "type ", 5) != 0)
85 0           return tag_error("type field not found");
86 1           buffer += 5;
87              
88 1           tag->type = GIT_OBJECT_INVALID;
89              
90 1 50         for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
91 1           size_t type_length = strlen(tag_types[i]);
92              
93 1 50         if (buffer + type_length >= buffer_end)
94 0           return tag_error("object too short");
95              
96 1 50         if (memcmp(buffer, tag_types[i], type_length) == 0) {
97 1           tag->type = i;
98 1           buffer += type_length;
99 1           break;
100             }
101             }
102              
103 1 50         if (tag->type == GIT_OBJECT_INVALID)
104 0           return tag_error("invalid object type");
105              
106 1 50         if (buffer + 4 >= buffer_end)
107 0           return tag_error("object too short");
108              
109 1 50         if (memcmp(buffer, "tag ", 4) != 0)
110 0           return tag_error("tag field not found");
111              
112 1           buffer += 4;
113              
114 1           search = memchr(buffer, '\n', buffer_end - buffer);
115 1 50         if (search == NULL)
116 0           return tag_error("object too short");
117              
118 1           text_len = search - buffer;
119              
120 1 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
    50          
121 1           tag->tag_name = git__malloc(alloc_len);
122 1 50         GIT_ERROR_CHECK_ALLOC(tag->tag_name);
123              
124 1           memcpy(tag->tag_name, buffer, text_len);
125 1           tag->tag_name[text_len] = '\0';
126              
127 1           buffer = search + 1;
128              
129 1           tag->tagger = NULL;
130 1 50         if (buffer < buffer_end && *buffer != '\n') {
    50          
131 1           tag->tagger = git__malloc(sizeof(git_signature));
132 1 50         GIT_ERROR_CHECK_ALLOC(tag->tagger);
133              
134 1 50         if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n')) < 0)
135 0           return error;
136             }
137              
138 1           tag->message = NULL;
139 1 50         if (buffer < buffer_end) {
140             /* If we're not at the end of the header, search for it */
141 1 50         if(*buffer != '\n') {
142 0           search = git__memmem(buffer, buffer_end - buffer,
143             "\n\n", 2);
144 0 0         if (search)
145 0           buffer = search + 1;
146             else
147 0           return tag_error("tag contains no message");
148             }
149              
150 1           text_len = buffer_end - ++buffer;
151              
152 1 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
    50          
153 1           tag->message = git__malloc(alloc_len);
154 1 50         GIT_ERROR_CHECK_ALLOC(tag->message);
155              
156 1           memcpy(tag->message, buffer, text_len);
157 1           tag->message[text_len] = '\0';
158             }
159              
160 1           return 0;
161             }
162              
163 0           int git_tag__parse_raw(void *_tag, const char *data, size_t size)
164             {
165 0           return tag_parse(_tag, data, data + size);
166             }
167              
168 1           int git_tag__parse(void *_tag, git_odb_object *odb_obj)
169             {
170 1           git_tag *tag = _tag;
171 1           const char *buffer = git_odb_object_data(odb_obj);
172 1           const char *buffer_end = buffer + git_odb_object_size(odb_obj);
173              
174 1           return tag_parse(tag, buffer, buffer_end);
175             }
176              
177 1           static int retrieve_tag_reference(
178             git_reference **tag_reference_out,
179             git_str *ref_name_out,
180             git_repository *repo,
181             const char *tag_name)
182             {
183             git_reference *tag_ref;
184             int error;
185              
186 1           *tag_reference_out = NULL;
187              
188 1 50         if (git_str_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
189 0           return -1;
190              
191 1           error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
192 1 50         if (error < 0)
193 0           return error; /* Be it not foundo or corrupted */
194              
195 1           *tag_reference_out = tag_ref;
196              
197 1           return 0;
198             }
199              
200 1           static int retrieve_tag_reference_oid(
201             git_oid *oid,
202             git_str *ref_name_out,
203             git_repository *repo,
204             const char *tag_name)
205             {
206 1 50         if (git_str_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
207 0           return -1;
208              
209 1           return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
210             }
211              
212 1           static int write_tag_annotation(
213             git_oid *oid,
214             git_repository *repo,
215             const char *tag_name,
216             const git_object *target,
217             const git_signature *tagger,
218             const char *message)
219             {
220 1           git_str tag = GIT_STR_INIT;
221             git_odb *odb;
222              
223 1           git_oid__writebuf(&tag, "object ", git_object_id(target));
224 1           git_str_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
225 1           git_str_printf(&tag, "tag %s\n", tag_name);
226 1           git_signature__writebuf(&tag, "tagger ", tagger);
227 1           git_str_putc(&tag, '\n');
228              
229 1 50         if (git_str_puts(&tag, message) < 0)
230 0           goto on_error;
231              
232 1 50         if (git_repository_odb__weakptr(&odb, repo) < 0)
233 0           goto on_error;
234              
235 1 50         if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJECT_TAG) < 0)
236 0           goto on_error;
237              
238 1           git_str_dispose(&tag);
239 1           return 0;
240              
241             on_error:
242 0           git_str_dispose(&tag);
243 0           git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation");
244 1           return -1;
245             }
246              
247 1           static bool tag_name_is_valid(const char *tag_name)
248             {
249             /*
250             * Discourage tag name starting with dash,
251             * https://github.com/git/git/commit/4f0accd638b8d2
252             */
253 1           return tag_name[0] != '-';
254             }
255              
256 1           static int git_tag_create__internal(
257             git_oid *oid,
258             git_repository *repo,
259             const char *tag_name,
260             const git_object *target,
261             const git_signature *tagger,
262             const char *message,
263             int allow_ref_overwrite,
264             int create_tag_annotation)
265             {
266 1           git_reference *new_ref = NULL;
267 1           git_str ref_name = GIT_STR_INIT;
268              
269             int error;
270              
271 1 50         GIT_ASSERT_ARG(repo);
272 1 50         GIT_ASSERT_ARG(tag_name);
273 1 50         GIT_ASSERT_ARG(target);
274 1 50         GIT_ASSERT_ARG(!create_tag_annotation || (tagger && message));
    50          
    50          
275              
276 1 50         if (git_object_owner(target) != repo) {
277 0           git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
278 0           return -1;
279             }
280              
281 1 50         if (!tag_name_is_valid(tag_name)) {
282 0           git_error_set(GIT_ERROR_TAG, "'%s' is not a valid tag name", tag_name);
283 0           return -1;
284             }
285              
286 1           error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
287 1 50         if (error < 0 && error != GIT_ENOTFOUND)
    50          
288 0           goto cleanup;
289              
290             /** Ensure the tag name doesn't conflict with an already existing
291             * reference unless overwriting has explicitly been requested **/
292 1 50         if (error == 0 && !allow_ref_overwrite) {
    0          
293 0           git_str_dispose(&ref_name);
294 0           git_error_set(GIT_ERROR_TAG, "tag already exists");
295 0           return GIT_EEXISTS;
296             }
297              
298 1 50         if (create_tag_annotation) {
299 1 50         if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
300 0           return -1;
301             } else
302 0           git_oid_cpy(oid, git_object_id(target));
303              
304 1           error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
305              
306             cleanup:
307 1           git_reference_free(new_ref);
308 1           git_str_dispose(&ref_name);
309 1           return error;
310             }
311              
312 1           int git_tag_create(
313             git_oid *oid,
314             git_repository *repo,
315             const char *tag_name,
316             const git_object *target,
317             const git_signature *tagger,
318             const char *message,
319             int allow_ref_overwrite)
320             {
321 1           return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
322             }
323              
324 0           int git_tag_annotation_create(
325             git_oid *oid,
326             git_repository *repo,
327             const char *tag_name,
328             const git_object *target,
329             const git_signature *tagger,
330             const char *message)
331             {
332 0 0         GIT_ASSERT_ARG(oid);
333 0 0         GIT_ASSERT_ARG(repo);
334 0 0         GIT_ASSERT_ARG(tag_name);
335 0 0         GIT_ASSERT_ARG(target);
336 0 0         GIT_ASSERT_ARG(tagger);
337 0 0         GIT_ASSERT_ARG(message);
338              
339 0           return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
340             }
341              
342 0           int git_tag_create_lightweight(
343             git_oid *oid,
344             git_repository *repo,
345             const char *tag_name,
346             const git_object *target,
347             int allow_ref_overwrite)
348             {
349 0           return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
350             }
351              
352 0           int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
353             {
354             git_tag tag;
355             int error;
356             git_odb *odb;
357             git_odb_stream *stream;
358             git_odb_object *target_obj;
359              
360 0           git_reference *new_ref = NULL;
361 0           git_str ref_name = GIT_STR_INIT;
362              
363 0 0         GIT_ASSERT_ARG(oid);
364 0 0         GIT_ASSERT_ARG(buffer);
365              
366 0           memset(&tag, 0, sizeof(tag));
367              
368 0 0         if (git_repository_odb__weakptr(&odb, repo) < 0)
369 0           return -1;
370              
371             /* validate the buffer */
372 0 0         if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
373 0           return -1;
374              
375             /* validate the target */
376 0 0         if (git_odb_read(&target_obj, odb, &tag.target) < 0)
377 0           goto on_error;
378              
379 0 0         if (tag.type != target_obj->cached.type) {
380 0           git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
381 0           goto on_error;
382             }
383              
384 0           error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
385 0 0         if (error < 0 && error != GIT_ENOTFOUND)
    0          
386 0           goto on_error;
387              
388             /* We don't need these objects after this */
389 0           git_signature_free(tag.tagger);
390 0           git__free(tag.tag_name);
391 0           git__free(tag.message);
392 0           git_odb_object_free(target_obj);
393              
394             /** Ensure the tag name doesn't conflict with an already existing
395             * reference unless overwriting has explicitly been requested **/
396 0 0         if (error == 0 && !allow_ref_overwrite) {
    0          
397 0           git_error_set(GIT_ERROR_TAG, "tag already exists");
398 0           return GIT_EEXISTS;
399             }
400              
401             /* write the buffer */
402 0 0         if ((error = git_odb_open_wstream(
403             &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
404 0           return error;
405              
406 0 0         if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
407 0           error = git_odb_stream_finalize_write(oid, stream);
408              
409 0           git_odb_stream_free(stream);
410              
411 0 0         if (error < 0) {
412 0           git_str_dispose(&ref_name);
413 0           return error;
414             }
415              
416 0           error = git_reference_create(
417 0           &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
418              
419 0           git_reference_free(new_ref);
420 0           git_str_dispose(&ref_name);
421              
422 0           return error;
423              
424             on_error:
425 0           git_signature_free(tag.tagger);
426 0           git__free(tag.tag_name);
427 0           git__free(tag.message);
428 0           git_odb_object_free(target_obj);
429 0           return -1;
430             }
431              
432 1           int git_tag_delete(git_repository *repo, const char *tag_name)
433             {
434             git_reference *tag_ref;
435 1           git_str ref_name = GIT_STR_INIT;
436             int error;
437              
438 1           error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
439              
440 1           git_str_dispose(&ref_name);
441              
442 1 50         if (error < 0)
443 0           return error;
444              
445 1           error = git_reference_delete(tag_ref);
446              
447 1           git_reference_free(tag_ref);
448              
449 1           return error;
450             }
451              
452             typedef struct {
453             git_repository *repo;
454             git_tag_foreach_cb cb;
455             void *cb_data;
456             } tag_cb_data;
457              
458 99           static int tags_cb(const char *ref, void *data)
459             {
460             int error;
461             git_oid oid;
462 99           tag_cb_data *d = (tag_cb_data *)data;
463              
464 99 100         if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
465 90           return 0; /* no tag */
466              
467 9 50         if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
468 9 100         if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
469 1           git_error_set_after_callback_function(error, "git_tag_foreach");
470             }
471              
472 99           return error;
473             }
474              
475 16           int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
476             {
477             tag_cb_data data;
478              
479 16 50         GIT_ASSERT_ARG(repo);
480 16 50         GIT_ASSERT_ARG(cb);
481              
482 16           data.cb = cb;
483 16           data.cb_data = cb_data;
484 16           data.repo = repo;
485              
486 16           return git_reference_foreach_name(repo, &tags_cb, &data);
487             }
488              
489             typedef struct {
490             git_vector *taglist;
491             const char *pattern;
492             } tag_filter_data;
493              
494             #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
495              
496 0           static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
497             {
498 0           tag_filter_data *filter = (tag_filter_data *)data;
499 0           GIT_UNUSED(oid);
500              
501 0           if (!*filter->pattern ||
502 0           wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
503             {
504 0           char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
505 0 0         GIT_ERROR_CHECK_ALLOC(matched);
506              
507 0           return git_vector_insert(filter->taglist, matched);
508             }
509              
510 0           return 0;
511             }
512              
513 0           int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
514             {
515             int error;
516             tag_filter_data filter;
517             git_vector taglist;
518              
519 0 0         GIT_ASSERT_ARG(tag_names);
520 0 0         GIT_ASSERT_ARG(repo);
521 0 0         GIT_ASSERT_ARG(pattern);
522              
523 0 0         if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
524 0           return error;
525              
526 0           filter.taglist = &taglist;
527 0           filter.pattern = pattern;
528              
529 0           error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
530              
531 0 0         if (error < 0)
532 0           git_vector_free(&taglist);
533              
534 0           tag_names->strings =
535 0           (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
536              
537 0           return 0;
538             }
539              
540 0           int git_tag_list(git_strarray *tag_names, git_repository *repo)
541             {
542 0           return git_tag_list_match(tag_names, "", repo);
543             }
544              
545 0           int git_tag_peel(git_object **tag_target, const git_tag *tag)
546             {
547 0           return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY);
548             }
549              
550 0           int git_tag_name_is_valid(int *valid, const char *name)
551             {
552 0           git_str ref_name = GIT_STR_INIT;
553 0           int error = 0;
554              
555 0 0         GIT_ASSERT(valid);
556              
557 0           *valid = 0;
558              
559 0 0         if (!name || !tag_name_is_valid(name))
    0          
560             goto done;
561              
562 0 0         if ((error = git_str_puts(&ref_name, GIT_REFS_TAGS_DIR)) < 0 ||
    0          
563             (error = git_str_puts(&ref_name, name)) < 0)
564             goto done;
565              
566 0           error = git_reference_name_is_valid(valid, ref_name.ptr);
567              
568             done:
569 0           git_str_dispose(&ref_name);
570 0           return error;
571             }
572              
573             /* Deprecated Functions */
574              
575             #ifndef GIT_DEPRECATE_HARD
576 0           int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
577             {
578 0           return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite);
579             }
580             #endif