Merge branch 'kn/non-transactional-batch-updates'

Updating multiple references have only been possible in all-or-none
fashion with transactions, but it can be more efficient to batch
multiple updates even when some of them are allowed to fail in a
best-effort manner.  A new "best effort batches of updates" mode
has been introduced.

* kn/non-transactional-batch-updates:
  update-ref: add --batch-updates flag for stdin mode
  refs: support rejection in batch updates during F/D checks
  refs: implement batch reference update support
  refs: introduce enum-based transaction error types
  refs/reftable: extract code from the transaction preparation
  refs/files: remove duplicate duplicates check
  refs: move duplicate refname update check to generic layer
  refs/files: remove redundant check in split_symref_update()
This commit is contained in:
Junio C Hamano
2025-04-16 13:54:19 -07:00
10 changed files with 969 additions and 523 deletions

70
refs.h
View File

@@ -16,6 +16,23 @@ struct worktree;
enum ref_storage_format ref_storage_format_by_name(const char *name);
const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_format);
enum ref_transaction_error {
/* Default error code */
REF_TRANSACTION_ERROR_GENERIC = -1,
/* Ref name conflict like A vs A/B */
REF_TRANSACTION_ERROR_NAME_CONFLICT = -2,
/* Ref to be created already exists */
REF_TRANSACTION_ERROR_CREATE_EXISTS = -3,
/* ref expected but doesn't exist */
REF_TRANSACTION_ERROR_NONEXISTENT_REF = -4,
/* Provided old_oid or old_target of reference doesn't match actual */
REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE = -5,
/* Provided new_oid or new_target is invalid */
REF_TRANSACTION_ERROR_INVALID_NEW_VALUE = -6,
/* Expected ref to be symref, but is a regular ref */
REF_TRANSACTION_ERROR_EXPECTED_SYMREF = -7,
};
/*
* Resolve a reference, recursively following symbolic references.
*
@@ -117,24 +134,12 @@ int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
*
* extras and skip must be sorted.
*/
int refs_verify_refname_available(struct ref_store *refs,
const char *refname,
const struct string_list *extras,
const struct string_list *skip,
unsigned int initial_transaction,
struct strbuf *err);
/*
* Same as `refs_verify_refname_available()`, but checking for a list of
* refnames instead of only a single item. This is more efficient in the case
* where one needs to check multiple refnames.
*/
int refs_verify_refnames_available(struct ref_store *refs,
const struct string_list *refnames,
const struct string_list *extras,
const struct string_list *skip,
unsigned int initial_transaction,
struct strbuf *err);
enum ref_transaction_error refs_verify_refname_available(struct ref_store *refs,
const char *refname,
const struct string_list *extras,
const struct string_list *skip,
unsigned int initial_transaction,
struct strbuf *err);
int refs_ref_exists(struct ref_store *refs, const char *refname);
@@ -650,6 +655,13 @@ enum ref_transaction_flag {
* either be absent or null_oid.
*/
REF_TRANSACTION_FLAG_INITIAL = (1 << 0),
/*
* The transaction mechanism by default fails all updates if any conflict
* is detected. This flag allows transactions to partially apply updates
* while rejecting updates which do not match the expected state.
*/
REF_TRANSACTION_ALLOW_FAILURE = (1 << 1),
};
/*
@@ -830,13 +842,6 @@ int ref_transaction_verify(struct ref_transaction *transaction,
unsigned int flags,
struct strbuf *err);
/* Naming conflict (for example, the ref names A and A/B conflict). */
#define TRANSACTION_NAME_CONFLICT -1
/* When only creation was requested, but the ref already exists. */
#define TRANSACTION_CREATE_EXISTS -2
/* All other errors. */
#define TRANSACTION_GENERIC_ERROR -3
/*
* Perform the preparatory stages of committing `transaction`. Acquire
* any needed locks, check preconditions, etc.; basically, do as much
@@ -887,6 +892,21 @@ void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
ref_transaction_for_each_queued_update_fn cb,
void *cb_data);
/*
* Execute the given callback function for each of the reference updates which
* have been rejected in the given transaction.
*/
typedef void ref_transaction_for_each_rejected_update_fn(const char *refname,
const struct object_id *old_oid,
const struct object_id *new_oid,
const char *old_target,
const char *new_target,
enum ref_transaction_error err,
void *cb_data);
void ref_transaction_for_each_rejected_update(struct ref_transaction *transaction,
ref_transaction_for_each_rejected_update_fn cb,
void *cb_data);
/*
* Free `*transaction` and all associated data.
*/