git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 08/12] initial_ref_transaction_commit(): function for initial ref creation

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Jun 13, 2015, 14:42 UTC
Message-ID
<19f421f504ae22538fd2a902840209c0799e67cb.1434206062.git.mhagger@alum.mit.edu>
In-Reply-To
<cover.1434206062.git.mhagger@alum.mit.edu>

"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>
---
 builtin/clone.c | 19 +++++++++++++++----
 refs.c          | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 refs.h          | 14 ++++++++++++++
 3 files changed, 76 insertions(+), 4 deletions(-)
diff --git a/builtin/clone.c b/builtin/clone.c
index b878252..bd2a50a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -500,16 +500,27 @@ 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(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(err.buf);
+	}
+
+	if (initial_ref_transaction_commit(t, &err)) {
+		die(err.buf);
 	}
 
-	if (commit_packed_refs())
-		die_errno("unable to overwrite old ref-pack file");
+	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 832d628..05e5d42 100644
--- a/refs.c
+++ b/refs.c
@@ -4045,6 +4045,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 5f3bea7..9602889 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);
-- 
2.1.4
Previous: Michael HaggertyNext: Junio C Hamano
Message 15 of 30 in “Improve "refs" module encapsulation”
  1. 00/12 Improve "refs" module encapsulationMichael Haggerty, Jun 13, 2015
  2. 01/12 delete_ref(): move declaration to refs.hMichael Haggerty, Jun 13, 2015
  3. 02/12 remove_branches(): remove temporaryMichael Haggerty, Jun 13, 2015
  4. 03/12 delete_ref(): handle special case more explicitlyMichael Haggerty, Jun 13, 2015
  5. Junio C HamanoJun 15, 2015
  6. 04/12 delete_refs(): new function for the refs APIMichael Haggerty, Jun 13, 2015
  7. 05/12 delete_refs(): improve error messageMichael Haggerty, Jun 13, 2015
  8. Junio C HamanoJun 15, 2015
  9. Michael HaggertyJun 19, 2015
  10. 06/12 prune_remote(): use delete_refs()Michael Haggerty, Jun 13, 2015
  11. Junio C HamanoJun 15, 2015
  12. Jeff KingJun 15, 2015
  13. Junio C HamanoJun 15, 2015
  14. 07/12 repack_without_refs(): make function privateMichael Haggerty, Jun 13, 2015
  15. 08/12 initial_ref_transaction_commit(): function for initial ref creationMichael Haggerty, Jun 13, 2015
  16. Junio C HamanoJun 15, 2015
  17. Junio C HamanoJun 15, 2015
  18. Jeff KingJun 15, 2015
  19. Junio C HamanoJun 15, 2015
  20. Michael HaggertyJun 20, 2015
  21. 09/12 refs: remove some functions from the module's public interfaceMichael Haggerty, Jun 13, 2015
  22. 10/12 initial_ref_transaction_commit(): check for duplicate refsMichael Haggerty, Jun 13, 2015
  23. 11/12 initial_ref_transaction_commit(): check for ref D/F conflictsMichael Haggerty, Jun 13, 2015
  24. 12/12 refs: move the remaining ref module declarations to refs.hMichael Haggerty, Jun 13, 2015
  25. Junio C HamanoJun 15, 2015
  26. Jeff KingJun 15, 2015
  27. Junio C HamanoJun 15, 2015
  28. Michael HaggertyJun 20, 2015
  29. Junio C HamanoJun 20, 2015
  30. Stefan BellerJun 15, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.