aboutsummaryrefslogtreecommitdiff
path: root/refs
diff options
context:
space:
mode:
Diffstat (limited to 'refs')
-rw-r--r--refs/files-backend.c12
-rw-r--r--refs/packed-backend.c27
-rw-r--r--refs/refs-internal.h26
-rw-r--r--refs/reftable-backend.c12
4 files changed, 73 insertions, 4 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 770acdfa97..9620dd86fb 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2852,8 +2852,15 @@ static int files_transaction_prepare(struct ref_store *ref_store,
ret = lock_ref_for_update(refs, update, transaction,
head_ref, &refnames_to_check,
err);
- if (ret)
+ if (ret) {
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+
+ continue;
+ }
goto cleanup;
+ }
if (update->flags & REF_DELETING &&
!(update->flags & REF_LOG_ONLY) &&
@@ -3151,6 +3158,9 @@ static int files_transaction_finish(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
struct ref_lock *lock = update->backend_data;
+ if (update->rejection_err)
+ continue;
+
if (update->flags & REF_NEEDS_COMMIT ||
update->flags & REF_LOG_ONLY) {
if (parse_and_write_reflog(refs, update, lock, err)) {
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index d90bd815a3..debca86a2b 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -1327,10 +1327,11 @@ static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
* remain locked when it is done.
*/
static enum ref_transaction_error write_with_updates(struct packed_ref_store *refs,
- struct string_list *updates,
+ struct ref_transaction *transaction,
struct strbuf *err)
{
enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
+ struct string_list *updates = &transaction->refnames;
struct ref_iterator *iter = NULL;
size_t i;
int ok;
@@ -1411,6 +1412,13 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
"reference already exists",
update->refname);
ret = REF_TRANSACTION_ERROR_CREATE_EXISTS;
+
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+ continue;
+ }
+
goto error;
} else if (!oideq(&update->old_oid, iter->oid)) {
strbuf_addf(err, "cannot update ref '%s': "
@@ -1419,6 +1427,13 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
oid_to_hex(iter->oid),
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE;
+
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+ continue;
+ }
+
goto error;
}
}
@@ -1456,6 +1471,13 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
update->refname,
oid_to_hex(&update->old_oid));
ret = REF_TRANSACTION_ERROR_NONEXISTENT_REF;
+
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+ continue;
+ }
+
goto error;
}
}
@@ -1521,6 +1543,7 @@ static enum ref_transaction_error write_with_updates(struct packed_ref_store *re
write_error:
strbuf_addf(err, "error writing to %s: %s",
get_tempfile_path(refs->tempfile), strerror(errno));
+ ret = REF_TRANSACTION_ERROR_GENERIC;
error:
ref_iterator_free(iter);
@@ -1679,7 +1702,7 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
data->own_lock = 1;
}
- ret = write_with_updates(refs, &transaction->refnames, err);
+ ret = write_with_updates(refs, transaction, err);
if (ret)
goto failure;
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 3f1d19abd9..73a5379b73 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -124,6 +124,12 @@ struct ref_update {
uint64_t index;
/*
+ * Used in batched reference updates to mark if a given update
+ * was rejected.
+ */
+ enum ref_transaction_error rejection_err;
+
+ /*
* If this ref_update was split off of a symref update via
* split_symref_update(), then this member points at that
* update. This is used for two purposes:
@@ -143,6 +149,13 @@ int refs_read_raw_ref(struct ref_store *ref_store, const char *refname,
unsigned int *type, int *failure_errno);
/*
+ * Mark a given update as rejected with a given reason.
+ */
+int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction,
+ size_t update_idx,
+ enum ref_transaction_error err);
+
+/*
* Add a ref_update with the specified properties to transaction, and
* return a pointer to the new object. This function does not verify
* that refname is well-formed. new_oid and old_oid are only
@@ -184,6 +197,18 @@ enum ref_transaction_state {
};
/*
+ * Data structure to hold indices of updates which were rejected, for batched
+ * reference updates. While the updates themselves hold the rejection error,
+ * this structure allows a transaction to iterate only over the rejected
+ * updates.
+ */
+struct ref_transaction_rejections {
+ size_t *update_indices;
+ size_t alloc;
+ size_t nr;
+};
+
+/*
* 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.
@@ -195,6 +220,7 @@ struct ref_transaction {
size_t alloc;
size_t nr;
enum ref_transaction_state state;
+ struct ref_transaction_rejections *rejections;
void *backend_data;
unsigned int flags;
uint64_t max_index;
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index e318e6270e..8fb7d6cc71 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1371,8 +1371,15 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
transaction->updates[i],
&refnames_to_check, head_type,
&head_referent, &referent, err);
- if (ret)
+ if (ret) {
+ if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
+ strbuf_reset(err);
+ ret = 0;
+
+ continue;
+ }
goto done;
+ }
}
ret = refs_verify_refnames_available(ref_store, &refnames_to_check,
@@ -1454,6 +1461,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct reftable_transaction_update *tx_update = &arg->updates[i];
struct ref_update *u = tx_update->update;
+ if (u->rejection_err)
+ continue;
+
/*
* Write a reflog entry when updating a ref to point to
* something new in either of the following cases: