]>
git.proxmox.com Git - libgit2.git/blob - src/tag.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
11 #include "signature.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"
19 void git_tag__free(void *_tag
)
22 git_signature_free(tag
->tagger
);
23 git__free(tag
->message
);
24 git__free(tag
->tag_name
);
28 int git_tag_target(git_object
**target
, const git_tag
*t
)
31 return git_object_lookup(target
, t
->object
.repo
, &t
->target
, t
->type
);
34 const git_oid
*git_tag_target_id(const git_tag
*t
)
36 GIT_ASSERT_ARG_WITH_RETVAL(t
, NULL
);
40 git_object_t
git_tag_target_type(const git_tag
*t
)
42 GIT_ASSERT_ARG_WITH_RETVAL(t
, GIT_OBJECT_INVALID
);
46 const char *git_tag_name(const git_tag
*t
)
48 GIT_ASSERT_ARG_WITH_RETVAL(t
, NULL
);
52 const git_signature
*git_tag_tagger(const git_tag
*t
)
57 const char *git_tag_message(const git_tag
*t
)
59 GIT_ASSERT_ARG_WITH_RETVAL(t
, NULL
);
63 static int tag_error(const char *str
)
65 git_error_set(GIT_ERROR_TAG
, "failed to parse tag: %s", str
);
69 static int tag_parse(git_tag
*tag
, const char *buffer
, const char *buffer_end
)
71 static const char *tag_types
[] = {
72 NULL
, "commit\n", "tree\n", "blob\n", "tag\n"
74 size_t text_len
, alloc_len
;
78 if (git_oid__parse(&tag
->target
, &buffer
, buffer_end
, "object ") < 0)
79 return tag_error("object field invalid");
81 if (buffer
+ 5 >= buffer_end
)
82 return tag_error("object too short");
84 if (memcmp(buffer
, "type ", 5) != 0)
85 return tag_error("type field not found");
88 tag
->type
= GIT_OBJECT_INVALID
;
90 for (i
= 1; i
< ARRAY_SIZE(tag_types
); ++i
) {
91 size_t type_length
= strlen(tag_types
[i
]);
93 if (buffer
+ type_length
>= buffer_end
)
94 return tag_error("object too short");
96 if (memcmp(buffer
, tag_types
[i
], type_length
) == 0) {
98 buffer
+= type_length
;
103 if (tag
->type
== GIT_OBJECT_INVALID
)
104 return tag_error("invalid object type");
106 if (buffer
+ 4 >= buffer_end
)
107 return tag_error("object too short");
109 if (memcmp(buffer
, "tag ", 4) != 0)
110 return tag_error("tag field not found");
114 search
= memchr(buffer
, '\n', buffer_end
- buffer
);
116 return tag_error("object too short");
118 text_len
= search
- buffer
;
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
);
124 memcpy(tag
->tag_name
, buffer
, text_len
);
125 tag
->tag_name
[text_len
] = '\0';
130 if (buffer
< buffer_end
&& *buffer
!= '\n') {
131 tag
->tagger
= git__malloc(sizeof(git_signature
));
132 GIT_ERROR_CHECK_ALLOC(tag
->tagger
);
134 if (git_signature__parse(tag
->tagger
, &buffer
, buffer_end
, "tagger ", '\n') < 0)
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
,
147 return tag_error("tag contains no message");
150 text_len
= buffer_end
- ++buffer
;
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
);
156 memcpy(tag
->message
, buffer
, text_len
);
157 tag
->message
[text_len
] = '\0';
163 int git_tag__parse_raw(void *_tag
, const char *data
, size_t size
)
165 return tag_parse(_tag
, data
, data
+ size
);
168 int git_tag__parse(void *_tag
, git_odb_object
*odb_obj
)
171 const char *buffer
= git_odb_object_data(odb_obj
);
172 const char *buffer_end
= buffer
+ git_odb_object_size(odb_obj
);
174 return tag_parse(tag
, buffer
, buffer_end
);
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
)
183 git_reference
*tag_ref
;
186 *tag_reference_out
= NULL
;
188 if (git_buf_joinpath(ref_name_out
, GIT_REFS_TAGS_DIR
, tag_name
) < 0)
191 error
= git_reference_lookup(&tag_ref
, repo
, ref_name_out
->ptr
);
193 return error
; /* Be it not foundo or corrupted */
195 *tag_reference_out
= tag_ref
;
200 static int retrieve_tag_reference_oid(
202 git_buf
*ref_name_out
,
203 git_repository
*repo
,
204 const char *tag_name
)
206 if (git_buf_joinpath(ref_name_out
, GIT_REFS_TAGS_DIR
, tag_name
) < 0)
209 return git_reference_name_to_id(oid
, repo
, ref_name_out
->ptr
);
212 static int write_tag_annotation(
214 git_repository
*repo
,
215 const char *tag_name
,
216 const git_object
*target
,
217 const git_signature
*tagger
,
220 git_buf tag
= GIT_BUF_INIT
;
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');
229 if (git_buf_puts(&tag
, message
) < 0)
232 if (git_repository_odb__weakptr(&odb
, repo
) < 0)
235 if (git_odb_write(oid
, odb
, tag
.ptr
, tag
.size
, GIT_OBJECT_TAG
) < 0)
238 git_buf_dispose(&tag
);
242 git_buf_dispose(&tag
);
243 git_error_set(GIT_ERROR_OBJECT
, "failed to create tag annotation");
247 static int git_tag_create__internal(
249 git_repository
*repo
,
250 const char *tag_name
,
251 const git_object
*target
,
252 const git_signature
*tagger
,
254 int allow_ref_overwrite
,
255 int create_tag_annotation
)
257 git_reference
*new_ref
= NULL
;
258 git_buf ref_name
= GIT_BUF_INIT
;
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
));
267 if (git_object_owner(target
) != repo
) {
268 git_error_set(GIT_ERROR_INVALID
, "the given target does not belong to this repository");
272 error
= retrieve_tag_reference_oid(oid
, &ref_name
, repo
, tag_name
);
273 if (error
< 0 && error
!= GIT_ENOTFOUND
)
276 /** Ensure the tag name doesn't conflict with an already existing
277 * reference unless overwriting has explicitly been requested **/
278 if (error
== 0 && !allow_ref_overwrite
) {
279 git_buf_dispose(&ref_name
);
280 git_error_set(GIT_ERROR_TAG
, "tag already exists");
284 if (create_tag_annotation
) {
285 if (write_tag_annotation(oid
, repo
, tag_name
, target
, tagger
, message
) < 0)
288 git_oid_cpy(oid
, git_object_id(target
));
290 error
= git_reference_create(&new_ref
, repo
, ref_name
.ptr
, oid
, allow_ref_overwrite
, NULL
);
293 git_reference_free(new_ref
);
294 git_buf_dispose(&ref_name
);
300 git_repository
*repo
,
301 const char *tag_name
,
302 const git_object
*target
,
303 const git_signature
*tagger
,
305 int allow_ref_overwrite
)
307 return git_tag_create__internal(oid
, repo
, tag_name
, target
, tagger
, message
, allow_ref_overwrite
, 1);
310 int git_tag_annotation_create(
312 git_repository
*repo
,
313 const char *tag_name
,
314 const git_object
*target
,
315 const git_signature
*tagger
,
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
);
325 return write_tag_annotation(oid
, repo
, tag_name
, target
, tagger
, message
);
328 int git_tag_create_lightweight(
330 git_repository
*repo
,
331 const char *tag_name
,
332 const git_object
*target
,
333 int allow_ref_overwrite
)
335 return git_tag_create__internal(oid
, repo
, tag_name
, target
, NULL
, NULL
, allow_ref_overwrite
, 0);
338 int git_tag_create_from_buffer(git_oid
*oid
, git_repository
*repo
, const char *buffer
, int allow_ref_overwrite
)
343 git_odb_stream
*stream
;
344 git_odb_object
*target_obj
;
346 git_reference
*new_ref
= NULL
;
347 git_buf ref_name
= GIT_BUF_INIT
;
350 GIT_ASSERT_ARG(buffer
);
352 memset(&tag
, 0, sizeof(tag
));
354 if (git_repository_odb__weakptr(&odb
, repo
) < 0)
357 /* validate the buffer */
358 if (tag_parse(&tag
, buffer
, buffer
+ strlen(buffer
)) < 0)
361 /* validate the target */
362 if (git_odb_read(&target_obj
, odb
, &tag
.target
) < 0)
365 if (tag
.type
!= target_obj
->cached
.type
) {
366 git_error_set(GIT_ERROR_TAG
, "the type for the given target is invalid");
370 error
= retrieve_tag_reference_oid(oid
, &ref_name
, repo
, tag
.tag_name
);
371 if (error
< 0 && error
!= GIT_ENOTFOUND
)
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
);
380 /** Ensure the tag name doesn't conflict with an already existing
381 * reference unless overwriting has explicitly been requested **/
382 if (error
== 0 && !allow_ref_overwrite
) {
383 git_error_set(GIT_ERROR_TAG
, "tag already exists");
387 /* write the buffer */
388 if ((error
= git_odb_open_wstream(
389 &stream
, odb
, strlen(buffer
), GIT_OBJECT_TAG
)) < 0)
392 if (!(error
= git_odb_stream_write(stream
, buffer
, strlen(buffer
))))
393 error
= git_odb_stream_finalize_write(oid
, stream
);
395 git_odb_stream_free(stream
);
398 git_buf_dispose(&ref_name
);
402 error
= git_reference_create(
403 &new_ref
, repo
, ref_name
.ptr
, oid
, allow_ref_overwrite
, NULL
);
405 git_reference_free(new_ref
);
406 git_buf_dispose(&ref_name
);
411 git_signature_free(tag
.tagger
);
412 git__free(tag
.tag_name
);
413 git__free(tag
.message
);
414 git_odb_object_free(target_obj
);
418 int git_tag_delete(git_repository
*repo
, const char *tag_name
)
420 git_reference
*tag_ref
;
421 git_buf ref_name
= GIT_BUF_INIT
;
424 error
= retrieve_tag_reference(&tag_ref
, &ref_name
, repo
, tag_name
);
426 git_buf_dispose(&ref_name
);
431 error
= git_reference_delete(tag_ref
);
433 git_reference_free(tag_ref
);
439 git_repository
*repo
;
440 git_tag_foreach_cb cb
;
444 static int tags_cb(const char *ref
, void *data
)
448 tag_cb_data
*d
= (tag_cb_data
*)data
;
450 if (git__prefixcmp(ref
, GIT_REFS_TAGS_DIR
) != 0)
451 return 0; /* no tag */
453 if (!(error
= git_reference_name_to_id(&oid
, d
->repo
, ref
))) {
454 if ((error
= d
->cb(ref
, &oid
, d
->cb_data
)) != 0)
455 git_error_set_after_callback_function(error
, "git_tag_foreach");
461 int git_tag_foreach(git_repository
*repo
, git_tag_foreach_cb cb
, void *cb_data
)
465 GIT_ASSERT_ARG(repo
);
469 data
.cb_data
= cb_data
;
472 return git_reference_foreach_name(repo
, &tags_cb
, &data
);
480 #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
482 static int tag_list_cb(const char *tag_name
, git_oid
*oid
, void *data
)
484 tag_filter_data
*filter
= (tag_filter_data
*)data
;
487 if (!*filter
->pattern
||
488 wildmatch(filter
->pattern
, tag_name
+ GIT_REFS_TAGS_DIR_LEN
, 0) == 0)
490 char *matched
= git__strdup(tag_name
+ GIT_REFS_TAGS_DIR_LEN
);
491 GIT_ERROR_CHECK_ALLOC(matched
);
493 return git_vector_insert(filter
->taglist
, matched
);
499 int git_tag_list_match(git_strarray
*tag_names
, const char *pattern
, git_repository
*repo
)
502 tag_filter_data filter
;
505 GIT_ASSERT_ARG(tag_names
);
506 GIT_ASSERT_ARG(repo
);
507 GIT_ASSERT_ARG(pattern
);
509 if ((error
= git_vector_init(&taglist
, 8, NULL
)) < 0)
512 filter
.taglist
= &taglist
;
513 filter
.pattern
= pattern
;
515 error
= git_tag_foreach(repo
, &tag_list_cb
, (void *)&filter
);
518 git_vector_free(&taglist
);
521 (char **)git_vector_detach(&tag_names
->count
, NULL
, &taglist
);
526 int git_tag_list(git_strarray
*tag_names
, git_repository
*repo
)
528 return git_tag_list_match(tag_names
, "", repo
);
531 int git_tag_peel(git_object
**tag_target
, const git_tag
*tag
)
533 return git_object_peel(tag_target
, (const git_object
*)tag
, GIT_OBJECT_ANY
);
536 int git_tag_name_is_valid(int *valid
, const char *name
)
538 git_buf ref_name
= GIT_BUF_INIT
;
544 * Discourage tag name starting with dash,
545 * https://github.com/git/git/commit/4f0accd638b8d2
547 if (!name
|| name
[0] == '-')
550 if ((error
= git_buf_puts(&ref_name
, GIT_REFS_TAGS_DIR
)) < 0 ||
551 (error
= git_buf_puts(&ref_name
, name
)) < 0)
554 error
= git_reference_name_is_valid(valid
, ref_name
.ptr
);
557 git_buf_dispose(&ref_name
);
561 /* Deprecated Functions */
563 #ifndef GIT_DEPRECATE_HARD
564 int git_tag_create_frombuffer(git_oid
*oid
, git_repository
*repo
, const char *buffer
, int allow_ref_overwrite
)
566 return git_tag_create_from_buffer(oid
, repo
, buffer
, allow_ref_overwrite
);