]> git.proxmox.com Git - libgit2.git/blob - src/tag.c
Merge pull request #3303 from libgit2/cmn/index-add-submodule
[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 "common.h"
9 #include "commit.h"
10 #include "tag.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_otype 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 giterr_set(GITERR_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
74 unsigned int i;
75 size_t text_len, alloc_len;
76 char *search;
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_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)
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_OBJ_BAD)
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 GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
121 tag->tag_name = git__malloc(alloc_len);
122 GITERR_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 GITERR_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( *buffer != '\n' )
141 return tag_error("No new line before message");
142
143 text_len = buffer_end - ++buffer;
144
145 GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
146 tag->message = git__malloc(alloc_len);
147 GITERR_CHECK_ALLOC(tag->message);
148
149 memcpy(tag->message, buffer, text_len);
150 tag->message[text_len] = '\0';
151 }
152
153 return 0;
154 }
155
156 int 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
165 static 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)
170 {
171 git_reference *tag_ref;
172 int error;
173
174 *tag_reference_out = NULL;
175
176 if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
177 return -1;
178
179 error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
180 if (error < 0)
181 return error; /* Be it not foundo or corrupted */
182
183 *tag_reference_out = tag_ref;
184
185 return 0;
186 }
187
188 static 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
197 return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
198 }
199
200 static 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 {
208 git_buf tag = GIT_BUF_INIT;
209 git_odb *odb;
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');
216
217 if (git_buf_puts(&tag, message) < 0)
218 goto on_error;
219
220 if (git_repository_odb__weakptr(&odb, repo) < 0)
221 goto on_error;
222
223 if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)
224 goto on_error;
225
226 git_buf_free(&tag);
227 return 0;
228
229 on_error:
230 git_buf_free(&tag);
231 giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
232 return -1;
233 }
234
235 static int git_tag_create__internal(
236 git_oid *oid,
237 git_repository *repo,
238 const char *tag_name,
239 const git_object *target,
240 const git_signature *tagger,
241 const char *message,
242 int allow_ref_overwrite,
243 int create_tag_annotation)
244 {
245 git_reference *new_ref = NULL;
246 git_buf ref_name = GIT_BUF_INIT;
247
248 int error;
249
250 assert(repo && tag_name && target);
251 assert(!create_tag_annotation || (tagger && message));
252
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 }
257
258 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
259 if (error < 0 && error != GIT_ENOTFOUND)
260 goto cleanup;
261
262 /** Ensure the tag name doesn't conflict with an already existing
263 * reference unless overwriting has explicitly been requested **/
264 if (error == 0 && !allow_ref_overwrite) {
265 git_buf_free(&ref_name);
266 giterr_set(GITERR_TAG, "Tag already exists");
267 return GIT_EEXISTS;
268 }
269
270 if (create_tag_annotation) {
271 if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
272 return -1;
273 } else
274 git_oid_cpy(oid, git_object_id(target));
275
276 error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
277
278 cleanup:
279 git_reference_free(new_ref);
280 git_buf_free(&ref_name);
281 return error;
282 }
283
284 int git_tag_create(
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)
292 {
293 return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
294 }
295
296 int 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
309 int git_tag_create_lightweight(
310 git_oid *oid,
311 git_repository *repo,
312 const char *tag_name,
313 const git_object *target,
314 int allow_ref_overwrite)
315 {
316 return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
317 }
318
319 int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
320 {
321 git_tag tag;
322 int error;
323 git_odb *odb;
324 git_odb_stream *stream;
325 git_odb_object *target_obj;
326
327 git_reference *new_ref = NULL;
328 git_buf ref_name = GIT_BUF_INIT;
329
330 assert(oid && buffer);
331
332 memset(&tag, 0, sizeof(tag));
333
334 if (git_repository_odb__weakptr(&odb, repo) < 0)
335 return -1;
336
337 /* validate the buffer */
338 if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
339 return -1;
340
341 /* validate the target */
342 if (git_odb_read(&target_obj, odb, &tag.target) < 0)
343 goto on_error;
344
345 if (tag.type != target_obj->cached.type) {
346 giterr_set(GITERR_TAG, "The type for the given target is invalid");
347 goto on_error;
348 }
349
350 error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
351 if (error < 0 && error != GIT_ENOTFOUND)
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);
359
360 /** Ensure the tag name doesn't conflict with an already existing
361 * reference unless overwriting has explictly been requested **/
362 if (error == 0 && !allow_ref_overwrite) {
363 giterr_set(GITERR_TAG, "Tag already exists");
364 return GIT_EEXISTS;
365 }
366
367 /* write the buffer */
368 if ((error = git_odb_open_wstream(
369 &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
370 return error;
371
372 if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
373 error = git_odb_stream_finalize_write(oid, stream);
374
375 git_odb_stream_free(stream);
376
377 if (error < 0) {
378 git_buf_free(&ref_name);
379 return error;
380 }
381
382 error = git_reference_create(
383 &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
384
385 git_reference_free(new_ref);
386 git_buf_free(&ref_name);
387
388 return error;
389
390 on_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;
396 }
397
398 int git_tag_delete(git_repository *repo, const char *tag_name)
399 {
400 git_reference *tag_ref;
401 git_buf ref_name = GIT_BUF_INIT;
402 int error;
403
404 error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
405
406 git_buf_free(&ref_name);
407
408 if (error < 0)
409 return error;
410
411 error = git_reference_delete(tag_ref);
412
413 git_reference_free(tag_ref);
414
415 return error;
416 }
417
418 typedef struct {
419 git_repository *repo;
420 git_tag_foreach_cb cb;
421 void *cb_data;
422 } tag_cb_data;
423
424 static int tags_cb(const char *ref, void *data)
425 {
426 int error;
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
433 if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
434 if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
435 giterr_set_after_callback_function(error, "git_tag_foreach");
436 }
437
438 return error;
439 }
440
441 int 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;
450
451 return git_reference_foreach_name(repo, &tags_cb, &data);
452 }
453
454 typedef struct {
455 git_vector *taglist;
456 const char *pattern;
457 } tag_filter_data;
458
459 #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
460
461 static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
462 {
463 tag_filter_data *filter = (tag_filter_data *)data;
464 GIT_UNUSED(oid);
465
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);
470 GITERR_CHECK_ALLOC(matched);
471
472 return git_vector_insert(filter->taglist, matched);
473 }
474
475 return 0;
476 }
477
478 int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
479 {
480 int error;
481 tag_filter_data filter;
482 git_vector taglist;
483
484 assert(tag_names && repo && pattern);
485
486 if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
487 return error;
488
489 filter.taglist = &taglist;
490 filter.pattern = pattern;
491
492 error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
493
494 if (error < 0)
495 git_vector_free(&taglist);
496
497 tag_names->strings =
498 (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
499
500 return 0;
501 }
502
503 int git_tag_list(git_strarray *tag_names, git_repository *repo)
504 {
505 return git_tag_list_match(tag_names, "", repo);
506 }
507
508 int git_tag_peel(git_object **tag_target, const git_tag *tag)
509 {
510 return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJ_ANY);
511 }