diff --git a/include/git2/tag.h b/include/git2/tag.h index b83d44733..6468cfdd7 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -189,6 +189,64 @@ GIT_EXTERN(int) git_tag_create_o( const git_signature *tagger, const char *message); +/** +* Create a new tag in the repository from an OID +* and overwrite an already existing tag reference, if any. +* +* @param oid Pointer where to store the OID of the +* newly created tag +* +* @param repo Repository where to store the tag +* +* @param tag_name Name for the tag; this name is validated +* for consistency. +* +* @param target OID to which this tag points; note that no +* validation is done on this OID. Use the _o_f version of this +* method to assure a proper object is being tagged +* +* @param target_type Type of the tagged OID; note that no +* validation is performed here either +* +* @param tagger Signature of the tagger for this tag, and +* of the tagging time +* +* @param message Full message for this tag +* +* @return 0 on success; error code otherwise. +* A tag object is written to the ODB, and a proper reference +* is written in the /refs/tags folder, pointing to it +*/ +GIT_EXTERN(int) git_tag_create_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message); + +/** + * Create a new tag in the repository from an existing + * `git_object` instance and overwrite an already existing + * tag reference, if any. + * + * This method replaces the `target` and `target_type` + * paremeters of `git_tag_create_f` by a single instance + * of a `const git_object *`, which is assured to be + * a proper object in the ODB and hence will create + * a valid tag + * + * @see git_tag_create_f + */ +GIT_EXTERN(int) git_tag_create_o_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message); + /** @} */ GIT_END_DECL #endif diff --git a/src/tag.c b/src/tag.c index 9a0069448..e75c46916 100644 --- a/src/tag.c +++ b/src/tag.c @@ -153,29 +153,15 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) return GIT_SUCCESS; } -int git_tag_create_o( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_object *target, - const git_signature *tagger, - const char *message) -{ - return git_tag_create( - oid, repo, tag_name, - git_object_id(target), - git_object_type(target), - tagger, message); -} - -int git_tag_create( +static int tag_create( git_oid *oid, git_repository *repo, const char *tag_name, const git_oid *target, git_otype target_type, const git_signature *tagger, - const char *message) + const char *message, + int allow_ref_overwrite) { size_t final_size = 0; git_odb_stream *stream; @@ -187,12 +173,27 @@ int git_tag_create( char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; int type_str_len, tag_name_len, tagger_str_len, message_len; - int error; + int error, should_update_ref = 0; - /** Ensure the tag name doesn't conflict with an already existing reference **/ + /** Ensure the tag name doesn't conflict with an already existing + reference unless overwriting has explictly been requested **/ git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); - if (!git_reference_lookup(&new_ref, repo, ref_name)) - return GIT_EEXISTS; + error = git_reference_lookup(&new_ref, repo, ref_name); + + switch (error) { + case GIT_SUCCESS: + if (!allow_ref_overwrite) + return GIT_EEXISTS; + should_update_ref = 1; + + /* Fall trough */ + + case GIT_ENOTFOUND: + break; + + default: + return error; + } type_str = git_object_type2string(target_type); @@ -234,9 +235,75 @@ int git_tag_create( if (error < GIT_SUCCESS) return error; - return git_reference_create_oid(&new_ref, repo, ref_name, oid); + if (!should_update_ref) + error = git_reference_create_oid(&new_ref, repo, ref_name, oid); + else + error = git_reference_set_oid(new_ref, oid); + + return error; } +int git_tag_create_o( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + git_object_id(target), + git_object_type(target), + tagger, message, 0); +} + +int git_tag_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + target, + target_type, + tagger, message, 0); +} + +int git_tag_create_o_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + git_object_id(target), + git_object_type(target), + tagger, message, 1); +} + +int git_tag_create_f( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message) +{ + return tag_create( + oid, repo, tag_name, + target, + target_type, + tagger, message, 1); +} int git_tag__parse(git_tag *tag, git_odb_object *obj) { diff --git a/tests/t08-tag.c b/tests/t08-tag.c index a5bdee3e0..2bea4bc95 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -189,10 +189,46 @@ BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already END_TEST +BEGIN_TEST(write3, "Replace an already existing tag") + git_repository *repo; + git_oid target_id, tag_id, old_tag_id; + const git_signature *tagger; + git_reference *ref_tag; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + git_oid_mkstr(&target_id, tagged_commit); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + git_oid_cpy(&old_tag_id, git_reference_oid(ref_tag)); + + /* create signature */ + tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); + must_be_true(tagger != NULL); + + must_pass(git_tag_create_f( + &tag_id, /* out id */ + repo, + "very-simple", + &target_id, + GIT_OBJ_COMMIT, + tagger, + TAGGER_MESSAGE)); + + git_signature_free((git_signature *)tagger); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple")); + must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0); + must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &old_tag_id) != 0); + + close_temp_repo(repo); + +END_TEST BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(write0); ADD_TEST(write1); ADD_TEST(write2); + ADD_TEST(write3); END_SUITE