aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Haggerty <mhagger@alum.mit.edu>2015-06-22 16:03:01 +0200
committerJunio C Hamano <gitster@pobox.com>2015-06-22 13:17:11 -0700
commit58f233ce1ed67bbc31a429fde5c65d5050fdbd7d (patch)
tree8f767c602864bd958f26d0870c56322f256dac11
parent79e4d8a9b839ed18816e78ed52f0ff5a5f6e9a63 (diff)
downloadgit-58f233ce1ed67bbc31a429fde5c65d5050fdbd7d.tar.gz
git-58f233ce1ed67bbc31a429fde5c65d5050fdbd7d.tar.xz
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>
-rw-r--r--builtin/clone.c18
-rw-r--r--refs.c47
-rw-r--r--refs.h14
3 files changed, 75 insertions, 4 deletions
diff --git a/builtin/clone.c b/builtin/clone.c
index 00535d017..8539b8de6 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -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)
diff --git a/refs.c b/refs.c
index 081ea6ead..c6f2f64bc 100644
--- a/refs.c
+++ b/refs.c
@@ -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;
diff --git a/refs.h b/refs.h
index 5f3bea7a4..9602889c3 100644
--- a/refs.h
+++ b/refs.h
@@ -366,6 +366,20 @@ 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.
*/
void ref_transaction_free(struct ref_transaction *transaction);