]> git.proxmox.com Git - libgit2.git/blob - src/tag.c
037dc6664748458108b0cd82a9c4171f3a988c93
[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 assert(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 assert(t);
37 return &t->target;
38 }
39
40 git_object_t git_tag_target_type(const git_tag *t)
41 {
42 assert(t);
43 return t->type;
44 }
45
46 const char *git_tag_name(const git_tag *t)
47 {
48 assert(t);
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 assert(t);
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 assert(repo && tag_name && target);
263 assert(!create_tag_annotation || (tagger && message));
264
265 if (git_object_owner(target) != repo) {
266 git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
267 return -1;
268 }
269
270 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
271 if (error < 0 && error != GIT_ENOTFOUND)
272 goto cleanup;
273
274 /** Ensure the tag name doesn't conflict with an already existing
275 * reference unless overwriting has explicitly been requested **/
276 if (error == 0 && !allow_ref_overwrite) {
277 git_buf_dispose(&ref_name);
278 git_error_set(GIT_ERROR_TAG, "tag already exists");
279 return GIT_EEXISTS;
280 }
281
282 if (create_tag_annotation) {
283 if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
284 return -1;
285 } else
286 git_oid_cpy(oid, git_object_id(target));
287
288 error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
289
290 cleanup:
291 git_reference_free(new_ref);
292 git_buf_dispose(&ref_name);
293 return error;
294 }
295
296 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 return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
306 }
307
308 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 assert(oid && repo && tag_name && target && tagger && message);
317
318 return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
319 }
320
321 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 return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
329 }
330
331 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 git_reference *new_ref = NULL;
340 git_buf ref_name = GIT_BUF_INIT;
341
342 assert(oid && buffer);
343
344 memset(&tag, 0, sizeof(tag));
345
346 if (git_repository_odb__weakptr(&odb, repo) < 0)
347 return -1;
348
349 /* validate the buffer */
350 if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
351 return -1;
352
353 /* validate the target */
354 if (git_odb_read(&target_obj, odb, &tag.target) < 0)
355 goto on_error;
356
357 if (tag.type != target_obj->cached.type) {
358 git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
359 goto on_error;
360 }
361
362 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
363 if (error < 0 && error != GIT_ENOTFOUND)
364 goto on_error;
365
366 /* We don't need these objects after this */
367 git_signature_free(tag.tagger);
368 git__free(tag.tag_name);
369 git__free(tag.message);
370 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 if (error == 0 && !allow_ref_overwrite) {
375 git_error_set(GIT_ERROR_TAG, "tag already exists");
376 return GIT_EEXISTS;
377 }
378
379 /* write the buffer */
380 if ((error = git_odb_open_wstream(
381 &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
382 return error;
383
384 if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
385 error = git_odb_stream_finalize_write(oid, stream);
386
387 git_odb_stream_free(stream);
388
389 if (error < 0) {
390 git_buf_dispose(&ref_name);
391 return error;
392 }
393
394 error = git_reference_create(
395 &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
396
397 git_reference_free(new_ref);
398 git_buf_dispose(&ref_name);
399
400 return error;
401
402 on_error:
403 git_signature_free(tag.tagger);
404 git__free(tag.tag_name);
405 git__free(tag.message);
406 git_odb_object_free(target_obj);
407 return -1;
408 }
409
410 int git_tag_delete(git_repository *repo, const char *tag_name)
411 {
412 git_reference *tag_ref;
413 git_buf ref_name = GIT_BUF_INIT;
414 int error;
415
416 error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
417
418 git_buf_dispose(&ref_name);
419
420 if (error < 0)
421 return error;
422
423 error = git_reference_delete(tag_ref);
424
425 git_reference_free(tag_ref);
426
427 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 static int tags_cb(const char *ref, void *data)
437 {
438 int error;
439 git_oid oid;
440 tag_cb_data *d = (tag_cb_data *)data;
441
442 if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
443 return 0; /* no tag */
444
445 if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
446 if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
447 git_error_set_after_callback_function(error, "git_tag_foreach");
448 }
449
450 return error;
451 }
452
453 int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
454 {
455 tag_cb_data data;
456
457 assert(repo && cb);
458
459 data.cb = cb;
460 data.cb_data = cb_data;
461 data.repo = repo;
462
463 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 static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
474 {
475 tag_filter_data *filter = (tag_filter_data *)data;
476 GIT_UNUSED(oid);
477
478 if (!*filter->pattern ||
479 wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
480 {
481 char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
482 GIT_ERROR_CHECK_ALLOC(matched);
483
484 return git_vector_insert(filter->taglist, matched);
485 }
486
487 return 0;
488 }
489
490 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 assert(tag_names && repo && pattern);
497
498 if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
499 return error;
500
501 filter.taglist = &taglist;
502 filter.pattern = pattern;
503
504 error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
505
506 if (error < 0)
507 git_vector_free(&taglist);
508
509 tag_names->strings =
510 (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
511
512 return 0;
513 }
514
515 int git_tag_list(git_strarray *tag_names, git_repository *repo)
516 {
517 return git_tag_list_match(tag_names, "", repo);
518 }
519
520 int git_tag_peel(git_object **tag_target, const git_tag *tag)
521 {
522 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 int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
529 {
530 return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite);
531 }
532 #endif