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 "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 517 2 void git_tag__free(void *_tag)
20 - {
21 517 2 git_tag *tag = _tag;
22 517 2 git_signature_free(tag->tagger);
23 517 3 git__free(tag->message);
24 517 4 git__free(tag->tag_name);
25 517 5 git__free(tag);
26 517 6 }
27 -
28 732 2 int git_tag_target(git_object **target, const git_tag *t)
29 - {
30 732 2,3 assert(t);
31 732 4 return git_object_lookup(target, t->object.repo, &t->target, t->type);
32 - }
33 -
34 337 2 const git_oid *git_tag_target_id(const git_tag *t)
35 - {
36 337 2,3 assert(t);
37 337 4 return &t->target;
38 - }
39 -
40 2 2 git_object_t git_tag_target_type(const git_tag *t)
41 - {
42 2 2,3 assert(t);
43 2 4 return t->type;
44 - }
45 -
46 34 2 const char *git_tag_name(const git_tag *t)
47 - {
48 34 2,3 assert(t);
49 34 4 return t->tag_name;
50 - }
51 -
52 1 2 const git_signature *git_tag_tagger(const git_tag *t)
53 - {
54 1 2 return t->tagger;
55 - }
56 -
57 2 2 const char *git_tag_message(const git_tag *t)
58 - {
59 2 2,3 assert(t);
60 2 4 return t->message;
61 - }
62 -
63 6 2 static int tag_error(const char *str)
64 - {
65 6 2 git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str);
66 6 3 return -1;
67 - }
68 -
69 517 2 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 517 2,3 if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
79 2 4 return tag_error("object field invalid");
80 -
81 515 5 if (buffer + 5 >= buffer_end)
82 ##### 6 return tag_error("object too short");
83 -
84 515 7 if (memcmp(buffer, "type ", 5) != 0)
85 1 8 return tag_error("type field not found");
86 514 9 buffer += 5;
87 -
88 514 9 tag->type = GIT_OBJECT_INVALID;
89 -
90 1044 9,14,15 for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
91 1043 10 size_t type_length = strlen(tag_types[i]);
92 -
93 1043 10 if (buffer + type_length >= buffer_end)
94 ##### 11 return tag_error("object too short");
95 -
96 1043 12 if (memcmp(buffer, tag_types[i], type_length) == 0) {
97 513 13 tag->type = i;
98 513 13 buffer += type_length;
99 513 13 break;
100 - }
101 - }
102 -
103 514 16 if (tag->type == GIT_OBJECT_INVALID)
104 1 17 return tag_error("invalid object type");
105 -
106 513 18 if (buffer + 4 >= buffer_end)
107 ##### 19 return tag_error("object too short");
108 -
109 513 20 if (memcmp(buffer, "tag ", 4) != 0)
110 1 21 return tag_error("tag field not found");
111 -
112 512 22 buffer += 4;
113 -
114 512 22 search = memchr(buffer, '\n', buffer_end - buffer);
115 512 22 if (search == NULL)
116 ##### 23 return tag_error("object too short");
117 -
118 512 24 text_len = search - buffer;
119 -
120 512 24-30 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
121 512 31 tag->tag_name = git__malloc(alloc_len);
122 512 32,33 GIT_ERROR_CHECK_ALLOC(tag->tag_name);
123 -
124 512 34 memcpy(tag->tag_name, buffer, text_len);
125 512 34 tag->tag_name[text_len] = '\0';
126 -
127 512 34 buffer = search + 1;
128 -
129 512 34 tag->tagger = NULL;
130 512 34,35 if (buffer < buffer_end && *buffer != '\n') {
131 426 36 tag->tagger = git__malloc(sizeof(git_signature));
132 426 37,38 GIT_ERROR_CHECK_ALLOC(tag->tagger);
133 -
134 426 39,40 if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
135 2 41 return -1;
136 - }
137 -
138 510 42 tag->message = NULL;
139 510 42 if (buffer < buffer_end) {
140 - /* If we're not at the end of the header, search for it */
141 424 43 if(*buffer != '\n') {
142 3 44 search = git__memmem(buffer, buffer_end - buffer,
143 - "\n\n", 2);
144 3 45 if (search)
145 2 46 buffer = search + 1;
146 - else
147 1 47 return tag_error("tag contains no message");
148 - }
149 -
150 423 48 text_len = buffer_end - ++buffer;
151 -
152 423 48-54 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
153 423 55 tag->message = git__malloc(alloc_len);
154 423 56,57 GIT_ERROR_CHECK_ALLOC(tag->message);
155 -
156 423 58 memcpy(tag->message, buffer, text_len);
157 423 58 tag->message[text_len] = '\0';
158 - }
159 -
160 509 59 return 0;
161 - }
162 -
163 12 2 int git_tag__parse_raw(void *_tag, const char *data, size_t size)
164 - {
165 12 2 return tag_parse(_tag, data, data + size);
166 - }
167 -
168 505 2 int git_tag__parse(void *_tag, git_odb_object *odb_obj)
169 - {
170 505 2 git_tag *tag = _tag;
171 505 2 const char *buffer = git_odb_object_data(odb_obj);
172 505 3 const char *buffer_end = buffer + git_odb_object_size(odb_obj);
173 -
174 505 4 return tag_parse(tag, buffer, buffer_end);
175 - }
176 -
177 12 2 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 12 2 *tag_reference_out = NULL;
187 -
188 12 2,3 if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
189 ##### 4 return -1;
190 -
191 12 5 error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
192 12 6 if (error < 0)
193 1 7 return error; /* Be it not foundo or corrupted */
194 -
195 11 8 *tag_reference_out = tag_ref;
196 -
197 11 8 return 0;
198 - }
199 -
200 9 2 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 9 2,3 if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
207 ##### 4 return -1;
208 -
209 9 5 return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
210 - }
211 -
212 6 2 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 6 2 git_buf tag = GIT_BUF_INIT;
221 - git_odb *odb;
222 -
223 6 2,3 git_oid__writebuf(&tag, "object ", git_object_id(target));
224 6 4-6 git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
225 6 7 git_buf_printf(&tag, "tag %s\n", tag_name);
226 6 8 git_signature__writebuf(&tag, "tagger ", tagger);
227 6 9 git_buf_putc(&tag, '\n');
228 -
229 6 10,11 if (git_buf_puts(&tag, message) < 0)
230 ##### 12 goto on_error;
231 -
232 6 13,14 if (git_repository_odb__weakptr(&odb, repo) < 0)
233 ##### 15 goto on_error;
234 -
235 6 16,17 if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJECT_TAG) < 0)
236 ##### 18 goto on_error;
237 -
238 6 19 git_buf_dispose(&tag);
239 6 20 return 0;
240 -
241 - on_error:
242 ##### 21 git_buf_dispose(&tag);
243 ##### 22 git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation");
244 ##### 23 return -1;
245 - }
246 -
247 9 2 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 9 2 git_reference *new_ref = NULL;
258 9 2 git_buf ref_name = GIT_BUF_INIT;
259 -
260 - int error;
261 -
262 9 2-5 assert(repo && tag_name && target);
263 9 6-9 assert(!create_tag_annotation || (tagger && message));
264 -
265 9 10,11 if (git_object_owner(target) != repo) {
266 ##### 12 git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
267 ##### 13 return -1;
268 - }
269 -
270 9 14 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
271 9 15,16 if (error < 0 && error != GIT_ENOTFOUND)
272 2 17 goto cleanup;
273 -
274 - /** Ensure the tag name doesn't conflict with an already existing
275 - * reference unless overwriting has explicitly been requested **/
276 7 18,19 if (error == 0 && !allow_ref_overwrite) {
277 2 20 git_buf_dispose(&ref_name);
278 2 21 git_error_set(GIT_ERROR_TAG, "tag already exists");
279 2 22 return GIT_EEXISTS;
280 - }
281 -
282 5 23 if (create_tag_annotation) {
283 4 24,25 if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
284 ##### 26 return -1;
285 - } else
286 1 27,28 git_oid_cpy(oid, git_object_id(target));
287 -
288 5 29 error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
289 -
290 - cleanup:
291 7 30 git_reference_free(new_ref);
292 7 31 git_buf_dispose(&ref_name);
293 7 32 return error;
294 - }
295 -
296 6 2 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 6 2 return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
306 - }
307 -
308 2 2 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 2 2-8 assert(oid && repo && tag_name && target && tagger && message);
317 -
318 2 9 return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
319 - }
320 -
321 3 2 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 3 2 return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
329 - }
330 -
331 ##### 2 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 ##### 2 git_reference *new_ref = NULL;
340 ##### 2 git_buf ref_name = GIT_BUF_INIT;
341 -
342 ##### 2-4 assert(oid && buffer);
343 -
344 ##### 5 memset(&tag, 0, sizeof(tag));
345 -
346 ##### 5,6 if (git_repository_odb__weakptr(&odb, repo) < 0)
347 ##### 7 return -1;
348 -
349 - /* validate the buffer */
350 ##### 8,9 if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
351 ##### 10 return -1;
352 -
353 - /* validate the target */
354 ##### 11,12 if (git_odb_read(&target_obj, odb, &tag.target) < 0)
355 ##### 13 goto on_error;
356 -
357 ##### 14 if (tag.type != target_obj->cached.type) {
358 ##### 15 git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
359 ##### 42 goto on_error;
360 - }
361 -
362 ##### 16 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
363 ##### 17,18 if (error < 0 && error != GIT_ENOTFOUND)
364 ##### 19 goto on_error;
365 -
366 - /* We don't need these objects after this */
367 ##### 20 git_signature_free(tag.tagger);
368 ##### 21 git__free(tag.tag_name);
369 ##### 22 git__free(tag.message);
370 ##### 23 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 ##### 24,25 if (error == 0 && !allow_ref_overwrite) {
375 ##### 26 git_error_set(GIT_ERROR_TAG, "tag already exists");
376 ##### 27 return GIT_EEXISTS;
377 - }
378 -
379 - /* write the buffer */
380 ##### 28,29 if ((error = git_odb_open_wstream(
381 - &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
382 ##### 30 return error;
383 -
384 ##### 31,32 if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
385 ##### 33 error = git_odb_stream_finalize_write(oid, stream);
386 -
387 ##### 34 git_odb_stream_free(stream);
388 -
389 ##### 35 if (error < 0) {
390 ##### 36 git_buf_dispose(&ref_name);
391 ##### 37 return error;
392 - }
393 -
394 ##### 38 error = git_reference_create(
395 ##### 38 &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
396 -
397 ##### 39 git_reference_free(new_ref);
398 ##### 40 git_buf_dispose(&ref_name);
399 -
400 ##### 41 return error;
401 -
402 - on_error:
403 ##### 43 git_signature_free(tag.tagger);
404 ##### 44 git__free(tag.tag_name);
405 ##### 45 git__free(tag.message);
406 ##### 46 git_odb_object_free(target_obj);
407 ##### 47 return -1;
408 - }
409 -
410 12 2 int git_tag_delete(git_repository *repo, const char *tag_name)
411 - {
412 - git_reference *tag_ref;
413 12 2 git_buf ref_name = GIT_BUF_INIT;
414 - int error;
415 -
416 12 2 error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
417 -
418 12 3 git_buf_dispose(&ref_name);
419 -
420 12 4 if (error < 0)
421 1 5 return error;
422 -
423 11 6 error = git_reference_delete(tag_ref);
424 -
425 11 7 git_reference_free(tag_ref);
426 -
427 11 8 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 1274 2 static int tags_cb(const char *ref, void *data)
437 - {
438 - int error;
439 - git_oid oid;
440 1274 2 tag_cb_data *d = (tag_cb_data *)data;
441 -
442 1274 2,3 if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
443 870 4 return 0; /* no tag */
444 -
445 404 5,6 if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
446 404 7,8 if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
447 ##### 9 git_error_set_after_callback_function(error, "git_tag_foreach");
448 - }
449 -
450 404 10 return error;
451 - }
452 -
453 63 2 int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
454 - {
455 - tag_cb_data data;
456 -
457 63 2-4 assert(repo && cb);
458 -
459 63 5 data.cb = cb;
460 63 5 data.cb_data = cb_data;
461 63 5 data.repo = repo;
462 -
463 63 5 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 54 2 static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
474 - {
475 54 2 tag_filter_data *filter = (tag_filter_data *)data;
476 - GIT_UNUSED(oid);
477 -
478 54 2,4 if (!*filter->pattern ||
479 42 3 wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
480 - {
481 20 5 char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
482 20 6,7 GIT_ERROR_CHECK_ALLOC(matched);
483 -
484 20 8 return git_vector_insert(filter->taglist, matched);
485 - }
486 -
487 34 9 return 0;
488 - }
489 -
490 9 2 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 9 2-5 assert(tag_names && repo && pattern);
497 -
498 9 6,7 if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
499 ##### 8 return error;
500 -
501 9 9 filter.taglist = &taglist;
502 9 9 filter.pattern = pattern;
503 -
504 9 9 error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
505 -
506 9 10 if (error < 0)
507 ##### 11 git_vector_free(&taglist);
508 -
509 9 13 tag_names->strings =
510 9 12 (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
511 -
512 9 13 return 0;
513 - }
514 -
515 1 2 int git_tag_list(git_strarray *tag_names, git_repository *repo)
516 - {
517 1 2 return git_tag_list_match(tag_names, "", repo);
518 - }
519 -
520 455 2 int git_tag_peel(git_object **tag_target, const git_tag *tag)
521 - {
522 455 2 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 ##### 2 int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
529 - {
530 ##### 2 return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite);
531 - }
532 - #endif