From: Junio C Hamano Date: Fri, 20 Sep 2013 19:36:12 +0000 (-0700) Subject: Merge branch 'bk/refs-multi-update' X-Git-Tag: v1.8.5-rc0~94 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9a86b899415c8a49cc33d6b43bcab8113ddca517;p=thirdparty%2Fgit.git Merge branch 'bk/refs-multi-update' Give "update-refs" a "--stdin" option to read multiple update requests and perform them in an all-or-none fashion. * bk/refs-multi-update: update-ref: add test cases covering --stdin signature update-ref: support multiple simultaneous updates refs: add update_refs for multiple simultaneous updates refs: add function to repack without multiple refs refs: factor delete_ref loose ref step into a helper refs: factor update_ref steps into helpers refs: report ref type from lock_any_ref_for_update reset: rename update_refs to reset_refs --- 9a86b899415c8a49cc33d6b43bcab8113ddca517 diff --cc builtin/reset.c index 088ccffba0,789ee489b7..1a53448772 --- a/builtin/reset.c +++ b/builtin/reset.c @@@ -220,13 -216,10 +220,13 @@@ static void parse_args(struct pathspec } } *rev_ret = rev; - return argv[0] ? get_pathspec(prefix, argv) : NULL; + parse_pathspec(pathspec, 0, + PATHSPEC_PREFER_FULL | + (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0), + prefix, argv); } - static int update_refs(const char *rev, const unsigned char *sha1) + static int reset_refs(const char *rev, const unsigned char *sha1) { int update_ref_status; struct strbuf msg = STRBUF_INIT; @@@ -351,10 -347,10 +351,10 @@@ int cmd_reset(int argc, const char **ar die(_("Could not write new index file.")); } - if (!pathspec && !unborn) { + if (!pathspec.nr && !unborn) { /* Any resets without paths update HEAD to the head being * switched to, saving the previous head in ORIG_HEAD before. */ - update_ref_status = update_refs(rev, sha1); + update_ref_status = reset_refs(rev, sha1); if (reset_type == HARD && !update_ref_status && !quiet) print_new_head_line(lookup_commit_reference(sha1)); diff --cc builtin/update-ref.c index 7484d36a65,894f16bc59..702e90db2a --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@@ -13,12 -249,14 +249,14 @@@ int cmd_update_ref(int argc, const cha { const char *refname, *oldval, *msg = NULL; unsigned char sha1[20], oldsha1[20]; - int delete = 0, no_deref = 0, flags = 0; + int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0; struct option options[] = { OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), - OPT_BOOLEAN('d', NULL, &delete, N_("delete the reference")), - OPT_BOOLEAN('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")), - OPT_BOOLEAN( 0 , "no-deref", &no_deref, + OPT_BOOL('d', NULL, &delete, N_("delete the reference")), + OPT_BOOL( 0 , "no-deref", &no_deref, N_("update not the one it points to")), - OPT_BOOLEAN( 0 , "stdin", &read_stdin, N_("read updates from stdin")), ++ OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")), ++ OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")), OPT_END(), }; diff --cc refs.c index d78860c46d,46177ad256..6383813627 --- a/refs.c +++ b/refs.c @@@ -3196,6 -3226,125 +3226,117 @@@ static int update_ref_write(const char 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, 0, onerr); + if (!lock) + return 1; + return update_ref_write(action, refname, sha1, lock, onerr); + } + + static int ref_update_compare(const void *r1, const void *r2) + { + const struct ref_update * const *u1 = r1; + const struct ref_update * const *u2 = r2; + return strcmp((*u1)->ref_name, (*u2)->ref_name); + } + + static int ref_update_reject_duplicates(struct ref_update **updates, int n, + enum action_on_err onerr) + { + int i; + for (i = 1; i < n; i++) + if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) { + const char *str = + "Multiple updates for ref '%s' not allowed."; + switch (onerr) { + case MSG_ON_ERR: + error(str, updates[i]->ref_name); break; + case DIE_ON_ERR: + die(str, updates[i]->ref_name); break; + case QUIET_ON_ERR: + break; + } + return 1; + } + return 0; + } + + int update_refs(const char *action, const struct ref_update **updates_orig, + int n, enum action_on_err onerr) + { + int ret = 0, delnum = 0, i; + struct ref_update **updates; + int *types; + struct ref_lock **locks; + const char **delnames; + + if (!updates_orig || !n) + return 0; + + /* Allocate work space */ + updates = xmalloc(sizeof(*updates) * n); + types = xmalloc(sizeof(*types) * n); + locks = xcalloc(n, sizeof(*locks)); + delnames = xmalloc(sizeof(*delnames) * n); + + /* Copy, sort, and reject duplicate refs */ + memcpy(updates, updates_orig, sizeof(*updates) * n); + qsort(updates, n, sizeof(*updates), ref_update_compare); + ret = ref_update_reject_duplicates(updates, n, onerr); + if (ret) + goto cleanup; + + /* Acquire all locks while verifying old values */ + for (i = 0; i < n; i++) { + locks[i] = update_ref_lock(updates[i]->ref_name, + (updates[i]->have_old ? + updates[i]->old_sha1 : NULL), + updates[i]->flags, + &types[i], onerr); + if (!locks[i]) { + ret = 1; + goto cleanup; + } + } + + /* Perform updates first so live commits remain referenced */ + for (i = 0; i < n; i++) + if (!is_null_sha1(updates[i]->new_sha1)) { + ret = update_ref_write(action, + updates[i]->ref_name, + updates[i]->new_sha1, + locks[i], onerr); + locks[i] = NULL; /* freed by update_ref_write */ + if (ret) + goto cleanup; + } + + /* Perform deletes now that updates are safely completed */ + for (i = 0; i < n; i++) + if (locks[i]) { + delnames[delnum++] = locks[i]->ref_name; + ret |= delete_ref_loose(locks[i], types[i]); + } + ret |= repack_without_refs(delnames, delnum); + for (i = 0; i < delnum; i++) + unlink_or_warn(git_path("logs/%s", delnames[i])); + clear_loose_ref_cache(&ref_cache); + + cleanup: + for (i = 0; i < n; i++) + if (locks[i]) + unlock_ref(locks[i]); + free(updates); + free(types); + free(locks); + free(delnames); + return ret; + } + -struct ref *find_ref_by_name(const struct ref *list, const char *name) -{ - for ( ; list; list = list->next) - if (!strcmp(list->name, name)) - return (struct ref *)list; - return NULL; -} - /* * generate a format suitable for scanf from a ref_rev_parse_rules * rule, that is replace the "%.*s" spec with a "%s" spec