summaryrefslogtreecommitdiff
path: root/src/refs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/refs.c')
-rw-r--r--src/refs.c192
1 files changed, 22 insertions, 170 deletions
diff --git a/src/refs.c b/src/refs.c
index a8583de19..c60e042d9 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -98,122 +98,9 @@ void git_reference_free(git_reference *reference)
git__free(reference);
}
-struct reference_available_t {
- const char *new_ref;
- const char *old_ref;
- int available;
-};
-
-static int _reference_available_cb(const char *refname, void *data)
-{
- struct reference_available_t *d;
-
- assert(refname && data);
- d = (struct reference_available_t *)data;
-
- if (!d->old_ref || strcmp(d->old_ref, refname)) {
- size_t reflen = strlen(refname);
- size_t newlen = strlen(d->new_ref);
- size_t cmplen = reflen < newlen ? reflen : newlen;
- const char *lead = reflen < newlen ? d->new_ref : refname;
-
- if (!strncmp(d->new_ref, refname, cmplen) && lead[cmplen] == '/') {
- d->available = 0;
- return -1;
- }
- }
-
- return 0;
-}
-
-/**
- * TODO: this should be part of the FS backend
- */
-static int reference_path_available(
- git_repository *repo,
- const char *ref,
- const char* old_ref)
-{
- int error;
- struct reference_available_t data;
-
- data.new_ref = ref;
- data.old_ref = old_ref;
- data.available = 1;
-
- error = git_reference_foreach_name(repo, _reference_available_cb, (void *)&data);
- if (error < 0)
- return error;
-
- if (!data.available) {
- giterr_set(GITERR_REFERENCE,
- "The path to reference '%s' collides with an existing one", ref);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Check if a reference could be written to disk, based on:
- *
- * - Whether a reference with the same name already exists,
- * and we are allowing or disallowing overwrites
- *
- * - Whether the name of the reference would collide with
- * an existing path
- */
-static int reference_can_write(
- git_repository *repo,
- const char *refname,
- const char *previous_name,
- int force)
-{
- git_refdb *refdb;
-
- if (git_repository_refdb__weakptr(&refdb, repo) < 0)
- return -1;
-
- /* see if the reference shares a path with an existing reference;
- * if a path is shared, we cannot create the reference, even when forcing */
- if (reference_path_available(repo, refname, previous_name) < 0)
- return -1;
-
- /* check if the reference actually exists, but only if we are not forcing
- * the rename. If we are forcing, it's OK to overwrite */
- if (!force) {
- int exists;
-
- if (git_refdb_exists(&exists, refdb, refname) < 0)
- return -1;
-
- /* We cannot proceed if the reference already exists and we're not forcing
- * the rename; the existing one would be overwritten */
- if (exists) {
- giterr_set(GITERR_REFERENCE,
- "A reference with that name (%s) already exists", refname);
- return GIT_EEXISTS;
- }
- }
-
- /* 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
- */
-
- return 0;
-}
-
int git_reference_delete(git_reference *ref)
{
- return git_refdb_delete(ref->db, ref);
+ return git_refdb_delete(ref->db, ref->name);
}
int git_reference_lookup(git_reference **ref_out,
@@ -420,23 +307,24 @@ static int reference__create(
if (ref_out)
*ref_out = NULL;
- if ((error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name)) < 0 ||
- (error = reference_can_write(repo, normalized, NULL, force)) < 0 ||
- (error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name);
+ if (error < 0)
+ return error;
+
+ error = git_repository_refdb__weakptr(&refdb, repo);
+ if (error < 0)
return error;
if (oid != NULL) {
assert(symbolic == NULL);
- ref = git_reference__alloc(name, oid, NULL);
+ ref = git_reference__alloc(normalized, oid, NULL);
} else {
- ref = git_reference__alloc_symbolic(name, symbolic);
+ ref = git_reference__alloc_symbolic(normalized, symbolic);
}
- /* TODO: this needs to be written more explicitly */
GITERR_CHECK_ALLOC(ref);
- ref->db = refdb;
- if ((error = git_refdb_write(refdb, ref)) < 0) {
+ if ((error = git_refdb_write(refdb, ref, force)) < 0) {
git_reference_free(ref);
return error;
}
@@ -533,77 +421,41 @@ int git_reference_rename(
unsigned int normalization_flags;
char normalized[GIT_REFNAME_MAX];
bool should_head_be_updated = false;
- git_reference *result = NULL;
int error = 0;
int reference_has_log;
- *out = NULL;
-
normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
if ((error = git_reference_normalize_name(
- normalized, sizeof(normalized), new_name, normalization_flags)) < 0 ||
- (error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0)
+ normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
return error;
- /*
- * Create the new reference.
- */
- if (ref->type == GIT_REF_OID) {
- result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel);
- } else if (ref->type == GIT_REF_SYMBOLIC) {
- result = git_reference__alloc_symbolic(new_name, ref->target.symbolic);
- } else {
- assert(0);
- }
-
- if (result == NULL)
- return -1;
-
- /* TODO: this is bad */
- result->db = ref->db;
-
/* Check if we have to update HEAD. */
if ((error = git_branch_is_head(ref)) < 0)
- goto on_error;
+ return error;
should_head_be_updated = (error > 0);
- /* Now delete the old ref and save the new one. */
- if ((error = git_refdb_delete(ref->db, ref)) < 0)
- goto on_error;
-
- /* Save the new reference. */
- if ((error = git_refdb_write(ref->db, result)) < 0)
- goto rollback;
+ if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0)
+ return error;
/* Update HEAD it was poiting to the reference being renamed. */
- if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
+ if (should_head_be_updated &&
+ (error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
- goto on_error;
+ return error;
}
/* Rename the reflog file, if it exists. */
reference_has_log = git_reference_has_log(ref);
- if (reference_has_log < 0) {
- error = reference_has_log;
- goto on_error;
- }
- if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
- goto on_error;
-
- *out = result;
-
- return error;
+ if (reference_has_log < 0)
+ return reference_has_log;
-rollback:
- git_refdb_write(ref->db, ref);
-
-on_error:
- git_reference_free(result);
+ if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
+ return error;
- return error;
+ return 0;
}
int git_reference_resolve(git_reference **ref_out, const git_reference *ref)