threads / patch / 42524

patch, 38 partsresolve_gitlink_ref(): eliminate temporary variable

Subject: [PATCH 01/38] resolve_gitlink_ref(): eliminate temporary variable

## tl;dr

67 messages between Jun 3, 2016 and Jul 13, 2016. Diffs are folded; open one to read it.

replies: 66people: 5as markdown or json

Michael Haggerty· Jun 3, 2016, 21:03 UTC · lore

[PATCH 00/38] Virtualization of the refs API

Since the that ref-iterator [1] changes seem to have gotten a positive reception, let's try to keep up the momentum. I hope I'm not overloading the review pipeline...

I think all of the groundwork is in place now to virtualize the refs API. This will open the way to storing refs in ways other than the familiar loose refs / packed refs format, such as David Turner's proposed LMDB-based storage [2].

This is a long patch series, but most of the patches are pretty simple and formulaic. The goal is to implement a `ref_store`. In the language of object-oriented programming, `ref_store` is an abstract base class representing a reference storage backend. It provides methods to read, write, and delete references and symrefs, and to iterate over references, reflogs, and reflog entries, plus a number of other things—19 methods in all.

The one concrete implementation of this class is files_ref_store, which implements the traditional loose/packed refs scheme with caching. After this patch series, about the only things left in `refs/files-backend.c` with external linkage are vtables.

On these 19 methods are built the refs API as used by the rest of Git. The OO interface is not exposed; instead, the OO implementation is wrapped in traditional C functions. In fact, this patch series doesn't change the existing public interface at all, and adds only two new functions!

Somebody who wants to implement a new way to store references needs to implement a new class derived from `ref_store`, including its 19 methods, plus one or two supporting ref_iterator classes, and wire it up to be instantiated when needed.

This patch series opens up a lot of possibilities for improving the internal design of the files backend; I've described some of my plans in recent emails [3,4]. But that can be done separately; for now, the main point of this series is to clear the way for LMDB-based reference storage.

This patch series owes a lot to Ronnie Sahlberg and David Turner, who wrote earlier drafts along the same basic lines. The current patch series differs from theirs in many details of where exactly to draw the line of abstraction. But the most important difference in my opinion is that this series allows multiple ref_stores of different types to coexist. I think this is important for the UI (for example, if somebody converts a submodule to use a different reference storage scheme than the umbrella repository uses), but also because it allows ref_stores to be compounded together internally to decrease the coupling between different parts of the system. I've tried to retain their authorship of any patches that are more or less recognizable from their versions (hopefully without introducing any bugs!) But the spirit of their versions permeates this patch series. Thanks a lot to both of you!

This series applies on top of the "ref-iterator v2" series that I just submitted [1]. It can also be obtained from my GitHub repo [5] as branch "ref-store".

Michael

[1] http://thread.gmane.org/gmane.comp.version-control.git/296322 [2] http://thread.gmane.org/gmane.comp.version-control.git/286572 [3] http://thread.gmane.org/gmane.comp.version-control.git/295961/focus=296096 [4] http://thread.gmane.org/gmane.comp.version-control.git/295961/focus=296186 [5] https://github.com/mhagger/git

David Turner (8):
  rename_ref_available(): add docstring
  refs: add methods for reflog
  refs: add method for initial ref transaction commit
  refs: add method for delete_refs
  refs: add methods to init refs db
  refs: add method to rename refs
  refs: make lock generic
  refs: implement iteration over only per-worktree refs
Michael Haggerty (28):
  resolve_gitlink_ref(): eliminate temporary variable
  refs: rename struct ref_cache to files_ref_store
  refs: create a base class "ref_store" for files_ref_store
  add_packed_ref(): add a files_ref_store argument
  get_packed_ref(): add a files_ref_store argument
  resolve_missing_loose_ref(): add a files_ref_store argument
  {lock,commit,rollback}_packed_refs(): add files_ref_store arguments
  refs: reorder definitions
  resolve_packed_ref(): rename function from resolve_missing_loose_ref()
  resolve_gitlink_packed_ref(): remove function
  read_raw_ref(): take a (struct ref_store *) argument
  resolve_ref_recursively(): new function
  resolve_gitlink_ref(): implement using resolve_ref_recursively()
  resolve_gitlink_ref(): avoid memory allocation in many cases
  resolve_gitlink_ref(): rename path parameter to submodule
  refs: make read_raw_ref() virtual
  refs: make verify_refname_available() virtual
  refs: make pack_refs() virtual
  refs: make create_symref() virtual
  refs: make peel_ref() virtual
  repack_without_refs(): add a files_ref_store argument
  lock_raw_ref(): add a files_ref_store argument
  commit_ref_update(): add a files_ref_store argument
  lock_ref_for_update(): add a files_ref_store argument
  lock_ref_sha1_basic(): add a files_ref_store argument
  split_symref_update(): add a files_ref_store argument
  files_ref_iterator_begin(): take a ref_store argument
  refs: add method iterator_begin
Ronnie Sahlberg (2):
  refs: add a backend method structure
  refs: add a transaction_commit() method
 builtin/init-db.c    |  21 +-
 refs.c               | 294 +++++++++++++++++++++++++-
 refs.h               |  13 +-
 refs/files-backend.c | 567 +++++++++++++++++++++++++++------------------------
 refs/refs-internal.h | 213 +++++++++++++++++--
 5 files changed, 799 insertions(+), 309 deletions(-)
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
Show changes to refs/files-backend.c +2 −3
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 36bf128..2f9d79a 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1387,7 +1387,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
 
 int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
 {
-	int len = strlen(path), retval;
+	int len = strlen(path);
 	struct strbuf submodule = STRBUF_INIT;
 	struct ref_cache *refs;
 
@@ -1404,8 +1404,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
 	}
 	strbuf_release(&submodule);
 
-	retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
-	return retval;
+	return resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
 }
 
 /*
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 02/38] rename_ref_available(): add docstring

From: David Turner <dturner@twopensource.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/refs-internal.h | 5 +++++
 1 file changed, 5 insertions(+)
Show changes to refs/refs-internal.h +5 −0
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index efe5847..d8a2606 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -240,6 +240,11 @@ const char *find_descendant_ref(const char *dirname,
 				const struct string_list *extras,
 				const struct string_list *skip);
 
+/*
+ * Check if the new name does not conflict with any existing refs
+ * (other than possibly the old ref).  Return 0 if the ref can be
+ * renamed to the new name.
+ */
 int rename_ref_available(const char *oldname, const char *newname);
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 18:10 UTC · re: Michael Haggerty · lore

Re: [PATCH 02/38] rename_ref_available(): add docstring

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 23 quoted lines
> From: David Turner <dturner@twopensource.com>
>
> Signed-off-by: David Turner <dturner@twopensource.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  refs/refs-internal.h | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> index efe5847..d8a2606 100644
> --- a/refs/refs-internal.h
> +++ b/refs/refs-internal.h
> @@ -240,6 +240,11 @@ const char *find_descendant_ref(const char *dirname,
>  				const struct string_list *extras,
>  				const struct string_list *skip);
>  
> +/*
> + * Check if the new name does not conflict with any existing refs
> + * (other than possibly the old ref).  Return 0 if the ref can be
> + * renamed to the new name.
> + */
>  int rename_ref_available(const char *oldname, const char *newname);

I do not quite understand the comment. Partly because it is unclear what "conflict" means here, but I guess it means a D/F conflict that is explained near verify_refname_available()?

A new name can conflict with an existing, possibly old ref? Are you referring to this condition?

    You are trying to rename "refs/a/b" to "refs/a", which would
    conflict, but as long as there is no other ref that share the
    prefix "refs/a/", e.g. "refs/a/c", the new name "refs/a" is
    available.

I wonder if it is necessary to document that this function is not meant to protect us from others racing with us. That is, when you are renaming something to "refs/a", you call this function and it finds, by calling verify_refname_available(), that the repository has nothing that conflicts with the name and says "OK", but before you actually do the rename, somebody may push from sideways to create "refs/a/b", making the result of an earlier check with this function invalid.

Or is this to be called only under a lock that protects us from such a race?

Michael Haggerty· Jun 9, 2016, 13:09 UTC · re: Junio C Hamano · lore

Re: [PATCH 02/38] rename_ref_available(): add docstring

On 06/07/2016 08:10 PM, Junio C Hamano wrote:
Show 29 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> From: David Turner <dturner@twopensource.com>
>>
>> Signed-off-by: David Turner <dturner@twopensource.com>
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
>>  refs/refs-internal.h | 5 +++++
>>  1 file changed, 5 insertions(+)
>>
>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
>> index efe5847..d8a2606 100644
>> --- a/refs/refs-internal.h
>> +++ b/refs/refs-internal.h
>> @@ -240,6 +240,11 @@ const char *find_descendant_ref(const char *dirname,
>>  				const struct string_list *extras,
>>  				const struct string_list *skip);
>>  
>> +/*
>> + * Check if the new name does not conflict with any existing refs
>> + * (other than possibly the old ref).  Return 0 if the ref can be
>> + * renamed to the new name.
>> + */
>>  int rename_ref_available(const char *oldname, const char *newname);
> 
> I do not quite understand the comment.  Partly because it is unclear
> what "conflict" means here, but I guess it means a D/F conflict that
> is explained near verify_refname_available()?
Yes.
Show 7 quoted lines
> A new name can conflict with an existing, possibly old ref?  Are you
> referring to this condition?
> 
>     You are trying to rename "refs/a/b" to "refs/a", which would
>     conflict, but as long as there is no other ref that share the
>     prefix "refs/a/", e.g. "refs/a/c", the new name "refs/a" is
>     available.
That is correct.
Show 11 quoted lines
> I wonder if it is necessary to document that this function is not
> meant to protect us from others racing with us.  That is, when you
> are renaming something to "refs/a", you call this function and it
> finds, by calling verify_refname_available(), that the repository
> has nothing that conflicts with the name and says "OK", but before
> you actually do the rename, somebody may push from sideways to
> create "refs/a/b", making the result of an earlier check with this
> function invalid.
> 
> Or is this to be called only under a lock that protects us from such
> a race?

It would be really awkward (maybe impossible?) to guard against all such races even using locks. One problem is that the lockfiles for the old and new refnames would themselves, in some cases, not be able to coexist due to D/F conflicts. Also, there is no way to prevent the creation of "any reference in `refs/a/*`" except by creating the reference `refs/a` (the presence of `refs/a.lock` is not enough), but by that time it is too late.

In the end, this function mostly exists as a pre-check that `rename_ref()` is *likely* to succeed, so that the latter function is less likely to detect a problem after it has started moving things around. But `rename_ref()` is the final arbiter and is a bit more robust than this check.

I also noticed that the docstring in this patch got the polarity of the return value backwards.

I propose to change the parameter names to `old_refname` and `new_refname` and to change the docstring to

/*
 * Check whether an attempt to rename old_refname to new_refname would
 * cause a D/F conflict with any existing reference (other than
 * possibly old_refname). If there would be a conflict, emit an error
 * message and return false; otherwise, return true.
 *
 * Note that this function is not safe against all races with other
 * processes (though rename_ref() catches some races that might get by
 * this check).
 */
Does that sound good?
Michael
Junio C Hamano· Jun 9, 2016, 15:08 UTC · re: Michael Haggerty · lore

Re: [PATCH 02/38] rename_ref_available(): add docstring

Michael Haggerty <mhagger@alum.mit.edu> writes:
> I propose to change the parameter names to `old_refname` and
> `new_refname` and to change the docstring to
> ...
> Does that sound good?
Yes.
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 03/38] refs: rename struct ref_cache to files_ref_store

The greater goal of this patch series is to develop the concept of a reference store, which is a place that references, their values, and their reflogs are stored, and to virtualize the reference interface so that different types of ref_stores can be implemented. We will then, for example, use ref_store instances to access submodule references and worktree references.

Currently, we keep a ref_cache for each submodule that has had its references iterated over. It is a far cry from a ref_store, but they are stored the way we will want to store ref_stores, and ref_stores will eventually have to hold the reference caches. So let's treat ref_caches as embryo ref_stores, and build them out from there.

As the first step, simply rename `ref_cache` to `files_ref_store`, and rename some functions and attributes correspondingly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 126 +++++++++++++++++++++++++--------------------------
 1 file changed, 63 insertions(+), 63 deletions(-)
Show changes to refs/files-backend.c +63 −63
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 2f9d79a..b94aad2 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -39,7 +39,7 @@ struct ref_value {
 	struct object_id peeled;
 };
 
-struct ref_cache;
+struct files_ref_store;
 
 /*
  * Information used (along with the information in ref_entry) to
@@ -78,8 +78,8 @@ struct ref_dir {
 	 */
 	int sorted;
 
-	/* A pointer to the ref_cache that contains this ref_dir. */
-	struct ref_cache *ref_cache;
+	/* A pointer to the files_ref_store that contains this ref_dir. */
+	struct files_ref_store *ref_store;
 
 	struct ref_entry **entries;
 };
@@ -161,7 +161,7 @@ struct ref_entry {
 
 static void read_loose_refs(const char *dirname, struct ref_dir *dir);
 static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
-static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
 					  const char *dirname, size_t len,
 					  int incomplete);
 static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
