initial_ref_transaction_commit(): function for initial ref creation

"git clone" uses shortcuts when creating the initial set of
references:

* It writes them directly to packed-refs.

* It doesn't lock the individual references (though it does lock the
  packed-refs file).

* It doesn't check for refname conflicts between two new references or
  between one new reference and any hypothetical old ones.

* It doesn't create reflog entries for the reference creations.

This functionality was implemented in builtin/clone.c. But really that
file shouldn't have such intimate knowledge of how references are
stored. So provide a new function in the refs API,
initial_ref_transaction_commit(), which can be used for initial
reference creation. The new function is based on the ref_transaction
interface.

This means that we can make some other functions private to the refs
module. That will be done in a followup commit.

It would seem to make sense to add a test here that there are no
existing references, because that is how the function *should* be
used. But in fact, the "testgit" remote helper appears to call it
*after* having set up refs/remotes/<name>/HEAD and
refs/remotes/<name>/master, so we can't be so strict. For now, the
function trusts its caller to only call it when it makes sense. Future
commits will add some more limited sanity checks.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Michael Haggerty 2015-06-22 16:03:01 +02:00 committed by Junio C Hamano
parent 79e4d8a9b8
commit 58f233ce1e
3 changed files with 75 additions and 4 deletions

View File

@ -491,16 +491,26 @@ static void write_remote_refs(const struct ref *local_refs)
{
const struct ref *r;
lock_packed_refs(LOCK_DIE_ON_ERROR);
struct ref_transaction *t;
struct strbuf err = STRBUF_INIT;
t = ref_transaction_begin(&err);
if (!t)
die("%s", err.buf);
for (r = local_refs; r; r = r->next) {
if (!r->peer_ref)
continue;
add_packed_ref(r->peer_ref->name, r->old_sha1);
if (ref_transaction_create(t, r->peer_ref->name, r->old_sha1,
0, NULL, &err))
die("%s", err.buf);
}
if (commit_packed_refs())
die_errno("unable to overwrite old ref-pack file");
if (initial_ref_transaction_commit(t, &err))
die("%s", err.buf);
strbuf_release(&err);
ref_transaction_free(t);
}
static void write_followtags(const struct ref *refs, const char *msg)

47
refs.c
View File

@ -4076,6 +4076,53 @@ cleanup:
return ret;
}
int initial_ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
int ret = 0, i;
int n = transaction->nr;
struct ref_update **updates = transaction->updates;
assert(err);
if (transaction->state != REF_TRANSACTION_OPEN)
die("BUG: commit called for transaction that is not open");
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 (lock_packed_refs(0)) {
strbuf_addf(err, "unable to lock packed-refs file: %s",
strerror(errno));
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
if ((update->flags & REF_HAVE_NEW) &&
!is_null_sha1(update->new_sha1))
add_packed_ref(update->refname, update->new_sha1);
}
if (commit_packed_refs()) {
strbuf_addf(err, "unable to commit packed-refs file: %s",
strerror(errno));
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
cleanup:
transaction->state = REF_TRANSACTION_CLOSED;
return ret;
}
char *shorten_unambiguous_ref(const char *refname, int strict)
{
int i;

14
refs.h
View File

@ -365,6 +365,20 @@ int ref_transaction_verify(struct ref_transaction *transaction,
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
/*
* Like ref_transaction_commit(), but optimized for creating
* references when originally initializing a repository (e.g., by "git
* clone"). It writes the new references directly to packed-refs
* without locking the individual references.
*
* It is a bug to call this function when there might be other
* processes accessing the repository or if there are existing
* references that might conflict with the ones being created. All
* old_sha1 values must either be absent or NULL_SHA1.
*/
int initial_ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
/*
* Free an existing transaction and all associated data.
*/