git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v2 19/27] refs: Add a concept of a reference transaction

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Apr 2, 2014, 04:57 UTC
Message-ID
<533B98A0.7030305@alum.mit.edu>
In-Reply-To
<xmqqtxaczvod.fsf@gitster.dls.corp.google.com>
On 04/01/2014 09:39 PM, Junio C Hamano wrote:
Show 34 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> 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>
>> ---
>>  refs.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  refs.h | 65 +++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 161 insertions(+)
>>
>> diff --git a/refs.c b/refs.c
>> index 1305eb1..e788c27 100644
>> --- a/refs.c
>> +++ b/refs.c
>> @@ -3267,6 +3267,93 @@ 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;
> 
> Don't we try to name an array update[] (not plural updates[]) so
> that we can say update[7] to mean the seventh update?

I know that some people prefer to name their arrays using singular, but I wasn't aware that this is a project rule. If it is, I think it is a bad rule. If followed, it would basically rule out the use of plural nouns as identifiers, and why would we want to deprive ourselves of the ability to use the singular/plural distinction to help clarify our code?

I like to use plural names to make it clear that an identifier refers to a collection of objects. This leaves the singular noun available to refer to single objects taken out of the aggregate like

    for (i = 0; i < nr; i++) {
            struct ref_update *update = updates[i];
            /* ... work with update... */
    }

The singular/plural distinction can also be used to give a good hint about a pointer: does it point at a single object, or does it point at the start of an array, linked list, etc? This convention is especially useful in C, given that C declarations mostly don't distinguish between pointers and arrays.

In SQL, the "name aggregates using singular noun" convention makes sense. SQL table names appear directly in expressions, and there is no need to "assign" a record to an iteration variable. I suspect that this convention, sensible in SQL, has been carried over to traditional programming languages where it is not (IMHO) sensible.

But...you're the maintainer. If you would like to make this a CodingGuidelines-level policy, I will of course conform to it.

