ref_transaction_prepare(): new optional step for reference updates
In the future, compound reference stores will sometimes need to modify references in two different reference stores at the same time, meaning that a single logical reference transaction might have to be implemented as two internal sub-transactions. They won't want to call `ref_transaction_commit()` for the two sub-transactions one after the other, because that wouldn't be atomic (the first commit could succeed and the second one fail). Instead, they will want to prepare both sub-transactions (i.e., obtain any necessary locks and do any pre-checks), and only if both prepare steps succeed, then commit both sub-transactions. Start preparing for that day by adding a new, optional `ref_transaction_prepare()` step to the reference transaction sequence, which obtains the locks and does any prechecks, reporting any errors that occur. Also add a `ref_transaction_abort()` function that can be used to abort a sub-transaction even if it has already been prepared. That is on the side of the public-facing API. On the side of the `ref_store` VTABLE, get rid of `transaction_commit` and instead add methods `transaction_prepare`, `transaction_finish`, and `transaction_abort`. A `ref_transaction_commit()` now basically calls methods `transaction_prepare` then `transaction_finish`. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:

committed by
Junio C Hamano

parent
8d4240d3c8
commit
30173b8851
74
refs.c
74
refs.c
@ -853,6 +853,19 @@ void ref_transaction_free(struct ref_transaction *transaction)
|
||||
if (!transaction)
|
||||
return;
|
||||
|
||||
switch (transaction->state) {
|
||||
case REF_TRANSACTION_OPEN:
|
||||
case REF_TRANSACTION_CLOSED:
|
||||
/* OK */
|
||||
break;
|
||||
case REF_TRANSACTION_PREPARED:
|
||||
die("BUG: free called on a prepared reference transaction");
|
||||
break;
|
||||
default:
|
||||
die("BUG: unexpected reference transaction state");
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < transaction->nr; i++) {
|
||||
free(transaction->updates[i]->msg);
|
||||
free(transaction->updates[i]);
|
||||
@ -1689,8 +1702,8 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
|
||||
refs_heads_master, logmsg);
|
||||
}
|
||||
|
||||
int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
struct strbuf *err)
|
||||
int ref_transaction_prepare(struct ref_transaction *transaction,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_store *refs = transaction->ref_store;
|
||||
|
||||
@ -1698,6 +1711,9 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
case REF_TRANSACTION_OPEN:
|
||||
/* Good. */
|
||||
break;
|
||||
case REF_TRANSACTION_PREPARED:
|
||||
die("BUG: prepare called twice on reference transaction");
|
||||
break;
|
||||
case REF_TRANSACTION_CLOSED:
|
||||
die("BUG: prepare called on a closed reference transaction");
|
||||
break;
|
||||
@ -1712,7 +1728,59 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
return -1;
|
||||
}
|
||||
|
||||
return refs->be->transaction_commit(refs, transaction, err);
|
||||
return refs->be->transaction_prepare(refs, transaction, err);
|
||||
}
|
||||
|
||||
int ref_transaction_abort(struct ref_transaction *transaction,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_store *refs = transaction->ref_store;
|
||||
int ret = 0;
|
||||
|
||||
switch (transaction->state) {
|
||||
case REF_TRANSACTION_OPEN:
|
||||
/* No need to abort explicitly. */
|
||||
break;
|
||||
case REF_TRANSACTION_PREPARED:
|
||||
ret = refs->be->transaction_abort(refs, transaction, err);
|
||||
break;
|
||||
case REF_TRANSACTION_CLOSED:
|
||||
die("BUG: abort called on a closed reference transaction");
|
||||
break;
|
||||
default:
|
||||
die("BUG: unexpected reference transaction state");
|
||||
break;
|
||||
}
|
||||
|
||||
ref_transaction_free(transaction);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_store *refs = transaction->ref_store;
|
||||
int ret;
|
||||
|
||||
switch (transaction->state) {
|
||||
case REF_TRANSACTION_OPEN:
|
||||
/* Need to prepare first. */
|
||||
ret = ref_transaction_prepare(transaction, err);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case REF_TRANSACTION_PREPARED:
|
||||
/* Fall through to finish. */
|
||||
break;
|
||||
case REF_TRANSACTION_CLOSED:
|
||||
die("BUG: commit called on a closed reference transaction");
|
||||
break;
|
||||
default:
|
||||
die("BUG: unexpected reference transaction state");
|
||||
break;
|
||||
}
|
||||
|
||||
return refs->be->transaction_finish(refs, transaction, err);
|
||||
}
|
||||
|
||||
int refs_verify_refname_available(struct ref_store *refs,
|
||||
|
Reference in New Issue
Block a user