diff --git a/include/git2/refs.h b/include/git2/refs.h index c319bfb3d..773ae445c 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -23,8 +23,7 @@ GIT_BEGIN_DECL /** * Lookup a reference by its name in a repository. * - * The generated reference is owned by the repository and - * should not be freed by the user. + * The generated reference must be freed by the user. * * @param reference_out pointer to the looked-up reference * @param repo the repository to look up the reference @@ -39,8 +38,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito * 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. + * The generated reference must be freed by the user. * * If `force` is true and there already exists a reference * with the same name, it will be overwritten. @@ -60,8 +58,7 @@ 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. + * The generated reference must be freed by the user. * * If `force` is true and there already exists a reference * with the same name, it will be overwritten. @@ -173,7 +170,9 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id); * The new name will be checked for validity and may be * modified into a normalized form. * - * The refernece will be immediately renamed in-memory + * The given git_reference will be updated. + * + * The reference will be immediately renamed in-memory * and on disk. * */ @@ -200,9 +199,6 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); * Once the `packed-refs` file has been written properly, * the loose references will be removed from disk. * - * WARNING: calling this method may invalidate any existing - * references previously loaded on the cache. - * * @param repo Repository where the loose refs will be packed * @return GIT_SUCCESS or an error code */ @@ -253,6 +249,21 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, */ GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload); +/** + * Check if a reference is packed + * + * @param ref git_reference + * @return 0 in case it's not packed; 1 otherwise + */ +GIT_EXTERN(int) git_reference_is_packed(git_reference *ref); + +/** + * Free the given reference + * + * @param ref git_reference + */ +GIT_EXTERN(void) git_reference_free(git_reference *ref); + /** @} */ GIT_END_DECL #endif diff --git a/src/commit.c b/src/commit.c index b9eb3650f..1010fdc56 100644 --- a/src/commit.c +++ b/src/commit.c @@ -137,12 +137,13 @@ int git_commit_create( if (error == GIT_SUCCESS && update_ref != NULL) { git_reference *head; + git_reference *target; error = git_reference_lookup(&head, repo, update_ref); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to create commit"); - error = git_reference_resolve(&head, head); + error = git_reference_resolve(&target, head); if (error < GIT_SUCCESS) { if (error != GIT_ENOTFOUND) return git__rethrow(error, "Failed to create commit"); @@ -156,7 +157,7 @@ int git_commit_create( return git_reference_create_oid(&head, repo, git_reference_target(head), oid, 1); } - error = git_reference_set_oid(head, oid); + error = git_reference_set_oid(target, oid); } if (error < GIT_SUCCESS) diff --git a/src/refs.c b/src/refs.c index 679d7bbcc..1c33f73b2 100644 --- a/src/refs.c +++ b/src/refs.c @@ -17,13 +17,20 @@ #define MAX_NESTING_LEVEL 5 typedef struct { - git_reference ref; + git_repository *owner; + char *name; + unsigned int type; + time_t mtime; +} reference; + +typedef struct { + reference ref; git_oid oid; git_oid peel_target; } reference_oid; typedef struct { - git_reference ref; + reference ref; char *target; } reference_symbolic; @@ -40,16 +47,21 @@ static uint32_t reftable_hash(const void *key, int hash_id) return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]); } -static void reference_free(git_reference *reference); -static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type); +static void reference_free(reference *reference); +static int reference_create(reference **ref_out, git_repository *repo, const char *name, git_rtype type); static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated); +static int reference_set_target(reference *ref, const char *target); +static int reference_set_oid(reference *ref, const git_oid *oid); + +static int reference_delete(reference *ref); + /* loose refs */ -static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content); -static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content); -static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic); -static int loose_write(git_reference *ref); -static int loose_update(git_reference *ref); +static int loose_parse_symbolic(reference *ref, git_fbuffer *file_content); +static int loose_parse_oid(reference *ref, git_fbuffer *file_content); +static int loose_lookup(reference **ref_out, git_repository *repo, const char *name, int skip_symbolic); +static int loose_write(reference *ref); +static int loose_update(reference *ref); /* packed refs */ static int packed_parse_peel(reference_oid *tag_ref, const char **buffer_out, const char *buffer_end); @@ -64,15 +76,19 @@ static int packed_write(git_repository *repo); /* internal helpers */ static int reference_available(git_repository *repo, const char *ref, const char *old_ref); +static int reference_lookup(reference **out, git_repository *repo, const char *name); /* name normalization */ static int check_valid_ref_char(char ch); static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref); -/***************************************** - * Internal methods - Constructor/destructor - *****************************************/ -static void reference_free(git_reference *reference) +/* reference transition */ +static git_reference * reference_create_external(reference *ref); +static reference * reference_get_internal(git_reference *ref); + + + +static void reference_free(reference *reference) { if (reference == NULL) return; @@ -87,14 +103,14 @@ static void reference_free(git_reference *reference) } static int reference_create( - git_reference **ref_out, + reference **ref_out, git_repository *repo, const char *name, git_rtype type) { char normalized[GIT_REFNAME_MAX]; int error = GIT_SUCCESS, size; - git_reference *reference = NULL; + reference *reference = NULL; assert(ref_out && repo && name); @@ -145,13 +161,38 @@ static int reference_read(git_fbuffer *file_content, time_t *mtime, const char * return git_futils_readbuffer_updated(file_content, path, mtime, updated); } +static git_reference * reference_create_external(reference *ref) +{ + size_t size = sizeof(git_reference); + git_reference *out; + out = git__malloc(size); + if (out == NULL) + return NULL; + memset(out, 0x0, size); -/***************************************** - * Internal methods - Loose references - *****************************************/ -static int loose_update(git_reference *ref) + out->owner = ref->owner; + + out->name = git__strdup(ref->name); + if (out->name == NULL) { + git__free(out); + return NULL; + } + + return out; +} + +static reference * reference_get_internal(git_reference *ref) +{ + reference *out = NULL; + + reference_lookup(&out, ref->owner, ref->name); + + return out; +} + +static int loose_update(reference *ref) { int error, updated; git_fbuffer ref_file = GIT_FBUFFER_INIT; @@ -159,6 +200,7 @@ static int loose_update(git_reference *ref) if (ref->type & GIT_REF_PACKED) return packed_load(ref->owner); + /* error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name); if (error < GIT_SUCCESS) goto cleanup; @@ -181,7 +223,6 @@ static int loose_update(git_reference *ref) error = git__throw(GIT_EOBJCORRUPTED, "Invalid reference type (%d) for loose reference", ref->type); - cleanup: git_futils_freebuffer(&ref_file); if (error != GIT_SUCCESS) { @@ -192,7 +233,7 @@ cleanup: return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to update loose reference"); } -static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content) +static int loose_parse_symbolic(reference *ref, git_fbuffer *file_content) { const unsigned int header_len = strlen(GIT_SYMREF); const char *refname_start; @@ -231,7 +272,7 @@ static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content) return GIT_SUCCESS; } -static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content) +static int loose_parse_oid(reference *ref, git_fbuffer *file_content) { int error; reference_oid *ref_oid; @@ -279,14 +320,14 @@ static git_rtype loose_guess_rtype(const char *full_path) } static int loose_lookup( - git_reference **ref_out, + reference **ref_out, git_repository *repo, const char *name, int skip_symbolic) { int error = GIT_SUCCESS; git_fbuffer ref_file = GIT_FBUFFER_INIT; - git_reference *ref = NULL; + reference *ref = NULL; time_t ref_time = 0; *ref_out = NULL; @@ -326,7 +367,7 @@ cleanup: return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to lookup loose reference"); } -static int loose_write(git_reference *ref) +static int loose_write(reference *ref) { git_filebuf file; char ref_path[GIT_PATH_MAX]; @@ -370,15 +411,6 @@ unlock: return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference"); } - - - - - -/***************************************** - * Internal methods - Packed references - *****************************************/ - static int packed_parse_peel( reference_oid *tag_ref, const char **buffer_out, @@ -458,7 +490,7 @@ static int packed_parse_oid( if (refname[refname_len - 1] == '\r') refname[refname_len - 1] = 0; - error = reference_create(&_ref, repo, refname, GIT_REF_OID); + error = reference_create((reference **)&_ref, repo, refname, GIT_REF_OID); if (error < GIT_SUCCESS) goto cleanup; @@ -473,7 +505,7 @@ static int packed_parse_oid( return GIT_SUCCESS; cleanup: - reference_free((git_reference *)ref); + reference_free((reference *)ref); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse OID of packed reference"); } @@ -550,7 +582,7 @@ static int packed_load(git_repository *repo) error = git_hashtable_insert(ref_cache->packfile, ref->ref.name, ref); if (error < GIT_SUCCESS) { - reference_free((git_reference *)ref); + reference_free((reference *)ref); goto cleanup; } } @@ -566,8 +598,6 @@ cleanup: } - - struct dirent_list_data { git_repository *repo; size_t repo_path_len; @@ -601,8 +631,8 @@ static int _dirent_loose_listall(void *_data, char *full_path) static int _dirent_loose_load(void *data, char *full_path) { git_repository *repository = (git_repository *)data; - git_reference *reference; void *old_ref = NULL; + reference *ref; char *file_path; int error; @@ -610,17 +640,17 @@ static int _dirent_loose_load(void *data, char *full_path) return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_load, repository); file_path = full_path + strlen(repository->path_repository); - error = loose_lookup(&reference, repository, file_path, 1); - if (error == GIT_SUCCESS && reference != NULL) { - reference->type |= GIT_REF_PACKED; + error = loose_lookup(&ref, repository, file_path, 1); + if (error == GIT_SUCCESS && ref != NULL) { + ref->type |= GIT_REF_PACKED; - if (git_hashtable_insert2(repository->references.packfile, reference->name, reference, &old_ref) < GIT_SUCCESS) { - reference_free(reference); + if (git_hashtable_insert2(repository->references.packfile, ref->name, ref, &old_ref) < GIT_SUCCESS) { + reference_free(ref); return GIT_ENOMEM; } if (old_ref != NULL) - reference_free((git_reference *)old_ref); + reference_free((reference *)old_ref); } return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load loose dirent"); @@ -644,7 +674,7 @@ static int packed_loadloose(git_repository *repository) /* Remove any loose references from the cache */ { const void *GIT_UNUSED(_unused); - git_reference *reference; + reference *reference; GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference, reference_free(reference); @@ -767,16 +797,16 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list) unsigned int i; char full_path[GIT_PATH_MAX]; int error = GIT_SUCCESS; - git_reference *reference; + reference *r; for (i = 0; i < packing_list->length; ++i) { - git_reference *ref = git_vector_get(packing_list, i); + reference *ref = git_vector_get(packing_list, i); /* Ensure the packed reference doesn't exist * in a (more up-to-date?) state as a loose reference */ - reference = git_hashtable_lookup(ref->owner->references.loose_cache, ref->name); - if (reference != NULL) + r = git_hashtable_lookup(ref->owner->references.loose_cache, ref->name); + if (r != NULL) continue; git_path_join(full_path, repo->path_repository, ref->name); @@ -801,8 +831,8 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list) static int packed_sort(const void *a, const void *b) { - const git_reference *ref_a = (const git_reference *)a; - const git_reference *ref_b = (const git_reference *)b; + const reference *ref_a = (const reference *)a; + const reference *ref_b = (const reference *)b; return strcmp(ref_a->name, ref_b->name); } @@ -828,7 +858,7 @@ static int packed_write(git_repository *repo) /* Load all the packfile into a vector */ { - git_reference *reference; + reference *reference; const void *GIT_UNUSED(_unused); GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference, @@ -935,14 +965,7 @@ static int reference_available(git_repository *repo, const char *ref, const char return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref); } -/***************************************** - * External Library API - *****************************************/ - -/** - * Constructors - */ -int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name) +int reference_lookup(reference **ref_out, git_repository *repo, const char *name) { int error; char normalized_name[GIT_REFNAME_MAX]; @@ -990,10 +1013,30 @@ 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_lookup(git_reference **ref_out, git_repository *repo, const char *name) +{ + int error; + reference *ref; + + assert(ref_out && repo && name); + + *ref_out = NULL; + + error = reference_lookup(&ref, repo, name); + if (error < GIT_SUCCESS) + return error; + + *ref_out = reference_create_external(ref); + if (*ref_out == NULL) + return GIT_ENOMEM; + + return GIT_SUCCESS; +} + /** * Getters */ -git_rtype git_reference_type(git_reference *ref) +static git_rtype ref_type(reference *ref) { assert(ref); @@ -1006,6 +1049,43 @@ git_rtype git_reference_type(git_reference *ref) return GIT_REF_INVALID; } +git_rtype git_reference_type(git_reference *ref_in) +{ + reference *ref; + + assert(ref_in); + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return GIT_REF_INVALID; + + return ref_type(ref); +} + +int git_reference_is_packed(git_reference *ref_in) +{ + reference *ref; + + assert(ref_in); + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return GIT_REF_INVALID; + + return !!(ref->type & GIT_REF_PACKED); +} + +void git_reference_free(git_reference *ref) +{ + if (ref == NULL) + return; + + if (ref->name) + git__free(ref->name); + + git__free(ref); +} + const char *git_reference_name(git_reference *ref) { assert(ref); @@ -1018,7 +1098,7 @@ git_repository *git_reference_owner(git_reference *ref) return ref->owner; } -const git_oid *git_reference_oid(git_reference *ref) +static const git_oid *ref_oid(reference *ref) { assert(ref); @@ -1031,7 +1111,20 @@ const git_oid *git_reference_oid(git_reference *ref) return &((reference_oid *)ref)->oid; } -const char *git_reference_target(git_reference *ref) +const git_oid *git_reference_oid(git_reference *ref_in) +{ + reference *ref; + + assert(ref_in); + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return NULL; + + return ref_oid(ref); +} + +static const char *ref_target(reference *ref) { assert(ref); @@ -1044,14 +1137,27 @@ 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) +const char *git_reference_target(git_reference *ref_in) +{ + reference *ref; + + assert(ref_in); + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return NULL; + + return ref_target(ref); +} + +static int reference_create_symbolic(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; + reference *ref = NULL; void *old_ref = NULL; - if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) + if (reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists"); /* @@ -1076,7 +1182,7 @@ int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, goto cleanup; /* set the target; this will write the reference on disk */ - error = git_reference_set_target(ref, normalized); + error = reference_set_target(ref, normalized); if (error < GIT_SUCCESS) goto cleanup; @@ -1090,7 +1196,7 @@ int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, goto cleanup; if (old_ref != NULL) - reference_free((git_reference *)old_ref); + reference_free((reference *)old_ref); } *ref_out = ref; @@ -1102,13 +1208,29 @@ cleanup: 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 git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force) +{ + int error; + reference *ref; + + error = reference_create_symbolic(&ref, repo, name, target, force); + if (error < GIT_SUCCESS) + return error; + + *ref_out = reference_create_external(ref); + if (*ref_out == NULL) + return GIT_ENOMEM; + + return GIT_SUCCESS; +} + +static int reference_create_oid(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; + reference *ref = NULL; void *old_ref = NULL; - if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) + if(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) @@ -1131,7 +1253,7 @@ int git_reference_create_oid(git_reference **ref_out, git_repository *repo, cons } /* set the oid; this will write the reference on disk */ - error = git_reference_set_oid(ref, id); + error = reference_set_oid(ref, id); if (error < GIT_SUCCESS) goto cleanup; @@ -1141,7 +1263,7 @@ int git_reference_create_oid(git_reference **ref_out, git_repository *repo, cons goto cleanup; if (old_ref != NULL) - reference_free((git_reference *)old_ref); + reference_free((reference *)old_ref); } *ref_out = ref; @@ -1153,9 +1275,21 @@ cleanup: return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); } -/** - * Setters - */ +int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force) +{ + int error; + reference *ref; + + error = reference_create_oid(&ref, repo, name, id, force); + if (error < GIT_SUCCESS) + return error; + + *ref_out = reference_create_external(ref); + if (*ref_out == NULL) + return GIT_ENOMEM; + + return GIT_SUCCESS; +} /* * Change the OID target of a reference. @@ -1177,7 +1311,7 @@ cleanup: * 4. Write the original to the loose cache * 5. Replace the original with the copy (old reference) in the packfile cache */ -int git_reference_set_oid(git_reference *ref, const git_oid *id) +int reference_set_oid(reference *ref, const git_oid *id) { reference_oid *ref_oid; reference_oid *ref_old = NULL; @@ -1233,10 +1367,21 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) return GIT_SUCCESS; cleanup: - reference_free((git_reference *)ref_old); + reference_free((reference *)ref_old); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set OID target of reference"); } +int git_reference_set_oid(git_reference *ref_in, const git_oid *target) +{ + reference *ref; + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return git__throw(GIT_ENOTFOUND, "Failed to rename reference. Reference `%s` doesn't exist", ref->name); + + return reference_set_oid(ref, target); +} + /* * Change the target of a symbolic reference. * @@ -1244,7 +1389,7 @@ cleanup: * a pack. We just change the target in memory * and overwrite the file on disk. */ -int git_reference_set_target(git_reference *ref, const char *target) +int reference_set_target(reference *ref, const char *target) { reference_symbolic *ref_sym; @@ -1261,11 +1406,18 @@ int git_reference_set_target(git_reference *ref, const char *target) return loose_write(ref); } -/** - * Other - */ +int git_reference_set_target(git_reference *ref_in, const char *target) +{ + reference *ref; -int git_reference_rename(git_reference *ref, const char *new_name, int force) + ref = reference_get_internal(ref_in); + if (ref == NULL) + return git__throw(GIT_ENOTFOUND, "Failed to rename reference. Reference `%s` doesn't exist", ref->name); + + return reference_set_target(ref, target); +} + +int git_reference_rename(git_reference *ref_in, const char *new_name, int force) { int error; char *old_name = NULL; @@ -1276,9 +1428,13 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) const char *target_ref = NULL; const char *head_target = NULL; const git_oid *target_oid = NULL; - git_reference *new_ref = NULL, *head = NULL; + reference *ref = NULL, *new_ref = NULL, *head = NULL; - assert(ref); + assert(ref_in); + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return git__throw(GIT_ENOTFOUND, "Failed to rename reference. Reference `%s` doesn't exist", ref->name); error = normalize_name(normalized, sizeof(normalized), new_name, ref->type & GIT_REF_OID); if (error < GIT_SUCCESS) @@ -1286,12 +1442,12 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) new_name = normalized; - error = git_reference_lookup(&new_ref, ref->owner, new_name); + error = reference_lookup(&new_ref, ref->owner, new_name); if (error == GIT_SUCCESS) { if (!force) return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists"); - error = git_reference_delete(new_ref); + error = reference_delete(new_ref); } if (error < GIT_SUCCESS) { @@ -1314,10 +1470,10 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) old_name = git__strdup(ref->name); if (ref->type & GIT_REF_SYMBOLIC) { - if ((target_ref = git_reference_target(ref)) == NULL) + if ((target_ref = ref_target(ref)) == NULL) goto cleanup; } else { - if ((target_oid = git_reference_oid(ref)) == NULL) + if ((target_oid = ref_oid(ref)) == NULL) goto cleanup; } @@ -1375,22 +1531,18 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) * Finally we can create the new reference. */ if (ref->type & GIT_REF_SYMBOLIC) { - if ((error = git_reference_create_symbolic(&new_ref, ref->owner, new_name, target_ref, 0)) < GIT_SUCCESS) + if ((error = reference_create_symbolic(&new_ref, ref->owner, new_name, target_ref, 0)) < GIT_SUCCESS) goto rollback; } else { - if ((error = git_reference_create_oid(&new_ref, ref->owner, new_name, target_oid, 0)) < GIT_SUCCESS) + if ((error = reference_create_oid(&new_ref, ref->owner, new_name, target_oid, 0)) < GIT_SUCCESS) goto rollback; } - git__free(ref->name); - ref->name = new_ref->name; - /* - * No need in new_ref anymore. We created it to fix the change on disk. - * TODO: Refactoring required. + * Change the name of the reference given by the user. */ - new_ref->name = NULL; - reference_free(new_ref); + git__free(ref_in->name); + ref_in->name = git__strdup(new_ref->name); if ((error = git_hashtable_insert(ref->owner->references.loose_cache, ref->name, ref)) < GIT_SUCCESS) goto rollback; @@ -1399,13 +1551,13 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) * Check if we have to update HEAD. */ - if ((error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE)) < GIT_SUCCESS) + if ((error = reference_lookup(&head, new_ref->owner, GIT_HEAD_FILE)) < GIT_SUCCESS) goto cleanup; - head_target = git_reference_target(head); + head_target = ref_target(head); if (head_target && !strcmp(head_target, old_name)) - if ((error = git_reference_create_symbolic(&head, ref->owner, "HEAD", ref->name, 1)) < GIT_SUCCESS) + if ((error = reference_create_symbolic(&head, new_ref->owner, "HEAD", new_ref->name, 1)) < GIT_SUCCESS) goto rollback; cleanup: @@ -1417,11 +1569,11 @@ rollback: * Try to create the old reference again. */ if (ref->type & GIT_REF_SYMBOLIC) - error = git_reference_create_symbolic(&new_ref, ref->owner, old_name, target_ref, 0); + error = reference_create_symbolic(&new_ref, ref->owner, old_name, target_ref, 0); else - error = git_reference_create_oid(&new_ref, ref->owner, old_name, target_oid, 0); + error = reference_create_oid(&new_ref, ref->owner, old_name, target_oid, 0); - ref->name = old_name; + ref_in->name = old_name; return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Failed to rollback"); } @@ -1441,10 +1593,10 @@ rollback: * * This obviously invalidates the `ref` pointer. */ -int git_reference_delete(git_reference *ref) +int reference_delete(reference *ref) { int error; - git_reference *reference; + reference *reference; assert(ref); @@ -1468,9 +1620,9 @@ int git_reference_delete(git_reference *ref) /* When deleting a loose reference, we have to ensure that an older * packed version of it doesn't exist */ - if (!git_reference_lookup(&reference, ref->owner, ref->name)) { + if (!reference_lookup(&reference, ref->owner, ref->name)) { assert((reference->type & GIT_REF_PACKED) != 0); - error = git_reference_delete(reference); + error = reference_delete(reference); } } @@ -1479,7 +1631,20 @@ cleanup: return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference"); } -int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) +int git_reference_delete(git_reference *ref_in) +{ + reference *ref; + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return git__throw(GIT_ENOTFOUND, "Failed to delete reference. Reference `%s` doesn't exist", ref_in->name); + + git_reference_free(ref_in); + + return reference_delete(ref); +} + +static int reference_resolve(reference **resolved_ref, reference *ref) { git_repository *repo; int error, i; @@ -1501,13 +1666,35 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) return GIT_SUCCESS; ref_sym = (reference_symbolic *)ref; - if ((error = git_reference_lookup(&ref, repo, ref_sym->target)) < GIT_SUCCESS) + if ((error = reference_lookup(&ref, repo, ref_sym->target)) < GIT_SUCCESS) return error; } return git__throw(GIT_ENOMEM, "Failed to resolve reference. Reference is too nested"); } +int git_reference_resolve(git_reference **resolved_ref, git_reference *ref_in) +{ + int error; + reference *ref = NULL, *out = NULL; + + *resolved_ref = NULL; + + ref = reference_get_internal(ref_in); + if (ref == NULL) + return git__throw(GIT_ENOTFOUND, "Failed to resolve reference. Reference `%s` doesn't exist", ref_in->name); + + error = reference_resolve(&out, ref); + if (error < GIT_SUCCESS) + return error; + + *resolved_ref = reference_create_external(out); + if (*resolved_ref == NULL) + return GIT_ENOMEM; + + return GIT_SUCCESS; +} + int git_reference_packall(git_repository *repo) { int error; @@ -1588,12 +1775,6 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in return GIT_SUCCESS; } - - - -/***************************************** - * Init/free (repository API) - *****************************************/ int git_repository__refcache_init(git_refcache *refs) { assert(refs); @@ -1612,7 +1793,7 @@ int git_repository__refcache_init(git_refcache *refs) void git_repository__refcache_free(git_refcache *refs) { - git_reference *reference; + reference *reference; const void *GIT_UNUSED(_unused); assert(refs); @@ -1632,11 +1813,6 @@ void git_repository__refcache_free(git_refcache *refs) } } - - -/***************************************** - * Name normalization - *****************************************/ static int check_valid_ref_char(char ch) { if ((unsigned) ch <= ' ') diff --git a/src/refs.h b/src/refs.h index 33c1e6983..db0f5c4df 100644 --- a/src/refs.h +++ b/src/refs.h @@ -35,8 +35,6 @@ struct git_reference { git_repository *owner; char *name; - unsigned int type; - time_t mtime; }; typedef struct { diff --git a/tests/t10-refs.c b/tests/t10-refs.c index 4cb31d5e7..cfedff0a5 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -42,8 +42,8 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name)); - must_be_true(reference->type & GIT_REF_OID); - must_be_true((reference->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_type(reference) & GIT_REF_OID); + must_be_true(git_reference_is_packed(reference) == 0); must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0); must_pass(git_object_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY)); @@ -81,12 +81,12 @@ BEGIN_TEST(readsym0, "lookup a symbolic reference") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE)); - must_be_true(reference->type & GIT_REF_SYMBOLIC); - must_be_true((reference->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_type(reference) & GIT_REF_SYMBOLIC); + must_be_true(git_reference_is_packed(reference) == 0); must_be_true(strcmp(reference->name, GIT_HEAD_FILE) == 0); must_pass(git_reference_resolve(&resolved_ref, reference)); - must_be_true(resolved_ref->type == GIT_REF_OID); + must_be_true(git_reference_type(resolved_ref) == GIT_REF_OID); must_pass(git_object_lookup(&object, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY)); must_be_true(object != NULL); @@ -108,12 +108,12 @@ BEGIN_TEST(readsym1, "lookup a nested symbolic reference") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name)); - must_be_true(reference->type & GIT_REF_SYMBOLIC); - must_be_true((reference->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_type(reference) & GIT_REF_SYMBOLIC); + must_be_true(git_reference_is_packed(reference) == 0); must_be_true(strcmp(reference->name, head_tracker_sym_ref_name) == 0); must_pass(git_reference_resolve(&resolved_ref, reference)); - must_be_true(resolved_ref->type == GIT_REF_OID); + must_be_true(git_reference_type(resolved_ref) == GIT_REF_OID); must_pass(git_object_lookup(&object, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY)); must_be_true(object != NULL); @@ -173,8 +173,8 @@ BEGIN_TEST(readpacked0, "lookup a packed reference") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&reference, repo, packed_head_name)); - must_be_true(reference->type & GIT_REF_OID); - must_be_true((reference->type & GIT_REF_PACKED) != 0); + must_be_true(git_reference_type(reference) & GIT_REF_OID); + must_be_true(git_reference_is_packed(reference)); must_be_true(strcmp(reference->name, packed_head_name) == 0); must_pass(git_object_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY)); @@ -192,8 +192,8 @@ BEGIN_TEST(readpacked1, "assure that a loose reference is looked up before a pac must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&reference, repo, packed_head_name)); must_pass(git_reference_lookup(&reference, repo, packed_test_head_name)); - must_be_true(reference->type & GIT_REF_OID); - must_be_true((reference->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_type(reference) & GIT_REF_OID); + must_be_true(git_reference_is_packed(reference) == 0); must_be_true(strcmp(reference->name, packed_test_head_name) == 0); git_repository_free(repo); @@ -219,13 +219,13 @@ BEGIN_TEST(create0, "create a new symbolic reference") /* Ensure the reference can be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker)); - must_be_true(looked_up_ref->type & GIT_REF_SYMBOLIC); - must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC); + must_be_true(git_reference_is_packed(looked_up_ref) == 0); must_be_true(strcmp(looked_up_ref->name, new_head_tracker) == 0); /* ...peeled.. */ must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); - must_be_true(resolved_ref->type == GIT_REF_OID); + must_be_true(git_reference_type(resolved_ref) == GIT_REF_OID); /* ...and that it points to the current master tip */ must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); @@ -283,8 +283,8 @@ BEGIN_TEST(create2, "create a new OID reference") /* Ensure the reference can be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, new_head)); - must_be_true(looked_up_ref->type & GIT_REF_OID); - must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_type(looked_up_ref) & GIT_REF_OID); + must_be_true(git_reference_is_packed(looked_up_ref) == 0); must_be_true(strcmp(looked_up_ref->name, new_head) == 0); /* ...and that it points to the current master tip */ @@ -359,14 +359,14 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); - must_be_true(ref->type & GIT_REF_OID); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); /* Create it */ 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); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); /* Ensure we can't overwrite unless we force it */ @@ -388,7 +388,7 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); - must_be_true(ref->type & GIT_REF_OID); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); @@ -411,7 +411,7 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); - must_be_true(ref->type & GIT_REF_OID); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); /* Create the symbolic ref */ @@ -451,7 +451,7 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo") /* Ensure a known loose ref can be looked up */ must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name)); - must_be_true((reference->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(reference) == 0); must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0); /* @@ -467,7 +467,7 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo") /* Ensure the known ref can still be looked up but is now packed */ must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name)); - must_be_true((reference->type & GIT_REF_PACKED) != 0); + must_be_true(git_reference_is_packed(reference)); must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0); /* Ensure the known ref has been removed from the loose folder structure */ @@ -493,7 +493,7 @@ BEGIN_TEST(rename0, "rename a loose reference") must_pass(git_reference_lookup(&looked_up_ref, repo, loose_tag_ref_name)); /* ... which is indeed loose */ - must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(looked_up_ref) == 0); /* Now that the reference is renamed... */ must_pass(git_reference_rename(looked_up_ref, new_name, 0)); @@ -507,8 +507,8 @@ BEGIN_TEST(rename0, "rename a loose reference") must_be_true(!strcmp(another_looked_up_ref->name, new_name)); /* .. the ref is still loose... */ - must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0); - must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(another_looked_up_ref) == 0); + must_be_true(git_reference_is_packed(looked_up_ref) == 0); /* ...and the ref can be found in the file system */ git_path_join(temp_path, repo->path_repository, new_name); @@ -533,7 +533,7 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)") must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); /* .. and it's packed */ - must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0); + must_be_true(git_reference_is_packed(looked_up_ref) != 0); /* Now that the reference is renamed... */ must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0)); @@ -547,8 +547,8 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)") must_be_true(!strcmp(another_looked_up_ref->name, brand_new_name)); /* .. the ref is no longer packed... */ - must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0); - must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(another_looked_up_ref) == 0); + must_be_true(git_reference_is_packed(looked_up_ref) == 0); /* ...and the ref now happily lives in the file system */ git_path_join(temp_path, repo->path_repository, brand_new_name); @@ -573,13 +573,13 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name)); /* Ensure it's loose */ - must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(another_looked_up_ref) == 0); /* Lookup the reference to rename */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); /* Ensure it's packed */ - must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0); + must_be_true(git_reference_is_packed(looked_up_ref) != 0); /* Now that the reference is renamed... */ must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0)); @@ -588,7 +588,7 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name)); /* Ensure it's loose */ - must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(another_looked_up_ref) == 0); /* Ensure the other ref still exists on the file system */ must_pass(git_futils_exists(temp_path)); @@ -699,7 +699,7 @@ BEGIN_TEST(rename7, "can not overwrite name of existing reference") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); - must_be_true(ref->type & GIT_REF_OID); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); @@ -729,7 +729,7 @@ BEGIN_TEST(rename8, "can be renamed to a new name prefixed with the old name") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); - must_be_true(ref->type & GIT_REF_OID); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); @@ -758,7 +758,7 @@ BEGIN_TEST(rename9, "can move a reference to a upper reference hierarchy") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); - must_be_true(ref->type & GIT_REF_OID); + must_be_true(git_reference_type(ref) & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); @@ -794,7 +794,7 @@ BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name)); /* Ensure it's the loose version that has been found */ - must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); + must_be_true(git_reference_is_packed(looked_up_ref) == 0); /* Now that the reference is deleted... */ must_pass(git_reference_delete(looked_up_ref));