]> git.proxmox.com Git - libgit2.git/blame - src/tag.c
push: remove own copy of callbacks
[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
8#include "common.h"
9#include "commit.h"
10#include "tag.h"
638c2ca4 11#include "signature.h"
458b9450 12#include "message.h"
44908fe7
VM
13#include "git2/object.h"
14#include "git2/repository.h"
638c2ca4 15#include "git2/signature.h"
83cc70d9 16#include "git2/odb_backend.h"
f8758044 17
78606263 18void git_tag__free(void *_tag)
f8758044 19{
78606263 20 git_tag *tag = _tag;
638c2ca4 21 git_signature_free(tag->tagger);
3286c408
VM
22 git__free(tag->message);
23 git__free(tag->tag_name);
24 git__free(tag);
f8758044
VM
25}
26
d9023dbe 27int git_tag_target(git_object **target, const git_tag *t)
f8758044 28{
58519018 29 assert(t);
6b2a1941 30 return git_object_lookup(target, t->object.repo, &t->target, t->type);
f8758044
VM
31}
32
d9023dbe 33const git_oid *git_tag_target_id(const git_tag *t)
ec25391d 34{
6b2a1941
VM
35 assert(t);
36 return &t->target;
37}
38
d9023dbe 39git_otype git_tag_target_type(const git_tag *t)
f8758044 40{
58519018 41 assert(t);
f8758044
VM
42 return t->type;
43}
44
d9023dbe 45const char *git_tag_name(const git_tag *t)
f8758044 46{
58519018 47 assert(t);
f8758044
VM
48 return t->tag_name;
49}
50
d9023dbe 51const git_signature *git_tag_tagger(const git_tag *t)
f8758044 52{
f8758044
VM
53 return t->tagger;
54}
55
d9023dbe 56const char *git_tag_message(const git_tag *t)
f8758044 57{
58519018 58 assert(t);
f8758044
VM
59 return t->message;
60}
61
3aa351ea
CMN
62static int tag_error(const char *str)
63{
64 giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);
65 return -1;
66}
67
3f27127d 68static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
f8758044
VM
69{
70 static const char *tag_types[] = {
71 NULL, "commit\n", "tree\n", "blob\n", "tag\n"
72 };
73
44ef8b1b 74 unsigned int i;
f1453c59 75 size_t text_len, alloc_len;
f8758044
VM
76 char *search;
77
3aa351ea
CMN
78 if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
79 return tag_error("Object field invalid");
f8758044
VM
80
81 if (buffer + 5 >= buffer_end)
3aa351ea 82 return tag_error("Object too short");
f8758044
VM
83
84 if (memcmp(buffer, "type ", 5) != 0)
3aa351ea 85 return tag_error("Type field not found");
f8758044
VM
86 buffer += 5;
87
88 tag->type = GIT_OBJ_BAD;
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)
3aa351ea 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
103 if (tag->type == GIT_OBJ_BAD)
3aa351ea 104 return tag_error("Invalid object type");
f8758044 105
f8758044 106 if (buffer + 4 >= buffer_end)
3aa351ea 107 return tag_error("Object too short");
f8758044
VM
108
109 if (memcmp(buffer, "tag ", 4) != 0)
3aa351ea 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)
3aa351ea 116 return tag_error("Object too short");
f8758044
VM
117
118 text_len = search - buffer;
119
f1453c59
ET
120 GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
121 tag->tag_name = git__malloc(alloc_len);
3aa351ea 122 GITERR_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));
3aa351ea 132 GITERR_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) {
140 if( *buffer != '\n' )
141 return tag_error("No new line before message");
fbfc7580 142
6bb9fea1 143 text_len = buffer_end - ++buffer;
f8758044 144
f1453c59
ET
145 GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
146 tag->message = git__malloc(alloc_len);
6bb9fea1 147 GITERR_CHECK_ALLOC(tag->message);
076141a1 148
6bb9fea1
EZ
149 memcpy(tag->message, buffer, text_len);
150 tag->message[text_len] = '\0';
151 }
f8758044 152
3aa351ea 153 return 0;
f8758044
VM
154}
155
3f27127d
RB
156int git_tag__parse(void *_tag, git_odb_object *odb_obj)
157{
158 git_tag *tag = _tag;
159 const char *buffer = git_odb_object_data(odb_obj);
160 const char *buffer_end = buffer + git_odb_object_size(odb_obj);
161
162 return tag_parse(tag, buffer, buffer_end);
163}
164
97769280
RB
165static int retrieve_tag_reference(
166 git_reference **tag_reference_out,
167 git_buf *ref_name_out,
168 git_repository *repo,
169 const char *tag_name)
ec25391d 170{
9e680bcc 171 git_reference *tag_ref;
172 int error;
173
75abd2b9
MS
174 *tag_reference_out = NULL;
175
3aa351ea
CMN
176 if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
177 return -1;
97769280
RB
178
179 error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
e172cf08 180 if (error < 0)
3aa351ea 181 return error; /* Be it not foundo or corrupted */
9e680bcc 182
3e3e4631 183 *tag_reference_out = tag_ref;
9e680bcc 184
3aa351ea
CMN
185 return 0;
186}
187
188static int retrieve_tag_reference_oid(
189 git_oid *oid,
190 git_buf *ref_name_out,
191 git_repository *repo,
192 const char *tag_name)
193{
194 if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
195 return -1;
196
2508cc66 197 return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
72a3fe42 198}
ec25391d 199
bfbb5562 200static int write_tag_annotation(
201 git_oid *oid,
202 git_repository *repo,
203 const char *tag_name,
204 const git_object *target,
205 const git_signature *tagger,
206 const char *message)
207{
743a4b3b 208 git_buf tag = GIT_BUF_INIT;
9462c471 209 git_odb *odb;
bfbb5562 210
211 git_oid__writebuf(&tag, "object ", git_object_id(target));
212 git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
213 git_buf_printf(&tag, "tag %s\n", tag_name);
214 git_signature__writebuf(&tag, "tagger ", tagger);
215 git_buf_putc(&tag, '\n');
bfbb5562 216
743a4b3b 217 if (git_buf_puts(&tag, message) < 0)
3aa351ea 218 goto on_error;
bfbb5562 219
3aa351ea
CMN
220 if (git_repository_odb__weakptr(&odb, repo) < 0)
221 goto on_error;
9462c471 222
3aa351ea
CMN
223 if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)
224 goto on_error;
bfbb5562 225
3aa351ea
CMN
226 git_buf_free(&tag);
227 return 0;
458b9450 228
3aa351ea
CMN
229on_error:
230 git_buf_free(&tag);
458b9450 231 giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
3aa351ea 232 return -1;
bfbb5562 233}
234
235static int git_tag_create__internal(
72a3fe42
VM
236 git_oid *oid,
237 git_repository *repo,
238 const char *tag_name,
d5afc039 239 const git_object *target,
72a3fe42 240 const git_signature *tagger,
a50c1458 241 const char *message,
bfbb5562 242 int allow_ref_overwrite,
243 int create_tag_annotation)
72a3fe42 244{
d5afc039 245 git_reference *new_ref = NULL;
97769280 246 git_buf ref_name = GIT_BUF_INIT;
ec25391d 247
3aa351ea 248 int error;
72a3fe42 249
bfbb5562 250 assert(repo && tag_name && target);
251 assert(!create_tag_annotation || (tagger && message));
252
3aa351ea
CMN
253 if (git_object_owner(target) != repo) {
254 giterr_set(GITERR_INVALID, "The given target does not belong to this repository");
255 return -1;
256 }
d5afc039 257
3aa351ea 258 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
904b67e6 259 if (error < 0 && error != GIT_ENOTFOUND)
18d6f120 260 goto cleanup;
d5afc039 261
932d1baf 262 /** Ensure the tag name doesn't conflict with an already existing
b874629b 263 * reference unless overwriting has explicitly been requested **/
3aa351ea
CMN
264 if (error == 0 && !allow_ref_overwrite) {
265 git_buf_free(&ref_name);
266 giterr_set(GITERR_TAG, "Tag already exists");
904b67e6 267 return GIT_EEXISTS;
81234673
CMN
268 }
269
bfbb5562 270 if (create_tag_annotation) {
3aa351ea
CMN
271 if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
272 return -1;
bfbb5562 273 } else
274 git_oid_cpy(oid, git_object_id(target));
72a3fe42 275
659cf202 276 error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
932d1baf 277
18d6f120 278cleanup:
75abd2b9 279 git_reference_free(new_ref);
97769280 280 git_buf_free(&ref_name);
97769280 281 return error;
ec25391d
VM
282}
283
bfbb5562 284int git_tag_create(
3f27127d
RB
285 git_oid *oid,
286 git_repository *repo,
287 const char *tag_name,
288 const git_object *target,
289 const git_signature *tagger,
290 const char *message,
291 int allow_ref_overwrite)
bfbb5562 292{
293 return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
294}
295
b81cc1d6 296int git_tag_annotation_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{
304 assert(oid && repo && tag_name && target && tagger && message);
305
306 return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
307}
308
bfbb5562 309int git_tag_create_lightweight(
3f27127d
RB
310 git_oid *oid,
311 git_repository *repo,
312 const char *tag_name,
313 const git_object *target,
314 int allow_ref_overwrite)
bfbb5562 315{
316 return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
317}
318
d5afc039 319int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
7b4a16e2
CMN
320{
321 git_tag tag;
3aa351ea 322 int error;
9462c471 323 git_odb *odb;
ac4fcf17 324 git_odb_stream *stream;
d5afc039 325 git_odb_object *target_obj;
932d1baf 326
75abd2b9 327 git_reference *new_ref = NULL;
97769280 328 git_buf ref_name = GIT_BUF_INIT;
932d1baf 329
7b4a16e2 330 assert(oid && buffer);
932d1baf 331
7b4a16e2 332 memset(&tag, 0, sizeof(tag));
932d1baf 333
3aa351ea
CMN
334 if (git_repository_odb__weakptr(&odb, repo) < 0)
335 return -1;
9462c471 336
ac4fcf17 337 /* validate the buffer */
3f27127d 338 if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
3aa351ea 339 return -1;
d5afc039
VM
340
341 /* validate the target */
3aa351ea
CMN
342 if (git_odb_read(&target_obj, odb, &tag.target) < 0)
343 goto on_error;
d5afc039 344
8842c75f 345 if (tag.type != target_obj->cached.type) {
3aa351ea
CMN
346 giterr_set(GITERR_TAG, "The type for the given target is invalid");
347 goto on_error;
97769280 348 }
d5afc039 349
3aa351ea 350 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
904b67e6 351 if (error < 0 && error != GIT_ENOTFOUND)
3aa351ea
CMN
352 goto on_error;
353
354 /* We don't need these objects after this */
355 git_signature_free(tag.tagger);
356 git__free(tag.tag_name);
357 git__free(tag.message);
358 git_odb_object_free(target_obj);
d5afc039
VM
359
360 /** Ensure the tag name doesn't conflict with an already existing
87d9869f 361 * reference unless overwriting has explictly been requested **/
3aa351ea
CMN
362 if (error == 0 && !allow_ref_overwrite) {
363 giterr_set(GITERR_TAG, "Tag already exists");
904b67e6 364 return GIT_EEXISTS;
ac4fcf17 365 }
932d1baf 366
ac4fcf17 367 /* write the buffer */
a37aa82e
RB
368 if ((error = git_odb_open_wstream(
369 &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
370 return error;
932d1baf 371
a37aa82e
RB
372 if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
373 error = git_odb_stream_finalize_write(oid, stream);
932d1baf 374
376e6c9f 375 git_odb_stream_free(stream);
932d1baf 376
3aa351ea
CMN
377 if (error < 0) {
378 git_buf_free(&ref_name);
a37aa82e 379 return error;
3aa351ea 380 }
d5afc039 381
a37aa82e 382 error = git_reference_create(
659cf202 383 &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
932d1baf 384
75abd2b9 385 git_reference_free(new_ref);
97769280
RB
386 git_buf_free(&ref_name);
387
97769280 388 return error;
3aa351ea
CMN
389
390on_error:
391 git_signature_free(tag.tagger);
392 git__free(tag.tag_name);
393 git__free(tag.message);
394 git_odb_object_free(target_obj);
395 return -1;
7b4a16e2
CMN
396}
397
9e680bcc 398int git_tag_delete(git_repository *repo, const char *tag_name)
399{
9e680bcc 400 git_reference *tag_ref;
97769280 401 git_buf ref_name = GIT_BUF_INIT;
d00d5464 402 int error;
97769280
RB
403
404 error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
405
406 git_buf_free(&ref_name);
9e680bcc 407
3aa351ea 408 if (error < 0)
18d6f120 409 return error;
9e680bcc 410
1ad15540
POL
411 error = git_reference_delete(tag_ref);
412
413 git_reference_free(tag_ref);
9e680bcc 414
78606263 415 return error;
f8758044
VM
416}
417
2b5af615 418typedef struct {
2f05339e
MS
419 git_repository *repo;
420 git_tag_foreach_cb cb;
421 void *cb_data;
422} tag_cb_data;
423
424static int tags_cb(const char *ref, void *data)
425{
dab89f9b 426 int error;
2f05339e
MS
427 git_oid oid;
428 tag_cb_data *d = (tag_cb_data *)data;
429
430 if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
431 return 0; /* no tag */
432
dab89f9b 433 if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
26c1cb91
RB
434 if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
435 giterr_set_after_callback_function(error, "git_tag_foreach");
dab89f9b 436 }
2f05339e 437
25e0b157 438 return error;
2f05339e
MS
439}
440
441int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
442{
443 tag_cb_data data;
444
445 assert(repo && cb);
446
447 data.cb = cb;
448 data.cb_data = cb_data;
449 data.repo = repo;
dab89f9b 450
25e0b157 451 return git_reference_foreach_name(repo, &tags_cb, &data);
2f05339e
MS
452}
453
454typedef struct {
455 git_vector *taglist;
456 const char *pattern;
2b5af615 457} tag_filter_data;
458
932669b8 459#define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
2b5af615 460
2f05339e 461static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
def3fef1 462{
2f05339e
MS
463 tag_filter_data *filter = (tag_filter_data *)data;
464 GIT_UNUSED(oid);
2b5af615 465
dab89f9b
RB
466 if (!*filter->pattern ||
467 p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
468 {
469 char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
25e0b157
RB
470 GITERR_CHECK_ALLOC(matched);
471
dab89f9b
RB
472 return git_vector_insert(filter->taglist, matched);
473 }
def3fef1 474
3aa351ea 475 return 0;
def3fef1
VM
476}
477
2b5af615 478int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
def3fef1
VM
479{
480 int error;
2b5af615 481 tag_filter_data filter;
def3fef1
VM
482 git_vector taglist;
483
2b5af615 484 assert(tag_names && repo && pattern);
485
dab89f9b
RB
486 if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
487 return error;
def3fef1 488
2b5af615 489 filter.taglist = &taglist;
490 filter.pattern = pattern;
491
2f05339e 492 error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
dab89f9b 493
25e0b157 494 if (error < 0)
def3fef1 495 git_vector_free(&taglist);
def3fef1 496
25e0b157
RB
497 tag_names->strings =
498 (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
499
3aa351ea 500 return 0;
def3fef1 501}
2b5af615 502
503int git_tag_list(git_strarray *tag_names, git_repository *repo)
504{
505 return git_tag_list_match(tag_names, "", repo);
d483a911 506}
3f46f313 507
d9023dbe 508int git_tag_peel(git_object **tag_target, const git_tag *tag)
3f46f313 509{
d9023dbe 510 return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJ_ANY);
3f46f313 511}