]> git.proxmox.com Git - libgit2.git/blame - src/tag.c
New upstream version 1.3.0+dfsg.1
[libgit2.git] / src / tag.c
CommitLineData
f8758044 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
f8758044 3 *
bb742ede
VM
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.
f8758044
VM
6 */
7
f8758044 8#include "tag.h"
eae0bfdc
PP
9
10#include "commit.h"
638c2ca4 11#include "signature.h"
458b9450 12#include "message.h"
22a2d3d5 13#include "wildmatch.h"
44908fe7
VM
14#include "git2/object.h"
15#include "git2/repository.h"
638c2ca4 16#include "git2/signature.h"
83cc70d9 17#include "git2/odb_backend.h"
f8758044 18
78606263 19void git_tag__free(void *_tag)
f8758044 20{
78606263 21 git_tag *tag = _tag;
638c2ca4 22 git_signature_free(tag->tagger);
3286c408
VM
23 git__free(tag->message);
24 git__free(tag->tag_name);
25 git__free(tag);
f8758044
VM
26}
27
d9023dbe 28int git_tag_target(git_object **target, const git_tag *t)
f8758044 29{
c25aa7cd 30 GIT_ASSERT_ARG(t);
6b2a1941 31 return git_object_lookup(target, t->object.repo, &t->target, t->type);
f8758044
VM
32}
33
d9023dbe 34const git_oid *git_tag_target_id(const git_tag *t)
ec25391d 35{
c25aa7cd 36 GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
6b2a1941
VM
37 return &t->target;
38}
39
ac3d33df 40git_object_t git_tag_target_type(const git_tag *t)
f8758044 41{
c25aa7cd 42 GIT_ASSERT_ARG_WITH_RETVAL(t, GIT_OBJECT_INVALID);
f8758044
VM
43 return t->type;
44}
45
d9023dbe 46const char *git_tag_name(const git_tag *t)
f8758044 47{
c25aa7cd 48 GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
f8758044
VM
49 return t->tag_name;
50}
51
d9023dbe 52const git_signature *git_tag_tagger(const git_tag *t)
f8758044 53{
f8758044
VM
54 return t->tagger;
55}
56
d9023dbe 57const char *git_tag_message(const git_tag *t)
f8758044 58{
c25aa7cd 59 GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
f8758044
VM
60 return t->message;
61}
62
3aa351ea
CMN
63static int tag_error(const char *str)
64{
ac3d33df 65 git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str);
3aa351ea
CMN
66 return -1;
67}
68
3f27127d 69static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
f8758044
VM
70{
71 static const char *tag_types[] = {
72 NULL, "commit\n", "tree\n", "blob\n", "tag\n"
73 };
f1453c59 74 size_t text_len, alloc_len;
6c7cee42
RD
75 const char *search;
76 unsigned int i;
f8758044 77
3aa351ea 78 if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
909d5494 79 return tag_error("object field invalid");
f8758044
VM
80
81 if (buffer + 5 >= buffer_end)
909d5494 82 return tag_error("object too short");
f8758044
VM
83
84 if (memcmp(buffer, "type ", 5) != 0)
909d5494 85 return tag_error("type field not found");
f8758044
VM
86 buffer += 5;
87
ac3d33df 88 tag->type = GIT_OBJECT_INVALID;
f8758044
VM
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)
909d5494 94 return tag_error("object too short");
f8758044
VM
95
96 if (memcmp(buffer, tag_types[i], type_length) == 0) {
97 tag->type = i;
98 buffer += type_length;
99 break;
100 }
101 }
102
ac3d33df 103 if (tag->type == GIT_OBJECT_INVALID)
909d5494 104 return tag_error("invalid object type");
f8758044 105
f8758044 106 if (buffer + 4 >= buffer_end)
909d5494 107 return tag_error("object too short");
f8758044
VM
108
109 if (memcmp(buffer, "tag ", 4) != 0)
909d5494 110 return tag_error("tag field not found");
44dc0d26 111
f8758044
VM
112 buffer += 4;
113
114 search = memchr(buffer, '\n', buffer_end - buffer);
115 if (search == NULL)
909d5494 116 return tag_error("object too short");
f8758044
VM
117
118 text_len = search - buffer;
119
ac3d33df 120 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
f1453c59 121 tag->tag_name = git__malloc(alloc_len);
ac3d33df 122 GIT_ERROR_CHECK_ALLOC(tag->tag_name);
076141a1 123
f8758044
VM
124 memcpy(tag->tag_name, buffer, text_len);
125 tag->tag_name[text_len] = '\0';
126
127 buffer = search + 1;
128
15b0bed2 129 tag->tagger = NULL;
24cb87e2 130 if (buffer < buffer_end && *buffer != '\n') {
15b0bed2 131 tag->tagger = git__malloc(sizeof(git_signature));
ac3d33df 132 GIT_ERROR_CHECK_ALLOC(tag->tagger);
15b0bed2 133
3aa351ea
CMN
134 if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
135 return -1;
15b0bed2 136 }
f8758044 137
6bb9fea1
EZ
138 tag->message = NULL;
139 if (buffer < buffer_end) {
eb39284b 140 /* If we're not at the end of the header, search for it */
6c7cee42
RD
141 if(*buffer != '\n') {
142 search = git__memmem(buffer, buffer_end - buffer,
143 "\n\n", 2);
eb39284b
CMN
144 if (search)
145 buffer = search + 1;
146 else
147 return tag_error("tag contains no message");
148 }
fbfc7580 149
6bb9fea1 150 text_len = buffer_end - ++buffer;
f8758044 151
ac3d33df 152 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
f1453c59 153 tag->message = git__malloc(alloc_len);
ac3d33df 154 GIT_ERROR_CHECK_ALLOC(tag->message);
076141a1 155
6bb9fea1
EZ
156 memcpy(tag->message, buffer, text_len);
157 tag->message[text_len] = '\0';
158 }
f8758044 159
3aa351ea 160 return 0;
f8758044
VM
161}
162
ac3d33df
JK
163int git_tag__parse_raw(void *_tag, const char *data, size_t size)
164{
165 return tag_parse(_tag, data, data + size);
166}
167
3f27127d
RB
168int 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
97769280
RB
177static 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)
ec25391d 182{
9e680bcc 183 git_reference *tag_ref;
184 int error;
185
75abd2b9
MS
186 *tag_reference_out = NULL;
187
3aa351ea
CMN
188 if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
189 return -1;
97769280
RB
190
191 error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
e172cf08 192 if (error < 0)
3aa351ea 193 return error; /* Be it not foundo or corrupted */
9e680bcc 194
3e3e4631 195 *tag_reference_out = tag_ref;
9e680bcc 196
3aa351ea
CMN
197 return 0;
198}
199
200static 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
2508cc66 209 return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
72a3fe42 210}
ec25391d 211
bfbb5562 212static 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{
743a4b3b 220 git_buf tag = GIT_BUF_INIT;
9462c471 221 git_odb *odb;
bfbb5562 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');
bfbb5562 228
743a4b3b 229 if (git_buf_puts(&tag, message) < 0)
3aa351ea 230 goto on_error;
bfbb5562 231
3aa351ea
CMN
232 if (git_repository_odb__weakptr(&odb, repo) < 0)
233 goto on_error;
9462c471 234
ac3d33df 235 if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJECT_TAG) < 0)
3aa351ea 236 goto on_error;
bfbb5562 237
ac3d33df 238 git_buf_dispose(&tag);
3aa351ea 239 return 0;
458b9450 240
3aa351ea 241on_error:
ac3d33df
JK
242 git_buf_dispose(&tag);
243 git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation");
3aa351ea 244 return -1;
bfbb5562 245}
246
247static int git_tag_create__internal(
72a3fe42
VM
248 git_oid *oid,
249 git_repository *repo,
250 const char *tag_name,
d5afc039 251 const git_object *target,
72a3fe42 252 const git_signature *tagger,
a50c1458 253 const char *message,
bfbb5562 254 int allow_ref_overwrite,
255 int create_tag_annotation)
72a3fe42 256{
d5afc039 257 git_reference *new_ref = NULL;
97769280 258 git_buf ref_name = GIT_BUF_INIT;
ec25391d 259
3aa351ea 260 int error;
72a3fe42 261
c25aa7cd
PP
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));
bfbb5562 266
3aa351ea 267 if (git_object_owner(target) != repo) {
ac3d33df 268 git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
3aa351ea
CMN
269 return -1;
270 }
d5afc039 271
3aa351ea 272 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
904b67e6 273 if (error < 0 && error != GIT_ENOTFOUND)
18d6f120 274 goto cleanup;
d5afc039 275
932d1baf 276 /** Ensure the tag name doesn't conflict with an already existing
b874629b 277 * reference unless overwriting has explicitly been requested **/
3aa351ea 278 if (error == 0 && !allow_ref_overwrite) {
ac3d33df
JK
279 git_buf_dispose(&ref_name);
280 git_error_set(GIT_ERROR_TAG, "tag already exists");
904b67e6 281 return GIT_EEXISTS;
81234673
CMN
282 }
283
bfbb5562 284 if (create_tag_annotation) {
3aa351ea
CMN
285 if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
286 return -1;
bfbb5562 287 } else
288 git_oid_cpy(oid, git_object_id(target));
72a3fe42 289
659cf202 290 error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
932d1baf 291
18d6f120 292cleanup:
75abd2b9 293 git_reference_free(new_ref);
ac3d33df 294 git_buf_dispose(&ref_name);
97769280 295 return error;
ec25391d
VM
296}
297
bfbb5562 298int git_tag_create(
3f27127d
RB
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)
bfbb5562 306{
307 return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
308}
309
b81cc1d6 310int 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{
c25aa7cd
PP
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);
b81cc1d6 324
325 return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
326}
327
bfbb5562 328int git_tag_create_lightweight(
3f27127d
RB
329 git_oid *oid,
330 git_repository *repo,
331 const char *tag_name,
332 const git_object *target,
333 int allow_ref_overwrite)
bfbb5562 334{
335 return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
336}
337
22a2d3d5 338int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
7b4a16e2
CMN
339{
340 git_tag tag;
3aa351ea 341 int error;
9462c471 342 git_odb *odb;
ac4fcf17 343 git_odb_stream *stream;
d5afc039 344 git_odb_object *target_obj;
932d1baf 345
75abd2b9 346 git_reference *new_ref = NULL;
97769280 347 git_buf ref_name = GIT_BUF_INIT;
932d1baf 348
c25aa7cd
PP
349 GIT_ASSERT_ARG(oid);
350 GIT_ASSERT_ARG(buffer);
932d1baf 351
7b4a16e2 352 memset(&tag, 0, sizeof(tag));
932d1baf 353
3aa351ea
CMN
354 if (git_repository_odb__weakptr(&odb, repo) < 0)
355 return -1;
9462c471 356
ac4fcf17 357 /* validate the buffer */
3f27127d 358 if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
3aa351ea 359 return -1;
d5afc039
VM
360
361 /* validate the target */
3aa351ea
CMN
362 if (git_odb_read(&target_obj, odb, &tag.target) < 0)
363 goto on_error;
d5afc039 364
8842c75f 365 if (tag.type != target_obj->cached.type) {
ac3d33df 366 git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
3aa351ea 367 goto on_error;
97769280 368 }
d5afc039 369
3aa351ea 370 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
904b67e6 371 if (error < 0 && error != GIT_ENOTFOUND)
3aa351ea
CMN
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);
d5afc039
VM
379
380 /** Ensure the tag name doesn't conflict with an already existing
91f0d186 381 * reference unless overwriting has explicitly been requested **/
3aa351ea 382 if (error == 0 && !allow_ref_overwrite) {
ac3d33df 383 git_error_set(GIT_ERROR_TAG, "tag already exists");
904b67e6 384 return GIT_EEXISTS;
ac4fcf17 385 }
932d1baf 386
ac4fcf17 387 /* write the buffer */
a37aa82e 388 if ((error = git_odb_open_wstream(
ac3d33df 389 &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0)
a37aa82e 390 return error;
932d1baf 391
a37aa82e
RB
392 if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
393 error = git_odb_stream_finalize_write(oid, stream);
932d1baf 394
376e6c9f 395 git_odb_stream_free(stream);
932d1baf 396
3aa351ea 397 if (error < 0) {
ac3d33df 398 git_buf_dispose(&ref_name);
a37aa82e 399 return error;
3aa351ea 400 }
d5afc039 401
a37aa82e 402 error = git_reference_create(
659cf202 403 &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
932d1baf 404
75abd2b9 405 git_reference_free(new_ref);
ac3d33df 406 git_buf_dispose(&ref_name);
97769280 407
97769280 408 return error;
3aa351ea
CMN
409
410on_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;
7b4a16e2
CMN
416}
417
9e680bcc 418int git_tag_delete(git_repository *repo, const char *tag_name)
419{
9e680bcc 420 git_reference *tag_ref;
97769280 421 git_buf ref_name = GIT_BUF_INIT;
d00d5464 422 int error;
97769280
RB
423
424 error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
425
ac3d33df 426 git_buf_dispose(&ref_name);
9e680bcc 427
3aa351ea 428 if (error < 0)
18d6f120 429 return error;
9e680bcc 430
1ad15540
POL
431 error = git_reference_delete(tag_ref);
432
433 git_reference_free(tag_ref);
9e680bcc 434
78606263 435 return error;
f8758044
VM
436}
437
2b5af615 438typedef struct {
2f05339e
MS
439 git_repository *repo;
440 git_tag_foreach_cb cb;
441 void *cb_data;
442} tag_cb_data;
443
444static int tags_cb(const char *ref, void *data)
445{
dab89f9b 446 int error;
2f05339e
MS
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
dab89f9b 453 if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
26c1cb91 454 if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
ac3d33df 455 git_error_set_after_callback_function(error, "git_tag_foreach");
dab89f9b 456 }
2f05339e 457
25e0b157 458 return error;
2f05339e
MS
459}
460
461int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
462{
463 tag_cb_data data;
464
c25aa7cd
PP
465 GIT_ASSERT_ARG(repo);
466 GIT_ASSERT_ARG(cb);
2f05339e
MS
467
468 data.cb = cb;
469 data.cb_data = cb_data;
470 data.repo = repo;
dab89f9b 471
25e0b157 472 return git_reference_foreach_name(repo, &tags_cb, &data);
2f05339e
MS
473}
474
475typedef struct {
476 git_vector *taglist;
477 const char *pattern;
2b5af615 478} tag_filter_data;
479
932669b8 480#define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
2b5af615 481
2f05339e 482static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
def3fef1 483{
2f05339e
MS
484 tag_filter_data *filter = (tag_filter_data *)data;
485 GIT_UNUSED(oid);
2b5af615 486
dab89f9b 487 if (!*filter->pattern ||
22a2d3d5 488 wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
dab89f9b
RB
489 {
490 char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
ac3d33df 491 GIT_ERROR_CHECK_ALLOC(matched);
25e0b157 492
dab89f9b
RB
493 return git_vector_insert(filter->taglist, matched);
494 }
def3fef1 495
3aa351ea 496 return 0;
def3fef1
VM
497}
498
2b5af615 499int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
def3fef1
VM
500{
501 int error;
2b5af615 502 tag_filter_data filter;
def3fef1
VM
503 git_vector taglist;
504
c25aa7cd
PP
505 GIT_ASSERT_ARG(tag_names);
506 GIT_ASSERT_ARG(repo);
507 GIT_ASSERT_ARG(pattern);
2b5af615 508
dab89f9b
RB
509 if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
510 return error;
def3fef1 511
2b5af615 512 filter.taglist = &taglist;
513 filter.pattern = pattern;
514
2f05339e 515 error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
dab89f9b 516
25e0b157 517 if (error < 0)
def3fef1 518 git_vector_free(&taglist);
def3fef1 519
25e0b157
RB
520 tag_names->strings =
521 (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
522
3aa351ea 523 return 0;
def3fef1 524}
2b5af615 525
526int git_tag_list(git_strarray *tag_names, git_repository *repo)
527{
528 return git_tag_list_match(tag_names, "", repo);
d483a911 529}
3f46f313 530
d9023dbe 531int git_tag_peel(git_object **tag_target, const git_tag *tag)
3f46f313 532{
ac3d33df 533 return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY);
3f46f313 534}
22a2d3d5 535
c25aa7cd
PP
536int 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
556done:
557 git_buf_dispose(&ref_name);
558 return error;
559}
560
22a2d3d5
UG
561/* Deprecated Functions */
562
563#ifndef GIT_DEPRECATE_HARD
564int 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