File Coverage

deps/libgit2/src/tag.c
Criterion Covered Total %
statement 119 240 49.5
branch 51 156 32.6
condition n/a
subroutine n/a
pod n/a
total 170 396 42.9


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 "message.h"
13             #include "wildmatch.h"
14             #include "git2/object.h"
15             #include "git2/repository.h"
16             #include "git2/signature.h"
17             #include "git2/odb_backend.h"
18              
19 0           void git_tag__free(void *_tag)
20             {
21 0           git_tag *tag = _tag;
22 0           git_signature_free(tag->tagger);
23 0           git__free(tag->message);
24 0           git__free(tag->tag_name);
25 0           git__free(tag);
26 0           }
27              
28 1           int git_tag_target(git_object **target, const git_tag *t)
29             {
30 1 50         assert(t);
31 1           return git_object_lookup(target, t->object.repo, &t->target, t->type);
32             }
33              
34 0           const git_oid *git_tag_target_id(const git_tag *t)
35             {
36 0 0         assert(t);
37 0           return &t->target;
38             }
39              
40 0           git_object_t git_tag_target_type(const git_tag *t)
41             {
42 0 0         assert(t);
43 0           return t->type;
44             }
45              
46 3           const char *git_tag_name(const git_tag *t)
47             {
48 3 50         assert(t);
49 3           return t->tag_name;
50             }
51              
52 4           const git_signature *git_tag_tagger(const git_tag *t)
53             {
54 4           return t->tagger;
55             }
56              
57 2           const char *git_tag_message(const git_tag *t)
58             {
59 2 50         assert(t);
60 2           return t->message;
61             }
62              
63 0           static int tag_error(const char *str)
64             {
65 0           git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str);
66 0           return -1;
67             }
68              
69 1           static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
70             {
71             static const char *tag_types[] = {
72             NULL, "commit\n", "tree\n", "blob\n", "tag\n"
73             };
74             size_t text_len, alloc_len;
75             const char *search;
76             unsigned int i;
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 (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
135 0           return -1;
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_buf *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_buf_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_buf *ref_name_out,
203             git_repository *repo,
204             const char *tag_name)
205             {
206 1 50         if (git_buf_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_buf tag = GIT_BUF_INIT;
221             git_odb *odb;
222              
223 1           git_oid__writebuf(&tag, "object ", git_object_id(target));
224 1           git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
225 1           git_buf_printf(&tag, "tag %s\n", tag_name);
226 1           git_signature__writebuf(&tag, "tagger ", tagger);
227 1           git_buf_putc(&tag, '\n');
228              
229 1 50         if (git_buf_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_buf_dispose(&tag);
239 1           return 0;
240              
241             on_error:
242 0           git_buf_dispose(&tag);
243 0           git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation");
244 1           return -1;
245             }
246              
247 1           static int git_tag_create__internal(
248             git_oid *oid,
249             git_repository *repo,
250             const char *tag_name,
251             const git_object *target,
252             const git_signature *tagger,
253             const char *message,
254             int allow_ref_overwrite,
255             int create_tag_annotation)
256             {
257 1           git_reference *new_ref = NULL;
258 1           git_buf ref_name = GIT_BUF_INIT;
259              
260             int error;
261              
262 1 50         assert(repo && tag_name && target);
    50          
    50          
263 1 50         assert(!create_tag_annotation || (tagger && message));
    50          
    50          
264              
265 1 50         if (git_object_owner(target) != repo) {
266 0           git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
267 0           return -1;
268             }
269              
270 1           error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
271 1 50         if (error < 0 && error != GIT_ENOTFOUND)
    50          
272 0           goto cleanup;
273              
274             /** Ensure the tag name doesn't conflict with an already existing
275             * reference unless overwriting has explicitly been requested **/
276 1 50         if (error == 0 && !allow_ref_overwrite) {
    0          
277 0           git_buf_dispose(&ref_name);
278 0           git_error_set(GIT_ERROR_TAG, "tag already exists");
279 0           return GIT_EEXISTS;
280             }
281              
282 1 50         if (create_tag_annotation) {
283 1 50         if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
284 0           return -1;
285             } else
286 0           git_oid_cpy(oid, git_object_id(target));
287              
288 1           error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
289              
290             cleanup:
291 1           git_reference_free(new_ref);
292 1           git_buf_dispose(&ref_name);
293 1           return error;
294             }
295              
296 1           int git_tag_create(
297             git_oid *oid,
298             git_repository *repo,
299             const char *tag_name,
300             const git_object *target,
301             const git_signature *tagger,
302             const char *message,
303             int allow_ref_overwrite)
304             {
305 1           return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
306             }
307              
308 0           int git_tag_annotation_create(
309             git_oid *oid,
310             git_repository *repo,
311             const char *tag_name,
312             const git_object *target,
313             const git_signature *tagger,
314             const char *message)
315             {
316 0 0         assert(oid && repo && tag_name && target && tagger && message);
    0          
    0          
    0          
    0          
    0          
317              
318 0           return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
319             }
320              
321 0           int git_tag_create_lightweight(
322             git_oid *oid,
323             git_repository *repo,
324             const char *tag_name,
325             const git_object *target,
326             int allow_ref_overwrite)
327             {
328 0           return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
329             }
330              
331 0           int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
332             {
333             git_tag tag;
334             int error;
335             git_odb *odb;
336             git_odb_stream *stream;
337             git_odb_object *target_obj;
338              
339 0           git_reference *new_ref = NULL;
340 0           git_buf ref_name = GIT_BUF_INIT;
341              
342 0 0         assert(oid && buffer);
    0          
343              
344 0           memset(&tag, 0, sizeof(tag));
345              
346 0 0         if (git_repository_odb__weakptr(&odb, repo) < 0)
347 0           return -1;
348              
349             /* validate the buffer */
350 0 0         if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
351 0           return -1;
352              
353             /* validate the target */
354 0 0         if (git_odb_read(&target_obj, odb, &tag.target) < 0)
355 0           goto on_error;
356              
357 0 0         if (tag.type != target_obj->cached.type) {
358 0           git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
359 0           goto on_error;
360             }
361              
362 0           error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
363 0 0         if (error < 0 && error != GIT_ENOTFOUND)
    0          
364 0           goto on_error;
365              
366             /* We don't need these objects after this */
367 0           git_signature_free(tag.tagger);
368 0           git__free(tag.tag_name);
369 0           git__free(tag.message);
370 0           git_odb_object_free(target_obj);
371              
372             /** Ensure the tag name doesn't conflict with an already existing
373             * reference unless overwriting has explicitly been requested **/
374 0 0         if (error == 0 && !allow_ref_overwrite) {
    0          
375 0           git_error_set(GIT_ERROR_TAG, "tag already exists");
376 0           return GIT_EEXISTS;
377             }
378              
379             /* write the buffer */
380 0 0         if ((error = git_odb_open_wstream(
381             &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
382 0           return error;
383              
384 0 0         if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
385 0           error = git_odb_stream_finalize_write(oid, stream);
386              
387 0           git_odb_stream_free(stream);
388              
389 0 0         if (error < 0) {
390 0           git_buf_dispose(&ref_name);
391 0           return error;
392             }
393              
394 0           error = git_reference_create(
395 0           &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
396              
397 0           git_reference_free(new_ref);
398 0           git_buf_dispose(&ref_name);
399              
400 0           return error;
401              
402             on_error:
403 0           git_signature_free(tag.tagger);
404 0           git__free(tag.tag_name);
405 0           git__free(tag.message);
406 0           git_odb_object_free(target_obj);
407 0           return -1;
408             }
409              
410 1           int git_tag_delete(git_repository *repo, const char *tag_name)
411             {
412             git_reference *tag_ref;
413 1           git_buf ref_name = GIT_BUF_INIT;
414             int error;
415              
416 1           error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
417              
418 1           git_buf_dispose(&ref_name);
419              
420 1 50         if (error < 0)
421 0           return error;
422              
423 1           error = git_reference_delete(tag_ref);
424              
425 1           git_reference_free(tag_ref);
426              
427 1           return error;
428             }
429              
430             typedef struct {
431             git_repository *repo;
432             git_tag_foreach_cb cb;
433             void *cb_data;
434             } tag_cb_data;
435              
436 113           static int tags_cb(const char *ref, void *data)
437             {
438             int error;
439             git_oid oid;
440 113           tag_cb_data *d = (tag_cb_data *)data;
441              
442 113 100         if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
443 104           return 0; /* no tag */
444              
445 9 50         if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
446 9 100         if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
447 1           git_error_set_after_callback_function(error, "git_tag_foreach");
448             }
449              
450 113           return error;
451             }
452              
453 17           int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
454             {
455             tag_cb_data data;
456              
457 17 50         assert(repo && cb);
    50          
458              
459 17           data.cb = cb;
460 17           data.cb_data = cb_data;
461 17           data.repo = repo;
462              
463 17           return git_reference_foreach_name(repo, &tags_cb, &data);
464             }
465              
466             typedef struct {
467             git_vector *taglist;
468             const char *pattern;
469             } tag_filter_data;
470              
471             #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
472              
473 0           static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
474             {
475 0           tag_filter_data *filter = (tag_filter_data *)data;
476             GIT_UNUSED(oid);
477              
478 0           if (!*filter->pattern ||
479 0           wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
480             {
481 0           char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
482 0 0         GIT_ERROR_CHECK_ALLOC(matched);
483              
484 0           return git_vector_insert(filter->taglist, matched);
485             }
486              
487 0           return 0;
488             }
489              
490 0           int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
491             {
492             int error;
493             tag_filter_data filter;
494             git_vector taglist;
495              
496 0 0         assert(tag_names && repo && pattern);
    0          
    0          
497              
498 0 0         if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
499 0           return error;
500              
501 0           filter.taglist = &taglist;
502 0           filter.pattern = pattern;
503              
504 0           error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
505              
506 0 0         if (error < 0)
507 0           git_vector_free(&taglist);
508              
509 0           tag_names->strings =
510 0           (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
511              
512 0           return 0;
513             }
514              
515 0           int git_tag_list(git_strarray *tag_names, git_repository *repo)
516             {
517 0           return git_tag_list_match(tag_names, "", repo);
518             }
519              
520 0           int git_tag_peel(git_object **tag_target, const git_tag *tag)
521             {
522 0           return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY);
523             }
524              
525             /* Deprecated Functions */
526              
527             #ifndef GIT_DEPRECATE_HARD
528 0           int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
529             {
530 0           return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite);
531             }
532             #endif