summaryrefslogtreecommitdiff
path: root/refs/files-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r--refs/files-backend.c58
1 files changed, 39 insertions, 19 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 6078668c99..02cb4907d8 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -71,6 +71,7 @@ struct ref_lock {
char *ref_name;
struct lock_file lk;
struct object_id old_oid;
+ unsigned int count; /* track users of the lock (ref update + reflog updates) */
};
struct files_ref_store {
@@ -638,9 +639,12 @@ int parse_loose_ref_contents(const struct git_hash_algo *algop,
static void unlock_ref(struct ref_lock *lock)
{
- rollback_lock_file(&lock->lk);
- free(lock->ref_name);
- free(lock);
+ lock->count--;
+ if (!lock->count) {
+ rollback_lock_file(&lock->lk);
+ free(lock->ref_name);
+ free(lock);
+ }
}
/*
@@ -696,6 +700,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
*lock_p = CALLOC_ARRAY(lock, 1);
lock->ref_name = xstrdup(refname);
+ lock->count = 1;
files_ref_path(refs, &ref_file, refname);
retry:
@@ -1169,6 +1174,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
goto error_return;
lock->ref_name = xstrdup(refname);
+ lock->count = 1;
if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
unable_to_lock_message(ref_file.buf, errno, err);
@@ -2535,6 +2541,12 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
return -1;
}
+struct files_transaction_backend_data {
+ struct ref_transaction *packed_transaction;
+ int packed_refs_locked;
+ struct strmap ref_locks;
+};
+
/*
* Prepare for carrying out update:
* - Lock the reference referred to by update.
@@ -2557,11 +2569,14 @@ static int lock_ref_for_update(struct files_ref_store *refs,
{
struct strbuf referent = STRBUF_INIT;
int mustexist = ref_update_expects_existing_old_ref(update);
+ struct files_transaction_backend_data *backend_data;
int ret = 0;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
+ backend_data = transaction->backend_data;
+
if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update))
update->flags |= REF_DELETING;
@@ -2572,18 +2587,25 @@ static int lock_ref_for_update(struct files_ref_store *refs,
goto out;
}
- ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames,
- &lock, &referent,
- &update->type, err);
- if (ret) {
- char *reason;
+ lock = strmap_get(&backend_data->ref_locks, update->refname);
+ if (lock) {
+ lock->count++;
+ } else {
+ ret = lock_raw_ref(refs, update->refname, mustexist,
+ affected_refnames,
+ &lock, &referent,
+ &update->type, err);
+ if (ret) {
+ char *reason;
+
+ reason = strbuf_detach(err, NULL);
+ strbuf_addf(err, "cannot lock ref '%s': %s",
+ ref_update_original_update_refname(update), reason);
+ free(reason);
+ goto out;
+ }
- reason = strbuf_detach(err, NULL);
- strbuf_addf(err, "cannot lock ref '%s': %s",
- ref_update_original_update_refname(update), reason);
- free(reason);
- goto out;
+ strmap_put(&backend_data->ref_locks, update->refname, lock);
}
update->backend_data = lock;
@@ -2730,11 +2752,6 @@ out:
return ret;
}
-struct files_transaction_backend_data {
- struct ref_transaction *packed_transaction;
- int packed_refs_locked;
-};
-
/*
* Unlock any references in `transaction` that are still locked, and
* mark the transaction closed.
@@ -2767,6 +2784,8 @@ static void files_transaction_cleanup(struct files_ref_store *refs,
if (backend_data->packed_refs_locked)
packed_refs_unlock(refs->packed_ref_store);
+ strmap_clear(&backend_data->ref_locks, 0);
+
free(backend_data);
}
@@ -2796,6 +2815,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
goto cleanup;
CALLOC_ARRAY(backend_data, 1);
+ strmap_init(&backend_data->ref_locks);
transaction->backend_data = backend_data;
/*