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
  ...
pull/106/merge
Junio C Hamano 8 years ago
commit 01d678a226
  1. 31
      branch.c
  2. 25
      builtin/commit.c
  3. 25
      builtin/receive-pack.c
  4. 14
      builtin/replace.c
  5. 16
      builtin/tag.c
  6. 52
      builtin/update-ref.c
  7. 54
      fast-import.c
  8. 245
      refs.c
  9. 77
      refs.h
  10. 26
      sequencer.c
  11. 81
      walker.c

@ -210,7 +210,6 @@ void create_branch(const char *head,
int force, int reflog, int clobber_head,
int quiet, enum branch_track track)
{
struct ref_lock *lock = NULL;
struct commit *commit;
unsigned char sha1[20];
char *real_ref, msg[PATH_MAX + 20];
@ -269,15 +268,6 @@ void create_branch(const char *head,
die(_("Not a valid branch point: '%s'."), start_name);
hashcpy(sha1, commit->object.sha1);
if (!dont_change_ref) {
lock = lock_any_ref_for_update(ref.buf, NULL, 0, NULL);
if (!lock)
die_errno(_("Failed to lock ref for update"));
}
if (reflog)
log_all_ref_updates = 1;
if (forcing)
snprintf(msg, sizeof msg, "branch: Reset to %s",
start_name);
@ -285,13 +275,26 @@ void create_branch(const char *head,
snprintf(msg, sizeof msg, "branch: Created from %s",
start_name);
if (reflog)
log_all_ref_updates = 1;
if (!dont_change_ref) {
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, sha1,
null_sha1, 0, !forcing, &err) ||
ref_transaction_commit(transaction, msg, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
}
if (real_ref && track)
setup_tracking(ref.buf + 11, real_ref, track, quiet);
if (!dont_change_ref)
if (write_ref_sha1(lock, sha1, msg) < 0)
die_errno(_("Failed to write ref"));
strbuf_release(&ref);
free(real_ref);
}

@ -1651,11 +1651,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
const char *index_file, *reflog_msg;
char *nl;
unsigned char sha1[20];
struct ref_lock *ref_lock;
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
struct commit *current_head = NULL;
struct commit_extra_header *extra = NULL;
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_commit_usage, builtin_commit_options);
@ -1777,16 +1778,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
strbuf_release(&author_ident);
free_commit_extra_headers(extra);
ref_lock = lock_any_ref_for_update("HEAD",
!current_head
? NULL
: current_head->object.sha1,
0, NULL);
if (!ref_lock) {
rollback_index_files();
die(_("cannot lock HEAD ref"));
}
nl = strchr(sb.buf, '\n');
if (nl)
strbuf_setlen(&sb, nl + 1 - sb.buf);
@ -1795,10 +1786,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD", sha1,
current_head
? current_head->object.sha1 : NULL,
0, !!current_head, &err) ||
ref_transaction_commit(transaction, sb.buf, &err)) {
rollback_index_files();
die(_("cannot update HEAD ref"));
die("%s", err.buf);
}
ref_transaction_free(transaction);
unlink(git_path("CHERRY_PICK_HEAD"));
unlink(git_path("REVERT_HEAD"));
@ -1827,5 +1825,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (!quiet)
print_summary(prefix, sha1, !current_head);
strbuf_release(&err);
return 0;
}

@ -473,7 +473,6 @@ static const char *update(struct command *cmd, struct shallow_info *si)
const char *namespaced_name;
unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1;
struct ref_lock *lock;
/* only refs/... are allowed */
if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@ -574,19 +573,27 @@ static const char *update(struct command *cmd, struct shallow_info *si)
return NULL; /* good */
}
else {
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction;
if (shallow_update && si->shallow_ref[cmd->index] &&
update_shallow_ref(cmd, si))
return "shallow error";
lock = lock_any_ref_for_update(namespaced_name, old_sha1,
0, NULL);
if (!lock) {
rp_error("failed to lock %s", name);
return "failed to lock";
}
if (write_ref_sha1(lock, new_sha1, "push")) {
return "failed to write"; /* error() already called */
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, namespaced_name,
new_sha1, old_sha1, 0, 1, &err) ||
ref_transaction_commit(transaction, "push", &err)) {
ref_transaction_free(transaction);
rp_error("%s", err.buf);
strbuf_release(&err);
return "failed to update ref";
}
ref_transaction_free(transaction);
strbuf_release(&err);
return NULL; /* good */
}
}

