diff --git a/include/git2/commit.h b/include/git2/commit.h index 356b875cd..84adef596 100644 --- a/include/git2/commit.h +++ b/include/git2/commit.h @@ -193,7 +193,8 @@ GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsig GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned int n); /** - * Create a new commit in the repository + * Create a new commit in the repository using `git_object` + * instances as parameters. * * @param oid Pointer where to store the OID of the * newly created commit @@ -214,44 +215,22 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i * * @param message Full message for this commit * - * @param tree_oid Object ID of the tree for this commit. Note that - * no validation is performed on this OID. Use the _o variants of - * this method to assure a proper tree is passed to the commit. + * @param tree An instance of a `git_tree` object that will + * be used as the tree for the commit. This tree object must + * also be owned by the given `repo`. * * @param parent_count Number of parents for this commit * - * @param parent_oids[] Array of pointers to parent OIDs for this commit. - * Note that no validation is performed on these OIDs. Use the _o - * variants of this method to assure that are parents for the commit - * are proper objects. + * @param parents[] Array of `parent_count` pointers to `git_commit` + * objects that will be used as the parents for this commit. This + * array may be NULL if `parent_count` is 0 (root commit). All the + * given commits must be owned by the `repo`. * * @return 0 on success; error code otherwise * The created commit will be written to the Object Database and * the given reference will be updated to point to it */ GIT_EXTERN(int) git_commit_create( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message, - const git_oid *tree_oid, - int parent_count, - const git_oid *parent_oids[]); - -/** - * Create a new commit in the repository using `git_object` - * instances as parameters. - * - * The `tree_oid` and `parent_oids` paremeters now take a instance - * of `git_tree` and `git_commit`, respectively. - * - * All other parameters remain the same - * - * @see git_commit_create - */ -GIT_EXTERN(int) git_commit_create_o( git_oid *oid, git_repository *repo, const char *update_ref, @@ -263,11 +242,8 @@ GIT_EXTERN(int) git_commit_create_o( const git_commit *parents[]); /** - * Create a new commit in the repository using `git_object` - * instances and a variable argument list. - * - * The `tree_oid` paremeter now takes a instance - * of `const git_tree *`. + * Create a new commit in the repository using a variable + * argument list. * * The parents for the commit are specified as a variable * list of pointers to `const git_commit *`. Note that this @@ -278,31 +254,6 @@ GIT_EXTERN(int) git_commit_create_o( * * @see git_commit_create */ -GIT_EXTERN(int) git_commit_create_ov( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message, - const git_tree *tree, - int parent_count, - ...); - - -/** - * Create a new commit in the repository using - * a variable argument list. - * - * The parents for the commit are specified as a variable - * list of pointers to `const git_oid *`. Note that this - * is a convenience method which may not be safe to export - * for certain languages or compilers - * - * All other parameters remain the same - * - * @see git_commit_create - */ GIT_EXTERN(int) git_commit_create_v( git_oid *oid, git_repository *repo, @@ -310,7 +261,7 @@ GIT_EXTERN(int) git_commit_create_v( const git_signature *author, const git_signature *committer, const char *message, - const git_oid *tree_oid, + const git_tree *tree, int parent_count, ...); diff --git a/include/git2/oid.h b/include/git2/oid.h index 06bbfcc55..46d0dce0d 100644 --- a/include/git2/oid.h +++ b/include/git2/oid.h @@ -64,6 +64,19 @@ typedef struct { */ GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str); +/** + * Parse N characters of a hex formatted object id into a git_oid + * + * If N is odd, N-1 characters will be parsed instead. + * The remaining space in the git_oid will be set to zero. + * + * @param out oid structure the result is written into. + * @param str input hex string of at least size `length` + * @param length length of the input string + * @return GIT_SUCCESS if valid; GIT_ENOTOID on failure. + */ +GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length); + /** * Copy an already raw oid into a git_oid structure. * diff --git a/include/git2/refs.h b/include/git2/refs.h index 2c3aac7b0..384e8e2f8 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -60,34 +60,17 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito * This reference is owned by the repository and shall not * be free'd by the user. * - * @param ref_out Pointer to the newly created reference - * @param repo Repository where that reference will live - * @param name The name of the reference - * @param target The target of the reference - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target); - -/** - * Create a new symbolic reference, overwriting an existing one with - * the same name, if it exists. - * - * If the new reference isn't a symbolic one, any pointers to the old - * reference become invalid. - * - * The reference will be created in the repository and written - * to the disk. - * - * This reference is owned by the repository and shall not - * be free'd by the user. + * If `force` is true and there already exists a reference + * with the same name, it will be overwritten. * * @param ref_out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param target The target of the reference + * @param force Overwrite existing references * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_reference_create_symbolic_f(git_reference **ref_out, git_repository *repo, const char *name, const char *target); +GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force); /** * Create a new object id reference. @@ -98,34 +81,17 @@ GIT_EXTERN(int) git_reference_create_symbolic_f(git_reference **ref_out, git_rep * This reference is owned by the repository and shall not * be free'd by the user. * - * @param ref_out Pointer to the newly created reference - * @param repo Repository where that reference will live - * @param name The name of the reference - * @param id The object id pointed to by the reference. - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id); - -/** - * Create a new object id reference, overwriting an existing one with - * the same name, if it exists. - * - * If the new reference isn't an object id one, any pointers to the - * old reference become invalid. - * - * The reference will be created in the repository and written - * to the disk. - * - * This reference is owned by the repository and shall not - * be free'd by the user. + * If `force` is true and there already exists a reference + * with the same name, it will be overwritten. * * @param ref_out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param id The object id pointed to by the reference. + * @param force Overwrite existing references * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_reference_create_oid_f(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id); +GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force); /** * Get the OID pointed to by a reference. diff --git a/include/git2/tag.h b/include/git2/tag.h index b92f79d22..11eac90e3 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -151,6 +151,10 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag); /** * Create a new tag in the repository from an OID * + * A new reference will also be created pointing to + * this tag 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 * newly created tag. If the tag already exists, this parameter * will be the oid of the existed tag, and the function will @@ -162,123 +166,43 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag); * 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 - * 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 target Object to which this tag points. This object + * must belong to the given `repo`. * * @param tagger Signature of the tagger for this tag, and * of the tagging time * * @param message Full message for this tag * + * @param force Overwritte existing references + * * @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( - 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 - * - * This method replaces the `target` and `target_type` - * paremeters of `git_tag_create` 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 - */ -GIT_EXTERN(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); + const char *message, + int force); /** * Create a new tag in the repository from a buffer * * @param oid Pointer where to store the OID of the newly created tag - * * @param repo Repository where to store the tag - * * @param buffer Raw tag data + * @param force Overwrite existing tags + * @return 0 on sucess; error code otherwise */ GIT_EXTERN(int) git_tag_create_frombuffer( git_oid *oid, 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); + const char *buffer, + int force); /** * Delete an existing tag reference. diff --git a/src/commit.c b/src/commit.c index 6857eddab..c94ea7618 100644 --- a/src/commit.c +++ b/src/commit.c @@ -74,39 +74,7 @@ const git_oid *git_commit_id(git_commit *c) return git_object_id((git_object *)c); } - int git_commit_create_v( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message, - const git_oid *tree_oid, - int parent_count, - ...) -{ - va_list ap; - int i, error; - const git_oid **oids; - - oids = git__malloc(parent_count * sizeof(git_oid *)); - - va_start(ap, parent_count); - for (i = 0; i < parent_count; ++i) - oids[i] = va_arg(ap, const git_oid *); - va_end(ap); - - error = git_commit_create( - oid, repo, update_ref, author, committer, message, - tree_oid, parent_count, oids); - - free((void *)oids); - - return error; -} - -int git_commit_create_ov( git_oid *oid, git_repository *repo, const char *update_ref, @@ -119,50 +87,20 @@ int git_commit_create_ov( { va_list ap; int i, error; - const git_oid **oids; + const git_commit **parents; - oids = git__malloc(parent_count * sizeof(git_oid *)); + parents = git__malloc(parent_count * sizeof(git_commit *)); va_start(ap, parent_count); for (i = 0; i < parent_count; ++i) - oids[i] = git_object_id(va_arg(ap, const git_object *)); + parents[i] = va_arg(ap, const git_commit *); va_end(ap); error = git_commit_create( oid, repo, update_ref, author, committer, message, - git_object_id((git_object *)tree), - parent_count, oids); + tree, parent_count, parents); - free((void *)oids); - - return error; -} - -int git_commit_create_o( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message, - const git_tree *tree, - int parent_count, - const git_commit *parents[]) -{ - int i, error; - const git_oid **oids; - - oids = git__malloc(parent_count * sizeof(git_oid *)); - - for (i = 0; i < parent_count; ++i) - oids[i] = git_object_id((git_object *)parents[i]); - - error = git_commit_create( - oid, repo, update_ref, author, committer, message, - git_object_id((git_object *)tree), - parent_count, oids); - - free((void *)oids); + free((void *)parents); return error; } @@ -174,9 +112,9 @@ int git_commit_create( const git_signature *author, const git_signature *committer, const char *message, - const git_oid *tree_oid, + const git_tree *tree, int parent_count, - const git_oid *parents[]) + const git_commit *parents[]) { size_t final_size = 0; int message_length, author_length, committer_length; @@ -202,10 +140,17 @@ int git_commit_create( if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_COMMIT)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create commit"); - git__write_oid(stream, "tree", tree_oid); + if (git_object_owner((const git_object *)tree) != repo) + return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository"); - for (i = 0; i < parent_count; ++i) - git__write_oid(stream, "parent", parents[i]); + git__write_oid(stream, "tree", git_object_id((const git_object *)tree)); + + for (i = 0; i < parent_count; ++i) { + if (git_object_owner((const git_object *)parents[i]) != repo) + return git__throw(GIT_EINVALIDARGS, "The given parent does not belong to this repository"); + + git__write_oid(stream, "parent", git_object_id((const git_object *)parents[i])); + } stream->write(stream, author_str, author_length); free(author_str); @@ -213,7 +158,6 @@ int git_commit_create( stream->write(stream, committer_str, committer_length); free(committer_str); - stream->write(stream, "\n", 1); stream->write(stream, message, message_length); @@ -238,7 +182,7 @@ int git_commit_create( * point to) or after an orphan checkout, so if the target * branch doesn't exist yet, create it and return. */ - return git_reference_create_oid_f(&head, repo, git_reference_target(head), oid); + return git_reference_create_oid(&head, repo, git_reference_target(head), oid, 1); } error = git_reference_set_oid(head, oid); diff --git a/src/oid.c b/src/oid.c index 3e3237f9c..70dd7c597 100644 --- a/src/oid.c +++ b/src/oid.c @@ -49,19 +49,37 @@ static signed char from_hex[] = { }; static char to_hex[] = "0123456789abcdef"; -int git_oid_fromstr(git_oid *out, const char *str) +int git_oid_fromstrn(git_oid *out, const char *str, size_t length) { size_t p; - for (p = 0; p < sizeof(out->id); p++, str += 2) { - int v = (from_hex[(unsigned char)str[0]] << 4) - | from_hex[(unsigned char)str[1]]; + + if (length > GIT_OID_HEXSZ) + length = GIT_OID_HEXSZ; + + if (length % 2) + length--; + + for (p = 0; p < length; p += 2) { + int v = (from_hex[(unsigned char)str[p + 0]] << 4) + | from_hex[(unsigned char)str[p + 1]]; + if (v < 0) return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is not a valid sha1 hash"); - out->id[p] = (unsigned char)v; + + out->id[p / 2] = (unsigned char)v; } + + for (; p < GIT_OID_HEXSZ; p += 2) + out->id[p / 2] = 0x0; + return GIT_SUCCESS; } +int git_oid_fromstr(git_oid *out, const char *str) +{ + return git_oid_fromstrn(out, str, GIT_OID_HEXSZ); +} + GIT_INLINE(char) *fmt_one(char *str, unsigned int val) { *str++ = to_hex[val >> 4]; diff --git a/src/refs.c b/src/refs.c index ac13736eb..d4a820b9b 100644 --- a/src/refs.c +++ b/src/refs.c @@ -920,117 +920,6 @@ cleanup: return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference"); } -/***************************************** - * Internal methods - reference creation - *****************************************/ - -static int reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force) -{ - char normalized[GIT_REFNAME_MAX]; - int error = GIT_SUCCESS, updated = 0; - git_reference *ref = NULL, *old_ref = NULL; - - if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) - return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists"); - - /* - * If they old ref was of the same type, then we can just update - * it (once we've checked that the target is valid). Otherwise we - * need a new reference because we can't make a symbolic ref out - * of an oid one. - * If if didn't exist, then we need to create a new one anyway. - */ - if (ref && ref->type & GIT_REF_SYMBOLIC){ - updated = 1; - } else { - ref = NULL; - error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); - if (error < GIT_SUCCESS) - goto cleanup; - } - - /* The target can aither be the name of an object id reference or the name of another symbolic reference */ - error = normalize_name(normalized, sizeof(normalized), target, 0); - if (error < GIT_SUCCESS) - goto cleanup; - - /* set the target; this will write the reference on disk */ - error = git_reference_set_target(ref, normalized); - if (error < GIT_SUCCESS) - goto cleanup; - - /* - * If we didn't update the ref, then we need to insert or replace - * it in the loose cache. If we replaced a ref, free it. - */ - if (!updated){ - error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); - if (error < GIT_SUCCESS) - goto cleanup; - - if(old_ref) - reference_free(old_ref); - } - - *ref_out = ref; - - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); - -cleanup: - reference_free(ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); -} - -static int reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force) -{ - int error = GIT_SUCCESS, updated = 0; - git_reference *ref = NULL, *old_ref = NULL; - - if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) - return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists"); - - if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to create reference"); - - /* - * If they old ref was of the same type, then we can just update - * it (once we've checked that the target is valid). Otherwise we - * need a new reference because we can't make a symbolic ref out - * of an oid one. - * If if didn't exist, then we need to create a new one anyway. - */ - if (ref && ref-> type & GIT_REF_OID){ - updated = 1; - } else { - ref = NULL; - error = reference_create(&ref, repo, name, GIT_REF_OID); - if (error < GIT_SUCCESS) - goto cleanup; - } - - /* set the oid; this will write the reference on disk */ - error = git_reference_set_oid(ref, id); - if (error < GIT_SUCCESS) - goto cleanup; - - if(!updated){ - error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); - if (error < GIT_SUCCESS) - goto cleanup; - - if(old_ref) - reference_free(old_ref); - } - - *ref_out = ref; - - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); - -cleanup: - reference_free(ref); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); -} - static int _reference_available_cb(const char *ref, void *data) { const char *new, *old; @@ -1261,26 +1150,6 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch return git__throw(GIT_ENOTFOUND, "Failed to lookup reference. Reference doesn't exist"); } -int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target) -{ - return reference_create_symbolic(ref_out, repo, name, target, 0); -} - -int git_reference_create_symbolic_f(git_reference **ref_out, git_repository *repo, const char *name, const char *target) -{ - return reference_create_symbolic(ref_out, repo, name, target, 1); -} - -int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id) -{ - return reference_create_oid(ref_out, repo, name, id, 0); -} - -int git_reference_create_oid_f(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id) -{ - return reference_create_oid(ref_out, repo, name, id, 1); -} - /** * Getters */ @@ -1335,6 +1204,113 @@ const char *git_reference_target(git_reference *ref) return ((reference_symbolic *)ref)->target; } +int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force) +{ + char normalized[GIT_REFNAME_MAX]; + int error = GIT_SUCCESS, updated = 0; + git_reference *ref = NULL, *old_ref = NULL; + + if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) + return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists"); + + /* + * If they old ref was of the same type, then we can just update + * it (once we've checked that the target is valid). Otherwise we + * need a new reference because we can't make a symbolic ref out + * of an oid one. + * If if didn't exist, then we need to create a new one anyway. + */ + if (ref && ref->type & GIT_REF_SYMBOLIC){ + updated = 1; + } else { + ref = NULL; + error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* The target can aither be the name of an object id reference or the name of another symbolic reference */ + error = normalize_name(normalized, sizeof(normalized), target, 0); + if (error < GIT_SUCCESS) + goto cleanup; + + /* set the target; this will write the reference on disk */ + error = git_reference_set_target(ref, normalized); + if (error < GIT_SUCCESS) + goto cleanup; + + /* + * If we didn't update the ref, then we need to insert or replace + * it in the loose cache. If we replaced a ref, free it. + */ + if (!updated){ + error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); + if (error < GIT_SUCCESS) + goto cleanup; + + if(old_ref) + reference_free(old_ref); + } + + *ref_out = ref; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); + +cleanup: + reference_free(ref); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); +} + +int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force) +{ + int error = GIT_SUCCESS, updated = 0; + git_reference *ref = NULL, *old_ref = NULL; + + if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) + return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists"); + + if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to create reference"); + + /* + * If they old ref was of the same type, then we can just update + * it (once we've checked that the target is valid). Otherwise we + * need a new reference because we can't make a symbolic ref out + * of an oid one. + * If if didn't exist, then we need to create a new one anyway. + */ + if (ref && ref-> type & GIT_REF_OID){ + updated = 1; + } else { + ref = NULL; + error = reference_create(&ref, repo, name, GIT_REF_OID); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* set the oid; this will write the reference on disk */ + error = git_reference_set_oid(ref, id); + if (error < GIT_SUCCESS) + goto cleanup; + + if(!updated){ + error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); + if (error < GIT_SUCCESS) + goto cleanup; + + if(old_ref) + reference_free(old_ref); + } + + *ref_out = ref; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); + +cleanup: + reference_free(ref); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); +} + /** * Setters */ diff --git a/src/repository.c b/src/repository.c index 1fef73907..7e3f26e1b 100644 --- a/src/repository.c +++ b/src/repository.c @@ -626,7 +626,7 @@ static int repo_init_reinit(repo_init *results) static int repo_init_createhead(git_repository *repo) { git_reference *head_reference; - return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE); /* TODO: finalize moving refs.c to new error handling */ + return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0); } static int repo_init_check_head_existence(char * repository_path) diff --git a/src/tag.c b/src/tag.c index c3924a1f8..45450b933 100644 --- a/src/tag.c +++ b/src/tag.c @@ -180,52 +180,11 @@ static int retreive_tag_reference(git_reference **tag_reference_out, char *ref_n return GIT_SUCCESS; } -/* tag_reference_out will contain the reference of the tag if exists, otherwise NULL */ -static int tag_valid_in_odb( - git_reference **tag_reference_out, - char *ref_name_out, - const git_oid *target, - git_otype target_type, - git_repository *repo, - const char *tag_name) { - - int error; - - *tag_reference_out = NULL; - - - error = retreive_tag_reference(tag_reference_out, ref_name_out, repo, tag_name); - - switch (error) { - case GIT_SUCCESS: - /* Fall trough */ - case GIT_ENOTFOUND: - break; - - default: - return git__rethrow(error, "Failed to create tag"); - } - - if (!git_odb_exists(repo->db, target)) - return git__throw(GIT_ENOTFOUND, "Failed to create tag. Object to tag doesn't exist"); - - /* Try to find out what the type is */ - if (target_type == GIT_OBJ_ANY) { - size_t _unused; - error = git_odb_read_header(&_unused, &target_type, repo->db, target); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to create tag"); - } - - return GIT_SUCCESS; -} - -static int tag_create( +int git_tag_create( git_oid *oid, git_repository *repo, const char *tag_name, - const git_oid *target, - git_otype target_type, + const git_object *target, const git_signature *tagger, const char *message, int allow_ref_overwrite) @@ -235,20 +194,31 @@ static int tag_create( const char *type_str; char *tagger_str; - git_reference *new_ref; + git_reference *new_ref = NULL; char ref_name[GIT_REFNAME_MAX]; int type_str_len, tag_name_len, tagger_str_len, message_len; int error, should_update_ref = 0; - if ((error = tag_valid_in_odb(&new_ref, ref_name, target, target_type, repo, tag_name)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to create tag"); + if (git_object_owner(target) != repo) + return git__throw(GIT_EINVALIDARGS, "The given target does not belong to this repository"); + + error = retreive_tag_reference(&new_ref, ref_name, repo, tag_name); + switch (error) { + case GIT_SUCCESS: + case GIT_ENOTFOUND: + break; + + default: + return git__rethrow(error, "Failed to create tag"); + } + /** Ensure the tag name doesn't conflict with an already existing * reference unless overwriting has explictly been requested **/ - if(new_ref != NULL) { - if(!allow_ref_overwrite) { + if (new_ref != NULL) { + if (!allow_ref_overwrite) { git_oid_cpy(oid, git_reference_oid(new_ref)); return git__throw(GIT_EEXISTS, "Tag already exists"); } else { @@ -256,8 +226,7 @@ static int tag_create( } } - type_str = git_object_type2string(target_type); - + type_str = git_object_type2string(git_object_type(target)); tagger_str_len = git_signature__write(&tagger_str, "tagger", tagger); type_str_len = strlen(type_str); @@ -273,7 +242,7 @@ static int tag_create( if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_TAG)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create tag"); - git__write_oid(stream, "object", target); + git__write_oid(stream, "object", git_object_id(target)); stream->write(stream, "type ", STRLEN("type ")); stream->write(stream, type_str, type_str_len); @@ -296,18 +265,19 @@ static int tag_create( return git__rethrow(error, "Failed to create tag"); if (!should_update_ref) - error = git_reference_create_oid(&new_ref, repo, ref_name, oid); + error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0); else error = git_reference_set_oid(new_ref, oid); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); } -int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer) +int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite) { git_tag tag; - int error; + int error, should_update_ref = 0; git_odb_stream *stream; + git_odb_object *target_obj; git_reference *new_ref; char ref_name[GIT_REFNAME_MAX]; @@ -317,16 +287,38 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu memset(&tag, 0, sizeof(tag)); /* validate the buffer */ - if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS) return git__rethrow(error, "Failed to create tag"); - - if ((error = tag_valid_in_odb(&new_ref, ref_name, &tag.target, tag.type, repo, tag.tag_name)) < GIT_SUCCESS) + + /* validate the target */ + if ((error = git_odb_read(&target_obj, repo->db, &tag.target)) < GIT_SUCCESS) return git__rethrow(error, "Failed to create tag"); + + if (tag.type != target_obj->raw.type) + return git__throw(error, "The type for the given target is invalid"); + + git_odb_object_close(target_obj); + error = retreive_tag_reference(&new_ref, ref_name, repo, tag.tag_name); + + switch (error) { + case GIT_SUCCESS: + case GIT_ENOTFOUND: + break; + + default: + return git__rethrow(error, "Failed to create tag"); + } + + /** Ensure the tag name doesn't conflict with an already existing + * reference unless overwriting has explictly been requested **/ if (new_ref != NULL) { - git_oid_cpy(oid, git_reference_oid(new_ref)); - return git__throw(GIT_EEXISTS, "Tag already exists"); + if (!allow_ref_overwrite) { + git_oid_cpy(oid, git_reference_oid(new_ref)); + return git__throw(GIT_EEXISTS, "Tag already exists"); + } else { + should_update_ref = 1; + } } /* write the buffer */ @@ -340,9 +332,11 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to create tag"); - - - error = 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, 0); + else + error = git_reference_set_oid(new_ref, oid); git_signature_free(tag.tagger); free(tag.tag_name); @@ -351,68 +345,6 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); } -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; diff --git a/tests/t04-commit.c b/tests/t04-commit.c index 0ae0cdfb2..b7e0afe1e 100644 --- a/tests/t04-commit.c +++ b/tests/t04-commit.c @@ -415,19 +415,21 @@ This is a commit created in memory and it will be written back to disk\n" static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; - BEGIN_TEST(write0, "write a new commit object from memory to disk") git_repository *repo; git_commit *commit; git_oid tree_id, parent_id, commit_id; const git_signature *author, *committer; - /* char hex_oid[41]; */ + git_commit *parent; + git_tree *tree; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_fromstr(&tree_id, tree_oid); + must_pass(git_tree_lookup(&tree, repo, &tree_id)); + git_oid_fromstr(&parent_id, commit_ids[4]); + must_pass(git_commit_lookup(&parent, repo, &parent_id)); /* create signatures */ committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60); @@ -443,8 +445,11 @@ BEGIN_TEST(write0, "write a new commit object from memory to disk") author, committer, COMMIT_MESSAGE, - &tree_id, - 1, &parent_id)); + tree, + 1, parent)); + + git_object_close((git_object *)parent); + git_object_close((git_object *)tree); git_signature_free((git_signature *)committer); git_signature_free((git_signature *)author); @@ -486,10 +491,12 @@ BEGIN_TEST(root0, "create a root commit") const char *branch_name = "refs/heads/root-commit-branch"; git_reference *head, *branch; char *head_old; + git_tree *tree; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); git_oid_fromstr(&tree_id, tree_oid); + must_pass(git_tree_lookup(&tree, repo, &tree_id)); /* create signatures */ committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60); @@ -513,9 +520,10 @@ BEGIN_TEST(root0, "create a root commit") author, committer, ROOT_COMMIT_MESSAGE, - &tree_id, + tree, 0)); + git_object_close((git_object *)tree); git_signature_free((git_signature *)committer); git_signature_free((git_signature *)author); diff --git a/tests/t08-tag.c b/tests/t08-tag.c index 48d48fe39..64a939f0e 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -88,10 +88,12 @@ 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; + 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)); /* create signature */ tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); @@ -101,11 +103,12 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") &tag_id, /* out id */ repo, "the-tag", - &target_id, - GIT_OBJ_COMMIT, + target, tagger, - TAGGER_MESSAGE)); + TAGGER_MESSAGE, + 0)); + git_object_close(target); git_signature_free((git_signature *)tagger); must_pass(git_tag_lookup(&tag, repo, &tag_id)); @@ -132,42 +135,16 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") END_TEST -BEGIN_TEST(write1, "write a tag to the repository which points to an unknown oid should fail") - git_repository *repo; - git_oid target_id, tag_id; - const git_signature *tagger; - - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - - git_oid_fromstr(&target_id, "deadbeef1b46c854b31185ea97743be6a8774479"); - - /* 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, - "the-zombie-tag", - &target_id, - GIT_OBJ_COMMIT, - tagger, - TAGGER_MESSAGE)); - - git_signature_free((git_signature *)tagger); - - 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; + 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)); /* create signature */ tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); @@ -177,11 +154,12 @@ BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already &tag_id, /* out id */ repo, "e90810b", - &target_id, - GIT_OBJ_COMMIT, + target, tagger, - TAGGER_MESSAGE)); + TAGGER_MESSAGE, + 0)); + git_object_close(target); git_signature_free((git_signature *)tagger); git_repository_free(repo); @@ -193,10 +171,12 @@ BEGIN_TEST(write3, "Replace an already existing tag") git_oid target_id, tag_id, old_tag_id; const git_signature *tagger; git_reference *ref_tag; + git_object *target; must_pass(open_temp_repo(&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_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); git_oid_cpy(&old_tag_id, git_reference_oid(ref_tag)); @@ -205,15 +185,16 @@ BEGIN_TEST(write3, "Replace an already existing tag") tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); must_be_true(tagger != NULL); - must_pass(git_tag_create_f( + must_pass(git_tag_create( &tag_id, /* out id */ repo, "e90810b", - &target_id, - GIT_OBJ_COMMIT, + target, tagger, - TAGGER_MESSAGE)); + TAGGER_MESSAGE, + 1)); + git_object_close(target); git_signature_free((git_signature *)tagger); must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); @@ -242,7 +223,6 @@ BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(read1); ADD_TEST(write0); - ADD_TEST(write1); ADD_TEST(write2); ADD_TEST(write3); ADD_TEST(write4); diff --git a/tests/t10-refs.c b/tests/t10-refs.c index 5efe80447..0ca30d058 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -212,7 +212,7 @@ BEGIN_TEST(create0, "create a new symbolic reference") git__joinpath(ref_path, repo->path_repository, new_head_tracker); /* Create and write the new symbolic reference */ - must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target)); + must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0)); /* Ensure the reference can be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker)); @@ -252,7 +252,7 @@ BEGIN_TEST(create1, "create a deep symbolic reference") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); git__joinpath(ref_path, repo->path_repository, new_head_tracker); - must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target)); + must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0)); must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker)); must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); @@ -276,7 +276,7 @@ BEGIN_TEST(create2, "create a new OID reference") git__joinpath(ref_path, repo->path_repository, new_head); /* Create and write the new object id reference */ - must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id)); + must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id, 0)); /* Ensure the reference can be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, new_head)); @@ -310,7 +310,7 @@ BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unkn must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); /* Create and write the new object id reference */ - must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id)); + must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id, 0)); /* Ensure the reference can't be looked-up... */ must_fail(git_reference_lookup(&looked_up_ref, repo, new_head)); @@ -329,16 +329,16 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* The target needds to exist and we need to check the name has changed */ - must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name)); - must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_branch_name)); + must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name, 0)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_branch_name, 0)); /* Ensure it points to the right place*/ must_pass(git_reference_lookup(&ref, repo, ref_name)); must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC); must_be_true(!strcmp(git_reference_target(ref), ref_branch_name)); /* Ensure we can't create it unless we force it to */ - must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name)); - must_pass(git_reference_create_symbolic_f(&ref, repo, ref_name, ref_master_name)); + must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 1)); /* Ensure it points to the right place */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -360,15 +360,15 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference") git_oid_cpy(&id, git_reference_oid(ref)); /* Create it */ - must_pass(git_reference_create_oid(&ref, repo, ref_name, &id)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); must_pass(git_reference_lookup(&ref, repo, ref_test_name)); must_be_true(ref->type & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); /* Ensure we can't overwrite unless we force it */ - must_fail(git_reference_create_oid(&ref, repo, ref_name, &id)); - must_pass(git_reference_create_oid_f(&ref, repo, ref_name, &id)); + must_fail(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 1)); /* Ensure it has been overwritten */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -388,9 +388,9 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli must_be_true(ref->type & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); - must_pass(git_reference_create_oid(&ref, repo, ref_name, &id)); - must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name)); - must_pass(git_reference_create_symbolic_f(&ref, repo, ref_name, ref_master_name)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); + must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 1)); /* Ensure it points to the right place */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -412,10 +412,10 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object git_oid_cpy(&id, git_reference_oid(ref)); /* Create the symbolic ref */ - must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0)); /* It shouldn't overwrite unless we tell it to */ - must_fail(git_reference_create_oid(&ref, repo, ref_name, &id)); - must_pass(git_reference_create_oid_f(&ref, repo, ref_name, &id)); + must_fail(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 1)); /* Ensure it points to the right place */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -671,14 +671,14 @@ BEGIN_TEST(rename6, "can not overwrite name of existing reference") git_oid_cpy(&id, git_reference_oid(ref)); /* Create loose references */ - must_pass(git_reference_create_oid(&ref_one, repo, ref_one_name, &id)); - must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id)); + must_pass(git_reference_create_oid(&ref_one, repo, ref_one_name, &id, 0)); + must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id, 0)); /* Pack everything */ must_pass(git_reference_packall(repo)); /* Attempt to create illegal reference */ - must_fail(git_reference_create_oid(&ref_one_new, repo, ref_one_name_new, &id)); + must_fail(git_reference_create_oid(&ref_one_new, repo, ref_one_name_new, &id, 0)); /* Illegal reference couldn't be created so this is supposed to fail */ must_fail(git_reference_lookup(&ref_one_new, repo, ref_one_name_new));