Merge branch 'rs/ref-transaction-1'
The second batch of the transactional ref update series. * rs/ref-transaction-1: (22 commits) update-ref --stdin: pass transaction around explicitly update-ref --stdin: narrow scope of err strbuf refs.c: make delete_ref use a transaction refs.c: make prune_ref use a transaction to delete the ref refs.c: remove lock_ref_sha1 refs.c: remove the update_ref_write function refs.c: remove the update_ref_lock function refs.c: make lock_ref_sha1 static walker.c: use ref transaction for ref updates fast-import.c: use a ref transaction when dumping tags receive-pack.c: use a reference transaction for updating the refs refs.c: change update_ref to use a transaction branch.c: use ref transaction for all ref updates fast-import.c: change update_branch to use ref transactions sequencer.c: use ref transactions for all ref updates commit.c: use ref transactions for updates replace.c: use the ref transaction functions for updates tag.c: use ref transactions when doing updates refs.c: add transaction.status and track OPEN/CLOSED refs.c: make ref_transaction_begin take an err argument ...
This commit is contained in:
243
refs.c
243
refs.c
@ -24,6 +24,11 @@ static unsigned char refname_disposition[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
|
||||
};
|
||||
|
||||
/*
|
||||
* Used as a flag to ref_transaction_delete when a loose ref is being
|
||||
* pruned.
|
||||
*/
|
||||
#define REF_ISPRUNING 0x0100
|
||||
/*
|
||||
* Try to read one refname component from the front of refname.
|
||||
* Return the length of the component found, or -1 if the component is
|
||||
@ -2068,7 +2073,10 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
|
||||
return logs_found;
|
||||
}
|
||||
|
||||
/* This function should make sure errno is meaningful on error */
|
||||
/*
|
||||
* Locks a "refs/" ref returning the lock on success and NULL on failure.
|
||||
* On failure errno is set to something meaningful.
|
||||
*/
|
||||
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
|
||||
const unsigned char *old_sha1,
|
||||
int flags, int *type_p)
|
||||
@ -2169,15 +2177,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1)
|
||||
{
|
||||
char refpath[PATH_MAX];
|
||||
if (check_refname_format(refname, 0))
|
||||
return NULL;
|
||||
strcpy(refpath, mkpath("refs/%s", refname));
|
||||
return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
|
||||
}
|
||||
|
||||
struct ref_lock *lock_any_ref_for_update(const char *refname,
|
||||
const unsigned char *old_sha1,
|
||||
int flags, int *type_p)
|
||||
@ -2387,13 +2386,25 @@ static void try_remove_empty_parents(char *name)
|
||||
/* make sure nobody touched the ref, and unlink */
|
||||
static void prune_ref(struct ref_to_prune *r)
|
||||
{
|
||||
struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
if (lock) {
|
||||
unlink_or_warn(git_path("%s", r->name));
|
||||
unlock_ref(lock);
|
||||
try_remove_empty_parents(r->name);
|
||||
if (check_refname_format(r->name + 5, 0))
|
||||
return;
|
||||
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_delete(transaction, r->name, r->sha1,
|
||||
REF_ISPRUNING, 1, &err) ||
|
||||
ref_transaction_commit(transaction, NULL, &err)) {
|
||||
ref_transaction_free(transaction);
|
||||
error("%s", err.buf);
|
||||
strbuf_release(&err);
|
||||
return;
|
||||
}
|
||||
ref_transaction_free(transaction);
|
||||
strbuf_release(&err);
|
||||
try_remove_empty_parents(r->name);
|
||||
}
|
||||
|
||||
static void prune_refs(struct ref_to_prune *r)
|
||||
@ -2536,11 +2547,6 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int repack_without_ref(const char *refname)
|
||||
{
|
||||
return repack_without_refs(&refname, 1, NULL);
|
||||
}
|
||||
|
||||
static int delete_ref_loose(struct ref_lock *lock, int flag)
|
||||
{
|
||||
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
|
||||
@ -2558,24 +2564,22 @@ static int delete_ref_loose(struct ref_lock *lock, int flag)
|
||||
|
||||
int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
int ret = 0, flag = 0;
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
|
||||
if (!lock)
|
||||
transaction = ref_transaction_begin(&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_delete(transaction, refname, sha1, delopt,
|
||||
sha1 && !is_null_sha1(sha1), &err) ||
|
||||
ref_transaction_commit(transaction, NULL, &err)) {
|
||||
error("%s", err.buf);
|
||||
ref_transaction_free(transaction);
|
||||
strbuf_release(&err);
|
||||
return 1;
|
||||
ret |= delete_ref_loose(lock, flag);
|
||||
|
||||
/* removing the loose one could have resurrected an earlier
|
||||
* packed one. Also, if it was not loose we need to repack
|
||||
* without it.
|
||||
*/
|
||||
ret |= repack_without_ref(lock->ref_name);
|
||||
|
||||
unlink_or_warn(git_path("logs/%s", lock->ref_name));
|
||||
clear_loose_ref_cache(&ref_cache);
|
||||
unlock_ref(lock);
|
||||
return ret;
|
||||
}
|
||||
ref_transaction_free(transaction);
|
||||
strbuf_release(&err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3332,43 +3336,6 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct ref_lock *update_ref_lock(const char *refname,
|
||||
const unsigned char *oldval,
|
||||
int flags, int *type_p,
|
||||
enum action_on_err onerr)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
lock = lock_any_ref_for_update(refname, oldval, flags, type_p);
|
||||
if (!lock) {
|
||||
const char *str = "Cannot lock the ref '%s'.";
|
||||
switch (onerr) {
|
||||
case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
|
||||
case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
|
||||
case UPDATE_REFS_QUIET_ON_ERR: break;
|
||||
}
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
|
||||
static int update_ref_write(const char *action, const char *refname,
|
||||
const unsigned char *sha1, struct ref_lock *lock,
|
||||
struct strbuf *err, enum action_on_err onerr)
|
||||
{
|
||||
if (write_ref_sha1(lock, sha1, action) < 0) {
|
||||
const char *str = "Cannot update the ref '%s'.";
|
||||
if (err)
|
||||
strbuf_addf(err, str, refname);
|
||||
|
||||
switch (onerr) {
|
||||
case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
|
||||
case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
|
||||
case UPDATE_REFS_QUIET_ON_ERR: break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information needed for a single ref update. Set new_sha1 to the
|
||||
* new value or to zero to delete the ref. To check the old value
|
||||
@ -3385,6 +3352,21 @@ struct ref_update {
|
||||
const char refname[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
/*
|
||||
* Transaction states.
|
||||
* OPEN: The transaction is in a valid state and can accept new updates.
|
||||
* An OPEN transaction can be committed.
|
||||
* CLOSED: A closed transaction is no longer active and no other operations
|
||||
* than free can be used on it in this state.
|
||||
* A transaction can either become closed by successfully committing
|
||||
* an active transaction or if there is a failure while building
|
||||
* the transaction thus rendering it failed/inactive.
|
||||
*/
|
||||
enum ref_transaction_state {
|
||||
REF_TRANSACTION_OPEN = 0,
|
||||
REF_TRANSACTION_CLOSED = 1
|
||||
};
|
||||
|
||||
/*
|
||||
* Data structure for holding a reference transaction, which can
|
||||
* consist of checks and updates to multiple references, carried out
|
||||
@ -3394,9 +3376,10 @@ struct ref_transaction {
|
||||
struct ref_update **updates;
|
||||
size_t alloc;
|
||||
size_t nr;
|
||||
enum ref_transaction_state state;
|
||||
};
|
||||
|
||||
struct ref_transaction *ref_transaction_begin(void)
|
||||
struct ref_transaction *ref_transaction_begin(struct strbuf *err)
|
||||
{
|
||||
return xcalloc(1, sizeof(struct ref_transaction));
|
||||
}
|
||||
@ -3436,6 +3419,9 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
||||
{
|
||||
struct ref_update *update;
|
||||
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: update called for transaction that is not open");
|
||||
|
||||
if (have_old && !old_sha1)
|
||||
die("BUG: have_old is true but old_sha1 is NULL");
|
||||
|
||||
@ -3448,44 +3434,84 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ref_transaction_create(struct ref_transaction *transaction,
|
||||
const char *refname,
|
||||
const unsigned char *new_sha1,
|
||||
int flags)
|
||||
int ref_transaction_create(struct ref_transaction *transaction,
|
||||
const char *refname,
|
||||
const unsigned char *new_sha1,
|
||||
int flags,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_update *update = add_update(transaction, refname);
|
||||
struct ref_update *update;
|
||||
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: create called for transaction that is not open");
|
||||
|
||||
if (!new_sha1 || is_null_sha1(new_sha1))
|
||||
die("BUG: create ref with null new_sha1");
|
||||
|
||||
update = add_update(transaction, refname);
|
||||
|
||||
assert(!is_null_sha1(new_sha1));
|
||||
hashcpy(update->new_sha1, new_sha1);
|
||||
hashclr(update->old_sha1);
|
||||
update->flags = flags;
|
||||
update->have_old = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ref_transaction_delete(struct ref_transaction *transaction,
|
||||
const char *refname,
|
||||
const unsigned char *old_sha1,
|
||||
int flags, int have_old)
|
||||
int ref_transaction_delete(struct ref_transaction *transaction,
|
||||
const char *refname,
|
||||
const unsigned char *old_sha1,
|
||||
int flags, int have_old,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct ref_update *update = add_update(transaction, refname);
|
||||
struct ref_update *update;
|
||||
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: delete called for transaction that is not open");
|
||||
|
||||
if (have_old && !old_sha1)
|
||||
die("BUG: have_old is true but old_sha1 is NULL");
|
||||
|
||||
update = add_update(transaction, refname);
|
||||
update->flags = flags;
|
||||
update->have_old = have_old;
|
||||
if (have_old) {
|
||||
assert(!is_null_sha1(old_sha1));
|
||||
hashcpy(update->old_sha1, old_sha1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_ref(const char *action, const char *refname,
|
||||
const unsigned char *sha1, const unsigned char *oldval,
|
||||
int flags, enum action_on_err onerr)
|
||||
{
|
||||
struct ref_lock *lock;
|
||||
lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
|
||||
if (!lock)
|
||||
struct ref_transaction *t;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
|
||||
t = ref_transaction_begin(&err);
|
||||
if (!t ||
|
||||
ref_transaction_update(t, refname, sha1, oldval, flags,
|
||||
!!oldval, &err) ||
|
||||
ref_transaction_commit(t, action, &err)) {
|
||||
const char *str = "update_ref failed for ref '%s': %s";
|
||||
|
||||
ref_transaction_free(t);
|
||||
switch (onerr) {
|
||||
case UPDATE_REFS_MSG_ON_ERR:
|
||||
error(str, refname, err.buf);
|
||||
break;
|
||||
case UPDATE_REFS_DIE_ON_ERR:
|
||||
die(str, refname, err.buf);
|
||||
break;
|
||||
case UPDATE_REFS_QUIET_ON_ERR:
|
||||
break;
|
||||
}
|
||||
strbuf_release(&err);
|
||||
return 1;
|
||||
return update_ref_write(action, refname, sha1, lock, NULL, onerr);
|
||||
}
|
||||
strbuf_release(&err);
|
||||
ref_transaction_free(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ref_update_compare(const void *r1, const void *r2)
|
||||
@ -3519,8 +3545,13 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
int n = transaction->nr;
|
||||
struct ref_update **updates = transaction->updates;
|
||||
|
||||
if (!n)
|
||||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
die("BUG: commit called for transaction that is not open");
|
||||
|
||||
if (!n) {
|
||||
transaction->state = REF_TRANSACTION_CLOSED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate work space */
|
||||
delnames = xmalloc(sizeof(*delnames) * n);
|
||||
@ -3535,12 +3566,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
for (i = 0; i < n; i++) {
|
||||
struct ref_update *update = updates[i];
|
||||
|
||||
update->lock = update_ref_lock(update->refname,
|
||||
(update->have_old ?
|
||||
update->old_sha1 : NULL),
|
||||
update->flags,
|
||||
&update->type,
|
||||
UPDATE_REFS_QUIET_ON_ERR);
|
||||
update->lock = lock_any_ref_for_update(update->refname,
|
||||
(update->have_old ?
|
||||
update->old_sha1 :
|
||||
NULL),
|
||||
update->flags,
|
||||
&update->type);
|
||||
if (!update->lock) {
|
||||
if (err)
|
||||
strbuf_addf(err, "Cannot lock the ref '%s'.",
|
||||
@ -3555,14 +3586,15 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
struct ref_update *update = updates[i];
|
||||
|
||||
if (!is_null_sha1(update->new_sha1)) {
|
||||
ret = update_ref_write(msg,
|
||||
update->refname,
|
||||
update->new_sha1,
|
||||
update->lock, err,
|
||||
UPDATE_REFS_QUIET_ON_ERR);
|
||||
update->lock = NULL; /* freed by update_ref_write */
|
||||
if (ret)
|
||||
ret = write_ref_sha1(update->lock, update->new_sha1,
|
||||
msg);
|
||||
update->lock = NULL; /* freed by write_ref_sha1 */
|
||||
if (ret) {
|
||||
if (err)
|
||||
strbuf_addf(err, "Cannot update the ref '%s'.",
|
||||
update->refname);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3571,8 +3603,9 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
struct ref_update *update = updates[i];
|
||||
|
||||
if (update->lock) {
|
||||
delnames[delnum++] = update->lock->ref_name;
|
||||
ret |= delete_ref_loose(update->lock, update->type);
|
||||
if (!(update->flags & REF_ISPRUNING))
|
||||
delnames[delnum++] = update->lock->ref_name;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3582,6 +3615,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
|
||||
clear_loose_ref_cache(&ref_cache);
|
||||
|
||||
cleanup:
|
||||
transaction->state = REF_TRANSACTION_CLOSED;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (updates[i]->lock)
|
||||
unlock_ref(updates[i]->lock);
|
||||
|
||||
Reference in New Issue
Block a user