Build out the API for dealing with a bunch of reference checks and
changes within a transaction.  Define an opaque ref_transaction type
that is managed entirely within refs.c.  Introduce functions for
starting a transaction, adding updates to a transaction, and
committing a transaction.

This API will soon replace update_refs().

Signed-off-by: Michael Haggerty <mhag...@alum.mit.edu>
---
 refs.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 refs.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 148 insertions(+)

diff --git a/refs.c b/refs.c
index 0963077..54260ce 100644
--- a/refs.c
+++ b/refs.c
@@ -3267,6 +3267,85 @@ static int update_ref_write(const char *action, const 
char *refname,
        return 0;
 }
 
+/*
+ * 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 *create_ref_transaction(void)
+{
+       return xcalloc(1, sizeof(struct ref_transaction));
+}
+
+void free_ref_transaction(struct ref_transaction *transaction)
+{
+       int i;
+
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+
+               free((char *)update->ref_name);
+               free(update);
+       }
+
+       free(transaction->updates);
+       free(transaction);
+}
+
+static struct ref_update *add_update(struct ref_transaction *transaction,
+                                    const char *refname)
+{
+       struct ref_update *update = xcalloc(1, sizeof(*update));
+
+       update->ref_name = xstrdup(refname);
+       ALLOC_GROW(transaction->updates, transaction->nr + 1, 
transaction->alloc);
+       transaction->updates[transaction->nr++] = update;
+       return update;
+}
+
+void queue_update_ref(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 queue_create_ref(struct ref_transaction *transaction, const char *refname,
+                     unsigned char *new_sha1,
+                     int flags)
+{
+       struct ref_update *update = add_update(transaction, refname);
+
+       hashcpy(update->new_sha1, new_sha1);
+       hashclr(update->old_sha1);
+       update->flags = flags;
+       update->have_old = 1;
+}
+
+void queue_delete_ref(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)
+               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)
@@ -3378,6 +3457,12 @@ cleanup:
        return ret;
 }
 
+int commit_ref_transaction(struct ref_transaction *transaction,
+                          const char *msg, enum action_on_err onerr)
+{
+       return update_refs(msg, transaction->updates, transaction->nr, onerr);
+}
+
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
        int i;
diff --git a/refs.h b/refs.h
index 08e60ac..2848fb7 100644
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,8 @@ struct ref_update {
        int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 };
 
+struct ref_transaction;
+
 /*
  * Bit values set in the flags argument passed to each_ref_fn():
  */
@@ -220,6 +222,67 @@ enum action_on_err {
        UPDATE_REFS_QUIET_ON_ERR
 };
 
+/*
+ * Allocate and initialize a ref_transaction object.  The object must
+ * be freed by calling free_ref_transaction().
+ */
+struct ref_transaction *create_ref_transaction(void);
+
+/*
+ * Free a ref_transaction and all associated data.  This function does
+ * not commit the transaction; that must be done first (if desired) by
+ * calling commit_ref_transaction().
+ */
+void free_ref_transaction(struct ref_transaction *transaction);
+
+
+/*
+ * The following functions add a reference check or update to a
+ * ref_transaction.  In all of them, refname is the name of the
+ * reference to be affected.  The functions make internal copies of
+ * refname, so the caller retains ownership of the parameter.  flags
+ * can be REF_NODEREF; it is passed to update_ref_lock().
+ */
+
+
+/*
+ * Add a reference update to transaction.  new_sha1 is the value that
+ * the reference should have after the update, or zeros if it should
+ * be deleted.  If have_old is true, then old_sha1 holds the value
+ * that the reference should have had before the update, or zeros if
+ * it must not have existed beforehand.
+ */
+void queue_update_ref(struct ref_transaction *transaction, const char *refname,
+                     unsigned char *new_sha1, unsigned char *old_sha1,
+                     int flags, int have_old);
+
+/*
+ * Add a reference creation to transaction.  new_sha1 is the value
+ * that the reference should have after the update, or zeros if it
+ * should be deleted.  It is verified that the reference does not
+ * exist already.
+ */
+void queue_create_ref(struct ref_transaction *transaction, const char *refname,
+                     unsigned char *new_sha1,
+                     int flags);
+
+/*
+ * Add a reference deletion to transaction.  If have_old is true, then
+ * old_sha1 holds the value that the reference should have had before
+ * the update.
+ */
+void queue_delete_ref(struct ref_transaction *transaction, const char *refname,
+                     unsigned char *old_sha1,
+                     int flags, int have_old);
+
+/*
+ * Commit all of the changes that have been queued in transaction, as
+ * atomically as possible.  Return a nonzero value if there is a
+ * problem.  The transaction is unmodified by this function.
+ */
+int commit_ref_transaction(struct ref_transaction *transaction,
+                          const char *msg, enum action_on_err onerr);
+
 /** Lock a ref and then write its file */
 int update_ref(const char *action, const char *refname,
                const unsigned char *sha1, const unsigned char *oldval,
-- 
1.9.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to