Show 89 quoted lines
>> +	size_t alloc;
>> +	size_t nr;
>> +};
>> +
>> +struct ref_transaction *ref_transaction_begin(void)
>> +{
>> +	return xcalloc(1, sizeof(struct ref_transaction));
>> +}
>> +
>> +static void ref_transaction_free(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);
>> +}
> 
> OK.
> 
>> +void ref_transaction_rollback(struct ref_transaction *transaction)
>> +{
>> +	ref_transaction_free(transaction);
>> +}
> 
> OK.
> 
>> +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 ref_transaction_update(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 ref_transaction_create(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 ref_transaction_delete(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);
>> +}
>> +
> 
> I can see that the chosen set of primitives update/create/delete
> mirrors what update-ref allows us to do, but given the explanation
> of "update" in refs.h, wouldn't it make more sense to implement the
> others in terms of it?

I didn't want to put a lot of thought and refactoring into the implementation yet, because I still have plans to change ref_update but I haven't yet had time to decide how. The two possibilities that I am considering are:

1. Change ref_update to have not just have_old but also a have_new
boolean attribute (perhaps inserting both into the flags field).  This
would allow a neater coding of all of the elementary operations,
including "verify", which currently is implemented as "update
$EXPECTED_VALUE to $EXPECTED_VALUE", which is a bit silly.
2. Simplify each ref_update object to do only one operation: either
verify an old value, or set a new value.  It would then only need one
SHA-1 field.  A new flag bit would tell whether the old or new value is
being operated on, and no "have_old" attribute would be needed at all.
This approach would require a general "update <newvalue> <oldvalue>" to
be decomposed into two ref_update records, but many other operations
would only require one.  If I go this route, then it would make more
sense to implement ref_transaction_update() and the others in terms of a
"verify" and/or a "set" operation.

So this patch series has mostly focused on putting a new API between users and refs.c and I'd rather put off this decision.

Show 84 quoted lines
>>  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 +3465,15 @@ cleanup:
>>  	return ret;
>>  }
>>  
>> +int ref_transaction_commit(struct ref_transaction *transaction,
>> +			   const char *msg, enum action_on_err onerr)
>> +{
>> +	int ret = update_refs(msg, transaction->updates, transaction->nr,
>> +			      onerr);
>> +	ref_transaction_free(transaction);
>> +	return ret;
>> +}
> 
> OK.
> 
>>  char *shorten_unambiguous_ref(const char *refname, int strict)
>>  {
>>  	int i;
>> diff --git a/refs.h b/refs.h
>> index 08e60ac..476a923 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,69 @@ enum action_on_err {
>>  	UPDATE_REFS_QUIET_ON_ERR
>>  };
>>  
>> +/*
>> + * Begin a reference transaction.  The reference transaction must
>> + * eventually be commited using ref_transaction_commit() or rolled
>> + * back using ref_transaction_rollback().
>> + */
>> +struct ref_transaction *ref_transaction_begin(void);
>> +
>> +/*
>> + * Roll back a ref_transaction and free all associated data.
>> + */
>> +void ref_transaction_rollback(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
> 
> Good to see the ownership rules described.
> 
>> + * 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 ref_transaction_update(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.
>> + */
> 
> Sounds a bit crazy that you can ask "create", which verifies the
> absense of the thing, to delete a thing.

Yes, you are right. I can't remember why I defined it this way. Maybe I just wanted to avoid the extra call to is_null_sha1() as a consistency check? In any case, I'll change "create" to require a nonzero new value. I think an assert() will be adequate, since it would be a programming error to call it with a zero hash.

By analogy, ref_transaction_delete() should insist that if have_old is set, then old_sha1 is not zeros. I will make that change as well.

Michael
-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
Previous: Junio C HamanoNext: Michael Haggerty
Message 43 of 65 in “Clean up update-refs --stdin and implement ref_transaction”
  1. 00/27 Clean up update-refs --stdin and implement ref_transactionMichael Haggerty, Mar 24, 2014
  2. 01/27 t1400: Fix name and expected result of one testMichael Haggerty, Mar 24, 2014
  3. Junio C HamanoMar 31, 2014
  4. Michael HaggertyMar 31, 2014
  5. 02/27 t1400: Provide more usual input to the commandMichael Haggerty, Mar 24, 2014
  6. Junio C HamanoMar 31, 2014
  7. 03/27 parse_arg(): Really test that argument is properly terminatedMichael Haggerty, Mar 24, 2014
  8. Junio C HamanoMar 31, 2014
  9. Michael HaggertyMar 31, 2014
  10. 04/27 t1400: Add some more tests involving quoted argumentsMichael Haggerty, Mar 24, 2014
  11. 05/27 refs.h: Rename the action_on_err constantsMichael Haggerty, Mar 24, 2014
  12. 06/27 update_refs(): Fix constnessMichael Haggerty, Mar 24, 2014
  13. Junio C HamanoMar 31, 2014
  14. Michael HaggertyMar 31, 2014
  15. Junio C HamanoMar 31, 2014
  16. 07/27 update-ref --stdin: Read the whole input at onceMichael Haggerty, Mar 24, 2014
  17. 08/27 parse_cmd_verify(): Copy old_sha1 instead of evaluating <oldvalue> twiceMichael Haggerty, Mar 24, 2014
  18. 09/27 update-ref.c: Extract a new function, parse_refname()Michael Haggerty, Mar 24, 2014
  19. 10/27 update-ref --stdin: Improve error messages for invalid valuesMichael Haggerty, Mar 24, 2014
  20. 11/27 update-ref --stdin: Make error messages more consistentMichael Haggerty, Mar 24, 2014
  21. 12/27 update-ref --stdin: Simplify error messages for missing oldvaluesMichael Haggerty, Mar 24, 2014
  22. 13/27 t1400: Test that stdin -z update treats empty <newvalue> as zerosMichael Haggerty, Mar 24, 2014
  23. Junio C HamanoMar 31, 2014
  24. Michael HaggertyMar 31, 2014
  25. 14/27 update-ref.c: Extract a new function, parse_next_sha1()Michael Haggerty, Mar 24, 2014
  26. Brad KingMar 26, 2014
  27. 15/27 update-ref --stdin -z: Deprecate interpreting the empty string as zerosMichael Haggerty, Mar 24, 2014
  28. Junio C HamanoMar 31, 2014
  29. 16/27 t1400: Test one mistake at a timeMichael Haggerty, Mar 24, 2014
  30. Brad KingMar 26, 2014
  31. Junio C HamanoMar 31, 2014
  32. Michael HaggertyMar 31, 2014
  33. 17/27 update-ref --stdin: Improve the error message for unexpected EOFMichael Haggerty, Mar 24, 2014
  34. 18/27 update-ref --stdin: Harmonize error messagesMichael Haggerty, Mar 24, 2014
  35. Junio C HamanoMar 31, 2014
  36. Michael HaggertyMar 31, 2014
  37. Michael HaggertyApr 1, 2014
  38. Junio C HamanoApr 2, 2014
  39. 19/27 refs: Add a concept of a reference transactionMichael Haggerty, Mar 24, 2014
  40. Brad KingMar 26, 2014
  41. Michael HaggertyMar 26, 2014
  42. Junio C HamanoApr 1, 2014
  43. Michael HaggertyApr 2, 2014
  44. 20/27 update-ref --stdin: Reimplement using reference transactionsMichael Haggerty, Mar 24, 2014
  45. Junio C HamanoApr 1, 2014
  46. Michael HaggertyApr 2, 2014
  47. Junio C HamanoApr 3, 2014
  48. Michael HaggertyApr 4, 2014
  49. 21/27 refs: Remove API function update_refs()Michael Haggerty, Mar 24, 2014
  50. Junio C HamanoApr 1, 2014
  51. 22/27 struct ref_update: Rename field "ref_name" to "refname"Michael Haggerty, Mar 24, 2014
  52. Junio C HamanoApr 1, 2014
  53. Michael HaggertyApr 2, 2014
  54. 23/27 struct ref_update: Store refname as a FLEX_ARRAY.Michael Haggerty, Mar 24, 2014
  55. Junio C HamanoApr 1, 2014
  56. 24/27 ref_transaction_commit(): Introduce temporary variablesMichael Haggerty, Mar 24, 2014
  57. Junio C HamanoApr 1, 2014
  58. 25/27 struct ref_update: Add a lock memberMichael Haggerty, Mar 24, 2014
  59. 26/27 struct ref_update: Add type fieldMichael Haggerty, Mar 24, 2014
  60. Junio C HamanoApr 1, 2014
  61. Michael HaggertyApr 2, 2014
  62. Junio C HamanoApr 2, 2014
  63. 27/27 ref_transaction_commit(): Work with transaction->updates in placeMichael Haggerty, Mar 24, 2014
  64. Brad KingMar 26, 2014
  65. Michael HaggertyMar 26, 2014

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.