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:
Junio C Hamano
2014-09-11 10:33:30 -07:00
11 changed files with 400 additions and 250 deletions

243
refs.c
View File

@ -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);