aboutsummaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
authorMichael Haggerty <mhagger@alum.mit.edu>2017-05-22 16:17:44 +0200
committerJunio C Hamano <gitster@pobox.com>2017-05-23 14:29:55 +0900
commit30173b8851bb7203de938a638386cb9e6d7c501b (patch)
tree98d17ab75bd17c47a78266a704d82a1554414b3c /refs.c
parent8d4240d3c8a2d31b7bedda8408c0b3c217c76998 (diff)
downloadgit-30173b8851bb7203de938a638386cb9e6d7c501b.tar.gz
git-30173b8851bb7203de938a638386cb9e6d7c501b.tar.xz
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>
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c74
1 files changed, 71 insertions, 3 deletions
diff --git a/refs.c b/refs.c
index f8f41ffb0..b3860a9e3 100644
--- a/refs.c
+++ b/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,