diff --git a/src/git2/refs.h b/src/git2/refs.h index 57d398583..ae77f56f4 100644 --- a/src/git2/refs.h +++ b/src/git2/refs.h @@ -44,6 +44,9 @@ GIT_BEGIN_DECL * 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. + * * @param ref_out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference @@ -58,6 +61,9 @@ GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repos * 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. + * * @param ref_out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference @@ -119,22 +125,6 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref); */ GIT_EXTERN(int) git_reference_resolve(git_reference **resolved_ref, git_reference *ref); -/** - * Write a reference back to disk. - * - * The reference must have a valid name and a valid target - * (either direct or symbolic). - * - * If the reference has been loaded from disk and no changes - * have been made, no action will take place. - * - * The writing to disk is atomic. - * - * @param ref The reference - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_reference_write(git_reference *ref); - /** * Get the repository where a reference resides * @@ -144,14 +134,13 @@ GIT_EXTERN(int) git_reference_write(git_reference *ref); GIT_EXTERN(git_repository *) git_reference_owner(git_reference *ref); /** - * Set the target reference of a reference. + * Set the symbolic target of a reference. * - * This converts the reference into a symbolic - * reference. + * The reference must be a symbolic reference, otherwise + * this method will fail. * - * This marks the reference as modified; changes - * won't take effect until it is manually written back - * to disk. + * The reference will be automatically updated in + * memory and on disk. * * @param ref The reference * @param target The new target for the reference @@ -162,12 +151,11 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target) /** * Set the OID target of a reference. * - * This converts the reference into a direct - * reference. + * The reference must be a direct reference, otherwise + * this method will fail. * - * This marks the reference as modified; changes - * won't take effect until it is manually written back - * to disk. + * The reference will be automatically updated in + * memory and on disk. * * @param ref The reference * @param target The new target OID for the reference @@ -175,6 +163,30 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target) */ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id); +/** + * Rename an existing reference + * + * This method works for both direct and symbolic references. + * The new name will be checked for validity and may be + * modified into a normalized form. + * + * The refernece will be immediately renamed in-memory + * and on disk. + * + */ +GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name); + +/** + * Delete an existing reference + * + * This method works for both direct and symbolic references. + * + * The reference will be immediately removed on disk and from + * memory. The given reference pointer will no longer be valid. + * + */ +GIT_EXTERN(int) git_reference_delete(git_reference *ref); + /** @} */ GIT_END_DECL #endif diff --git a/src/refs.c b/src/refs.c index 0cae6ee13..8fa157cb2 100644 --- a/src/refs.c +++ b/src/refs.c @@ -30,6 +30,8 @@ #define MAX_NESTING_LEVEL 5 +static int reference_write(git_reference *ref); + static const int default_table_size = 32; static uint32_t reftable_hash(const void *key, int hash_id) @@ -43,17 +45,6 @@ static uint32_t reftable_hash(const void *key, int hash_id) return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]); } -static int check_refname(const char *name) -{ - /* - * TODO: To be implemented - * Check if the given name is a valid name - * for a reference - */ - - return name ? GIT_SUCCESS : GIT_ERROR; -} - static void reference_free(git_reference *reference) { if (reference == NULL) @@ -68,7 +59,7 @@ static void reference_free(git_reference *reference) free(reference); } -static int reference__create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type) { +static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type) { char normalized[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; int error = GIT_SUCCESS; git_reference *reference = NULL; @@ -111,7 +102,7 @@ int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, int error = GIT_SUCCESS; git_reference *ref = NULL; - error = reference__create(&ref, repo, name, GIT_REF_SYMBOLIC); + error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); if (error < GIT_SUCCESS) goto cleanup; @@ -120,14 +111,11 @@ int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, 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; - error = git_reference_write(ref); - if (error < GIT_SUCCESS) - goto cleanup; - *ref_out = ref; return error; @@ -142,18 +130,15 @@ int git_reference_create_oid(git_reference **ref_out, git_repository *repo, cons int error = GIT_SUCCESS; git_reference *ref = NULL; - error = reference__create(&ref, repo, name, GIT_REF_OID); + 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; - error = git_reference_write(ref); - if (error < GIT_SUCCESS) - goto cleanup; - *ref_out = ref; return error; @@ -168,7 +153,6 @@ static int parse_sym_ref(git_reference *ref, gitfo_buf *file_content) const unsigned int header_len = strlen(GIT_SYMREF); const char *refname_start; char *eol; - int error; refname_start = (const char *)file_content->data; @@ -182,9 +166,9 @@ static int parse_sym_ref(git_reference *ref, gitfo_buf *file_content) refname_start += header_len; - error = git_reference_set_target(ref, refname_start); - if (error < GIT_SUCCESS) - return error; + ref->target.ref = git__strdup(refname_start); + if (ref->target.ref == NULL) + return GIT_ENOMEM; /* remove newline at the end of file */ eol = strchr(ref->target.ref, '\n'); @@ -202,8 +186,6 @@ static int parse_oid_ref(git_reference *ref, gitfo_buf *file_content) { char *buffer; git_oid id; - int error; - buffer = (char *)file_content->data; /* File format: 40 chars (OID) + newline */ @@ -213,9 +195,7 @@ static int parse_oid_ref(git_reference *ref, gitfo_buf *file_content) if (git_oid_mkstr(&id, buffer) < GIT_SUCCESS) return GIT_EREFCORRUPTED; - error = git_reference_set_oid(ref, &id); - if (error < GIT_SUCCESS) - return error; + git_oid_cpy(&ref->target.oid, &id); buffer = buffer + GIT_OID_HEXSZ; if (*buffer == '\r') @@ -265,21 +245,19 @@ static int lookup_loose_ref( goto cleanup; if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) { - error = reference__create(&ref, repo, name, GIT_REF_SYMBOLIC); + error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); if (error < GIT_SUCCESS) goto cleanup; error = parse_sym_ref(ref, &ref_file); } else { - error = reference__create(&ref, repo, name, GIT_REF_OID); + error = reference_create(&ref, repo, name, GIT_REF_OID); if (error < GIT_SUCCESS) goto cleanup; error = parse_oid_ref(ref, &ref_file); } - ref->modified = 0; - if (error < GIT_SUCCESS) goto cleanup; @@ -397,16 +375,13 @@ static int parse_packed_line( if (refname[refname_len - 1] == '\r') refname[refname_len - 1] = 0; - error = reference__create(&ref, repo, refname, GIT_REF_OID); + error = reference_create(&ref, repo, refname, GIT_REF_OID); if (error < GIT_SUCCESS) goto cleanup; - error = git_reference_set_oid(ref, &id); - if (error < GIT_SUCCESS) - goto cleanup; + git_oid_cpy(&ref->target.oid, &id); ref->packed = 1; - ref->modified = 0; *ref_out = ref; *buffer_out = refname_end + 1; @@ -496,9 +471,7 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) git_oid_cpy(&ref->target.oid, id); - ref->modified = 1; - - return GIT_SUCCESS; + return reference_write(ref); } int git_reference_set_target(git_reference *ref, const char *target) @@ -511,9 +484,7 @@ int git_reference_set_target(git_reference *ref, const char *target) if (ref->target.ref == NULL) return GIT_ENOMEM; - ref->modified = 1; - - return GIT_SUCCESS; + return reference_write(ref); } const git_oid *git_reference_oid(git_reference *ref) @@ -528,6 +499,8 @@ const git_oid *git_reference_oid(git_reference *ref) const char *git_reference_target(git_reference *ref) { + assert(ref); + if (ref->type != GIT_REF_SYMBOLIC) return NULL; @@ -576,19 +549,14 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) return GIT_ETOONESTEDSYMREF; } -int git_reference_write(git_reference *ref) +static int reference_write(git_reference *ref) { git_filebuf file; - git_reference *looked_up_reference; char ref_path[GIT_PATH_MAX]; int error, contents_size; char *ref_contents = NULL; - if (ref->type == GIT_REF_INVALID || ref->type == GIT_REF_ANY) - return GIT_EINVALIDREFSTATE; - - if (!ref->modified) - return GIT_SUCCESS; + assert(ref->type == GIT_REF_OID || ref->type == GIT_REF_SYMBOLIC); git__joinpath(ref_path, ref->owner->path_repository, ref->name); @@ -619,27 +587,25 @@ int git_reference_write(git_reference *ref) strcat(ref_contents, ref->target.ref); } + /* TODO: win32 carriage return when writing references in Windows? */ ref_contents[contents_size - 1] = '\n'; if ((error = git_filebuf_write(&file, ref_contents, contents_size)) < GIT_SUCCESS) goto error_cleanup; error = git_filebuf_commit(&file); + if (error < GIT_SUCCESS) + goto unlock; - looked_up_reference = git_hashtable_lookup(ref->owner->references.cache, ref->name); + error = git_hashtable_insert(ref->owner->references.cache, ref->name, ref); + if (error < GIT_SUCCESS) + goto unlock; - if (looked_up_reference == NULL) { - error = git_hashtable_insert(ref->owner->references.cache, ref->name, ref); - if (error < GIT_SUCCESS) - goto cleanup; - } - - goto cleanup; + free(ref_contents); + return GIT_SUCCESS; unlock: - git_filelock_unlock(&lock); - -cleanup: + git_filebuf_cleanup(&lock); free(ref_contents); return error; } diff --git a/src/refs.h b/src/refs.h index 2e9f340b8..33bedc4b7 100644 --- a/src/refs.h +++ b/src/refs.h @@ -21,8 +21,7 @@ struct git_reference { git_rtype type; char *name; - unsigned packed:1, - modified:1; + unsigned packed:1; union { char *ref;