diff options
Diffstat (limited to 'refs.c')
-rw-r--r-- | refs.c | 195 |
1 files changed, 150 insertions, 45 deletions
@@ -3243,9 +3243,9 @@ static struct ref_lock *update_ref_lock(const char *refname, if (!lock) { const char *str = "Cannot lock the ref '%s'."; switch (onerr) { - case MSG_ON_ERR: error(str, refname); break; - case DIE_ON_ERR: die(str, refname); break; - case QUIET_ON_ERR: break; + case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; + case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } } return lock; @@ -3258,15 +3258,118 @@ static int update_ref_write(const char *action, const char *refname, if (write_ref_sha1(lock, sha1, action) < 0) { const char *str = "Cannot update the ref '%s'."; switch (onerr) { - case MSG_ON_ERR: error(str, refname); break; - case DIE_ON_ERR: die(str, refname); break; - case QUIET_ON_ERR: break; + case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; + case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } return 1; } return 0; } +/** + * Information needed for a single ref update. Set new_sha1 to the + * new value or to zero to delete the ref. To check the old value + * while locking the ref, set have_old to 1 and set old_sha1 to the + * value or to zero to ensure the ref does not exist before update. + */ +struct ref_update { + unsigned char new_sha1[20]; + unsigned char old_sha1[20]; + int flags; /* REF_NODEREF? */ + int have_old; /* 1 if old_sha1 is valid, 0 otherwise */ + struct ref_lock *lock; + int type; + const char refname[FLEX_ARRAY]; +}; + +/* + * Data structure for holding a reference transaction, which can + * consist of checks and updates to multiple references, carried out + * as atomically as possible. This structure is opaque to callers. + */ +struct ref_transaction { + struct ref_update **updates; + size_t alloc; + size_t nr; +}; + +struct ref_transaction *ref_transaction_begin(void) +{ + return xcalloc(1, sizeof(struct ref_transaction)); +} + +static void ref_transaction_free(struct ref_transaction *transaction) +{ + int i; + + for (i = 0; i < transaction->nr; i++) + free(transaction->updates[i]); + + free(transaction->updates); + free(transaction); +} + +void ref_transaction_rollback(struct ref_transaction *transaction) +{ + ref_transaction_free(transaction); +} + +static struct ref_update *add_update(struct ref_transaction *transaction, + const char *refname) +{ + size_t len = strlen(refname); + struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1); + + strcpy((char *)update->refname, refname); + ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); + transaction->updates[transaction->nr++] = update; + return update; +} + +void ref_transaction_update(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, unsigned char *old_sha1, + int flags, int have_old) +{ + struct ref_update *update = add_update(transaction, refname); + + hashcpy(update->new_sha1, new_sha1); + update->flags = flags; + update->have_old = have_old; + if (have_old) + hashcpy(update->old_sha1, old_sha1); +} + +void ref_transaction_create(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, + int flags) +{ + struct ref_update *update = add_update(transaction, refname); + + assert(!is_null_sha1(new_sha1)); + hashcpy(update->new_sha1, new_sha1); + hashclr(update->old_sha1); + update->flags = flags; + update->have_old = 1; +} + +void ref_transaction_delete(struct ref_transaction *transaction, + const char *refname, + unsigned char *old_sha1, + int flags, int have_old) +{ + struct ref_update *update = add_update(transaction, refname); + + update->flags = flags; + update->have_old = have_old; + if (have_old) { + assert(!is_null_sha1(old_sha1)); + hashcpy(update->old_sha1, old_sha1); + } +} + int update_ref(const char *action, const char *refname, const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr) @@ -3282,7 +3385,7 @@ static int ref_update_compare(const void *r1, const void *r2) { const struct ref_update * const *u1 = r1; const struct ref_update * const *u2 = r2; - return strcmp((*u1)->ref_name, (*u2)->ref_name); + return strcmp((*u1)->refname, (*u2)->refname); } static int ref_update_reject_duplicates(struct ref_update **updates, int n, @@ -3290,15 +3393,15 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, { int i; for (i = 1; i < n; i++) - if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) { + if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) { const char *str = "Multiple updates for ref '%s' not allowed."; switch (onerr) { - case MSG_ON_ERR: - error(str, updates[i]->ref_name); break; - case DIE_ON_ERR: - die(str, updates[i]->ref_name); break; - case QUIET_ON_ERR: + case UPDATE_REFS_MSG_ON_ERR: + error(str, updates[i]->refname); break; + case UPDATE_REFS_DIE_ON_ERR: + die(str, updates[i]->refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } return 1; @@ -3306,26 +3409,21 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, return 0; } -int update_refs(const char *action, const struct ref_update **updates_orig, - int n, enum action_on_err onerr) +int ref_transaction_commit(struct ref_transaction *transaction, + const char *msg, enum action_on_err onerr) { int ret = 0, delnum = 0, i; - struct ref_update **updates; - int *types; - struct ref_lock **locks; const char **delnames; + int n = transaction->nr; + struct ref_update **updates = transaction->updates; - if (!updates_orig || !n) + if (!n) return 0; /* Allocate work space */ - updates = xmalloc(sizeof(*updates) * n); - types = xmalloc(sizeof(*types) * n); - locks = xcalloc(n, sizeof(*locks)); delnames = xmalloc(sizeof(*delnames) * n); /* Copy, sort, and reject duplicate refs */ - memcpy(updates, updates_orig, sizeof(*updates) * n); qsort(updates, n, sizeof(*updates), ref_update_compare); ret = ref_update_reject_duplicates(updates, n, onerr); if (ret) @@ -3333,35 +3431,44 @@ int update_refs(const char *action, const struct ref_update **updates_orig, /* Acquire all locks while verifying old values */ for (i = 0; i < n; i++) { - locks[i] = update_ref_lock(updates[i]->ref_name, - (updates[i]->have_old ? - updates[i]->old_sha1 : NULL), - updates[i]->flags, - &types[i], onerr); - if (!locks[i]) { + struct ref_update *update = updates[i]; + + update->lock = update_ref_lock(update->refname, + (update->have_old ? + update->old_sha1 : NULL), + update->flags, + &update->type, onerr); + if (!update->lock) { ret = 1; goto cleanup; } } /* Perform updates first so live commits remain referenced */ - for (i = 0; i < n; i++) - if (!is_null_sha1(updates[i]->new_sha1)) { - ret = update_ref_write(action, - updates[i]->ref_name, - updates[i]->new_sha1, - locks[i], onerr); - locks[i] = NULL; /* freed by update_ref_write */ + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if (!is_null_sha1(update->new_sha1)) { + ret = update_ref_write(msg, + update->refname, + update->new_sha1, + update->lock, onerr); + update->lock = NULL; /* freed by update_ref_write */ if (ret) goto cleanup; } + } /* Perform deletes now that updates are safely completed */ - for (i = 0; i < n; i++) - if (locks[i]) { - delnames[delnum++] = locks[i]->ref_name; - ret |= delete_ref_loose(locks[i], types[i]); + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if (update->lock) { + delnames[delnum++] = update->lock->ref_name; + ret |= delete_ref_loose(update->lock, update->type); } + } + ret |= repack_without_refs(delnames, delnum); for (i = 0; i < delnum; i++) unlink_or_warn(git_path("logs/%s", delnames[i])); @@ -3369,12 +3476,10 @@ int update_refs(const char *action, const struct ref_update **updates_orig, cleanup: for (i = 0; i < n; i++) - if (locks[i]) - unlock_ref(locks[i]); - free(updates); - free(types); - free(locks); + if (updates[i]->lock) + unlock_ref(updates[i]->lock); free(delnames); + ref_transaction_free(transaction); return ret; } |