Commit 3e3e4631 by Vicent Marti

Merge branch 'tagging' of https://github.com/nulltoken/libgit2 into development

Conflicts:
	include/git2/tag.h
	src/tag.c
parents 720d5472 ac26e245
......@@ -140,7 +140,8 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *t);
* @param repo Repository where to store the tag
*
* @param tag_name Name for the tag; this name is validated
* for consistency
* for consistency. It should also not conflict with an
* already existing tag name
*
* @param target OID to which this tag points; note that no
* validation is done on this OID. Use the _o version of this
......@@ -202,6 +203,78 @@ GIT_EXTERN(int) git_tag_create_frombuffer(
git_repository *repo,
const char *buffer);
/**
* 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 _fo 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_fo(
git_oid *oid,
git_repository *repo,
const char *tag_name,
const git_object *target,
const git_signature *tagger,
const char *message);
/**
* Delete an existing tag reference.
*
* @param repo Repository where lives the tag
*
* @param tag_name Name of the tag to be deleted;
* this name is validated for consistency.
*
* @return 0 on success; error code otherwise.
*/
GIT_EXTERN(int) git_tag_delete(
git_repository *repo,
const char *tag_name);
/** @} */
GIT_END_DECL
#endif
......@@ -153,38 +153,61 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
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)
static int retreive_tag_reference(git_reference **tag_reference_out, char *ref_name_out, git_repository *repo, const char *tag_name)
{
return git_tag_create(
oid, repo, tag_name,
git_object_id(target),
git_object_type(target),
tagger, message);
git_reference *tag_ref;
int error;
git__joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
error = git_reference_lookup(&tag_ref, repo, ref_name_out);
if (error < GIT_SUCCESS)
return error;
*tag_reference_out = tag_ref;
return GIT_SUCCESS;
}
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;
const char *type_str;
char *tagger_str;
git_reference *new_ref;
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 unless overwriting has explictly been requested **/
error = retreive_tag_reference(&new_ref, ref_name, repo, tag_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);
......@@ -222,13 +245,14 @@ int git_tag_create(
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error == GIT_SUCCESS) {
char ref_name[512];
git_reference *new_ref;
git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name);
error = git_reference_create_oid(&new_ref, repo, ref_name, oid);
}
if (error < GIT_SUCCESS)
return error;
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;
}
......@@ -261,6 +285,81 @@ cleanup:
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_fo(
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_delete(git_repository *repo, const char *tag_name)
{
int error;
git_reference *tag_ref;
char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH];
error = retreive_tag_reference(&tag_ref, ref_name, repo, tag_name);
if (error < GIT_SUCCESS)
return error;
return git_reference_delete(tag_ref);
}
int git_tag__parse(git_tag *tag, git_odb_object *obj)
{
assert(tag);
......
......@@ -58,6 +58,9 @@ BEGIN_TEST(read0, "read and parse a tag from the repository")
must_be_true(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
git_tag_close(tag1);
git_tag_close(tag2);
git_commit_close(commit);
git_repository_free(repo);
END_TEST
......@@ -72,20 +75,19 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again")
git_oid target_id, tag_id;
const git_signature *tagger;
git_reference *ref_tag;
/* char hex_oid[41]; */
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
git_oid_mkstr(&target_id, tagged_commit);
/* create signatures */
/* create signature */
tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60);
must_be_true(tagger != NULL);
must_pass(git_tag_create(
&tag_id, /* out id */
repo,
"the-tag", /* do not update the HEAD */
"the-tag",
&target_id,
GIT_OBJ_COMMIT,
tagger,
......@@ -94,6 +96,7 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again")
git_signature_free((git_signature *)tagger);
must_pass(git_tag_lookup(&tag, repo, &tag_id));
must_be_true(git_oid_cmp(git_tag_target_oid(tag), &target_id) == 0);
/* Check attributes were set correctly */
tagger = git_tag_tagger(tag);
......@@ -111,12 +114,136 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again")
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag));
git_tag_close(tag);
git_repository_free(repo);
END_TEST
BEGIN_TEST(write1, "write a tag to the repository which points to an unknown oid and read it again")
git_repository *repo;
git_tag *tag;
git_oid target_id, tag_id;
const git_signature *tagger;
git_reference *ref_tag;
git_object *zombie;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
git_oid_mkstr(&target_id, "deadbeef1b46c854b31185ea97743be6a8774479");
/* create signature */
tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60);
must_be_true(tagger != NULL);
must_pass(git_tag_create(
&tag_id, /* out id */
repo,
"the-zombie-tag",
&target_id,
GIT_OBJ_COMMIT,
tagger,
TAGGER_MESSAGE));
git_signature_free((git_signature *)tagger);
must_pass(git_tag_lookup(&tag, repo, &tag_id));
/* The non existent target can not be looked up */
must_fail(git_tag_target(&zombie, tag));
must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/the-zombie-tag"));
must_pass(git_reference_delete(ref_tag));
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag));
git_tag_close(tag);
git_repository_free(repo);
END_TEST
BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already existing tag")
git_repository *repo;
git_oid target_id, tag_id;
const git_signature *tagger;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
git_oid_mkstr(&target_id, tagged_commit);
/* create signature */
tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60);
must_be_true(tagger != NULL);
must_fail(git_tag_create(
&tag_id, /* out id */
repo,
"very-simple",
&target_id,
GIT_OBJ_COMMIT,
tagger,
TAGGER_MESSAGE));
git_signature_free((git_signature *)tagger);
git_repository_free(repo);
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_TEST(write4, "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,"very-simple"));
must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/very-simple"));
close_temp_repo(repo);
END_TEST
BEGIN_SUITE(tag)
ADD_TEST(read0);
ADD_TEST(write0);
ADD_TEST(write0);
ADD_TEST(write1);
ADD_TEST(write2);
ADD_TEST(write3);
ADD_TEST(write4);
END_SUITE
......@@ -227,9 +227,8 @@ BEGIN_TEST(create0, "create a new symbolic reference")
must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
git_reference_delete(looked_up_ref);
git_repository_free(repo);
must_pass(gitfo_unlink(ref_path)); /* TODO: replace with git_reference_delete() when available */
END_TEST
BEGIN_TEST(create1, "create a deep symbolic reference")
......@@ -250,9 +249,8 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
git_reference_delete(looked_up_ref);
git_repository_free(repo);
must_pass(gitfo_unlink(ref_path)); /* TODO: replace with git_reference_delete() when available */
END_TEST
BEGIN_TEST(create2, "create a new OID reference")
......@@ -290,9 +288,28 @@ BEGIN_TEST(create2, "create a new OID reference")
must_pass(git_reference_lookup(&looked_up_ref, repo, new_head));
must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0);
git_reference_delete(looked_up_ref);
git_repository_free(repo);
END_TEST
BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unknown id")
git_reference *new_reference, *looked_up_ref;
git_repository *repo;
git_oid id;
const char *new_head = "refs/heads/new-head";
git_oid_mkstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(gitfo_unlink(ref_path)); /* TODO: replace with git_reference_delete() when available */
/* Create and write the new object id reference */
must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id));
/* Ensure the reference can't be looked-up... */
must_fail(git_reference_lookup(&looked_up_ref, repo, new_head));
git_repository_free(repo);
END_TEST
static const char *ref_name = "refs/heads/other";
......@@ -892,6 +909,7 @@ BEGIN_SUITE(refs)
ADD_TEST(create0);
ADD_TEST(create1);
ADD_TEST(create2);
ADD_TEST(create3);
ADD_TEST(overwrite0);
ADD_TEST(overwrite1);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment