From 45d387ac78bcf3167d69b736d0b322717bc492d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicent=20Mart=C3=AD?= Date: Wed, 15 Feb 2012 16:54:17 +0100 Subject: [PATCH] refs: Error handling rework. WIP --- include/git2/errors.h | 3 + src/path.c | 27 ++- src/refs.c | 513 ++++++++++++++++++++---------------------- 3 files changed, 259 insertions(+), 284 deletions(-) diff --git a/include/git2/errors.h b/include/git2/errors.h index 54da869b4..e3f5f4cb0 100644 --- a/include/git2/errors.h +++ b/include/git2/errors.h @@ -122,9 +122,12 @@ typedef struct { typedef enum { GITERR_NOMEMORY, + GITERR_REFERENCE, } git_error_class; +#define GITERR_CHECK_ALLOC(ptr, error) if (ptr == NULL) { giterr_set_oom(error); return -1 } + GIT_EXTERN(void) giterr_set(git_error **error_out, int error_class, const char *string, ...); GIT_EXTERN(void) giterr_set_oom(git_error **error); GIT_EXTERN(void) giterr_free(git_error *error); diff --git a/src/path.c b/src/path.c index d2c292bf2..c882fe387 100644 --- a/src/path.c +++ b/src/path.c @@ -486,20 +486,23 @@ GIT_INLINE(int) is_dot_or_dotdot(const char *name) int git_path_direach( git_buf *path, - int (*fn)(void *, git_buf *), - void *arg) + int (*fn)(void *, git_buf *, git_error **), + void *arg, + git_error **error) { ssize_t wd_len; DIR *dir; struct dirent de_buf, *de; - if (git_path_to_dir(path) < GIT_SUCCESS) - return git_buf_lasterror(path); + if (git_path_to_dir(path, error) < 0) + return -1; wd_len = path->size; dir = opendir(path->ptr); - if (!dir) - return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr); + if (!dir) { + giterr_set(error, GITERR_OS, "Failed to `opendir` %s: %s", path->ptr, strerror(errno)); + return -1; + } while (p_readdir_r(dir, &de_buf, &de) == 0 && de != NULL) { int result; @@ -507,16 +510,18 @@ int git_path_direach( if (is_dot_or_dotdot(de->d_name)) continue; - if (git_buf_puts(path, de->d_name) < GIT_SUCCESS) - return git_buf_lasterror(path); + if (git_buf_puts(path, de->d_name) < 0) { + giterr_set_oom(error); + return -1; + } - result = fn(arg, path); + result = fn(arg, path, error); git_buf_truncate(path, wd_len); /* restore path */ - if (result != GIT_SUCCESS) { + if (result < 0) { closedir(dir); - return result; /* The callee is reponsible for setting the correct error message */ + return -1; } } diff --git a/src/refs.c b/src/refs.c index f3388bf53..ebfabc635 100644 --- a/src/refs.c +++ b/src/refs.c @@ -62,7 +62,7 @@ static int packed_write(git_repository *repo); /* internal helpers */ static int reference_available(git_repository *repo, - const char *ref, const char *old_ref); + const char *ref, const char *old_ref, git_error **error); static int reference_delete(git_reference *ref); static int reference_lookup(git_reference *ref); @@ -521,7 +521,7 @@ struct dirent_list_data { void *callback_payload; }; -static int _dirent_loose_listall(void *_data, git_buf *full_path) +static int _dirent_loose_listall(void *_data, git_buf *full_path, git_error **error) { struct dirent_list_data *data = (struct dirent_list_data *)_data; const char *file_path = full_path->ptr + data->repo_path_len; @@ -844,49 +844,55 @@ cleanup: return error; } +struct reference_available_t { + const char *new_ref; + const char *old_ref; + int available; +}; + static int _reference_available_cb(const char *ref, void *data) { - const char *new, *old; - const char **refs; + struct reference_available_t *d; assert(ref && data); + d = (reference_available_t *)data; refs = (const char **)data; - new = (const char *)refs[0]; - old = (const char *)refs[1]; - - if (!old || strcmp(old, ref)) { + if (!d->old_ref || strcmp(d->old_ref, ref)) { int reflen = strlen(ref); - int newlen = strlen(new); + int newlen = strlen(d->new_ref); int cmplen = reflen < newlen ? reflen : newlen; - const char *lead = reflen < newlen ? new : ref; + const char *lead = reflen < newlen ? d->new_ref : ref; - if (!strncmp(new, ref, cmplen) && - lead[cmplen] == '/') - return GIT_EEXISTS; + if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') { + d->available = 0; + return -1; + } } - return GIT_SUCCESS; + return 0; } static int reference_available( + int *available, git_repository *repo, const char *ref, - const char* old_ref) + const char* old_ref, + git_error **error) { - const char *refs[2]; + struct reference_available_t data; - refs[0] = ref; - refs[1] = old_ref; + data.new_ref = ref; + data.old_ref = old_ref; + data.available = 1; if (git_reference_foreach(repo, GIT_REF_LISTALL, - _reference_available_cb, (void *)refs) < 0) { - return git__throw(GIT_EEXISTS, - "Reference name `%s` conflicts with existing reference", ref); - } + _reference_available_cb, (void *)&data, error) < 0) + return -1; - return GIT_SUCCESS; + *available = data.available; + return 0; } static int reference_exists(int *exists, git_repository *repo, const char *ref_name) @@ -946,17 +952,17 @@ static int packed_lookup(git_reference *ref) return GIT_SUCCESS; } -static int reference_lookup(git_reference *ref) +static int reference_lookup(git_reference *ref, git_error **error) { - int error_loose, error_packed; + int result; - error_loose = loose_lookup(ref); - if (error_loose == GIT_SUCCESS) - return GIT_SUCCESS; + result = loose_lookup(ref, error); + if (result != GIT_ENOTFOUND) + return result; - error_packed = packed_lookup(ref); - if (error_packed == GIT_SUCCESS) - return GIT_SUCCESS; + result = packed_lookup(ref, error); + if (result != GIT_ENOTFOUND) + return result; git_reference_free(ref); @@ -974,9 +980,9 @@ static int reference_lookup(git_reference *ref) * This is an internal method; the reference is removed * from disk or the packfile, but the pointer is not freed */ -static int reference_delete(git_reference *ref) +static int reference_delete(git_reference *ref, git_error **error) { - int error; + int result; assert(ref); @@ -986,15 +992,19 @@ static int reference_delete(git_reference *ref) if (ref->flags & GIT_REF_PACKED) { struct packref *packref; /* load the existing packfile */ - if ((error = packed_load(ref->owner)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to delete reference"); + if (packed_load(ref->owner, error) < 0) + return -1; if (git_hashtable_remove2(ref->owner->references.packfile, - ref->name, (void **) &packref) < GIT_SUCCESS) - return git__throw(GIT_ENOTFOUND, "Reference not found"); + ref->name, (void **) &packref) < 0) { + giterr_set(error, GITERR_REFERENCE, + "Reference %s stopped existing in the packfile", ref->name); + return -1; + } - git__free (packref); - error = packed_write(ref->owner); + git__free(packref); + if (packed_write(ref->owner, error) < 0) + return -1; /* If the reference is loose, we can just remove the reference * from the filesystem */ @@ -1002,66 +1012,55 @@ static int reference_delete(git_reference *ref) git_reference *ref_in_pack; git_buf full_path = GIT_BUF_INIT; - error = git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name); - if (error < GIT_SUCCESS) - goto cleanup; + if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0) { + giterr_set_oom(error); + return -1; + } - error = p_unlink(full_path.ptr); + result = p_unlink(full_path.ptr); git_buf_free(&full_path); /* done with path at this point */ - if (error < GIT_SUCCESS) - goto cleanup; + + if (result < 0) { + giterr_set(error, GITERR_OS, + "Failed to unlink '%s': %s", full_path.ptr, strerror(errno)); + return -1; + } /* When deleting a loose reference, we have to ensure that an older * packed version of it doesn't exist */ - if (git_reference_lookup(&ref_in_pack, ref->owner, - ref->name) == GIT_SUCCESS) { + if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name, NULL) == GIT_SUCCESS) { assert((ref_in_pack->flags & GIT_REF_PACKED) != 0); - error = git_reference_delete(ref_in_pack); + return git_reference_delete(ref_in_pack, error); } } -cleanup: - return error == GIT_SUCCESS ? - GIT_SUCCESS : - git__rethrow(error, "Failed to delete reference"); + return 0; } -int git_reference_delete(git_reference *ref) +int git_reference_delete(git_reference *ref, git_error **error) { - int error = reference_delete(ref); - if (error < GIT_SUCCESS) - return error; - + int result = reference_delete(ref, error); git_reference_free(ref); - return GIT_SUCCESS; + return result; } int git_reference_lookup(git_reference **ref_out, - git_repository *repo, const char *name) + git_repository *repo, const char *name, git_error **error) { - int error; char normalized_name[GIT_REFNAME_MAX]; git_reference *ref = NULL; assert(ref_out && repo && name); - *ref_out = NULL; + if (normalize_name(normalized_name, sizeof(normalized_name), name, 0, error) < 0) + return -1; - error = normalize_name(normalized_name, sizeof(normalized_name), name, 0); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to lookup reference"); - - error = reference_alloc(&ref, repo, normalized_name); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to lookup reference"); - - error = reference_lookup(ref); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to lookup reference"); + if (reference_alloc(&ref, repo, normalized_name, error) < 0) + return -1; *ref_out = ref; - return GIT_SUCCESS; + return reference_lookup(ref, error); } /** @@ -1123,46 +1122,43 @@ int git_reference_create_symbolic( git_repository *repo, const char *name, const char *target, - int force) + int force, + git_error **error) { char normalized[GIT_REFNAME_MAX]; - int ref_exists, error = GIT_SUCCESS; + int exists; git_reference *ref = NULL; - error = normalize_name(normalized, sizeof(normalized), name, 0); - if (error < GIT_SUCCESS) - goto cleanup; + if (normalize_name(normalized, sizeof(normalized), name, 0, error) < 0) + return -1; - if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS)) - return git__rethrow(error, "Failed to create symbolic reference"); + if (reference_exists(&exists, repo, normalized, error) < 0) + return -1; - if (ref_exists && !force) - return git__throw(GIT_EEXISTS, - "Failed to create symbolic reference. Reference already exists"); + if (exists && !force) { + giterr_set(error, GITERR_REFERENCE, + "A reference with that name (%s) already exists"); + return GIT_EEXISTS; + } - error = reference_alloc(&ref, repo, normalized); - if (error < GIT_SUCCESS) - goto cleanup; + if (reference_alloc(&ref, repo, normalized, error) < 0) + return -1; ref->flags |= GIT_REF_SYMBOLIC; /* set the target; this will normalize the name automatically * and write the reference on disk */ - error = git_reference_set_target(ref, target); - if (error < GIT_SUCCESS) - goto cleanup; - + if (git_reference_set_target(ref, target, error) < 0) { + git_reference_free(ref); + return -1; + } if (ref_out == NULL) { git_reference_free(ref); } else { *ref_out = ref; } - return GIT_SUCCESS; - -cleanup: - git_reference_free(ref); - return git__rethrow(error, "Failed to create symbolic reference"); + return 0; } int git_reference_create_oid( @@ -1170,36 +1166,35 @@ int git_reference_create_oid( git_repository *repo, const char *name, const git_oid *id, - int force) + int force, + git_error **error) { - int error = GIT_SUCCESS, ref_exists; + int exists; git_reference *ref = NULL; char normalized[GIT_REFNAME_MAX]; - error = normalize_name(normalized, sizeof(normalized), name, 1); - if (error < GIT_SUCCESS) - goto cleanup; + if (normalize_name(normalized, sizeof(normalized), name, 1, error) < 0) + return -1; - if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS)) - return git__rethrow(error, "Failed to create OID reference"); + if (reference_exists(&exists, repo, normalized, error) < 0) + return -1; - if (ref_exists && !force) - return git__throw(GIT_EEXISTS, - "Failed to create OID reference. Reference already exists"); + if (exists && !force) { + giterr_set(error, GITERR_REFERENCE, + "A reference with that name (%s) already exists"); + return GIT_EEXISTS; + } - if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to create reference"); - - error = reference_alloc(&ref, repo, name); - if (error < GIT_SUCCESS) - goto cleanup; + if (reference_alloc(&ref, repo, name, error) < 0) + return -1; ref->flags |= GIT_REF_OID; /* set the oid; this will write the reference on disk */ - error = git_reference_set_oid(ref, id); - if (error < GIT_SUCCESS) - goto cleanup; + if (git_reference_set_oid(ref, id, error) < 0) { + git_reference_free(ref); + return -1; + } if (ref_out == NULL) { git_reference_free(ref); @@ -1207,13 +1202,8 @@ int git_reference_create_oid( *ref_out = ref; } - return GIT_SUCCESS; - -cleanup: - git_reference_free(ref); - return git__rethrow(error, "Failed to create reference OID"); + return 0; } - /* * Change the OID target of a reference. * @@ -1223,40 +1213,34 @@ cleanup: * We do not repack packed references because of performance * reasons. */ -int git_reference_set_oid(git_reference *ref, const git_oid *id) +int git_reference_set_oid(git_reference *ref, const git_oid *id, git_error **error) { - int error = GIT_SUCCESS, exists; git_odb *odb = NULL; - if ((ref->flags & GIT_REF_OID) == 0) - return git__throw(GIT_EINVALIDREFSTATE, - "Failed to set OID target of reference. Not an OID reference"); + if ((ref->flags & GIT_REF_OID) == 0) { + giterr_set(error, GITERR_REFERENCE, + "Cannot set OID on symbolic reference"); + return -1; + } assert(ref->owner); - error = git_repository_odb__weakptr(&odb, ref->owner); - if (error < GIT_SUCCESS) - return error; - - exists = git_odb_exists(odb, id); - - git_odb_free(odb); + if (git_repository_odb__weakptr(&odb, ref->owner, error) < 0) + return -1; /* Don't let the user create references to OIDs that * don't exist in the ODB */ - if (!exists) - return git__throw(GIT_ENOTFOUND, - "Failed to set OID target of reference. OID doesn't exist in ODB"); + if (!git_odb_exists(odb, id)) { + giterr_set(error, GITERR_REFERENCE, + "Target OID for the reference doesn't exist on the repository"); + return -1; + } /* Update the OID value on `ref` */ git_oid_cpy(&ref->target.oid, id); /* Write back to disk */ - error = loose_write(ref); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to set OID target of reference"); - - return GIT_SUCCESS; + return loose_write(ref, error); } /* @@ -1266,84 +1250,72 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) * 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 git_reference_set_target(git_reference *ref, const char *target, git_error **error) { - int error; char normalized[GIT_REFNAME_MAX]; - if ((ref->flags & GIT_REF_SYMBOLIC) == 0) - return git__throw(GIT_EINVALIDREFSTATE, - "Failed to set reference target. Not a symbolic reference"); + if ((ref->flags & GIT_REF_SYMBOLIC) == 0) { + giterr_set(error, GITERR_REFERENCE, + "Cannot set symbolic target on a direct reference"); + return -1; + } - error = normalize_name(normalized, sizeof(normalized), target, 0); - if (error < GIT_SUCCESS) - return git__rethrow(error, - "Failed to set reference target. Invalid target name"); + if (normalize_name(normalized, sizeof(normalized), target, 0, error)) + return -1; git__free(ref->target.symbolic); ref->target.symbolic = git__strdup(normalized); - if (ref->target.symbolic == NULL) - return GIT_ENOMEM; + GITERR_CHECK_ALLOC(ref->target.symbolic, error); - return loose_write(ref); + return loose_write(ref, error); } -int git_reference_rename(git_reference *ref, const char *new_name, int force) +int git_reference_rename(git_reference *ref, const char *new_name, int force, git_error **error) { - int error; + int result, ref_available; git_buf aux_path = GIT_BUF_INIT; char normalized[GIT_REFNAME_MAX]; const char *head_target = NULL; git_reference *existing_ref = NULL, *head = NULL; - error = normalize_name(normalized, sizeof(normalized), - new_name, ref->flags & GIT_REF_OID); - - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to rename reference. Invalid name"); + if (normalize_name(normalized, sizeof(normalized), + new_name, ref->flags & GIT_REF_OID, error) < 0) + return -1; new_name = normalized; - /* If we are forcing the rename, try to lookup a reference with the - * new one. If the lookup succeeds, we need to delete that ref - * before the renaming can proceed */ - if (force) { - error = git_reference_lookup(&existing_ref, ref->owner, new_name); + /* see if the reference already exists */ + if (reference_available(&ref_available, ref->owner, new_name, ref->name, error) < 0) + return -1; - if (error == GIT_SUCCESS) { - error = git_reference_delete(existing_ref); - if (error < GIT_SUCCESS) - return git__rethrow(error, - "Failed to rename reference. " - "The existing reference cannot be deleted"); - } else if (error != GIT_ENOTFOUND) - goto cleanup; - - /* If we're not forcing the rename, check if the reference exists. - * If it does, renaming cannot continue */ - } else { - int exists; - - error = reference_exists(&exists, ref->owner, normalized); - if (error < GIT_SUCCESS) - goto cleanup; - - if (exists) - return git__throw(GIT_EEXISTS, - "Failed to rename reference. Reference already exists"); + /* We cannot proceed if the reference already exists and we're not forcing + * the rename; the existing one would be overwritten */ + if (!force && !ref_available) { + giterr_set(error, GITERR_REFERENCE, + "A reference with the same name (%s) already exists", normalized); + return GIT_EEXISTS; } - if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS) - return git__rethrow(error, - "Failed to rename reference. Reference already exists"); + /* FIXME: if the reference exists and we are forcing, do we really need to + * remove the reference first? + * + * Two cases: + * + * - the reference already exists and is loose: not a problem, the file + * gets overwritten on disk + * + * - the reference already exists and is packed: we write a new one as + * loose, which by all means renders the packed one useless + */ /* Initialize path now so we won't get an allocation failure once * we actually start removing things. */ - error = git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name); - if (error < GIT_SUCCESS) - goto cleanup; + if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0) { + giterr_set_oom(error); + return -1; + } /* * Now delete the old ref and remove an possibly existing directory @@ -1351,12 +1323,12 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) * method deletes the ref from disk but doesn't free the pointer, so * we can still access the ref's attributes for creating the new one */ - if ((error = reference_delete(ref)) < GIT_SUCCESS) + if (reference_delete(ref, error) < 0) goto cleanup; if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) { if (git_path_isdir(aux_path.ptr) == GIT_SUCCESS) { - if ((error = git_futils_rmdir_r(aux_path.ptr, 0)) < GIT_SUCCESS) + if (git_futils_rmdir_r(aux_path.ptr, 0, error) < 0) goto rollback; } else goto rollback; } @@ -1365,43 +1337,48 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) * Finally we can create the new reference. */ if (ref->flags & GIT_REF_SYMBOLIC) { - error = git_reference_create_symbolic( - NULL, ref->owner, new_name, ref->target.symbolic, 0); + result = git_reference_create_symbolic( + NULL, ref->owner, new_name, ref->target.symbolic, force, error); } else { - error = git_reference_create_oid( - NULL, ref->owner, new_name, &ref->target.oid, 0); + result = git_reference_create_oid( + NULL, ref->owner, new_name, &ref->target.oid, force, error); } - if (error < GIT_SUCCESS) + if (result < 0) goto rollback; /* * Check if we have to update HEAD. */ - error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE); - if (error < GIT_SUCCESS) + if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE, NULL) < 0) { + giterr_set(error, GITERR_REFERENCE, + "Failed to update HEAD after renaming reference"); goto cleanup; + } head_target = git_reference_target(head); if (head_target && !strcmp(head_target, ref->name)) { - error = git_reference_create_symbolic( - &head, ref->owner, "HEAD", new_name, 1); - - if (error < GIT_SUCCESS) + if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1, NULL) < 0) { + giterr_set(error, GITERR_REFERENCE, + "Failed to update HEAD after renaming reference"); goto cleanup; + } } /* * Rename the reflog file. */ - error = git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, - GIT_REFLOG_DIR, ref->name); - if (error < GIT_SUCCESS) + if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, + GIT_REFLOG_DIR, ref->name) < 0) { + giterr_set_oom(error); goto cleanup; + } - if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) - error = git_reflog_rename(ref, new_name); + if (git_path_exists(aux_path.ptr) == 0) { + if (git_reflog_rename(ref, new_name, error) < 0) + goto cleanup; + } /* * Change the name of the reference given by the user. @@ -1412,38 +1389,37 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force) /* The reference is no longer packed */ ref->flags &= ~GIT_REF_PACKED; -cleanup: - /* We no longer need the newly created reference nor the head */ git_reference_free(head); git_buf_free(&aux_path); - return error == GIT_SUCCESS ? - GIT_SUCCESS : - git__rethrow(error, "Failed to rename reference"); + return 0; + +cleanup: + git_reference_free(head); + git_buf_free(&aux_path); + return -1; rollback: /* - * Try to create the old reference again. + * Try to create the old reference again, ignore failures */ if (ref->flags & GIT_REF_SYMBOLIC) - error = git_reference_create_symbolic( - NULL, ref->owner, ref->name, ref->target.symbolic, 0); + git_reference_create_symbolic( + NULL, ref->owner, ref->name, ref->target.symbolic, 0, NULL); else - error = git_reference_create_oid( - NULL, ref->owner, ref->name, &ref->target.oid, 0); + git_reference_create_oid( + NULL, ref->owner, ref->name, &ref->target.oid, 0. NULL); /* The reference is no longer packed */ ref->flags &= ~GIT_REF_PACKED; git_buf_free(&aux_path); - return error == GIT_SUCCESS ? - git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") : - git__rethrow(error, "Failed to rename reference. Failed to rollback"); + return -1; } -int git_reference_resolve(git_reference **ref_out, git_reference *ref) +int git_reference_resolve(git_reference **ref_out, git_reference *ref, git_error **error) { - int error, i = 0; + int result, i = 0; git_repository *repo; assert(ref); @@ -1455,15 +1431,15 @@ int git_reference_resolve(git_reference **ref_out, git_reference *ref) * copy. Instead of duplicating `ref`, we look it up again to * ensure the copy is out to date */ if (ref->flags & GIT_REF_OID) - return git_reference_lookup(ref_out, ref->owner, ref->name); + return git_reference_lookup(ref_out, ref->owner, ref->name, error); /* Otherwise, keep iterating until the reference is resolved */ for (i = 0; i < MAX_NESTING_LEVEL; ++i) { git_reference *new_ref; - error = git_reference_lookup(&new_ref, repo, ref->target.symbolic); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to resolve reference"); + result = git_reference_lookup(&new_ref, repo, ref->target.symbolic, error); + if (result < 0) + return result; /* Free intermediate references, except for the original one * we've received */ @@ -1480,33 +1456,30 @@ int git_reference_resolve(git_reference **ref_out, git_reference *ref) } } - return git__throw(GIT_ENOMEM, - "Failed to resolve reference. Reference is too nested"); + giterr_set(error, GITERR_REFERENCE, + "Symbolic reference too nested (%d levels deep)", MAX_NESTING_LEVEL); + + return -1; } -int git_reference_packall(git_repository *repo) +int git_reference_packall(git_repository *repo, git_error **error) { - int error; + if (packed_load(repo, error) < 0 || /* load the existing packfile */ + packed_loadloose(repo, error) < 0 || /* add all the loose refs */ + packed_write(repo, error) < 0) /* write back to disk */ + return -1; - /* load the existing packfile */ - if ((error = packed_load(repo)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to pack references"); - - /* update it in-memory with all the loose references */ - if ((error = packed_loadloose(repo)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to pack references"); - - /* write it back to disk */ - return packed_write(repo); + return 0; } int git_reference_foreach( git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), - void *payload) + void *payload, + git_error **error) { - int error; + int result; struct dirent_list_data data; git_buf refs_path = GIT_BUF_INIT; @@ -1514,13 +1487,12 @@ int git_reference_foreach( if (list_flags & GIT_REF_PACKED) { const char *ref_name; - if ((error = packed_load(repo)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to list references"); + if (packed_load(repo, error) < 0) + return -1; - GIT_HASHTABLE_FOREACH_KEY(repo->references.packfile, ref_name, - if ((error = callback(ref_name, payload)) < GIT_SUCCESS) - return git__throw(error, - "Failed to list references. User callback failed"); + GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused, + if (callback(ref_name, payload) < 0) + return 0; ); } @@ -1533,15 +1505,15 @@ int git_reference_foreach( data.callback = callback; data.callback_payload = payload; - if ((error = git_buf_joinpath(&refs_path, - repo->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to alloc space for references"); - - error = git_path_direach(&refs_path, _dirent_loose_listall, &data); + if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0) { + giterr_set_oom(error); + return -1; + } + result = git_path_direach(&refs_path, _dirent_loose_listall, &data, error); git_buf_free(&refs_path); - return error; + return result; } static int cb__reflist_add(const char *ref, void *data) @@ -1552,9 +1524,10 @@ static int cb__reflist_add(const char *ref, void *data) int git_reference_listall( git_strarray *array, git_repository *repo, - unsigned int list_flags) + unsigned int list_flags, + git_error **error) { - int error; + int result; git_vector ref_list; assert(array && repo); @@ -1562,15 +1535,15 @@ int git_reference_listall( array->strings = NULL; array->count = 0; - if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS) - return GIT_ENOMEM; + if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS) { + giterr_set_oom(error); + return -1; + } - error = git_reference_foreach( - repo, list_flags, &cb__reflist_add, (void *)&ref_list); - - if (error < GIT_SUCCESS) { + if (git_reference_foreach( + repo, list_flags, &cb__reflist_add, (void *)&ref_list, error) < 0) { git_vector_free(&ref_list); - return error; + return -1; } array->strings = (char **)ref_list.contents; @@ -1578,17 +1551,11 @@ int git_reference_listall( return GIT_SUCCESS; } -int git_reference_reload(git_reference *ref) +int git_reference_reload(git_reference *ref, git_error **error) { - int error = reference_lookup(ref); - - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to reload reference"); - - return GIT_SUCCESS; + return reference_lookup(ref, error); } - void git_repository__refcache_free(git_refcache *refs) { assert(refs);