Break the function `ref_transaction_commit()` into two functions,
`ref_transaction_prepare()` and `ref_transaction_finish()`, with a
third function, `ref_transaction_abort()`, that can be used to abort
the transaction. Break up the `ref_store` methods similarly.
This split will make it easier to implement compound reference stores,
where a transaction might have to span multiple underlying stores. In
that case, we would first want to "prepare" the sub-transactions in
each of the reference stores. If any of the "prepare" steps fails, we
would "abort" all of the sub-transactions. Only if all of the
"prepare" steps succeed would we "finish" each of them.
Signed-off-by: Michael Haggerty
---
refs.c | 34 ++---
refs.h | 37 ++-
refs/files-backend.c | 71 +---
refs/refs-internal.h | 42 ---
4 files changed, 157 insertions(+), 27 deletions(-)
diff --git a/refs.c b/refs.c
index 14dec88e7f..689362db1e 100644
--- a/refs.c
+++ b/refs.c
@@ -1689,8 +1689,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;
@@ -1700,7 +1700,35 @@ 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_finish(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = transaction->ref_store;
+
+ return refs->be->transaction_finish(refs, transaction, err);
+}
+
+int ref_transaction_abort(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = transaction->ref_store;
+
+ return refs->be->transaction_abort(refs, transaction, err);
+}
+
+int ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ int ret;
+
+ ret = ref_transaction_prepare(transaction, err);
+ if (ret)
+ return ret;
+
+ return ref_transaction_finish(transaction, err);
}
int refs_verify_refname_available(struct ref_store *refs,
diff --git a/refs.h b/refs.h
index b2d9626be6..4139e917f5 100644
--- a/refs.h
+++ b/refs.h
@@ -525,7 +525,10 @@ int ref_transaction_verify(struct ref_transaction
*transaction,
/*
* Commit all of the changes that have been queued in transaction, as
- * atomically as possible.
+ * atomically as possible. This is equivalent to calling
+ * `ref_transaction_prepare()` then `ref_transaction_finish()` (see
+ * below), which you can call yourself if you want finer control over
+ * reference updates.
*
* Returns 0 for success, or one of the below error codes for errors.
*/
@@ -536,6 +539,38 @@ int ref_transaction_verify(struct ref_transaction
*transaction,
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
+/*
+ * Perform the preparatory stages of commiting `transaction`. Acquire
+ * any needed locks, check preconditions, etc.; basically, do as much
+ * as possible to ensure that the transaction will be able to go
+ * through, stopping just short of making any irrevocable or
+ * user-visible changes. Anything that this function does should be
+ * able to be finished up by calling `ref_transaction_finish()` or
+ * rolled back by calling `ref_transaction_abort()`.
+ *
+ * On success, return 0 and leave the transaction in "prepared" state.
+ * On failure, abort the transaction and return one of the
+ * `TRANSACTION_*` constants.
+ *
+ * Callers who don't need such fine-grained control over commiting
+ * reference transactions should just call `ref_transaction_commit()`.
+ */
+int ref_transaction_prepare(struct ref_transaction *transaction,
+ struct strbuf *err);
+
+/*
+ * Perform the final commit of `transaction`, which has already been
+ * prepared.
+ */
+int ref_transaction_finish(struct ref_transaction *transaction,
+ struct strbuf *err);
+
+/*
+ * Abort `transaction`, which has already been prepared.
+ */
+int ref_transaction_abort(struct ref_transaction *transaction,
+ struct strbuf *err);
+
/*
* Like ref_transaction_commit(), but optimized for creating
* references when originally initializing a repository (e.g., by "git
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 7ddd4f87d5..f48409132d 100644
---