From b416af5bcdca3ac8426220f09efbfca2f1bec6e0 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 15:26:44 -0700 Subject: refs.c: change ref_transaction_create to do error checking and return status Do basic error checking in ref_transaction_create() and make it return non-zero on error. Update all callers to check the result of ref_transaction_create(). There are currently no conditions in _create that will return error but there will be in the future. Add an err argument that will be updated on failure. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 3067b11..41121fa 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -226,7 +226,9 @@ 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); diff --git a/refs.c b/refs.c index 3f05e88..c49f1c6 100644 --- a/refs.c +++ b/refs.c @@ -3449,18 +3449,24 @@ 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 (!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, diff --git a/refs.h b/refs.h index c5376ce..b648819 100644 --- a/refs.h +++ b/refs.h @@ -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; /* @@ -248,7 +280,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, @@ -262,11 +294,15 @@ 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 @@ -282,8 +318,6 @@ void ref_transaction_delete(struct ref_transaction *transaction, * 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); -- cgit v0.10.2-6-g49f6 From 8c8bdc0d3582e42c13815a191344b1aa3e06c792 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 15:27:45 -0700 Subject: refs.c: update ref_transaction_delete to check for error and return status Change ref_transaction_delete() to do basic error checking and return non-zero on error. Update all callers to check the return for ref_transaction_delete(). There are currently no conditions in _delete that will return error but there will be in the future. Add an err argument that will be updated on failure. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 41121fa..7c9c248 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -258,8 +258,9 @@ 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); diff --git a/refs.c b/refs.c index c49f1c6..40f04f4 100644 --- a/refs.c +++ b/refs.c @@ -3469,19 +3469,25 @@ int ref_transaction_create(struct ref_transaction *transaction, 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 (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, diff --git a/refs.h b/refs.h index b648819..71389a1 100644 --- a/refs.h +++ b/refs.h @@ -308,11 +308,15 @@ int ref_transaction_create(struct ref_transaction *transaction, * 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 -- cgit v0.10.2-6-g49f6 From 93a644ea9d3702cc1cc62c0d413f81f8e46fabe7 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 19 May 2014 10:42:34 -0700 Subject: refs.c: make ref_transaction_begin take an err argument Add an err argument to _begin so that on non-fatal failures in future ref backends we can report a nice error back to the caller. While _begin can currently never fail for other reasons than OOM, in which case we die() anyway, we may add other types of backends in the future. For example, a hypothetical MySQL backend could fail in _begin with "Can not connect to MySQL server. No route to host". Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 7c9c248..96a53b9 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -365,7 +365,9 @@ 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(); + 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) @@ -374,6 +376,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (ref_transaction_commit(transaction, msg, &err)) die("%s", err.buf); ref_transaction_free(transaction); + strbuf_release(&err); return 0; } diff --git a/refs.c b/refs.c index 40f04f4..9cb7908 100644 --- a/refs.c +++ b/refs.c @@ -3397,7 +3397,7 @@ struct ref_transaction { size_t nr; }; -struct ref_transaction *ref_transaction_begin(void) +struct ref_transaction *ref_transaction_begin(struct strbuf *err) { return xcalloc(1, sizeof(struct ref_transaction)); } diff --git a/refs.h b/refs.h index 71389a1..3f37c65 100644 --- a/refs.h +++ b/refs.h @@ -262,7 +262,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 -- cgit v0.10.2-6-g49f6 From 2bdc785fd7f699669a582f7a83f5b81192d24d88 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 29 Apr 2014 12:06:19 -0700 Subject: refs.c: add transaction.status and track OPEN/CLOSED Track the state of a transaction in a new state field. Check the field for sanity, i.e. that state must be OPEN when _commit/_create/_delete or _update is called or else die(BUG:...) Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 9cb7908..cc63056 100644 --- a/refs.c +++ b/refs.c @@ -3387,6 +3387,21 @@ struct ref_update { }; /* + * 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 * as atomically as possible. This structure is opaque to callers. @@ -3395,6 +3410,7 @@ struct ref_transaction { struct ref_update **updates; size_t alloc; size_t nr; + enum ref_transaction_state state; }; struct ref_transaction *ref_transaction_begin(struct strbuf *err) @@ -3437,6 +3453,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"); @@ -3457,6 +3476,9 @@ int ref_transaction_create(struct ref_transaction *transaction, { 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"); @@ -3477,6 +3499,9 @@ int ref_transaction_delete(struct ref_transaction *transaction, { 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"); @@ -3532,8 +3557,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); @@ -3595,6 +3625,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); -- cgit v0.10.2-6-g49f6 From e5074bfe8cb895a060720a7cbb3f2ed4fd8cf710 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 15:30:41 -0700 Subject: tag.c: use ref transactions when doing updates Change tag.c to use ref transactions for all ref updates. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/tag.c b/builtin/tag.c index c6e8a71..f3f172f 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -548,7 +548,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; @@ -556,6 +555,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"), @@ -701,14 +702,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; -- cgit v0.10.2-6-g49f6 From 867c2fac0aef75d8c883010780eb163e82aaa266 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 15:32:29 -0700 Subject: replace.c: use the ref transaction functions for updates Update replace.c to use ref transactions for updates. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/replace.c b/builtin/replace.c index 1bb491d..1fcd06d 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -153,7 +153,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); @@ -166,12 +167,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; } -- cgit v0.10.2-6-g49f6 From c0fe1ed0841868989c088681e1263f248629e92f Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 15:34:19 -0700 Subject: commit.c: use ref transactions for updates Change commit.c to use ref transactions for all ref updates. Make sure we pass a NULL pointer to ref_transaction_update if have_old is false. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/commit.c b/builtin/commit.c index 5e2221c..9bf1003 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1627,11 +1627,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); @@ -1753,16 +1754,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); @@ -1771,10 +1762,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")); @@ -1803,5 +1801,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; } -- cgit v0.10.2-6-g49f6 From d668d16ca7f6c31ee60cb4cc061dd1b62ea658de Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 15:37:45 -0700 Subject: sequencer.c: use ref transactions for all ref updates Change to use ref transactions for all updates to refs. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/sequencer.c b/sequencer.c index 0a80c58..5e93b6a 100644 --- a/sequencer.c +++ b/sequencer.c @@ -272,23 +272,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(1); /* 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, -- cgit v0.10.2-6-g49f6 From de7e86f522ea806ef40b3c9557e040d4109e55fb Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 16:21:13 -0700 Subject: fast-import.c: change update_branch to use ref transactions Change update_branch() to use ref transactions for updates. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/fast-import.c b/fast-import.c index 6707a66..79160d5 100644 --- a/fast-import.c +++ b/fast-import.c @@ -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; } -- cgit v0.10.2-6-g49f6 From d43f990fac72264c1553beb7dbc6e8e795083d42 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 16 Apr 2014 16:21:53 -0700 Subject: branch.c: use ref transaction for all ref updates Change create_branch to use a ref transaction when creating the new branch. This also fixes a race condition in the old code where two concurrent create_branch could race since the lock_any_ref_for_update/write_ref_sha1 did not protect against the ref already existing. I.e. one thread could end up overwriting a branch even if the forcing flag is false. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/branch.c b/branch.c index 660097b..37ac555 100644 --- a/branch.c +++ b/branch.c @@ -226,7 +226,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]; @@ -285,15 +284,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); @@ -301,13 +291,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); } -- cgit v0.10.2-6-g49f6 From b4d75ac1d152bbab44b0777a4cc0c48db75f6024 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 24 Apr 2014 16:36:55 -0700 Subject: refs.c: change update_ref to use a transaction Change the update_ref helper function to use a ref transaction internally. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index cc63056..a3532ab 100644 --- a/refs.c +++ b/refs.c @@ -3519,11 +3519,33 @@ 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) -- cgit v0.10.2-6-g49f6 From 6629ea2d4a5faa0a84367f6d4aedba53cb0f26b4 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 28 Apr 2014 14:36:15 -0700 Subject: receive-pack.c: use a reference transaction for updating the refs Wrap all the ref updates inside a transaction. In the new API there is no distinction between failure to lock and failure to write a ref. Both can be permanent (e.g., a ref "refs/heads/topic" is blocking creation of the lock file "refs/heads/topic/1.lock") or transient (e.g., file system full) and there's no clear difference in how the client should respond, so replace the two statuses "failed to lock" and "failed to write" with a single status "failed to update ref". In both cases a more detailed message is sent by sideband to diagnose the problem. Example, before: error: there are still refs under 'refs/heads/topic' remote: error: failed to lock refs/heads/topic To foo ! [remote rejected] HEAD -> topic (failed to lock) After: error: there are still refs under 'refs/heads/topic' remote: error: Cannot lock the ref 'refs/heads/topic'. To foo ! [remote rejected] HEAD -> topic (failed to update ref) Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c323081..224fadc 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -475,7 +475,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)) { @@ -576,19 +575,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 */ } } -- cgit v0.10.2-6-g49f6 From 3f09ba754367fcc0ddad94e22385d80b5f317584 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 28 Apr 2014 15:23:58 -0700 Subject: fast-import.c: use a ref transaction when dumping tags Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/fast-import.c b/fast-import.c index 79160d5..e7f6e37 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1735,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, -- cgit v0.10.2-6-g49f6 From b6b10bb44c31b5a830c1636c7eb8b122f9b67858 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 17 Apr 2014 11:31:06 -0700 Subject: walker.c: use ref transaction for ref updates Switch to using ref transactions in walker_fetch(). As part of the refactoring to use ref transactions we also fix a potential memory leak where in the original code if write_ref_sha1() would fail we would end up returning from the function without free()ing the msg string. Note that this function is only called when fetching from a remote HTTP repository onto the local (most of the time single-user) repository which likely means that the type of collisions that the previous locking would protect against and cause the fetch to fail for are even more rare. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/walker.c b/walker.c index 1dd86b8..b8a5441 100644 --- a/walker.c +++ b/walker.c @@ -251,40 +251,40 @@ 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); - char *msg; - 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; - + goto done; + if (!write_ref) { + ret = 0; + goto done; + } if (write_ref_log_details) { msg = xmalloc(strlen(write_ref_log_details) + 12); sprintf(msg, "fetch from %s", write_ref_log_details); @@ -292,23 +292,33 @@ int walker_fetch(struct walker *walker, int targets, char **target, 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 ? msg : "fetch (unknown)"); - 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(msg); - - return 0; -unlock_and_fail: - for (i = 0; i < targets; i++) - if (lock[i]) - unlock_ref(lock[i]); + 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) -- cgit v0.10.2-6-g49f6 From 88b680ae8d7a8f88409eff0c4cd2f12acf07155f Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Mon, 28 Apr 2014 15:38:47 -0700 Subject: refs.c: make lock_ref_sha1 static No external callers reference lock_ref_sha1 any more so let's declare it static. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index a3532ab..d4cd44b 100644 --- a/refs.c +++ b/refs.c @@ -2069,7 +2069,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) @@ -2170,7 +2173,7 @@ 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) +static struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1) { char refpath[PATH_MAX]; if (check_refname_format(refname, 0)) diff --git a/refs.h b/refs.h index 3f37c65..65dd593 100644 --- a/refs.h +++ b/refs.h @@ -170,12 +170,6 @@ extern int ref_exists(const char *); */ 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. - */ -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 */ -- cgit v0.10.2-6-g49f6 From 45421e24e8322184a35a32a9072f04afabe33880 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 29 Apr 2014 12:14:47 -0700 Subject: refs.c: remove the update_ref_lock function Since we now only call update_ref_lock with onerr==QUIET_ON_ERR we no longer need this function and can replace it with just calling lock_any_ref_for_update directly. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index d4cd44b..a4445a1 100644 --- a/refs.c +++ b/refs.c @@ -3336,24 +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) @@ -3603,12 +3585,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'.", -- cgit v0.10.2-6-g49f6 From 04ad6223ec1163163c4f39308149bee852ee245f Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 29 Apr 2014 13:42:07 -0700 Subject: refs.c: remove the update_ref_write function Since we only call update_ref_write from a single place and we only call it with onerr==QUIET_ON_ERR we can just as well get rid of it and just call write_ref_sha1 directly. This changes the return status for _commit from 1 to -1 on failures when writing to the ref. Eventually we will want _commit to start returning more detailed error conditions than the current simple success/failure. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index a4445a1..a6b39ec 100644 --- a/refs.c +++ b/refs.c @@ -3336,25 +3336,6 @@ int for_each_reflog(each_ref_fn fn, void *cb_data) return retval; } -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 @@ -3605,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; + } } } -- cgit v0.10.2-6-g49f6 From cba12021c3c932e42de838d0fc05d60b93790599 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 29 Apr 2014 15:45:52 -0700 Subject: refs.c: remove lock_ref_sha1 lock_ref_sha1 was only called from one place in refs.c and only provided a check that the refname was sane before adding back the initial "refs/" part of the ref path name, the initial "refs/" that this caller had already stripped off before calling lock_ref_sha1. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index a6b39ec..fd67684 100644 --- a/refs.c +++ b/refs.c @@ -2173,15 +2173,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, return NULL; } -static 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) @@ -2391,8 +2382,12 @@ 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_lock *lock; + + if (check_refname_format(r->name + 5, 0)) + return; + lock = lock_ref_sha1_basic(r->name, r->sha1, 0, NULL); if (lock) { unlink_or_warn(git_path("%s", r->name)); unlock_ref(lock); -- cgit v0.10.2-6-g49f6 From 029cdb4ab21c49a916efd68eaf2d2431c7fab7c7 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 30 Apr 2014 09:03:36 -0700 Subject: refs.c: make prune_ref use a transaction to delete the ref Change prune_ref to delete the ref using a ref transaction. To do this we also need to add a new flag REF_ISPRUNING that will tell the transaction that we do not want to delete this ref from the packed refs. This flag is private to refs.c and not exposed to external callers. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index fd67684..22eb3dd 100644 --- a/refs.c +++ b/refs.c @@ -25,6 +25,11 @@ static unsigned char refname_disposition[256] = { }; /* + * 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 * not legal. It is legal if it is something reasonable to have under @@ -2382,17 +2387,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; + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; if (check_refname_format(r->name + 5, 0)) return; - lock = lock_ref_sha1_basic(r->name, r->sha1, 0, NULL); - if (lock) { - unlink_or_warn(git_path("%s", r->name)); - unlock_ref(lock); - try_remove_empty_parents(r->name); + 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) @@ -3598,8 +3611,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; } } diff --git a/refs.h b/refs.h index 65dd593..69ef28c 100644 --- a/refs.h +++ b/refs.h @@ -170,9 +170,18 @@ extern int ref_exists(const char *); */ extern int peel_ref(const char *refname, unsigned char *sha1); -/** Locks any ref (for 'HEAD' type refs). */ +/* + * 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. + */ #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); -- cgit v0.10.2-6-g49f6 From 7521cc4611a783f4a8174bd0fcec5f4a47357ac1 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 30 Apr 2014 09:22:45 -0700 Subject: refs.c: make delete_ref use a transaction Change delete_ref to use a ref transaction for the deletion. At the same time since we no longer have any callers of repack_without_ref we can now delete this function. Change delete_ref to return 0 on success and 1 on failure instead of the previous 0 on success either 1 or -1 on failure. Signed-off-by: Ronnie Sahlberg Reviewed-by: Michael Haggerty Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 22eb3dd..7235574 100644 --- a/refs.c +++ b/refs.c @@ -2548,11 +2548,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) { @@ -2570,24 +2565,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; } /* -- cgit v0.10.2-6-g49f6 From ab5ac95725f7a4f36f72be48f340a84e46996e90 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 2 Sep 2014 14:10:52 -0700 Subject: update-ref --stdin: narrow scope of err strbuf Making the strbuf local in each function that needs to print errors saves the reader from having to think about action at a distance, such as * errors piling up and being concatenated with no newline between them * errors unhandled in one function, to be later handled in another * concurrency issues, if this code starts using threads some day No functional change intended. Signed-off-by: Jonathan Nieder Reviewed-by: Michael Haggerty Signed-off-by: Junio C Hamano diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 96a53b9..866bbee 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -16,7 +16,6 @@ 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 @@ -179,6 +178,7 @@ static int parse_next_sha1(struct strbuf *input, const char **next, static const char *parse_cmd_update(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 +204,14 @@ 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) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char new_sha1[20]; @@ -232,12 +234,14 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next) update_flags = 0; free(refname); + strbuf_release(&err); return next; } static const char *parse_cmd_delete(struct strbuf *input, const char *next) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char old_sha1[20]; int have_old; @@ -264,12 +268,14 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next) update_flags = 0; free(refname); + strbuf_release(&err); return next; } static const char *parse_cmd_verify(struct strbuf *input, const char *next) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char new_sha1[20]; unsigned char old_sha1[20]; @@ -297,6 +303,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next) update_flags = 0; free(refname); + strbuf_release(&err); return next; } @@ -365,6 +372,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("Refusing to perform update with empty message."); if (read_stdin) { + struct strbuf err = STRBUF_INIT; + transaction = ref_transaction_begin(&err); if (!transaction) die("%s", err.buf); -- cgit v0.10.2-6-g49f6 From 88499b296b5f62338d7fa4019c7b5f9012b4ab88 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 2 Sep 2014 14:11:21 -0700 Subject: update-ref --stdin: pass transaction around explicitly This makes it more obvious at a glance where the output of functions parsing the --stdin stream goes. No functional change intended. Signed-off-by: Jonathan Nieder Reviewed-by: Michael Haggerty Signed-off-by: Junio C Hamano diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 866bbee..54a48c0 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -12,8 +12,6 @@ static const char * const git_update_ref_usage[] = { NULL }; -static struct ref_transaction *transaction; - static char line_termination = '\n'; static int update_flags; @@ -176,7 +174,8 @@ 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; @@ -209,7 +208,8 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next) 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; @@ -239,7 +239,8 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next) 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; @@ -273,7 +274,8 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next) 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; @@ -317,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; @@ -332,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 @@ -373,6 +375,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (read_stdin) { struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; transaction = ref_transaction_begin(&err); if (!transaction) @@ -381,7 +384,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) 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); -- cgit v0.10.2-6-g49f6