{"thread":{"id":"28785","subject":"[PATCH 07/28] sort_ref_dir(): do not sort if already sorted","startedAt":"2011-10-28T12:28:13Z","lastAt":"2011-11-16T12:51:04Z","messageCount":36,"participants":["mhagger@alum.mit.edu","Ramkumar Ramachandra","Michael Haggerty","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":28},"messages":[{"id":"178440","messageId":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":null,"subject":"[PATCH 00/28] Store references hierarchically in cache","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:13Z","receivedAt":"2011-10-28T12:28:13Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nThis patch series applies on top of v3 of \"Use refs API more\nconsistently\", which ultimately branches from gitster/master.\n\nThis patch series changes how references are stored in the reference\ncache data structures (entirely internal to refs.c).  Previously, the\npacked refs were stored in one big array of pointers-to-struct, and\nthe loose refs were stored in another.  This had a few problems:\n\n* Whenever any loose refs were needed, the whole refs tree had to be\n  read from disk into memory.  This can be quite expensive when there\n  are a lot of loose references.  And it is often overkill, as\n  frequently only a small part of the refs tree is needed.  For\n  example:\n\n  * refs/replace is almost *always* needed even though it often\n    doesn't even exist.  Thus the presence of many loose references\n    slows down *many* git commands for no reason whatsoever.\n\n  * When a new reference is created, is_refname_available() is called\n    to see whether there is another another reference whose name\n    conflicts with the new one.  Currently this loads and iterates\n    through *all* references.  But there are only a few refnames that\n    can possibly conflict; for example, given the refname\n    \"refs/heads/foo/bar\", the only possible conflicts are with\n    \"refs/heads/foo\" and \"refs/heads/foo/bar/*\".  Therefore it is\n    silly to load and iterate through the whole refname hierarchy.\n\n  * \"git for-each-ref\" is capable of searching a subtree of the\n    references.  But currently this causes all references to be\n    loaded.\n\nTherefore, this patch series changes the data structure used to store\nthe reference cache from a single array of pointers-to-struct into a\ntree-like structure in which each subdirectory of the reference\nnamespace is stored as an array of pointers-to-entry and entries can\nbe either references or subdirectories containing more references.\nMoreover, each subdirectory of loose references is only read from disk\nwhen a reference from that subdirectory (or one of its descendants) is\nneeded.  This slightly slows down commands that need to iterate\nthrough all references (simply because the new data structures are\nmore complicated), but it *dramatically* decreases the time needed for\nsome common operations.  For example, in a test repository with 20000\nrevisions and 10000 loose tags:\n\n* the time to create a new branch goes from 180 ms to less than 10 ms\n  (my test resolution only includes two decimal places) and the time\n  to checkout a new branch does the same.\n\n* the time for a \"git filter-branch\" of all commits (which used to\n  scale like N^2) goes from 4 hours to 13 minutes.  (Since\n  filter-branch necessarily *creates* lots of loose references, the\n  savings are also there if the references are originally packed.)\n\nThe efficiency gains are such that some operations are now faster with\nloose references than with packed references; however, some operations\nwith packed references slow down a bit.\n\nThese changes do not increase the amount of space per reference needed\nfor the reference cache, but they do add one similarly-sized entry for\neach subdirectory (for each of loose and packed).  I don't think that\nthe space increase should be significant in any reasonable situation.\n\nAfter these changes, there is a benefit to sharding the reference\nnamespace, especially for loose references.\n\n\nThe patches:\n\nThe first few patches are just preparation.\n\nPatch 6 \"refs: store references hierarchically\" introduces the big\ndata structure change.  It causes much extra sorting to be done and\ntherefore slows down performance significantly, but the following two\ncommits restore performance to approximately the old level.\n\nPatches 11-24 change most of the internal functions to work with\n\"struct ref_entry *\" (namely the kind of ref_entry that holds a\ndirectory of references) instead of \"struct ref_dir *\".  The reason\nfor this change it to allow these functions access to the \"flag\" and\n\"name\" fields that are stored in ref_entry and thereby avoid having to\nstore redundant information in \"struct ref_dir\" (which would increase\nthe size of *every* ref_entry because of its presence in the union).\n\nThe other patches (9-10 and 25-28) reap the benefits of the new data\nstructure:\n\n* do_for_each_ref() can iterate only over the desired subtree instead\n  of iterating over all references and filtering out the unwanted ones\n\n* loose references can be read on demand, one directory at a time\n\n* is_refname_available() can query only the possibly-conflicting parts\n  of the reference namespace\n\nMichael Haggerty (28):\n  refs.c: reorder definitions more logically\n  free_ref_entry(): new function\n  check_refname_component(): return 0 for zero-length components\n  struct ref_entry: nest the value part in a union\n  refs.c: rename ref_array -> ref_dir\n  refs: store references hierarchically\n  sort_ref_dir(): do not sort if already sorted\n  refs: sort ref_dirs lazily\n  do_for_each_ref(): only iterate over the subtree that was requested\n  get_ref_dir(): keep track of the current ref_dir\n  refs: wrap top-level ref_dirs in ref_entries\n  get_packed_refs(): return (ref_entry *) instead of (ref_dir *)\n  get_loose_refs(): return (ref_entry *) instead of (ref_dir *)\n  is_refname_available(): take (ref_entry *) instead of (ref_dir *)\n  find_ref(): take (ref_entry *) instead of (ref_dir *)\n  read_packed_refs(): take (ref_entry *) instead of (ref_dir *)\n  add_ref(): take (ref_entry *) instead of (ref_dir *)\n  find_containing_direntry(): use (ref_entry *) instead of (ref_dir *)\n  search_ref_dir(): take (ref_entry *) instead of (ref_dir *)\n  add_entry(): take (ref_entry *) instead of (ref_dir *)\n  do_for_each_ref_in_dir*(): take (ref_entry *) instead of (ref_dir *)\n  sort_ref_dir(): take (ref_entry *) instead of (ref_dir *)\n  struct ref_dir: store a reference to the enclosing ref_cache\n  read_loose_refs(): take a (ref_entry *) as argument\n  refs: read loose references lazily\n  is_refname_available(): query only possibly-conflicting references\n  read_packed_refs(): keep track of the directory being worked in\n  repack_without_ref(): call clear_packed_ref_cache()\n\n refs.c | 1268 ++++++++++++++++++++++++++++++++++++++++++---------------------\n refs.h |    7 +-\n 2 files changed, 850 insertions(+), 425 deletions(-)\n\n-- \n1.7.7\n"},{"id":"178436","messageId":"1319804921-17545-2-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 01/28] refs.c: reorder definitions more logically","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:14Z","receivedAt":"2011-10-28T12:28:14Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nReorder definitions in file: first check_refname_format() and helper\nfunctions, then the functions for managing the ref_entry and ref_array\ndata structures, then ref_cache, then the more \"business-logicky\"\nstuff.  No code is changed.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |  669 ++++++++++++++++++++++++++++++++--------------------------------\n 1 files changed, 336 insertions(+), 333 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 96466f3..56868a5 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -4,6 +4,108 @@\n #include \"tag.h\"\n #include \"dir.h\"\n \n+/*\n+ * Make sure \"ref\" is something reasonable to have under \".git/refs/\";\n+ * We do not like it if:\n+ *\n+ * - any path component of it begins with \".\", or\n+ * - it has double dots \"..\", or\n+ * - it has ASCII control character, \"~\", \"^\", \":\" or SP, anywhere, or\n+ * - it ends with a \"/\".\n+ * - it ends with \".lock\"\n+ * - it contains a \"\\\" (backslash)\n+ */\n+\n+/* Return true iff ch is not allowed in reference names. */\n+static inline int bad_ref_char(int ch)\n+{\n+\tif (((unsigned) ch) <= ' ' || ch == 0x7f ||\n+\t    ch == '~' || ch == '^' || ch == ':' || ch == '\\\\')\n+\t\treturn 1;\n+\t/* 2.13 Pattern Matching Notation */\n+\tif (ch == '*' || ch == '?' || ch == '[') /* Unsupported */\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+/*\n+ * Try to read one refname component from the front of refname.  Return\n+ * the length of the component found, or -1 if the component is not\n+ * legal.\n+ */\n+static int check_refname_component(const char *refname, int flags)\n+{\n+\tconst char *cp;\n+\tchar last = '\\0';\n+\n+\tfor (cp = refname; ; cp++) {\n+\t\tchar ch = *cp;\n+\t\tif (ch == '\\0' || ch == '/')\n+\t\t\tbreak;\n+\t\tif (bad_ref_char(ch))\n+\t\t\treturn -1; /* Illegal character in refname. */\n+\t\tif (last == '.' && ch == '.')\n+\t\t\treturn -1; /* Refname contains \"..\". */\n+\t\tif (last == '@' && ch == '{')\n+\t\t\treturn -1; /* Refname contains \"@{\". */\n+\t\tlast = ch;\n+\t}\n+\tif (cp == refname)\n+\t\treturn -1; /* Component has zero length. */\n+\tif (refname[0] == '.') {\n+\t\tif (!(flags & REFNAME_DOT_COMPONENT))\n+\t\t\treturn -1; /* Component starts with '.'. */\n+\t\t/*\n+\t\t * Even if leading dots are allowed, don't allow \".\"\n+\t\t * as a component (\"..\" is prevented by a rule above).\n+\t\t */\n+\t\tif (refname[1] == '\\0')\n+\t\t\treturn -1; /* Component equals \".\". */\n+\t}\n+\tif (cp - refname >= 5 && !memcmp(cp - 5, \".lock\", 5))\n+\t\treturn -1; /* Refname ends with \".lock\". */\n+\treturn cp - refname;\n+}\n+\n+int check_refname_format(const char *refname, int flags)\n+{\n+\tint component_len, component_count = 0;\n+\n+\twhile (1) {\n+\t\t/* We are at the start of a path component. */\n+\t\tcomponent_len = check_refname_component(refname, flags);\n+\t\tif (component_len < 0) {\n+\t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n+\t\t\t\t\trefname[0] == '*' &&\n+\t\t\t\t\t(refname[1] == '\\0' || refname[1] == '/')) {\n+\t\t\t\t/* Accept one wildcard as a full refname component. */\n+\t\t\t\tflags &= ~REFNAME_REFSPEC_PATTERN;\n+\t\t\t\tcomponent_len = 1;\n+\t\t\t} else {\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t\tcomponent_count++;\n+\t\tif (refname[component_len] == '\\0')\n+\t\t\tbreak;\n+\t\t/* Skip to next component. */\n+\t\trefname += component_len + 1;\n+\t}\n+\n+\tif (refname[component_len - 1] == '.')\n+\t\treturn -1; /* Refname ends with '.'. */\n+\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n+\t\treturn -1; /* Refname has only one component. */\n+\treturn 0;\n+}\n+\n+struct ref_entry;\n+\n+struct ref_array {\n+\tint nr, alloc;\n+\tstruct ref_entry **refs;\n+};\n+\n /* ISSYMREF=0x01, ISPACKED=0x02 and ISBROKEN=0x04 are public interfaces */\n #define REF_KNOWS_PEELED 0x10\n \n@@ -15,47 +117,6 @@ struct ref_entry {\n \tchar name[FLEX_ARRAY];\n };\n \n-struct ref_array {\n-\tint nr, alloc;\n-\tstruct ref_entry **refs;\n-};\n-\n-/*\n- * Parse one line from a packed-refs file.  Write the SHA1 to sha1.\n- * Return a pointer to the refname within the line (null-terminated),\n- * or NULL if there was a problem.\n- */\n-static const char *parse_ref_line(char *line, unsigned char *sha1)\n-{\n-\t/*\n-\t * 42: the answer to everything.\n-\t *\n-\t * In this case, it happens to be the answer to\n-\t *  40 (length of sha1 hex representation)\n-\t *  +1 (space in between hex and name)\n-\t *  +1 (newline at the end of the line)\n-\t */\n-\tint len = strlen(line) - 42;\n-\n-\tif (len <= 0)\n-\t\treturn NULL;\n-\tif (get_sha1_hex(line, sha1) < 0)\n-\t\treturn NULL;\n-\tif (!isspace(line[40]))\n-\t\treturn NULL;\n-\tline += 41;\n-\tif (isspace(*line))\n-\t\treturn NULL;\n-\tif (line[len] != '\\n')\n-\t\treturn NULL;\n-\tline[len] = 0;\n-\n-\tif (check_refname_format(line, REFNAME_ALLOW_ONELEVEL))\n-\t\treturn NULL;\n-\n-\treturn line;\n-}\n-\n static struct ref_entry *create_ref_entry(const char *refname,\n \t\t\t\t\t  const unsigned char *sha1, int flag)\n {\n@@ -80,6 +141,16 @@ static void add_ref(struct ref_array *refs, struct ref_entry *ref)\n \trefs->refs[refs->nr++] = ref;\n }\n \n+static void clear_ref_array(struct ref_array *array)\n+{\n+\tint i;\n+\tfor (i = 0; i < array->nr; i++)\n+\t\tfree(array->refs[i]);\n+\tfree(array->refs);\n+\tarray->nr = array->alloc = 0;\n+\tarray->refs = NULL;\n+}\n+\n static int ref_entry_cmp(const void *a, const void *b)\n {\n \tstruct ref_entry *one = *(struct ref_entry **)a;\n@@ -87,6 +158,31 @@ static int ref_entry_cmp(const void *a, const void *b)\n \treturn strcmp(one->name, two->name);\n }\n \n+static struct ref_entry *search_ref_array(struct ref_array *array, const char *refname)\n+{\n+\tstruct ref_entry *e, **r;\n+\tint len;\n+\n+\tif (refname == NULL)\n+\t\treturn NULL;\n+\n+\tif (!array->nr)\n+\t\treturn NULL;\n+\n+\tlen = strlen(refname) + 1;\n+\te = xmalloc(sizeof(struct ref_entry) + len);\n+\tmemcpy(e->name, refname, len);\n+\n+\tr = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);\n+\n+\tfree(e);\n+\n+\tif (r == NULL)\n+\t\treturn NULL;\n+\n+\treturn *r;\n+}\n+\n /*\n  * Emit a warning and return true iff ref1 and ref2 have the same name\n  * and the same sha1.  Die if they have the same name but different\n@@ -130,29 +226,137 @@ static void sort_ref_array(struct ref_array *array)\n \tarray->nr = i + 1;\n }\n \n-static struct ref_entry *search_ref_array(struct ref_array *array, const char *refname)\n+#define DO_FOR_EACH_INCLUDE_BROKEN 01\n+\n+static struct ref_entry *current_ref;\n+\n+static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n+\t\t      int flags, void *cb_data, struct ref_entry *entry)\n {\n-\tstruct ref_entry *e, **r;\n-\tint len;\n+\tif (prefixcmp(entry->name, base))\n+\t\treturn 0;\n \n-\tif (refname == NULL)\n-\t\treturn NULL;\n+\tif (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {\n+\t\tif (entry->flag & REF_ISBROKEN)\n+\t\t\treturn 0; /* ignore broken refs e.g. dangling symref */\n+\t\tif (!has_sha1_file(entry->sha1)) {\n+\t\t\terror(\"%s does not point to a valid object!\", entry->name);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\tcurrent_ref = entry;\n+\treturn fn(entry->name + trim, entry->sha1, entry->flag, cb_data);\n+}\n \n-\tif (!array->nr)\n-\t\treturn NULL;\n+static int do_for_each_ref_in_array(struct ref_array *array, int offset,\n+\t\t\t\t    const char *base,\n+\t\t\t\t    each_ref_fn fn, int trim, int flags, void *cb_data)\n+{\n+\tint i;\n+\tfor (i = offset; i < array->nr; i++) {\n+\t\tint retval = do_one_ref(base, fn, trim, flags, cb_data, array->refs[i]);\n+\t\tif (retval)\n+\t\t\treturn retval;\n+\t}\n+\treturn 0;\n+}\n \n-\tlen = strlen(refname) + 1;\n-\te = xmalloc(sizeof(struct ref_entry) + len);\n-\tmemcpy(e->name, refname, len);\n+static int do_for_each_ref_in_arrays(struct ref_array *array1,\n+\t\t\t\t     struct ref_array *array2,\n+\t\t\t\t     const char *base, each_ref_fn fn, int trim,\n+\t\t\t\t     int flags, void *cb_data)\n+{\n+\tint retval;\n+\tint i1 = 0, i2 = 0;\n \n-\tr = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);\n+\twhile (1) {\n+\t\tstruct ref_entry *e1, *e2;\n+\t\tint cmp;\n+\t\tif (i1 == array1->nr) {\n+\t\t\treturn do_for_each_ref_in_array(array2, i2,\n+\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\t}\n+\t\tif (i2 == array2->nr) {\n+\t\t\treturn do_for_each_ref_in_array(array1, i1,\n+\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\t}\n+\t\te1 = array1->refs[i1];\n+\t\te2 = array2->refs[i2];\n+\t\tcmp = strcmp(e1->name, e2->name);\n+\t\tif (cmp == 0) {\n+\t\t\t/* Two refs with the same name; ignore the one from array1. */\n+\t\t\ti1++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (cmp < 0) {\n+\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e1);\n+\t\t\ti1++;\n+\t\t} else {\n+\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e2);\n+\t\t\ti2++;\n+\t\t}\n+\t\tif (retval)\n+\t\t\treturn retval;\n+\t}\n+}\n \n-\tfree(e);\n+/*\n+ * Return true iff refname1 and refname2 conflict with each other.\n+ * Two reference names conflict if one of them exactly matches the\n+ * leading components of the other; e.g., \"foo/bar\" conflicts with\n+ * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n+ * \"foo/barbados\".\n+ */\n+static int names_conflict(const char *refname1, const char *refname2)\n+{\n+\tfor (; *refname1 && *refname1 == *refname2; refname1++, refname2++)\n+\t\t;\n+\treturn (*refname1 == '\\0' && *refname2 == '/')\n+\t\t|| (*refname1 == '/' && *refname2 == '\\0');\n+}\n \n-\tif (r == NULL)\n-\t\treturn NULL;\n+struct name_conflict_cb {\n+\tconst char *refname;\n+\tconst char *oldrefname;\n+\tconst char *conflicting_refname;\n+};\n \n-\treturn *r;\n+static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,\n+\t\t\t    int flags, void *cb_data)\n+{\n+\tstruct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;\n+\tif (data->oldrefname && !strcmp(data->oldrefname, existingrefname))\n+\t\treturn 0;\n+\tif (names_conflict(data->refname, existingrefname)) {\n+\t\tdata->conflicting_refname = existingrefname;\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Return true iff a reference named refname could be created without\n+ * conflicting with the name of an existing reference.  If oldrefname\n+ * is non-NULL, ignore potential conflicts with oldrefname (e.g.,\n+ * because oldrefname is scheduled for deletion in the same\n+ * operation).\n+ */\n+static int is_refname_available(const char *refname, const char *oldrefname,\n+\t\t\t\tstruct ref_array *array)\n+{\n+\tstruct name_conflict_cb data;\n+\tdata.refname = refname;\n+\tdata.oldrefname = oldrefname;\n+\tdata.conflicting_refname = NULL;\n+\n+\tif (do_for_each_ref_in_array(array, 0, \"\", name_conflict_fn,\n+\t\t\t\t     0, DO_FOR_EACH_INCLUDE_BROKEN,\n+\t\t\t\t     &data)) {\n+\t\terror(\"'%s' exists; cannot create '%s'\",\n+\t\t      data.conflicting_refname, refname);\n+\t\treturn 0;\n+\t}\n+\treturn 1;\n }\n \n /*\n@@ -169,20 +373,8 @@ static struct ref_cache {\n \tchar name[FLEX_ARRAY];\n } *ref_cache;\n \n-static struct ref_entry *current_ref;\n-\n static struct ref_array extra_refs;\n \n-static void clear_ref_array(struct ref_array *array)\n-{\n-\tint i;\n-\tfor (i = 0; i < array->nr; i++)\n-\t\tfree(array->refs[i]);\n-\tfree(array->refs);\n-\tarray->nr = array->alloc = 0;\n-\tarray->refs = NULL;\n-}\n-\n static void clear_packed_ref_cache(struct ref_cache *refs)\n {\n \tif (refs->did_packed)\n@@ -239,6 +431,42 @@ void invalidate_ref_cache(const char *submodule)\n \tclear_loose_ref_cache(refs);\n }\n \n+/*\n+ * Parse one line from a packed-refs file.  Write the SHA1 to sha1.\n+ * Return a pointer to the refname within the line (null-terminated),\n+ * or NULL if there was a problem.\n+ */\n+static const char *parse_ref_line(char *line, unsigned char *sha1)\n+{\n+\t/*\n+\t * 42: the answer to everything.\n+\t *\n+\t * In this case, it happens to be the answer to\n+\t *  40 (length of sha1 hex representation)\n+\t *  +1 (space in between hex and name)\n+\t *  +1 (newline at the end of the line)\n+\t */\n+\tint len = strlen(line) - 42;\n+\n+\tif (len <= 0)\n+\t\treturn NULL;\n+\tif (get_sha1_hex(line, sha1) < 0)\n+\t\treturn NULL;\n+\tif (!isspace(line[40]))\n+\t\treturn NULL;\n+\tline += 41;\n+\tif (isspace(*line))\n+\t\treturn NULL;\n+\tif (line[len] != '\\n')\n+\t\treturn NULL;\n+\tline[len] = 0;\n+\n+\tif (check_refname_format(line, REFNAME_ALLOW_ONELEVEL))\n+\t\treturn NULL;\n+\n+\treturn line;\n+}\n+\n static void read_packed_refs(FILE *f, struct ref_array *array)\n {\n \tstruct ref_entry *last = NULL;\n@@ -355,53 +583,19 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag = 0;\n \t\t\t\tif (resolve_gitlink_ref(refs->name, refname, sha1) < 0) {\n-\t\t\t\t\thashclr(sha1);\n-\t\t\t\t\tflag |= REF_ISBROKEN;\n-\t\t\t\t}\n-\t\t\t} else\n-\t\t\t\tif (!resolve_ref(refname, sha1, 1, &flag)) {\n-\t\t\t\t\thashclr(sha1);\n-\t\t\t\t\tflag |= REF_ISBROKEN;\n-\t\t\t\t}\n-\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag));\n-\t\t}\n-\t\tfree(refname);\n-\t\tclosedir(dir);\n-\t}\n-}\n-\n-struct warn_if_dangling_data {\n-\tFILE *fp;\n-\tconst char *refname;\n-\tconst char *msg_fmt;\n-};\n-\n-static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,\n-\t\t\t\t   int flags, void *cb_data)\n-{\n-\tstruct warn_if_dangling_data *d = cb_data;\n-\tconst char *resolves_to;\n-\tunsigned char junk[20];\n-\n-\tif (!(flags & REF_ISSYMREF))\n-\t\treturn 0;\n-\n-\tresolves_to = resolve_ref(refname, junk, 0, NULL);\n-\tif (!resolves_to || strcmp(resolves_to, d->refname))\n-\t\treturn 0;\n-\n-\tfprintf(d->fp, d->msg_fmt, refname);\n-\treturn 0;\n-}\n-\n-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)\n-{\n-\tstruct warn_if_dangling_data data;\n-\n-\tdata.fp = fp;\n-\tdata.refname = refname;\n-\tdata.msg_fmt = msg_fmt;\n-\tfor_each_rawref(warn_if_dangling_symref, &data);\n+\t\t\t\t\thashclr(sha1);\n+\t\t\t\t\tflag |= REF_ISBROKEN;\n+\t\t\t\t}\n+\t\t\t} else\n+\t\t\t\tif (!resolve_ref(refname, sha1, 1, &flag)) {\n+\t\t\t\t\thashclr(sha1);\n+\t\t\t\t\tflag |= REF_ISBROKEN;\n+\t\t\t\t}\n+\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag));\n+\t\t}\n+\t\tfree(refname);\n+\t\tclosedir(dir);\n+\t}\n }\n \n static struct ref_array *get_loose_refs(struct ref_cache *refs)\n@@ -631,27 +825,8 @@ int read_ref(const char *refname, unsigned char *sha1)\n \treturn -1;\n }\n \n-#define DO_FOR_EACH_INCLUDE_BROKEN 01\n-static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n-\t\t      int flags, void *cb_data, struct ref_entry *entry)\n-{\n-\tif (prefixcmp(entry->name, base))\n-\t\treturn 0;\n-\n-\tif (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {\n-\t\tif (entry->flag & REF_ISBROKEN)\n-\t\t\treturn 0; /* ignore broken refs e.g. dangling symref */\n-\t\tif (!has_sha1_file(entry->sha1)) {\n-\t\t\terror(\"%s does not point to a valid object!\", entry->name);\n-\t\t\treturn 0;\n-\t\t}\n-\t}\n-\tcurrent_ref = entry;\n-\treturn fn(entry->name + trim, entry->sha1, entry->flag, cb_data);\n-}\n-\n static int filter_refs(const char *refname, const unsigned char *sha, int flags,\n-\tvoid *data)\n+\t\t       void *data)\n {\n \tstruct ref_filter *filter = (struct ref_filter *)data;\n \tif (fnmatch(filter->pattern, refname, 0))\n@@ -700,56 +875,38 @@ fallback:\n \treturn -1;\n }\n \n-static int do_for_each_ref_in_array(struct ref_array *array, int offset,\n-\t\t\t\t    const char *base,\n-\t\t\t\t    each_ref_fn fn, int trim, int flags, void *cb_data)\n+struct warn_if_dangling_data {\n+\tFILE *fp;\n+\tconst char *refname;\n+\tconst char *msg_fmt;\n+};\n+\n+static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,\n+\t\t\t\t   int flags, void *cb_data)\n {\n-\tint i;\n-\tfor (i = offset; i < array->nr; i++) {\n-\t\tint retval = do_one_ref(base, fn, trim, flags, cb_data, array->refs[i]);\n-\t\tif (retval)\n-\t\t\treturn retval;\n-\t}\n+\tstruct warn_if_dangling_data *d = cb_data;\n+\tconst char *resolves_to;\n+\tunsigned char junk[20];\n+\n+\tif (!(flags & REF_ISSYMREF))\n+\t\treturn 0;\n+\n+\tresolves_to = resolve_ref(refname, junk, 0, NULL);\n+\tif (!resolves_to || strcmp(resolves_to, d->refname))\n+\t\treturn 0;\n+\n+\tfprintf(d->fp, d->msg_fmt, refname);\n \treturn 0;\n }\n \n-static int do_for_each_ref_in_arrays(struct ref_array *array1,\n-\t\t\t\t     struct ref_array *array2,\n-\t\t\t\t     const char *base, each_ref_fn fn, int trim,\n-\t\t\t\t     int flags, void *cb_data)\n+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)\n {\n-\tint retval;\n-\tint i1 = 0, i2 = 0;\n+\tstruct warn_if_dangling_data data;\n \n-\twhile (1) {\n-\t\tstruct ref_entry *e1, *e2;\n-\t\tint cmp;\n-\t\tif (i1 == array1->nr) {\n-\t\t\treturn do_for_each_ref_in_array(array2, i2,\n-\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n-\t\t}\n-\t\tif (i2 == array2->nr) {\n-\t\t\treturn do_for_each_ref_in_array(array1, i1,\n-\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n-\t\t}\n-\t\te1 = array1->refs[i1];\n-\t\te2 = array2->refs[i2];\n-\t\tcmp = strcmp(e1->name, e2->name);\n-\t\tif (cmp == 0) {\n-\t\t\t/* Two refs with the same name; ignore the one from array1. */\n-\t\t\ti1++;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (cmp < 0) {\n-\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e1);\n-\t\t\ti1++;\n-\t\t} else {\n-\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e2);\n-\t\t\ti2++;\n-\t\t}\n-\t\tif (retval)\n-\t\t\treturn retval;\n-\t}\n+\tdata.fp = fp;\n+\tdata.refname = refname;\n+\tdata.msg_fmt = msg_fmt;\n+\tfor_each_rawref(warn_if_dangling_symref, &data);\n }\n \n static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,\n@@ -920,101 +1077,6 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)\n \t\t\t       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);\n }\n \n-/*\n- * Make sure \"ref\" is something reasonable to have under \".git/refs/\";\n- * We do not like it if:\n- *\n- * - any path component of it begins with \".\", or\n- * - it has double dots \"..\", or\n- * - it has ASCII control character, \"~\", \"^\", \":\" or SP, anywhere, or\n- * - it ends with a \"/\".\n- * - it ends with \".lock\"\n- * - it contains a \"\\\" (backslash)\n- */\n-\n-/* Return true iff ch is not allowed in reference names. */\n-static inline int bad_ref_char(int ch)\n-{\n-\tif (((unsigned) ch) <= ' ' || ch == 0x7f ||\n-\t    ch == '~' || ch == '^' || ch == ':' || ch == '\\\\')\n-\t\treturn 1;\n-\t/* 2.13 Pattern Matching Notation */\n-\tif (ch == '*' || ch == '?' || ch == '[') /* Unsupported */\n-\t\treturn 1;\n-\treturn 0;\n-}\n-\n-/*\n- * Try to read one refname component from the front of refname.  Return\n- * the length of the component found, or -1 if the component is not\n- * legal.\n- */\n-static int check_refname_component(const char *refname, int flags)\n-{\n-\tconst char *cp;\n-\tchar last = '\\0';\n-\n-\tfor (cp = refname; ; cp++) {\n-\t\tchar ch = *cp;\n-\t\tif (ch == '\\0' || ch == '/')\n-\t\t\tbreak;\n-\t\tif (bad_ref_char(ch))\n-\t\t\treturn -1; /* Illegal character in refname. */\n-\t\tif (last == '.' && ch == '.')\n-\t\t\treturn -1; /* Refname contains \"..\". */\n-\t\tif (last == '@' && ch == '{')\n-\t\t\treturn -1; /* Refname contains \"@{\". */\n-\t\tlast = ch;\n-\t}\n-\tif (cp == refname)\n-\t\treturn -1; /* Component has zero length. */\n-\tif (refname[0] == '.') {\n-\t\tif (!(flags & REFNAME_DOT_COMPONENT))\n-\t\t\treturn -1; /* Component starts with '.'. */\n-\t\t/*\n-\t\t * Even if leading dots are allowed, don't allow \".\"\n-\t\t * as a component (\"..\" is prevented by a rule above).\n-\t\t */\n-\t\tif (refname[1] == '\\0')\n-\t\t\treturn -1; /* Component equals \".\". */\n-\t}\n-\tif (cp - refname >= 5 && !memcmp(cp - 5, \".lock\", 5))\n-\t\treturn -1; /* Refname ends with \".lock\". */\n-\treturn cp - refname;\n-}\n-\n-int check_refname_format(const char *refname, int flags)\n-{\n-\tint component_len, component_count = 0;\n-\n-\twhile (1) {\n-\t\t/* We are at the start of a path component. */\n-\t\tcomponent_len = check_refname_component(refname, flags);\n-\t\tif (component_len < 0) {\n-\t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n-\t\t\t\t\trefname[0] == '*' &&\n-\t\t\t\t\t(refname[1] == '\\0' || refname[1] == '/')) {\n-\t\t\t\t/* Accept one wildcard as a full refname component. */\n-\t\t\t\tflags &= ~REFNAME_REFSPEC_PATTERN;\n-\t\t\t\tcomponent_len = 1;\n-\t\t\t} else {\n-\t\t\t\treturn -1;\n-\t\t\t}\n-\t\t}\n-\t\tcomponent_count++;\n-\t\tif (refname[component_len] == '\\0')\n-\t\t\tbreak;\n-\t\t/* Skip to next component. */\n-\t\trefname += component_len + 1;\n-\t}\n-\n-\tif (refname[component_len - 1] == '.')\n-\t\treturn -1; /* Refname ends with '.'. */\n-\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n-\t\treturn -1; /* Refname has only one component. */\n-\treturn 0;\n-}\n-\n const char *prettify_refname(const char *name)\n {\n \treturn name + (\n@@ -1092,65 +1154,6 @@ static int remove_empty_directories(const char *file)\n }\n \n /*\n- * Return true iff refname1 and refname2 conflict with each other.\n- * Two reference names conflict if one of them exactly matches the\n- * leading components of the other; e.g., \"foo/bar\" conflicts with\n- * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n- * \"foo/barbados\".\n- */\n-static int names_conflict(const char *refname1, const char *refname2)\n-{\n-\tfor (; *refname1 && *refname1 == *refname2; refname1++, refname2++)\n-\t\t;\n-\treturn (*refname1 == '\\0' && *refname2 == '/')\n-\t\t|| (*refname1 == '/' && *refname2 == '\\0');\n-}\n-\n-struct name_conflict_cb {\n-\tconst char *refname;\n-\tconst char *oldrefname;\n-\tconst char *conflicting_refname;\n-};\n-\n-static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,\n-\t\t\t    int flags, void *cb_data)\n-{\n-\tstruct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;\n-\tif (data->oldrefname && !strcmp(data->oldrefname, existingrefname))\n-\t\treturn 0;\n-\tif (names_conflict(data->refname, existingrefname)) {\n-\t\tdata->conflicting_refname = existingrefname;\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n-}\n-\n-/*\n- * Return true iff a reference named refname could be created without\n- * conflicting with the name of an existing reference.  If oldrefname\n- * is non-NULL, ignore potential conflicts with oldrefname (e.g.,\n- * because oldrefname is scheduled for deletion in the same\n- * operation).\n- */\n-static int is_refname_available(const char *refname, const char *oldrefname,\n-\t\t\t\tstruct ref_array *array)\n-{\n-\tstruct name_conflict_cb data;\n-\tdata.refname = refname;\n-\tdata.oldrefname = oldrefname;\n-\tdata.conflicting_refname = NULL;\n-\n-\tif (do_for_each_ref_in_array(array, 0, \"\", name_conflict_fn,\n-\t\t\t\t     0, DO_FOR_EACH_INCLUDE_BROKEN,\n-\t\t\t\t     &data)) {\n-\t\terror(\"'%s' exists; cannot create '%s'\",\n-\t\t      data.conflicting_refname, refname);\n-\t\treturn 0;\n-\t}\n-\treturn 1;\n-}\n-\n-/*\n  * *string and *len will only be substituted, and *string returned (for\n  * later free()ing) if the string passed in is a magic short-hand form\n  * to name a branch.\n-- \n1.7.7\n"},{"id":"178439","messageId":"1319804921-17545-3-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 02/28] free_ref_entry(): new function","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:15Z","receivedAt":"2011-10-28T12:28:15Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nAdd a function free_ref_entry().  This function will become nontrivial\nwhen ref_entry (soon) becomes polymorphic.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    9 +++++++--\n 1 files changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 56868a5..eff1abe 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -134,6 +134,11 @@ static struct ref_entry *create_ref_entry(const char *refname,\n \treturn ref;\n }\n \n+static void free_ref_entry(struct ref_entry *entry)\n+{\n+\tfree(entry);\n+}\n+\n /* Add a ref_entry to the end of the ref_array (unsorted). */\n static void add_ref(struct ref_array *refs, struct ref_entry *ref)\n {\n@@ -145,7 +150,7 @@ static void clear_ref_array(struct ref_array *array)\n {\n \tint i;\n \tfor (i = 0; i < array->nr; i++)\n-\t\tfree(array->refs[i]);\n+\t\tfree_ref_entry(array->refs[i]);\n \tfree(array->refs);\n \tarray->nr = array->alloc = 0;\n \tarray->refs = NULL;\n@@ -217,7 +222,7 @@ static void sort_ref_array(struct ref_array *array)\n \t\tstruct ref_entry *a = array->refs[i];\n \t\tstruct ref_entry *b = array->refs[j];\n \t\tif (is_dup_ref(a, b)) {\n-\t\t\tfree(b);\n+\t\t\tfree_ref_entry(b);\n \t\t\tcontinue;\n \t\t}\n \t\ti++;\n-- \n1.7.7\n"},{"id":"178433","messageId":"1319804921-17545-4-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 03/28] check_refname_component(): return 0 for zero-length components","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:16Z","receivedAt":"2011-10-28T12:28:16Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nReturn 0 (instead of -1) for zero-length components.  Move the\ninterpretation of zero-length components as illegal to\ncheck_refname_format().\n\nThis will make it easier to extend check_refname_format() to also\ncheck whether directory names are valid.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex eff1abe..61dfb32 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -51,7 +51,7 @@ static int check_refname_component(const char *refname, int flags)\n \t\tlast = ch;\n \t}\n \tif (cp == refname)\n-\t\treturn -1; /* Component has zero length. */\n+\t\treturn 0; /* Component has zero length. */\n \tif (refname[0] == '.') {\n \t\tif (!(flags & REFNAME_DOT_COMPONENT))\n \t\t\treturn -1; /* Component starts with '.'. */\n@@ -74,7 +74,7 @@ int check_refname_format(const char *refname, int flags)\n \twhile (1) {\n \t\t/* We are at the start of a path component. */\n \t\tcomponent_len = check_refname_component(refname, flags);\n-\t\tif (component_len < 0) {\n+\t\tif (component_len <= 0) {\n \t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n \t\t\t\t\trefname[0] == '*' &&\n \t\t\t\t\t(refname[1] == '\\0' || refname[1] == '/')) {\n-- \n1.7.7\n"},{"id":"178434","messageId":"1319804921-17545-5-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 04/28] struct ref_entry: nest the value part in a union","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:17Z","receivedAt":"2011-10-28T12:28:17Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nThis change is obviously silly by itself, but it is a step towards\nadding a second member to the union.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   32 +++++++++++++++++++-------------\n 1 files changed, 19 insertions(+), 13 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 61dfb32..dafb585 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -101,6 +101,11 @@ int check_refname_format(const char *refname, int flags)\n \n struct ref_entry;\n \n+struct ref_value {\n+\tunsigned char sha1[20];\n+\tunsigned char peeled[20];\n+};\n+\n struct ref_array {\n \tint nr, alloc;\n \tstruct ref_entry **refs;\n@@ -111,8 +116,9 @@ struct ref_array {\n \n struct ref_entry {\n \tunsigned char flag; /* ISSYMREF? ISPACKED? */\n-\tunsigned char sha1[20];\n-\tunsigned char peeled[20];\n+\tunion {\n+\t\tstruct ref_value value;\n+\t} u;\n \t/* The full name of the reference (e.g., \"refs/heads/master\"): */\n \tchar name[FLEX_ARRAY];\n };\n@@ -127,8 +133,8 @@ static struct ref_entry *create_ref_entry(const char *refname,\n \t\tdie(\"Reference has invalid format: '%s'\", refname);\n \tlen = strlen(refname) + 1;\n \tref = xmalloc(sizeof(struct ref_entry) + len);\n-\thashcpy(ref->sha1, sha1);\n-\thashclr(ref->peeled);\n+\thashcpy(ref->u.value.sha1, sha1);\n+\thashclr(ref->u.value.peeled);\n \tmemcpy(ref->name, refname, len);\n \tref->flag = flag;\n \treturn ref;\n@@ -197,7 +203,7 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2\n {\n \tif (!strcmp(ref1->name, ref2->name)) {\n \t\t/* Duplicate name; make sure that the SHA1s match: */\n-\t\tif (hashcmp(ref1->sha1, ref2->sha1))\n+\t\tif (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))\n \t\t\tdie(\"Duplicated ref, and SHA1s don't match: %s\",\n \t\t\t    ref1->name);\n \t\twarning(\"Duplicated ref: %s\", ref1->name);\n@@ -244,13 +250,13 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n \tif (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {\n \t\tif (entry->flag & REF_ISBROKEN)\n \t\t\treturn 0; /* ignore broken refs e.g. dangling symref */\n-\t\tif (!has_sha1_file(entry->sha1)) {\n+\t\tif (!has_sha1_file(entry->u.value.sha1)) {\n \t\t\terror(\"%s does not point to a valid object!\", entry->name);\n \t\t\treturn 0;\n \t\t}\n \t}\n \tcurrent_ref = entry;\n-\treturn fn(entry->name + trim, entry->sha1, entry->flag, cb_data);\n+\treturn fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);\n }\n \n static int do_for_each_ref_in_array(struct ref_array *array, int offset,\n@@ -502,7 +508,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \t\t    strlen(refline) == 42 &&\n \t\t    refline[41] == '\\n' &&\n \t\t    !get_sha1_hex(refline + 1, sha1))\n-\t\t\thashcpy(last->peeled, sha1);\n+\t\t\thashcpy(last->u.value.peeled, sha1);\n \t}\n \tsort_ref_array(array);\n }\n@@ -632,7 +638,7 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n \tif (ref == NULL)\n \t\treturn -1;\n \n-\tmemcpy(sha1, ref->sha1, 20);\n+\tmemcpy(sha1, ref->u.value.sha1, 20);\n \treturn 0;\n }\n \n@@ -702,7 +708,7 @@ static int get_packed_ref(const char *refname, unsigned char *sha1)\n \tstruct ref_array *packed = get_packed_refs(get_ref_cache(NULL));\n \tstruct ref_entry *entry = search_ref_array(packed, refname);\n \tif (entry) {\n-\t\thashcpy(sha1, entry->sha1);\n+\t\thashcpy(sha1, entry->u.value.sha1);\n \t\treturn 0;\n \t}\n \treturn -1;\n@@ -848,10 +854,10 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \tif (current_ref && (current_ref->name == refname\n \t\t|| !strcmp(current_ref->name, refname))) {\n \t\tif (current_ref->flag & REF_KNOWS_PEELED) {\n-\t\t\thashcpy(sha1, current_ref->peeled);\n+\t\t\thashcpy(sha1, current_ref->u.value.peeled);\n \t\t\treturn 0;\n \t\t}\n-\t\thashcpy(base, current_ref->sha1);\n+\t\thashcpy(base, current_ref->u.value.sha1);\n \t\tgoto fallback;\n \t}\n \n@@ -863,7 +869,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \t\tstruct ref_entry *r = search_ref_array(array, refname);\n \n \t\tif (r != NULL && r->flag & REF_KNOWS_PEELED) {\n-\t\t\thashcpy(sha1, r->peeled);\n+\t\t\thashcpy(sha1, r->u.value.peeled);\n \t\t\treturn 0;\n \t\t}\n \t}\n-- \n1.7.7\n"},{"id":"178441","messageId":"1319804921-17545-6-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 05/28] refs.c: rename ref_array -> ref_dir","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:18Z","receivedAt":"2011-10-28T12:28:18Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nThis purely textual change is in preparation for storing references\nhierarchically, when the old ref_array structure will represent one\n\"directory\" of references.  Rename functions that deal with this\nstructure analogously, and also rename the structure's \"refs\" member\nto \"entries\".\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |  168 ++++++++++++++++++++++++++++++++--------------------------------\n 1 files changed, 84 insertions(+), 84 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex dafb585..708eebf 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -106,9 +106,9 @@ struct ref_value {\n \tunsigned char peeled[20];\n };\n \n-struct ref_array {\n+struct ref_dir {\n \tint nr, alloc;\n-\tstruct ref_entry **refs;\n+\tstruct ref_entry **entries;\n };\n \n /* ISSYMREF=0x01, ISPACKED=0x02 and ISBROKEN=0x04 are public interfaces */\n@@ -145,21 +145,21 @@ static void free_ref_entry(struct ref_entry *entry)\n \tfree(entry);\n }\n \n-/* Add a ref_entry to the end of the ref_array (unsorted). */\n-static void add_ref(struct ref_array *refs, struct ref_entry *ref)\n+/* Add a ref_entry to the end of the ref_dir (unsorted). */\n+static void add_ref(struct ref_dir *refs, struct ref_entry *ref)\n {\n-\tALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc);\n-\trefs->refs[refs->nr++] = ref;\n+\tALLOC_GROW(refs->entries, refs->nr + 1, refs->alloc);\n+\trefs->entries[refs->nr++] = ref;\n }\n \n-static void clear_ref_array(struct ref_array *array)\n+static void clear_ref_dir(struct ref_dir *dir)\n {\n \tint i;\n-\tfor (i = 0; i < array->nr; i++)\n-\t\tfree_ref_entry(array->refs[i]);\n-\tfree(array->refs);\n-\tarray->nr = array->alloc = 0;\n-\tarray->refs = NULL;\n+\tfor (i = 0; i < dir->nr; i++)\n+\t\tfree_ref_entry(dir->entries[i]);\n+\tfree(dir->entries);\n+\tdir->nr = dir->alloc = 0;\n+\tdir->entries = NULL;\n }\n \n static int ref_entry_cmp(const void *a, const void *b)\n@@ -169,7 +169,7 @@ static int ref_entry_cmp(const void *a, const void *b)\n \treturn strcmp(one->name, two->name);\n }\n \n-static struct ref_entry *search_ref_array(struct ref_array *array, const char *refname)\n+static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)\n {\n \tstruct ref_entry *e, **r;\n \tint len;\n@@ -177,14 +177,14 @@ static struct ref_entry *search_ref_array(struct ref_array *array, const char *r\n \tif (refname == NULL)\n \t\treturn NULL;\n \n-\tif (!array->nr)\n+\tif (!dir->nr)\n \t\treturn NULL;\n \n \tlen = strlen(refname) + 1;\n \te = xmalloc(sizeof(struct ref_entry) + len);\n \tmemcpy(e->name, refname, len);\n \n-\tr = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);\n+\tr = bsearch(&e, dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);\n \n \tfree(e);\n \n@@ -213,28 +213,28 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2\n \t}\n }\n \n-static void sort_ref_array(struct ref_array *array)\n+static void sort_ref_dir(struct ref_dir *dir)\n {\n \tint i = 0, j = 1;\n \n \t/* Nothing to sort unless there are at least two entries */\n-\tif (array->nr < 2)\n+\tif (dir->nr < 2)\n \t\treturn;\n \n-\tqsort(array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);\n+\tqsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);\n \n-\t/* Remove any duplicates from the ref_array */\n-\tfor (; j < array->nr; j++) {\n-\t\tstruct ref_entry *a = array->refs[i];\n-\t\tstruct ref_entry *b = array->refs[j];\n+\t/* Remove any duplicates from the ref_dir */\n+\tfor (; j < dir->nr; j++) {\n+\t\tstruct ref_entry *a = dir->entries[i];\n+\t\tstruct ref_entry *b = dir->entries[j];\n \t\tif (is_dup_ref(a, b)) {\n \t\t\tfree_ref_entry(b);\n \t\t\tcontinue;\n \t\t}\n \t\ti++;\n-\t\tarray->refs[i] = array->refs[j];\n+\t\tdir->entries[i] = dir->entries[j];\n \t}\n-\tarray->nr = i + 1;\n+\tdir->nr = i + 1;\n }\n \n #define DO_FOR_EACH_INCLUDE_BROKEN 01\n@@ -259,23 +259,23 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n \treturn fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);\n }\n \n-static int do_for_each_ref_in_array(struct ref_array *array, int offset,\n-\t\t\t\t    const char *base,\n-\t\t\t\t    each_ref_fn fn, int trim, int flags, void *cb_data)\n+static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,\n+\t\t\t\t  const char *base,\n+\t\t\t\t  each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n \tint i;\n-\tfor (i = offset; i < array->nr; i++) {\n-\t\tint retval = do_one_ref(base, fn, trim, flags, cb_data, array->refs[i]);\n+\tfor (i = offset; i < dir->nr; i++) {\n+\t\tint retval = do_one_ref(base, fn, trim, flags, cb_data, dir->entries[i]);\n \t\tif (retval)\n \t\t\treturn retval;\n \t}\n \treturn 0;\n }\n \n-static int do_for_each_ref_in_arrays(struct ref_array *array1,\n-\t\t\t\t     struct ref_array *array2,\n-\t\t\t\t     const char *base, each_ref_fn fn, int trim,\n-\t\t\t\t     int flags, void *cb_data)\n+static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n+\t\t\t\t   struct ref_dir *dir2,\n+\t\t\t\t   const char *base, each_ref_fn fn, int trim,\n+\t\t\t\t   int flags, void *cb_data)\n {\n \tint retval;\n \tint i1 = 0, i2 = 0;\n@@ -283,19 +283,19 @@ static int do_for_each_ref_in_arrays(struct ref_array *array1,\n \twhile (1) {\n \t\tstruct ref_entry *e1, *e2;\n \t\tint cmp;\n-\t\tif (i1 == array1->nr) {\n-\t\t\treturn do_for_each_ref_in_array(array2, i2,\n-\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\tif (i1 == dir1->nr) {\n+\t\t\treturn do_for_each_ref_in_dir(dir2, i2,\n+\t\t\t\t\t\t      base, fn, trim, flags, cb_data);\n \t\t}\n-\t\tif (i2 == array2->nr) {\n-\t\t\treturn do_for_each_ref_in_array(array1, i1,\n-\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\tif (i2 == dir2->nr) {\n+\t\t\treturn do_for_each_ref_in_dir(dir1, i1,\n+\t\t\t\t\t\t      base, fn, trim, flags, cb_data);\n \t\t}\n-\t\te1 = array1->refs[i1];\n-\t\te2 = array2->refs[i2];\n+\t\te1 = dir1->entries[i1];\n+\t\te2 = dir2->entries[i2];\n \t\tcmp = strcmp(e1->name, e2->name);\n \t\tif (cmp == 0) {\n-\t\t\t/* Two refs with the same name; ignore the one from array1. */\n+\t\t\t/* Two refs with the same name; ignore the one from dir1. */\n \t\t\ti1++;\n \t\t\tcontinue;\n \t\t}\n@@ -353,16 +353,16 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh\n  * operation).\n  */\n static int is_refname_available(const char *refname, const char *oldrefname,\n-\t\t\t\tstruct ref_array *array)\n+\t\t\t\tstruct ref_dir *dir)\n {\n \tstruct name_conflict_cb data;\n \tdata.refname = refname;\n \tdata.oldrefname = oldrefname;\n \tdata.conflicting_refname = NULL;\n \n-\tif (do_for_each_ref_in_array(array, 0, \"\", name_conflict_fn,\n-\t\t\t\t     0, DO_FOR_EACH_INCLUDE_BROKEN,\n-\t\t\t\t     &data)) {\n+\tif (do_for_each_ref_in_dir(dir, 0, \"\", name_conflict_fn,\n+\t\t\t\t   0, DO_FOR_EACH_INCLUDE_BROKEN,\n+\t\t\t\t   &data)) {\n \t\terror(\"'%s' exists; cannot create '%s'\",\n \t\t      data.conflicting_refname, refname);\n \t\treturn 0;\n@@ -378,25 +378,25 @@ static struct ref_cache {\n \tstruct ref_cache *next;\n \tchar did_loose;\n \tchar did_packed;\n-\tstruct ref_array loose;\n-\tstruct ref_array packed;\n+\tstruct ref_dir loose;\n+\tstruct ref_dir packed;\n \t/* The submodule name, or \"\" for the main repo. */\n \tchar name[FLEX_ARRAY];\n } *ref_cache;\n \n-static struct ref_array extra_refs;\n+static struct ref_dir extra_refs;\n \n static void clear_packed_ref_cache(struct ref_cache *refs)\n {\n \tif (refs->did_packed)\n-\t\tclear_ref_array(&refs->packed);\n+\t\tclear_ref_dir(&refs->packed);\n \trefs->did_packed = 0;\n }\n \n static void clear_loose_ref_cache(struct ref_cache *refs)\n {\n \tif (refs->did_loose)\n-\t\tclear_ref_array(&refs->loose);\n+\t\tclear_ref_dir(&refs->loose);\n \trefs->did_loose = 0;\n }\n \n@@ -478,7 +478,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)\n \treturn line;\n }\n \n-static void read_packed_refs(FILE *f, struct ref_array *array)\n+static void read_packed_refs(FILE *f, struct ref_dir *dir)\n {\n \tstruct ref_entry *last = NULL;\n \tchar refline[PATH_MAX];\n@@ -500,7 +500,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \t\trefname = parse_ref_line(refline, sha1);\n \t\tif (refname) {\n \t\t\tlast = create_ref_entry(refname, sha1, flag);\n-\t\t\tadd_ref(array, last);\n+\t\t\tadd_ref(dir, last);\n \t\t\tcontinue;\n \t\t}\n \t\tif (last &&\n@@ -510,7 +510,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \t\t    !get_sha1_hex(refline + 1, sha1))\n \t\t\thashcpy(last->u.value.peeled, sha1);\n \t}\n-\tsort_ref_array(array);\n+\tsort_ref_dir(dir);\n }\n \n void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n@@ -520,10 +520,10 @@ void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n \n void clear_extra_refs(void)\n {\n-\tclear_ref_array(&extra_refs);\n+\tclear_ref_dir(&extra_refs);\n }\n \n-static struct ref_array *get_packed_refs(struct ref_cache *refs)\n+static struct ref_dir *get_packed_refs(struct ref_cache *refs)\n {\n \tif (!refs->did_packed) {\n \t\tconst char *packed_refs_file;\n@@ -544,9 +544,9 @@ static struct ref_array *get_packed_refs(struct ref_cache *refs)\n }\n \n static void get_ref_dir(struct ref_cache *refs, const char *base,\n-\t\t\tstruct ref_array *array)\n+\t\t\tstruct ref_dir *dir)\n {\n-\tDIR *dir;\n+\tDIR *d;\n \tconst char *path;\n \n \tif (*refs->name)\n@@ -555,9 +555,9 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\tpath = git_path(\"%s\", base);\n \n \n-\tdir = opendir(path);\n+\td = opendir(path);\n \n-\tif (dir) {\n+\tif (d) {\n \t\tstruct dirent *de;\n \t\tint baselen = strlen(base);\n \t\tchar *refname = xmalloc(baselen + 257);\n@@ -566,7 +566,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\tif (baselen && base[baselen-1] != '/')\n \t\t\trefname[baselen++] = '/';\n \n-\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\twhile ((de = readdir(d)) != NULL) {\n \t\t\tunsigned char sha1[20];\n \t\t\tstruct stat st;\n \t\t\tint flag;\n@@ -587,7 +587,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\tif (stat(refdir, &st) < 0)\n \t\t\t\tcontinue;\n \t\t\tif (S_ISDIR(st.st_mode)) {\n-\t\t\t\tget_ref_dir(refs, refname, array);\n+\t\t\t\tget_ref_dir(refs, refname, dir);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (*refs->name) {\n@@ -602,18 +602,18 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\t\thashclr(sha1);\n \t\t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t\t}\n-\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag));\n+\t\t\tadd_ref(dir, create_ref_entry(refname, sha1, flag));\n \t\t}\n \t\tfree(refname);\n-\t\tclosedir(dir);\n+\t\tclosedir(d);\n \t}\n }\n \n-static struct ref_array *get_loose_refs(struct ref_cache *refs)\n+static struct ref_dir *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->did_loose) {\n \t\tget_ref_dir(refs, \"refs\", &refs->loose);\n-\t\tsort_ref_array(&refs->loose);\n+\t\tsort_ref_dir(&refs->loose);\n \t\trefs->did_loose = 1;\n \t}\n \treturn &refs->loose;\n@@ -632,9 +632,9 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n \t\t\t\t      const char *refname, unsigned char *sha1)\n {\n \tstruct ref_entry *ref;\n-\tstruct ref_array *array = get_packed_refs(refs);\n+\tstruct ref_dir *dir = get_packed_refs(refs);\n \n-\tref = search_ref_array(array, refname);\n+\tref = search_ref_dir(dir, refname);\n \tif (ref == NULL)\n \t\treturn -1;\n \n@@ -705,8 +705,8 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh\n  */\n static int get_packed_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_array *packed = get_packed_refs(get_ref_cache(NULL));\n-\tstruct ref_entry *entry = search_ref_array(packed, refname);\n+\tstruct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));\n+\tstruct ref_entry *entry = search_ref_dir(packed, refname);\n \tif (entry) {\n \t\thashcpy(sha1, entry->u.value.sha1);\n \t\treturn 0;\n@@ -865,8 +865,8 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \t\treturn -1;\n \n \tif ((flag & REF_ISPACKED)) {\n-\t\tstruct ref_array *array = get_packed_refs(get_ref_cache(NULL));\n-\t\tstruct ref_entry *r = search_ref_array(array, refname);\n+\t\tstruct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));\n+\t\tstruct ref_entry *r = search_ref_dir(dir, refname);\n \n \t\tif (r != NULL && r->flag & REF_KNOWS_PEELED) {\n \t\t\thashcpy(sha1, r->u.value.peeled);\n@@ -926,12 +926,12 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \tint retval = 0;\n \tstruct ref_cache *refs = get_ref_cache(submodule);\n \n-\tretval = do_for_each_ref_in_array(&extra_refs, 0,\n-\t\t\t\t\t  base, fn, trim, flags, cb_data);\n+\tretval = do_for_each_ref_in_dir(&extra_refs, 0,\n+\t\t\t\t\tbase, fn, trim, flags, cb_data);\n \tif (!retval)\n-\t\tretval = do_for_each_ref_in_arrays(get_packed_refs(refs),\n-\t\t\t\t\t\t   get_loose_refs(refs),\n-\t\t\t\t\t\t   base, fn, trim, flags, cb_data);\n+\t\tretval = do_for_each_ref_in_dirs(get_packed_refs(refs),\n+\t\t\t\t\t\t get_loose_refs(refs),\n+\t\t\t\t\t\t base, fn, trim, flags, cb_data);\n \n \tcurrent_ref = NULL;\n \treturn retval;\n@@ -1377,10 +1377,10 @@ static struct lock_file packlock;\n static int repack_without_ref(const char *refname)\n {\n \tstruct repack_without_ref_sb data;\n-\tstruct ref_array *packed;\n+\tstruct ref_dir *packed;\n \n \tpacked = get_packed_refs(get_ref_cache(NULL));\n-\tif (search_ref_array(packed, refname) == NULL)\n+\tif (search_ref_dir(packed, refname) == NULL)\n \t\treturn 0;\n \tdata.refname = refname;\n \tdata.fd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n@@ -1388,7 +1388,7 @@ static int repack_without_ref(const char *refname)\n \t\tunable_to_lock_error(git_path(\"packed-refs\"), errno);\n \t\treturn error(\"cannot delete '%s' from packed refs\", refname);\n \t}\n-\tdo_for_each_ref_in_array(packed, 0, \"\", repack_without_ref_fn, 0, 0, &data);\n+\tdo_for_each_ref_in_dir(packed, 0, \"\", repack_without_ref_fn, 0, 0, &data);\n \treturn commit_lock_file(&packlock);\n }\n \n@@ -1998,10 +1998,10 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat\n \n static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)\n {\n-\tDIR *dir = opendir(git_path(\"logs/%s\", base));\n+\tDIR *d = opendir(git_path(\"logs/%s\", base));\n \tint retval = 0;\n \n-\tif (dir) {\n+\tif (d) {\n \t\tstruct dirent *de;\n \t\tint baselen = strlen(base);\n \t\tchar *log = xmalloc(baselen + 257);\n@@ -2010,7 +2010,7 @@ static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)\n \t\tif (baselen && base[baselen-1] != '/')\n \t\t\tlog[baselen++] = '/';\n \n-\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\twhile ((de = readdir(d)) != NULL) {\n \t\t\tstruct stat st;\n \t\t\tint namelen;\n \n@@ -2037,7 +2037,7 @@ static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)\n \t\t\t\tbreak;\n \t\t}\n \t\tfree(log);\n-\t\tclosedir(dir);\n+\t\tclosedir(d);\n \t}\n \telse if (*base)\n \t\treturn errno;\n-- \n1.7.7\n"},{"id":"178435","messageId":"1319804921-17545-7-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 06/28] refs: store references hierarchically","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:19Z","receivedAt":"2011-10-28T12:28:19Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nStore references hierarchically in a tree that matches the\npseudo-directory structure of the reference names.  Add a new kind of\nref_entry (with flag REF_DIR) to represent a whole subdirectory of\nreferences.  Teach check_refname_format() to decide whether a\nreference directory name is valid.\n\nPlease note that this change causes some extra sorting to be required,\nand therefore a performance regression.  The old performance will be\nregained in the next couple of commits by (1) keeping track of when a\ndirectory is already sorted and not re-sorting it; (2) only sorting a\ndirectory when the correct order needed; and (3) not sorting\ndirectories recursively.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |  325 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----------\n 1 files changed, 271 insertions(+), 54 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 708eebf..6b4d5d5 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -67,6 +67,23 @@ static int check_refname_component(const char *refname, int flags)\n \treturn cp - refname;\n }\n \n+/*\n+ * Non-public option to check_refname_format(), for asking it to check\n+ * that refname is a valid reference \"directory\" name.\n+ */\n+#define REFNAME_DIR 0x08\n+\n+/*\n+ * See refs.h for a description of the public interface of this\n+ * function.  In addition, there is a private interface that can be\n+ * accessed by setting the REFNAME_DIR bit.  If REFNAME_DIR is set,\n+ * then refname must be a valid reference \"directory\" name.  This\n+ * means that it must include at least one valid component (which *is*\n+ * allowed to end with '.') followed by a trailing '/'.  If\n+ * REFNAME_DIR and REFNAME_ALLOW_ONELEVEL are both passed, then\n+ * additionally the empty string (no trailing slash allowed) is\n+ * allowed.\n+ */\n int check_refname_format(const char *refname, int flags)\n {\n \tint component_len, component_count = 0;\n@@ -74,7 +91,7 @@ int check_refname_format(const char *refname, int flags)\n \twhile (1) {\n \t\t/* We are at the start of a path component. */\n \t\tcomponent_len = check_refname_component(refname, flags);\n-\t\tif (component_len <= 0) {\n+\t\tif (component_len < 0) {\n \t\t\tif ((flags & REFNAME_REFSPEC_PATTERN) &&\n \t\t\t\t\trefname[0] == '*' &&\n \t\t\t\t\t(refname[1] == '\\0' || refname[1] == '/')) {\n@@ -86,16 +103,36 @@ int check_refname_format(const char *refname, int flags)\n \t\t\t}\n \t\t}\n \t\tcomponent_count++;\n-\t\tif (refname[component_len] == '\\0')\n+\t\tif (refname[component_len] == '\\0') {\n+\t\t\tif (flags & REFNAME_DIR) {\n+\t\t\t\t/*\n+\t\t\t\t * The last component of a REFNAME_DIR\n+\t\t\t\t * *must* be the empty string.\n+\t\t\t\t */\n+\t\t\t\tif (component_len != 0)\n+\t\t\t\t\treturn -1;\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * The last component of a\n+\t\t\t\t * non-REFNAME_DIR *must not* be the\n+\t\t\t\t * empty string.\n+\t\t\t\t */\n+\t\t\t\tif (component_len == 0)\n+\t\t\t\t\treturn -1;\n+\t\t\t\tif (refname[component_len - 1] == '.')\n+\t\t\t\t\treturn -1; /* Refname ends with '.'. */\n+\t\t\t}\n+\t\t\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n+\t\t\t\treturn -1; /* Refname has only one component. */\n+\n \t\t\tbreak;\n+\t\t}\n+\t\tif (component_len == 0)\n+\t\t\treturn -1; /* Double '/' */\n \t\t/* Skip to next component. */\n \t\trefname += component_len + 1;\n \t}\n \n-\tif (refname[component_len - 1] == '.')\n-\t\treturn -1; /* Refname ends with '.'. */\n-\tif (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)\n-\t\treturn -1; /* Refname has only one component. */\n \treturn 0;\n }\n \n@@ -111,15 +148,54 @@ struct ref_dir {\n \tstruct ref_entry **entries;\n };\n \n-/* ISSYMREF=0x01, ISPACKED=0x02 and ISBROKEN=0x04 are public interfaces */\n-#define REF_KNOWS_PEELED 0x10\n+/* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */\n+#define REF_KNOWS_PEELED 0x08\n+#define REF_DIR 0x10\n \n+/*\n+ * A ref_entry represents either a reference or a \"subdirectory\" of\n+ * references.  Each directory in the reference namespace is\n+ * represented by a ref_entry with (flags & REF_DIR) set and\n+ * containing a subdir member that holds the entries in that\n+ * directory.  References are represented by a ref_entry with (flags &\n+ * REF_DIR) unset and a value member that describes the reference's\n+ * value.  The flag member is at the ref_entry level, but it is also\n+ * needed to interpret the contents of the value field (in other\n+ * words, a ref_value object is not very much use without the\n+ * enclosing ref_entry).\n+ *\n+ * Reference names cannot end with slash and directories' names are\n+ * always stored with a trailing slash (except for the top-level\n+ * directory, which is always denoted by \"\").  This has two nice\n+ * consequences: (1) when the entries in each subdir are sorted\n+ * lexicographically by name (as they usually are), the references in\n+ * a whole tree can be generated in lexicographic order by traversing\n+ * the tree in left-to-right, depth-first order; (2) the names of\n+ * references and subdirectories cannot conflict, and therefore the\n+ * presence of an empty subdirectory does not block the creation of a\n+ * similarly-named reference.  (The fact that reference names with the\n+ * same leading components can conflict *with each other* is a\n+ * separate issue that is regulated by is_refname_available().)\n+ *\n+ * Please note that the name field contains the fully-qualified\n+ * reference (or subdirectory) name.  Space could be saved by only\n+ * storing the relative names.  But that would require the full names\n+ * to be generated on the fly when iterating in do_for_each_ref(), and\n+ * would break callback functions, who have always been able to assume\n+ * that the name strings that they are passed will not be freed during\n+ * the iteration.\n+ */\n struct ref_entry {\n \tunsigned char flag; /* ISSYMREF? ISPACKED? */\n \tunion {\n-\t\tstruct ref_value value;\n+\t\tstruct ref_value value; /* if not (flags&REF_DIR) */\n+\t\tstruct ref_dir subdir; /* if (flags&REF_DIR) */\n \t} u;\n-\t/* The full name of the reference (e.g., \"refs/heads/master\"): */\n+\t/*\n+\t * The full name of the reference (e.g., \"refs/heads/master\")\n+\t * or the full name of the directory with a trailing slash\n+\t * (e.g., \"refs/heads/\"):\n+\t */\n \tchar name[FLEX_ARRAY];\n };\n \n@@ -140,18 +216,29 @@ static struct ref_entry *create_ref_entry(const char *refname,\n \treturn ref;\n }\n \n+static void clear_ref_dir(struct ref_dir *dir);\n+\n static void free_ref_entry(struct ref_entry *entry)\n {\n+\tif (entry->flag & REF_DIR)\n+\t\tclear_ref_dir(&entry->u.subdir);\n \tfree(entry);\n }\n \n-/* Add a ref_entry to the end of the ref_dir (unsorted). */\n-static void add_ref(struct ref_dir *refs, struct ref_entry *ref)\n+/*\n+ * Add a ref_entry to the end of dir (unsorted).  Entry is always\n+ * stored directly in dir; no recursion into subdirectories is\n+ * done.\n+ */\n+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)\n {\n-\tALLOC_GROW(refs->entries, refs->nr + 1, refs->alloc);\n-\trefs->entries[refs->nr++] = ref;\n+\tALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);\n+\tdir->entries[dir->nr++] = entry;\n }\n \n+/*\n+ * Clear and free all entries in dir, recursively.\n+ */\n static void clear_ref_dir(struct ref_dir *dir)\n {\n \tint i;\n@@ -162,6 +249,28 @@ static void clear_ref_dir(struct ref_dir *dir)\n \tdir->entries = NULL;\n }\n \n+/*\n+ * Create a struct ref_entry object for the specified dirname.\n+ * dirname is the name of the directory with a trailing slash (e.g.,\n+ * \"refs/heads/\").\n+ */\n+static struct ref_entry *create_dir_entry(const char *dirname)\n+{\n+\tstruct ref_entry *direntry;\n+\tif (*dirname) {\n+\t\tint len = strlen(dirname);\n+\t\tdirentry = xcalloc(1, sizeof(struct ref_entry) + len + 1);\n+\t\tmemcpy(direntry->name, dirname, len + 1);\n+\t\tif (check_refname_format(direntry->name, REFNAME_DIR|REFNAME_ALLOW_ONELEVEL))\n+\t\t\tdie(\"reference directory has invalid format: '%s'\", direntry->name);\n+\t} else {\n+\t\tdirentry = xcalloc(1, sizeof(struct ref_entry) + 1);\n+\t\tdirentry->name[0] = '\\0';\n+\t}\n+\tdirentry->flag = REF_DIR;\n+\treturn direntry;\n+}\n+\n static int ref_entry_cmp(const void *a, const void *b)\n {\n \tstruct ref_entry *one = *(struct ref_entry **)a;\n@@ -169,16 +278,26 @@ static int ref_entry_cmp(const void *a, const void *b)\n \treturn strcmp(one->name, two->name);\n }\n \n+static void sort_ref_dir(struct ref_dir *dir);\n+\n+/*\n+ * Return the entry with the given refname from the ref_dir\n+ * (non-recursively).  Return NULL if no such entry is found.\n+ */\n static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)\n {\n \tstruct ref_entry *e, **r;\n \tint len;\n \n-\tif (refname == NULL)\n+\tif (refname == NULL || !dir->nr)\n \t\treturn NULL;\n \n-\tif (!dir->nr)\n-\t\treturn NULL;\n+\t/*\n+\t * We need dir to be sorted so that binary search works.\n+\t * FIXME: Sorting the array each time is terribly inefficient,\n+\t * and has to be changed.\n+\t */\n+\tsort_ref_dir(dir);\n \n \tlen = strlen(refname) + 1;\n \te = xmalloc(sizeof(struct ref_entry) + len);\n@@ -195,46 +314,116 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname\n }\n \n /*\n+ * If refname is a reference name, find the ref_dir within the dir\n+ * tree that should hold refname.  If refname is a directory name\n+ * (i.e., ends in '/'), then return that ref_dir itself.  dir must\n+ * represent the top-level directory.  Recurse into subdirectories as\n+ * necessary.  If mkdir is set, then create any missing directories;\n+ * otherwise, return NULL if the desired directory cannot be found.\n+ */\n+static struct ref_dir *find_containing_dir(struct ref_dir *dir,\n+\t\t\t\t\t   const char *refname, int mkdir)\n+{\n+\tchar *refname_copy = xstrdup(refname);\n+\tchar *slash;\n+\tstruct ref_entry *entry;\n+\tfor (slash = strchr(refname_copy, '/'); slash; slash = strchr(slash + 1, '/')) {\n+\t\tchar tmp = slash[1];\n+\t\tslash[1] = '\\0';\n+\t\tentry = search_ref_dir(dir, refname_copy);\n+\t\tif (!entry) {\n+\t\t\tif (!mkdir) {\n+\t\t\t\tdir = NULL;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tentry = create_dir_entry(refname_copy);\n+\t\t\tadd_entry_to_dir(dir, entry);\n+\t\t}\n+\t\tslash[1] = tmp;\n+\t\tassert(entry->flag & REF_DIR);\n+\t\tdir = &entry->u.subdir;\n+\t}\n+\n+\tfree(refname_copy);\n+\treturn dir;\n+}\n+\n+/*\n+ * Find the value entry with the given name in dir, recursing into\n+ * subdirectories as necessary.  If the name is not found or it\n+ * corresponds to a directory entry, return NULL.\n+ */\n+static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)\n+{\n+\tstruct ref_entry *entry;\n+\tdir = find_containing_dir(dir, refname, 0);\n+\tif (!dir)\n+\t\treturn NULL;\n+\tentry = search_ref_dir(dir, refname);\n+\treturn (entry && !(entry->flag & REF_DIR)) ? entry : NULL;\n+}\n+\n+/*\n+ * Add a ref_entry to the ref_dir (unsorted), recursing into\n+ * subdirectories as necessary.  dir must represent the top-level\n+ * directory.  Return 0 on success.\n+ */\n+static int add_ref(struct ref_dir *dir, struct ref_entry *ref)\n+{\n+\tdir = find_containing_dir(dir, ref->name, 1);\n+\tif (!dir)\n+\t\treturn -1;\n+\tadd_entry_to_dir(dir, ref);\n+\treturn 0;\n+}\n+\n+/*\n  * Emit a warning and return true iff ref1 and ref2 have the same name\n  * and the same sha1.  Die if they have the same name but different\n  * sha1s.\n  */\n static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)\n {\n-\tif (!strcmp(ref1->name, ref2->name)) {\n-\t\t/* Duplicate name; make sure that the SHA1s match: */\n-\t\tif (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))\n-\t\t\tdie(\"Duplicated ref, and SHA1s don't match: %s\",\n-\t\t\t    ref1->name);\n-\t\twarning(\"Duplicated ref: %s\", ref1->name);\n-\t\treturn 1;\n-\t} else {\n+\tif (strcmp(ref1->name, ref2->name))\n \t\treturn 0;\n-\t}\n+\n+\t/* Duplicate name; make sure that they don't conflict: */\n+\n+\tif ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))\n+\t\t/* This is impossible by construction */\n+\t\tdie(\"Reference directory conflict: %s\", ref1->name);\n+\n+\tif (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))\n+\t\tdie(\"Duplicated ref, and SHA1s don't match: %s\", ref1->name);\n+\n+\twarning(\"Duplicated ref: %s\", ref1->name);\n+\treturn 1;\n }\n \n static void sort_ref_dir(struct ref_dir *dir)\n {\n-\tint i = 0, j = 1;\n+\tint i, j;\n+\tstruct ref_entry *last = NULL;\n \n-\t/* Nothing to sort unless there are at least two entries */\n-\tif (dir->nr < 2)\n+\tif (!dir->nr)\n \t\treturn;\n \n \tqsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);\n \n-\t/* Remove any duplicates from the ref_dir */\n-\tfor (; j < dir->nr; j++) {\n-\t\tstruct ref_entry *a = dir->entries[i];\n-\t\tstruct ref_entry *b = dir->entries[j];\n-\t\tif (is_dup_ref(a, b)) {\n-\t\t\tfree_ref_entry(b);\n-\t\t\tcontinue;\n+\t/* Remove any duplicates and sort subdirectories: */\n+\tfor (i = 0, j = 0; j < dir->nr; j++) {\n+\t\tstruct ref_entry *entry = dir->entries[j];\n+\t\tif (last && is_dup_ref(last, entry)) {\n+\t\t\tfree_ref_entry(entry);\n+\t\t} else if (entry->flag & REF_DIR) {\n+\t\t\tsort_ref_dir(&entry->u.subdir);\n+\t\t\tdir->entries[i++] = entry;\n+\t\t\tlast = NULL;\n+\t\t} else {\n+\t\t\tlast = dir->entries[i++] = entry;\n \t\t}\n-\t\ti++;\n-\t\tdir->entries[i] = dir->entries[j];\n \t}\n-\tdir->nr = i + 1;\n+\tdir->nr = i;\n }\n \n #define DO_FOR_EACH_INCLUDE_BROKEN 01\n@@ -265,7 +454,14 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,\n {\n \tint i;\n \tfor (i = offset; i < dir->nr; i++) {\n-\t\tint retval = do_one_ref(base, fn, trim, flags, cb_data, dir->entries[i]);\n+\t\tstruct ref_entry *entry = dir->entries[i];\n+\t\tint retval;\n+\t\tif (entry->flag & REF_DIR) {\n+\t\t\tretval = do_for_each_ref_in_dir(&entry->u.subdir, 0,\n+\t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\t} else {\n+\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, entry);\n+\t\t}\n \t\tif (retval)\n \t\t\treturn retval;\n \t}\n@@ -281,7 +477,7 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n \tint i1 = 0, i2 = 0;\n \n \twhile (1) {\n-\t\tstruct ref_entry *e1, *e2;\n+\t\tstruct ref_entry *e1, *e2, *entry;\n \t\tint cmp;\n \t\tif (i1 == dir1->nr) {\n \t\t\treturn do_for_each_ref_in_dir(dir2, i2,\n@@ -295,16 +491,37 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n \t\te2 = dir2->entries[i2];\n \t\tcmp = strcmp(e1->name, e2->name);\n \t\tif (cmp == 0) {\n-\t\t\t/* Two refs with the same name; ignore the one from dir1. */\n-\t\t\ti1++;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (cmp < 0) {\n-\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e1);\n-\t\t\ti1++;\n+\t\t\tif ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {\n+\t\t\t\t/* Both are directories; descend them in parallel. */\n+\t\t\t\tretval = do_for_each_ref_in_dirs(\n+\t\t\t\t\t\t&e1->u.subdir, &e2->u.subdir,\n+\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\t\t\ti1++;\n+\t\t\t\ti2++;\n+\t\t\t} else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {\n+\t\t\t\t/* Both are references; ignore the one from dir1. */\n+\t\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e2);\n+\t\t\t\ti1++;\n+\t\t\t\ti2++;\n+\t\t\t} else {\n+\t\t\t\tdie(\"conflict between reference and directory: %s\",\n+\t\t\t\t    e1->name);\n+\t\t\t}\n \t\t} else {\n-\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e2);\n-\t\t\ti2++;\n+\t\t\tif (cmp < 0) {\n+\t\t\t\tentry = e1;\n+\t\t\t\ti1++;\n+\t\t\t} else {\n+\t\t\t\tentry = e2;\n+\t\t\t\ti2++;\n+\t\t\t}\n+\t\t\tif (entry->flag & REF_DIR) {\n+\t\t\t\tretval = do_for_each_ref_in_dir(\n+\t\t\t\t\t\t&entry->u.subdir, 0,\n+\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\t\t} else {\n+\t\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, entry);\n+\t\t\t}\n \t\t}\n \t\tif (retval)\n \t\t\treturn retval;\n@@ -634,7 +851,7 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n \tstruct ref_entry *ref;\n \tstruct ref_dir *dir = get_packed_refs(refs);\n \n-\tref = search_ref_dir(dir, refname);\n+\tref = find_ref(dir, refname);\n \tif (ref == NULL)\n \t\treturn -1;\n \n@@ -706,7 +923,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh\n static int get_packed_ref(const char *refname, unsigned char *sha1)\n {\n \tstruct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));\n-\tstruct ref_entry *entry = search_ref_dir(packed, refname);\n+\tstruct ref_entry *entry = find_ref(packed, refname);\n \tif (entry) {\n \t\thashcpy(sha1, entry->u.value.sha1);\n \t\treturn 0;\n@@ -866,7 +1083,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \n \tif ((flag & REF_ISPACKED)) {\n \t\tstruct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));\n-\t\tstruct ref_entry *r = search_ref_dir(dir, refname);\n+\t\tstruct ref_entry *r = find_ref(dir, refname);\n \n \t\tif (r != NULL && r->flag & REF_KNOWS_PEELED) {\n \t\t\thashcpy(sha1, r->u.value.peeled);\n@@ -1380,7 +1597,7 @@ static int repack_without_ref(const char *refname)\n \tstruct ref_dir *packed;\n \n \tpacked = get_packed_refs(get_ref_cache(NULL));\n-\tif (search_ref_dir(packed, refname) == NULL)\n+\tif (find_ref(packed, refname) == NULL)\n \t\treturn 0;\n \tdata.refname = refname;\n \tdata.fd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n-- \n1.7.7\n"},{"id":"178432","messageId":"1319804921-17545-8-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 07/28] sort_ref_dir(): do not sort if already sorted","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:20Z","receivedAt":"2011-10-28T12:28:20Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nKeep track of how many entries in a ref_dir are already sorted.  In\nsort_ref_dir(), only call qsort() if the dir contains unsorted\nentries.\n\nWe could store a binary \"sorted\" value instead of an integer, but\nstoring the number of sorted entries leaves the way open for a couple\nof possible future optimizations:\n\n* In sort_ref_dir(), sort *only* the unsorted entries, then merge them\n  with the sorted entries.  This should be faster if most of the\n  entries are already sorted.\n\n* Teach search_ref_dir() to do a binary search of any sorted entries,\n  and if unsuccessful do a linear search of any unsorted entries.\n  This would avoid the need to sort the list every time that\n  search_ref_dir() is called, and (given some intelligence about how\n  often to sort) could significantly improve the speed in certain\n  hypothetical usage patterns.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   29 ++++++++++++++++++++++++-----\n 1 files changed, 24 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6b4d5d5..8733a08 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -145,6 +145,10 @@ struct ref_value {\n \n struct ref_dir {\n \tint nr, alloc;\n+\n+\t/* How many of the entries in this directory are sorted? */\n+\tint sorted;\n+\n \tstruct ref_entry **entries;\n };\n \n@@ -245,7 +249,7 @@ static void clear_ref_dir(struct ref_dir *dir)\n \tfor (i = 0; i < dir->nr; i++)\n \t\tfree_ref_entry(dir->entries[i]);\n \tfree(dir->entries);\n-\tdir->nr = dir->alloc = 0;\n+\tdir->sorted = dir->nr = dir->alloc = 0;\n \tdir->entries = NULL;\n }\n \n@@ -294,8 +298,9 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname\n \n \t/*\n \t * We need dir to be sorted so that binary search works.\n-\t * FIXME: Sorting the array each time is terribly inefficient,\n-\t * and has to be changed.\n+\t * Calling sort_ref_dir() here is not quite as terribly\n+\t * inefficient as it looks, because directories that are\n+\t * already sorted are not re-sorted.\n \t */\n \tsort_ref_dir(dir);\n \n@@ -400,13 +405,27 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2\n \treturn 1;\n }\n \n+/*\n+ * Sort the entries in dir and its subdirectories (if they are not\n+ * already sorted).\n+ */\n static void sort_ref_dir(struct ref_dir *dir)\n {\n \tint i, j;\n \tstruct ref_entry *last = NULL;\n \n-\tif (!dir->nr)\n+\tif (dir->sorted == dir->nr) {\n+\t\t/*\n+\t\t * This directory is already sorted and de-duped, but\n+\t\t * we still have to sort subdirectories.\n+\t\t */\n+\t\tfor (i = 0; i < dir->nr; i++) {\n+\t\t\tstruct ref_entry *entry = dir->entries[i];\n+\t\t\tif (entry->flag & REF_DIR)\n+\t\t\t\tsort_ref_dir(&entry->u.subdir);\n+\t\t}\n \t\treturn;\n+\t}\n \n \tqsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);\n \n@@ -423,7 +442,7 @@ static void sort_ref_dir(struct ref_dir *dir)\n \t\t\tlast = dir->entries[i++] = entry;\n \t\t}\n \t}\n-\tdir->nr = i;\n+\tdir->sorted = dir->nr = i;\n }\n \n #define DO_FOR_EACH_INCLUDE_BROKEN 01\n-- \n1.7.7\n"},{"id":"178437","messageId":"1319804921-17545-9-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 08/28] refs: sort ref_dirs lazily","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:21Z","receivedAt":"2011-10-28T12:28:21Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nSort ref_dirs lazily, when the ordering is needed: for searching via\nsearch_ref_dir(), and when iterating over them via\ndo_for_each_ref_in_dir() and do_for_each_ref_in_dirs().\n\nThis change means that we never have to sort directories recursively,\nso change sort_ref_dirs() to not recurse.\n\nNOTE: the dirs can now be sorted as a side-effect of other function\ncalls.  Therefore, it would be problematic to do something from a\neach_ref_fn callback that could provoke the sorting of the directory\nthat is currently being iterated over.  This is not so likely, because\na directory is always sorted just before being iterated over and thus\ncan be searched through during the iteration without causing a\nre-sort.  But if a callback function would add a reference to a parent\ndirectory of the reference in the iteration, then try to resolve a\nreference under that directory, inconsistency could result.\n\nAdd a comment in refs.h warning against modifications during\niteration.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   35 +++++++++++++++--------------------\n refs.h |    7 +++++--\n 2 files changed, 20 insertions(+), 22 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8733a08..733d7a8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -298,9 +298,14 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname\n \n \t/*\n \t * We need dir to be sorted so that binary search works.\n-\t * Calling sort_ref_dir() here is not quite as terribly\n-\t * inefficient as it looks, because directories that are\n-\t * already sorted are not re-sorted.\n+\t * Calling sort_ref_dir() here is not as terribly inefficient\n+\t * as it looks.  (1) If the directory is already sorted, it is\n+\t * not re-sorted. (2) When adding a reference,\n+\t * search_ref_dir() is only called to find the containing\n+\t * subdirectories; there is no search of the directory to\n+\t * which the reference will be stored.  Thus adding a bunch of\n+\t * references one after the other to a single subdirectory\n+\t * doesn't require *any* intermediate sorting.\n \t */\n \tsort_ref_dir(dir);\n \n@@ -406,26 +411,16 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2\n }\n \n /*\n- * Sort the entries in dir and its subdirectories (if they are not\n- * already sorted).\n+ * Sort the entries in dir (if they are not already sorted).  Sort\n+ * only dir itself, not its subdirectories.\n  */\n static void sort_ref_dir(struct ref_dir *dir)\n {\n \tint i, j;\n \tstruct ref_entry *last = NULL;\n \n-\tif (dir->sorted == dir->nr) {\n-\t\t/*\n-\t\t * This directory is already sorted and de-duped, but\n-\t\t * we still have to sort subdirectories.\n-\t\t */\n-\t\tfor (i = 0; i < dir->nr; i++) {\n-\t\t\tstruct ref_entry *entry = dir->entries[i];\n-\t\t\tif (entry->flag & REF_DIR)\n-\t\t\t\tsort_ref_dir(&entry->u.subdir);\n-\t\t}\n-\t\treturn;\n-\t}\n+\tif (dir->sorted == dir->nr)\n+\t\treturn; /* This directory is already sorted and de-duped */\n \n \tqsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);\n \n@@ -435,7 +430,6 @@ static void sort_ref_dir(struct ref_dir *dir)\n \t\tif (last && is_dup_ref(last, entry)) {\n \t\t\tfree_ref_entry(entry);\n \t\t} else if (entry->flag & REF_DIR) {\n-\t\t\tsort_ref_dir(&entry->u.subdir);\n \t\t\tdir->entries[i++] = entry;\n \t\t\tlast = NULL;\n \t\t} else {\n@@ -472,6 +466,7 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,\n \t\t\t\t  each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n \tint i;\n+\tsort_ref_dir(dir);\n \tfor (i = offset; i < dir->nr; i++) {\n \t\tstruct ref_entry *entry = dir->entries[i];\n \t\tint retval;\n@@ -495,6 +490,8 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n \tint retval;\n \tint i1 = 0, i2 = 0;\n \n+\tsort_ref_dir(dir1);\n+\tsort_ref_dir(dir2);\n \twhile (1) {\n \t\tstruct ref_entry *e1, *e2, *entry;\n \t\tint cmp;\n@@ -746,7 +743,6 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n \t\t    !get_sha1_hex(refline + 1, sha1))\n \t\t\thashcpy(last->u.value.peeled, sha1);\n \t}\n-\tsort_ref_dir(dir);\n }\n \n void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n@@ -849,7 +845,6 @@ static struct ref_dir *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->did_loose) {\n \t\tget_ref_dir(refs, \"refs\", &refs->loose);\n-\t\tsort_ref_dir(&refs->loose);\n \t\trefs->did_loose = 1;\n \t}\n \treturn &refs->loose;\ndiff --git a/refs.h b/refs.h\nindex d498291..5bb4678 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -15,8 +15,11 @@ struct ref_lock {\n #define REF_ISBROKEN 0x04\n \n /*\n- * Calls the specified function for each ref file until it returns nonzero,\n- * and returns the value\n+ * Calls the specified function for each ref file until it returns\n+ * nonzero, and returns the value.  Please note that it is not safe to\n+ * modify references while an iteration is in progress, unless the\n+ * same callback function invocation that modifies the reference also\n+ * returns a nonzero value to immediately stop the iteration.\n  */\n typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);\n extern int head_ref(each_ref_fn, void *);\n-- \n1.7.7\n"},{"id":"178438","messageId":"1319804921-17545-10-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 09/28] do_for_each_ref(): only iterate over the subtree that was requested","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:22Z","receivedAt":"2011-10-28T12:28:22Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nIf the base argument has a \"/\" chararacter, then only iterate over the\nreference subdir whose name is the part up to the last \"/\".\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   31 ++++++++++++++++++++++++++-----\n 1 files changed, 26 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 733d7a8..515b44c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1156,13 +1156,34 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n {\n \tint retval = 0;\n \tstruct ref_cache *refs = get_ref_cache(submodule);\n+\tstruct ref_dir *extra_dir = &extra_refs;\n+\tstruct ref_dir *packed_dir = get_packed_refs(refs);\n+\tstruct ref_dir *loose_dir = get_loose_refs(refs);\n \n-\tretval = do_for_each_ref_in_dir(&extra_refs, 0,\n+\tif (base && *base) {\n+\t\textra_dir = find_containing_dir(extra_dir, base, 0);\n+\t\tpacked_dir = find_containing_dir(packed_dir, base, 0);\n+\t\tloose_dir = find_containing_dir(loose_dir, base, 0);\n+\t}\n+\n+\tif (extra_dir)\n+\t\tretval = do_for_each_ref_in_dir(\n+\t\t\t\textra_dir, 0,\n+\t\t\t\tbase, fn, trim, flags, cb_data);\n+\tif (!retval) {\n+\t\tif (packed_dir && loose_dir)\n+\t\t\tretval = do_for_each_ref_in_dirs(\n+\t\t\t\t\tpacked_dir, loose_dir,\n+\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t\telse if (packed_dir)\n+\t\t\tretval = do_for_each_ref_in_dir(\n+\t\t\t\t\tpacked_dir, 0,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n-\tif (!retval)\n-\t\tretval = do_for_each_ref_in_dirs(get_packed_refs(refs),\n-\t\t\t\t\t\t get_loose_refs(refs),\n-\t\t\t\t\t\t base, fn, trim, flags, cb_data);\n+\t\telse if (loose_dir)\n+\t\t\tretval = do_for_each_ref_in_dir(\n+\t\t\t\t\tloose_dir, 0,\n+\t\t\t\t\tbase, fn, trim, flags, cb_data);\n+\t}\n \n \tcurrent_ref = NULL;\n \treturn retval;\n-- \n1.7.7\n"},{"id":"178450","messageId":"1319804921-17545-11-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 10/28] get_ref_dir(): keep track of the current ref_dir","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:23Z","receivedAt":"2011-10-28T12:28:23Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nLook up the ref_dir that will hold the new entries once at the start\nof processing of a directory.  This eliminates the need to search down\nthe reference tree to find the place to put each new reference.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   43 ++++++++++++++++++++++++++-----------------\n 1 files changed, 26 insertions(+), 17 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 515b44c..bdd90c5 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -775,29 +775,36 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)\n \treturn &refs->packed;\n }\n \n-static void get_ref_dir(struct ref_cache *refs, const char *base,\n-\t\t\tstruct ref_dir *dir)\n+/*\n+ * dirname must match the name associated with dir; in particular, it\n+ * must end with '/'.\n+ */\n+static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n {\n \tDIR *d;\n-\tconst char *path;\n+\tchar *path;\n+\tint dirnamelen = strlen(dirname);\n+\tint pathlen;\n+\tstruct ref_dir *dir;\n+\n+\tassert(dirnamelen && dirname[dirnamelen - 1] == '/');\n+\n+\tdir = find_containing_dir(&refs->loose, dirname, 1);\n \n \tif (*refs->name)\n-\t\tpath = git_path_submodule(refs->name, \"%s\", base);\n+\t\tpath = git_path_submodule(refs->name, \"%s\", dirname);\n \telse\n-\t\tpath = git_path(\"%s\", base);\n-\n+\t\tpath = git_path(\"%s\", dirname);\n+\tpathlen = strlen(path);\n+\tassert(pathlen && path[pathlen - 1] == '/');\n+\tpath[pathlen - 1] = '\\0';\n \n \td = opendir(path);\n \n \tif (d) {\n \t\tstruct dirent *de;\n-\t\tint baselen = strlen(base);\n-\t\tchar *refname = xmalloc(baselen + 257);\n-\n-\t\tmemcpy(refname, base, baselen);\n-\t\tif (baselen && base[baselen-1] != '/')\n-\t\t\trefname[baselen++] = '/';\n-\n+\t\tchar *refname = xmalloc(dirnamelen + 257);\n+\t\tmemcpy(refname, dirname, dirnamelen);\n \t\twhile ((de = readdir(d)) != NULL) {\n \t\t\tunsigned char sha1[20];\n \t\t\tstruct stat st;\n@@ -812,14 +819,16 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\tcontinue;\n \t\t\tif (has_extension(de->d_name, \".lock\"))\n \t\t\t\tcontinue;\n-\t\t\tmemcpy(refname + baselen, de->d_name, namelen+1);\n+\t\t\tmemcpy(refname + dirnamelen, de->d_name, namelen+1);\n \t\t\trefdir = *refs->name\n \t\t\t\t? git_path_submodule(refs->name, \"%s\", refname)\n \t\t\t\t: git_path(\"%s\", refname);\n \t\t\tif (stat(refdir, &st) < 0)\n \t\t\t\tcontinue;\n \t\t\tif (S_ISDIR(st.st_mode)) {\n-\t\t\t\tget_ref_dir(refs, refname, dir);\n+\t\t\t\trefname[dirnamelen + namelen] = '/';\n+\t\t\t\trefname[dirnamelen + namelen + 1] = '\\0';\n+\t\t\t\tget_ref_dir(refs, refname);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (*refs->name) {\n@@ -834,7 +843,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\t\thashclr(sha1);\n \t\t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t\t}\n-\t\t\tadd_ref(dir, create_ref_entry(refname, sha1, flag));\n+\t\t\tadd_entry_to_dir(dir, create_ref_entry(refname, sha1, flag));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(d);\n@@ -844,7 +853,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n static struct ref_dir *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->did_loose) {\n-\t\tget_ref_dir(refs, \"refs\", &refs->loose);\n+\t\tget_ref_dir(refs, \"refs/\");\n \t\trefs->did_loose = 1;\n \t}\n \treturn &refs->loose;\n-- \n1.7.7\n"},{"id":"178452","messageId":"1319804921-17545-12-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 11/28] refs: wrap top-level ref_dirs in ref_entries","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:24Z","receivedAt":"2011-10-28T12:28:24Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nWrap the top-level ref_dirs in REF_DIR style ref_entries so that we\nhave the flag and name available when dealing with them.  This\naffects:\n\n* cache_ref::loose\n* cache_ref::packed\n* extra_refs\n\nThe next several commits will expand the use of ref_entry as opposed\nto ref_dir, culminating in the ability of a ref_entry representing a\ndirectory of loose references to load itself only when used.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   54 ++++++++++++++++++++++++++++++------------------------\n 1 files changed, 30 insertions(+), 24 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex bdd90c5..673fa04 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -256,7 +256,7 @@ static void clear_ref_dir(struct ref_dir *dir)\n /*\n  * Create a struct ref_entry object for the specified dirname.\n  * dirname is the name of the directory with a trailing slash (e.g.,\n- * \"refs/heads/\").\n+ * \"refs/heads/\") or \"\" for the top-level directory.\n  */\n static struct ref_entry *create_dir_entry(const char *dirname)\n {\n@@ -609,28 +609,28 @@ static int is_refname_available(const char *refname, const char *oldrefname,\n  */\n static struct ref_cache {\n \tstruct ref_cache *next;\n-\tchar did_loose;\n-\tchar did_packed;\n-\tstruct ref_dir loose;\n-\tstruct ref_dir packed;\n+\tstruct ref_entry *loose;\n+\tstruct ref_entry *packed;\n \t/* The submodule name, or \"\" for the main repo. */\n \tchar name[FLEX_ARRAY];\n } *ref_cache;\n \n-static struct ref_dir extra_refs;\n+static struct ref_entry *extra_refs;\n \n static void clear_packed_ref_cache(struct ref_cache *refs)\n {\n-\tif (refs->did_packed)\n-\t\tclear_ref_dir(&refs->packed);\n-\trefs->did_packed = 0;\n+\tif (refs->packed) {\n+\t\tfree_ref_entry(refs->packed);\n+\t\trefs->packed = NULL;\n+\t}\n }\n \n static void clear_loose_ref_cache(struct ref_cache *refs)\n {\n-\tif (refs->did_loose)\n-\t\tclear_ref_dir(&refs->loose);\n-\trefs->did_loose = 0;\n+\tif (refs->loose) {\n+\t\tfree_ref_entry(refs->loose);\n+\t\trefs->loose = NULL;\n+\t}\n }\n \n static struct ref_cache *create_ref_cache(const char *submodule)\n@@ -747,32 +747,37 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n \n void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n {\n-\tadd_ref(&extra_refs, create_ref_entry(refname, sha1, flag));\n+\tif (!extra_refs)\n+\t\textra_refs = create_dir_entry(\"\");\n+\tadd_ref(&extra_refs->u.subdir, create_ref_entry(refname, sha1, flag));\n }\n \n void clear_extra_refs(void)\n {\n-\tclear_ref_dir(&extra_refs);\n+\tif (extra_refs) {\n+\t\tfree_ref_entry(extra_refs);\n+\t\textra_refs = NULL;\n+\t}\n }\n \n static struct ref_dir *get_packed_refs(struct ref_cache *refs)\n {\n-\tif (!refs->did_packed) {\n+\tif (!refs->packed) {\n \t\tconst char *packed_refs_file;\n \t\tFILE *f;\n \n+\t\trefs->packed = create_dir_entry(\"\");\n \t\tif (*refs->name)\n \t\t\tpacked_refs_file = git_path_submodule(refs->name, \"packed-refs\");\n \t\telse\n \t\t\tpacked_refs_file = git_path(\"packed-refs\");\n \t\tf = fopen(packed_refs_file, \"r\");\n \t\tif (f) {\n-\t\t\tread_packed_refs(f, &refs->packed);\n+\t\t\tread_packed_refs(f, &refs->packed->u.subdir);\n \t\t\tfclose(f);\n \t\t}\n-\t\trefs->did_packed = 1;\n \t}\n-\treturn &refs->packed;\n+\treturn &refs->packed->u.subdir;\n }\n \n /*\n@@ -789,7 +794,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \n \tassert(dirnamelen && dirname[dirnamelen - 1] == '/');\n \n-\tdir = find_containing_dir(&refs->loose, dirname, 1);\n+\tdir = find_containing_dir(&refs->loose->u.subdir, dirname, 1);\n \n \tif (*refs->name)\n \t\tpath = git_path_submodule(refs->name, \"%s\", dirname);\n@@ -852,11 +857,11 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \n static struct ref_dir *get_loose_refs(struct ref_cache *refs)\n {\n-\tif (!refs->did_loose) {\n+\tif (!refs->loose) {\n+\t\trefs->loose = create_dir_entry(\"\");\n \t\tget_ref_dir(refs, \"refs/\");\n-\t\trefs->did_loose = 1;\n \t}\n-\treturn &refs->loose;\n+\treturn &refs->loose->u.subdir;\n }\n \n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n@@ -1165,12 +1170,13 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n {\n \tint retval = 0;\n \tstruct ref_cache *refs = get_ref_cache(submodule);\n-\tstruct ref_dir *extra_dir = &extra_refs;\n+\tstruct ref_dir *extra_dir = extra_refs ? &extra_refs->u.subdir : NULL;\n \tstruct ref_dir *packed_dir = get_packed_refs(refs);\n \tstruct ref_dir *loose_dir = get_loose_refs(refs);\n \n \tif (base && *base) {\n-\t\textra_dir = find_containing_dir(extra_dir, base, 0);\n+\t\tif (extra_dir)\n+\t\t\textra_dir = find_containing_dir(extra_dir, base, 0);\n \t\tpacked_dir = find_containing_dir(packed_dir, base, 0);\n \t\tloose_dir = find_containing_dir(loose_dir, base, 0);\n \t}\n-- \n1.7.7\n"},{"id":"178447","messageId":"1319804921-17545-13-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 12/28] get_packed_refs(): return (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:25Z","receivedAt":"2011-10-28T12:28:25Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   33 +++++++++++++++++----------------\n 1 files changed, 17 insertions(+), 16 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 673fa04..425fdaa 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -760,7 +760,7 @@ void clear_extra_refs(void)\n \t}\n }\n \n-static struct ref_dir *get_packed_refs(struct ref_cache *refs)\n+static struct ref_entry *get_packed_refs(struct ref_cache *refs)\n {\n \tif (!refs->packed) {\n \t\tconst char *packed_refs_file;\n@@ -777,7 +777,7 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)\n \t\t\tfclose(f);\n \t\t}\n \t}\n-\treturn &refs->packed->u.subdir;\n+\treturn refs->packed;\n }\n \n /*\n@@ -877,9 +877,9 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n \t\t\t\t      const char *refname, unsigned char *sha1)\n {\n \tstruct ref_entry *ref;\n-\tstruct ref_dir *dir = get_packed_refs(refs);\n+\tstruct ref_entry *direntry = get_packed_refs(refs);\n \n-\tref = find_ref(dir, refname);\n+\tref = find_ref(&direntry->u.subdir, refname);\n \tif (ref == NULL)\n \t\treturn -1;\n \n@@ -950,8 +950,8 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh\n  */\n static int get_packed_ref(const char *refname, unsigned char *sha1)\n {\n-\tstruct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));\n-\tstruct ref_entry *entry = find_ref(packed, refname);\n+\tstruct ref_entry *packed = get_packed_refs(get_ref_cache(NULL));\n+\tstruct ref_entry *entry = find_ref(&packed->u.subdir, refname);\n \tif (entry) {\n \t\thashcpy(sha1, entry->u.value.sha1);\n \t\treturn 0;\n@@ -1110,8 +1110,8 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \t\treturn -1;\n \n \tif ((flag & REF_ISPACKED)) {\n-\t\tstruct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));\n-\t\tstruct ref_entry *r = find_ref(dir, refname);\n+\t\tstruct ref_entry *direntry = get_packed_refs(get_ref_cache(NULL));\n+\t\tstruct ref_entry *r = find_ref(&direntry->u.subdir, refname);\n \n \t\tif (r != NULL && r->flag & REF_KNOWS_PEELED) {\n \t\t\thashcpy(sha1, r->u.value.peeled);\n@@ -1171,7 +1171,8 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \tint retval = 0;\n \tstruct ref_cache *refs = get_ref_cache(submodule);\n \tstruct ref_dir *extra_dir = extra_refs ? &extra_refs->u.subdir : NULL;\n-\tstruct ref_dir *packed_dir = get_packed_refs(refs);\n+\tstruct ref_entry *packed_direntry = get_packed_refs(refs);\n+\tstruct ref_dir *packed_dir = &packed_direntry->u.subdir;\n \tstruct ref_dir *loose_dir = get_loose_refs(refs);\n \n \tif (base && *base) {\n@@ -1564,7 +1565,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \t * name is a proper prefix of our refname.\n \t */\n \tif (missing &&\n-\t     !is_refname_available(refname, NULL, get_packed_refs(get_ref_cache(NULL)))) {\n+\t     !is_refname_available(refname, NULL,\n+\t\t\t\t   &get_packed_refs(get_ref_cache(NULL))->u.subdir)) {\n \t\tlast_errno = ENOTDIR;\n \t\tgoto error_return;\n \t}\n@@ -1644,10 +1646,8 @@ static struct lock_file packlock;\n static int repack_without_ref(const char *refname)\n {\n \tstruct repack_without_ref_sb data;\n-\tstruct ref_dir *packed;\n-\n-\tpacked = get_packed_refs(get_ref_cache(NULL));\n-\tif (find_ref(packed, refname) == NULL)\n+\tstruct ref_entry *packed = get_packed_refs(get_ref_cache(NULL));\n+\tif (find_ref(&packed->u.subdir, refname) == NULL)\n \t\treturn 0;\n \tdata.refname = refname;\n \tdata.fd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n@@ -1655,7 +1655,8 @@ static int repack_without_ref(const char *refname)\n \t\tunable_to_lock_error(git_path(\"packed-refs\"), errno);\n \t\treturn error(\"cannot delete '%s' from packed refs\", refname);\n \t}\n-\tdo_for_each_ref_in_dir(packed, 0, \"\", repack_without_ref_fn, 0, 0, &data);\n+\tdo_for_each_ref_in_dir(&packed->u.subdir, 0,\n+\t\t\t       \"\", repack_without_ref_fn, 0, 0, &data);\n \treturn commit_lock_file(&packlock);\n }\n \n@@ -1726,7 +1727,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms\n \tif (!symref)\n \t\treturn error(\"refname %s not found\", oldrefname);\n \n-\tif (!is_refname_available(newrefname, oldrefname, get_packed_refs(refs)))\n+\tif (!is_refname_available(newrefname, oldrefname, &get_packed_refs(refs)->u.subdir))\n \t\treturn 1;\n \n \tif (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))\n-- \n1.7.7\n"},{"id":"178453","messageId":"1319804921-17545-14-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 13/28] get_loose_refs(): return (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:26Z","receivedAt":"2011-10-28T12:28:26Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    9 +++++----\n 1 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 425fdaa..d6d5d0e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -855,13 +855,13 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \t}\n }\n \n-static struct ref_dir *get_loose_refs(struct ref_cache *refs)\n+static struct ref_entry *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->loose) {\n \t\trefs->loose = create_dir_entry(\"\");\n \t\tget_ref_dir(refs, \"refs/\");\n \t}\n-\treturn &refs->loose->u.subdir;\n+\treturn refs->loose;\n }\n \n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n@@ -1173,7 +1173,8 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \tstruct ref_dir *extra_dir = extra_refs ? &extra_refs->u.subdir : NULL;\n \tstruct ref_entry *packed_direntry = get_packed_refs(refs);\n \tstruct ref_dir *packed_dir = &packed_direntry->u.subdir;\n-\tstruct ref_dir *loose_dir = get_loose_refs(refs);\n+\tstruct ref_entry *loose_direntry = get_loose_refs(refs);\n+\tstruct ref_dir *loose_dir = &loose_direntry->u.subdir;\n \n \tif (base && *base) {\n \t\tif (extra_dir)\n@@ -1730,7 +1731,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms\n \tif (!is_refname_available(newrefname, oldrefname, &get_packed_refs(refs)->u.subdir))\n \t\treturn 1;\n \n-\tif (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))\n+\tif (!is_refname_available(newrefname, oldrefname, &get_loose_refs(refs)->u.subdir))\n \t\treturn 1;\n \n \tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n-- \n1.7.7\n"},{"id":"178454","messageId":"1319804921-17545-15-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 14/28] is_refname_available(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:27Z","receivedAt":"2011-10-28T12:28:27Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   12 +++++++-----\n 1 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex d6d5d0e..6bda033 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -586,14 +586,16 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh\n  * operation).\n  */\n static int is_refname_available(const char *refname, const char *oldrefname,\n-\t\t\t\tstruct ref_dir *dir)\n+\t\t\t\tstruct ref_entry *direntry)\n {\n \tstruct name_conflict_cb data;\n \tdata.refname = refname;\n \tdata.oldrefname = oldrefname;\n \tdata.conflicting_refname = NULL;\n \n-\tif (do_for_each_ref_in_dir(dir, 0, \"\", name_conflict_fn,\n+\tassert(direntry->flag & REF_DIR);\n+\n+\tif (do_for_each_ref_in_dir(&direntry->u.subdir, 0, \"\", name_conflict_fn,\n \t\t\t\t   0, DO_FOR_EACH_INCLUDE_BROKEN,\n \t\t\t\t   &data)) {\n \t\terror(\"'%s' exists; cannot create '%s'\",\n@@ -1567,7 +1569,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n \t */\n \tif (missing &&\n \t     !is_refname_available(refname, NULL,\n-\t\t\t\t   &get_packed_refs(get_ref_cache(NULL))->u.subdir)) {\n+\t\t\t\t   get_packed_refs(get_ref_cache(NULL)))) {\n \t\tlast_errno = ENOTDIR;\n \t\tgoto error_return;\n \t}\n@@ -1728,10 +1730,10 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms\n \tif (!symref)\n \t\treturn error(\"refname %s not found\", oldrefname);\n \n-\tif (!is_refname_available(newrefname, oldrefname, &get_packed_refs(refs)->u.subdir))\n+\tif (!is_refname_available(newrefname, oldrefname, get_packed_refs(refs)))\n \t\treturn 1;\n \n-\tif (!is_refname_available(newrefname, oldrefname, &get_loose_refs(refs)->u.subdir))\n+\tif (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))\n \t\treturn 1;\n \n \tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n-- \n1.7.7\n"},{"id":"178455","messageId":"1319804921-17545-16-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 15/28] find_ref(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:28Z","receivedAt":"2011-10-28T12:28:28Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   14 ++++++++------\n 1 files changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6bda033..e7d4482 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -363,10 +363,12 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,\n  * subdirectories as necessary.  If the name is not found or it\n  * corresponds to a directory entry, return NULL.\n  */\n-static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)\n+static struct ref_entry *find_ref(struct ref_entry *direntry, const char *refname)\n {\n \tstruct ref_entry *entry;\n-\tdir = find_containing_dir(dir, refname, 0);\n+\tstruct ref_dir *dir;\n+\tassert(direntry->flag & REF_DIR);\n+\tdir = find_containing_dir(&direntry->u.subdir, refname, 0);\n \tif (!dir)\n \t\treturn NULL;\n \tentry = search_ref_dir(dir, refname);\n@@ -881,7 +883,7 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n \tstruct ref_entry *ref;\n \tstruct ref_entry *direntry = get_packed_refs(refs);\n \n-\tref = find_ref(&direntry->u.subdir, refname);\n+\tref = find_ref(direntry, refname);\n \tif (ref == NULL)\n \t\treturn -1;\n \n@@ -953,7 +955,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh\n static int get_packed_ref(const char *refname, unsigned char *sha1)\n {\n \tstruct ref_entry *packed = get_packed_refs(get_ref_cache(NULL));\n-\tstruct ref_entry *entry = find_ref(&packed->u.subdir, refname);\n+\tstruct ref_entry *entry = find_ref(packed, refname);\n \tif (entry) {\n \t\thashcpy(sha1, entry->u.value.sha1);\n \t\treturn 0;\n@@ -1113,7 +1115,7 @@ int peel_ref(const char *refname, unsigned char *sha1)\n \n \tif ((flag & REF_ISPACKED)) {\n \t\tstruct ref_entry *direntry = get_packed_refs(get_ref_cache(NULL));\n-\t\tstruct ref_entry *r = find_ref(&direntry->u.subdir, refname);\n+\t\tstruct ref_entry *r = find_ref(direntry, refname);\n \n \t\tif (r != NULL && r->flag & REF_KNOWS_PEELED) {\n \t\t\thashcpy(sha1, r->u.value.peeled);\n@@ -1650,7 +1652,7 @@ static int repack_without_ref(const char *refname)\n {\n \tstruct repack_without_ref_sb data;\n \tstruct ref_entry *packed = get_packed_refs(get_ref_cache(NULL));\n-\tif (find_ref(&packed->u.subdir, refname) == NULL)\n+\tif (find_ref(packed, refname) == NULL)\n \t\treturn 0;\n \tdata.refname = refname;\n \tdata.fd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n-- \n1.7.7\n"},{"id":"178446","messageId":"1319804921-17545-17-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 16/28] read_packed_refs(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:29Z","receivedAt":"2011-10-28T12:28:29Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    7 ++++---\n 1 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex e7d4482..f402d39 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -715,12 +715,13 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)\n \treturn line;\n }\n \n-static void read_packed_refs(FILE *f, struct ref_dir *dir)\n+static void read_packed_refs(FILE *f, struct ref_entry *direntry)\n {\n \tstruct ref_entry *last = NULL;\n \tchar refline[PATH_MAX];\n \tint flag = REF_ISPACKED;\n \n+\tassert(direntry->flag & REF_DIR);\n \twhile (fgets(refline, sizeof(refline), f)) {\n \t\tunsigned char sha1[20];\n \t\tconst char *refname;\n@@ -737,7 +738,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)\n \t\trefname = parse_ref_line(refline, sha1);\n \t\tif (refname) {\n \t\t\tlast = create_ref_entry(refname, sha1, flag);\n-\t\t\tadd_ref(dir, last);\n+\t\t\tadd_ref(&direntry->u.subdir, last);\n \t\t\tcontinue;\n \t\t}\n \t\tif (last &&\n@@ -777,7 +778,7 @@ static struct ref_entry *get_packed_refs(struct ref_cache *refs)\n \t\t\tpacked_refs_file = git_path(\"packed-refs\");\n \t\tf = fopen(packed_refs_file, \"r\");\n \t\tif (f) {\n-\t\t\tread_packed_refs(f, &refs->packed->u.subdir);\n+\t\t\tread_packed_refs(f, refs->packed);\n \t\t\tfclose(f);\n \t\t}\n \t}\n-- \n1.7.7\n"},{"id":"178451","messageId":"1319804921-17545-18-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 17/28] add_ref(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:30Z","receivedAt":"2011-10-28T12:28:30Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   10 ++++++----\n 1 files changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f402d39..f5aaafe 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -380,9 +380,11 @@ static struct ref_entry *find_ref(struct ref_entry *direntry, const char *refnam\n  * subdirectories as necessary.  dir must represent the top-level\n  * directory.  Return 0 on success.\n  */\n-static int add_ref(struct ref_dir *dir, struct ref_entry *ref)\n+static int add_ref(struct ref_entry *direntry, struct ref_entry *ref)\n {\n-\tdir = find_containing_dir(dir, ref->name, 1);\n+\tstruct ref_dir *dir;\n+\tassert(direntry->flag & REF_DIR);\n+\tdir = find_containing_dir(&direntry->u.subdir, ref->name, 1);\n \tif (!dir)\n \t\treturn -1;\n \tadd_entry_to_dir(dir, ref);\n@@ -738,7 +740,7 @@ static void read_packed_refs(FILE *f, struct ref_entry *direntry)\n \t\trefname = parse_ref_line(refline, sha1);\n \t\tif (refname) {\n \t\t\tlast = create_ref_entry(refname, sha1, flag);\n-\t\t\tadd_ref(&direntry->u.subdir, last);\n+\t\t\tadd_ref(direntry, last);\n \t\t\tcontinue;\n \t\t}\n \t\tif (last &&\n@@ -754,7 +756,7 @@ void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n {\n \tif (!extra_refs)\n \t\textra_refs = create_dir_entry(\"\");\n-\tadd_ref(&extra_refs->u.subdir, create_ref_entry(refname, sha1, flag));\n+\tadd_ref(extra_refs, create_ref_entry(refname, sha1, flag));\n }\n \n void clear_extra_refs(void)\n-- \n1.7.7\n"},{"id":"178449","messageId":"1319804921-17545-19-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 18/28] find_containing_direntry(): use (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:31Z","receivedAt":"2011-10-28T12:28:31Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nChange type of both argument and return value.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   78 ++++++++++++++++++++++++++++++++--------------------------------\n 1 files changed, 39 insertions(+), 39 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f5aaafe..35b3ff2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -324,38 +324,40 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname\n }\n \n /*\n- * If refname is a reference name, find the ref_dir within the dir\n+ * If refname is a reference name, find the ref_entry within the dir\n  * tree that should hold refname.  If refname is a directory name\n- * (i.e., ends in '/'), then return that ref_dir itself.  dir must\n- * represent the top-level directory.  Recurse into subdirectories as\n- * necessary.  If mkdir is set, then create any missing directories;\n- * otherwise, return NULL if the desired directory cannot be found.\n+ * (i.e., \"\" or ends in '/'), then return that ref_entry itself.  dir\n+ * must represent the top-level directory.  Recurse into\n+ * subdirectories as necessary.  If mkdir is set, then create any\n+ * missing directories; otherwise, return NULL if the desired\n+ * directory cannot be found.\n  */\n-static struct ref_dir *find_containing_dir(struct ref_dir *dir,\n-\t\t\t\t\t   const char *refname, int mkdir)\n+static struct ref_entry *find_containing_direntry(struct ref_entry *direntry,\n+\t\t\t\t\t\t  const char *refname, int mkdir)\n {\n \tchar *refname_copy = xstrdup(refname);\n \tchar *slash;\n-\tstruct ref_entry *entry;\n+\tassert(direntry->flag & REF_DIR);\n \tfor (slash = strchr(refname_copy, '/'); slash; slash = strchr(slash + 1, '/')) {\n \t\tchar tmp = slash[1];\n+\t\tstruct ref_entry *entry;\n \t\tslash[1] = '\\0';\n-\t\tentry = search_ref_dir(dir, refname_copy);\n+\t\tentry = search_ref_dir(&direntry->u.subdir, refname_copy);\n \t\tif (!entry) {\n \t\t\tif (!mkdir) {\n-\t\t\t\tdir = NULL;\n+\t\t\t\tdirentry = NULL;\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tentry = create_dir_entry(refname_copy);\n-\t\t\tadd_entry_to_dir(dir, entry);\n+\t\t\tadd_entry_to_dir(&direntry->u.subdir, entry);\n \t\t}\n \t\tslash[1] = tmp;\n \t\tassert(entry->flag & REF_DIR);\n-\t\tdir = &entry->u.subdir;\n+\t\tdirentry = entry;\n \t}\n \n \tfree(refname_copy);\n-\treturn dir;\n+\treturn direntry;\n }\n \n /*\n@@ -366,12 +368,11 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,\n static struct ref_entry *find_ref(struct ref_entry *direntry, const char *refname)\n {\n \tstruct ref_entry *entry;\n-\tstruct ref_dir *dir;\n \tassert(direntry->flag & REF_DIR);\n-\tdir = find_containing_dir(&direntry->u.subdir, refname, 0);\n-\tif (!dir)\n+\tdirentry = find_containing_direntry(direntry, refname, 0);\n+\tif (!direntry)\n \t\treturn NULL;\n-\tentry = search_ref_dir(dir, refname);\n+\tentry = search_ref_dir(&direntry->u.subdir, refname);\n \treturn (entry && !(entry->flag & REF_DIR)) ? entry : NULL;\n }\n \n@@ -382,12 +383,11 @@ static struct ref_entry *find_ref(struct ref_entry *direntry, const char *refnam\n  */\n static int add_ref(struct ref_entry *direntry, struct ref_entry *ref)\n {\n-\tstruct ref_dir *dir;\n \tassert(direntry->flag & REF_DIR);\n-\tdir = find_containing_dir(&direntry->u.subdir, ref->name, 1);\n-\tif (!dir)\n+\tdirentry = find_containing_direntry(direntry, ref->name, 1);\n+\tif (!direntry)\n \t\treturn -1;\n-\tadd_entry_to_dir(dir, ref);\n+\tadd_entry_to_dir(&direntry->u.subdir, ref);\n \treturn 0;\n }\n \n@@ -797,11 +797,11 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \tchar *path;\n \tint dirnamelen = strlen(dirname);\n \tint pathlen;\n-\tstruct ref_dir *dir;\n+\tstruct ref_entry *direntry;\n \n \tassert(dirnamelen && dirname[dirnamelen - 1] == '/');\n \n-\tdir = find_containing_dir(&refs->loose->u.subdir, dirname, 1);\n+\tdirentry = find_containing_direntry(refs->loose, dirname, 1);\n \n \tif (*refs->name)\n \t\tpath = git_path_submodule(refs->name, \"%s\", dirname);\n@@ -855,7 +855,8 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \t\t\t\t\thashclr(sha1);\n \t\t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t\t}\n-\t\t\tadd_entry_to_dir(dir, create_ref_entry(refname, sha1, flag));\n+\t\t\tadd_entry_to_dir(&direntry->u.subdir,\n+\t\t\t\t\t create_ref_entry(refname, sha1, flag));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(d);\n@@ -1177,35 +1178,34 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n {\n \tint retval = 0;\n \tstruct ref_cache *refs = get_ref_cache(submodule);\n-\tstruct ref_dir *extra_dir = extra_refs ? &extra_refs->u.subdir : NULL;\n+\tstruct ref_entry *extra_direntry = extra_refs;\n \tstruct ref_entry *packed_direntry = get_packed_refs(refs);\n-\tstruct ref_dir *packed_dir = &packed_direntry->u.subdir;\n \tstruct ref_entry *loose_direntry = get_loose_refs(refs);\n-\tstruct ref_dir *loose_dir = &loose_direntry->u.subdir;\n \n \tif (base && *base) {\n-\t\tif (extra_dir)\n-\t\t\textra_dir = find_containing_dir(extra_dir, base, 0);\n-\t\tpacked_dir = find_containing_dir(packed_dir, base, 0);\n-\t\tloose_dir = find_containing_dir(loose_dir, base, 0);\n+\t\tif (extra_direntry)\n+\t\t\textra_direntry = find_containing_direntry(extra_direntry, base, 0);\n+\t\tpacked_direntry = find_containing_direntry(packed_direntry, base, 0);\n+\t\tloose_direntry = find_containing_direntry(loose_direntry, base, 0);\n \t}\n \n-\tif (extra_dir)\n+\tif (extra_direntry)\n \t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\textra_dir, 0,\n+\t\t\t\t&extra_direntry->u.subdir, 0,\n \t\t\t\tbase, fn, trim, flags, cb_data);\n \tif (!retval) {\n-\t\tif (packed_dir && loose_dir)\n+\t\tif (packed_direntry && loose_direntry)\n \t\t\tretval = do_for_each_ref_in_dirs(\n-\t\t\t\t\tpacked_dir, loose_dir,\n+\t\t\t\t\t&packed_direntry->u.subdir,\n+\t\t\t\t\t&loose_direntry->u.subdir,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n-\t\telse if (packed_dir)\n+\t\telse if (packed_direntry)\n \t\t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\t\tpacked_dir, 0,\n+\t\t\t\t\t&packed_direntry->u.subdir, 0,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n-\t\telse if (loose_dir)\n+\t\telse if (loose_direntry)\n \t\t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\t\tloose_dir, 0,\n+\t\t\t\t\t&loose_direntry->u.subdir, 0,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t}\n \n-- \n1.7.7\n"},{"id":"178444","messageId":"1319804921-17545-20-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 19/28] search_ref_dir(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:32Z","receivedAt":"2011-10-28T12:28:32Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    9 ++++++---\n 1 files changed, 6 insertions(+), 3 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 35b3ff2..e91cc15 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -288,11 +288,14 @@ static void sort_ref_dir(struct ref_dir *dir);\n  * Return the entry with the given refname from the ref_dir\n  * (non-recursively).  Return NULL if no such entry is found.\n  */\n-static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)\n+static struct ref_entry *search_ref_dir(struct ref_entry *direntry, const char *refname)\n {\n \tstruct ref_entry *e, **r;\n \tint len;\n+\tstruct ref_dir *dir;\n \n+\tassert(direntry->flag & REF_DIR);\n+\tdir = &direntry->u.subdir;\n \tif (refname == NULL || !dir->nr)\n \t\treturn NULL;\n \n@@ -342,7 +345,7 @@ static struct ref_entry *find_containing_direntry(struct ref_entry *direntry,\n \t\tchar tmp = slash[1];\n \t\tstruct ref_entry *entry;\n \t\tslash[1] = '\\0';\n-\t\tentry = search_ref_dir(&direntry->u.subdir, refname_copy);\n+\t\tentry = search_ref_dir(direntry, refname_copy);\n \t\tif (!entry) {\n \t\t\tif (!mkdir) {\n \t\t\t\tdirentry = NULL;\n@@ -372,7 +375,7 @@ static struct ref_entry *find_ref(struct ref_entry *direntry, const char *refnam\n \tdirentry = find_containing_direntry(direntry, refname, 0);\n \tif (!direntry)\n \t\treturn NULL;\n-\tentry = search_ref_dir(&direntry->u.subdir, refname);\n+\tentry = search_ref_dir(direntry, refname);\n \treturn (entry && !(entry->flag & REF_DIR)) ? entry : NULL;\n }\n \n-- \n1.7.7\n"},{"id":"178456","messageId":"1319804921-17545-21-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 20/28] add_entry(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:33Z","receivedAt":"2011-10-28T12:28:33Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   12 +++++++-----\n 1 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex e91cc15..3567d56 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -234,8 +234,11 @@ static void free_ref_entry(struct ref_entry *entry)\n  * stored directly in dir; no recursion into subdirectories is\n  * done.\n  */\n-static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)\n+static void add_entry(struct ref_entry *direntry, struct ref_entry *entry)\n {\n+\tstruct ref_dir *dir;\n+\tassert(direntry->flag & REF_DIR);\n+\tdir = &direntry->u.subdir;\n \tALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);\n \tdir->entries[dir->nr++] = entry;\n }\n@@ -352,7 +355,7 @@ static struct ref_entry *find_containing_direntry(struct ref_entry *direntry,\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\tentry = create_dir_entry(refname_copy);\n-\t\t\tadd_entry_to_dir(&direntry->u.subdir, entry);\n+\t\t\tadd_entry(direntry, entry);\n \t\t}\n \t\tslash[1] = tmp;\n \t\tassert(entry->flag & REF_DIR);\n@@ -390,7 +393,7 @@ static int add_ref(struct ref_entry *direntry, struct ref_entry *ref)\n \tdirentry = find_containing_direntry(direntry, ref->name, 1);\n \tif (!direntry)\n \t\treturn -1;\n-\tadd_entry_to_dir(&direntry->u.subdir, ref);\n+\tadd_entry(direntry, ref);\n \treturn 0;\n }\n \n@@ -858,8 +861,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \t\t\t\t\thashclr(sha1);\n \t\t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t\t}\n-\t\t\tadd_entry_to_dir(&direntry->u.subdir,\n-\t\t\t\t\t create_ref_entry(refname, sha1, flag));\n+\t\t\tadd_entry(direntry, create_ref_entry(refname, sha1, flag));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(d);\n-- \n1.7.7\n"},{"id":"178457","messageId":"1319804921-17545-22-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 21/28] do_for_each_ref_in_dir*(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:34Z","receivedAt":"2011-10-28T12:28:34Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   38 ++++++++++++++++++++++----------------\n 1 files changed, 22 insertions(+), 16 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 3567d56..c3ee93d 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -471,17 +471,20 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n \treturn fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);\n }\n \n-static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,\n+static int do_for_each_ref_in_dir(struct ref_entry *direntry, int offset,\n \t\t\t\t  const char *base,\n \t\t\t\t  each_ref_fn fn, int trim, int flags, void *cb_data)\n {\n \tint i;\n+\tstruct ref_dir *dir;\n+\tassert(direntry->flag & REF_DIR);\n+\tdir = &direntry->u.subdir;\n \tsort_ref_dir(dir);\n \tfor (i = offset; i < dir->nr; i++) {\n \t\tstruct ref_entry *entry = dir->entries[i];\n \t\tint retval;\n \t\tif (entry->flag & REF_DIR) {\n-\t\t\tretval = do_for_each_ref_in_dir(&entry->u.subdir, 0,\n+\t\t\tretval = do_for_each_ref_in_dir(entry, 0,\n \t\t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t\t} else {\n \t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, entry);\n@@ -492,25 +495,30 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,\n \treturn 0;\n }\n \n-static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n-\t\t\t\t   struct ref_dir *dir2,\n+static int do_for_each_ref_in_dirs(struct ref_entry *direntry1,\n+\t\t\t\t   struct ref_entry *direntry2,\n \t\t\t\t   const char *base, each_ref_fn fn, int trim,\n \t\t\t\t   int flags, void *cb_data)\n {\n \tint retval;\n \tint i1 = 0, i2 = 0;\n+\tstruct ref_dir *dir1, *dir2;\n \n+\tassert(direntry1->flag & REF_DIR);\n+\tassert(direntry2->flag & REF_DIR);\n+\tdir1 = &direntry1->u.subdir;\n+\tdir2 = &direntry2->u.subdir;\n \tsort_ref_dir(dir1);\n \tsort_ref_dir(dir2);\n \twhile (1) {\n \t\tstruct ref_entry *e1, *e2, *entry;\n \t\tint cmp;\n \t\tif (i1 == dir1->nr) {\n-\t\t\treturn do_for_each_ref_in_dir(dir2, i2,\n+\t\t\treturn do_for_each_ref_in_dir(direntry2, i2,\n \t\t\t\t\t\t      base, fn, trim, flags, cb_data);\n \t\t}\n \t\tif (i2 == dir2->nr) {\n-\t\t\treturn do_for_each_ref_in_dir(dir1, i1,\n+\t\t\treturn do_for_each_ref_in_dir(direntry1, i1,\n \t\t\t\t\t\t      base, fn, trim, flags, cb_data);\n \t\t}\n \t\te1 = dir1->entries[i1];\n@@ -520,7 +528,7 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n \t\t\tif ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {\n \t\t\t\t/* Both are directories; descend them in parallel. */\n \t\t\t\tretval = do_for_each_ref_in_dirs(\n-\t\t\t\t\t\t&e1->u.subdir, &e2->u.subdir,\n+\t\t\t\t\t\te1, e2,\n \t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t\t\t\ti1++;\n \t\t\t\ti2++;\n@@ -543,7 +551,7 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,\n \t\t\t}\n \t\t\tif (entry->flag & REF_DIR) {\n \t\t\t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\t\t\t&entry->u.subdir, 0,\n+\t\t\t\t\t\tentry, 0,\n \t\t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t\t\t} else {\n \t\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, entry);\n@@ -605,7 +613,7 @@ static int is_refname_available(const char *refname, const char *oldrefname,\n \n \tassert(direntry->flag & REF_DIR);\n \n-\tif (do_for_each_ref_in_dir(&direntry->u.subdir, 0, \"\", name_conflict_fn,\n+\tif (do_for_each_ref_in_dir(direntry, 0, \"\", name_conflict_fn,\n \t\t\t\t   0, DO_FOR_EACH_INCLUDE_BROKEN,\n \t\t\t\t   &data)) {\n \t\terror(\"'%s' exists; cannot create '%s'\",\n@@ -1196,21 +1204,20 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \n \tif (extra_direntry)\n \t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\t&extra_direntry->u.subdir, 0,\n+\t\t\t\textra_direntry, 0,\n \t\t\t\tbase, fn, trim, flags, cb_data);\n \tif (!retval) {\n \t\tif (packed_direntry && loose_direntry)\n \t\t\tretval = do_for_each_ref_in_dirs(\n-\t\t\t\t\t&packed_direntry->u.subdir,\n-\t\t\t\t\t&loose_direntry->u.subdir,\n+\t\t\t\t\tpacked_direntry, loose_direntry,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t\telse if (packed_direntry)\n \t\t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\t\t&packed_direntry->u.subdir, 0,\n+\t\t\t\t\tpacked_direntry, 0,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t\telse if (loose_direntry)\n \t\t\tretval = do_for_each_ref_in_dir(\n-\t\t\t\t\t&loose_direntry->u.subdir, 0,\n+\t\t\t\t\tloose_direntry, 0,\n \t\t\t\t\tbase, fn, trim, flags, cb_data);\n \t}\n \n@@ -1668,8 +1675,7 @@ static int repack_without_ref(const char *refname)\n \t\tunable_to_lock_error(git_path(\"packed-refs\"), errno);\n \t\treturn error(\"cannot delete '%s' from packed refs\", refname);\n \t}\n-\tdo_for_each_ref_in_dir(&packed->u.subdir, 0,\n-\t\t\t       \"\", repack_without_ref_fn, 0, 0, &data);\n+\tdo_for_each_ref_in_dir(packed, 0, \"\", repack_without_ref_fn, 0, 0, &data);\n \treturn commit_lock_file(&packlock);\n }\n \n-- \n1.7.7\n"},{"id":"178458","messageId":"1319804921-17545-23-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 22/28] sort_ref_dir(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:35Z","receivedAt":"2011-10-28T12:28:35Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   16 +++++++++-------\n 1 files changed, 9 insertions(+), 7 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c3ee93d..98c8569 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -285,7 +285,7 @@ static int ref_entry_cmp(const void *a, const void *b)\n \treturn strcmp(one->name, two->name);\n }\n \n-static void sort_ref_dir(struct ref_dir *dir);\n+static void sort_ref_dir(struct ref_entry *direntry);\n \n /*\n  * Return the entry with the given refname from the ref_dir\n@@ -313,7 +313,7 @@ static struct ref_entry *search_ref_dir(struct ref_entry *direntry, const char *\n \t * references one after the other to a single subdirectory\n \t * doesn't require *any* intermediate sorting.\n \t */\n-\tsort_ref_dir(dir);\n+\tsort_ref_dir(direntry);\n \n \tlen = strlen(refname) + 1;\n \te = xmalloc(sizeof(struct ref_entry) + len);\n@@ -424,11 +424,13 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2\n  * Sort the entries in dir (if they are not already sorted).  Sort\n  * only dir itself, not its subdirectories.\n  */\n-static void sort_ref_dir(struct ref_dir *dir)\n+static void sort_ref_dir(struct ref_entry *direntry)\n {\n \tint i, j;\n \tstruct ref_entry *last = NULL;\n-\n+\tstruct ref_dir *dir;\n+\tassert(direntry->flag & REF_DIR);\n+\tdir = &direntry->u.subdir;\n \tif (dir->sorted == dir->nr)\n \t\treturn; /* This directory is already sorted and de-duped */\n \n@@ -479,7 +481,7 @@ static int do_for_each_ref_in_dir(struct ref_entry *direntry, int offset,\n \tstruct ref_dir *dir;\n \tassert(direntry->flag & REF_DIR);\n \tdir = &direntry->u.subdir;\n-\tsort_ref_dir(dir);\n+\tsort_ref_dir(direntry);\n \tfor (i = offset; i < dir->nr; i++) {\n \t\tstruct ref_entry *entry = dir->entries[i];\n \t\tint retval;\n@@ -508,8 +510,8 @@ static int do_for_each_ref_in_dirs(struct ref_entry *direntry1,\n \tassert(direntry2->flag & REF_DIR);\n \tdir1 = &direntry1->u.subdir;\n \tdir2 = &direntry2->u.subdir;\n-\tsort_ref_dir(dir1);\n-\tsort_ref_dir(dir2);\n+\tsort_ref_dir(direntry1);\n+\tsort_ref_dir(direntry2);\n \twhile (1) {\n \t\tstruct ref_entry *e1, *e2, *entry;\n \t\tint cmp;\n-- \n1.7.7\n"},{"id":"178459","messageId":"1319804921-17545-24-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 23/28] struct ref_dir: store a reference to the enclosing ref_cache","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:36Z","receivedAt":"2011-10-28T12:28:36Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nThis means that it contains enough information to serve as the sole\nargument to get_ref_dir(), which will be changed in the next commit.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   21 ++++++++++++++++-----\n 1 files changed, 16 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 98c8569..6b2b2f6 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -143,6 +143,8 @@ struct ref_value {\n \tunsigned char peeled[20];\n };\n \n+struct ref_cache;\n+\n struct ref_dir {\n \tint nr, alloc;\n \n@@ -150,6 +152,12 @@ struct ref_dir {\n \tint sorted;\n \n \tstruct ref_entry **entries;\n+\n+\t/*\n+\t * A pointer to the ref_cache that contains this ref_dir, or\n+\t * NULL if this ref_dir is used for extra_refs.\n+\t */\n+\tstruct ref_cache *ref_cache;\n };\n \n /* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */\n@@ -261,7 +269,8 @@ static void clear_ref_dir(struct ref_dir *dir)\n  * dirname is the name of the directory with a trailing slash (e.g.,\n  * \"refs/heads/\") or \"\" for the top-level directory.\n  */\n-static struct ref_entry *create_dir_entry(const char *dirname)\n+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,\n+\t\t\t\t\t  const char *dirname)\n {\n \tstruct ref_entry *direntry;\n \tif (*dirname) {\n@@ -275,6 +284,7 @@ static struct ref_entry *create_dir_entry(const char *dirname)\n \t\tdirentry->name[0] = '\\0';\n \t}\n \tdirentry->flag = REF_DIR;\n+\tdirentry->u.subdir.ref_cache = ref_cache;\n \treturn direntry;\n }\n \n@@ -354,7 +364,8 @@ static struct ref_entry *find_containing_direntry(struct ref_entry *direntry,\n \t\t\t\tdirentry = NULL;\n \t\t\t\tbreak;\n \t\t\t}\n-\t\t\tentry = create_dir_entry(refname_copy);\n+\t\t\tentry = create_dir_entry(direntry->u.subdir.ref_cache,\n+\t\t\t\t\t\t refname_copy);\n \t\t\tadd_entry(direntry, entry);\n \t\t}\n \t\tslash[1] = tmp;\n@@ -771,7 +782,7 @@ static void read_packed_refs(FILE *f, struct ref_entry *direntry)\n void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n {\n \tif (!extra_refs)\n-\t\textra_refs = create_dir_entry(\"\");\n+\t\textra_refs = create_dir_entry(NULL, \"\");\n \tadd_ref(extra_refs, create_ref_entry(refname, sha1, flag));\n }\n \n@@ -789,7 +800,7 @@ static struct ref_entry *get_packed_refs(struct ref_cache *refs)\n \t\tconst char *packed_refs_file;\n \t\tFILE *f;\n \n-\t\trefs->packed = create_dir_entry(\"\");\n+\t\trefs->packed = create_dir_entry(refs, \"\");\n \t\tif (*refs->name)\n \t\t\tpacked_refs_file = git_path_submodule(refs->name, \"packed-refs\");\n \t\telse\n@@ -881,7 +892,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n static struct ref_entry *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->loose) {\n-\t\trefs->loose = create_dir_entry(\"\");\n+\t\trefs->loose = create_dir_entry(refs, \"\");\n \t\tget_ref_dir(refs, \"refs/\");\n \t}\n \treturn refs->loose;\n-- \n1.7.7\n"},{"id":"178445","messageId":"1319804921-17545-25-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 24/28] read_loose_refs(): take a (ref_entry *) as argument","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:37Z","receivedAt":"2011-10-28T12:28:37Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nRename get_ref_dir() to read_loose_refs(), and change its signature.\nThis is another step towards reading loose references one directory\nat a time.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   21 +++++++++++----------\n 1 files changed, 11 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6b2b2f6..f3910de 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -815,21 +815,20 @@ static struct ref_entry *get_packed_refs(struct ref_cache *refs)\n }\n \n /*\n- * dirname must match the name associated with dir; in particular, it\n- * must end with '/'.\n+ * Fill direntry with loose references read from the filesystem.\n  */\n-static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n+static void read_loose_refs(struct ref_entry *direntry)\n {\n \tDIR *d;\n \tchar *path;\n+\tchar *dirname = direntry->name;\n \tint dirnamelen = strlen(dirname);\n \tint pathlen;\n-\tstruct ref_entry *direntry;\n-\n-\tassert(dirnamelen && dirname[dirnamelen - 1] == '/');\n-\n-\tdirentry = find_containing_direntry(refs->loose, dirname, 1);\n+\tstruct ref_cache *refs;\n \n+\tassert(direntry->flag & REF_DIR);\n+\tassert(dirnamelen && direntry->name[dirnamelen - 1] == '/');\n+\trefs = direntry->u.subdir.ref_cache;\n \tif (*refs->name)\n \t\tpath = git_path_submodule(refs->name, \"%s\", dirname);\n \telse\n@@ -867,7 +866,9 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \t\t\tif (S_ISDIR(st.st_mode)) {\n \t\t\t\trefname[dirnamelen + namelen] = '/';\n \t\t\t\trefname[dirnamelen + namelen + 1] = '\\0';\n-\t\t\t\tget_ref_dir(refs, refname);\n+\t\t\t\tread_loose_refs(find_containing_direntry(\n+\t\t\t\t\t\t\t\trefs->loose,\n+\t\t\t\t\t\t\t\trefname, 1));\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (*refs->name) {\n@@ -893,7 +894,7 @@ static struct ref_entry *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->loose) {\n \t\trefs->loose = create_dir_entry(refs, \"\");\n-\t\tget_ref_dir(refs, \"refs/\");\n+\t\tread_loose_refs(find_containing_direntry(refs->loose, \"refs/\", 1));\n \t}\n \treturn refs->loose;\n }\n-- \n1.7.7\n"},{"id":"178443","messageId":"1319804921-17545-26-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 25/28] refs: read loose references lazily","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:38Z","receivedAt":"2011-10-28T12:28:38Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nInstead of reading the whole directory of loose references the first\ntime any are needed, only read them on demand, one directory at a\ntime.\n\nUse a new ref_entry flag value REF_DIR_INCOMPLETE to indicate that the\nentry represents a REF_DIR that hasn't been read yet.  Whenever any\nentries from such a directory are needed, read all of the loose\nreferences from that directory.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |  112 ++++++++++++++++++++++++++++++++++++++++++++++++++--------------\n 1 files changed, 88 insertions(+), 24 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f3910de..88ef9dd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -138,6 +138,12 @@ int check_refname_format(const char *refname, int flags)\n \n struct ref_entry;\n \n+/*\n+ * Information used (along with the information in ref_entry) to\n+ * describe a single cached reference.  This data structure only\n+ * occurs embedded in a union in struct ref_entry, and only when\n+ * (ref_entry->flag & REF_DIR) is zero.\n+ */\n struct ref_value {\n \tunsigned char sha1[20];\n \tunsigned char peeled[20];\n@@ -145,6 +151,34 @@ struct ref_value {\n \n struct ref_cache;\n \n+/*\n+ * Information used (along with the information in ref_entry) to\n+ * describe a level in the hierarchy of references.  This data\n+ * structure only occurs embedded in a union in struct ref_entry, and\n+ * only when (ref_entry.flag & REF_DIR) is nonzero.  In that case,\n+ * (ref_entry.flag & REF_DIR) can take the following values:\n+ *\n+ *     REF_DIR_COMPLETE -- a directory of loose or packed references,\n+ *         already read.\n+ *\n+ *     REF_DIR_INCOMPLETE -- a directory of loose references that\n+ *         hasn't been read yet (nor has any of its subdirectories).\n+ *\n+ * Entries within a directory are stored within a growable array of\n+ * pointers to ref_entries (entries, nr, alloc).  Entries 0 <= i <\n+ * sorted are sorted by their component name in strcmp() order and the\n+ * remaining entries are unsorted.\n+ *\n+ * Loose references are read lazily, one directory at a time.  When a\n+ * directory of loose references is read, then all of the references\n+ * in that directory are stored, and REF_DIR_INCOMPLETE stubs are\n+ * created for any subdirectories, but the subdirectories themselves\n+ * are not read.  The reading is triggered either by search_ref_dir()\n+ * (called when single references are added or interrogated), by\n+ * sort_ref_dir(), or by iteration over a subdirectory of references\n+ * using one of the for_each_ref*() functions (which calls\n+ * sort_ref_dir() for each subdirectory).\n+ */\n struct ref_dir {\n \tint nr, alloc;\n \n@@ -162,19 +196,33 @@ struct ref_dir {\n \n /* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */\n #define REF_KNOWS_PEELED 0x08\n-#define REF_DIR 0x10\n+\n+/* If any of these bits are set, the entry represents a directory: */\n+#define REF_DIR 0x30\n+\n+/* A directory that has already been fully read. */\n+#define REF_DIR_COMPLETE 0x10\n+\n+/* A directory of loose references that has not yet been fully read. */\n+#define REF_DIR_INCOMPLETE 0x20\n \n /*\n  * A ref_entry represents either a reference or a \"subdirectory\" of\n- * references.  Each directory in the reference namespace is\n- * represented by a ref_entry with (flags & REF_DIR) set and\n- * containing a subdir member that holds the entries in that\n- * directory.  References are represented by a ref_entry with (flags &\n- * REF_DIR) unset and a value member that describes the reference's\n- * value.  The flag member is at the ref_entry level, but it is also\n- * needed to interpret the contents of the value field (in other\n- * words, a ref_value object is not very much use without the\n- * enclosing ref_entry).\n+ * references.\n+ *\n+ * Each directory in the reference namespace is represented by a\n+ * ref_entry with (flags & REF_DIR) set and containing a subdir member\n+ * that holds the entries in that directory that have been read so\n+ * far.  If (flags & REF_DIR) == REF_DIR_INCOMPLETE, then the\n+ * directory and its subdirectories haven't been read yet.\n+ * REF_DIR_INCOMPLETE is only used for loose references.\n+ *\n+ * References are represented by a ref_entry with (flags & REF_DIR) ==\n+ * 0 and a value member that describes the reference's value.  The\n+ * flag member is at the ref_entry level, but it is also needed to\n+ * interpret the contents of the value field (in other words, a\n+ * ref_value object is not very much use without the enclosing\n+ * ref_entry).\n  *\n  * Reference names cannot end with slash and directories' names are\n  * always stored with a trailing slash (except for the top-level\n@@ -264,13 +312,15 @@ static void clear_ref_dir(struct ref_dir *dir)\n \tdir->entries = NULL;\n }\n \n+static void read_loose_refs(struct ref_entry *direntry);\n+\n /*\n  * Create a struct ref_entry object for the specified dirname.\n  * dirname is the name of the directory with a trailing slash (e.g.,\n  * \"refs/heads/\") or \"\" for the top-level directory.\n  */\n static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,\n-\t\t\t\t\t  const char *dirname)\n+\t\t\t\t\t  const char *dirname, int flag)\n {\n \tstruct ref_entry *direntry;\n \tif (*dirname) {\n@@ -283,7 +333,7 @@ static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,\n \t\tdirentry = xcalloc(1, sizeof(struct ref_entry) + 1);\n \t\tdirentry->name[0] = '\\0';\n \t}\n-\tdirentry->flag = REF_DIR;\n+\tdirentry->flag = flag;\n \tdirentry->u.subdir.ref_cache = ref_cache;\n \treturn direntry;\n }\n@@ -308,6 +358,7 @@ static struct ref_entry *search_ref_dir(struct ref_entry *direntry, const char *\n \tstruct ref_dir *dir;\n \n \tassert(direntry->flag & REF_DIR);\n+\tread_loose_refs(direntry);\n \tdir = &direntry->u.subdir;\n \tif (refname == NULL || !dir->nr)\n \t\treturn NULL;\n@@ -364,8 +415,14 @@ static struct ref_entry *find_containing_direntry(struct ref_entry *direntry,\n \t\t\t\tdirentry = NULL;\n \t\t\t\tbreak;\n \t\t\t}\n+\t\t\t/*\n+\t\t\t * If search_ref_dir() above didn't make the\n+\t\t\t * entry spring into existence, then this must\n+\t\t\t * not be an unread loose reference tree, so\n+\t\t\t * the correct flag is REF_DIR_COMPLETE.\n+\t\t\t */\n \t\t\tentry = create_dir_entry(direntry->u.subdir.ref_cache,\n-\t\t\t\t\t\t refname_copy);\n+\t\t\t\t\t\t refname_copy, REF_DIR_COMPLETE);\n \t\t\tadd_entry(direntry, entry);\n \t\t}\n \t\tslash[1] = tmp;\n@@ -441,6 +498,7 @@ static void sort_ref_dir(struct ref_entry *direntry)\n \tstruct ref_entry *last = NULL;\n \tstruct ref_dir *dir;\n \tassert(direntry->flag & REF_DIR);\n+\tread_loose_refs(direntry);\n \tdir = &direntry->u.subdir;\n \tif (dir->sorted == dir->nr)\n \t\treturn; /* This directory is already sorted and de-duped */\n@@ -491,8 +549,8 @@ static int do_for_each_ref_in_dir(struct ref_entry *direntry, int offset,\n \tint i;\n \tstruct ref_dir *dir;\n \tassert(direntry->flag & REF_DIR);\n-\tdir = &direntry->u.subdir;\n \tsort_ref_dir(direntry);\n+\tdir = &direntry->u.subdir;\n \tfor (i = offset; i < dir->nr; i++) {\n \t\tstruct ref_entry *entry = dir->entries[i];\n \t\tint retval;\n@@ -519,10 +577,10 @@ static int do_for_each_ref_in_dirs(struct ref_entry *direntry1,\n \n \tassert(direntry1->flag & REF_DIR);\n \tassert(direntry2->flag & REF_DIR);\n-\tdir1 = &direntry1->u.subdir;\n-\tdir2 = &direntry2->u.subdir;\n \tsort_ref_dir(direntry1);\n \tsort_ref_dir(direntry2);\n+\tdir1 = &direntry1->u.subdir;\n+\tdir2 = &direntry2->u.subdir;\n \twhile (1) {\n \t\tstruct ref_entry *e1, *e2, *entry;\n \t\tint cmp;\n@@ -782,7 +840,7 @@ static void read_packed_refs(FILE *f, struct ref_entry *direntry)\n void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n {\n \tif (!extra_refs)\n-\t\textra_refs = create_dir_entry(NULL, \"\");\n+\t\textra_refs = create_dir_entry(NULL, \"\", REF_DIR_COMPLETE);\n \tadd_ref(extra_refs, create_ref_entry(refname, sha1, flag));\n }\n \n@@ -800,7 +858,7 @@ static struct ref_entry *get_packed_refs(struct ref_cache *refs)\n \t\tconst char *packed_refs_file;\n \t\tFILE *f;\n \n-\t\trefs->packed = create_dir_entry(refs, \"\");\n+\t\trefs->packed = create_dir_entry(refs, \"\", REF_DIR_COMPLETE);\n \t\tif (*refs->name)\n \t\t\tpacked_refs_file = git_path_submodule(refs->name, \"packed-refs\");\n \t\telse\n@@ -822,11 +880,14 @@ static void read_loose_refs(struct ref_entry *direntry)\n \tDIR *d;\n \tchar *path;\n \tchar *dirname = direntry->name;\n-\tint dirnamelen = strlen(dirname);\n+\tint dirnamelen;\n \tint pathlen;\n \tstruct ref_cache *refs;\n \n \tassert(direntry->flag & REF_DIR);\n+\tif ((direntry->flag & REF_DIR) != REF_DIR_INCOMPLETE)\n+\t\treturn;\n+\tdirnamelen = strlen(dirname);\n \tassert(dirnamelen && direntry->name[dirnamelen - 1] == '/');\n \trefs = direntry->u.subdir.ref_cache;\n \tif (*refs->name)\n@@ -864,11 +925,12 @@ static void read_loose_refs(struct ref_entry *direntry)\n \t\t\tif (stat(refdir, &st) < 0)\n \t\t\t\tcontinue;\n \t\t\tif (S_ISDIR(st.st_mode)) {\n+\t\t\t\tstruct ref_entry *subdirentry;\n \t\t\t\trefname[dirnamelen + namelen] = '/';\n \t\t\t\trefname[dirnamelen + namelen + 1] = '\\0';\n-\t\t\t\tread_loose_refs(find_containing_direntry(\n-\t\t\t\t\t\t\t\trefs->loose,\n-\t\t\t\t\t\t\t\trefname, 1));\n+\t\t\t\tsubdirentry = create_dir_entry(direntry->u.subdir.ref_cache,\n+\t\t\t\t\t\t\t       refname, REF_DIR_INCOMPLETE);\n+\t\t\t\tadd_entry(direntry, subdirentry);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (*refs->name) {\n@@ -888,13 +950,15 @@ static void read_loose_refs(struct ref_entry *direntry)\n \t\tfree(refname);\n \t\tclosedir(d);\n \t}\n+\tdirentry->flag = REF_DIR_COMPLETE;\n }\n \n static struct ref_entry *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->loose) {\n-\t\trefs->loose = create_dir_entry(refs, \"\");\n-\t\tread_loose_refs(find_containing_direntry(refs->loose, \"refs/\", 1));\n+\t\trefs->loose = create_dir_entry(refs, \"\", REF_DIR_COMPLETE);\n+\t\tadd_entry(refs->loose,\n+\t\t\t  create_dir_entry(refs, \"refs/\", REF_DIR_INCOMPLETE));\n \t}\n \treturn refs->loose;\n }\n-- \n1.7.7\n"},{"id":"178460","messageId":"1319804921-17545-27-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 26/28] is_refname_available(): query only possibly-conflicting references","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:39Z","receivedAt":"2011-10-28T12:28:39Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nInstead of iterating through all of the references, inquire more\npointedly about the references that could conflict with the new name.\nThis requires checking for a few individual references, plus iterating\nthrough a small subtree of the rest of the references (and usually the\nsubtree iteration ends without having to recurse).  A big benefit is\nthat populating the whole loose reference cache (which can be very\nexpensive) can usually be avoided.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   40 +++++++++++++++++++++++++++++++++-------\n 1 files changed, 33 insertions(+), 7 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 88ef9dd..6a7f9c3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -658,7 +658,7 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh\n \t\t\t    int flags, void *cb_data)\n {\n \tstruct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;\n-\tif (data->oldrefname && !strcmp(data->oldrefname, existingrefname))\n+\tif (!strcmp(data->oldrefname, existingrefname))\n \t\treturn 0;\n \tif (names_conflict(data->refname, existingrefname)) {\n \t\tdata->conflicting_refname = existingrefname;\n@@ -669,22 +669,48 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh\n \n /*\n  * Return true iff a reference named refname could be created without\n- * conflicting with the name of an existing reference.  If oldrefname\n- * is non-NULL, ignore potential conflicts with oldrefname (e.g.,\n- * because oldrefname is scheduled for deletion in the same\n+ * conflicting with the name of an existing reference in direntry.  If\n+ * oldrefname is non-NULL, ignore potential conflicts with oldrefname\n+ * (e.g., because oldrefname is scheduled for deletion in the same\n  * operation).\n  */\n static int is_refname_available(const char *refname, const char *oldrefname,\n \t\t\t\tstruct ref_entry *direntry)\n {\n+\tint prefixlen = strlen(refname);\n+\tchar *prefix;\n+\tchar *slash;\n \tstruct name_conflict_cb data;\n+\n+\tassert(direntry->flag & REF_DIR);\n+\n+\tif (!oldrefname)\n+\t\toldrefname = \"\"; /* invalid; cannot match any existing refname */\n+\n+\t/* Check whether a prefix of refname is an existing reference: */\n+\tprefix = xmalloc(prefixlen + 2);\n+\tmemcpy(prefix, refname, prefixlen + 1);\n+\tfor (slash = strchr(prefix, '/'); slash; slash = strchr(slash + 1, '/')) {\n+\t\t*slash = '\\0';\n+\t\tif (strcmp(oldrefname, prefix)) {\n+\t\t\tstruct ref_entry *entry = find_ref(direntry, prefix);\n+\t\t\tif (entry) {\n+\t\t\t\terror(\"'%s' exists; cannot create '%s'\", prefix, refname);\n+\t\t\t\tfree(prefix);\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n+\t\t*slash = '/';\n+\t}\n+\n+\t/* Check whether refname is a proper prefix of an existing reference: */\n+\tprefix[prefixlen++] = '/';\n+\tprefix[prefixlen] = '\\0';\n \tdata.refname = refname;\n \tdata.oldrefname = oldrefname;\n \tdata.conflicting_refname = NULL;\n \n-\tassert(direntry->flag & REF_DIR);\n-\n-\tif (do_for_each_ref_in_dir(direntry, 0, \"\", name_conflict_fn,\n+\tif (do_for_each_ref_in_dir(direntry, 0, prefix, name_conflict_fn,\n \t\t\t\t   0, DO_FOR_EACH_INCLUDE_BROKEN,\n \t\t\t\t   &data)) {\n \t\terror(\"'%s' exists; cannot create '%s'\",\n-- \n1.7.7\n"},{"id":"178448","messageId":"1319804921-17545-28-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 27/28] read_packed_refs(): keep track of the directory being worked in","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:40Z","receivedAt":"2011-10-28T12:28:40Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nPacked references are stored in $GIT_DIR/packed-refs sorted, so\nadjacent ones are pretty likely to be in the same directory.  So while\nreading them, keep track of the last directory used, and reuse it if\npossible to avoid searching the reference namespace from the root each\ntime.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   16 +++++++++++++++-\n 1 files changed, 15 insertions(+), 1 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6a7f9c3..6a19d19 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -831,6 +831,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)\n static void read_packed_refs(FILE *f, struct ref_entry *direntry)\n {\n \tstruct ref_entry *last = NULL;\n+\tstruct ref_entry *current_direntry = NULL;\n \tchar refline[PATH_MAX];\n \tint flag = REF_ISPACKED;\n \n@@ -850,8 +851,21 @@ static void read_packed_refs(FILE *f, struct ref_entry *direntry)\n \n \t\trefname = parse_ref_line(refline, sha1);\n \t\tif (refname) {\n+\t\t\tif (current_direntry) {\n+\t\t\t\tchar *slash = strrchr(refname, '/');\n+\t\t\t\tif (!slash\n+\t\t\t\t    || strncmp(current_direntry->name, refname,\n+\t\t\t\t\t       slash - refname + 1)\n+\t\t\t\t    || current_direntry->name[slash - refname + 1] != '\\0')\n+\t\t\t\t\t/* The new refname does not go in current_direntry */\n+\t\t\t\t\tcurrent_direntry = NULL;\n+\t\t\t}\n+\t\t\tif (!current_direntry)\n+\t\t\t\tcurrent_direntry = find_containing_direntry(\n+\t\t\t\t\t\tdirentry, refname, 1);\n+\n \t\t\tlast = create_ref_entry(refname, sha1, flag);\n-\t\t\tadd_ref(direntry, last);\n+\t\t\tadd_entry(current_direntry, last);\n \t\t\tcontinue;\n \t\t}\n \t\tif (last &&\n-- \n1.7.7\n"},{"id":"178442","messageId":"1319804921-17545-29-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH 28/28] repack_without_ref(): call clear_packed_ref_cache()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T12:28:41Z","receivedAt":"2011-10-28T12:28:41Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nCall clear_packed_ref_cache() from repack_without_ref().  This is a\nmore logical place to call it than from delete_ref().  Also,\nrepack_without_ref() is smart enough to know that it doesn't have to\ninvalidate the cache if the reference was not found among the packed\nrefs.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    7 +++++--\n 1 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6a19d19..bbf9e3e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1784,7 +1784,9 @@ static struct lock_file packlock;\n static int repack_without_ref(const char *refname)\n {\n \tstruct repack_without_ref_sb data;\n-\tstruct ref_entry *packed = get_packed_refs(get_ref_cache(NULL));\n+\tstruct ref_cache *refs = get_ref_cache(NULL);\n+\tstruct ref_entry *packed = get_packed_refs(refs);\n+\n \tif (find_ref(packed, refname) == NULL)\n \t\treturn 0;\n \tdata.refname = refname;\n@@ -1794,6 +1796,7 @@ static int repack_without_ref(const char *refname)\n \t\treturn error(\"cannot delete '%s' from packed refs\", refname);\n \t}\n \tdo_for_each_ref_in_dir(packed, 0, \"\", repack_without_ref_fn, 0, 0, &data);\n+\tclear_packed_ref_cache(refs);\n \treturn commit_lock_file(&packlock);\n }\n \n@@ -1822,6 +1825,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n \n \t\tif (!(delopt & REF_NODEREF))\n \t\t\tlock->lk->filename[i] = '.';\n+\t\tclear_loose_ref_cache(get_ref_cache(NULL));\n \t}\n \t/* removing the loose one could have resurrected an earlier\n \t * packed one.  Also, if it was not loose we need to repack\n@@ -1830,7 +1834,6 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n \tret |= repack_without_ref(refname);\n \n \tunlink_or_warn(git_path(\"logs/%s\", lock->ref_name));\n-\tinvalidate_ref_cache(NULL);\n \tunlock_ref(lock);\n \treturn ret;\n }\n-- \n1.7.7\n"},{"id":"178463","messageId":"CALkWK0=Hsq_yg1Vyr-3_jf9n=WcB_XNYRQa0SEhSWD5VxKBXQg@mail.gmail.com","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH 00/28] Store references hierarchically in cache","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2011-10-28T13:07:13Z","receivedAt":"2011-10-28T13:07:13Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Michael,\n\nMichael Haggerty writes:\n> Therefore, this patch series changes the data structure used to store\n> the reference cache from a single array of pointers-to-struct into a\n> tree-like structure in which each subdirectory of the reference\n> namespace is stored as an array of pointers-to-entry and entries can\n> be either references or subdirectories containing more references.\n\nVery nice! I like the idea. Can't wait to start reading the series.\n\n>  * refs/replace is almost *always* needed even though it often\n>    doesn't even exist.  Thus the presence of many loose references\n>    slows down *many* git commands for no reason whatsoever.\n\nWas this one of your primary inspirations for writing this series?\n\n>  * When a new reference is created, is_refname_available() is called\n>    to see whether there is another another reference whose name\n>    conflicts with the new one.  Currently this loads and iterates\n>    through *all* references.  But there are only a few refnames that\n>    can possibly conflict; for example, given the refname\n>    \"refs/heads/foo/bar\", the only possible conflicts are with\n>    \"refs/heads/foo\" and \"refs/heads/foo/bar/*\".  Therefore it is\n>    silly to load and iterate through the whole refname hierarchy.\n\nHm, the original design does sound quite sub-optimal.  I suppose it\nwas written when Git didn't have so many refs in so many\nsubdirectories.\n\n>  * \"git for-each-ref\" is capable of searching a subtree of the\n>    references.  But currently this causes all references to be\n>    loaded.\n\nAh.  I was using git for-each-ref to write a filter-branch like thing\nearlier, and I was wondering why it was so slow.\n\n> * the time to create a new branch goes from 180 ms to less than 10 ms\n>  (my test resolution only includes two decimal places) and the time\n>  to checkout a new branch does the same.\n\nI'm interested in seeing how the callgraph changed.  Assuming you used\nValgrind to profile it, could you publish the outputs?\n\n> * the time for a \"git filter-branch\" of all commits (which used to\n>  scale like N^2) goes from 4 hours to 13 minutes.  (Since\n>  filter-branch necessarily *creates* lots of loose references, the\n>  savings are also there if the references are originally packed.)\n\nThis is seriously awesome.\n\n> The efficiency gains are such that some operations are now faster with\n> loose references than with packed references; however, some operations\n> with packed references slow down a bit.\n\nCuriously, why do operations with packed references slow down?  (I'll\nprobably find out in a few minutes after reading the series, but I'm\nasking anyway because it it's very non-obvious to me now)\n\n> These changes do not increase the amount of space per reference needed\n> for the reference cache, but they do add one similarly-sized entry for\n> each subdirectory (for each of loose and packed).  I don't think that\n> the space increase should be significant in any reasonable situation.\n>\n> After these changes, there is a benefit to sharding the reference\n> namespace, especially for loose references.\n\nHm, I wonder what this means for Git hosting services.\n\n> Patches 11-24 change most of the internal functions to work with\n> \"struct ref_entry *\" (namely the kind of ref_entry that holds a\n> directory of references) instead of \"struct ref_dir *\".  The reason\n> for this change it to allow these functions access to the \"flag\" and\n> \"name\" fields that are stored in ref_entry and thereby avoid having to\n> store redundant information in \"struct ref_dir\" (which would increase\n> the size of *every* ref_entry because of its presence in the union).\n\nHm, I was wondering why the series was looking so intimidating.  Is it\nnot possible to squash all (or atleast some) of these together?\n\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n\nNit: Can't you configure your email client to put this in the \"From: \"\nheader of your emails?\n\nThanks for the interesting read.\n\n-- Ram\n"},{"id":"178485","messageId":"4EAAF841.3090900@alum.mit.edu","threadId":"28785","inReplyTo":"CALkWK0=Hsq_yg1Vyr-3_jf9n=WcB_XNYRQa0SEhSWD5VxKBXQg@mail.gmail.com","subject":"Re: [PATCH 00/28] Store references hierarchically in cache","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-10-28T18:45:21Z","receivedAt":"2011-10-28T18:45:21Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 10/28/2011 03:07 PM, Ramkumar Ramachandra wrote:\n> Michael Haggerty writes:\n>> Therefore, this patch series changes the data structure used to store\n>> the reference cache from a single array of pointers-to-struct into a\n>> tree-like structure in which each subdirectory of the reference\n>> namespace is stored as an array of pointers-to-entry and entries can\n>> be either references or subdirectories containing more references.\n> \n> Very nice! I like the idea. Can't wait to start reading the series.\n> \n>>  * refs/replace is almost *always* needed even though it often\n>>    doesn't even exist.  Thus the presence of many loose references\n>>    slows down *many* git commands for no reason whatsoever.\n> \n> Was this one of your primary inspirations for writing this series?\n\nMy primary inspiration was that \"git filter-branch\" was so slow, which\nis partly because of the refs/replace thing and partly just the built-in\ninefficiency of the old reference cache.\n\n>> * the time to create a new branch goes from 180 ms to less than 10 ms\n>>  (my test resolution only includes two decimal places) and the time\n>>  to checkout a new branch does the same.\n> \n> I'm interested in seeing how the callgraph changed.  Assuming you used\n> Valgrind to profile it, could you publish the outputs?\n\nI didn't use valgrind; I just timed commands using time(1).\n\n>> The efficiency gains are such that some operations are now faster with\n>> loose references than with packed references; however, some operations\n>> with packed references slow down a bit.\n> \n> Curiously, why do operations with packed references slow down?  (I'll\n> probably find out in a few minutes after reading the series, but I'm\n> asking anyway because it it's very non-obvious to me now)\n\nI think it's just because the new data structure is slightly slower than\nthe old one for the task of appending thousands of refs without doing\nany searching or sorting.  Since packed refs are read all-or-nothing\n(even after my changes), there is no way to read the packed refs only\nfor the directory that you are interested in.\n\n>> Patches 11-24 change most of the internal functions to work with\n>> \"struct ref_entry *\" (namely the kind of ref_entry that holds a\n>> directory of references) instead of \"struct ref_dir *\".  The reason\n>> for this change it to allow these functions access to the \"flag\" and\n>> \"name\" fields that are stored in ref_entry and thereby avoid having to\n>> store redundant information in \"struct ref_dir\" (which would increase\n>> the size of *every* ref_entry because of its presence in the union).\n> \n> Hm, I was wondering why the series was looking so intimidating.  Is it\n> not possible to squash all (or atleast some) of these together?\n\nWhy would I possibly want to squash them together?  Each one is\nself-contained and logically separate from the rest.  After each commit\nthe code compiles and works.  Squashing them together wouldn't increase\nthe cognitive burden of reading them; on the contrary, it would make it\nharder to read them because several logically-separate changes would be\nconfounded into a single patch.  Most of these patches have only a few\nnontrivial lines which you can read at a glance.  And if the series ever\nhas to be bisected, the error can be narrowed down to a very small diff.\n\n>> From: Michael Haggerty <mhagger@alum.mit.edu>\n> \n> Nit: Can't you configure your email client to put this in the \"From: \"\n> header of your emails?\n\nI'm not sure where those extra lines come from.  My email client is\ngit-send-email.  I just checked, and they have appeared in patch series\nsent through two completely different SMTP servers, so it seems unlikely\nthat the SMTP server is guilty.  I'll see if I can figure it out.\n\n> Thanks for the interesting read.\n\nThanks for reading :-)\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"179466","messageId":"1321336525-19374-1-git-send-email-mhagger@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-27-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH] Fix \"is_refname_available(): query only possibly-conflicting references\"","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-11-15T05:55:25Z","receivedAt":"2011-11-15T05:55:25Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"From: Michael Haggerty <mhagger@alum.mit.edu>\n\nThe above-named commit didn't do all that its commit message claimed.\nThe problem is that do_for_each_ref_in_dir() doesn't avoid iterating\nthrough reference subtrees outside of \"prefix\"; it only skips passing\nthose references to the callback function.  So the function\nunnecessarily caused all loose references to be loaded rather than\njust those in the required subtree.\n\nSo instead, explicitly select the possibly-conflicting subtree and\npass it to do_for_each_ref_in_dir().\n\nAlso, simplify name_conflict_fn().  Since it will only be called for\npossibly-conflicting references, there is necessarily a conflict if it\nis called for *any* reference besides \"oldrefname\".\n\nRemove function names_conflict(), which is now unused.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n\nThis patch can be squashed on top of \"is_refname_available(): query\nonly possibly-conflicting references\", or applied at the end of\nmh/ref-api-take-2; it does not conflict with the two commits later in\nthe series.\n\n refs.c |   40 +++++++++++++++++-----------------------\n 1 files changed, 17 insertions(+), 23 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6a7f9c3..b185c32 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -633,23 +633,7 @@ static int do_for_each_ref_in_dirs(struct ref_entry *direntry1,\n \t}\n }\n \n-/*\n- * Return true iff refname1 and refname2 conflict with each other.\n- * Two reference names conflict if one of them exactly matches the\n- * leading components of the other; e.g., \"foo/bar\" conflicts with\n- * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n- * \"foo/barbados\".\n- */\n-static int names_conflict(const char *refname1, const char *refname2)\n-{\n-\tfor (; *refname1 && *refname1 == *refname2; refname1++, refname2++)\n-\t\t;\n-\treturn (*refname1 == '\\0' && *refname2 == '/')\n-\t\t|| (*refname1 == '/' && *refname2 == '\\0');\n-}\n-\n struct name_conflict_cb {\n-\tconst char *refname;\n \tconst char *oldrefname;\n \tconst char *conflicting_refname;\n };\n@@ -660,11 +644,13 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh\n \tstruct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;\n \tif (!strcmp(data->oldrefname, existingrefname))\n \t\treturn 0;\n-\tif (names_conflict(data->refname, existingrefname)) {\n-\t\tdata->conflicting_refname = existingrefname;\n-\t\treturn 1;\n-\t}\n-\treturn 0;\n+\n+\t/*\n+\t * Since we are only iterating over the subtree that has the\n+\t * new refname as prefix, *any* reference found is a conflict.\n+\t */\n+\tdata->conflicting_refname = existingrefname;\n+\treturn 1;\n }\n \n /*\n@@ -673,6 +659,11 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh\n  * oldrefname is non-NULL, ignore potential conflicts with oldrefname\n  * (e.g., because oldrefname is scheduled for deletion in the same\n  * operation).\n+ *\n+ * Two reference names conflict if one of them exactly matches the\n+ * leading components of the other; e.g., \"foo/bar\" conflicts with\n+ * both \"foo\" and with \"foo/bar/baz\" but not with \"foo/bar\" or\n+ * \"foo/barbados\".\n  */\n static int is_refname_available(const char *refname, const char *oldrefname,\n \t\t\t\tstruct ref_entry *direntry)\n@@ -706,11 +697,14 @@ static int is_refname_available(const char *refname, const char *oldrefname,\n \t/* Check whether refname is a proper prefix of an existing reference: */\n \tprefix[prefixlen++] = '/';\n \tprefix[prefixlen] = '\\0';\n-\tdata.refname = refname;\n \tdata.oldrefname = oldrefname;\n \tdata.conflicting_refname = NULL;\n \n-\tif (do_for_each_ref_in_dir(direntry, 0, prefix, name_conflict_fn,\n+\tdirentry = find_containing_direntry(direntry, prefix, 0);\n+\tif (!direntry)\n+\t\treturn 1;\n+\n+\tif (do_for_each_ref_in_dir(direntry, 0, \"\", name_conflict_fn,\n \t\t\t\t   0, DO_FOR_EACH_INCLUDE_BROKEN,\n \t\t\t\t   &data)) {\n \t\terror(\"'%s' exists; cannot create '%s'\",\n-- \n1.7.7.2\n"},{"id":"179481","messageId":"7vty65x2zl.fsf@alter.siamese.dyndns.org","threadId":"28785","inReplyTo":"1321336525-19374-1-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH] Fix \"is_refname_available(): query only possibly-conflicting references\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-11-15T07:24:14Z","receivedAt":"2011-11-15T07:24:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"mhagger@alum.mit.edu writes:\n\n> This patch can be squashed on top of \"is_refname_available(): query\n> only possibly-conflicting references\", or applied at the end of\n> mh/ref-api-take-2; it does not conflict with the two commits later in\n> the series.\n\nThanks. At the microscopic level (i.e. in the context of the said series),\nthe patch makes sense to me.\n\nHowever, I'd rather see us spend effort to make absolutely sure that other\ntopics already in next that touch the related codepaths (I think you have\ntwo such series yourself and I suspect there are other minor fixes that\nmay textually conflict) are in good shape and have them graduate early\nafter 1.7.8 ships, before queuing a re-roll of the ref-api series, which\nis rather extensive.\n"},{"id":"179508","messageId":"4EC29120.2020400@alum.mit.edu","threadId":"28785","inReplyTo":"7vty65x2zl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Fix \"is_refname_available(): query only possibly-conflicting references\"","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-11-15T16:19:44Z","receivedAt":"2011-11-15T16:19:44Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 11/15/2011 08:24 AM, Junio C Hamano wrote:\n> However, I'd rather see us spend effort to make absolutely sure that other\n> topics already in next that touch the related codepaths (I think you have\n> two such series yourself and I suspect there are other minor fixes that\n> may textually conflict) are in good shape and have them graduate early\n> after 1.7.8 ships, before queuing a re-roll of the ref-api series, which\n> is rather extensive.\n\nIf you have a preference for which patch series you would like to\nintegrate in which order (and especially if you think that there are\ngaps that need to be filled), please let me know.  It would be a lot\nless work to put them in the right order from the start rather than\ntrying to keep them all synchronized with master and continually reroll\nthem based on what you have merged so far.\n\nAlso, I am working under the assumption that the patch series that are\nalready in \"next\" should be left alone; if you have doubts about any of\nthose patch series (i.e., are thinking of ejecting them from next during\nthe post-release chaos), please let me know what needs changing.\n\nI'm still getting the hang of this workflow, so suggestions are welcome.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"179526","messageId":"7vd3cturae.fsf@alter.siamese.dyndns.org","threadId":"28785","inReplyTo":"4EC29120.2020400@alum.mit.edu","subject":"Re: [PATCH] Fix \"is_refname_available(): query only possibly-conflicting references\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-11-15T19:19:53Z","receivedAt":"2011-11-15T19:19:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> If you have a preference for which patch series you would like to\n> integrate in which order (and especially if you think that there are\n> gaps that need to be filled), please let me know.  It would be a lot\n> less work to put them in the right order from the start rather than\n> trying to keep them all synchronized with master and continually reroll\n> them based on what you have merged so far.\n\nI've re-read mh/ref-api-[23] a few times myself during this feature-freeze\nperiod and found that the checks they enforce seemed to be sensible for\nnewly created refs. But I do not know if there are widespread mispractices\nof using \"wrong\" refnames, created by either older versions of Git and\ncommon third-party ones, that may make existing repository unusable\nwithout first correcting them, and more importantly, I do not know if the\nupdated code is lenient enough to give users necessary escape hatches to\ncorrect the existing problems. As we recently found out on the 'master'\nfront for your earlier topic, an updated check that is more strict and\nsaner than the older one is not necessarily an improvement, if it causes\npain to existing users to adjust to the new world order.\n\nThe output frm \"git log --oneline --first-parent master..pu\" should give\nrough idea of what I have in mind. Obvious and nonintrusive clean-ups come\nearly, then features that are shown to be needed in the field and are with\nuser-facing design that are perfected come next to give them longer time\nto fix potential issues in implemementation, followed by all the rest.\n"},{"id":"179584","messageId":"4EC3B1B8.2040106@alum.mit.edu","threadId":"28785","inReplyTo":"1319804921-17545-1-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH 00/28] Store references hierarchically in cache -- benchmark results","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-11-16T12:51:04Z","receivedAt":"2011-11-16T12:51:04Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 10/28/2011 02:28 PM, mhagger@alum.mit.edu wrote:\n> [...]\n> This patch series changes how references are stored in the reference\n> cache data structures (entirely internal to refs.c).  Previously, the\n> packed refs were stored in one big array of pointers-to-struct, and\n> the loose refs were stored in another.  [...]\n> \n> Therefore, this patch series changes the data structure used to store\n> the reference cache from a single array of pointers-to-struct into a\n> tree-like structure in which each subdirectory of the reference\n> namespace is stored as an array of pointers-to-entry and entries can\n> be either references or subdirectories containing more references.\n> Moreover, each subdirectory of loose references is only read from disk\n> when a reference from that subdirectory (or one of its descendants) is\n> needed.  This slightly slows down commands that need to iterate\n> through all references (simply because the new data structures are\n> more complicated), but it *dramatically* decreases the time needed for\n> some common operations.  For example, in a test repository with 20000\n> revisions and 10000 loose tags:\n\nDue to the death of my old computer, I got distracted and never\npublished the benchmark results that I cited above.  (The numbers here\nare different than the originals because they are done on a different\ncomputer.)  The benchmarks are done using code from [1]; the test\nrepository consists of a linear series of 20000 commits (with very\nlittle content) and 10000 tags (on every second commit); the tags are\nunsharded.  The numbers are times in seconds; \"cold\" means that the disk\ncache was flushed immediately before the test; \"warm\" usually means that\nthe same operation was done a second time.\n\nColumn #0 is Git 1.7.7.2; column #1 is Git 1.7.8-rc2; column #2 is a\nrecent git master plus my ref-related patch series (what Junio calls\nmh/ref-api-take-2 plus the fix that I posted yesterday).\n\nThe main change between 1.7.7.2 and 1.7.8 is that the latter stores the\nreference cache in a sorted array rather than a linked list, meaning\nthat it is possible to use bisection to locate a reference quickly by\nname rather than having to search linearly through a linked list.  This\ngreatly helps some operations when most references are packed and can be\nread from disk quickly.  It doesn't make much difference when most\nreferences are loose, because the time required to read the loose\nreferences from disk overwhelms the time for iterating through the array\nin memory.\n\nThe main improvements between 1.7.8 and the new version are when the old\ncode would have read all of the loose references but the new code only\nneeds to read a couple of directories of them.  Given that almost all of\nthe references in the test repository are tags, they often don't need to\nbe read at all when branches are being manipulated.  By contrast, many\nold code paths force *all* of the references to be read, for example\nwhen they check for replacements in refs/replace/.\n\nI've done other benchmarks with varying numbers of references.  The\nresults suggest that many operations go from O(N) in the number of loose\nreferences to O(1) (e.g., if all they do is check refs/replace/) or some\nother slow scaling that depends on how the reference namespace is organized.\n\nAnything involving packed references necessarily scales at least like\nO(N) because packed references are all read at once.  OTOH reading\npacked refs is so much faster than reading loose references that with\n10000 references, packing is still advantageous.  (For some number of\nreferences, of course, the curves must cross and loose references will\nbe more efficient than packed references for some operations.)\n\nThe case of git-filter-branch is particularly dramatic; the old code\nscales like O(N^2) whereas the new code scales like O(N) as expected.\nMoreover, git-filter-branch creates lots of loose references while it\nworks, so it is not possible to evade the problem by packing the\nreferences before invoking git-filter-branch.  I believe that\ngit-filter-branch is mostly slowed down be each subcommand's check for\nreplacements in refs/replace (even though there are none in my test\nrepository) because in the old code this check forces *all* loose\nreferences to be read.  Versions 1.7.7 and 1.7.8 of git-filter-branch\nruns much faster if the --no-replace-objects option is used.\n\nWith these changes, it becomes thinkable to work with repositories with\nvery large numbers of references (especially if the reference namespace\nis sharded appropriately), whereas in 1.7.7 some operations were\nannoyingly slow.\n\nMichael\n\n[1] branch \"refperf\" at git://github.com/mhagger/git.git\n\n> ===================================  ========  ========  ========\n> Test name                                 [0]       [1]       [2]\n> ===================================  ========  ========  ========\n> branch-loose-cold                        1.59      1.68      0.29\n> branch-loose-warm                        0.04      0.04      0.00\n> for-each-ref-loose-cold                  1.86      1.96      1.88\n> for-each-ref-loose-warm                  0.10      0.10      0.11\n> checkout-loose-cold                      1.66      1.86      0.39\n> checkout-loose-warm                      0.04      0.05      0.01\n> checkout-orphan-loose                    0.04      0.04      0.00\n> checkout-from-detached-loose-cold        2.04      2.11      1.81\n> checkout-from-detached-loose-warm        0.24      0.15      0.16\n> branch-contains-loose-cold               1.79      1.86      1.87\n> branch-contains-loose-warm               0.14      0.14      0.14\n> pack-refs-loose                          0.49      0.53      0.53\n> branch-packed-cold                       0.28      0.25      0.25\n> branch-packed-warm                       0.02      0.00      0.00\n> for-each-ref-packed-cold                 0.34      0.39      0.40\n> for-each-ref-packed-warm                 0.07      0.07      0.07\n> checkout-packed-cold                     2.81      0.50      0.55\n> checkout-packed-warm                     0.01      0.00      0.01\n> checkout-orphan-packed                   0.00      0.00      0.00\n> checkout-from-detached-packed-cold       2.83      0.55      0.46\n> checkout-from-detached-packed-warm       2.45      0.12      0.13\n> branch-contains-packed-cold              0.38      0.32      0.43\n> branch-contains-packed-warm              0.11      0.11      0.11\n> clone-loose-cold                        30.16     30.31     30.51\n> clone-loose-warm                         1.28      1.30      1.33\n> fetch-nothing-loose                      0.21      0.39      0.38\n> pack-refs                                0.14      0.12      0.14\n> fetch-nothing-packed                     0.21      0.40      0.39\n> clone-packed-cold                        1.07      1.24      1.18\n> clone-packed-warm                        0.23      0.23      0.22\n> fetch-everything-cold                   30.49     30.89     31.09\n> fetch-everything-warm                    1.78      2.01      2.06\n> filter-branch-warm                    2949.81   2891.51    440.60\n> ===================================  ========  ========  ========\n> \n> \n> [0] 8d19b44 (tag: v1.7.7.2) Git 1.7.7.2\n>     Test repository created using: t/make-refperf-repo --commits 20000 --refs 10000\n> [1] bc1bbe0 (tag: v1.7.8-rc2) Git 1.7.8-rc2\n>     Test repository created using: t/make-refperf-repo --commits 20000 --refs 10000\n> [2] 01494b4 (github/ref-api-D) repack_without_ref(): call clear_packed_ref_cache()\n>     Test repository created using: t/make-refperf-repo --commits 20000 --refs 10000\n\n\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"}]}