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