@ -155,7 +155,8 @@ static int replace_object_sha1(const char *object_ref,
unsigned char prev[20];
enum object_type obj_type, repl_type;
char ref[PATH_MAX];
struct ref_lock *lock;
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
obj_type = sha1_object_info(object, NULL);
repl_type = sha1_object_info(repl, NULL);
@ -168,12 +169,13 @@ static int replace_object_sha1(const char *object_ref,
check_ref_valid(object, prev, ref, sizeof(ref), force);
lock = lock_any_ref_for_update(ref, prev, 0, NULL);
if (!lock)
die("%s: cannot lock the ref", ref);
if (write_ref_sha1(lock, repl, NULL) < 0)
die("%s: cannot update the ref", ref);
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref, repl, prev, 0, 1, &err) ||
ref_transaction_commit(transaction, NULL, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
return 0;
}

@ -576,7 +576,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf ref = STRBUF_INIT;
unsigned char object[20], prev[20];
const char *object_ref, *tag;
struct ref_lock *lock;
struct create_tag_options opt;
char *cleanup_arg = NULL;
int annotate = 0, force = 0, lines = -1;
@ -584,6 +583,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
struct option options[] = {
OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
{ OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
@ -729,14 +730,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (annotate)
create_tag(object, tag, &buf, &opt, prev, object);
lock = lock_any_ref_for_update(ref.buf, prev, 0, NULL);
if (!lock)
die(_("%s: cannot lock the ref"), ref.buf);
if (write_ref_sha1(lock, object, NULL) < 0)
die(_("%s: cannot update the ref"), ref.buf);
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, object, prev,
0, 1, &err) ||
ref_transaction_commit(transaction, NULL, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
if (force && !is_null_sha1(prev) && hashcmp(prev, object))
printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
strbuf_release(&err);
strbuf_release(&buf);
strbuf_release(&ref);
return 0;

@ -12,11 +12,8 @@ static const char * const git_update_ref_usage[] = {
NULL
};
static struct ref_transaction *transaction;
static char line_termination = '\n';
static int update_flags;
static struct strbuf err = STRBUF_INIT;
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@ -177,8 +174,10 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
* depending on how line_termination is set.
*/
static const char *parse_cmd_update(struct strbuf *input, const char *next)
static const char *parse_cmd_update(struct ref_transaction *transaction,
struct strbuf *input, const char *next)
{
struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char new_sha1[20];
unsigned char old_sha1[20];
@ -204,12 +203,15 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
update_flags = 0;
free(refname);
strbuf_release(&err);
return next;
}
static const char *parse_cmd_create(struct strbuf *input, const char *next)
static const char *parse_cmd_create(struct ref_transaction *transaction,
struct strbuf *input, const char *next)
{
struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char new_sha1[20];
@ -226,16 +228,21 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
if (*next != line_termination)
die("create %s: extra input: %s", refname, next);
ref_transaction_create(transaction, refname, new_sha1, update_flags);
if (ref_transaction_create(transaction, refname, new_sha1,
update_flags, &err))
die("%s", err.buf);
update_flags = 0;
free(refname);
strbuf_release(&err);
return next;
}
static const char *parse_cmd_delete(struct strbuf *input, const char *next)
static const char *parse_cmd_delete(struct ref_transaction *transaction,
struct strbuf *input, const char *next)
{
struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char old_sha1[20];
int have_old;
@ -256,17 +263,21 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
if (*next != line_termination)
die("delete %s: extra input: %s", refname, next);
ref_transaction_delete(transaction, refname, old_sha1,
update_flags, have_old);
if (ref_transaction_delete(transaction, refname, old_sha1,
update_flags, have_old, &err))
die("%s", err.buf);
update_flags = 0;
free(refname);
strbuf_release(&err);
return next;
}
static const char *parse_cmd_verify(struct strbuf *input, const char *next)
static const char *parse_cmd_verify(struct ref_transaction *transaction,
struct strbuf *input, const char *next)
{
struct strbuf err = STRBUF_INIT;
char *refname;
unsigned char new_sha1[20];
unsigned char old_sha1[20];
@ -294,6 +305,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
update_flags = 0;
free(refname);
strbuf_release(&err);
return next;
}
@ -307,7 +319,7 @@ static const char *parse_cmd_option(struct strbuf *input, const char *next)
return next + 8;
}
static void update_refs_stdin(void)
static void update_refs_stdin(struct ref_transaction *transaction)
{
struct strbuf input = STRBUF_INIT;
const char *next;
@ -322,13 +334,13 @@ static void update_refs_stdin(void)
else if (isspace(*next))
die("whitespace before command: %s", next);
else if (starts_with(next, "update "))
next = parse_cmd_update(&input, next + 7);
next = parse_cmd_update(transaction, &input, next + 7);
else if (starts_with(next, "create "))
next = parse_cmd_create(&input, next + 7);
next = parse_cmd_create(transaction, &input, next + 7);
else if (starts_with(next, "delete "))
next = parse_cmd_delete(&input, next + 7);
next = parse_cmd_delete(transaction, &input, next + 7);
else if (starts_with(next, "verify "))
next = parse_cmd_verify(&input, next + 7);
next = parse_cmd_verify(transaction, &input, next + 7);
else if (starts_with(next, "option "))
next = parse_cmd_option(&input, next + 7);
else
@ -362,15 +374,21 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with empty message.");
if (read_stdin) {
transaction = ref_transaction_begin();
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction;
transaction = ref_transaction_begin(&err);
if (!transaction)
die("%s", err.buf);
if (delete || no_deref || argc > 0)
usage_with_options(git_update_ref_usage, options);
if (end_null)
line_termination = '\0';
update_refs_stdin();
update_refs_stdin(transaction);
if (ref_transaction_commit(transaction, msg, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
return 0;
}

@ -1679,8 +1679,9 @@ found_entry:
static int update_branch(struct branch *b)
{
static const char *msg = "fast-import";
struct ref_lock *lock;
struct ref_transaction *transaction;
unsigned char old_sha1[20];
struct strbuf err = STRBUF_INIT;
if (read_ref(b->name, old_sha1))
hashclr(old_sha1);
@ -1689,29 +1690,33 @@ static int update_branch(struct branch *b)
delete_ref(b->name, old_sha1, 0);
return 0;
}
lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
if (!lock)
return error("Unable to lock %s", b->name);
if (!force_update && !is_null_sha1(old_sha1)) {
struct commit *old_cmit, *new_cmit;
old_cmit = lookup_commit_reference_gently(old_sha1, 0);
new_cmit = lookup_commit_reference_gently(b->sha1, 0);
if (!old_cmit || !new_cmit) {
unlock_ref(lock);
if (!old_cmit || !new_cmit)
return error("Branch %s is missing commits.", b->name);
}
if (!in_merge_bases(old_cmit, new_cmit)) {
unlock_ref(lock);
warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
return -1;
}
}
if (write_ref_sha1(lock, b->sha1, msg) < 0)
return error("Unable to update %s", b->name);
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
0, 1, &err) ||
ref_transaction_commit(transaction, msg, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&err);
return -1;
}
ref_transaction_free(transaction);
strbuf_release(&err);
return 0;
}
@ -1730,15 +1735,32 @@ static void dump_tags(void)
{
static const char *msg = "fast-import";
struct tag *t;
struct ref_lock *lock;
char ref_name[PATH_MAX];
struct strbuf ref_name = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction;
transaction = ref_transaction_begin(&err);
if (!transaction) {
failure |= error("%s", err.buf);
goto cleanup;
}
for (t = first_tag; t; t = t->next_tag) {
sprintf(ref_name, "tags/%s", t->name);
lock = lock_ref_sha1(ref_name, NULL);
if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
failure |= error("Unable to update %s", ref_name);
strbuf_reset(&ref_name);
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
if (ref_transaction_update(transaction, ref_name.buf, t->sha1,
NULL, 0, 0, &err)) {
failure |= error("%s", err.buf);
goto cleanup;
}
}
if (ref_transaction_commit(transaction, msg, &err))
failure |= error("%s", err.buf);
cleanup:
ref_transaction_free(transaction);
strbuf_release(&ref_name);
strbuf_release(&err);
}
static void dump_marks_helper(FILE *f,

245
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;
lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
if (!lock)
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
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);

@ -10,6 +10,38 @@ struct ref_lock {
int force_write;
};
/*
* A ref_transaction represents a collection of ref updates
* that should succeed or fail together.
*
* Calling sequence
* ----------------
* - Allocate and initialize a `struct ref_transaction` by calling
* `ref_transaction_begin()`.
*
* - List intended ref updates by calling functions like
* `ref_transaction_update()` and `ref_transaction_create()`.
*
* - Call `ref_transaction_commit()` to execute the transaction.
* If this succeeds, the ref updates will have taken place and
* the transaction cannot be rolled back.
*
* - At any time call `ref_transaction_free()` to discard the
* transaction and free associated resources. In particular,
* this rolls back the transaction if it has not been
* successfully committed.
*
* Error handling
* --------------
*
* On error, transaction functions append a message about what
* went wrong to the 'err' argument. The message mentions what
* ref was being updated (if any) when the error occurred so it
* can be passed to 'die' or 'error' as-is.
*
* The message is appended to err without first clearing err.
* err will not be '\n' terminated.
*/
struct ref_transaction;
/*
@ -141,14 +173,17 @@ extern int is_branch(const char *refname);
extern int peel_ref(const char *refname, unsigned char *sha1);
/*
* Locks a "refs/" ref returning the lock on success and NULL on failure.
* On failure errno is set to something meaningful.
* Flags controlling lock_any_ref_for_update(), ref_transaction_update(),
* ref_transaction_create(), etc.
* REF_NODEREF: act on the ref directly, instead of dereferencing
* symbolic references.
*
* Flags >= 0x100 are reserved for internal use.
*/
extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */
#define REF_NODEREF 0x01
/* errno is set to something meaningful on failure */
/*
* This function sets errno to something meaningful on failure.
*/
extern struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p);
@ -232,7 +267,7 @@ enum action_on_err {
* Begin a reference transaction. The reference transaction must
* be freed by calling ref_transaction_free().
*/
struct ref_transaction *ref_transaction_begin(void);
struct ref_transaction *ref_transaction_begin(struct strbuf *err);
/*
* The following functions add a reference check or update to a
@ -250,7 +285,7 @@ struct ref_transaction *ref_transaction_begin(void);
* it must not have existed beforehand.
* Function returns 0 on success and non-zero on failure. A failure to update
* means that the transaction as a whole has failed and will need to be
* rolled back. On failure the err buffer will be updated.
* rolled back.
*/
int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
@ -264,28 +299,34 @@ int ref_transaction_update(struct ref_transaction *transaction,
* that the reference should have after the update; it must not be the
* null SHA-1. It is verified that the reference does not exist
* already.
* Function returns 0 on success and non-zero on failure. A failure to create
* means that the transaction as a whole has failed and will need to be
* rolled back.
*/
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);
/*
* Add a reference deletion to transaction. If have_old is true, then
* old_sha1 holds the value that the reference should have had before
* the update (which must not be the null SHA-1).
* Function returns 0 on success and non-zero on failure. A failure to delete
* means that the transaction as a whole has failed and will need to be
* rolled back.
*/
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);
/*
* Commit all of the changes that have been queued in transaction, as
* atomically as possible. Return a nonzero value if there is a
* problem.
* If err is non-NULL we will add an error string to it to explain why
* the transaction failed. The string does not end in newline.
*/
int ref_transaction_commit(struct ref_transaction *transaction,
const char *msg, struct strbuf *err);

@ -237,23 +237,33 @@ static int error_dirty_index(struct replay_opts *opts)
static int fast_forward_to(const unsigned char *to, const unsigned char *from,
int unborn, struct replay_opts *opts)
{
struct ref_lock *ref_lock;
struct ref_transaction *transaction;
struct strbuf sb = STRBUF_INIT;
int ret;
struct strbuf err = STRBUF_INIT;
read_cache();
if (checkout_fast_forward(from, to, 1))
exit(128); /* the callee should have complained already */
ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from,
0, NULL);
if (!ref_lock)
return error(_("Failed to lock HEAD during fast_forward_to"));
strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
ret = write_ref_sha1(ref_lock, to, sb.buf);
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn ? null_sha1 : from,
0, 1, &err) ||
ref_transaction_commit(transaction, sb.buf, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&sb);
strbuf_release(&err);
return -1;
}
strbuf_release(&sb);
return ret;
strbuf_release(&err);
ref_transaction_free(transaction);
return 0;
}
static int do_recursive_merge(struct commit *base, struct commit *next,

@ -251,64 +251,73 @@ void walker_targets_free(int targets, char **target, const char **write_ref)
int walker_fetch(struct walker *walker, int targets, char **target,
const char **write_ref, const char *write_ref_log_details)
{
struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
struct strbuf refname = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction = NULL;
unsigned char *sha1 = xmalloc(targets * 20);
const char *msg;
char *to_free = NULL;
int ret;
int i;
char *msg = NULL;
int i, ret = -1;
save_commit_buffer = 0;
for (i = 0; i < targets; i++) {
if (!write_ref || !write_ref[i])
continue;
lock[i] = lock_ref_sha1(write_ref[i], NULL);
if (!lock[i]) {
error("Can't lock ref %s", write_ref[i]);
goto unlock_and_fail;
if (write_ref) {
transaction = ref_transaction_begin(&err);
if (!transaction) {
error("%s", err.buf);
goto done;
}
}
if (!walker->get_recover)
for_each_ref(mark_complete, NULL);
for (i = 0; i < targets; i++) {
if (interpret_target(walker, target[i], &sha1[20 * i])) {
error("Could not interpret response from server '%s' as something to pull", target[i]);
goto unlock_and_fail;
goto done;
}
if (process(walker, lookup_unknown_object(&sha1[20 * i])))
goto unlock_and_fail;
goto done;
}
if (loop(walker))
goto unlock_and_fail;
if (write_ref_log_details)
msg = to_free = xstrfmt("fetch from %s", write_ref_log_details);
else
msg = "fetch (unknown)";
goto done;
if (!write_ref) {
ret = 0;
goto done;
}
if (write_ref_log_details) {
msg = xstrfmt("fetch from %s", write_ref_log_details);
} else {
msg = NULL;
}
for (i = 0; i < targets; i++) {
if (!write_ref || !write_ref[i])
if (!write_ref[i])
continue;
ret = write_ref_sha1(lock[i], &sha1[20 * i], msg);
lock[i] = NULL;
if (ret)
goto unlock_and_fail;
strbuf_reset(&refname);
strbuf_addf(&refname, "refs/%s", write_ref[i]);
if (ref_transaction_update(transaction, refname.buf,
&sha1[20 * i], NULL, 0, 0,
&err)) {
error("%s", err.buf);
goto done;
}
}
if (ref_transaction_commit(transaction,
msg ? msg : "fetch (unknown)",
&err)) {
error("%s", err.buf);
goto done;
}
free(to_free);
return 0;
unlock_and_fail:
for (i = 0; i < targets; i++)
if (lock[i])
unlock_ref(lock[i]);
free(to_free);
ret = 0;
return -1;
done:
ref_transaction_free(transaction);
free(msg);
free(sha1);
strbuf_release(&err);
strbuf_release(&refname);
return ret;
}
void walker_free(struct walker *walker)

Loading…
Cancel
Save