Commit a50c145855598b53463c3715f399cd5ebf27240d

nulltoken 2011-03-30T23:16:30

Add git_tag_create_o_f() and git_tag_create_f() which overwrite existing tag reference, if any

diff --git a/include/git2/tag.h b/include/git2/tag.h
index b83d447..6468cfd 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 9a00694..e75c469 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 a5bdee3..2bea4bc 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