diff options
author | Michael Haggerty <mhagger@alum.mit.edu> | 2015-06-22 17:03:04 +0300 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2015-06-22 23:17:12 +0300 |
commit | e426ff4222ba82a57ed459320509273dc8959ade (patch) | |
tree | 6beec8b70933e9068471a1c284b883e834acd885 /refs.c | |
parent | fb802b312961f49d0aa35f50e72a9a2d17fde9aa (diff) |
initial_ref_transaction_commit(): check for ref D/F conflicts
In initial_ref_transaction_commit(), check for D/F conflicts (i.e.,
the type of conflict that exists between "refs/foo" and
"refs/foo/bar") among the references being created and between the
references being created and any hypothetical existing references.
Ideally, there shouldn't *be* any existing references when this
function is called. But, at least in the case of the "testgit" remote
helper, "clone" can be called after the remote-tracking "HEAD" and
"master" branches have already been created. So let's just do the
full-blown check.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'refs.c')
-rw-r--r-- | refs.c | 34 |
1 files changed, 34 insertions, 0 deletions
@@ -4093,9 +4093,19 @@ cleanup: return ret; } +static int ref_present(const char *refname, + const struct object_id *oid, int flags, void *cb_data) +{ + struct string_list *affected_refnames = cb_data; + + return string_list_has_string(affected_refnames, refname); +} + int initial_ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { + struct ref_dir *loose_refs = get_loose_refs(&ref_cache); + struct ref_dir *packed_refs = get_packed_refs(&ref_cache); int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; @@ -4115,12 +4125,36 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, goto cleanup; } + /* + * It's really undefined to call this function in an active + * repository or when there are existing references: we are + * only locking and changing packed-refs, so (1) any + * simultaneous processes might try to change a reference at + * the same time we do, and (2) any existing loose versions of + * the references that we are setting would have precedence + * over our values. But some remote helpers create the remote + * "HEAD" and "master" branches before calling this function, + * so here we really only check that none of the references + * that we are creating already exists. + */ + if (for_each_rawref(ref_present, &affected_refnames)) + die("BUG: initial ref transaction called with existing refs"); + for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; if ((update->flags & REF_HAVE_OLD) && !is_null_sha1(update->old_sha1)) die("BUG: initial ref transaction with old_sha1 set"); + if (verify_refname_available(update->refname, + &affected_refnames, NULL, + loose_refs, err) || + verify_refname_available(update->refname, + &affected_refnames, NULL, + packed_refs, err)) { + ret = TRANSACTION_NAME_CONFLICT; + goto cleanup; + } } if (lock_packed_refs(0)) { |