refs: add a concept of a reference transaction

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 beginning a transaction, adding updates to a transaction, and committing/rolling back a transaction. This API will soon replace update_refs(). Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Apr 7, 2014 at 15:48 UTC caa4046c4f480ceae5afb20e3172a437865cc51f
2 files changed +164
refs.c
+99
@@ -3267,6 +3267,96 @@ static int update_ref_write(const char *action, const char *refname,
3267 return 0;
3268 }
3269
3270 +/*
3271 + * Data structure for holding a reference transaction, which can
3272 + * consist of checks and updates to multiple references, carried out
3273 + * as atomically as possible. This structure is opaque to callers.
3274 + */
3275 +struct ref_transaction {
3276 + struct ref_update **updates;
3277 + size_t alloc;
3278 + size_t nr;
3279 +};
3280 +
3281 +struct ref_transaction *ref_transaction_begin(void)
3282 +{
3283 + return xcalloc(1, sizeof(struct ref_transaction));
3284 +}
3285 +
3286 +static void ref_transaction_free(struct ref_transaction *transaction)
3287 +{
3288 + int i;
3289 +
3290 + for (i = 0; i < transaction->nr; i++) {
3291 + struct ref_update *update = transaction->updates[i];
3292 +
3293 + free((char *)update->ref_name);
3294 + free(update);
3295 + }
3296 +
3297 + free(transaction->updates);
3298 + free(transaction);
3299 +}
3300 +
3301 +void ref_transaction_rollback(struct ref_transaction *transaction)
3302 +{
3303 + ref_transaction_free(transaction);
3304 +}
3305 +
3306 +static struct ref_update *add_update(struct ref_transaction *transaction,
3307 + const char *refname)
3308 +{
3309 + struct ref_update *update = xcalloc(1, sizeof(*update));
3310 +
3311 + update->ref_name = xstrdup(refname);
3312 + ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
3313 + transaction->updates[transaction->nr++] = update;
3314 + return update;
3315 +}
3316 +
3317 +void ref_transaction_update(struct ref_transaction *transaction,
3318 + const char *refname,
3319 + unsigned char *new_sha1, unsigned char *old_sha1,
3320 + int flags, int have_old)
3321 +{
3322 + struct ref_update *update = add_update(transaction, refname);
3323 +
3324 + hashcpy(update->new_sha1, new_sha1);
3325 + update->flags = flags;
3326 + update->have_old = have_old;
3327 + if (have_old)
3328 + hashcpy(update->old_sha1, old_sha1);
3329 +}
3330 +
3331 +void ref_transaction_create(struct ref_transaction *transaction,
3332 + const char *refname,
3333 + unsigned char *new_sha1,
3334 + int flags)
3335 +{
3336 + struct ref_update *update = add_update(transaction, refname);
3337 +
3338 + assert(!is_null_sha1(new_sha1));
3339 + hashcpy(update->new_sha1, new_sha1);
3340 + hashclr(update->old_sha1);
3341 + update->flags = flags;
3342 + update->have_old = 1;
3343 +}
3344 +
3345 +void ref_transaction_delete(struct ref_transaction *transaction,
3346 + const char *refname,
3347 + unsigned char *old_sha1,
3348 + int flags, int have_old)
3349 +{
3350 + struct ref_update *update = add_update(transaction, refname);
3351 +
3352 + update->flags = flags;
3353 + update->have_old = have_old;
3354 + if (have_old) {
3355 + assert(!is_null_sha1(old_sha1));
3356 + hashcpy(update->old_sha1, old_sha1);
3357 + }
3358 +}
3359 +
3360 int update_ref(const char *action, const char *refname,
3361 const unsigned char *sha1, const unsigned char *oldval,
3362 int flags, enum action_on_err onerr)
@@ -3378,6 +3468,15 @@ cleanup:
3468 return ret;
3469 }
3470
3471 +int ref_transaction_commit(struct ref_transaction *transaction,
3472 + const char *msg, enum action_on_err onerr)
3473 +{
3474 + int ret = update_refs(msg, transaction->updates, transaction->nr,
3475 + onerr);
3476 + ref_transaction_free(transaction);
3477 + return ret;
3478 +}
3479 +
3480 char *shorten_unambiguous_ref(const char *refname, int strict)
3481 {
3482 int i;
refs.h
+65
@@ -24,6 +24,8 @@ struct ref_update {
24 int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
25 };
26
27 +struct ref_transaction;
28 +
29 /*
30 * Bit values set in the flags argument passed to each_ref_fn():
31 */
@@ -220,6 +222,69 @@ enum action_on_err {
222 UPDATE_REFS_QUIET_ON_ERR
223 };
224
225 +/*
226 + * Begin a reference transaction. The reference transaction must
227 + * eventually be commited using ref_transaction_commit() or rolled
228 + * back using ref_transaction_rollback().
229 + */
230 +struct ref_transaction *ref_transaction_begin(void);
231 +
232 +/*
233 + * Roll back a ref_transaction and free all associated data.
234 + */
235 +void ref_transaction_rollback(struct ref_transaction *transaction);
236 +
237 +
238 +/*
239 + * The following functions add a reference check or update to a
240 + * ref_transaction. In all of them, refname is the name of the
241 + * reference to be affected. The functions make internal copies of
242 + * refname, so the caller retains ownership of the parameter. flags
243 + * can be REF_NODEREF; it is passed to update_ref_lock().
244 + */
245 +
246 +
247 +/*
248 + * Add a reference update to transaction. new_sha1 is the value that
249 + * the reference should have after the update, or zeros if it should
250 + * be deleted. If have_old is true, then old_sha1 holds the value
251 + * that the reference should have had before the update, or zeros if
252 + * it must not have existed beforehand.
253 + */
254 +void ref_transaction_update(struct ref_transaction *transaction,
255 + const char *refname,
256 + unsigned char *new_sha1, unsigned char *old_sha1,
257 + int flags, int have_old);
258 +
259 +/*
260 + * Add a reference creation to transaction. new_sha1 is the value
261 + * that the reference should have after the update; it must not be the
262 + * null SHA-1. It is verified that the reference does not exist
263 + * already.
264 + */
265 +void ref_transaction_create(struct ref_transaction *transaction,
266 + const char *refname,
267 + unsigned char *new_sha1,
268 + int flags);
269 +
270 +/*
271 + * Add a reference deletion to transaction. If have_old is true, then
272 + * old_sha1 holds the value that the reference should have had before
273 + * the update (which must not be the null SHA-1).
274 + */
275 +void ref_transaction_delete(struct ref_transaction *transaction,
276 + const char *refname,
277 + unsigned char *old_sha1,
278 + int flags, int have_old);
279 +
280 +/*
281 + * Commit all of the changes that have been queued in transaction, as
282 + * atomically as possible. Return a nonzero value if there is a
283 + * problem. The ref_transaction is freed by this function.
284 + */
285 +int ref_transaction_commit(struct ref_transaction *transaction,
286 + const char *msg, enum action_on_err onerr);
287 +
288 /** Lock a ref and then write its file */
289 int update_ref(const char *action, const char *refname,
290 const unsigned char *sha1, const unsigned char *oldval,