Commit bfbb55628bee994b649117e787bc40749f4c5181

nulltoken 2011-07-11T16:30:46

tag: Add creation of lightweight tag

diff --git a/include/git2/tag.h b/include/git2/tag.h
index 71b27bb..3c32661 100644
--- a/include/git2/tag.h
+++ b/include/git2/tag.h
@@ -149,7 +149,7 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
 
 
 /**
- * Create a new tag in the repository from an OID
+ * Create a new tag in the repository from an object
  *
  * A new reference will also be created pointing to
  * this tag object. If `force` is true and a reference
@@ -157,7 +157,7 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
  *
  * @param oid Pointer where to store the OID of the
  * newly created tag. If the tag already exists, this parameter
- * will be the oid of the existed tag, and the function will
+ * will be the oid of the existing tag, and the function will
  * return a GIT_EEXISTS error code.
  *
  * @param repo Repository where to store the tag
@@ -174,7 +174,7 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
  *
  * @param message Full message for this tag
  *
- * @param force Overwritte existing references
+ * @param force Overwrite existing references
  *
  * @return 0 on success; error code otherwise.
  *	A tag object is written to the ODB, and a proper reference
@@ -205,6 +205,40 @@ GIT_EXTERN(int) git_tag_create_frombuffer(
 		int force);
 
 /**
+ * Create a new lightweight tag pointing at a target object
+ *
+ * A new direct reference will be created pointing to
+ * this target object. If `force` is true and a reference
+ * already exists with the given name, it'll be replaced.
+ *
+ * @param oid Pointer where to store the OID of the provided
+ * target object. If the tag already exists, this parameter
+ * will be filled with the oid of the existing pointed object
+ * and the function will return a GIT_EEXISTS error code.
+ *
+ * @param repo Repository where to store the lightweight tag
+ *
+ * @param tag_name Name for the tag; this name is validated
+ * for consistency. It should also not conflict with an
+ * already existing tag name
+ *
+ * @param target Object to which this tag points. This object
+ * must belong to the given `repo`.
+ *
+ * @param force Overwrite existing references
+ *
+ * @return 0 on success; error code otherwise.
+ *	A proper reference is written in the /refs/tags folder,
+ *  pointing to the provided target object
+ */
+int git_tag_create_lightweight(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		int force);
+
+/**
  * Delete an existing tag reference.
  *
  * @param repo Repository where lives the tag
diff --git a/src/tag.c b/src/tag.c
index e2b0cf4..348ad7c 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -180,21 +180,56 @@ static int retreive_tag_reference(git_reference **tag_reference_out, char *ref_n
 	return GIT_SUCCESS;
 }
 
-int git_tag_create(
+static int write_tag_annotation(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		const git_signature *tagger,
+		const char *message)
+{
+	int error = GIT_SUCCESS;
+	git_buf tag = GIT_BUF_INIT;
+
+	git_oid__writebuf(&tag, "object ", git_object_id(target));
+	git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
+	git_buf_printf(&tag, "tag %s\n", tag_name);
+	git_signature__writebuf(&tag, "tagger ", tagger);
+	git_buf_putc(&tag, '\n');
+	git_buf_puts(&tag, message);
+
+	if (git_buf_oom(&tag)) {
+		git_buf_free(&tag);
+		return git__throw(GIT_ENOMEM, "Not enough memory to build the tag data");
+	}
+
+	error = git_odb_write(oid, git_repository_database(repo), tag.ptr, tag.size, GIT_OBJ_TAG);
+	git_buf_free(&tag);
+
+	if (error < GIT_SUCCESS)
+		return git__rethrow(error, "Failed to create tag annotation");
+
+	return error;
+}
+
+static int git_tag_create__internal(
 		git_oid *oid,
 		git_repository *repo,
 		const char *tag_name,
 		const git_object *target,
 		const git_signature *tagger,
 		const char *message,
-		int allow_ref_overwrite)
+		int allow_ref_overwrite,
+		int create_tag_annotation)
 {
 	git_reference *new_ref = NULL;
 	char ref_name[GIT_REFNAME_MAX];
-	git_buf tag = GIT_BUF_INIT;
 
 	int error, should_update_ref = 0;
 
+	assert(repo && tag_name && target);
+	assert(!create_tag_annotation || (tagger && message));
+
 	if (git_object_owner(target) != repo)
 		return git__throw(GIT_EINVALIDARGS, "The given target does not belong to this repository");
 
@@ -220,23 +255,11 @@ int git_tag_create(
 		}
 	}
 
-	git_oid__writebuf(&tag, "object ", git_object_id(target));
-	git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
-	git_buf_printf(&tag, "tag %s\n", tag_name);
-	git_signature__writebuf(&tag, "tagger ", tagger);
-	git_buf_putc(&tag, '\n');
-	git_buf_puts(&tag, message);
-
-	if (git_buf_oom(&tag)) {
-		git_buf_free(&tag);
-		return git__throw(GIT_ENOMEM, "Not enough memory to build the tag data");
-	}
-
-	error = git_odb_write(oid, git_repository_database(repo), tag.ptr, tag.size, GIT_OBJ_TAG);
-	git_buf_free(&tag);
-
-	if (error < GIT_SUCCESS)
-		return git__rethrow(error, "Failed to create tag");
+	if (create_tag_annotation) {
+		if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS)
+			return error;
+	} else
+		git_oid_cpy(oid, git_object_id(target));
 
 	if (!should_update_ref)
 		error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0);
@@ -246,6 +269,28 @@ int git_tag_create(
 	return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag");
 }
 
+int git_tag_create(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		const git_signature *tagger,
+		const char *message,
+		int allow_ref_overwrite)
+{
+	return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
+}
+
+int git_tag_create_lightweight(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		int allow_ref_overwrite)
+{
+	return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
+}
+
 int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
 {
 	git_tag tag;
diff --git a/tests/t08-tag.c b/tests/t08-tag.c
index aeff8b3..2e33055 100644
--- a/tests/t08-tag.c
+++ b/tests/t08-tag.c
@@ -234,18 +234,72 @@ BEGIN_TEST(write3, "Replace an already existing tag")
 
 END_TEST
 
-BEGIN_TEST(write4, "Delete an already existing tag")
+BEGIN_TEST(write4, "write a lightweight tag to the repository and read it again")
+	git_repository *repo;
+	git_oid target_id, object_id;
+	git_reference *ref_tag;
+	git_object *target;
+
+	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+
+	git_oid_fromstr(&target_id, tagged_commit);
+	must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
+
+	must_pass(git_tag_create_lightweight(
+		&object_id,
+		repo,
+		"light-tag",
+		target,
+		0));
+
+	git_object_close(target);
+
+	must_be_true(git_oid_cmp(&object_id, &target_id) == 0);
+
+	must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/light-tag"));
+	must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &target_id) == 0);
+
+	must_pass(git_tag_delete(repo, "light-tag"));
+
+	git_repository_free(repo);
+END_TEST
+
+BEGIN_TEST(write5, "Attempt to write a lightweight tag bearing the same name than an already existing tag")
+	git_repository *repo;
+	git_oid target_id, object_id, existing_object_id;
+	git_object *target;
+
+	must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
+
+	git_oid_fromstr(&target_id, tagged_commit);
+	must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
+
+	must_fail(git_tag_create_lightweight(
+		&object_id,
+		repo,
+		"e90810b",
+		target,
+		0));
+
+	git_oid_fromstr(&existing_object_id, tag2_id);
+	must_be_true(git_oid_cmp(&object_id, &existing_object_id) == 0);
+
+	git_object_close(target);
+
+	git_repository_free(repo);
+END_TEST
+
+BEGIN_TEST(delete0, "Delete an already existing tag")
 	git_repository *repo;
 	git_reference *ref_tag;
 
 	must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
 
-	must_pass(git_tag_delete(repo,"e90810b"));
+	must_pass(git_tag_delete(repo, "e90810b"));
 
 	must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b"));
 
 	close_temp_repo(repo);
-
 END_TEST
 
 BEGIN_SUITE(tag)
@@ -257,4 +311,8 @@ BEGIN_SUITE(tag)
 	ADD_TEST(write2);
 	ADD_TEST(write3);
 	ADD_TEST(write4);
+	ADD_TEST(write5);
+
+	ADD_TEST(delete0);
+
 END_SUITE