@@ -183,7 +183,7 @@ static struct ref_dir *get_ref_dir(struct ref_entry *entry)
 			int pos = search_ref_dir(dir, "refs/bisect/", 12);
 			if (pos < 0) {
 				struct ref_entry *child_entry;
-				child_entry = create_dir_entry(dir->ref_cache,
+				child_entry = create_dir_entry(dir->ref_store,
 							       "refs/bisect/",
 							       12, 1);
 				add_entry_to_dir(dir, child_entry);
@@ -261,13 +261,13 @@ static void clear_ref_dir(struct ref_dir *dir)
  * dirname is the name of the directory with a trailing slash (e.g.,
  * "refs/heads/") or "" for the top-level directory.
  */
-static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
 					  const char *dirname, size_t len,
 					  int incomplete)
 {
 	struct ref_entry *direntry;
 	FLEX_ALLOC_MEM(direntry, name, dirname, len);
-	direntry->u.subdir.ref_cache = ref_cache;
+	direntry->u.subdir.ref_store = ref_store;
 	direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
 	return direntry;
 }
@@ -343,7 +343,7 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
 		 * therefore, create an empty record for it but mark
 		 * the record complete.
 		 */
-		entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
+		entry = create_dir_entry(dir->ref_store, subdirname, len, 0);
 		add_entry_to_dir(dir, entry);
 	} else {
 		entry = dir->entries[entry_index];
@@ -887,9 +887,9 @@ struct packed_ref_cache {
 
 	/*
 	 * Count of references to the data structure in this instance,
-	 * including the pointer from ref_cache::packed if any.  The
-	 * data will not be freed as long as the reference count is
-	 * nonzero.
+	 * including the pointer from files_ref_store::packed if any.
+	 * The data will not be freed as long as the reference count
+	 * is nonzero.
 	 */
 	unsigned int referrers;
 
@@ -910,17 +910,17 @@ struct packed_ref_cache {
  * Future: need to be in "struct repository"
  * when doing a full libification.
  */
-static struct ref_cache {
-	struct ref_cache *next;
+static struct files_ref_store {
+	struct files_ref_store *next;
 	struct ref_entry *loose;
 	struct packed_ref_cache *packed;
 	/*
-	 * The submodule name, or "" for the main repo.  We allocate
-	 * length 1 rather than FLEX_ARRAY so that the main ref_cache
-	 * is initialized correctly.
+	 * The submodule name, or "" for the main repo. We allocate
+	 * length 1 rather than FLEX_ARRAY so that the main
+	 * files_ref_store is initialized correctly.
 	 */
 	char name[1];
-} ref_cache, *submodule_ref_caches;
+} ref_store, *submodule_ref_stores;
 
 /* Lock used for the main packed-refs file: */
 static struct lock_file packlock;
@@ -949,7 +949,7 @@ static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
 	}
 }
 
-static void clear_packed_ref_cache(struct ref_cache *refs)
+static void clear_packed_ref_cache(struct files_ref_store *refs)
 {
 	if (refs->packed) {
 		struct packed_ref_cache *packed_refs = refs->packed;
@@ -961,7 +961,7 @@ static void clear_packed_ref_cache(struct ref_cache *refs)
 	}
 }
 
-static void clear_loose_ref_cache(struct ref_cache *refs)
+static void clear_loose_ref_cache(struct files_ref_store *refs)
 {
 	if (refs->loose) {
 		free_ref_entry(refs->loose);
@@ -973,32 +973,32 @@ static void clear_loose_ref_cache(struct ref_cache *refs)
  * Create a new submodule ref cache and add it to the internal
  * set of caches.
  */
-static struct ref_cache *create_ref_cache(const char *submodule)
+static struct files_ref_store *create_ref_store(const char *submodule)
 {
-	struct ref_cache *refs;
+	struct files_ref_store *refs;
 	if (!submodule)
 		submodule = "";
 	FLEX_ALLOC_STR(refs, name, submodule);
-	refs->next = submodule_ref_caches;
-	submodule_ref_caches = refs;
+	refs->next = submodule_ref_stores;
+	submodule_ref_stores = refs;
 	return refs;
 }
 
-static struct ref_cache *lookup_ref_cache(const char *submodule)
+static struct files_ref_store *lookup_ref_store(const char *submodule)
 {
-	struct ref_cache *refs;
+	struct files_ref_store *refs;
 
 	if (!submodule || !*submodule)
-		return &ref_cache;
+		return &ref_store;
 
-	for (refs = submodule_ref_caches; refs; refs = refs->next)
+	for (refs = submodule_ref_stores; refs; refs = refs->next)
 		if (!strcmp(submodule, refs->name))
 			return refs;
 	return NULL;
 }
 
 /*
- * Return a pointer to a ref_cache for the specified submodule. For
+ * Return a pointer to a files_ref_store for the specified submodule. For
  * the main repository, use submodule==NULL; such a call cannot fail.
  * For a submodule, the submodule must exist and be a nonbare
  * repository, otherwise return NULL.
@@ -1006,16 +1006,16 @@ static struct ref_cache *lookup_ref_cache(const char *submodule)
  * The returned structure will be allocated and initialized but not
  * necessarily populated; it should not be freed.
  */
-static struct ref_cache *get_ref_cache(const char *submodule)
+static struct files_ref_store *get_ref_store(const char *submodule)
 {
-	struct ref_cache *refs = lookup_ref_cache(submodule);
+	struct files_ref_store *refs = lookup_ref_store(submodule);
 
 	if (!refs) {
 		struct strbuf submodule_sb = STRBUF_INIT;
 
 		strbuf_addstr(&submodule_sb, submodule);
 		if (is_nonbare_repository_dir(&submodule_sb))
-			refs = create_ref_cache(submodule);
+			refs = create_ref_store(submodule);
 		strbuf_release(&submodule_sb);
 	}
 
@@ -1151,10 +1151,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
 }
 
 /*
- * Get the packed_ref_cache for the specified ref_cache, creating it
- * if necessary.
+ * Get the packed_ref_cache for the specified files_ref_store,
+ * creating it if necessary.
  */
-static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
+static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
 {
 	char *packed_refs_file;
 
@@ -1189,7 +1189,7 @@ static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_ca
 	return get_ref_dir(packed_ref_cache->root);
 }
 
-static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
 {
 	return get_packed_ref_dir(get_packed_ref_cache(refs));
 }
@@ -1203,7 +1203,7 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)
 static void add_packed_ref(const char *refname, const unsigned char *sha1)
 {
 	struct packed_ref_cache *packed_ref_cache =
-		get_packed_ref_cache(&ref_cache);
+		get_packed_ref_cache(&ref_store);
 
 	if (!packed_ref_cache->lock)
 		die("internal error: packed refs not locked");
@@ -1218,7 +1218,7 @@ static void add_packed_ref(const char *refname, const unsigned char *sha1)
  */
 static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 {
-	struct ref_cache *refs = dir->ref_cache;
+	struct files_ref_store *refs = dir->ref_store;
 	DIR *d;
 	struct dirent *de;
 	int dirnamelen = strlen(dirname);
@@ -1306,7 +1306,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 	closedir(d);
 }
 
-static struct ref_dir *get_loose_refs(struct ref_cache *refs)
+static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
 {
 	if (!refs->loose) {
 		/*
@@ -1328,10 +1328,10 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs)
 
 /*
  * Called by resolve_gitlink_ref_recursive() after it failed to read
- * from the loose refs in ref_cache refs. Find <refname> in the
- * packed-refs file for the submodule.
+ * from the loose refs in refs. Find <refname> in the packed-refs file
+ * for the submodule.
  */
-static int resolve_gitlink_packed_ref(struct ref_cache *refs,
+static int resolve_gitlink_packed_ref(struct files_ref_store *refs,
 				      const char *refname, unsigned char *sha1)
 {
 	struct ref_entry *ref;
@@ -1345,7 +1345,7 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,
 	return 0;
 }
 
-static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
+static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
 					 const char *refname, unsigned char *sha1,
 					 int recursion)
 {
@@ -1389,7 +1389,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
 {
 	int len = strlen(path);
 	struct strbuf submodule = STRBUF_INIT;
-	struct ref_cache *refs;
+	struct files_ref_store *refs;
 
 	while (len && path[len-1] == '/')
 		len--;
@@ -1397,7 +1397,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
 		return -1;
 
 	strbuf_add(&submodule, path, len);
-	refs = get_ref_cache(submodule.buf);
+	refs = get_ref_store(submodule.buf);
 	if (!refs) {
 		strbuf_release(&submodule);
 		return -1;
@@ -1413,7 +1413,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
  */
 static struct ref_entry *get_packed_ref(const char *refname)
 {
-	return find_ref(get_packed_refs(&ref_cache), refname);
+	return find_ref(get_packed_refs(&ref_store), refname);
 }
 
 /*
@@ -1745,7 +1745,7 @@ retry:
 							  REMOVE_DIR_EMPTY_ONLY)) {
 				if (verify_refname_available_dir(
 						    refname, extras, skip,
-						    get_loose_refs(&ref_cache),
+						    get_loose_refs(&ref_store),
 						    err)) {
 					/*
 					 * The error message set by
@@ -1784,7 +1784,7 @@ retry:
 		 */
 		if (verify_refname_available_dir(
 				    refname, extras, skip,
-				    get_packed_refs(&ref_cache),
+				    get_packed_refs(&ref_store),
 				    err)) {
 			goto error_return;
 		}
@@ -1942,7 +1942,7 @@ struct ref_iterator *files_ref_iterator_begin(
 		const char *submodule,
 		const char *prefix, unsigned int flags)
 {
-	struct ref_cache *refs = get_ref_cache(submodule);
+	struct files_ref_store *refs = get_ref_store(submodule);
 	struct ref_dir *loose_dir, *packed_dir;
 	struct ref_iterator *loose_iter, *packed_iter;
 	struct files_ref_iterator *iter;
@@ -2096,7 +2096,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 		if (remove_empty_directories(&ref_file)) {
 			last_errno = errno;
 			if (!verify_refname_available_dir(refname, extras, skip,
-							  get_loose_refs(&ref_cache), err))
+							  get_loose_refs(&ref_store), err))
 				strbuf_addf(err, "there are still refs under '%s'",
 					    refname);
 			goto error_return;
@@ -2108,7 +2108,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 		last_errno = errno;
 		if (last_errno != ENOTDIR ||
 		    !verify_refname_available_dir(refname, extras, skip,
-						  get_loose_refs(&ref_cache), err))
+						  get_loose_refs(&ref_store), err))
 			strbuf_addf(err, "unable to resolve reference '%s': %s",
 				    refname, strerror(last_errno));
 
@@ -2123,7 +2123,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 	 */
 	if (is_null_oid(&lock->old_oid) &&
 	    verify_refname_available_dir(refname, extras, skip,
-					 get_packed_refs(&ref_cache), err)) {
+					 get_packed_refs(&ref_store), err)) {
 		last_errno = ENOTDIR;
 		goto error_return;
 	}
@@ -2232,7 +2232,7 @@ static int lock_packed_refs(int flags)
 	 * this will automatically invalidate the cache and re-read
 	 * the packed-refs file.
 	 */
-	packed_ref_cache = get_packed_ref_cache(&ref_cache);
+	packed_ref_cache = get_packed_ref_cache(&ref_store);
 	packed_ref_cache->lock = &packlock;
 	/* Increment the reference count to prevent it from being freed: */
 	acquire_packed_ref_cache(packed_ref_cache);
@@ -2248,7 +2248,7 @@ static int lock_packed_refs(int flags)
 static int commit_packed_refs(void)
 {
 	struct packed_ref_cache *packed_ref_cache =
-		get_packed_ref_cache(&ref_cache);
+		get_packed_ref_cache(&ref_store);
 	int error = 0;
 	int save_errno = 0;
 	FILE *out;
@@ -2282,14 +2282,14 @@ static int commit_packed_refs(void)
 static void rollback_packed_refs(void)
 {
 	struct packed_ref_cache *packed_ref_cache =
-		get_packed_ref_cache(&ref_cache);
+		get_packed_ref_cache(&ref_store);
 
 	if (!packed_ref_cache->lock)
 		die("internal error: packed-refs not locked");
 	rollback_lock_file(packed_ref_cache->lock);
 	packed_ref_cache->lock = NULL;
 	release_packed_ref_cache(packed_ref_cache);
-	clear_packed_ref_cache(&ref_cache);
+	clear_packed_ref_cache(&ref_store);
 }
 
 struct ref_to_prune {
@@ -2428,9 +2428,9 @@ int pack_refs(unsigned int flags)
 	cbdata.flags = flags;
 
 	lock_packed_refs(LOCK_DIE_ON_ERROR);
-	cbdata.packed_refs = get_packed_refs(&ref_cache);
+	cbdata.packed_refs = get_packed_refs(&ref_store);
 
-	do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
+	do_for_each_entry_in_dir(get_loose_refs(&ref_store), 0,
 				 pack_if_possible_fn, &cbdata);
 
 	if (commit_packed_refs())
@@ -2471,7 +2471,7 @@ static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 		unable_to_lock_message(git_path("packed-refs"), errno, err);
 		return -1;
 	}
-	packed = get_packed_refs(&ref_cache);
+	packed = get_packed_refs(&ref_store);
 
 	/* Remove refnames from the cache */
 	for_each_string_list_item(refname, refnames)
@@ -2616,8 +2616,8 @@ int verify_refname_available(const char *newname,
 			     const struct string_list *skip,
 			     struct strbuf *err)
 {
-	struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
-	struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
+	struct ref_dir *packed_refs = get_packed_refs(&ref_store);
+	struct ref_dir *loose_refs = get_loose_refs(&ref_store);
 
 	if (verify_refname_available_dir(newname, extras, skip,
 					 packed_refs, err) ||
@@ -2968,7 +2968,7 @@ static int commit_ref_update(struct ref_lock *lock,
 			     const unsigned char *sha1, const char *logmsg,
 			     struct strbuf *err)
 {
-	clear_loose_ref_cache(&ref_cache);
+	clear_loose_ref_cache(&ref_store);
 	if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
 		char *old_msg = strbuf_detach(err, NULL);
 		strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -3790,7 +3790,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 			}
 		}
 		if (update->flags & REF_NEEDS_COMMIT) {
-			clear_loose_ref_cache(&ref_cache);
+			clear_loose_ref_cache(&ref_store);
 			if (commit_ref(lock)) {
 				strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
 				unlock_ref(lock);
@@ -3823,7 +3823,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 	}
 	for_each_string_list_item(ref_to_delete, &refs_to_delete)
 		unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
-	clear_loose_ref_cache(&ref_cache);
+	clear_loose_ref_cache(&ref_store);
 
 cleanup:
 	transaction->state = REF_TRANSACTION_CLOSED;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 04/38] refs: add a backend method structure

From: Ronnie Sahlberg <sahlberg@google.com>

Add a `struct ref_storage_be` to represent types of reference stores. In OO notation, this is the class, and will soon hold some class methods (e.g., a factory to create new ref_store instances) and will also serve as the vtable for ref_store instances of that type.

As yet, the backends cannot do anything.
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 19 +++++++++++++++++++
 refs.h               |  2 ++
 refs/files-backend.c |  5 +++++
 refs/refs-internal.h |  8 ++++++++
 4 files changed, 34 insertions(+)
Show changes to 4 files +34 −0

refs.c, refs.h, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 814cad3..f57a93e 100644
--- a/refs.c
+++ b/refs.c
@@ -10,6 +10,25 @@
 #include "tag.h"
 
 /*
+ * List of all available backends
+ */
+static struct ref_storage_be *refs_backends = &refs_be_files;
+
+static struct ref_storage_be *find_ref_storage_backend(const char *name)
+{
+	struct ref_storage_be *be;
+	for (be = refs_backends; be; be = be->next)
+		if (!strcmp(be->name, name))
+			return be;
+	return NULL;
+}
+
+int ref_storage_backend_exists(const char *name)
+{
+	return find_ref_storage_backend(name) != NULL;
+}
+
+/*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
  * 1: End-of-component
diff --git a/refs.h b/refs.h
index 442c1a5..52ea93b 100644
--- a/refs.h
+++ b/refs.h
@@ -544,4 +544,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 		  reflog_expiry_cleanup_fn cleanup_fn,
 		  void *policy_cb_data);
 
+int ref_storage_backend_exists(const char *name);
+
 #endif /* REFS_H */
diff --git a/refs/files-backend.c b/refs/files-backend.c
index b94aad2..bde6f0e 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -4066,3 +4066,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 	unlock_ref(lock);
 	return -1;
 }
+
+struct ref_storage_be refs_be_files = {
+	NULL,
+	"files"
+};
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index d8a2606..4d88849 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -520,4 +520,12 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
 int read_raw_ref(const char *refname, unsigned char *sha1,
 		 struct strbuf *referent, unsigned int *type);
 
+/* refs backends */
+struct ref_storage_be {
+	struct ref_storage_be *next;
+	const char *name;
+};
+
+extern struct ref_storage_be refs_be_files;
+
 #endif /* REFS_REFS_INTERNAL_H */
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

We want ref_stores to be polymorphic, so invent a base class of which files_ref_store is a derived class. For now there is a one-to-one relationship between ref_stores and submodules.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               |  91 ++++++++++++++++++++++++++
 refs/files-backend.c | 179 +++++++++++++++++++++++++++++----------------------
 refs/refs-internal.h |  78 ++++++++++++++++++++++
 3 files changed, 271 insertions(+), 77 deletions(-)
Show changes to 3 files +271 −77

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index f57a93e..5e20ae0 100644
--- a/refs.c
+++ b/refs.c
@@ -1151,8 +1151,12 @@ int head_ref(each_ref_fn fn, void *cb_data)
 static int do_for_each_ref(const char *submodule, const char *prefix,
 			   each_ref_fn fn, int trim, int flags, void *cb_data)
 {
+	struct ref_store *refs = get_ref_store(submodule);
 	struct ref_iterator *iter;
 
+	if (!refs)
+		return 0;
+
 	iter = files_ref_iterator_begin(submodule, prefix, flags);
 	iter = prefix_ref_iterator_begin(iter, prefix, trim);
 
@@ -1284,3 +1288,90 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 	errno = ELOOP;
 	return NULL;
 }
+
+static struct ref_store *main_ref_store = NULL;
+
+static struct ref_store *submodule_ref_stores = NULL;
+
+void base_ref_store_init(struct ref_store *refs,
+			 const struct ref_storage_be *be,
+			 const char *submodule)
+{
+	refs->be = be;
+	if (!submodule) {
+		if (main_ref_store)
+			die("BUG: main_ref_store initialized twice");
+
+		refs->submodule = "";
+		refs->next = NULL;
+		main_ref_store = refs;
+	} else {
+		if (lookup_ref_store(submodule))
+			die("BUG: ref_store for submodule '%s' initialized twice",
+			    submodule);
+
+		refs->submodule = xstrdup(submodule);
+		refs->next = submodule_ref_stores;
+		submodule_ref_stores = refs;
+	}
+}
+
+struct ref_store *ref_store_init(const char *submodule)
+{
+	const char *be_name = "files";
+	struct ref_storage_be *be = find_ref_storage_backend(be_name);
+
+	if (!be)
+		die("BUG: reference backend %s is unknown", be_name);
+
+	if (!submodule || !*submodule)
+		return be->init(NULL);
+	else
+		return be->init(submodule);
+}
+
+struct ref_store *lookup_ref_store(const char *submodule)
+{
+	struct ref_store *refs;
+
+	if (!submodule || !*submodule)
+		return main_ref_store;
+
+	for (refs = submodule_ref_stores; refs; refs = refs->next) {
+		if (!strcmp(submodule, refs->submodule))
+			return refs;
+	}
+
+	return NULL;
+}
+
+struct ref_store *get_ref_store(const char *submodule)
+{
+	struct ref_store *refs;
+
+	if (!submodule || !*submodule) {
+		refs = lookup_ref_store(NULL);
+
+		if (!refs)
+			refs = ref_store_init(NULL);
+	} else {
+		refs = lookup_ref_store(submodule);
+
+		if (!refs) {
+			struct strbuf submodule_sb = STRBUF_INIT;
+
+			strbuf_addstr(&submodule_sb, submodule);
+			if (is_nonbare_repository_dir(&submodule_sb))
+				refs = ref_store_init(submodule);
+			strbuf_release(&submodule_sb);
+		}
+	}
+
+	return refs;
+}
+
+void assert_main_repository(struct ref_store *refs, const char *caller)
+{
+	if (*refs->submodule)
+		die("BUG: %s called for a submodule", caller);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index bde6f0e..8ef79c2 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -910,17 +910,11 @@ struct packed_ref_cache {
  * Future: need to be in "struct repository"
  * when doing a full libification.
  */
-static struct files_ref_store {
-	struct files_ref_store *next;
+struct files_ref_store {
+	struct ref_store base;
 	struct ref_entry *loose;
 	struct packed_ref_cache *packed;
-	/*
-	 * The submodule name, or "" for the main repo. We allocate
-	 * length 1 rather than FLEX_ARRAY so that the main
-	 * files_ref_store is initialized correctly.
-	 */
-	char name[1];
-} ref_store, *submodule_ref_stores;
+};
 
 /* Lock used for the main packed-refs file: */
 static struct lock_file packlock;
@@ -973,53 +967,54 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
  * Create a new submodule ref cache and add it to the internal
  * set of caches.
  */
-static struct files_ref_store *create_ref_store(const char *submodule)
+static struct ref_store *files_ref_store_create(const char *submodule)
 {
-	struct files_ref_store *refs;
-	if (!submodule)
-		submodule = "";
-	FLEX_ALLOC_STR(refs, name, submodule);
-	refs->next = submodule_ref_stores;
-	submodule_ref_stores = refs;
-	return refs;
+	struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
+	struct ref_store *ref_store = (struct ref_store *)refs;
+
+	base_ref_store_init(ref_store, &refs_be_files, submodule);
+
+	return ref_store;
 }
 
-static struct files_ref_store *lookup_ref_store(const char *submodule)
+/*
+ * Downcast ref_store to files_ref_store. Die if ref_store is not a
+ * files_ref_store. If submodule_allowed is not true, then also die if
+ * files_ref_store is for a submodule (i.e., not for the main
+ * repository). caller is used in any necessary error messages.
+ */
+static struct files_ref_store *files_downcast(
+		struct ref_store *ref_store, int submodule_allowed,
+		const char *caller)
 {
 	struct files_ref_store *refs;
 
-	if (!submodule || !*submodule)
-		return &ref_store;
+	if (ref_store->be != &refs_be_files)
+		die("BUG: ref_store is type \"%s\" not \"files\" in %s",
+		    ref_store->be->name, caller);
 
-	for (refs = submodule_ref_stores; refs; refs = refs->next)
-		if (!strcmp(submodule, refs->name))
-			return refs;
-	return NULL;
+	refs = (struct files_ref_store *)ref_store;
+
+	if (!submodule_allowed)
+		assert_main_repository(ref_store, caller);
+
+	return refs;
 }
 
 /*
- * Return a pointer to a files_ref_store for the specified submodule. For
- * the main repository, use submodule==NULL; such a call cannot fail.
- * For a submodule, the submodule must exist and be a nonbare
- * repository, otherwise return NULL.
- *
- * The returned structure will be allocated and initialized but not
- * necessarily populated; it should not be freed.
+ * Return a pointer to the reference store for the specified
+ * submodule. For the main repository, use submodule==NULL; such a
+ * call cannot fail. For a submodule, the submodule must exist and be
+ * a nonbare repository, otherwise return NULL. Verify that the
+ * reference store is a files_ref_store, and cast it to that type
+ * before returning it.
  */
-static struct files_ref_store *get_ref_store(const char *submodule)
+static struct files_ref_store *get_files_ref_store(const char *submodule,
+						   const char *caller)
 {
-	struct files_ref_store *refs = lookup_ref_store(submodule);
+	struct ref_store *refs = get_ref_store(submodule);
 
-	if (!refs) {
-		struct strbuf submodule_sb = STRBUF_INIT;
-
-		strbuf_addstr(&submodule_sb, submodule);
-		if (is_nonbare_repository_dir(&submodule_sb))
-			refs = create_ref_store(submodule);
-		strbuf_release(&submodule_sb);
-	}
-
-	return refs;
+	return refs ? files_downcast(refs, 1, caller) : NULL;
 }
 
 /* The length of a peeled reference line in packed-refs, including EOL: */
@@ -1158,8 +1153,9 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
 {
 	char *packed_refs_file;
 
-	if (*refs->name)
-		packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
+	if (*refs->base.submodule)
+		packed_refs_file = git_pathdup_submodule(refs->base.submodule,
+							 "packed-refs");
 	else
 		packed_refs_file = git_pathdup("packed-refs");
 
@@ -1202,8 +1198,9 @@ static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
  */
 static void add_packed_ref(const char *refname, const unsigned char *sha1)
 {
-	struct packed_ref_cache *packed_ref_cache =
-		get_packed_ref_cache(&ref_store);
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "add_packed_ref");
+	struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
 
 	if (!packed_ref_cache->lock)
 		die("internal error: packed refs not locked");
@@ -1226,8 +1223,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 	struct strbuf path = STRBUF_INIT;
 	size_t path_baselen;
 
-	if (*refs->name)
-		strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
+	if (*refs->base.submodule)
+		strbuf_git_path_submodule(&path, refs->base.submodule, "%s", dirname);
 	else
 		strbuf_git_path(&path, "%s", dirname);
 	path_baselen = path.len;
@@ -1262,10 +1259,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 		} else {
 			int read_ok;
 
-			if (*refs->name) {
+			if (*refs->base.submodule) {
 				hashclr(sha1);
 				flag = 0;
-				read_ok = !resolve_gitlink_ref(refs->name,
+				read_ok = !resolve_gitlink_ref(refs->base.submodule,
 							       refname.buf, sha1);
 			} else {
 				read_ok = !read_ref_full(refname.buf,
@@ -1355,8 +1352,8 @@ static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
 
 	if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
 		return -1;
-	path = *refs->name
-		? git_pathdup_submodule(refs->name, "%s", refname)
+	path = *refs->base.submodule
+		? git_pathdup_submodule(refs->base.submodule, "%s", refname)
 		: git_pathdup("%s", refname);
 	fd = open(path, O_RDONLY);
 	free(path);
@@ -1397,7 +1394,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
 		return -1;
 
 	strbuf_add(&submodule, path, len);
-	refs = get_ref_store(submodule.buf);
+	refs = get_files_ref_store(submodule.buf, "resolve_gitlink_ref");
 	if (!refs) {
 		strbuf_release(&submodule);
 		return -1;
@@ -1413,7 +1410,10 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
  */
 static struct ref_entry *get_packed_ref(const char *refname)
 {
-	return find_ref(get_packed_refs(&ref_store), refname);
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "get_packed_ref");
+
+	return find_ref(get_packed_refs(refs), refname);
 }
 
 /*
@@ -1613,6 +1613,8 @@ static int lock_raw_ref(const char *refname, int mustexist,
 			unsigned int *type,
 			struct strbuf *err)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "lock_raw_ref");
 	struct ref_lock *lock;
 	struct strbuf ref_file = STRBUF_INIT;
 	int attempts_remaining = 3;
@@ -1745,7 +1747,7 @@ retry:
 							  REMOVE_DIR_EMPTY_ONLY)) {
 				if (verify_refname_available_dir(
 						    refname, extras, skip,
-						    get_loose_refs(&ref_store),
+						    get_loose_refs(refs),
 						    err)) {
 					/*
 					 * The error message set by
@@ -1784,7 +1786,7 @@ retry:
 		 */
 		if (verify_refname_available_dir(
 				    refname, extras, skip,
-				    get_packed_refs(&ref_store),
+				    get_packed_refs(refs),
 				    err)) {
 			goto error_return;
 		}
@@ -1942,7 +1944,8 @@ struct ref_iterator *files_ref_iterator_begin(
 		const char *submodule,
 		const char *prefix, unsigned int flags)
 {
-	struct files_ref_store *refs = get_ref_store(submodule);
+	struct files_ref_store *refs =
+		get_files_ref_store(submodule, "ref_iterator_begin");
 	struct ref_dir *loose_dir, *packed_dir;
 	struct ref_iterator *loose_iter, *packed_iter;
 	struct files_ref_iterator *iter;
@@ -2065,6 +2068,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 					    unsigned int flags, int *type,
 					    struct strbuf *err)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "lock_ref_sha1_basic");
 	struct strbuf ref_file = STRBUF_INIT;
 	struct ref_lock *lock;
 	int last_errno = 0;
@@ -2095,8 +2100,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 		 */
 		if (remove_empty_directories(&ref_file)) {
 			last_errno = errno;
-			if (!verify_refname_available_dir(refname, extras, skip,
-							  get_loose_refs(&ref_store), err))
+			if (!verify_refname_available_dir(
+					    refname, extras, skip,
+					    get_loose_refs(refs), err))
 				strbuf_addf(err, "there are still refs under '%s'",
 					    refname);
 			goto error_return;
@@ -2107,8 +2113,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 	if (!resolved) {
 		last_errno = errno;
 		if (last_errno != ENOTDIR ||
-		    !verify_refname_available_dir(refname, extras, skip,
-						  get_loose_refs(&ref_store), err))
+		    !verify_refname_available_dir(
+				    refname, extras, skip,
+				    get_loose_refs(refs), err))
 			strbuf_addf(err, "unable to resolve reference '%s': %s",
 				    refname, strerror(last_errno));
 
@@ -2123,7 +2130,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 	 */
 	if (is_null_oid(&lock->old_oid) &&
 	    verify_refname_available_dir(refname, extras, skip,
-					 get_packed_refs(&ref_store), err)) {
+					 get_packed_refs(refs),
+					 err)) {
 		last_errno = ENOTDIR;
 		goto error_return;
 	}
@@ -2212,9 +2220,10 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
  */
 static int lock_packed_refs(int flags)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "lock_packed_refs");
 	static int timeout_configured = 0;
 	static int timeout_value = 1000;
-
 	struct packed_ref_cache *packed_ref_cache;
 
 	if (!timeout_configured) {
@@ -2232,7 +2241,7 @@ static int lock_packed_refs(int flags)
 	 * this will automatically invalidate the cache and re-read
 	 * the packed-refs file.
 	 */
-	packed_ref_cache = get_packed_ref_cache(&ref_store);
+	packed_ref_cache = get_packed_ref_cache(refs);
 	packed_ref_cache->lock = &packlock;
 	/* Increment the reference count to prevent it from being freed: */
 	acquire_packed_ref_cache(packed_ref_cache);
@@ -2247,8 +2256,10 @@ static int lock_packed_refs(int flags)
  */
 static int commit_packed_refs(void)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "commit_packed_refs");
 	struct packed_ref_cache *packed_ref_cache =
-		get_packed_ref_cache(&ref_store);
+		get_packed_ref_cache(refs);
 	int error = 0;
 	int save_errno = 0;
 	FILE *out;
@@ -2281,15 +2292,17 @@ static int commit_packed_refs(void)
  */
 static void rollback_packed_refs(void)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "rollback_packed_refs");
 	struct packed_ref_cache *packed_ref_cache =
-		get_packed_ref_cache(&ref_store);
+		get_packed_ref_cache(refs);
 
 	if (!packed_ref_cache->lock)
 		die("internal error: packed-refs not locked");
 	rollback_lock_file(packed_ref_cache->lock);
 	packed_ref_cache->lock = NULL;
 	release_packed_ref_cache(packed_ref_cache);
-	clear_packed_ref_cache(&ref_store);
+	clear_packed_ref_cache(refs);
 }
 
 struct ref_to_prune {
@@ -2422,15 +2435,17 @@ static void prune_refs(struct ref_to_prune *r)
 
 int pack_refs(unsigned int flags)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "pack_refs");
 	struct pack_refs_cb_data cbdata;
 
 	memset(&cbdata, 0, sizeof(cbdata));
 	cbdata.flags = flags;
 
 	lock_packed_refs(LOCK_DIE_ON_ERROR);
-	cbdata.packed_refs = get_packed_refs(&ref_store);
+	cbdata.packed_refs = get_packed_refs(refs);
 
-	do_for_each_entry_in_dir(get_loose_refs(&ref_store), 0,
+	do_for_each_entry_in_dir(get_loose_refs(refs), 0,
 				 pack_if_possible_fn, &cbdata);
 
 	if (commit_packed_refs())
@@ -2449,6 +2464,8 @@ int pack_refs(unsigned int flags)
  */
 static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "repack_without_refs");
 	struct ref_dir *packed;
 	struct string_list_item *refname;
 	int ret, needs_repacking = 0, removed = 0;
@@ -2471,7 +2488,7 @@ static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 		unable_to_lock_message(git_path("packed-refs"), errno, err);
 		return -1;
 	}
-	packed = get_packed_refs(&ref_store);
+	packed = get_packed_refs(refs);
 
 	/* Remove refnames from the cache */
 	for_each_string_list_item(refname, refnames)
@@ -2616,8 +2633,10 @@ int verify_refname_available(const char *newname,
 			     const struct string_list *skip,
 			     struct strbuf *err)
 {
-	struct ref_dir *packed_refs = get_packed_refs(&ref_store);
-	struct ref_dir *loose_refs = get_loose_refs(&ref_store);
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "verify_refname_available");
+	struct ref_dir *packed_refs = get_packed_refs(refs);
+	struct ref_dir *loose_refs = get_loose_refs(refs);
 
 	if (verify_refname_available_dir(newname, extras, skip,
 					 packed_refs, err) ||
@@ -2968,7 +2987,10 @@ static int commit_ref_update(struct ref_lock *lock,
 			     const unsigned char *sha1, const char *logmsg,
 			     struct strbuf *err)
 {
-	clear_loose_ref_cache(&ref_store);
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "commit_ref_update");
+
+	clear_loose_ref_cache(refs);
 	if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
 		char *old_msg = strbuf_detach(err, NULL);
 		strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -3683,6 +3705,8 @@ static int lock_ref_for_update(struct ref_update *update,
 int ref_transaction_commit(struct ref_transaction *transaction,
 			   struct strbuf *err)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "ref_transaction_commit");
 	int ret = 0, i;
 	struct ref_update **updates = transaction->updates;
 	struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
@@ -3790,7 +3814,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 			}
 		}
 		if (update->flags & REF_NEEDS_COMMIT) {
-			clear_loose_ref_cache(&ref_store);
+			clear_loose_ref_cache(refs);
 			if (commit_ref(lock)) {
 				strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
 				unlock_ref(lock);
@@ -3823,7 +3847,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 	}
 	for_each_string_list_item(ref_to_delete, &refs_to_delete)
 		unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
-	clear_loose_ref_cache(&ref_store);
+	clear_loose_ref_cache(refs);
 
 cleanup:
 	transaction->state = REF_TRANSACTION_CLOSED;
@@ -4069,5 +4093,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 
 struct ref_storage_be refs_be_files = {
 	NULL,
-	"files"
+	"files",
+	files_ref_store_create
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4d88849..d6c74cf 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -521,11 +521,89 @@ int read_raw_ref(const char *refname, unsigned char *sha1,
 		 struct strbuf *referent, unsigned int *type);
 
 /* refs backends */
+
+/*
+ * Initialize the ref_store for the specified submodule, or for the
+ * main repository if submodule == NULL. These functions should call
+ * base_ref_store_init() to initialize the shared part of the
+ * ref_store and to record the ref_store for later lookup.
+ */
+typedef struct ref_store *ref_store_init_fn(const char *submodule);
+
 struct ref_storage_be {
 	struct ref_storage_be *next;
 	const char *name;
+	ref_store_init_fn *init;
 };
 
 extern struct ref_storage_be refs_be_files;
 
+/*
+ * A representation of the reference store for the main repository or
+ * a submodule. The ref_store instances for submodules are kept in a
+ * linked list.
+ */
+struct ref_store {
+	/* The backend describing this ref_store's storage scheme: */
+	const struct ref_storage_be *be;
+
+	/*
+	 * The name of the submodule represented by this object, or
+	 * the empty string if it represents the main repository's
+	 * reference store:
+	 */
+	const char *submodule;
+
+	/*
+	 * Submodule reference store instances are stored in a linked
+	 * list using this pointer.
+	 */
+	struct ref_store *next;
+};
+
+/*
+ * Fill in the generic part of refs for the specified submodule and
+ * add it to our collection of reference stores.
+ */
+void base_ref_store_init(struct ref_store *refs,
+			 const struct ref_storage_be *be,
+			 const char *submodule);
+
+/*
+ * Create, record, and return a ref_store instance for the specified
+ * submodule (or the main repository if submodule is NULL).
+ *
+ * For backwards compatibility, submodule=="" is treated the same as
+ * submodule==NULL.
+ */
+struct ref_store *ref_store_init(const char *submodule);
+
+/*
+ * Return the ref_store instance for the specified submodule (or the
+ * main repository if submodule is NULL). If that ref_store hasn't
+ * been initialized yet, return NULL.
+ *
+ * For backwards compatibility, submodule=="" is treated the same as
+ * submodule==NULL.
+ */
+struct ref_store *lookup_ref_store(const char *submodule);
+
+/*
+ * Return the ref_store instance for the specified submodule. For the
+ * main repository, use submodule==NULL; such a call cannot fail. For
+ * a submodule, the submodule must exist and be a nonbare repository,
+ * otherwise return NULL. If the requested reference store has not yet
+ * been initialized, initialize it first.
+ *
+ * For backwards compatibility, submodule=="" is treated the same as
+ * submodule==NULL.
+ */
+struct ref_store *get_ref_store(const char *submodule);
+
+/*
+ * Die if refs is for a submodule (i.e., not for the main repository).
+ * caller is used in any necessary error messages.
+ */
+void assert_main_repository(struct ref_store *refs, const char *caller);
+
 #endif /* REFS_REFS_INTERNAL_H */
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 16:31 UTC · re: Michael Haggerty · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

This is a tangent, but your series that ends at 4aa2c475 (grep: -W: don't extend context to trailing empty lines, 2016-05-28) does not seem to have much effect when viewing the change to refs.c this patch makes (it modifies a function in an early part, and then adds bunch of new functions at the end) with "git show -W".

Thanks.
Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 638 quoted lines
> We want ref_stores to be polymorphic, so invent a base class of which
> files_ref_store is a derived class. For now there is a one-to-one
> relationship between ref_stores and submodules.
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  refs.c               |  91 ++++++++++++++++++++++++++
>  refs/files-backend.c | 179 +++++++++++++++++++++++++++++----------------------
>  refs/refs-internal.h |  78 ++++++++++++++++++++++
>  3 files changed, 271 insertions(+), 77 deletions(-)
>
> diff --git a/refs.c b/refs.c
> index f57a93e..5e20ae0 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1151,8 +1151,12 @@ int head_ref(each_ref_fn fn, void *cb_data)
>  static int do_for_each_ref(const char *submodule, const char *prefix,
>  			   each_ref_fn fn, int trim, int flags, void *cb_data)
>  {
> +	struct ref_store *refs = get_ref_store(submodule);
>  	struct ref_iterator *iter;
>  
> +	if (!refs)
> +		return 0;
> +
>  	iter = files_ref_iterator_begin(submodule, prefix, flags);
>  	iter = prefix_ref_iterator_begin(iter, prefix, trim);
>  
> @@ -1284,3 +1288,90 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
>  	errno = ELOOP;
>  	return NULL;
>  }
> +
> +static struct ref_store *main_ref_store = NULL;
> +
> +static struct ref_store *submodule_ref_stores = NULL;
> +
> +void base_ref_store_init(struct ref_store *refs,
> +			 const struct ref_storage_be *be,
> +			 const char *submodule)
> +{
> +	refs->be = be;
> +	if (!submodule) {
> +		if (main_ref_store)
> +			die("BUG: main_ref_store initialized twice");
> +
> +		refs->submodule = "";
> +		refs->next = NULL;
> +		main_ref_store = refs;
> +	} else {
> +		if (lookup_ref_store(submodule))
> +			die("BUG: ref_store for submodule '%s' initialized twice",
> +			    submodule);
> +
> +		refs->submodule = xstrdup(submodule);
> +		refs->next = submodule_ref_stores;
> +		submodule_ref_stores = refs;
> +	}
> +}
> +
> +struct ref_store *ref_store_init(const char *submodule)
> +{
> +	const char *be_name = "files";
> +	struct ref_storage_be *be = find_ref_storage_backend(be_name);
> +
> +	if (!be)
> +		die("BUG: reference backend %s is unknown", be_name);
> +
> +	if (!submodule || !*submodule)
> +		return be->init(NULL);
> +	else
> +		return be->init(submodule);
> +}
> +
> +struct ref_store *lookup_ref_store(const char *submodule)
> +{
> +	struct ref_store *refs;
> +
> +	if (!submodule || !*submodule)
> +		return main_ref_store;
> +
> +	for (refs = submodule_ref_stores; refs; refs = refs->next) {
> +		if (!strcmp(submodule, refs->submodule))
> +			return refs;
> +	}
> +
> +	return NULL;
> +}
> +
> +struct ref_store *get_ref_store(const char *submodule)
> +{
> +	struct ref_store *refs;
> +
> +	if (!submodule || !*submodule) {
> +		refs = lookup_ref_store(NULL);
> +
> +		if (!refs)
> +			refs = ref_store_init(NULL);
> +	} else {
> +		refs = lookup_ref_store(submodule);
> +
> +		if (!refs) {
> +			struct strbuf submodule_sb = STRBUF_INIT;
> +
> +			strbuf_addstr(&submodule_sb, submodule);
> +			if (is_nonbare_repository_dir(&submodule_sb))
> +				refs = ref_store_init(submodule);
> +			strbuf_release(&submodule_sb);
> +		}
> +	}
> +
> +	return refs;
> +}
> +
> +void assert_main_repository(struct ref_store *refs, const char *caller)
> +{
> +	if (*refs->submodule)
> +		die("BUG: %s called for a submodule", caller);
> +}
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index bde6f0e..8ef79c2 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -910,17 +910,11 @@ struct packed_ref_cache {
>   * Future: need to be in "struct repository"
>   * when doing a full libification.
>   */
> -static struct files_ref_store {
> -	struct files_ref_store *next;
> +struct files_ref_store {
> +	struct ref_store base;
>  	struct ref_entry *loose;
>  	struct packed_ref_cache *packed;
> -	/*
> -	 * The submodule name, or "" for the main repo. We allocate
> -	 * length 1 rather than FLEX_ARRAY so that the main
> -	 * files_ref_store is initialized correctly.
> -	 */
> -	char name[1];
> -} ref_store, *submodule_ref_stores;
> +};
>  
>  /* Lock used for the main packed-refs file: */
>  static struct lock_file packlock;
> @@ -973,53 +967,54 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
>   * Create a new submodule ref cache and add it to the internal
>   * set of caches.
>   */
> -static struct files_ref_store *create_ref_store(const char *submodule)
> +static struct ref_store *files_ref_store_create(const char *submodule)
>  {
> -	struct files_ref_store *refs;
> -	if (!submodule)
> -		submodule = "";
> -	FLEX_ALLOC_STR(refs, name, submodule);
> -	refs->next = submodule_ref_stores;
> -	submodule_ref_stores = refs;
> -	return refs;
> +	struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
> +	struct ref_store *ref_store = (struct ref_store *)refs;
> +
> +	base_ref_store_init(ref_store, &refs_be_files, submodule);
> +
> +	return ref_store;
>  }
>  
> -static struct files_ref_store *lookup_ref_store(const char *submodule)
> +/*
> + * Downcast ref_store to files_ref_store. Die if ref_store is not a
> + * files_ref_store. If submodule_allowed is not true, then also die if
> + * files_ref_store is for a submodule (i.e., not for the main
> + * repository). caller is used in any necessary error messages.
> + */
> +static struct files_ref_store *files_downcast(
> +		struct ref_store *ref_store, int submodule_allowed,
> +		const char *caller)
>  {
>  	struct files_ref_store *refs;
>  
> -	if (!submodule || !*submodule)
> -		return &ref_store;
> +	if (ref_store->be != &refs_be_files)
> +		die("BUG: ref_store is type \"%s\" not \"files\" in %s",
> +		    ref_store->be->name, caller);
>  
> -	for (refs = submodule_ref_stores; refs; refs = refs->next)
> -		if (!strcmp(submodule, refs->name))
> -			return refs;
> -	return NULL;
> +	refs = (struct files_ref_store *)ref_store;
> +
> +	if (!submodule_allowed)
> +		assert_main_repository(ref_store, caller);
> +
> +	return refs;
>  }
>  
>  /*
> - * Return a pointer to a files_ref_store for the specified submodule. For
> - * the main repository, use submodule==NULL; such a call cannot fail.
> - * For a submodule, the submodule must exist and be a nonbare
> - * repository, otherwise return NULL.
> - *
> - * The returned structure will be allocated and initialized but not
> - * necessarily populated; it should not be freed.
> + * Return a pointer to the reference store for the specified
> + * submodule. For the main repository, use submodule==NULL; such a
> + * call cannot fail. For a submodule, the submodule must exist and be
> + * a nonbare repository, otherwise return NULL. Verify that the
> + * reference store is a files_ref_store, and cast it to that type
> + * before returning it.
>   */
> -static struct files_ref_store *get_ref_store(const char *submodule)
> +static struct files_ref_store *get_files_ref_store(const char *submodule,
> +						   const char *caller)
>  {
> -	struct files_ref_store *refs = lookup_ref_store(submodule);
> +	struct ref_store *refs = get_ref_store(submodule);
>  
> -	if (!refs) {
> -		struct strbuf submodule_sb = STRBUF_INIT;
> -
> -		strbuf_addstr(&submodule_sb, submodule);
> -		if (is_nonbare_repository_dir(&submodule_sb))
> -			refs = create_ref_store(submodule);
> -		strbuf_release(&submodule_sb);
> -	}
> -
> -	return refs;
> +	return refs ? files_downcast(refs, 1, caller) : NULL;
>  }
>  
>  /* The length of a peeled reference line in packed-refs, including EOL: */
> @@ -1158,8 +1153,9 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
>  {
>  	char *packed_refs_file;
>  
> -	if (*refs->name)
> -		packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
> +	if (*refs->base.submodule)
> +		packed_refs_file = git_pathdup_submodule(refs->base.submodule,
> +							 "packed-refs");
>  	else
>  		packed_refs_file = git_pathdup("packed-refs");
>  
> @@ -1202,8 +1198,9 @@ static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
>   */
>  static void add_packed_ref(const char *refname, const unsigned char *sha1)
>  {
> -	struct packed_ref_cache *packed_ref_cache =
> -		get_packed_ref_cache(&ref_store);
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "add_packed_ref");
> +	struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
>  
>  	if (!packed_ref_cache->lock)
>  		die("internal error: packed refs not locked");
> @@ -1226,8 +1223,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
>  	struct strbuf path = STRBUF_INIT;
>  	size_t path_baselen;
>  
> -	if (*refs->name)
> -		strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
> +	if (*refs->base.submodule)
> +		strbuf_git_path_submodule(&path, refs->base.submodule, "%s", dirname);
>  	else
>  		strbuf_git_path(&path, "%s", dirname);
>  	path_baselen = path.len;
> @@ -1262,10 +1259,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
>  		} else {
>  			int read_ok;
>  
> -			if (*refs->name) {
> +			if (*refs->base.submodule) {
>  				hashclr(sha1);
>  				flag = 0;
> -				read_ok = !resolve_gitlink_ref(refs->name,
> +				read_ok = !resolve_gitlink_ref(refs->base.submodule,
>  							       refname.buf, sha1);
>  			} else {
>  				read_ok = !read_ref_full(refname.buf,
> @@ -1355,8 +1352,8 @@ static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
>  
>  	if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
>  		return -1;
> -	path = *refs->name
> -		? git_pathdup_submodule(refs->name, "%s", refname)
> +	path = *refs->base.submodule
> +		? git_pathdup_submodule(refs->base.submodule, "%s", refname)
>  		: git_pathdup("%s", refname);
>  	fd = open(path, O_RDONLY);
>  	free(path);
> @@ -1397,7 +1394,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
>  		return -1;
>  
>  	strbuf_add(&submodule, path, len);
> -	refs = get_ref_store(submodule.buf);
> +	refs = get_files_ref_store(submodule.buf, "resolve_gitlink_ref");
>  	if (!refs) {
>  		strbuf_release(&submodule);
>  		return -1;
> @@ -1413,7 +1410,10 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
>   */
>  static struct ref_entry *get_packed_ref(const char *refname)
>  {
> -	return find_ref(get_packed_refs(&ref_store), refname);
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "get_packed_ref");
> +
> +	return find_ref(get_packed_refs(refs), refname);
>  }
>  
>  /*
> @@ -1613,6 +1613,8 @@ static int lock_raw_ref(const char *refname, int mustexist,
>  			unsigned int *type,
>  			struct strbuf *err)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "lock_raw_ref");
>  	struct ref_lock *lock;
>  	struct strbuf ref_file = STRBUF_INIT;
>  	int attempts_remaining = 3;
> @@ -1745,7 +1747,7 @@ retry:
>  							  REMOVE_DIR_EMPTY_ONLY)) {
>  				if (verify_refname_available_dir(
>  						    refname, extras, skip,
> -						    get_loose_refs(&ref_store),
> +						    get_loose_refs(refs),
>  						    err)) {
>  					/*
>  					 * The error message set by
> @@ -1784,7 +1786,7 @@ retry:
>  		 */
>  		if (verify_refname_available_dir(
>  				    refname, extras, skip,
> -				    get_packed_refs(&ref_store),
> +				    get_packed_refs(refs),
>  				    err)) {
>  			goto error_return;
>  		}
> @@ -1942,7 +1944,8 @@ struct ref_iterator *files_ref_iterator_begin(
>  		const char *submodule,
>  		const char *prefix, unsigned int flags)
>  {
> -	struct files_ref_store *refs = get_ref_store(submodule);
> +	struct files_ref_store *refs =
> +		get_files_ref_store(submodule, "ref_iterator_begin");
>  	struct ref_dir *loose_dir, *packed_dir;
>  	struct ref_iterator *loose_iter, *packed_iter;
>  	struct files_ref_iterator *iter;
> @@ -2065,6 +2068,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
>  					    unsigned int flags, int *type,
>  					    struct strbuf *err)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "lock_ref_sha1_basic");
>  	struct strbuf ref_file = STRBUF_INIT;
>  	struct ref_lock *lock;
>  	int last_errno = 0;
> @@ -2095,8 +2100,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
>  		 */
>  		if (remove_empty_directories(&ref_file)) {
>  			last_errno = errno;
> -			if (!verify_refname_available_dir(refname, extras, skip,
> -							  get_loose_refs(&ref_store), err))
> +			if (!verify_refname_available_dir(
> +					    refname, extras, skip,
> +					    get_loose_refs(refs), err))
>  				strbuf_addf(err, "there are still refs under '%s'",
>  					    refname);
>  			goto error_return;
> @@ -2107,8 +2113,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
>  	if (!resolved) {
>  		last_errno = errno;
>  		if (last_errno != ENOTDIR ||
> -		    !verify_refname_available_dir(refname, extras, skip,
> -						  get_loose_refs(&ref_store), err))
> +		    !verify_refname_available_dir(
> +				    refname, extras, skip,
> +				    get_loose_refs(refs), err))
>  			strbuf_addf(err, "unable to resolve reference '%s': %s",
>  				    refname, strerror(last_errno));
>  
> @@ -2123,7 +2130,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
>  	 */
>  	if (is_null_oid(&lock->old_oid) &&
>  	    verify_refname_available_dir(refname, extras, skip,
> -					 get_packed_refs(&ref_store), err)) {
> +					 get_packed_refs(refs),
> +					 err)) {
>  		last_errno = ENOTDIR;
>  		goto error_return;
>  	}
> @@ -2212,9 +2220,10 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
>   */
>  static int lock_packed_refs(int flags)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "lock_packed_refs");
>  	static int timeout_configured = 0;
>  	static int timeout_value = 1000;
> -
>  	struct packed_ref_cache *packed_ref_cache;
>  
>  	if (!timeout_configured) {
> @@ -2232,7 +2241,7 @@ static int lock_packed_refs(int flags)
>  	 * this will automatically invalidate the cache and re-read
>  	 * the packed-refs file.
>  	 */
> -	packed_ref_cache = get_packed_ref_cache(&ref_store);
> +	packed_ref_cache = get_packed_ref_cache(refs);
>  	packed_ref_cache->lock = &packlock;
>  	/* Increment the reference count to prevent it from being freed: */
>  	acquire_packed_ref_cache(packed_ref_cache);
> @@ -2247,8 +2256,10 @@ static int lock_packed_refs(int flags)
>   */
>  static int commit_packed_refs(void)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "commit_packed_refs");
>  	struct packed_ref_cache *packed_ref_cache =
> -		get_packed_ref_cache(&ref_store);
> +		get_packed_ref_cache(refs);
>  	int error = 0;
>  	int save_errno = 0;
>  	FILE *out;
> @@ -2281,15 +2292,17 @@ static int commit_packed_refs(void)
>   */
>  static void rollback_packed_refs(void)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "rollback_packed_refs");
>  	struct packed_ref_cache *packed_ref_cache =
> -		get_packed_ref_cache(&ref_store);
> +		get_packed_ref_cache(refs);
>  
>  	if (!packed_ref_cache->lock)
>  		die("internal error: packed-refs not locked");
>  	rollback_lock_file(packed_ref_cache->lock);
>  	packed_ref_cache->lock = NULL;
>  	release_packed_ref_cache(packed_ref_cache);
> -	clear_packed_ref_cache(&ref_store);
> +	clear_packed_ref_cache(refs);
>  }
>  
>  struct ref_to_prune {
> @@ -2422,15 +2435,17 @@ static void prune_refs(struct ref_to_prune *r)
>  
>  int pack_refs(unsigned int flags)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "pack_refs");
>  	struct pack_refs_cb_data cbdata;
>  
>  	memset(&cbdata, 0, sizeof(cbdata));
>  	cbdata.flags = flags;
>  
>  	lock_packed_refs(LOCK_DIE_ON_ERROR);
> -	cbdata.packed_refs = get_packed_refs(&ref_store);
> +	cbdata.packed_refs = get_packed_refs(refs);
>  
> -	do_for_each_entry_in_dir(get_loose_refs(&ref_store), 0,
> +	do_for_each_entry_in_dir(get_loose_refs(refs), 0,
>  				 pack_if_possible_fn, &cbdata);
>  
>  	if (commit_packed_refs())
> @@ -2449,6 +2464,8 @@ int pack_refs(unsigned int flags)
>   */
>  static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "repack_without_refs");
>  	struct ref_dir *packed;
>  	struct string_list_item *refname;
>  	int ret, needs_repacking = 0, removed = 0;
> @@ -2471,7 +2488,7 @@ static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
>  		unable_to_lock_message(git_path("packed-refs"), errno, err);
>  		return -1;
>  	}
> -	packed = get_packed_refs(&ref_store);
> +	packed = get_packed_refs(refs);
>  
>  	/* Remove refnames from the cache */
>  	for_each_string_list_item(refname, refnames)
> @@ -2616,8 +2633,10 @@ int verify_refname_available(const char *newname,
>  			     const struct string_list *skip,
>  			     struct strbuf *err)
>  {
> -	struct ref_dir *packed_refs = get_packed_refs(&ref_store);
> -	struct ref_dir *loose_refs = get_loose_refs(&ref_store);
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "verify_refname_available");
> +	struct ref_dir *packed_refs = get_packed_refs(refs);
> +	struct ref_dir *loose_refs = get_loose_refs(refs);
>  
>  	if (verify_refname_available_dir(newname, extras, skip,
>  					 packed_refs, err) ||
> @@ -2968,7 +2987,10 @@ static int commit_ref_update(struct ref_lock *lock,
>  			     const unsigned char *sha1, const char *logmsg,
>  			     struct strbuf *err)
>  {
> -	clear_loose_ref_cache(&ref_store);
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "commit_ref_update");
> +
> +	clear_loose_ref_cache(refs);
>  	if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
>  		char *old_msg = strbuf_detach(err, NULL);
>  		strbuf_addf(err, "cannot update the ref '%s': %s",
> @@ -3683,6 +3705,8 @@ static int lock_ref_for_update(struct ref_update *update,
>  int ref_transaction_commit(struct ref_transaction *transaction,
>  			   struct strbuf *err)
>  {
> +	struct files_ref_store *refs =
> +		get_files_ref_store(NULL, "ref_transaction_commit");
>  	int ret = 0, i;
>  	struct ref_update **updates = transaction->updates;
>  	struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
> @@ -3790,7 +3814,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
>  			}
>  		}
>  		if (update->flags & REF_NEEDS_COMMIT) {
> -			clear_loose_ref_cache(&ref_store);
> +			clear_loose_ref_cache(refs);
>  			if (commit_ref(lock)) {
>  				strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
>  				unlock_ref(lock);
> @@ -3823,7 +3847,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
>  	}
>  	for_each_string_list_item(ref_to_delete, &refs_to_delete)
>  		unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
> -	clear_loose_ref_cache(&ref_store);
> +	clear_loose_ref_cache(refs);
>  
>  cleanup:
>  	transaction->state = REF_TRANSACTION_CLOSED;
> @@ -4069,5 +4093,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
>  
>  struct ref_storage_be refs_be_files = {
>  	NULL,
> -	"files"
> +	"files",
> +	files_ref_store_create
>  };
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> index 4d88849..d6c74cf 100644
> --- a/refs/refs-internal.h
> +++ b/refs/refs-internal.h
> @@ -521,11 +521,89 @@ int read_raw_ref(const char *refname, unsigned char *sha1,
>  		 struct strbuf *referent, unsigned int *type);
>  
>  /* refs backends */
> +
> +/*
> + * Initialize the ref_store for the specified submodule, or for the
> + * main repository if submodule == NULL. These functions should call
> + * base_ref_store_init() to initialize the shared part of the
> + * ref_store and to record the ref_store for later lookup.
> + */
> +typedef struct ref_store *ref_store_init_fn(const char *submodule);
> +
>  struct ref_storage_be {
>  	struct ref_storage_be *next;
>  	const char *name;
> +	ref_store_init_fn *init;
>  };
>  
>  extern struct ref_storage_be refs_be_files;
>  
> +/*
> + * A representation of the reference store for the main repository or
> + * a submodule. The ref_store instances for submodules are kept in a
> + * linked list.
> + */
> +struct ref_store {
> +	/* The backend describing this ref_store's storage scheme: */
> +	const struct ref_storage_be *be;
> +
> +	/*
> +	 * The name of the submodule represented by this object, or
> +	 * the empty string if it represents the main repository's
> +	 * reference store:
> +	 */
> +	const char *submodule;
> +
> +	/*
> +	 * Submodule reference store instances are stored in a linked
> +	 * list using this pointer.
> +	 */
> +	struct ref_store *next;
> +};
> +
> +/*
> + * Fill in the generic part of refs for the specified submodule and
> + * add it to our collection of reference stores.
> + */
> +void base_ref_store_init(struct ref_store *refs,
> +			 const struct ref_storage_be *be,
> +			 const char *submodule);
> +
> +/*
> + * Create, record, and return a ref_store instance for the specified
> + * submodule (or the main repository if submodule is NULL).
> + *
> + * For backwards compatibility, submodule=="" is treated the same as
> + * submodule==NULL.
> + */
> +struct ref_store *ref_store_init(const char *submodule);
> +
> +/*
> + * Return the ref_store instance for the specified submodule (or the
> + * main repository if submodule is NULL). If that ref_store hasn't
> + * been initialized yet, return NULL.
> + *
> + * For backwards compatibility, submodule=="" is treated the same as
> + * submodule==NULL.
> + */
> +struct ref_store *lookup_ref_store(const char *submodule);
> +
> +/*
> + * Return the ref_store instance for the specified submodule. For the
> + * main repository, use submodule==NULL; such a call cannot fail. For
> + * a submodule, the submodule must exist and be a nonbare repository,
> + * otherwise return NULL. If the requested reference store has not yet
> + * been initialized, initialize it first.
> + *
> + * For backwards compatibility, submodule=="" is treated the same as
> + * submodule==NULL.
> + */
> +struct ref_store *get_ref_store(const char *submodule);
> +
> +/*
> + * Die if refs is for a submodule (i.e., not for the main repository).
> + * caller is used in any necessary error messages.
> + */
> +void assert_main_repository(struct ref_store *refs, const char *caller);
> +
>  #endif /* REFS_REFS_INTERNAL_H */
René Scharfe· Jun 9, 2016, 21:54 UTC · re: Junio C Hamano · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

Am 07.06.2016 um 18:31 schrieb Junio C Hamano:
Show 7 quoted lines
> This is a tangent, but your series that ends at 4aa2c475 (grep: -W:
> don't extend context to trailing empty lines, 2016-05-28) does not
> seem to have much effect when viewing the change to refs.c this
> patch makes (it modifies a function in an early part, and then adds
> bunch of new functions at the end) with "git show -W".
> 
> Thanks.
Good catch, thanks!
-- >8 --
Subject: [PATCH] xdiff: fix merging of appended hunk with -W

When -W is given we search the lines between the end of the current context and the next change for a function line. If there is none then we merge those two hunks as they must be part of the same function.

If the next change is an appended chunk we abort the search early in get_func_line(), however, because its line number is out of range. Fix that by searching from the end of the pre-image in that case instead.

Reported-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Rene Scharfe <l.s.r@web.de>
---
 t/t4051-diff-function-context.sh | 24 +++++++++++++++++++++++-
 xdiff/xemit.c                    |  3 ++-
 2 files changed, 25 insertions(+), 2 deletions(-)
Show changes to 2 files +25 −2

t/t4051-diff-function-context.sh, xdiff/xemit.c

diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
index b6bb04a..b79b877 100755
--- a/t/t4051-diff-function-context.sh
+++ b/t/t4051-diff-function-context.sh
@@ -64,7 +64,13 @@ test_expect_success 'setup' '
 
 	grep -v "Begin of second part" <file.c >file.c.new &&
 	mv file.c.new file.c &&
-	commit_and_tag long_common_tail file.c
+	commit_and_tag long_common_tail file.c &&
+
+	git checkout initial &&
+	grep -v "delete me from hello" <file.c >file.c.new &&
+	mv file.c.new file.c &&
+	cat "$dir/appended1.c" >>file.c &&
+	commit_and_tag changed_hello_appended file.c
 '
 
 check_diff changed_hello 'changed function'
@@ -157,4 +163,20 @@ test_expect_success ' context does not include preceding empty lines' '
 	test "$(first_context_line <long_common_tail.diff.diff)" != " "
 '
 
+check_diff changed_hello_appended 'changed function plus appended function'
+
+test_expect_success ' context includes begin' '
+	grep "^ .*Begin of hello" changed_hello_appended.diff &&
+	grep "^[+].*Begin of first part" changed_hello_appended.diff
+'
+
+test_expect_success ' context includes end' '
+	grep "^ .*End of hello" changed_hello_appended.diff &&
+	grep "^[+].*End of first part" changed_hello_appended.diff
+'
+
+test_expect_success ' context does not include other functions' '
+	test $(grep -c "^[ +-].*Begin" changed_hello_appended.diff) -le 2
+'
+
 test_done
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index bfa53d3..49aa16f 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -246,7 +246,8 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 			 * its new end.
 			 */
 			if (xche->next) {
-				long l = xche->next->i1;
+				long l = XDL_MIN(xche->next->i1,
+						 xe->xdf1.nrec - 1);
 				if (l <= e1 ||
 				    get_func_line(xe, xecfg, NULL, l, e1) < 0) {
 					xche = xche->next;
-- 
2.8.3
Junio C Hamano· Jun 7, 2016, 17:03 UTC · re: Michael Haggerty · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

Michael Haggerty <mhagger@alum.mit.edu> writes:
> We want ref_stores to be polymorphic, so invent a base class of which
> files_ref_store is a derived class. For now there is a one-to-one
> relationship between ref_stores and submodules.

The mention of "submodules" made me go "Huh?" but thinking about it for a second it is clear and obvious. We often peek into refs in a different repository that is a submodule, and we do not mix them with our own refs. Logically that is what a "ref store" is, and one-to-one relationship is expected.

Show 8 quoted lines
> @@ -1284,3 +1288,90 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
>  	errno = ELOOP;
>  	return NULL;
>  }
> +
> +static struct ref_store *main_ref_store = NULL;
> +
> +static struct ref_store *submodule_ref_stores = NULL;
Let's let BSS take care of these initialization.
Show 39 quoted lines
> +/*
> + * Downcast ref_store to files_ref_store. Die if ref_store is not a
> + * files_ref_store. If submodule_allowed is not true, then also die if
> + * files_ref_store is for a submodule (i.e., not for the main
> + * repository). caller is used in any necessary error messages.
> + */
> +static struct files_ref_store *files_downcast(
> +		struct ref_store *ref_store, int submodule_allowed,
> +		const char *caller)
>  {
>  	struct files_ref_store *refs;
>  
> +	if (ref_store->be != &refs_be_files)
> +		die("BUG: ref_store is type \"%s\" not \"files\" in %s",
> +		    ref_store->be->name, caller);
>  
> +	refs = (struct files_ref_store *)ref_store;
> +
> +	if (!submodule_allowed)
> +		assert_main_repository(ref_store, caller);
> +
> +	return refs;
>  }
>
>  /*
> + * Return a pointer to the reference store for the specified
> + * submodule. For the main repository, use submodule==NULL; such a
> + * call cannot fail. For a submodule, the submodule must exist and be
> + * a nonbare repository, otherwise return NULL. Verify that the
> + * reference store is a files_ref_store, and cast it to that type
> + * before returning it.
>   */
> +static struct files_ref_store *get_files_ref_store(const char *submodule,
> +						   const char *caller)
>  {
> +	struct ref_store *refs = get_ref_store(submodule);
>  
> +	return refs ? files_downcast(refs, 1, caller) : NULL;
>  }

This comment may be barking up a wrong tree, but the support for class inheritance makes me wonder if I can do something along this line:

 * implement a filesystem based ref_store, that is very similar to
   what you have as files_ref_store, but 
   - when storing a ref as a loose ref, or when checking if a ref
     exists as a loose ref, quote them somehow (e.g. a branch
     "Branch" is not stored as a file "$GIT_DIR/refs/heads/branch"
     but by using "^" as a single shift marker, i.e. as
     "$GIT_DIR/refs/heads/^branch");
   - when enumerating what refs we have as loose refs, unquote what
     readdir(3) gave us, e.g. seeing "$GIT_DIR/refs/heads/^branch",
     I say "there is a branch whose name is 'Branch'".
 * as locking and other 'methods' are likely to be very similar to
   your files_ref_store, make this new backend as a subclass of it,
   i.e. create a new class but function pointers to many methods are
   copied from files ref_store vtable.

Would the strict "when downcasting to 'files', we make sure vtable is that of 'files' backend" interfere with such an approach?

Michael Haggerty· Jun 9, 2016, 14:10 UTC · re: Junio C Hamano · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

On 06/07/2016 07:03 PM, Junio C Hamano wrote:
Show 22 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> We want ref_stores to be polymorphic, so invent a base class of which
>> files_ref_store is a derived class. For now there is a one-to-one
>> relationship between ref_stores and submodules.
> 
> The mention of "submodules" made me go "Huh?" but thinking about it
> for a second it is clear and obvious.  We often peek into refs in a
> different repository that is a submodule, and we do not mix them with
> our own refs.  Logically that is what a "ref store" is, and one-to-one
> relationship is expected.
> 
>> @@ -1284,3 +1288,90 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
>>  	errno = ELOOP;
>>  	return NULL;
>>  }
>> +
>> +static struct ref_store *main_ref_store = NULL;
>> +
>> +static struct ref_store *submodule_ref_stores = NULL;
> 
> Let's let BSS take care of these initialization.

I like the `= NULL` because it expresses "yes, I care about the initial values of these variables", which to me is more valuable than saving a few bytes in the size of the executable. But in fact, GCC does the obvious optimization: it detects that these variables are being initialized to zero, and puts them in BSS anyway. I'd be surprised if other compilers don't do the same. So I'd prefer to leave this as-is, if it's OK with you.

Show 38 quoted lines
>> [...]
>>  /*
>> + * Return a pointer to the reference store for the specified
>> + * submodule. For the main repository, use submodule==NULL; such a
>> + * call cannot fail. For a submodule, the submodule must exist and be
>> + * a nonbare repository, otherwise return NULL. Verify that the
>> + * reference store is a files_ref_store, and cast it to that type
>> + * before returning it.
>>   */
>> +static struct files_ref_store *get_files_ref_store(const char *submodule,
>> +						   const char *caller)
>>  {
>> +	struct ref_store *refs = get_ref_store(submodule);
>>  
>> +	return refs ? files_downcast(refs, 1, caller) : NULL;
>>  }
> 
> This comment may be barking up a wrong tree, but the support for
> class inheritance makes me wonder if I can do something along this
> line:
> 
>  * implement a filesystem based ref_store, that is very similar to
>    what you have as files_ref_store, but 
> 
>    - when storing a ref as a loose ref, or when checking if a ref
>      exists as a loose ref, quote them somehow (e.g. a branch
>      "Branch" is not stored as a file "$GIT_DIR/refs/heads/branch"
>      but by using "^" as a single shift marker, i.e. as
>      "$GIT_DIR/refs/heads/^branch");
> 
>    - when enumerating what refs we have as loose refs, unquote what
>      readdir(3) gave us, e.g. seeing "$GIT_DIR/refs/heads/^branch",
>      I say "there is a branch whose name is 'Branch'".
> 
>  * as locking and other 'methods' are likely to be very similar to
>    your files_ref_store, make this new backend as a subclass of it,
>    i.e. create a new class but function pointers to many methods are
>    copied from files ref_store vtable.

That is definitely something we could implement in the future. If I were going to design an extension like this, I think I'd go straight to something more expressive; maybe something like URL-encoding. For example, we might like a system that can record refnames with a Unicode encoding that is determined by us rather than by the filesystem.

Depending on the details, it might be preferable to implement the new ref-store as an encoding layer that delegates to an old-fashioned files backend rather than using inheritance. In fact, you'd only want to do the translation for the loose storage part, so in the end the implemention might look something like

    overlay_ref_store(
        encoding_ref_store(loose_ref_store()),
        packed_ref_store())
> Would the strict "when downcasting to 'files', we make sure vtable
> is that of 'files' backend" interfere with such an approach?

True, the simple approach that I use above doesn't generalize to implementation inheritance. But I'd rather cross that bridge when we come to it. Implementing an RTTI system in C is a bit ambitious and, I think, comes with runtime costs.

Michael
Junio C Hamano· Jun 9, 2016, 16:14 UTC · re: Michael Haggerty · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 14 quoted lines
>>> +
>>> +static struct ref_store *main_ref_store = NULL;
>>> +
>>> +static struct ref_store *submodule_ref_stores = NULL;
>> 
>> Let's let BSS take care of these initialization.
>
> I like the `= NULL` because it expresses "yes, I care about the initial
> values of these variables", which to me is more valuable than saving a
> few bytes in the size of the executable. But in fact, GCC does the
> obvious optimization: it detects that these variables are being
> initialized to zero, and puts them in BSS anyway. I'd be surprised if
> other compilers don't do the same. So I'd prefer to leave this as-is, if
> it's OK with you.

Sorry; I shouldn't have phrased it with BSS, because the main point of the convention is not about "saving bytes by placing it in BSS".

Lack of "= ..." is a clear-enough clue that the code wants these pointers initialized to NULL. And in this snippet:

	static struct foo the_foo;
        static struct foo *default_foo = &the_foo;
        static struct foo *other_foo;

we want the presence of "=" as a clue that something special is going on only for default_foo (it is not initialied to the nul value for the type like usual static variables). If you wrote it this way,

        static struct foo *default_foo = &the_foo;
        static struct foo *other_foo = NULL;

the reader has to say "something funny going on about other_foo?" when scanning up to "other_foo =" and then "... ah, no that is just wasted typing and reading, it is left to NULL after all".

So, no, it is not OK with me.
Otherwise I would have said "I wonder if ..." ;-)
Michael Haggerty· Jun 10, 2016, 06:18 UTC · re: Junio C Hamano · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

On 06/09/2016 06:14 PM, Junio C Hamano wrote:
Show 11 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>>>> +static struct ref_store *main_ref_store = NULL;
>>>> +
>>>> +static struct ref_store *submodule_ref_stores = NULL;
>>>
>>> Let's let BSS take care of these initialization.
> [...]
> Lack of "= ..." is a clear-enough clue that the code wants these
> pointers initialized to NULL.
> [...]
OK. While I'm at it I'll add docstrings for these variables.
Michael
Junio C Hamano· Jun 10, 2016, 15:53 UTC · re: Michael Haggerty · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 14 quoted lines
> On 06/09/2016 06:14 PM, Junio C Hamano wrote:
>> Michael Haggerty <mhagger@alum.mit.edu> writes:
>> 
>>>>> +static struct ref_store *main_ref_store = NULL;
>>>>> +
>>>>> +static struct ref_store *submodule_ref_stores = NULL;
>>>>
>>>> Let's let BSS take care of these initialization.
>> [...]
>> Lack of "= ..." is a clear-enough clue that the code wants these
>> pointers initialized to NULL.
>> [...]
>
> OK. While I'm at it I'll add docstrings for these variables.

Yeah, if you explain what they are used for, it would become obvious why their natural initial state is NULL.

Eric Sunshine· Jun 10, 2016, 08:08 UTC · re: Michael Haggerty · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

On Fri, Jun 3, 2016 at 5:03 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
Show 31 quoted lines
> We want ref_stores to be polymorphic, so invent a base class of which
> files_ref_store is a derived class. For now there is a one-to-one
> relationship between ref_stores and submodules.
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> @@ -973,53 +967,54 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
> +/*
> + * Downcast ref_store to files_ref_store. Die if ref_store is not a
> + * files_ref_store. If submodule_allowed is not true, then also die if
> + * files_ref_store is for a submodule (i.e., not for the main
> + * repository). caller is used in any necessary error messages.
> + */
> +static struct files_ref_store *files_downcast(
> +               struct ref_store *ref_store, int submodule_allowed,
> +               const char *caller)
>  {
>         struct files_ref_store *refs;
>
> +       if (ref_store->be != &refs_be_files)
> +               die("BUG: ref_store is type \"%s\" not \"files\" in %s",
> +                   ref_store->be->name, caller);
>
> +       refs = (struct files_ref_store *)ref_store;
> +
> +       if (!submodule_allowed)
> +               assert_main_repository(ref_store, caller);
> +
> +       return refs;
>  }

Aside from returning the downcasted value, 'refs' doesn't seem to be used for anything, thus could be dropped and the downcasted value returned directly:

    return (struct files_ref_store *)ref_store;
Not worth a re-roll.
Show 17 quoted lines
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> @@ -521,11 +521,89 @@ int read_raw_ref(const char *refname, unsigned char *sha1,
> +/*
> + * A representation of the reference store for the main repository or
> + * a submodule. The ref_store instances for submodules are kept in a
> + * linked list.
> + */
> +struct ref_store {
> +       /* The backend describing this ref_store's storage scheme: */
> +       const struct ref_storage_be *be;
> +
> +       /*
> +        * The name of the submodule represented by this object, or
> +        * the empty string if it represents the main repository's
> +        * reference store:
> +        */
> +       const char *submodule;
Tangent: Apart from backward compatibility due to all the existing
code which tests *submodule to distinguish between the main repository
and a submodule, is there a technical reason that this ought to store
an empty string rather than (the more idiomatic) NULL to signify the
main repository?
Michael Haggerty· Jun 10, 2016, 12:02 UTC · re: Eric Sunshine · lore

Re: [PATCH 05/38] refs: create a base class "ref_store" for files_ref_store

On 06/10/2016 10:08 AM, Eric Sunshine wrote:
Show 40 quoted lines
> On Fri, Jun 3, 2016 at 5:03 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
>> We want ref_stores to be polymorphic, so invent a base class of which
>> files_ref_store is a derived class. For now there is a one-to-one
>> relationship between ref_stores and submodules.
>>
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
>> diff --git a/refs/files-backend.c b/refs/files-backend.c
>> @@ -973,53 +967,54 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
>> +/*
>> + * Downcast ref_store to files_ref_store. Die if ref_store is not a
>> + * files_ref_store. If submodule_allowed is not true, then also die if
>> + * files_ref_store is for a submodule (i.e., not for the main
>> + * repository). caller is used in any necessary error messages.
>> + */
>> +static struct files_ref_store *files_downcast(
>> +               struct ref_store *ref_store, int submodule_allowed,
>> +               const char *caller)
>>  {
>>         struct files_ref_store *refs;
>>
>> +       if (ref_store->be != &refs_be_files)
>> +               die("BUG: ref_store is type \"%s\" not \"files\" in %s",
>> +                   ref_store->be->name, caller);
>>
>> +       refs = (struct files_ref_store *)ref_store;
>> +
>> +       if (!submodule_allowed)
>> +               assert_main_repository(ref_store, caller);
>> +
>> +       return refs;
>>  }
> 
> Aside from returning the downcasted value, 'refs' doesn't seem to be
> used for anything, thus could be dropped and  the downcasted value
> returned directly:
> 
>     return (struct files_ref_store *)ref_store;
> 
> Not worth a re-roll.
Good point. I'll fix it.
Show 23 quoted lines
>> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
>> @@ -521,11 +521,89 @@ int read_raw_ref(const char *refname, unsigned char *sha1,
>> +/*
>> + * A representation of the reference store for the main repository or
>> + * a submodule. The ref_store instances for submodules are kept in a
>> + * linked list.
>> + */
>> +struct ref_store {
>> +       /* The backend describing this ref_store's storage scheme: */
>> +       const struct ref_storage_be *be;
>> +
>> +       /*
>> +        * The name of the submodule represented by this object, or
>> +        * the empty string if it represents the main repository's
>> +        * reference store:
>> +        */
>> +       const char *submodule;
> 
> Tangent: Apart from backward compatibility due to all the existing
> code which tests *submodule to distinguish between the main repository
> and a submodule, is there a technical reason that this ought to store
> an empty string rather than (the more idiomatic) NULL to signify the
> main repository?

No, this was just how the old code worked and I just haven't gotten around to changing it. I actually started doing the conversion once, but it was turning into too much of a distraction, so I added the item to my TODO list instead.

Michael
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 06/38] add_packed_ref(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
Show changes to refs/files-backend.c +5 −4
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 8ef79c2..5bfa912 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1196,10 +1196,9 @@ static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
  * lock_packed_refs()).  To actually write the packed-refs file, call
  * commit_packed_refs().
  */
-static void add_packed_ref(const char *refname, const unsigned char *sha1)
+static void add_packed_ref(struct files_ref_store *refs,
+			   const char *refname, const unsigned char *sha1)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "add_packed_ref");
 	struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
 
 	if (!packed_ref_cache->lock)
@@ -3873,6 +3872,8 @@ static int ref_present(const char *refname,
 int initial_ref_transaction_commit(struct ref_transaction *transaction,
 				   struct strbuf *err)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "initial_ref_transaction_commit");
 	int ret = 0, i;
 	struct ref_update **updates = transaction->updates;
 	struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
@@ -3932,7 +3933,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
 
 		if ((update->flags & REF_HAVE_NEW) &&
 		    !is_null_sha1(update->new_sha1))
-			add_packed_ref(update->refname, update->new_sha1);
+			add_packed_ref(refs, update->refname, update->new_sha1);
 	}
 
 	if (commit_packed_refs()) {
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 07/38] get_packed_ref(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)
Show changes to refs/files-backend.c +9 −7
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 5bfa912..57f1965 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1407,11 +1407,9 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
  * Return the ref_entry for the given refname from the packed
  * references.  If it does not exist, return NULL.
  */
-static struct ref_entry *get_packed_ref(const char *refname)
+static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
+					const char *refname)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "get_packed_ref");
-
 	return find_ref(get_packed_refs(refs), refname);
 }
 
@@ -1422,13 +1420,16 @@ static int resolve_missing_loose_ref(const char *refname,
 				     unsigned char *sha1,
 				     unsigned int *flags)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "resolve_missing_loose_ref");
+
 	struct ref_entry *entry;
 
 	/*
 	 * The loose reference file does not exist; check for a packed
 	 * reference.
 	 */
-	entry = get_packed_ref(refname);
+	entry = get_packed_ref(refs, refname);
 	if (entry) {
 		hashcpy(sha1, entry->u.value.oid.hash);
 		*flags |= REF_ISPACKED;
@@ -1840,6 +1841,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
 
 int peel_ref(const char *refname, unsigned char *sha1)
 {
+	struct files_ref_store *refs = get_files_ref_store(NULL, "peel_ref");
 	int flag;
 	unsigned char base[20];
 
@@ -1864,7 +1866,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
 	 * have REF_KNOWS_PEELED.
 	 */
 	if (flag & REF_ISPACKED) {
-		struct ref_entry *r = get_packed_ref(refname);
+		struct ref_entry *r = get_packed_ref(refs, refname);
 		if (r) {
 			if (peel_entry(r, 0))
 				return -1;
@@ -2473,7 +2475,7 @@ static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 
 	/* Look for a packed ref */
 	for_each_string_list_item(refname, refnames) {
-		if (get_packed_ref(refname->string)) {
+		if (get_packed_ref(refs, refname->string)) {
 			needs_repacking = 1;
 			break;
 		}
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 08/38] resolve_missing_loose_ref(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
Show changes to refs/files-backend.c +6 −6
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 57f1965..9307fa0 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1416,13 +1416,11 @@ static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
 /*
  * A loose ref file doesn't exist; check for a packed ref.
  */
-static int resolve_missing_loose_ref(const char *refname,
+static int resolve_missing_loose_ref(struct files_ref_store *refs,
+				     const char *refname,
 				     unsigned char *sha1,
 				     unsigned int *flags)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "resolve_missing_loose_ref");
-
 	struct ref_entry *entry;
 
 	/*
@@ -1442,6 +1440,8 @@ static int resolve_missing_loose_ref(const char *refname,
 int read_raw_ref(const char *refname, unsigned char *sha1,
 		 struct strbuf *referent, unsigned int *type)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "read_raw_ref");
 	struct strbuf sb_contents = STRBUF_INIT;
 	struct strbuf sb_path = STRBUF_INIT;
 	const char *path;
@@ -1470,7 +1470,7 @@ stat_ref:
 	if (lstat(path, &st) < 0) {
 		if (errno != ENOENT)
 			goto out;
-		if (resolve_missing_loose_ref(refname, sha1, type)) {
+		if (resolve_missing_loose_ref(refs, refname, sha1, type)) {
 			errno = ENOENT;
 			goto out;
 		}
@@ -1504,7 +1504,7 @@ stat_ref:
 		 * ref is supposed to be, there could still be a
 		 * packed ref:
 		 */
-		if (resolve_missing_loose_ref(refname, sha1, type)) {
+		if (resolve_missing_loose_ref(refs, refname, sha1, type)) {
 			errno = EISDIR;
 			goto out;
 		}
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 09/38] {lock,commit,rollback}_packed_refs(): add files_ref_store arguments

These functions currently only work in the main repository, so add an assert_main_repository() check to each function.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
Show changes to refs/files-backend.c +16 −16
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 9307fa0..0d64a3d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2219,14 +2219,14 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
  * hold_lock_file_for_update(). Return 0 on success. On errors, set
  * errno appropriately and return a nonzero value.
  */
-static int lock_packed_refs(int flags)
+static int lock_packed_refs(struct files_ref_store *refs, int flags)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "lock_packed_refs");
 	static int timeout_configured = 0;
 	static int timeout_value = 1000;
 	struct packed_ref_cache *packed_ref_cache;
 
+	assert_main_repository(&refs->base, "lock_packed_refs");
+
 	if (!timeout_configured) {
 		git_config_get_int("core.packedrefstimeout", &timeout_value);
 		timeout_configured = 1;
@@ -2255,16 +2255,16 @@ static int lock_packed_refs(int flags)
  * lock_packed_refs()). Return zero on success. On errors, set errno
  * and return a nonzero value
  */
-static int commit_packed_refs(void)
+static int commit_packed_refs(struct files_ref_store *refs)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "commit_packed_refs");
 	struct packed_ref_cache *packed_ref_cache =
 		get_packed_ref_cache(refs);
 	int error = 0;
 	int save_errno = 0;
 	FILE *out;
 
+	assert_main_repository(&refs->base, "commit_packed_refs");
+
 	if (!packed_ref_cache->lock)
 		die("internal error: packed-refs not locked");
 
@@ -2291,13 +2291,13 @@ static int commit_packed_refs(void)
  * in-memory packed reference cache.  (The packed-refs file will be
  * read anew if it is needed again after this function is called.)
  */
-static void rollback_packed_refs(void)
+static void rollback_packed_refs(struct files_ref_store *refs)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "rollback_packed_refs");
 	struct packed_ref_cache *packed_ref_cache =
 		get_packed_ref_cache(refs);
 
+	assert_main_repository(&refs->base, "rollback_packed_refs");
+
 	if (!packed_ref_cache->lock)
 		die("internal error: packed-refs not locked");
 	rollback_lock_file(packed_ref_cache->lock);
@@ -2443,13 +2443,13 @@ int pack_refs(unsigned int flags)
 	memset(&cbdata, 0, sizeof(cbdata));
 	cbdata.flags = flags;
 
-	lock_packed_refs(LOCK_DIE_ON_ERROR);
+	lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
 	cbdata.packed_refs = get_packed_refs(refs);
 
 	do_for_each_entry_in_dir(get_loose_refs(refs), 0,
 				 pack_if_possible_fn, &cbdata);
 
-	if (commit_packed_refs())
+	if (commit_packed_refs(refs))
 		die_errno("unable to overwrite old ref-pack file");
 
 	prune_refs(cbdata.ref_to_prune);
@@ -2485,7 +2485,7 @@ static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 	if (!needs_repacking)
 		return 0; /* no refname exists in packed refs */
 
-	if (lock_packed_refs(0)) {
+	if (lock_packed_refs(refs, 0)) {
 		unable_to_lock_message(git_path("packed-refs"), errno, err);
 		return -1;
 	}
@@ -2500,12 +2500,12 @@ static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 		 * All packed entries disappeared while we were
 		 * acquiring the lock.
 		 */
-		rollback_packed_refs();
+		rollback_packed_refs(refs);
 		return 0;
 	}
 
 	/* Write what remains */
-	ret = commit_packed_refs();
+	ret = commit_packed_refs(refs);
 	if (ret)
 		strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
 			    strerror(errno));
@@ -3923,7 +3923,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
 		}
 	}
 
-	if (lock_packed_refs(0)) {
+	if (lock_packed_refs(refs, 0)) {
 		strbuf_addf(err, "unable to lock packed-refs file: %s",
 			    strerror(errno));
 		ret = TRANSACTION_GENERIC_ERROR;
@@ -3938,7 +3938,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
 			add_packed_ref(refs, update->refname, update->new_sha1);
 	}
 
-	if (commit_packed_refs()) {
+	if (commit_packed_refs(refs)) {
 		strbuf_addf(err, "unable to commit packed-refs file: %s",
 			    strerror(errno));
 		ret = TRANSACTION_GENERIC_ERROR;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 10/38] refs: add a transaction_commit() method

From: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               |  9 +++++++++
 refs/files-backend.c | 10 ++++++----
 refs/refs-internal.h |  5 +++++
 3 files changed, 20 insertions(+), 4 deletions(-)
Show changes to 3 files +20 −4

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 5e20ae0..904e758 100644
--- a/refs.c
+++ b/refs.c
@@ -1375,3 +1375,12 @@ void assert_main_repository(struct ref_store *refs, const char *caller)
 	if (*refs->submodule)
 		die("BUG: %s called for a submodule", caller);
 }
+
+/* backend functions */
+int ref_transaction_commit(struct ref_transaction *transaction,
+			   struct strbuf *err)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->transaction_commit(refs, transaction, err);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 0d64a3d..fea4487 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3703,11 +3703,12 @@ static int lock_ref_for_update(struct ref_update *update,
 	return 0;
 }
 
-int ref_transaction_commit(struct ref_transaction *transaction,
-			   struct strbuf *err)
+static int files_transaction_commit(struct ref_store *ref_store,
+				    struct ref_transaction *transaction,
+				    struct strbuf *err)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "ref_transaction_commit");
+		files_downcast(ref_store, 0, "ref_transaction_commit");
 	int ret = 0, i;
 	struct ref_update **updates = transaction->updates;
 	struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
@@ -4097,5 +4098,6 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 struct ref_storage_be refs_be_files = {
 	NULL,
 	"files",
-	files_ref_store_create
+	files_ref_store_create,
+	files_transaction_commit
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index d6c74cf..e24366b 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -530,10 +530,15 @@ int read_raw_ref(const char *refname, unsigned char *sha1,
  */
 typedef struct ref_store *ref_store_init_fn(const char *submodule);
 
+typedef int ref_transaction_commit_fn(struct ref_store *refs,
+				      struct ref_transaction *transaction,
+				      struct strbuf *err);
+
 struct ref_storage_be {
 	struct ref_storage_be *next;
 	const char *name;
 	ref_store_init_fn *init;
+	ref_transaction_commit_fn *transaction_commit;
 };
 
 extern struct ref_storage_be refs_be_files;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 11/38] refs: reorder definitions

Move resolve_gitlink_ref() and related functions lower in the file to avoid the need for forward declarations in the next step.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 166 +++++++++++++++++++++++++--------------------------
 1 file changed, 83 insertions(+), 83 deletions(-)
Show changes to refs/files-backend.c +83 −83
diff --git a/refs/files-backend.c b/refs/files-backend.c
index fea4487..50bfe97 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1320,89 +1320,6 @@ static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
 	return get_ref_dir(refs->loose);
 }
 
-#define MAXREFLEN (1024)
-
-/*
- * Called by resolve_gitlink_ref_recursive() after it failed to read
- * from the loose refs in refs. Find <refname> in the packed-refs file
- * for the submodule.
- */
-static int resolve_gitlink_packed_ref(struct files_ref_store *refs,
-				      const char *refname, unsigned char *sha1)
-{
-	struct ref_entry *ref;
-	struct ref_dir *dir = get_packed_refs(refs);
-
-	ref = find_ref(dir, refname);
-	if (ref == NULL)
-		return -1;
-
-	hashcpy(sha1, ref->u.value.oid.hash);
-	return 0;
-}
-
-static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
-					 const char *refname, unsigned char *sha1,
-					 int recursion)
-{
-	int fd, len;
-	char buffer[128], *p;
-	char *path;
-
-	if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
-		return -1;
-	path = *refs->base.submodule
-		? git_pathdup_submodule(refs->base.submodule, "%s", refname)
-		: git_pathdup("%s", refname);
-	fd = open(path, O_RDONLY);
-	free(path);
-	if (fd < 0)
-		return resolve_gitlink_packed_ref(refs, refname, sha1);
-
-	len = read(fd, buffer, sizeof(buffer)-1);
-	close(fd);
-	if (len < 0)
-		return -1;
-	while (len && isspace(buffer[len-1]))
-		len--;
-	buffer[len] = 0;
-
-	/* Was it a detached head or an old-fashioned symlink? */
-	if (!get_sha1_hex(buffer, sha1))
-		return 0;
-
-	/* Symref? */
-	if (strncmp(buffer, "ref:", 4))
-		return -1;
-	p = buffer + 4;
-	while (isspace(*p))
-		p++;
-
-	return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
-}
-
-int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
-{
-	int len = strlen(path);
-	struct strbuf submodule = STRBUF_INIT;
-	struct files_ref_store *refs;
-
-	while (len && path[len-1] == '/')
-		len--;
-	if (!len)
-		return -1;
-
-	strbuf_add(&submodule, path, len);
-	refs = get_files_ref_store(submodule.buf, "resolve_gitlink_ref");
-	if (!refs) {
-		strbuf_release(&submodule);
-		return -1;
-	}
-	strbuf_release(&submodule);
-
-	return resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
-}
-
 /*
  * Return the ref_entry for the given refname from the packed
  * references.  If it does not exist, return NULL.
@@ -1576,6 +1493,89 @@ static void unlock_ref(struct ref_lock *lock)
 	free(lock);
 }
 
+#define MAXREFLEN (1024)
+
+/*
+ * Called by resolve_gitlink_ref_recursive() after it failed to read
+ * from the loose refs in refs. Find <refname> in the packed-refs file
+ * for the submodule.
+ */
+static int resolve_gitlink_packed_ref(struct files_ref_store *refs,
+				      const char *refname, unsigned char *sha1)
+{
+	struct ref_entry *ref;
+	struct ref_dir *dir = get_packed_refs(refs);
+
+	ref = find_ref(dir, refname);
+	if (ref == NULL)
+		return -1;
+
+	hashcpy(sha1, ref->u.value.oid.hash);
+	return 0;
+}
+
+static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
+					 const char *refname, unsigned char *sha1,
+					 int recursion)
+{
+	int fd, len;
+	char buffer[128], *p;
+	char *path;
+
+	if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
+		return -1;
+	path = *refs->base.submodule
+		? git_pathdup_submodule(refs->base.submodule, "%s", refname)
+		: git_pathdup("%s", refname);
+	fd = open(path, O_RDONLY);
+	free(path);
+	if (fd < 0)
+		return resolve_gitlink_packed_ref(refs, refname, sha1);
+
+	len = read(fd, buffer, sizeof(buffer)-1);
+	close(fd);
+	if (len < 0)
+		return -1;
+	while (len && isspace(buffer[len-1]))
+		len--;
+	buffer[len] = 0;
+
+	/* Was it a detached head or an old-fashioned symlink? */
+	if (!get_sha1_hex(buffer, sha1))
+		return 0;
+
+	/* Symref? */
+	if (strncmp(buffer, "ref:", 4))
+		return -1;
+	p = buffer + 4;
+	while (isspace(*p))
+		p++;
+
+	return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
+}
+
+int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
+{
+	int len = strlen(path);
+	struct strbuf submodule = STRBUF_INIT;
+	struct files_ref_store *refs;
+
+	while (len && path[len-1] == '/')
+		len--;
+	if (!len)
+		return -1;
+
+	strbuf_add(&submodule, path, len);
+	refs = get_files_ref_store(submodule.buf, "resolve_gitlink_ref");
+	if (!refs) {
+		strbuf_release(&submodule);
+		return -1;
+	}
+	strbuf_release(&submodule);
+
+	return resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
+}
+
 /*
  * Lock refname, without following symrefs, and set *lock_p to point
  * at a newly-allocated lock object. Fill in lock->old_oid, referent,
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 12/38] resolve_packed_ref(): rename function from resolve_missing_loose_ref()

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
Show changes to refs/files-backend.c +5 −6
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 50bfe97..b54355d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1333,10 +1333,9 @@ static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
 /*
  * A loose ref file doesn't exist; check for a packed ref.
  */
-static int resolve_missing_loose_ref(struct files_ref_store *refs,
-				     const char *refname,
-				     unsigned char *sha1,
-				     unsigned int *flags)
+static int resolve_packed_ref(struct files_ref_store *refs,
+			      const char *refname,
+			      unsigned char *sha1, unsigned int *flags)
 {
 	struct ref_entry *entry;
 
@@ -1387,7 +1386,7 @@ stat_ref:
 	if (lstat(path, &st) < 0) {
 		if (errno != ENOENT)
 			goto out;
-		if (resolve_missing_loose_ref(refs, refname, sha1, type)) {
+		if (resolve_packed_ref(refs, refname, sha1, type)) {
 			errno = ENOENT;
 			goto out;
 		}
@@ -1421,7 +1420,7 @@ stat_ref:
 		 * ref is supposed to be, there could still be a
 		 * packed ref:
 		 */
-		if (resolve_missing_loose_ref(refs, refname, sha1, type)) {
+		if (resolve_packed_ref(refs, refname, sha1, type)) {
 			errno = EISDIR;
 			goto out;
 		}
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 13/38] resolve_gitlink_packed_ref(): remove function

Now that resolve_packed_ref() can work with an arbitrary files_ref_store, there is no need to have a separate resolve_gitlink_packed_ref() function.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 26 +++++---------------------
 1 file changed, 5 insertions(+), 21 deletions(-)
Show changes to refs/files-backend.c +5 −21
diff --git a/refs/files-backend.c b/refs/files-backend.c
index b54355d..5f14227 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1494,25 +1494,6 @@ static void unlock_ref(struct ref_lock *lock)
 
 #define MAXREFLEN (1024)
 
-/*
- * Called by resolve_gitlink_ref_recursive() after it failed to read
- * from the loose refs in refs. Find <refname> in the packed-refs file
- * for the submodule.
- */
-static int resolve_gitlink_packed_ref(struct files_ref_store *refs,
-				      const char *refname, unsigned char *sha1)
-{
-	struct ref_entry *ref;
-	struct ref_dir *dir = get_packed_refs(refs);
-
-	ref = find_ref(dir, refname);
-	if (ref == NULL)
-		return -1;
-
-	hashcpy(sha1, ref->u.value.oid.hash);
-	return 0;
-}
-
 static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
 					 const char *refname, unsigned char *sha1,
 					 int recursion)
@@ -1528,8 +1509,11 @@ static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
 		: git_pathdup("%s", refname);
 	fd = open(path, O_RDONLY);
 	free(path);
-	if (fd < 0)
-		return resolve_gitlink_packed_ref(refs, refname, sha1);
+	if (fd < 0) {
+		unsigned int flags;
+
+		return resolve_packed_ref(refs, refname, sha1, &flags);
+	}
 
 	len = read(fd, buffer, sizeof(buffer)-1);
 	close(fd);
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 14/38] read_raw_ref(): take a (struct ref_store *) argument

And make the function work for submodules.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               |  4 +++-
 refs/files-backend.c | 18 +++++++++++++-----
 refs/refs-internal.h |  9 ++++++---
 3 files changed, 22 insertions(+), 9 deletions(-)
Show changes to 3 files +22 −9

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 904e758..9d36dfe 100644
--- a/refs.c
+++ b/refs.c
@@ -1222,6 +1222,7 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 	static struct strbuf sb_refname = STRBUF_INIT;
 	int unused_flags;
 	int symref_count;
+	struct ref_store *refs = get_ref_store(NULL);
 
 	if (!flags)
 		flags = &unused_flags;
@@ -1249,7 +1250,8 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 	for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
 		unsigned int read_flags = 0;
 
-		if (read_raw_ref(refname, sha1, &sb_refname, &read_flags)) {
+		if (read_raw_ref(refs, refname,
+				 sha1, &sb_refname, &read_flags)) {
 			*flags |= read_flags;
 			if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
 				return NULL;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 5f14227..ff7ee7d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1353,11 +1353,12 @@ static int resolve_packed_ref(struct files_ref_store *refs,
 	return -1;
 }
 
-int read_raw_ref(const char *refname, unsigned char *sha1,
+int read_raw_ref(struct ref_store *ref_store,
+		 const char *refname, unsigned char *sha1,
 		 struct strbuf *referent, unsigned int *type)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "read_raw_ref");
+		files_downcast(ref_store, 1, "read_raw_ref");
 	struct strbuf sb_contents = STRBUF_INIT;
 	struct strbuf sb_path = STRBUF_INIT;
 	const char *path;
@@ -1369,7 +1370,12 @@ int read_raw_ref(const char *refname, unsigned char *sha1,
 
 	*type = 0;
 	strbuf_reset(&sb_path);
-	strbuf_git_path(&sb_path, "%s", refname);
+
+	if (*refs->base.submodule)
+		strbuf_git_path_submodule(&sb_path, refs->base.submodule, "%s", refname);
+	else
+		strbuf_git_path(&sb_path, "%s", refname);
+
 	path = sb_path.buf;
 
 stat_ref:
@@ -1596,8 +1602,9 @@ static int lock_raw_ref(const char *refname, int mustexist,
 			unsigned int *type,
 			struct strbuf *err)
 {
+	struct ref_store *ref_store = get_ref_store(NULL);
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "lock_raw_ref");
+		files_downcast(ref_store, 0, "lock_raw_ref");
 	struct ref_lock *lock;
 	struct strbuf ref_file = STRBUF_INIT;
 	int attempts_remaining = 3;
@@ -1687,7 +1694,8 @@ retry:
 	 * fear that its value will change.
 	 */
 
-	if (read_raw_ref(refname, lock->old_oid.hash, referent, type)) {
+	if (read_raw_ref(ref_store, refname,
+			 lock->old_oid.hash, referent, type)) {
 		if (errno == ENOENT) {
 			if (mustexist) {
 				/* Garden variety missing reference. */
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index e24366b..8c45ee3 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -479,9 +479,11 @@ extern struct ref_iterator *current_ref_iter;
 int do_for_each_ref_iterator(struct ref_iterator *iter,
 			     each_ref_fn fn, void *cb_data);
 
+struct ref_store;
+
 /*
- * Read the specified reference from the filesystem or packed refs
- * file, non-recursively. Set type to describe the reference, and:
+ * Read a reference from the specified reference store, non-recursively.
+ * Set type to describe the reference, and:
  *
  * - If refname is the name of a normal reference, fill in sha1
  *   (leaving referent unchanged).
@@ -517,7 +519,8 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
  * - in all other cases, referent will be untouched, and therefore
  *   refname will still be valid and unchanged.
  */
-int read_raw_ref(const char *refname, unsigned char *sha1,
+int read_raw_ref(struct ref_store *ref_store,
+		 const char *refname, unsigned char *sha1,
 		 struct strbuf *referent, unsigned int *type);
 
 /* refs backends */
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 15/38] resolve_ref_recursively(): new function

Add a new function, resolve_ref_recursively(), which is basically like the old resolve_ref_unsafe() except that it takes a (ref_store *) argument and also works for submodules.

Re-implement resolve_ref_unsafe() as a thin wrapper around resolve_ref_recursively().

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)
Show changes to refs.c +11 −3
diff --git a/refs.c b/refs.c
index 9d36dfe..93f2e4d 100644
--- a/refs.c
+++ b/refs.c
@@ -1216,13 +1216,14 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
 }
 
 /* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-			       unsigned char *sha1, int *flags)
+static const char *resolve_ref_recursively(struct ref_store *refs,
+					   const char *refname,
+					   int resolve_flags,
+					   unsigned char *sha1, int *flags)
 {
 	static struct strbuf sb_refname = STRBUF_INIT;
 	int unused_flags;
 	int symref_count;
-	struct ref_store *refs = get_ref_store(NULL);
 
 	if (!flags)
 		flags = &unused_flags;
@@ -1291,6 +1292,13 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 	return NULL;
 }
 
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+			       unsigned char *sha1, int *flags)
+{
+	return resolve_ref_recursively(get_ref_store(NULL), refname,
+				       resolve_flags, sha1, flags);
+}
+
 static struct ref_store *main_ref_store = NULL;
 
 static struct ref_store *submodule_ref_stores = NULL;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 16/38] resolve_gitlink_ref(): implement using resolve_ref_recursively()

resolve_ref_recursively() can handle references in arbitrary files reference stores, so use it to resolve "gitlink" (i.e., submodule) references. Aside from removing redundant code, this allows submodule lookups to benefit from the much more robust code that we use for reading non-submodule references. And, since the code is now agnostic about reference backends, it will work for any future references backend (so move its definition to refs.c).

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 24 +++++++++++++++++++
 refs/files-backend.c | 67 ----------------------------------------------------
 2 files changed, 24 insertions(+), 67 deletions(-)
Show changes to 2 files +24 −67

refs.c, refs/files-backend.c

diff --git a/refs.c b/refs.c
index 93f2e4d..5e0777a 100644
--- a/refs.c
+++ b/refs.c
@@ -1299,6 +1299,30 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 				       resolve_flags, sha1, flags);
 }
 
+int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
+{
+	int len = strlen(path);
+	struct strbuf submodule = STRBUF_INIT;
+	struct ref_store *refs;
+	int flags;
+
+	while (len && path[len-1] == '/')
+		len--;
+	if (!len)
+		return -1;
+
+	strbuf_add(&submodule, path, len);
+	refs = get_ref_store(submodule.buf);
+	strbuf_release(&submodule);
+	if (!refs)
+		return -1;
+
+	if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+	    is_null_sha1(sha1))
+		return -1;
+	return 0;
+}
+
 static struct ref_store *main_ref_store = NULL;
 
 static struct ref_store *submodule_ref_stores = NULL;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index ff7ee7d..d6d83d2 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1498,73 +1498,6 @@ static void unlock_ref(struct ref_lock *lock)
 	free(lock);
 }
 
-#define MAXREFLEN (1024)
-
-static int resolve_gitlink_ref_recursive(struct files_ref_store *refs,
-					 const char *refname, unsigned char *sha1,
-					 int recursion)
-{
-	int fd, len;
-	char buffer[128], *p;
-	char *path;
-
-	if (recursion > SYMREF_MAXDEPTH || strlen(refname) > MAXREFLEN)
-		return -1;
-	path = *refs->base.submodule
-		? git_pathdup_submodule(refs->base.submodule, "%s", refname)
-		: git_pathdup("%s", refname);
-	fd = open(path, O_RDONLY);
-	free(path);
-	if (fd < 0) {
-		unsigned int flags;
-
-		return resolve_packed_ref(refs, refname, sha1, &flags);
-	}
-
-	len = read(fd, buffer, sizeof(buffer)-1);
-	close(fd);
-	if (len < 0)
-		return -1;
-	while (len && isspace(buffer[len-1]))
-		len--;
-	buffer[len] = 0;
-
-	/* Was it a detached head or an old-fashioned symlink? */
-	if (!get_sha1_hex(buffer, sha1))
-		return 0;
-
-	/* Symref? */
-	if (strncmp(buffer, "ref:", 4))
-		return -1;
-	p = buffer + 4;
-	while (isspace(*p))
-		p++;
-
-	return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
-}
-
-int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
-{
-	int len = strlen(path);
-	struct strbuf submodule = STRBUF_INIT;
-	struct files_ref_store *refs;
-
-	while (len && path[len-1] == '/')
-		len--;
-	if (!len)
-		return -1;
-
-	strbuf_add(&submodule, path, len);
-	refs = get_files_ref_store(submodule.buf, "resolve_gitlink_ref");
-	if (!refs) {
-		strbuf_release(&submodule);
-		return -1;
-	}
-	strbuf_release(&submodule);
-
-	return resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
-}
-
 /*
  * Lock refname, without following symrefs, and set *lock_p to point
  * at a newly-allocated lock object. Fill in lock->old_oid, referent,
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 17:19 UTC · re: Michael Haggerty · lore

Re: [PATCH 16/38] resolve_gitlink_ref(): implement using resolve_ref_recursively()

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 7 quoted lines
> resolve_ref_recursively() can handle references in arbitrary files
> reference stores, so use it to resolve "gitlink" (i.e., submodule)
> references. Aside from removing redundant code, this allows submodule
> lookups to benefit from the much more robust code that we use for
> reading non-submodule references. And, since the code is now agnostic
> about reference backends, it will work for any future references
> backend (so move its definition to refs.c).
Nice.
Eric Sunshine· Jun 14, 2016, 05:03 UTC · re: Michael Haggerty · lore

Re: [PATCH 16/38] resolve_gitlink_ref(): implement using resolve_ref_recursively()

On Fri, Jun 3, 2016 at 5:03 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
Show 25 quoted lines
> resolve_ref_recursively() can handle references in arbitrary files
> reference stores, so use it to resolve "gitlink" (i.e., submodule)
> references. Aside from removing redundant code, this allows submodule
> lookups to benefit from the much more robust code that we use for
> reading non-submodule references. And, since the code is now agnostic
> about reference backends, it will work for any future references
> backend (so move its definition to refs.c).
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
> diff --git a/refs.c b/refs.c
> @@ -1299,6 +1299,30 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
> +int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
> +{
> +       int len = strlen(path);
> +       struct strbuf submodule = STRBUF_INIT;
> +       struct ref_store *refs;
> +       int flags;
> +
> +       while (len && path[len-1] == '/')
> +               len--;
> +       if (!len)
> +               return -1;
> +
> +       strbuf_add(&submodule, path, len);

It took me a moment to figure out that you're using the strbuf only for its side-effect of giving you a NUL-terminated string needed by get_ref_store(), and not because you need any fancy functionality of strbuf. I wonder if xstrndup() would have made this clearer.

Not worth a re-roll.
Show 10 quoted lines
> +       refs = get_ref_store(submodule.buf);
> +       strbuf_release(&submodule);
> +       if (!refs)
> +               return -1;
> +
> +       if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
> +           is_null_sha1(sha1))
> +               return -1;
> +       return 0;
> +}
Michael Haggerty· Jun 16, 2016, 04:03 UTC · re: Eric Sunshine · lore

Re: [PATCH 16/38] resolve_gitlink_ref(): implement using resolve_ref_recursively()

On 06/14/2016 07:03 AM, Eric Sunshine wrote:
Show 33 quoted lines
> On Fri, Jun 3, 2016 at 5:03 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
>> resolve_ref_recursively() can handle references in arbitrary files
>> reference stores, so use it to resolve "gitlink" (i.e., submodule)
>> references. Aside from removing redundant code, this allows submodule
>> lookups to benefit from the much more robust code that we use for
>> reading non-submodule references. And, since the code is now agnostic
>> about reference backends, it will work for any future references
>> backend (so move its definition to refs.c).
>>
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
>> diff --git a/refs.c b/refs.c
>> @@ -1299,6 +1299,30 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
>> +int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
>> +{
>> +       int len = strlen(path);
>> +       struct strbuf submodule = STRBUF_INIT;
>> +       struct ref_store *refs;
>> +       int flags;
>> +
>> +       while (len && path[len-1] == '/')
>> +               len--;
>> +       if (!len)
>> +               return -1;
>> +
>> +       strbuf_add(&submodule, path, len);
> 
> It took me a moment to figure out that you're using the strbuf only
> for its side-effect of giving you a NUL-terminated string needed by
> get_ref_store(), and not because you need any fancy functionality of
> strbuf. I wonder if xstrndup() would have made this clearer.
> 
> Not worth a re-roll.

I agree both that xstrndup() (or, in this case, xmemdupz()) would have been clearer, and also that it's not worth a re-roll. I'll keep it in mind if I touch this code again.

Thanks for your comment.
Michael
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 17/38] resolve_gitlink_ref(): avoid memory allocation in many cases

If we don't have to strip trailing '/' from the submodule path, then don't allocate and copy the submodule name.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
Show changes to refs.c +13 −7
diff --git a/refs.c b/refs.c
index 5e0777a..7a8ef6d 100644
--- a/refs.c
+++ b/refs.c
@@ -1301,19 +1301,25 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 
 int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
 {
-	int len = strlen(path);
-	struct strbuf submodule = STRBUF_INIT;
+	size_t len, orig_len = strlen(path);
 	struct ref_store *refs;
 	int flags;
 
-	while (len && path[len-1] == '/')
-		len--;
+	for (len = orig_len; len && path[len - 1] == '/'; len--)
+		;
+
 	if (!len)
 		return -1;
 
-	strbuf_add(&submodule, path, len);
-	refs = get_ref_store(submodule.buf);
-	strbuf_release(&submodule);
+	if (len == orig_len) {
+		refs = get_ref_store(path);
+	} else {
+		char *stripped = xmemdupz(path, len);
+
+		refs = get_ref_store(stripped);
+		free(stripped);
+	}
+
 	if (!refs)
 		return -1;
 
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 17:29 UTC · re: Michael Haggerty · lore

Re: [PATCH 17/38] resolve_gitlink_ref(): avoid memory allocation in many cases

Michael Haggerty <mhagger@alum.mit.edu> writes:
> If we don't have to strip trailing '/' from the submodule path, then
> don't allocate and copy the submodule name.
Makes sense.
Show 20 quoted lines
>  int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
>  {
> -	int len = strlen(path);
> -	struct strbuf submodule = STRBUF_INIT;
> +	size_t len, orig_len = strlen(path);
>  	struct ref_store *refs;
>  	int flags;
>  
> -	while (len && path[len-1] == '/')
> -		len--;
> +	for (len = orig_len; len && path[len - 1] == '/'; len--)
> +		;
> +
>  	if (!len)
>  		return -1;
>  
> -	strbuf_add(&submodule, path, len);
> -	refs = get_ref_store(submodule.buf);
> -	strbuf_release(&submodule);
> +	if (len == orig_len) {

You can keep the original while (), without introducing orig_len, and check if path[len] is NUL, which would probably be an end result that is easier to read.

Show 6 quoted lines
> +		refs = get_ref_store(path);
> +	} else {
> +		char *stripped = xmemdupz(path, len);
> +
> +		refs = get_ref_store(stripped);
> +		free(stripped);

An alternative might be to add get_ref_store_counted() that takes (path, len) instead of NUL-terminated path, which does not look too bad looking at the state after applying all 38 patches.

> +	}
> +
>  	if (!refs)
>  		return -1;
Michael Haggerty· Jun 9, 2016, 14:37 UTC · re: Junio C Hamano · lore

Re: [PATCH 17/38] resolve_gitlink_ref(): avoid memory allocation in many cases

On 06/07/2016 07:29 PM, Junio C Hamano wrote:
Show 31 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> If we don't have to strip trailing '/' from the submodule path, then
>> don't allocate and copy the submodule name.
> 
> Makes sense.
> 
>>  int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
>>  {
>> -	int len = strlen(path);
>> -	struct strbuf submodule = STRBUF_INIT;
>> +	size_t len, orig_len = strlen(path);
>>  	struct ref_store *refs;
>>  	int flags;
>>  
>> -	while (len && path[len-1] == '/')
>> -		len--;
>> +	for (len = orig_len; len && path[len - 1] == '/'; len--)
>> +		;
>> +
>>  	if (!len)
>>  		return -1;
>>  
>> -	strbuf_add(&submodule, path, len);
>> -	refs = get_ref_store(submodule.buf);
>> -	strbuf_release(&submodule);
>> +	if (len == orig_len) {
> 
> You can keep the original while (), without introducing orig_len,
> and check if path[len] is NUL, which would probably be an end result
> that is easier to read.
OK, I'll change it.
Show 10 quoted lines
>> +		refs = get_ref_store(path);
>> +	} else {
>> +		char *stripped = xmemdupz(path, len);
>> +
>> +		refs = get_ref_store(stripped);
>> +		free(stripped);
> 
> An alternative might be to add get_ref_store_counted() that takes
> (path, len) instead of NUL-terminated path, which does not look too
> bad looking at the state after applying all 38 patches.

This slash-stripping code was introduced in 2007 (0ebde32c87) and it's not my priority to improve it as part of this patch series.

Michael
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 18/38] resolve_gitlink_ref(): rename path parameter to submodule

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 11 ++++++-----
 refs.h |  9 +++++----
 2 files changed, 11 insertions(+), 9 deletions(-)
Show changes to 2 files +11 −9

refs.c, refs.h

diff --git a/refs.c b/refs.c
index 7a8ef6d..c39f85a 100644
--- a/refs.c
+++ b/refs.c
@@ -1299,22 +1299,23 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 				       resolve_flags, sha1, flags);
 }
 
-int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
+int resolve_gitlink_ref(const char *submodule, const char *refname,
+			unsigned char *sha1)
 {
-	size_t len, orig_len = strlen(path);
+	size_t len, orig_len = strlen(submodule);
 	struct ref_store *refs;
 	int flags;
 
-	for (len = orig_len; len && path[len - 1] == '/'; len--)
+	for (len = orig_len; len && submodule[len - 1] == '/'; len--)
 		;
 
 	if (!len)
 		return -1;
 
 	if (len == orig_len) {
-		refs = get_ref_store(path);
+		refs = get_ref_store(submodule);
 	} else {
-		char *stripped = xmemdupz(path, len);
+		char *stripped = xmemdupz(submodule, len);
 
 		refs = get_ref_store(stripped);
 		free(stripped);
diff --git a/refs.h b/refs.h
index 52ea93b..132dcef 100644
--- a/refs.h
+++ b/refs.h
@@ -77,11 +77,12 @@ int is_branch(const char *refname);
 int peel_ref(const char *refname, unsigned char *sha1);
 
 /**
- * Resolve refname in the nested "gitlink" repository that is located
- * at path.  If the resolution is successful, return 0 and set sha1 to
- * the name of the object; otherwise, return a non-zero value.
+ * Resolve refname in the nested "gitlink" repository in the specified
+ * submodule (which must be non-NULL). If the resolution is
+ * successful, return 0 and set sha1 to the name of the object;
+ * otherwise, return a non-zero value.
  */
-int resolve_gitlink_ref(const char *path, const char *refname,
+int resolve_gitlink_ref(const char *submodule, const char *refname,
 			unsigned char *sha1);
 
 /*
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 19/38] refs: make read_raw_ref() virtual

Reference backends will be able to customize this function to implement reference reading.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               |  4 ++--
 refs/files-backend.c | 14 ++++++++------
 refs/refs-internal.h | 36 +++++++++++++++++++-----------------
 3 files changed, 29 insertions(+), 25 deletions(-)
Show changes to 3 files +29 −25

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index c39f85a..1798f66 100644
--- a/refs.c
+++ b/refs.c
@@ -1251,8 +1251,8 @@ static const char *resolve_ref_recursively(struct ref_store *refs,
 	for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
 		unsigned int read_flags = 0;
 
-		if (read_raw_ref(refs, refname,
-				 sha1, &sb_refname, &read_flags)) {
+		if (refs->be->read_raw_ref(refs, refname,
+					   sha1, &sb_refname, &read_flags)) {
 			*flags |= read_flags;
 			if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
 				return NULL;
diff --git a/refs/files-backend.c b/refs/files-backend.c
index d6d83d2..5b6d388 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1353,9 +1353,9 @@ static int resolve_packed_ref(struct files_ref_store *refs,
 	return -1;
 }
 
-int read_raw_ref(struct ref_store *ref_store,
-		 const char *refname, unsigned char *sha1,
-		 struct strbuf *referent, unsigned int *type)
+static int files_read_raw_ref(struct ref_store *ref_store,
+			      const char *refname, unsigned char *sha1,
+			      struct strbuf *referent, unsigned int *type)
 {
 	struct files_ref_store *refs =
 		files_downcast(ref_store, 1, "read_raw_ref");
@@ -1627,8 +1627,8 @@ retry:
 	 * fear that its value will change.
 	 */
 
-	if (read_raw_ref(ref_store, refname,
-			 lock->old_oid.hash, referent, type)) {
+	if (files_read_raw_ref(ref_store, refname,
+			       lock->old_oid.hash, referent, type)) {
 		if (errno == ENOENT) {
 			if (mustexist) {
 				/* Garden variety missing reference. */
@@ -4023,5 +4023,7 @@ struct ref_storage_be refs_be_files = {
 	NULL,
 	"files",
 	files_ref_store_create,
-	files_transaction_commit
+	files_transaction_commit,
+
+	files_read_raw_ref
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 8c45ee3..8d4287c 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -481,6 +481,20 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
 
 struct ref_store;
 
+/* refs backends */
+
+/*
+ * Initialize the ref_store for the specified submodule, or for the
+ * main repository if submodule == NULL. These functions should call
+ * base_ref_store_init() to initialize the shared part of the
+ * ref_store and to record the ref_store for later lookup.
+ */
+typedef struct ref_store *ref_store_init_fn(const char *submodule);
+
+typedef int ref_transaction_commit_fn(struct ref_store *refs,
+				      struct ref_transaction *transaction,
+				      struct strbuf *err);
+
 /*
  * Read a reference from the specified reference store, non-recursively.
  * Set type to describe the reference, and:
@@ -519,29 +533,17 @@ struct ref_store;
  * - in all other cases, referent will be untouched, and therefore
  *   refname will still be valid and unchanged.
  */
-int read_raw_ref(struct ref_store *ref_store,
-		 const char *refname, unsigned char *sha1,
-		 struct strbuf *referent, unsigned int *type);
-
-/* refs backends */
-
-/*
- * Initialize the ref_store for the specified submodule, or for the
- * main repository if submodule == NULL. These functions should call
- * base_ref_store_init() to initialize the shared part of the
- * ref_store and to record the ref_store for later lookup.
- */
-typedef struct ref_store *ref_store_init_fn(const char *submodule);
-
-typedef int ref_transaction_commit_fn(struct ref_store *refs,
-				      struct ref_transaction *transaction,
-				      struct strbuf *err);
+typedef int read_raw_ref_fn(struct ref_store *ref_store,
+			    const char *refname, unsigned char *sha1,
+			    struct strbuf *referent, unsigned int *type);
 
 struct ref_storage_be {
 	struct ref_storage_be *next;
 	const char *name;
 	ref_store_init_fn *init;
 	ref_transaction_commit_fn *transaction_commit;
+
+	read_raw_ref_fn *read_raw_ref;
 };
 
 extern struct ref_storage_be refs_be_files;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 20/38] refs: make verify_refname_available() virtual

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 10 ++++++++++
 refs/files-backend.c | 14 ++++++++------
 refs/refs-internal.h |  7 +++++++
 3 files changed, 25 insertions(+), 6 deletions(-)
Show changes to 3 files +25 −6

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 1798f66..79ef443 100644
--- a/refs.c
+++ b/refs.c
@@ -1425,3 +1425,13 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 
 	return refs->be->transaction_commit(refs, transaction, err);
 }
+
+int verify_refname_available(const char *refname,
+			     const struct string_list *extra,
+			     const struct string_list *skip,
+			     struct strbuf *err)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 5b6d388..af8de85 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2553,13 +2553,14 @@ out:
 	return ret;
 }
 
-int verify_refname_available(const char *newname,
-			     const struct string_list *extras,
-			     const struct string_list *skip,
-			     struct strbuf *err)
+static int files_verify_refname_available(struct ref_store *ref_store,
+					  const char *newname,
+					  const struct string_list *extras,
+					  const struct string_list *skip,
+					  struct strbuf *err)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "verify_refname_available");
+		files_downcast(ref_store, 1, "verify_refname_available");
 	struct ref_dir *packed_refs = get_packed_refs(refs);
 	struct ref_dir *loose_refs = get_loose_refs(refs);
 
@@ -4025,5 +4026,6 @@ struct ref_storage_be refs_be_files = {
 	files_ref_store_create,
 	files_transaction_commit,
 
-	files_read_raw_ref
+	files_read_raw_ref,
+	files_verify_refname_available
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 8d4287c..18e01d0 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -537,6 +537,12 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store,
 			    const char *refname, unsigned char *sha1,
 			    struct strbuf *referent, unsigned int *type);
 
+typedef int verify_refname_available_fn(struct ref_store *ref_store,
+					const char *newname,
+					const struct string_list *extras,
+					const struct string_list *skip,
+					struct strbuf *err);
+
 struct ref_storage_be {
 	struct ref_storage_be *next;
 	const char *name;
@@ -544,6 +550,7 @@ struct ref_storage_be {
 	ref_transaction_commit_fn *transaction_commit;
 
 	read_raw_ref_fn *read_raw_ref;
+	verify_refname_available_fn *verify_refname_available;
 };
 
 extern struct ref_storage_be refs_be_files;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 21/38] refs: make pack_refs() virtual

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 7 +++++++
 refs/files-backend.c | 6 ++++--
 refs/refs-internal.h | 4 ++++
 3 files changed, 15 insertions(+), 2 deletions(-)
Show changes to 3 files +15 −2

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 79ef443..f4f5f32 100644
--- a/refs.c
+++ b/refs.c
@@ -1418,6 +1418,13 @@ void assert_main_repository(struct ref_store *refs, const char *caller)
 }
 
 /* backend functions */
+int pack_refs(unsigned int flags)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->pack_refs(refs, flags);
+}
+
 int ref_transaction_commit(struct ref_transaction *transaction,
 			   struct strbuf *err)
 {
diff --git a/refs/files-backend.c b/refs/files-backend.c
index af8de85..e5a8799 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2358,10 +2358,10 @@ static void prune_refs(struct ref_to_prune *r)
 	}
 }
 
-int pack_refs(unsigned int flags)
+static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "pack_refs");
+		files_downcast(ref_store, 0, "pack_refs");
 	struct pack_refs_cb_data cbdata;
 
 	memset(&cbdata, 0, sizeof(cbdata));
@@ -4026,6 +4026,8 @@ struct ref_storage_be refs_be_files = {
 	files_ref_store_create,
 	files_transaction_commit,
 
+	files_pack_refs,
+
 	files_read_raw_ref,
 	files_verify_refname_available
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 18e01d0..578c125 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -495,6 +495,8 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
 				      struct ref_transaction *transaction,
 				      struct strbuf *err);
 
+typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
+
 /*
  * Read a reference from the specified reference store, non-recursively.
  * Set type to describe the reference, and:
@@ -549,6 +551,8 @@ struct ref_storage_be {
 	ref_store_init_fn *init;
 	ref_transaction_commit_fn *transaction_commit;
 
+	pack_refs_fn *pack_refs;
+
 	read_raw_ref_fn *read_raw_ref;
 	verify_refname_available_fn *verify_refname_available;
 };
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 17:35 UTC · re: Michael Haggerty · lore

Re: [PATCH 21/38] refs: make pack_refs() virtual

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 22 quoted lines
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  refs.c               | 7 +++++++
>  refs/files-backend.c | 6 ++++--
>  refs/refs-internal.h | 4 ++++
>  3 files changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/refs.c b/refs.c
> index 79ef443..f4f5f32 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1418,6 +1418,13 @@ void assert_main_repository(struct ref_store *refs, const char *caller)
>  }
>  
>  /* backend functions */
> +int pack_refs(unsigned int flags)
> +{
> +	struct ref_store *refs = get_ref_store(NULL);
> +
> +	return refs->be->pack_refs(refs, flags);
> +}
> +

Makes me wonder what it even means to "pack_refs" in the context of other possible backends (e.g. lmdb), but higher level API users (e.g. "gc") needs something to call to give the backend "here is a chance for you to optimize yourself" cue, so perhaps it is OK.

Show 51 quoted lines
>  int ref_transaction_commit(struct ref_transaction *transaction,
>  			   struct strbuf *err)
>  {
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index af8de85..e5a8799 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -2358,10 +2358,10 @@ static void prune_refs(struct ref_to_prune *r)
>  	}
>  }
>  
> -int pack_refs(unsigned int flags)
> +static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
>  {
>  	struct files_ref_store *refs =
> -		get_files_ref_store(NULL, "pack_refs");
> +		files_downcast(ref_store, 0, "pack_refs");
>  	struct pack_refs_cb_data cbdata;
>  
>  	memset(&cbdata, 0, sizeof(cbdata));
> @@ -4026,6 +4026,8 @@ struct ref_storage_be refs_be_files = {
>  	files_ref_store_create,
>  	files_transaction_commit,
>  
> +	files_pack_refs,
> +
>  	files_read_raw_ref,
>  	files_verify_refname_available
>  };
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> index 18e01d0..578c125 100644
> --- a/refs/refs-internal.h
> +++ b/refs/refs-internal.h
> @@ -495,6 +495,8 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
>  				      struct ref_transaction *transaction,
>  				      struct strbuf *err);
>  
> +typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
> +
>  /*
>   * Read a reference from the specified reference store, non-recursively.
>   * Set type to describe the reference, and:
> @@ -549,6 +551,8 @@ struct ref_storage_be {
>  	ref_store_init_fn *init;
>  	ref_transaction_commit_fn *transaction_commit;
>  
> +	pack_refs_fn *pack_refs;
> +
>  	read_raw_ref_fn *read_raw_ref;
>  	verify_refname_available_fn *verify_refname_available;
>  };
Michael Haggerty· Jun 9, 2016, 14:46 UTC · re: Junio C Hamano · lore

Re: [PATCH 21/38] refs: make pack_refs() virtual

On 06/07/2016 07:35 PM, Junio C Hamano wrote:
Show 29 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
>>  refs.c               | 7 +++++++
>>  refs/files-backend.c | 6 ++++--
>>  refs/refs-internal.h | 4 ++++
>>  3 files changed, 15 insertions(+), 2 deletions(-)
>>
>> diff --git a/refs.c b/refs.c
>> index 79ef443..f4f5f32 100644
>> --- a/refs.c
>> +++ b/refs.c
>> @@ -1418,6 +1418,13 @@ void assert_main_repository(struct ref_store *refs, const char *caller)
>>  }
>>  
>>  /* backend functions */
>> +int pack_refs(unsigned int flags)
>> +{
>> +	struct ref_store *refs = get_ref_store(NULL);
>> +
>> +	return refs->be->pack_refs(refs, flags);
>> +}
>> +
> 
> Makes me wonder what it even means to "pack_refs" in the context of
> other possible backends (e.g. lmdb), but higher level API users
> (e.g. "gc") needs something to call to give the backend "here is a
> chance for you to optimize yourself" cue, so perhaps it is OK.

My thinking exactly. This might end up being renamed to optimize_refs() and be made an optional part of the ref_store interface. But it seemed premature to worry about that.

Michael
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 22/38] refs: make create_symref() virtual

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 9 +++++++++
 refs/files-backend.c | 7 ++++++-
 refs/refs-internal.h | 5 +++++
 3 files changed, 20 insertions(+), 1 deletion(-)
Show changes to 3 files +20 −1

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index f4f5f32..22837f4 100644
--- a/refs.c
+++ b/refs.c
@@ -1425,6 +1425,15 @@ int pack_refs(unsigned int flags)
 	return refs->be->pack_refs(refs, flags);
 }
 
+int create_symref(const char *ref_target, const char *refs_heads_master,
+		  const char *logmsg)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->create_symref(refs, ref_target, refs_heads_master,
+				       logmsg);
+}
+
 int ref_transaction_commit(struct ref_transaction *transaction,
 			   struct strbuf *err)
 {
diff --git a/refs/files-backend.c b/refs/files-backend.c
index e5a8799..f82a1be 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3015,12 +3015,16 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,
 	return 0;
 }
 
-int create_symref(const char *refname, const char *target, const char *logmsg)
+static int files_create_symref(struct ref_store *ref_store,
+			       const char *refname, const char *target,
+			       const char *logmsg)
 {
 	struct strbuf err = STRBUF_INIT;
 	struct ref_lock *lock;
 	int ret;
 
+	files_downcast(ref_store, 0, "create_symref");
+
 	lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL,
 				   &err);
 	if (!lock) {
@@ -4027,6 +4031,7 @@ struct ref_storage_be refs_be_files = {
 	files_transaction_commit,
 
 	files_pack_refs,
+	files_create_symref,
 
 	files_read_raw_ref,
 	files_verify_refname_available
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 578c125..c342e57 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -496,6 +496,10 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
 				      struct strbuf *err);
 
 typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
+typedef int create_symref_fn(struct ref_store *ref_store,
+			     const char *ref_target,
+			     const char *refs_heads_master,
+			     const char *logmsg);
 
 /*
  * Read a reference from the specified reference store, non-recursively.
@@ -552,6 +556,7 @@ struct ref_storage_be {
 	ref_transaction_commit_fn *transaction_commit;
 
 	pack_refs_fn *pack_refs;
+	create_symref_fn *create_symref;
 
 	read_raw_ref_fn *read_raw_ref;
 	verify_refname_available_fn *verify_refname_available;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 23/38] refs: make peel_ref() virtual

For now it only supports the main reference store.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 7 +++++++
 refs/files-backend.c | 6 ++++--
 refs/refs-internal.h | 3 +++
 3 files changed, 14 insertions(+), 2 deletions(-)
Show changes to 3 files +14 −2

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 22837f4..2d84c5c 100644
--- a/refs.c
+++ b/refs.c
@@ -1425,6 +1425,13 @@ int pack_refs(unsigned int flags)
 	return refs->be->pack_refs(refs, flags);
 }
 
+int peel_ref(const char *refname, unsigned char *sha1)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->peel_ref(refs, refname, sha1);
+}
+
 int create_symref(const char *ref_target, const char *refs_heads_master,
 		  const char *logmsg)
 {
diff --git a/refs/files-backend.c b/refs/files-backend.c
index f82a1be..91dcfcb 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1763,9 +1763,10 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
 	return status;
 }
 
-int peel_ref(const char *refname, unsigned char *sha1)
+static int files_peel_ref(struct ref_store *ref_store,
+			  const char *refname, unsigned char *sha1)
 {
-	struct files_ref_store *refs = get_files_ref_store(NULL, "peel_ref");
+	struct files_ref_store *refs = files_downcast(ref_store, 0, "peel_ref");
 	int flag;
 	unsigned char base[20];
 
@@ -4031,6 +4032,7 @@ struct ref_storage_be refs_be_files = {
 	files_transaction_commit,
 
 	files_pack_refs,
+	files_peel_ref,
 	files_create_symref,
 
 	files_read_raw_ref,
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index c342e57..ae67b49 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -496,6 +496,8 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
 				      struct strbuf *err);
 
 typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
+typedef int peel_ref_fn(struct ref_store *ref_store,
+			const char *refname, unsigned char *sha1);
 typedef int create_symref_fn(struct ref_store *ref_store,
 			     const char *ref_target,
 			     const char *refs_heads_master,
@@ -556,6 +558,7 @@ struct ref_storage_be {
 	ref_transaction_commit_fn *transaction_commit;
 
 	pack_refs_fn *pack_refs;
+	peel_ref_fn *peel_ref;
 	create_symref_fn *create_symref;
 
 	read_raw_ref_fn *read_raw_ref;
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 17:36 UTC · re: Michael Haggerty · lore

Re: [PATCH 23/38] refs: make peel_ref() virtual

Michael Haggerty <mhagger@alum.mit.edu> writes:
> For now it only supports the main reference store.

Isn't this comment applicable to a handful of recent changes that made other things virtual, too? Just wondering if I am missing something very special with the peel_ref() thing to single it out.

Show 72 quoted lines
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
>  refs.c               | 7 +++++++
>  refs/files-backend.c | 6 ++++--
>  refs/refs-internal.h | 3 +++
>  3 files changed, 14 insertions(+), 2 deletions(-)
>
> diff --git a/refs.c b/refs.c
> index 22837f4..2d84c5c 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1425,6 +1425,13 @@ int pack_refs(unsigned int flags)
>  	return refs->be->pack_refs(refs, flags);
>  }
>  
> +int peel_ref(const char *refname, unsigned char *sha1)
> +{
> +	struct ref_store *refs = get_ref_store(NULL);
> +
> +	return refs->be->peel_ref(refs, refname, sha1);
> +}
> +
>  int create_symref(const char *ref_target, const char *refs_heads_master,
>  		  const char *logmsg)
>  {
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index f82a1be..91dcfcb 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -1763,9 +1763,10 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
>  	return status;
>  }
>  
> -int peel_ref(const char *refname, unsigned char *sha1)
> +static int files_peel_ref(struct ref_store *ref_store,
> +			  const char *refname, unsigned char *sha1)
>  {
> -	struct files_ref_store *refs = get_files_ref_store(NULL, "peel_ref");
> +	struct files_ref_store *refs = files_downcast(ref_store, 0, "peel_ref");
>  	int flag;
>  	unsigned char base[20];
>  
> @@ -4031,6 +4032,7 @@ struct ref_storage_be refs_be_files = {
>  	files_transaction_commit,
>  
>  	files_pack_refs,
> +	files_peel_ref,
>  	files_create_symref,
>  
>  	files_read_raw_ref,
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> index c342e57..ae67b49 100644
> --- a/refs/refs-internal.h
> +++ b/refs/refs-internal.h
> @@ -496,6 +496,8 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
>  				      struct strbuf *err);
>  
>  typedef int pack_refs_fn(struct ref_store *ref_store, unsigned int flags);
> +typedef int peel_ref_fn(struct ref_store *ref_store,
> +			const char *refname, unsigned char *sha1);
>  typedef int create_symref_fn(struct ref_store *ref_store,
>  			     const char *ref_target,
>  			     const char *refs_heads_master,
> @@ -556,6 +558,7 @@ struct ref_storage_be {
>  	ref_transaction_commit_fn *transaction_commit;
>  
>  	pack_refs_fn *pack_refs;
> +	peel_ref_fn *peel_ref;
>  	create_symref_fn *create_symref;
>  
>  	read_raw_ref_fn *read_raw_ref;
Michael Haggerty· Jun 9, 2016, 15:05 UTC · re: Junio C Hamano · lore

Re: [PATCH 23/38] refs: make peel_ref() virtual

On 06/07/2016 07:36 PM, Junio C Hamano wrote:
Show 7 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> For now it only supports the main reference store.
> 
> Isn't this comment applicable to a handful of recent changes that
> made other things virtual, too?  Just wondering if I am missing
> something very special with the peel_ref() thing to single it out.

It is true that many (most?) virtual functions can currently only be used with the main reference store. That is likely to stay the case for reference-writing functions, because currently there isn't any code that wants to write references in other submodule. (Or maybe there is, but it's currently done by invoking a subprocess? I haven't actually looked.)

But peel_ref() is a read-only function, and it seems more plausible to implement it for other submodules. It just seems like work that can be put off. And there's the kindof ugly current_ref_iter hack that might go away if callers are rewritten to use ref_iterators, in which case implementing this method for other submodules would become even easier.

In other words, the main point of the comment is not "it only supports the main reference store", but rather "for now".

Michael
Michael Haggerty· Jun 3, 2016, 21:03 UTC · re: Michael Haggerty · lore

[PATCH 24/38] repack_without_refs(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
Show changes to refs/files-backend.c +7 −5
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 91dcfcb..48b37fa 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2388,14 +2388,14 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
  *
  * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
  */
-static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
+static int repack_without_refs(struct files_ref_store *refs,
+			       struct string_list *refnames, struct strbuf *err)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "repack_without_refs");
 	struct ref_dir *packed;
 	struct string_list_item *refname;
 	int ret, needs_repacking = 0, removed = 0;
 
+	assert_main_repository(&refs->base, "repack_without_refs");
 	assert(err);
 
 	/* Look for a packed ref */
@@ -2457,13 +2457,15 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
 
 int delete_refs(struct string_list *refnames, unsigned int flags)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "delete_refs");
 	struct strbuf err = STRBUF_INIT;
 	int i, result = 0;
 
 	if (!refnames->nr)
 		return 0;
 
-	result = repack_without_refs(refnames, &err);
+	result = repack_without_refs(refs, refnames, &err);
 	if (result) {
 		/*
 		 * If we failed to rewrite the packed-refs file, then
@@ -3773,7 +3775,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
 		}
 	}
 
-	if (repack_without_refs(&refs_to_delete, err)) {
+	if (repack_without_refs(refs, &refs_to_delete, err)) {
 		ret = TRANSACTION_GENERIC_ERROR;
 		goto cleanup;
 	}
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 25/38] lock_raw_ref(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
Show changes to refs/files-backend.c +8 −6
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 48b37fa..dfce4d1 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1527,7 +1527,8 @@ static void unlock_ref(struct ref_lock *lock)
  *   avoided, namely if we were successfully able to read the ref
  * - Generate informative error messages in the case of failure
  */
-static int lock_raw_ref(const char *refname, int mustexist,
+static int lock_raw_ref(struct files_ref_store *refs,
+			const char *refname, int mustexist,
 			const struct string_list *extras,
 			const struct string_list *skip,
 			struct ref_lock **lock_p,
@@ -1535,15 +1536,14 @@ static int lock_raw_ref(const char *refname, int mustexist,
 			unsigned int *type,
 			struct strbuf *err)
 {
-	struct ref_store *ref_store = get_ref_store(NULL);
-	struct files_ref_store *refs =
-		files_downcast(ref_store, 0, "lock_raw_ref");
 	struct ref_lock *lock;
 	struct strbuf ref_file = STRBUF_INIT;
 	int attempts_remaining = 3;
 	int ret = TRANSACTION_GENERIC_ERROR;
 
 	assert(err);
+	assert_main_repository(&refs->base, "lock_raw_ref");
+
 	*type = 0;
 
 	/* First lock the file so it can't change out from under us. */
@@ -1627,7 +1627,7 @@ retry:
 	 * fear that its value will change.
 	 */
 
-	if (files_read_raw_ref(ref_store, refname,
+	if (files_read_raw_ref(&refs->base, refname,
 			       lock->old_oid.hash, referent, type)) {
 		if (errno == ENOENT) {
 			if (mustexist) {
@@ -3489,6 +3489,8 @@ static int lock_ref_for_update(struct ref_update *update,
 			       struct string_list *affected_refnames,
 			       struct strbuf *err)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "lock_ref_for_update");
 	struct strbuf referent = STRBUF_INIT;
 	int mustexist = (update->flags & REF_HAVE_OLD) &&
 		!is_null_sha1(update->old_sha1);
@@ -3505,7 +3507,7 @@ static int lock_ref_for_update(struct ref_update *update,
 			return ret;
 	}
 
-	ret = lock_raw_ref(update->refname, mustexist,
+	ret = lock_raw_ref(refs, update->refname, mustexist,
 			   affected_refnames, NULL,
 			   &update->lock, &referent,
 			   &update->type, err);
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 26/38] commit_ref_update(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
Show changes to refs/files-backend.c +8 −6
diff --git a/refs/files-backend.c b/refs/files-backend.c
index dfce4d1..bc7d250 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2578,12 +2578,14 @@ static int files_verify_refname_available(struct ref_store *ref_store,
 
 static int write_ref_to_lockfile(struct ref_lock *lock,
 				 const unsigned char *sha1, struct strbuf *err);
-static int commit_ref_update(struct ref_lock *lock,
+static int commit_ref_update(struct files_ref_store *refs,
+			     struct ref_lock *lock,
 			     const unsigned char *sha1, const char *logmsg,
 			     struct strbuf *err);
 
 int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
 {
+	struct files_ref_store *refs = get_files_ref_store(NULL, "rename_ref");
 	unsigned char sha1[20], orig_sha1[20];
 	int flag = 0, logmoved = 0;
 	struct ref_lock *lock;
@@ -2656,7 +2658,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 	hashcpy(lock->old_oid.hash, orig_sha1);
 
 	if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
-	    commit_ref_update(lock, orig_sha1, logmsg, &err)) {
+	    commit_ref_update(refs, lock, orig_sha1, logmsg, &err)) {
 		error("unable to write current sha1 into %s: %s", newrefname, err.buf);
 		strbuf_release(&err);
 		goto rollback;
@@ -2676,7 +2678,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 	flag = log_all_ref_updates;
 	log_all_ref_updates = 0;
 	if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
-	    commit_ref_update(lock, orig_sha1, NULL, &err)) {
+	    commit_ref_update(refs, lock, orig_sha1, NULL, &err)) {
 		error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
 		strbuf_release(&err);
 	}
@@ -2912,12 +2914,12 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
  * to the loose reference lockfile. Also update the reflogs if
  * necessary, using the specified lockmsg (which can be NULL).
  */
-static int commit_ref_update(struct ref_lock *lock,
+static int commit_ref_update(struct files_ref_store *refs,
+			     struct ref_lock *lock,
 			     const unsigned char *sha1, const char *logmsg,
 			     struct strbuf *err)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "commit_ref_update");
+	assert_main_repository(&refs->base, "commit_ref_update");
 
 	clear_loose_ref_cache(refs);
 	if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 27/38] lock_ref_for_update(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)
Show changes to refs/files-backend.c +6 −5
diff --git a/refs/files-backend.c b/refs/files-backend.c
index bc7d250..225e0af 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3485,20 +3485,21 @@ static const char *original_update_refname(struct ref_update *update)
  * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
  *   update of HEAD.
  */
-static int lock_ref_for_update(struct ref_update *update,
+static int lock_ref_for_update(struct files_ref_store *refs,
+			       struct ref_update *update,
 			       struct ref_transaction *transaction,
 			       const char *head_ref,
 			       struct string_list *affected_refnames,
 			       struct strbuf *err)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "lock_ref_for_update");
 	struct strbuf referent = STRBUF_INIT;
 	int mustexist = (update->flags & REF_HAVE_OLD) &&
 		!is_null_sha1(update->old_sha1);
 	int ret;
 	struct ref_lock *lock;
 
+	assert_main_repository(&refs->base, "lock_ref_for_update");
+
 	if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
 		update->flags |= REF_DELETING;
 
@@ -3724,8 +3725,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
 	for (i = 0; i < transaction->nr; i++) {
 		struct ref_update *update = updates[i];
 
-		ret = lock_ref_for_update(update, transaction, head_ref,
-					  &affected_refnames, err);
+		ret = lock_ref_for_update(refs, update, transaction,
+					  head_ref, &affected_refnames, err);
 		if (ret)
 			goto cleanup;
 	}
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 28/38] lock_ref_sha1_basic(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)
Show changes to refs/files-backend.c +15 −11
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 225e0af..24b7c60 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1987,15 +1987,14 @@ static int remove_empty_directories(struct strbuf *path)
  * Locks a ref returning the lock on success and NULL on failure.
  * On failure errno is set to something meaningful.
  */
-static struct ref_lock *lock_ref_sha1_basic(const char *refname,
+static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
+					    const char *refname,
 					    const unsigned char *old_sha1,
 					    const struct string_list *extras,
 					    const struct string_list *skip,
 					    unsigned int flags, int *type,
 					    struct strbuf *err)
 {
-	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "lock_ref_sha1_basic");
 	struct strbuf ref_file = STRBUF_INIT;
 	struct ref_lock *lock;
 	int last_errno = 0;
@@ -2005,6 +2004,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 	int attempts_remaining = 3;
 	int resolved;
 
+	assert_main_repository(&refs->base, "lock_ref_sha1_basic");
 	assert(err);
 
 	lock = xcalloc(1, sizeof(struct ref_lock));
@@ -2648,8 +2648,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 
 	logmoved = log;
 
-	lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, REF_NODEREF,
-				   NULL, &err);
+	lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,
+				   REF_NODEREF, NULL, &err);
 	if (!lock) {
 		error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
 		strbuf_release(&err);
@@ -2667,8 +2667,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 	return 0;
 
  rollback:
-	lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, REF_NODEREF,
-				   NULL, &err);
+	lock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,
+				   REF_NODEREF, NULL, &err);
 	if (!lock) {
 		error("unable to lock %s for rollback: %s", oldrefname, err.buf);
 		strbuf_release(&err);
@@ -3024,13 +3024,14 @@ static int files_create_symref(struct ref_store *ref_store,
 			       const char *refname, const char *target,
 			       const char *logmsg)
 {
+	struct files_ref_store *refs =
+		files_downcast(ref_store, 0, "create_symref");
 	struct strbuf err = STRBUF_INIT;
 	struct ref_lock *lock;
 	int ret;
 
-	files_downcast(ref_store, 0, "create_symref");
-
-	lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL,
+	lock = lock_ref_sha1_basic(refs, refname, NULL,
+				   NULL, NULL, REF_NODEREF, NULL,
 				   &err);
 	if (!lock) {
 		error("%s", err.buf);
@@ -3933,6 +3934,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 		 reflog_expiry_cleanup_fn cleanup_fn,
 		 void *policy_cb_data)
 {
+	struct files_ref_store *refs =
+		get_files_ref_store(NULL, "reflog_expire");
 	static struct lock_file reflog_lock;
 	struct expire_reflog_cb cb;
 	struct ref_lock *lock;
@@ -3951,7 +3954,8 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 	 * reference itself, plus we might need to update the
 	 * reference if --updateref was specified:
 	 */
-	lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, REF_NODEREF,
+	lock = lock_ref_sha1_basic(refs, refname, sha1,
+				   NULL, NULL, REF_NODEREF,
 				   &type, &err);
 	if (!lock) {
 		error("cannot lock ref '%s': %s", refname, err.buf);
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 29/38] split_symref_update(): add a files_ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
Show changes to refs/files-backend.c +4 −3
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 24b7c60..8d3cf96 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3405,7 +3405,8 @@ static int split_head_update(struct ref_update *update,
  * Note that the new update will itself be subject to splitting when
  * the iteration gets to it.
  */
-static int split_symref_update(struct ref_update *update,
+static int split_symref_update(struct files_ref_store *refs,
+			       struct ref_update *update,
 			       const char *referent,
 			       struct ref_transaction *transaction,
 			       struct string_list *affected_refnames,
@@ -3556,7 +3557,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 					    sha1_to_hex(update->old_sha1));
 				return TRANSACTION_GENERIC_ERROR;
 			}
-
 		} else {
 			/*
 			 * Create a new update for the reference this
@@ -3565,7 +3565,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 			 * of processing the split-off update, so we
 			 * don't have to do it here.
 			 */
-			ret = split_symref_update(update, referent.buf, transaction,
+			ret = split_symref_update(refs, update,
+						  referent.buf, transaction,
 						  affected_refnames, err);
 			if (ret)
 				return ret;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 30/38] files_ref_iterator_begin(): take a ref_store argument

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 2 +-
 refs/files-backend.c | 4 ++--
 refs/refs-internal.h | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)
Show changes to 3 files +7 −7

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 2d84c5c..bda8fc1 100644
--- a/refs.c
+++ b/refs.c
@@ -1157,7 +1157,7 @@ static int do_for_each_ref(const char *submodule, const char *prefix,
 	if (!refs)
 		return 0;
 
-	iter = files_ref_iterator_begin(submodule, prefix, flags);
+	iter = files_ref_iterator_begin(refs, prefix, flags);
 	iter = prefix_ref_iterator_begin(iter, prefix, trim);
 
 	return do_for_each_ref_iterator(iter, fn, cb_data);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 8d3cf96..6699ad3 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1867,11 +1867,11 @@ struct ref_iterator_vtable files_ref_iterator_vtable = {
 };
 
 struct ref_iterator *files_ref_iterator_begin(
-		const char *submodule,
+		struct ref_store *ref_store,
 		const char *prefix, unsigned int flags)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(submodule, "ref_iterator_begin");
+		files_downcast(ref_store, 1, "ref_iterator_begin");
 	struct ref_dir *loose_dir, *packed_dir;
 	struct ref_iterator *loose_iter, *packed_iter;
 	struct files_ref_iterator *iter;
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index ae67b49..a5caecc 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -399,13 +399,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 					       const char *prefix,
 					       int trim);
 
+struct ref_store;
+
 /*
  * Iterate over the packed and loose references in the specified
- * submodule that are within find_containing_dir(prefix). If prefix is
+ * ref_store that are within find_containing_dir(prefix). If prefix is
  * NULL or the empty string, iterate over all references in the
  * submodule.
  */
-struct ref_iterator *files_ref_iterator_begin(const char *submodule,
+struct ref_iterator *files_ref_iterator_begin(struct ref_store *ref_store,
 					      const char *prefix,
 					      unsigned int flags);
 
@@ -479,8 +481,6 @@ extern struct ref_iterator *current_ref_iter;
 int do_for_each_ref_iterator(struct ref_iterator *iter,
 			     each_ref_fn fn, void *cb_data);
 
-struct ref_store;
-
 /* refs backends */
 
 /*
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 31/38] refs: add method iterator_begin

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               |  2 +-
 refs/files-backend.c |  3 ++-
 refs/refs-internal.h | 24 ++++++++++++------------
 3 files changed, 15 insertions(+), 14 deletions(-)
Show changes to 3 files +15 −14

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index bda8fc1..d1ab20b 100644
--- a/refs.c
+++ b/refs.c
@@ -1157,7 +1157,7 @@ static int do_for_each_ref(const char *submodule, const char *prefix,
 	if (!refs)
 		return 0;
 
-	iter = files_ref_iterator_begin(refs, prefix, flags);
+	iter = refs->be->iterator_begin(refs, prefix, flags);
 	iter = prefix_ref_iterator_begin(iter, prefix, trim);
 
 	return do_for_each_ref_iterator(iter, fn, cb_data);
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 6699ad3..40c05b2 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1866,7 +1866,7 @@ struct ref_iterator_vtable files_ref_iterator_vtable = {
 	files_ref_iterator_abort
 };
 
-struct ref_iterator *files_ref_iterator_begin(
+static struct ref_iterator *files_ref_iterator_begin(
 		struct ref_store *ref_store,
 		const char *prefix, unsigned int flags)
 {
@@ -4047,6 +4047,7 @@ struct ref_storage_be refs_be_files = {
 	files_peel_ref,
 	files_create_symref,
 
+	files_ref_iterator_begin,
 	files_read_raw_ref,
 	files_verify_refname_available
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index a5caecc..797d44f 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -399,18 +399,6 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 					       const char *prefix,
 					       int trim);
 
-struct ref_store;
-
-/*
- * Iterate over the packed and loose references in the specified
- * ref_store that are within find_containing_dir(prefix). If prefix is
- * NULL or the empty string, iterate over all references in the
- * submodule.
- */
-struct ref_iterator *files_ref_iterator_begin(struct ref_store *ref_store,
-					      const char *prefix,
-					      unsigned int flags);
-
 /*
  * Iterate over the references in the main ref_store that have a
  * reflog. The paths within a directory are iterated over in arbitrary
@@ -483,6 +471,8 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
 
 /* refs backends */
 
+struct ref_store;
+
 /*
  * Initialize the ref_store for the specified submodule, or for the
  * main repository if submodule == NULL. These functions should call
@@ -504,6 +494,15 @@ typedef int create_symref_fn(struct ref_store *ref_store,
 			     const char *logmsg);
 
 /*
+ * Iterate over the references in the specified ref_store that are
+ * within find_containing_dir(prefix). If prefix is NULL or the empty
+ * string, iterate over all references in the submodule.
+ */
+typedef struct ref_iterator *ref_iterator_begin_fn(
+		struct ref_store *ref_store,
+		const char *prefix, unsigned int flags);
+
+/*
  * Read a reference from the specified reference store, non-recursively.
  * Set type to describe the reference, and:
  *
@@ -561,6 +560,7 @@ struct ref_storage_be {
 	peel_ref_fn *peel_ref;
 	create_symref_fn *create_symref;
 
+	ref_iterator_begin_fn *iterator_begin;
 	read_raw_ref_fn *read_raw_ref;
 	verify_refname_available_fn *verify_refname_available;
 };
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 32/38] refs: add methods for reflog

From: David Turner <dturner@twopensource.com>

In the file-based backend, the reflog piggybacks on the ref lock. Since other backends won't have the same sort of ref lock, ref backends must also handle reflogs.

Signed-off-by: Ronnie Sahlberg <rsahlberg@google.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
This patch doesn't fix the known problem [1] that worktree reflogs are
not considered when considering reachability, but I think this whole
patch series will make the problem easier to fix in a relatively
elegant way.
[1] http://thread.gmane.org/gmane.comp.version-control.git/295961/focus=296068
 refs.c               | 63 ++++++++++++++++++++++++++++++++++++++++++++++
 refs/files-backend.c | 70 +++++++++++++++++++++++++++++++++++++---------------
 refs/refs-internal.h | 44 +++++++++++++++++++++++++++------
 3 files changed, 150 insertions(+), 27 deletions(-)
Show changes to 3 files +150 −27

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index d1ab20b..77d3986 100644
--- a/refs.c
+++ b/refs.c
@@ -1458,3 +1458,66 @@ int verify_refname_available(const char *refname,
 
 	return refs->be->verify_refname_available(refs, refname, extra, skip, err);
 }
+
+int for_each_reflog(each_ref_fn fn, void *cb_data)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+	struct ref_iterator *iter;
+
+	iter = refs->be->reflog_iterator_begin(refs);
+
+	return do_for_each_ref_iterator(iter, fn, cb_data);
+}
+
+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+				void *cb_data)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->for_each_reflog_ent_reverse(refs, refname,
+						     fn, cb_data);
+}
+
+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
+			void *cb_data)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
+int reflog_exists(const char *refname)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->reflog_exists(refs, refname);
+}
+
+int safe_create_reflog(const char *refname, int force_create,
+		       struct strbuf *err)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->create_reflog(refs, refname, force_create, err);
+}
+
+int delete_reflog(const char *refname)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->delete_reflog(refs, refname);
+}
+
+int reflog_expire(const char *refname, const unsigned char *sha1,
+		  unsigned int flags,
+		  reflog_expiry_prepare_fn prepare_fn,
+		  reflog_expiry_should_prune_fn should_prune_fn,
+		  reflog_expiry_cleanup_fn cleanup_fn,
+		  void *policy_cb_data)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->reflog_expire(refs, refname, sha1, flags,
+				       prepare_fn, should_prune_fn,
+				       cleanup_fn, policy_cb_data);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 40c05b2..b19ea02 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2781,11 +2781,16 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str
 }
 
 
-int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
+static int files_create_reflog(struct ref_store *ref_store,
+			       const char *refname, int force_create,
+			       struct strbuf *err)
 {
 	int ret;
 	struct strbuf sb = STRBUF_INIT;
 
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "create_reflog");
+
 	ret = log_ref_setup(refname, &sb, err, force_create);
 	strbuf_release(&sb);
 	return ret;
@@ -3079,16 +3084,24 @@ int set_worktree_head_symref(const char *gitdir, const char *target)
 	return ret;
 }
 
-int reflog_exists(const char *refname)
+static int files_reflog_exists(struct ref_store *ref_store,
+			       const char *refname)
 {
 	struct stat st;
 
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "reflog_exists");
+
 	return !lstat(git_path("logs/%s", refname), &st) &&
 		S_ISREG(st.st_mode);
 }
 
-int delete_reflog(const char *refname)
+static int files_delete_reflog(struct ref_store *ref_store,
+			       const char *refname)
 {
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "delete_reflog");
+
 	return remove_path(git_path("logs/%s", refname));
 }
 
@@ -3131,13 +3144,19 @@ static char *find_beginning_of_line(char *bob, char *scan)
 	return scan;
 }
 
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data)
+static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
+					     const char *refname,
+					     each_reflog_ent_fn fn,
+					     void *cb_data)
 {
 	struct strbuf sb = STRBUF_INIT;
 	FILE *logfp;
 	long pos;
 	int ret = 0, at_tail = 1;
 
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "for_each_reflog_ent_reverse");
+
 	logfp = fopen(git_path("logs/%s", refname), "r");
 	if (!logfp)
 		return -1;
@@ -3233,12 +3252,17 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void
 	return ret;
 }
 
-int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data)
+static int files_for_each_reflog_ent(struct ref_store *ref_store,
+				     const char *refname,
+				     each_reflog_ent_fn fn, void *cb_data)
 {
 	FILE *logfp;
 	struct strbuf sb = STRBUF_INIT;
 	int ret = 0;
 
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "for_each_reflog_ent");
+
 	logfp = fopen(git_path("logs/%s", refname), "r");
 	if (!logfp)
 		return -1;
@@ -3317,22 +3341,19 @@ struct ref_iterator_vtable files_reflog_iterator_vtable = {
 	files_reflog_iterator_abort
 };
 
-struct ref_iterator *files_reflog_iterator_begin(void)
+struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
 {
 	struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
 	struct ref_iterator *ref_iterator = &iter->base;
 
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "reflog_iterator_begin");
+
 	base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
 	iter->dir_iterator = dir_iterator_begin(git_path("logs"));
 	return ref_iterator;
 }
 
-int for_each_reflog(each_ref_fn fn, void *cb_data)
-{
-	return do_for_each_ref_iterator(files_reflog_iterator_begin(),
-					fn, cb_data);
-}
-
 static int ref_update_reject_duplicates(struct string_list *refnames,
 					struct strbuf *err)
 {
@@ -3928,15 +3949,16 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 	return 0;
 }
 
-int reflog_expire(const char *refname, const unsigned char *sha1,
-		 unsigned int flags,
-		 reflog_expiry_prepare_fn prepare_fn,
-		 reflog_expiry_should_prune_fn should_prune_fn,
-		 reflog_expiry_cleanup_fn cleanup_fn,
-		 void *policy_cb_data)
+static int files_reflog_expire(struct ref_store *ref_store,
+			       const char *refname, const unsigned char *sha1,
+			       unsigned int flags,
+			       reflog_expiry_prepare_fn prepare_fn,
+			       reflog_expiry_should_prune_fn should_prune_fn,
+			       reflog_expiry_cleanup_fn cleanup_fn,
+			       void *policy_cb_data)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "reflog_expire");
+		files_downcast(ref_store, 0, "reflog_expire");
 	static struct lock_file reflog_lock;
 	struct expire_reflog_cb cb;
 	struct ref_lock *lock;
@@ -4049,5 +4071,13 @@ struct ref_storage_be refs_be_files = {
 
 	files_ref_iterator_begin,
 	files_read_raw_ref,
-	files_verify_refname_available
+	files_verify_refname_available,
+
+	files_reflog_iterator_begin,
+	files_for_each_reflog_ent,
+	files_for_each_reflog_ent_reverse,
+	files_reflog_exists,
+	files_create_reflog,
+	files_delete_reflog,
+	files_reflog_expire
 };
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 797d44f..7d84984 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -399,13 +399,6 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 					       const char *prefix,
 					       int trim);
 
-/*
- * Iterate over the references in the main ref_store that have a
- * reflog. The paths within a directory are iterated over in arbitrary
- * order.
- */
-struct ref_iterator *files_reflog_iterator_begin(void);
-
 /* Internal implementation of reference iteration: */
 
 /*
@@ -502,6 +495,35 @@ typedef struct ref_iterator *ref_iterator_begin_fn(
 		struct ref_store *ref_store,
 		const char *prefix, unsigned int flags);
 
+/* reflog functions */
+
+/*
+ * Iterate over the references in the specified ref_store that have a
+ * reflog. The refs are iterated over in arbitrary order.
+ */
+typedef struct ref_iterator *reflog_iterator_begin_fn(
+		struct ref_store *ref_store);
+
+typedef int for_each_reflog_ent_fn(struct ref_store *ref_store,
+				   const char *refname,
+				   each_reflog_ent_fn fn,
+				   void *cb_data);
+typedef int for_each_reflog_ent_reverse_fn(struct ref_store *ref_store,
+					   const char *refname,
+					   each_reflog_ent_fn fn,
+					   void *cb_data);
+typedef int reflog_exists_fn(struct ref_store *ref_store, const char *refname);
+typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname,
+			     int force_create, struct strbuf *err);
+typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname);
+typedef int reflog_expire_fn(struct ref_store *ref_store,
+			     const char *refname, const unsigned char *sha1,
+			     unsigned int flags,
+			     reflog_expiry_prepare_fn prepare_fn,
+			     reflog_expiry_should_prune_fn should_prune_fn,
+			     reflog_expiry_cleanup_fn cleanup_fn,
+			     void *policy_cb_data);
+
 /*
  * Read a reference from the specified reference store, non-recursively.
  * Set type to describe the reference, and:
@@ -563,6 +585,14 @@ struct ref_storage_be {
 	ref_iterator_begin_fn *iterator_begin;
 	read_raw_ref_fn *read_raw_ref;
 	verify_refname_available_fn *verify_refname_available;
+
+	reflog_iterator_begin_fn *reflog_iterator_begin;
+	for_each_reflog_ent_fn *for_each_reflog_ent;
+	for_each_reflog_ent_reverse_fn *for_each_reflog_ent_reverse;
+	reflog_exists_fn *reflog_exists;
+	create_reflog_fn *create_reflog;
+	delete_reflog_fn *delete_reflog;
+	reflog_expire_fn *reflog_expire;
 };
 
 extern struct ref_storage_be refs_be_files;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 33/38] refs: add method for initial ref transaction commit

From: David Turner <dturner@twopensource.com>
Signed-off-by: Ronnie Sahlberg <rsahlberg@google.com>
Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               | 8 ++++++++
 refs/files-backend.c | 8 +++++---
 refs/refs-internal.h | 1 +
 3 files changed, 14 insertions(+), 3 deletions(-)
Show changes to 3 files +14 −3

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 77d3986..6c1e899 100644
--- a/refs.c
+++ b/refs.c
@@ -1521,3 +1521,11 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 				       prepare_fn, should_prune_fn,
 				       cleanup_fn, policy_cb_data);
 }
+
+int initial_ref_transaction_commit(struct ref_transaction *transaction,
+				   struct strbuf *err)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->initial_transaction_commit(refs, transaction, err);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index b19ea02..253899f 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3832,11 +3832,12 @@ static int ref_present(const char *refname,
 	return string_list_has_string(affected_refnames, refname);
 }
 
-int initial_ref_transaction_commit(struct ref_transaction *transaction,
-				   struct strbuf *err)
+static int files_initial_transaction_commit(struct ref_store *ref_store,
+					    struct ref_transaction *transaction,
+					    struct strbuf *err)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "initial_ref_transaction_commit");
+		files_downcast(ref_store, 0, "initial_ref_transaction_commit");
 	int ret = 0, i;
 	struct ref_update **updates = transaction->updates;
 	struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
@@ -4064,6 +4065,7 @@ struct ref_storage_be refs_be_files = {
 	"files",
 	files_ref_store_create,
 	files_transaction_commit,
+	files_initial_transaction_commit,
 
 	files_pack_refs,
 	files_peel_ref,
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 7d84984..e462b54 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -577,6 +577,7 @@ struct ref_storage_be {
 	const char *name;
 	ref_store_init_fn *init;
 	ref_transaction_commit_fn *transaction_commit;
+	ref_transaction_commit_fn *initial_transaction_commit;
 
 	pack_refs_fn *pack_refs;
 	peel_ref_fn *peel_ref;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 34/38] refs: add method for delete_refs

From: David Turner <dturner@twopensource.com>

In the file-based backend, delete_refs has some special optimization to deal with packed refs. In other backends, we might be able to make ref deletion faster by putting all deletions into a single transaction. So we need a special backend function for this.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
I think that we could get away without this method if we make
ref_transactions a bit smarter (for example, by supporting best-effort
updates that can fail without causing the entire transaction to be
aborted). But that would be a significant detour, so let's leave it
here for now.
 refs.c               | 7 +++++++
 refs/files-backend.c | 6 ++++--
 refs/refs-internal.h | 3 +++
 3 files changed, 14 insertions(+), 2 deletions(-)
Show changes to 3 files +14 −2

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 6c1e899..8ab9862 100644
--- a/refs.c
+++ b/refs.c
@@ -1529,3 +1529,10 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
 
 	return refs->be->initial_transaction_commit(refs, transaction, err);
 }
+
+int delete_refs(struct string_list *refnames, unsigned int flags)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->delete_refs(refs, refnames, flags);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 253899f..5681141 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2455,10 +2455,11 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
 	return 0;
 }
 
-int delete_refs(struct string_list *refnames, unsigned int flags)
+static int files_delete_refs(struct ref_store *ref_store,
+			     struct string_list *refnames, unsigned int flags)
 {
 	struct files_ref_store *refs =
-		get_files_ref_store(NULL, "delete_refs");
+		files_downcast(ref_store, 0, "delete_refs");
 	struct strbuf err = STRBUF_INIT;
 	int i, result = 0;
 
@@ -4070,6 +4071,7 @@ struct ref_storage_be refs_be_files = {
 	files_pack_refs,
 	files_peel_ref,
 	files_create_symref,
+	files_delete_refs,
 
 	files_ref_iterator_begin,
 	files_read_raw_ref,
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index e462b54..f944b7a 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -485,6 +485,8 @@ typedef int create_symref_fn(struct ref_store *ref_store,
 			     const char *ref_target,
 			     const char *refs_heads_master,
 			     const char *logmsg);
+typedef int delete_refs_fn(struct ref_store *ref_store,
+			   struct string_list *refnames, unsigned int flags);
 
 /*
  * Iterate over the references in the specified ref_store that are
@@ -582,6 +584,7 @@ struct ref_storage_be {
 	pack_refs_fn *pack_refs;
 	peel_ref_fn *peel_ref;
 	create_symref_fn *create_symref;
+	delete_refs_fn *delete_refs;
 
 	ref_iterator_begin_fn *iterator_begin;
 	read_raw_ref_fn *read_raw_ref;
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 17:43 UTC · re: Michael Haggerty · lore

Re: [PATCH 34/38] refs: add method for delete_refs

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 16 quoted lines
> From: David Turner <dturner@twopensource.com>
>
> In the file-based backend, delete_refs has some special optimization
> to deal with packed refs.  In other backends, we might be able to make
> ref deletion faster by putting all deletions into a single
> transaction.  So we need a special backend function for this.
>
> Signed-off-by: David Turner <dturner@twopensource.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
> ---
> I think that we could get away without this method if we make
> ref_transactions a bit smarter (for example, by supporting best-effort
> updates that can fail without causing the entire transaction to be
> aborted). But that would be a significant detour, so let's leave it
> here for now.

Hmm, I actually was wondering why 'pack without' was there while reading 24/38; IIUC, that is one of the "special optimization" that is very much tied to the files backend, and it may make sense to hide it behind delete_refs() as its implementation detail.

Which is exactly what this step is about, so I am happy ;-)

Unlike other changes like the ones that did read_raw_ref(), verify_refname_available(), etc., the title does not follow the pattern "refs: make X() virtual", even though as far as I can see the intent is the same as others. Perhaps a minor retitle is in order?

Show 67 quoted lines
>  refs.c               | 7 +++++++
>  refs/files-backend.c | 6 ++++--
>  refs/refs-internal.h | 3 +++
>  3 files changed, 14 insertions(+), 2 deletions(-)
>
> diff --git a/refs.c b/refs.c
> index 6c1e899..8ab9862 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1529,3 +1529,10 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
>  
>  	return refs->be->initial_transaction_commit(refs, transaction, err);
>  }
> +
> +int delete_refs(struct string_list *refnames, unsigned int flags)
> +{
> +	struct ref_store *refs = get_ref_store(NULL);
> +
> +	return refs->be->delete_refs(refs, refnames, flags);
> +}
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index 253899f..5681141 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -2455,10 +2455,11 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
>  	return 0;
>  }
>  
> -int delete_refs(struct string_list *refnames, unsigned int flags)
> +static int files_delete_refs(struct ref_store *ref_store,
> +			     struct string_list *refnames, unsigned int flags)
>  {
>  	struct files_ref_store *refs =
> -		get_files_ref_store(NULL, "delete_refs");
> +		files_downcast(ref_store, 0, "delete_refs");
>  	struct strbuf err = STRBUF_INIT;
>  	int i, result = 0;
>  
> @@ -4070,6 +4071,7 @@ struct ref_storage_be refs_be_files = {
>  	files_pack_refs,
>  	files_peel_ref,
>  	files_create_symref,
> +	files_delete_refs,
>  
>  	files_ref_iterator_begin,
>  	files_read_raw_ref,
> diff --git a/refs/refs-internal.h b/refs/refs-internal.h
> index e462b54..f944b7a 100644
> --- a/refs/refs-internal.h
> +++ b/refs/refs-internal.h
> @@ -485,6 +485,8 @@ typedef int create_symref_fn(struct ref_store *ref_store,
>  			     const char *ref_target,
>  			     const char *refs_heads_master,
>  			     const char *logmsg);
> +typedef int delete_refs_fn(struct ref_store *ref_store,
> +			   struct string_list *refnames, unsigned int flags);
>  
>  /*
>   * Iterate over the references in the specified ref_store that are
> @@ -582,6 +584,7 @@ struct ref_storage_be {
>  	pack_refs_fn *pack_refs;
>  	peel_ref_fn *peel_ref;
>  	create_symref_fn *create_symref;
> +	delete_refs_fn *delete_refs;
>  
>  	ref_iterator_begin_fn *iterator_begin;
>  	read_raw_ref_fn *read_raw_ref;
Michael Haggerty· Jun 9, 2016, 15:14 UTC · re: Junio C Hamano · lore

Re: [PATCH 34/38] refs: add method for delete_refs

On 06/07/2016 07:43 PM, Junio C Hamano wrote:
Show 25 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> From: David Turner <dturner@twopensource.com>
>>
>> In the file-based backend, delete_refs has some special optimization
>> to deal with packed refs.  In other backends, we might be able to make
>> ref deletion faster by putting all deletions into a single
>> transaction.  So we need a special backend function for this.
>>
>> Signed-off-by: David Turner <dturner@twopensource.com>
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
>> ---
>> I think that we could get away without this method if we make
>> ref_transactions a bit smarter (for example, by supporting best-effort
>> updates that can fail without causing the entire transaction to be
>> aborted). But that would be a significant detour, so let's leave it
>> here for now.
> 
> Hmm, I actually was wondering why 'pack without' was there while
> reading 24/38; IIUC, that is one of the "special optimization" that
> is very much tied to the files backend, and it may make sense to
> hide it behind delete_refs() as its implementation detail.
> 
> Which is exactly what this step is about, so I am happy ;-)

In the future I think this optimization will be even better hidden, namely within the transaction_commit method of a packed_refs_store class :-)

Show 5 quoted lines
> Unlike other changes like the ones that did read_raw_ref(),
> verify_refname_available(), etc., the title does not follow the
> pattern "refs: make X() virtual", even though as far as I can see
> the intent is the same as others.  Perhaps a minor retitle is in
> order?
OK.
Michael
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 35/38] refs: add methods to init refs db

From: David Turner <dturner@twopensource.com>

Alternate refs backends might not need the refs/heads directory and so on, so we make ref db initialization part of the backend.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/init-db.c    | 21 +++++++++++----------
 refs.c               |  8 ++++++++
 refs.h               |  2 ++
 refs/files-backend.c | 18 ++++++++++++++++++
 refs/refs-internal.h |  3 +++
 5 files changed, 42 insertions(+), 10 deletions(-)
Show changes to 5 files +42 −10

builtin/init-db.c, refs.c, refs.h, refs/files-backend.c, refs/refs-internal.h

diff --git a/builtin/init-db.c b/builtin/init-db.c
index b2d8d40..082fa9f 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -180,13 +180,7 @@ static int create_default_files(const char *template_path)
 	char junk[2];
 	int reinit;
 	int filemode;
-
-	/*
-	 * Create .git/refs/{heads,tags}
-	 */
-	safe_create_dir(git_path_buf(&buf, "refs"), 1);
-	safe_create_dir(git_path_buf(&buf, "refs/heads"), 1);
-	safe_create_dir(git_path_buf(&buf, "refs/tags"), 1);
+	struct strbuf err = STRBUF_INIT;
 
 	/* Just look for `init.templatedir` */
 	git_config(git_init_db_config, NULL);
@@ -210,12 +204,19 @@ static int create_default_files(const char *template_path)
 	 */
 	if (get_shared_repository()) {
 		adjust_shared_perm(get_git_dir());
-		adjust_shared_perm(git_path_buf(&buf, "refs"));
-		adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
-		adjust_shared_perm(git_path_buf(&buf, "refs/tags"));
 	}
 
 	/*
+	 * We need to create a "refs" dir in any case so that older
+	 * versions of git can tell that this is a repository.
+	 */
+	safe_create_dir(git_path("refs"), 1);
+	adjust_shared_perm(git_path("refs"));
+
+	if (refs_init_db(&err))
+		die("failed to set up refs db: %s", err.buf);
+
+	/*
 	 * Create the default symlink from ".git/HEAD" to the "master"
 	 * branch, if it does not exist yet.
 	 */
diff --git a/refs.c b/refs.c
index 8ab9862..7dc67a6 100644
--- a/refs.c
+++ b/refs.c
@@ -1292,6 +1292,14 @@ static const char *resolve_ref_recursively(struct ref_store *refs,
 	return NULL;
 }
 
+/* backend functions */
+int refs_init_db(struct strbuf *err)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->init_db(refs, err);
+}
+
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 			       unsigned char *sha1, int *flags)
 {
diff --git a/refs.h b/refs.h
index 132dcef..20fae94 100644
--- a/refs.h
+++ b/refs.h
@@ -66,6 +66,8 @@ int ref_exists(const char *refname);
 
 int is_branch(const char *refname);
 
+extern int refs_init_db(struct strbuf *err);
+
 /*
  * If refname is a non-symbolic reference that refers to a tag object,
  * and the tag can be (recursively) dereferenced to a non-tag object,
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 5681141..b2d677e 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -4061,10 +4061,28 @@ static int files_reflog_expire(struct ref_store *ref_store,
 	return -1;
 }
 
+static int files_init_db(struct ref_store *ref_store, struct strbuf *err)
+{
+	/* Check validity (but we don't need the result): */
+	files_downcast(ref_store, 0, "init_db");
+
+	/*
+	 * Create .git/refs/{heads,tags}
+	 */
+	safe_create_dir(git_path("refs/heads"), 1);
+	safe_create_dir(git_path("refs/tags"), 1);
+	if (get_shared_repository()) {
+		adjust_shared_perm(git_path("refs/heads"));
+		adjust_shared_perm(git_path("refs/tags"));
+	}
+	return 0;
+}
+
 struct ref_storage_be refs_be_files = {
 	NULL,
 	"files",
 	files_ref_store_create,
+	files_init_db,
 	files_transaction_commit,
 	files_initial_transaction_commit,
 
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index f944b7a..6c46353 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -474,6 +474,8 @@ struct ref_store;
  */
 typedef struct ref_store *ref_store_init_fn(const char *submodule);
 
+typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
+
 typedef int ref_transaction_commit_fn(struct ref_store *refs,
 				      struct ref_transaction *transaction,
 				      struct strbuf *err);
@@ -578,6 +580,7 @@ struct ref_storage_be {
 	struct ref_storage_be *next;
 	const char *name;
 	ref_store_init_fn *init;
+	ref_init_db_fn *init_db;
 	ref_transaction_commit_fn *transaction_commit;
 	ref_transaction_commit_fn *initial_transaction_commit;
 
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 36/38] refs: add method to rename refs

From: David Turner <dturner@twopensource.com>

This removes the last caller of function get_files_ref_store(), so remove it.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c               |  7 +++++++
 refs/files-backend.c | 24 ++++++------------------
 refs/refs-internal.h |  4 ++++
 3 files changed, 17 insertions(+), 18 deletions(-)
Show changes to 3 files +17 −18

refs.c, refs/files-backend.c, refs/refs-internal.h

diff --git a/refs.c b/refs.c
index 7dc67a6..17a36fb 100644
--- a/refs.c
+++ b/refs.c
@@ -1544,3 +1544,10 @@ int delete_refs(struct string_list *refnames, unsigned int flags)
 
 	return refs->be->delete_refs(refs, refnames, flags);
 }
+
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+	struct ref_store *refs = get_ref_store(NULL);
+
+	return refs->be->rename_ref(refs, oldref, newref, logmsg);
+}
diff --git a/refs/files-backend.c b/refs/files-backend.c
index b2d677e..d098c7d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1001,22 +1001,6 @@ static struct files_ref_store *files_downcast(
 	return refs;
 }
 
-/*
- * Return a pointer to the reference store for the specified
- * submodule. For the main repository, use submodule==NULL; such a
- * call cannot fail. For a submodule, the submodule must exist and be
- * a nonbare repository, otherwise return NULL. Verify that the
- * reference store is a files_ref_store, and cast it to that type
- * before returning it.
- */
-static struct files_ref_store *get_files_ref_store(const char *submodule,
-						   const char *caller)
-{
-	struct ref_store *refs = get_ref_store(submodule);
-
-	return refs ? files_downcast(refs, 1, caller) : NULL;
-}
-
 /* The length of a peeled reference line in packed-refs, including EOL: */
 #define PEELED_LINE_LENGTH 42
 
@@ -2584,9 +2568,12 @@ static int commit_ref_update(struct files_ref_store *refs,
 			     const unsigned char *sha1, const char *logmsg,
 			     struct strbuf *err);
 
-int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
+static int files_rename_ref(struct ref_store *ref_store,
+			    const char *oldrefname, const char *newrefname,
+			    const char *logmsg)
 {
-	struct files_ref_store *refs = get_files_ref_store(NULL, "rename_ref");
+	struct files_ref_store *refs =
+		files_downcast(ref_store, 0, "rename_ref");
 	unsigned char sha1[20], orig_sha1[20];
 	int flag = 0, logmoved = 0;
 	struct ref_lock *lock;
@@ -4090,6 +4077,7 @@ struct ref_storage_be refs_be_files = {
 	files_peel_ref,
 	files_create_symref,
 	files_delete_refs,
+	files_rename_ref,
 
 	files_ref_iterator_begin,
 	files_read_raw_ref,
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 6c46353..4bfa5ce 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -489,6 +489,9 @@ typedef int create_symref_fn(struct ref_store *ref_store,
 			     const char *logmsg);
 typedef int delete_refs_fn(struct ref_store *ref_store,
 			   struct string_list *refnames, unsigned int flags);
+typedef int rename_ref_fn(struct ref_store *ref_store,
+			  const char *oldref, const char *newref,
+			  const char *logmsg);
 
 /*
  * Iterate over the references in the specified ref_store that are
@@ -588,6 +591,7 @@ struct ref_storage_be {
 	peel_ref_fn *peel_ref;
 	create_symref_fn *create_symref;
 	delete_refs_fn *delete_refs;
+	rename_ref_fn *rename_ref;
 
 	ref_iterator_begin_fn *iterator_begin;
 	read_raw_ref_fn *read_raw_ref;
-- 
2.8.1
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 37/38] refs: make lock generic

From: David Turner <dturner@twopensource.com>

Instead of including a files-backend-specific struct ref_lock, change the generic ref_update struct to include a void pointer that backends can use for their own arbitrary data.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c | 25 +++++++++++++------------
 refs/refs-internal.h |  2 +-
 2 files changed, 14 insertions(+), 13 deletions(-)
Show changes to 2 files +14 −13

refs/files-backend.c, refs/refs-internal.h

diff --git a/refs/files-backend.c b/refs/files-backend.c
index d098c7d..53d0ba1 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3523,9 +3523,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 
 	ret = lock_raw_ref(refs, update->refname, mustexist,
 			   affected_refnames, NULL,
-			   &update->lock, &referent,
+			   &lock, &referent,
 			   &update->type, err);
-
 	if (ret) {
 		char *reason;
 
@@ -3536,7 +3535,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 		return ret;
 	}
 
-	lock = update->lock;
+	update->backend_data = lock;
 
 	if (update->type & REF_ISSYMREF) {
 		if (update->flags & REF_NODEREF) {
@@ -3591,7 +3590,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 		for (parent_update = update->parent_update;
 		     parent_update;
 		     parent_update = parent_update->parent_update) {
-			oidcpy(&parent_update->lock->old_oid, &lock->old_oid);
+			struct ref_lock *parent_lock = parent_update->backend_data;
+			oidcpy(&parent_lock->old_oid, &lock->old_oid);
 		}
 
 		if ((update->flags & REF_HAVE_OLD) &&
@@ -3626,7 +3626,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 			 * The lock was freed upon failure of
 			 * write_ref_to_lockfile():
 			 */
-			update->lock = NULL;
+			update->backend_data = NULL;
 			strbuf_addf(err,
 				    "cannot update the ref '%s': %s",
 				    update->refname, write_err);
@@ -3745,7 +3745,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
 	/* Perform updates first so live commits remain referenced */
 	for (i = 0; i < transaction->nr; i++) {
 		struct ref_update *update = updates[i];
-		struct ref_lock *lock = update->lock;
+		struct ref_lock *lock = update->backend_data;
 
 		if (update->flags & REF_NEEDS_COMMIT ||
 		    update->flags & REF_LOG_ONLY) {
@@ -3758,7 +3758,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
 					    lock->ref_name, old_msg);
 				free(old_msg);
 				unlock_ref(lock);
-				update->lock = NULL;
+				update->backend_data = NULL;
 				ret = TRANSACTION_GENERIC_ERROR;
 				goto cleanup;
 			}
@@ -3768,7 +3768,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
 			if (commit_ref(lock)) {
 				strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
 				unlock_ref(lock);
-				update->lock = NULL;
+				update->backend_data = NULL;
 				ret = TRANSACTION_GENERIC_ERROR;
 				goto cleanup;
 			}
@@ -3777,17 +3777,18 @@ static int files_transaction_commit(struct ref_store *ref_store,
 	/* Perform deletes now that updates are safely completed */
 	for (i = 0; i < transaction->nr; i++) {
 		struct ref_update *update = updates[i];
+		struct ref_lock *lock = update->backend_data;
 
 		if (update->flags & REF_DELETING &&
 		    !(update->flags & REF_LOG_ONLY)) {
-			if (delete_ref_loose(update->lock, update->type, err)) {
+			if (delete_ref_loose(lock, update->type, err)) {
 				ret = TRANSACTION_GENERIC_ERROR;
 				goto cleanup;
 			}
 
 			if (!(update->flags & REF_ISPRUNING))
 				string_list_append(&refs_to_delete,
-						   update->lock->ref_name);
+						   lock->ref_name);
 		}
 	}
 
@@ -3803,8 +3804,8 @@ cleanup:
 	transaction->state = REF_TRANSACTION_CLOSED;
 
 	for (i = 0; i < transaction->nr; i++)
-		if (updates[i]->lock)
-			unlock_ref(updates[i]->lock);
+		if (updates[i]->backend_data)
+			unlock_ref(updates[i]->backend_data);
 	string_list_clear(&refs_to_delete, 0);
 	free(head_ref);
 	string_list_clear(&affected_refnames, 0);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 4bfa5ce..db891e6 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -162,7 +162,7 @@ struct ref_update {
 	 */
 	unsigned int flags;
 
-	struct ref_lock *lock;
+	void *backend_data;
 	unsigned int type;
 	char *msg;
 
-- 
2.8.1
Junio C Hamano· Jun 7, 2016, 17:50 UTC · re: Michael Haggerty · lore

Re: [PATCH 37/38] refs: make lock generic

Michael Haggerty <mhagger@alum.mit.edu> writes:
Show 5 quoted lines
> From: David Turner <dturner@twopensource.com>
>
> Instead of including a files-backend-specific struct ref_lock, change
> the generic ref_update struct to include a void pointer that backends
> can use for their own arbitrary data.
Hmph.
Show 15 quoted lines
> @@ -3591,7 +3590,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
>  		for (parent_update = update->parent_update;
>  		     parent_update;
>  		     parent_update = parent_update->parent_update) {
> -			oidcpy(&parent_update->lock->old_oid, &lock->old_oid);
> +			struct ref_lock *parent_lock = parent_update->backend_data;
> +			oidcpy(&parent_lock->old_oid, &lock->old_oid);
>  		}
> ...
> @@ -3745,7 +3745,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
>  	/* Perform updates first so live commits remain referenced */
>  	for (i = 0; i < transaction->nr; i++) {
>  		struct ref_update *update = updates[i];
> -		struct ref_lock *lock = update->lock;
> +		struct ref_lock *lock = update->backend_data;

OK, and files_* backend method downcasts it to what it wants, which is good.

Michael Haggerty· Jun 9, 2016, 15:21 UTC · re: Junio C Hamano · lore

Re: [PATCH 37/38] refs: make lock generic

On 06/07/2016 07:50 PM, Junio C Hamano wrote:
Show 9 quoted lines
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> From: David Turner <dturner@twopensource.com>
>>
>> Instead of including a files-backend-specific struct ref_lock, change
>> the generic ref_update struct to include a void pointer that backends
>> can use for their own arbitrary data.
> 
> Hmph.

I don't know what your comment means. This step is a consequence of the design decision to stick with a single ref_transaction class that is used by all ref_stores, which was nice because it avoided the need to virtualize the functions ref_transaction_{begin,update,create,delete,verify}.

Show 18 quoted lines
>> @@ -3591,7 +3590,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
>>  		for (parent_update = update->parent_update;
>>  		     parent_update;
>>  		     parent_update = parent_update->parent_update) {
>> -			oidcpy(&parent_update->lock->old_oid, &lock->old_oid);
>> +			struct ref_lock *parent_lock = parent_update->backend_data;
>> +			oidcpy(&parent_lock->old_oid, &lock->old_oid);
>>  		}
>> ...
>> @@ -3745,7 +3745,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
>>  	/* Perform updates first so live commits remain referenced */
>>  	for (i = 0; i < transaction->nr; i++) {
>>  		struct ref_update *update = updates[i];
>> -		struct ref_lock *lock = update->lock;
>> +		struct ref_lock *lock = update->backend_data;
> 
> OK, and files_* backend method downcasts it to what it wants, which
> is good.
Michael
Michael Haggerty· Jun 9, 2016, 15:53 UTC · re: Junio C Hamano · lore

Re: [PATCH 37/38] refs: make lock generic

On 06/07/2016 07:50 PM, Junio C Hamano wrote:
> [...]

Thanks for all your comments, Junio. I've pushed a revised version of the patch series to my GitHub fork [1] as branch "ref-store", but I'll wait for a little longer to see if there are more comments on the list before sending a re-roll.

Michael
[1] https://github.com/mhagger/git
Michael Haggerty· Jun 3, 2016, 21:04 UTC · re: Michael Haggerty · lore

[PATCH 38/38] refs: implement iteration over only per-worktree refs

From: David Turner <dturner@twopensource.com>

Alternate refs backends might still use files to store per-worktree refs. So provide a way to iterate over only the per-worktree references in a ref_store. The other backend can set up a files ref_store and iterate using the new DO_FOR_EACH_PER_WORKTREE_ONLY flag when iterating.

Signed-off-by: David Turner <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs/files-backend.c |  4 ++++
 refs/refs-internal.h | 10 +++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)
Show changes to 2 files +13 −1

refs/files-backend.c, refs/refs-internal.h

diff --git a/refs/files-backend.c b/refs/files-backend.c
index 53d0ba1..b5ad21c 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1802,6 +1802,10 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
 	int ok;
 
 	while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
+		if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+		    ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
+			continue;
+
 		if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
 		    !ref_resolves_to_object(iter->iter0->refname,
 					    iter->iter0->oid,
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index db891e6..6d97ed9 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -462,10 +462,18 @@ extern struct ref_iterator *current_ref_iter;
 int do_for_each_ref_iterator(struct ref_iterator *iter,
 			     each_ref_fn fn, void *cb_data);
 
-/* refs backends */
+/*
+ * Only include per-worktree refs in a do_for_each_ref*() iteration.
+ * Normally this will be used with a files ref_store, since that's
+ * where all reference backends will presumably store their
+ * per-worktree refs.
+ */
+#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02
 
 struct ref_store;
 
+/* refs backends */
+
 /*
  * Initialize the ref_store for the specified submodule, or for the
  * main repository if submodule == NULL. These functions should call
-- 
2.8.1
Duy Nguyen· Jul 10, 2016, 15:09 UTC · re: Michael Haggerty · lore

Re: [PATCH 00/38] Virtualization of the refs API

On Fri, Jun 3, 2016 at 11:03 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
Show 16 quoted lines
> Since the that ref-iterator [1] changes seem to have gotten a positive
> reception, let's try to keep up the momentum. I hope I'm not
> overloading the review pipeline...
>
> I think all of the groundwork is in place now to virtualize the refs
> API. This will open the way to storing refs in ways other than the
> familiar loose refs / packed refs format, such as David Turner's
> proposed LMDB-based storage [2].
>
> This is a long patch series, but most of the patches are pretty simple
> and formulaic. The goal is to implement a `ref_store`. In the language
> of object-oriented programming, `ref_store` is an abstract base class
> representing a reference storage backend. It provides methods to read,
> write, and delete references and symrefs, and to iterate over
> references, reflogs, and reflog entries, plus a number of other
> things—19 methods in all.

I probably don't know what I'm talking about because I don't follow your work closely enough. Please ignore if this is nonsense. But if we extend/change API, we might need to update git-for-each-ref too, to expose it to shell scripts and external commands. I guess for iteration there's nothing else more needed, but we may need to introduction new options for the storage thing, e.g. to select storage...

-- 
Duy
Michael Haggerty· Jul 13, 2016, 15:26 UTC · re: Duy Nguyen · lore

Re: [PATCH 00/38] Virtualization of the refs API

On 07/10/2016 05:09 PM, Duy Nguyen wrote:
Show 25 quoted lines
> On Fri, Jun 3, 2016 at 11:03 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
>> Since the that ref-iterator [1] changes seem to have gotten a positive
>> reception, let's try to keep up the momentum. I hope I'm not
>> overloading the review pipeline...
>>
>> I think all of the groundwork is in place now to virtualize the refs
>> API. This will open the way to storing refs in ways other than the
>> familiar loose refs / packed refs format, such as David Turner's
>> proposed LMDB-based storage [2].
>>
>> This is a long patch series, but most of the patches are pretty simple
>> and formulaic. The goal is to implement a `ref_store`. In the language
>> of object-oriented programming, `ref_store` is an abstract base class
>> representing a reference storage backend. It provides methods to read,
>> write, and delete references and symrefs, and to iterate over
>> references, reflogs, and reflog entries, plus a number of other
>> things—19 methods in all.
> 
> I probably don't know what I'm talking about because I don't follow
> your work closely enough. Please ignore if this is nonsense. But if we
> extend/change API, we might need to update git-for-each-ref too, to
> expose it to shell scripts and external commands. I guess for
> iteration there's nothing else more needed, but we may need to
> introduction new options for the storage thing, e.g. to select
> storage...

This patch series doesn't change the external API in any significant way. It only wraps it up on a virtualization layer so that a different reference storage backends can be plugged in.

So as long as people are using plumbing commands to work with references (rather than reading/writing files under $GIT_DIR directly), they should notice no difference.

There are only two exceptions that I know of:
1. Users will need to be able to request that a repository use a
non-default reference backend, and (less importantly) inquire about
which reference backend a particular repository is using. Those
facilities will be added when the first non-files reference backend is
added.
2. At least one command (`git pack-refs`) is particular to the files
backend, and won't be needed (at least not in its current form) for
other backends. Conversely, it is conceivable that future reference
backends will require their own "maintenance" commands. Such commands
would be added as they are needed.

If there were operations that other reference backends could do much more efficiently than the files backend (like, hypothetically, return all references matching a regular expression without having to iterate through all references), then it might make sense for performance reasons to provide commands to access that functionality. But at the moment I don't know of any such cases.

Michael

← back to recent threads