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