refs: Error handling rework. WIP

This commit is contained in:
Vicent Martí 2012-02-15 16:54:17 +01:00
parent 60bc2d20c4
commit 45d387ac78
3 changed files with 259 additions and 284 deletions

View File

@ -122,9 +122,12 @@ typedef struct {
typedef enum { typedef enum {
GITERR_NOMEMORY, GITERR_NOMEMORY,
GITERR_REFERENCE,
} git_error_class; } 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(git_error **error_out, int error_class, const char *string, ...);
GIT_EXTERN(void) giterr_set_oom(git_error **error); GIT_EXTERN(void) giterr_set_oom(git_error **error);
GIT_EXTERN(void) giterr_free(git_error *error); GIT_EXTERN(void) giterr_free(git_error *error);

View File

@ -486,20 +486,23 @@ GIT_INLINE(int) is_dot_or_dotdot(const char *name)
int git_path_direach( int git_path_direach(
git_buf *path, git_buf *path,
int (*fn)(void *, git_buf *), int (*fn)(void *, git_buf *, git_error **),
void *arg) void *arg,
git_error **error)
{ {
ssize_t wd_len; ssize_t wd_len;
DIR *dir; DIR *dir;
struct dirent de_buf, *de; struct dirent de_buf, *de;
if (git_path_to_dir(path) < GIT_SUCCESS) if (git_path_to_dir(path, error) < 0)
return git_buf_lasterror(path); return -1;
wd_len = path->size; wd_len = path->size;
dir = opendir(path->ptr); dir = opendir(path->ptr);
if (!dir) if (!dir) {
return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr); 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) { while (p_readdir_r(dir, &de_buf, &de) == 0 && de != NULL) {
int result; int result;
@ -507,16 +510,18 @@ int git_path_direach(
if (is_dot_or_dotdot(de->d_name)) if (is_dot_or_dotdot(de->d_name))
continue; continue;
if (git_buf_puts(path, de->d_name) < GIT_SUCCESS) if (git_buf_puts(path, de->d_name) < 0) {
return git_buf_lasterror(path); giterr_set_oom(error);
return -1;
}
result = fn(arg, path); result = fn(arg, path, error);
git_buf_truncate(path, wd_len); /* restore path */ git_buf_truncate(path, wd_len); /* restore path */
if (result != GIT_SUCCESS) { if (result < 0) {
closedir(dir); closedir(dir);
return result; /* The callee is reponsible for setting the correct error message */ return -1;
} }
} }

View File

@ -62,7 +62,7 @@ static int packed_write(git_repository *repo);
/* internal helpers */ /* internal helpers */
static int reference_available(git_repository *repo, 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_delete(git_reference *ref);
static int reference_lookup(git_reference *ref); static int reference_lookup(git_reference *ref);
@ -521,7 +521,7 @@ struct dirent_list_data {
void *callback_payload; 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; struct dirent_list_data *data = (struct dirent_list_data *)_data;
const char *file_path = full_path->ptr + data->repo_path_len; const char *file_path = full_path->ptr + data->repo_path_len;
@ -844,49 +844,55 @@ cleanup:
return error; 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) static int _reference_available_cb(const char *ref, void *data)
{ {
const char *new, *old; struct reference_available_t *d;
const char **refs;
assert(ref && data); assert(ref && data);
d = (reference_available_t *)data;
refs = (const char **)data; refs = (const char **)data;
new = (const char *)refs[0]; if (!d->old_ref || strcmp(d->old_ref, ref)) {
old = (const char *)refs[1];
if (!old || strcmp(old, ref)) {
int reflen = strlen(ref); int reflen = strlen(ref);
int newlen = strlen(new); int newlen = strlen(d->new_ref);
int cmplen = reflen < newlen ? reflen : newlen; 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) && if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
lead[cmplen] == '/') d->available = 0;
return GIT_EEXISTS; return -1;
}
} }
return GIT_SUCCESS; return 0;
} }
static int reference_available( static int reference_available(
int *available,
git_repository *repo, git_repository *repo,
const char *ref, 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; data.new_ref = ref;
refs[1] = old_ref; data.old_ref = old_ref;
data.available = 1;
if (git_reference_foreach(repo, GIT_REF_LISTALL, if (git_reference_foreach(repo, GIT_REF_LISTALL,
_reference_available_cb, (void *)refs) < 0) { _reference_available_cb, (void *)&data, error) < 0)
return git__throw(GIT_EEXISTS, return -1;
"Reference name `%s` conflicts with existing reference", ref);
}
return GIT_SUCCESS; *available = data.available;
return 0;
} }
static int reference_exists(int *exists, git_repository *repo, const char *ref_name) 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; 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); result = loose_lookup(ref, error);
if (error_loose == GIT_SUCCESS) if (result != GIT_ENOTFOUND)
return GIT_SUCCESS; return result;
error_packed = packed_lookup(ref); result = packed_lookup(ref, error);
if (error_packed == GIT_SUCCESS) if (result != GIT_ENOTFOUND)
return GIT_SUCCESS; return result;
git_reference_free(ref); git_reference_free(ref);
@ -974,9 +980,9 @@ static int reference_lookup(git_reference *ref)
* This is an internal method; the reference is removed * This is an internal method; the reference is removed
* from disk or the packfile, but the pointer is not freed * 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); assert(ref);
@ -986,15 +992,19 @@ static int reference_delete(git_reference *ref)
if (ref->flags & GIT_REF_PACKED) { if (ref->flags & GIT_REF_PACKED) {
struct packref *packref; struct packref *packref;
/* load the existing packfile */ /* load the existing packfile */
if ((error = packed_load(ref->owner)) < GIT_SUCCESS) if (packed_load(ref->owner, error) < 0)
return git__rethrow(error, "Failed to delete reference"); return -1;
if (git_hashtable_remove2(ref->owner->references.packfile, if (git_hashtable_remove2(ref->owner->references.packfile,
ref->name, (void **) &packref) < GIT_SUCCESS) ref->name, (void **) &packref) < 0) {
return git__throw(GIT_ENOTFOUND, "Reference not found"); giterr_set(error, GITERR_REFERENCE,
"Reference %s stopped existing in the packfile", ref->name);
return -1;
}
git__free (packref); git__free(packref);
error = packed_write(ref->owner); if (packed_write(ref->owner, error) < 0)
return -1;
/* If the reference is loose, we can just remove the reference /* If the reference is loose, we can just remove the reference
* from the filesystem */ * from the filesystem */
@ -1002,66 +1012,55 @@ static int reference_delete(git_reference *ref)
git_reference *ref_in_pack; git_reference *ref_in_pack;
git_buf full_path = GIT_BUF_INIT; git_buf full_path = GIT_BUF_INIT;
error = git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name); if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0) {
if (error < GIT_SUCCESS) giterr_set_oom(error);
goto cleanup; 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 */ 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 /* When deleting a loose reference, we have to ensure that an older
* packed version of it doesn't exist */ * packed version of it doesn't exist */
if (git_reference_lookup(&ref_in_pack, ref->owner, if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name, NULL) == GIT_SUCCESS) {
ref->name) == GIT_SUCCESS) {
assert((ref_in_pack->flags & GIT_REF_PACKED) != 0); 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 0;
return error == GIT_SUCCESS ?
GIT_SUCCESS :
git__rethrow(error, "Failed to delete reference");
} }
int git_reference_delete(git_reference *ref) int git_reference_delete(git_reference *ref, git_error **error)
{ {
int error = reference_delete(ref); int result = reference_delete(ref, error);
if (error < GIT_SUCCESS)
return error;
git_reference_free(ref); git_reference_free(ref);
return GIT_SUCCESS; return result;
} }
int git_reference_lookup(git_reference **ref_out, 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]; char normalized_name[GIT_REFNAME_MAX];
git_reference *ref = NULL; git_reference *ref = NULL;
assert(ref_out && repo && name); 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 (reference_alloc(&ref, repo, normalized_name, error) < 0)
if (error < GIT_SUCCESS) return -1;
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");
*ref_out = ref; *ref_out = ref;
return GIT_SUCCESS; return reference_lookup(ref, error);
} }
/** /**
@ -1123,46 +1122,43 @@ int git_reference_create_symbolic(
git_repository *repo, git_repository *repo,
const char *name, const char *name,
const char *target, const char *target,
int force) int force,
git_error **error)
{ {
char normalized[GIT_REFNAME_MAX]; char normalized[GIT_REFNAME_MAX];
int ref_exists, error = GIT_SUCCESS; int exists;
git_reference *ref = NULL; git_reference *ref = NULL;
error = normalize_name(normalized, sizeof(normalized), name, 0); if (normalize_name(normalized, sizeof(normalized), name, 0, error) < 0)
if (error < GIT_SUCCESS) return -1;
goto cleanup;
if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS)) if (reference_exists(&exists, repo, normalized, error) < 0)
return git__rethrow(error, "Failed to create symbolic reference"); return -1;
if (ref_exists && !force) if (exists && !force) {
return git__throw(GIT_EEXISTS, giterr_set(error, GITERR_REFERENCE,
"Failed to create symbolic reference. Reference already exists"); "A reference with that name (%s) already exists");
return GIT_EEXISTS;
}
error = reference_alloc(&ref, repo, normalized); if (reference_alloc(&ref, repo, normalized, error) < 0)
if (error < GIT_SUCCESS) return -1;
goto cleanup;
ref->flags |= GIT_REF_SYMBOLIC; ref->flags |= GIT_REF_SYMBOLIC;
/* set the target; this will normalize the name automatically /* set the target; this will normalize the name automatically
* and write the reference on disk */ * and write the reference on disk */
error = git_reference_set_target(ref, target); if (git_reference_set_target(ref, target, error) < 0) {
if (error < GIT_SUCCESS) git_reference_free(ref);
goto cleanup; return -1;
}
if (ref_out == NULL) { if (ref_out == NULL) {
git_reference_free(ref); git_reference_free(ref);
} else { } else {
*ref_out = ref; *ref_out = ref;
} }
return GIT_SUCCESS; return 0;
cleanup:
git_reference_free(ref);
return git__rethrow(error, "Failed to create symbolic reference");
} }
int git_reference_create_oid( int git_reference_create_oid(
@ -1170,36 +1166,35 @@ int git_reference_create_oid(
git_repository *repo, git_repository *repo,
const char *name, const char *name,
const git_oid *id, const git_oid *id,
int force) int force,
git_error **error)
{ {
int error = GIT_SUCCESS, ref_exists; int exists;
git_reference *ref = NULL; git_reference *ref = NULL;
char normalized[GIT_REFNAME_MAX]; char normalized[GIT_REFNAME_MAX];
error = normalize_name(normalized, sizeof(normalized), name, 1); if (normalize_name(normalized, sizeof(normalized), name, 1, error) < 0)
if (error < GIT_SUCCESS) return -1;
goto cleanup;
if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS)) if (reference_exists(&exists, repo, normalized, error) < 0)
return git__rethrow(error, "Failed to create OID reference"); return -1;
if (ref_exists && !force) if (exists && !force) {
return git__throw(GIT_EEXISTS, giterr_set(error, GITERR_REFERENCE,
"Failed to create OID reference. Reference already exists"); "A reference with that name (%s) already exists");
return GIT_EEXISTS;
}
if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS) if (reference_alloc(&ref, repo, name, error) < 0)
return git__rethrow(error, "Failed to create reference"); return -1;
error = reference_alloc(&ref, repo, name);
if (error < GIT_SUCCESS)
goto cleanup;
ref->flags |= GIT_REF_OID; ref->flags |= GIT_REF_OID;
/* set the oid; this will write the reference on disk */ /* set the oid; this will write the reference on disk */
error = git_reference_set_oid(ref, id); if (git_reference_set_oid(ref, id, error) < 0) {
if (error < GIT_SUCCESS) git_reference_free(ref);
goto cleanup; return -1;
}
if (ref_out == NULL) { if (ref_out == NULL) {
git_reference_free(ref); git_reference_free(ref);
@ -1207,13 +1202,8 @@ int git_reference_create_oid(
*ref_out = ref; *ref_out = ref;
} }
return GIT_SUCCESS; return 0;
cleanup:
git_reference_free(ref);
return git__rethrow(error, "Failed to create reference OID");
} }
/* /*
* Change the OID target of a reference. * Change the OID target of a reference.
* *
@ -1223,40 +1213,34 @@ cleanup:
* We do not repack packed references because of performance * We do not repack packed references because of performance
* reasons. * 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; git_odb *odb = NULL;
if ((ref->flags & GIT_REF_OID) == 0) if ((ref->flags & GIT_REF_OID) == 0) {
return git__throw(GIT_EINVALIDREFSTATE, giterr_set(error, GITERR_REFERENCE,
"Failed to set OID target of reference. Not an OID reference"); "Cannot set OID on symbolic reference");
return -1;
}
assert(ref->owner); assert(ref->owner);
error = git_repository_odb__weakptr(&odb, ref->owner); if (git_repository_odb__weakptr(&odb, ref->owner, error) < 0)
if (error < GIT_SUCCESS) return -1;
return error;
exists = git_odb_exists(odb, id);
git_odb_free(odb);
/* Don't let the user create references to OIDs that /* Don't let the user create references to OIDs that
* don't exist in the ODB */ * don't exist in the ODB */
if (!exists) if (!git_odb_exists(odb, id)) {
return git__throw(GIT_ENOTFOUND, giterr_set(error, GITERR_REFERENCE,
"Failed to set OID target of reference. OID doesn't exist in ODB"); "Target OID for the reference doesn't exist on the repository");
return -1;
}
/* Update the OID value on `ref` */ /* Update the OID value on `ref` */
git_oid_cpy(&ref->target.oid, id); git_oid_cpy(&ref->target.oid, id);
/* Write back to disk */ /* Write back to disk */
error = loose_write(ref); return loose_write(ref, error);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to set OID target of reference");
return GIT_SUCCESS;
} }
/* /*
@ -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 * a pack. We just change the target in memory
* and overwrite the file on disk. * 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]; char normalized[GIT_REFNAME_MAX];
if ((ref->flags & GIT_REF_SYMBOLIC) == 0) if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {
return git__throw(GIT_EINVALIDREFSTATE, giterr_set(error, GITERR_REFERENCE,
"Failed to set reference target. Not a symbolic reference"); "Cannot set symbolic target on a direct reference");
return -1;
}
error = normalize_name(normalized, sizeof(normalized), target, 0); if (normalize_name(normalized, sizeof(normalized), target, 0, error))
if (error < GIT_SUCCESS) return -1;
return git__rethrow(error,
"Failed to set reference target. Invalid target name");
git__free(ref->target.symbolic); git__free(ref->target.symbolic);
ref->target.symbolic = git__strdup(normalized); ref->target.symbolic = git__strdup(normalized);
if (ref->target.symbolic == NULL) GITERR_CHECK_ALLOC(ref->target.symbolic, error);
return GIT_ENOMEM;
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; git_buf aux_path = GIT_BUF_INIT;
char normalized[GIT_REFNAME_MAX]; char normalized[GIT_REFNAME_MAX];
const char *head_target = NULL; const char *head_target = NULL;
git_reference *existing_ref = NULL, *head = NULL; git_reference *existing_ref = NULL, *head = NULL;
error = normalize_name(normalized, sizeof(normalized), if (normalize_name(normalized, sizeof(normalized),
new_name, ref->flags & GIT_REF_OID); new_name, ref->flags & GIT_REF_OID, error) < 0)
return -1;
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to rename reference. Invalid name");
new_name = normalized; new_name = normalized;
/* If we are forcing the rename, try to lookup a reference with the /* see if the reference already exists */
* new one. If the lookup succeeds, we need to delete that ref if (reference_available(&ref_available, ref->owner, new_name, ref->name, error) < 0)
* before the renaming can proceed */ return -1;
if (force) {
error = git_reference_lookup(&existing_ref, ref->owner, new_name);
if (error == GIT_SUCCESS) { /* We cannot proceed if the reference already exists and we're not forcing
error = git_reference_delete(existing_ref); * the rename; the existing one would be overwritten */
if (error < GIT_SUCCESS) if (!force && !ref_available) {
return git__rethrow(error, giterr_set(error, GITERR_REFERENCE,
"Failed to rename reference. " "A reference with the same name (%s) already exists", normalized);
"The existing reference cannot be deleted"); return GIT_EEXISTS;
} 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");
} }
if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS) /* FIXME: if the reference exists and we are forcing, do we really need to
return git__rethrow(error, * remove the reference first?
"Failed to rename reference. Reference already exists"); *
* 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 /* Initialize path now so we won't get an allocation failure once
* we actually start removing things. * we actually start removing things.
*/ */
error = git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name); if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0) {
if (error < GIT_SUCCESS) giterr_set_oom(error);
goto cleanup; return -1;
}
/* /*
* Now delete the old ref and remove an possibly existing directory * 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 * 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 * 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; goto cleanup;
if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) { if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) {
if (git_path_isdir(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; goto rollback;
} else 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. * Finally we can create the new reference.
*/ */
if (ref->flags & GIT_REF_SYMBOLIC) { if (ref->flags & GIT_REF_SYMBOLIC) {
error = git_reference_create_symbolic( result = git_reference_create_symbolic(
NULL, ref->owner, new_name, ref->target.symbolic, 0); NULL, ref->owner, new_name, ref->target.symbolic, force, error);
} else { } else {
error = git_reference_create_oid( result = git_reference_create_oid(
NULL, ref->owner, new_name, &ref->target.oid, 0); NULL, ref->owner, new_name, &ref->target.oid, force, error);
} }
if (error < GIT_SUCCESS) if (result < 0)
goto rollback; goto rollback;
/* /*
* Check if we have to update HEAD. * Check if we have to update HEAD.
*/ */
error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE); if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE, NULL) < 0) {
if (error < GIT_SUCCESS) giterr_set(error, GITERR_REFERENCE,
"Failed to update HEAD after renaming reference");
goto cleanup; goto cleanup;
}
head_target = git_reference_target(head); head_target = git_reference_target(head);
if (head_target && !strcmp(head_target, ref->name)) { if (head_target && !strcmp(head_target, ref->name)) {
error = git_reference_create_symbolic( if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1, NULL) < 0) {
&head, ref->owner, "HEAD", new_name, 1); giterr_set(error, GITERR_REFERENCE,
"Failed to update HEAD after renaming reference");
if (error < GIT_SUCCESS)
goto cleanup; goto cleanup;
}
} }
/* /*
* Rename the reflog file. * Rename the reflog file.
*/ */
error = git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name); GIT_REFLOG_DIR, ref->name) < 0) {
if (error < GIT_SUCCESS) giterr_set_oom(error);
goto cleanup; goto cleanup;
}
if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) if (git_path_exists(aux_path.ptr) == 0) {
error = git_reflog_rename(ref, new_name); if (git_reflog_rename(ref, new_name, error) < 0)
goto cleanup;
}
/* /*
* Change the name of the reference given by the user. * 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 */ /* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED; ref->flags &= ~GIT_REF_PACKED;
cleanup:
/* We no longer need the newly created reference nor the head */
git_reference_free(head); git_reference_free(head);
git_buf_free(&aux_path); git_buf_free(&aux_path);
return error == GIT_SUCCESS ? return 0;
GIT_SUCCESS :
git__rethrow(error, "Failed to rename reference"); cleanup:
git_reference_free(head);
git_buf_free(&aux_path);
return -1;
rollback: rollback:
/* /*
* Try to create the old reference again. * Try to create the old reference again, ignore failures
*/ */
if (ref->flags & GIT_REF_SYMBOLIC) if (ref->flags & GIT_REF_SYMBOLIC)
error = git_reference_create_symbolic( git_reference_create_symbolic(
NULL, ref->owner, ref->name, ref->target.symbolic, 0); NULL, ref->owner, ref->name, ref->target.symbolic, 0, NULL);
else else
error = git_reference_create_oid( git_reference_create_oid(
NULL, ref->owner, ref->name, &ref->target.oid, 0); NULL, ref->owner, ref->name, &ref->target.oid, 0. NULL);
/* The reference is no longer packed */ /* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED; ref->flags &= ~GIT_REF_PACKED;
git_buf_free(&aux_path); git_buf_free(&aux_path);
return error == GIT_SUCCESS ? return -1;
git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") :
git__rethrow(error, "Failed to rename reference. Failed to rollback");
} }
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; git_repository *repo;
assert(ref); 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 * copy. Instead of duplicating `ref`, we look it up again to
* ensure the copy is out to date */ * ensure the copy is out to date */
if (ref->flags & GIT_REF_OID) 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 */ /* Otherwise, keep iterating until the reference is resolved */
for (i = 0; i < MAX_NESTING_LEVEL; ++i) { for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
git_reference *new_ref; git_reference *new_ref;
error = git_reference_lookup(&new_ref, repo, ref->target.symbolic); result = git_reference_lookup(&new_ref, repo, ref->target.symbolic, error);
if (error < GIT_SUCCESS) if (result < 0)
return git__rethrow(error, "Failed to resolve reference"); return result;
/* Free intermediate references, except for the original one /* Free intermediate references, except for the original one
* we've received */ * we've received */
@ -1480,33 +1456,30 @@ int git_reference_resolve(git_reference **ref_out, git_reference *ref)
} }
} }
return git__throw(GIT_ENOMEM, giterr_set(error, GITERR_REFERENCE,
"Failed to resolve reference. Reference is too nested"); "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 */ return 0;
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);
} }
int git_reference_foreach( int git_reference_foreach(
git_repository *repo, git_repository *repo,
unsigned int list_flags, unsigned int list_flags,
int (*callback)(const char *, void *), int (*callback)(const char *, void *),
void *payload) void *payload,
git_error **error)
{ {
int error; int result;
struct dirent_list_data data; struct dirent_list_data data;
git_buf refs_path = GIT_BUF_INIT; git_buf refs_path = GIT_BUF_INIT;
@ -1514,13 +1487,12 @@ int git_reference_foreach(
if (list_flags & GIT_REF_PACKED) { if (list_flags & GIT_REF_PACKED) {
const char *ref_name; const char *ref_name;
if ((error = packed_load(repo)) < GIT_SUCCESS) if (packed_load(repo, error) < 0)
return git__rethrow(error, "Failed to list references"); return -1;
GIT_HASHTABLE_FOREACH_KEY(repo->references.packfile, ref_name, GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
if ((error = callback(ref_name, payload)) < GIT_SUCCESS) if (callback(ref_name, payload) < 0)
return git__throw(error, return 0;
"Failed to list references. User callback failed");
); );
} }
@ -1533,15 +1505,15 @@ int git_reference_foreach(
data.callback = callback; data.callback = callback;
data.callback_payload = payload; data.callback_payload = payload;
if ((error = git_buf_joinpath(&refs_path, if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0) {
repo->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS) giterr_set_oom(error);
return git__rethrow(error, "Failed to alloc space for references"); return -1;
}
error = git_path_direach(&refs_path, _dirent_loose_listall, &data);
result = git_path_direach(&refs_path, _dirent_loose_listall, &data, error);
git_buf_free(&refs_path); git_buf_free(&refs_path);
return error; return result;
} }
static int cb__reflist_add(const char *ref, void *data) 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( int git_reference_listall(
git_strarray *array, git_strarray *array,
git_repository *repo, git_repository *repo,
unsigned int list_flags) unsigned int list_flags,
git_error **error)
{ {
int error; int result;
git_vector ref_list; git_vector ref_list;
assert(array && repo); assert(array && repo);
@ -1562,15 +1535,15 @@ int git_reference_listall(
array->strings = NULL; array->strings = NULL;
array->count = 0; array->count = 0;
if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS) if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS) {
return GIT_ENOMEM; giterr_set_oom(error);
return -1;
}
error = git_reference_foreach( if (git_reference_foreach(
repo, list_flags, &cb__reflist_add, (void *)&ref_list); repo, list_flags, &cb__reflist_add, (void *)&ref_list, error) < 0) {
if (error < GIT_SUCCESS) {
git_vector_free(&ref_list); git_vector_free(&ref_list);
return error; return -1;
} }
array->strings = (char **)ref_list.contents; array->strings = (char **)ref_list.contents;
@ -1578,17 +1551,11 @@ int git_reference_listall(
return GIT_SUCCESS; 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); return reference_lookup(ref, error);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to reload reference");
return GIT_SUCCESS;
} }
void git_repository__refcache_free(git_refcache *refs) void git_repository__refcache_free(git_refcache *refs)
{ {
assert(refs); assert(refs);