{"thread":{"id":"29136","subject":"[PATCH v2 00/51] ref-api-C and ref-api-D re-roll","startedAt":"2011-12-12T05:38:07Z","lastAt":"2012-02-11T06:33:57Z","messageCount":79,"participants":["mhagger@alum.mit.edu","Junio C Hamano","Jeff King","Michael Haggerty"],"isPatch":true,"patchVersion":2,"patchTotal":51},"messages":[{"id":"180842","messageId":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":null,"subject":"[PATCH v2 00/51] ref-api-C and ref-api-D re-roll","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:07Z","receivedAt":"2011-12-12T05:38:07Z","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\nFollowing is a re-roll of the combination of the following patch\nseries:\n\nref-api-C == mh/ref-api-3 [1]\n    [PATCH v2 00/12] Use refs API more consistently\n\nref-api-D == mh/ref-api-take-2 [2]\n    [PATCH 00/28] Store references hierarchically in cache\n\nref-api-D-fix-v1 [3]\n    [PATCH] Fix \"is_refname_available(): query only possibly-conflicting references\"\n\nIt differs from the original patch series in the following ways:\n\n* Re-rolled onto the current master.\n\n* Combined some patches that logically belonged together but were\n  split in earlier series (mainly textual changes like variable\n  renamings); made other minor cleanups.\n\n* Incorporated ref-api-D-fix-v1 into the commit that it was fixing.\n\n* Removed any changes to the enforcement of refname checks.  The\n  earlier patch series included some changes that slightly tightened\n  up the checks applied to refnames, and could therefore have\n  theoretically caused problems for people whose repositories\n  currently include invalid reference names.  Following Junio's\n  suggestion, I separated those changes out of this patch series so\n  that they can be addressed separately.\n\nOtherwise, this patch series includes the substance of the earlier\npatch series, which is basically a change to storing reference-caches\nhierarchically and reading loose references lazily.  This, in turn,\nbrings big performance improvements for repositories with many\nreferences (especially many loose references) [4].\n\n[1] http://permalink.gmane.org/gmane.comp.version-control.git/184368\n[2] http://permalink.gmane.org/gmane.comp.version-control.git/184382\n[3] http://permalink.gmane.org/gmane.comp.version-control.git/185423\n[4] http://permalink.gmane.org/gmane.comp.version-control.git/185541\n\nMichael Haggerty (51):\n  struct ref_entry: document name member\n  refs: rename \"refname\" variables\n  refs: rename parameters result -> sha1\n  clear_ref_array(): rename from free_ref_array()\n  is_refname_available(): remove the \"quiet\" argument\n  parse_ref_line(): add docstring\n  add_ref(): add docstring\n  is_dup_ref(): extract function from sort_ref_array()\n  refs: change signatures of get_packed_refs() and get_loose_refs()\n  get_ref_dir(): change signature\n  resolve_gitlink_ref(): improve docstring\n  Pass a (ref_cache *) to the resolve_gitlink_*() helper functions\n  resolve_gitlink_ref_recursive(): change to work with struct ref_cache\n  repack_without_ref(): remove temporary\n  create_ref_entry(): extract function from add_ref()\n  add_ref(): take a (struct ref_entry *) parameter\n  do_for_each_ref(): correctly terminate while processesing extra_refs\n  do_for_each_ref_in_array(): new function\n  do_for_each_ref_in_arrays(): new function\n  repack_without_ref(): reimplement using do_for_each_ref_in_array()\n  names_conflict(): new function, extracted from is_refname_available()\n  names_conflict(): simplify implementation\n  is_refname_available(): reimplement using do_for_each_ref_in_array()\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 cache.h |    6 +-\n refs.c  | 1569 ++++++++++++++++++++++++++++++++++++++++----------------------\n refs.h  |   41 +-\n 3 files changed, 1041 insertions(+), 575 deletions(-)\n\n-- \n1.7.8\n"},{"id":"180843","messageId":"1323668338-1764-2-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 01/51] struct ref_entry: document name member","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:08Z","receivedAt":"2011-12-12T05:38:08Z","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\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f5cb297..669782f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -11,6 +11,7 @@ struct ref_entry {\n \tunsigned char flag; /* ISSYMREF? ISPACKED? */\n \tunsigned char sha1[20];\n \tunsigned char peeled[20];\n+\t/* The full name of the reference (e.g., \"refs/heads/master\"): */\n \tchar name[FLEX_ARRAY];\n };\n \n-- \n1.7.8\n"},{"id":"180847","messageId":"1323668338-1764-3-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 02/51] refs: rename \"refname\" variables","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:09Z","receivedAt":"2011-12-12T05:38:09Z","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\nTry to consistently use the variable name \"refname\" when referring to\na string that names a reference.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n cache.h |    6 +-\n refs.c  |  306 ++++++++++++++++++++++++++++++++-------------------------------\n refs.h  |   26 +++---\n 3 files changed, 175 insertions(+), 163 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 8c98d05..e1644b1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -831,9 +831,9 @@ static inline int get_sha1_with_context(const char *str, unsigned char *sha1, st\n extern int get_sha1_hex(const char *hex, unsigned char *sha1);\n \n extern char *sha1_to_hex(const unsigned char *sha1);\t/* static buffer result! */\n-extern int read_ref_full(const char *filename, unsigned char *sha1,\n+extern int read_ref_full(const char *refname, unsigned char *sha1,\n \t\t\t int reading, int *flags);\n-extern int read_ref(const char *filename, unsigned char *sha1);\n+extern int read_ref(const char *refname, unsigned char *sha1);\n \n /*\n  * Resolve a reference, recursively following symbolic refererences.\n@@ -865,7 +865,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);\n  *\n  * errno is sometimes set on errors, but not always.\n  */\n-extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);\n+extern const char *resolve_ref(const char *refname, unsigned char *sha1, int reading, int *flag);\n \n extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);\n extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);\ndiff --git a/refs.c b/refs.c\nindex 669782f..8ddd874 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -48,7 +48,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)\n \treturn line;\n }\n \n-static void add_ref(const char *name, const unsigned char *sha1,\n+static void add_ref(const char *refname, const unsigned char *sha1,\n \t\t    int flag, int check_name, struct ref_array *refs,\n \t\t    struct ref_entry **new_entry)\n {\n@@ -56,14 +56,14 @@ static void add_ref(const char *name, const unsigned char *sha1,\n \tstruct ref_entry *entry;\n \n \t/* Allocate it and add it in.. */\n-\tlen = strlen(name) + 1;\n+\tlen = strlen(refname) + 1;\n \tentry = xmalloc(sizeof(struct ref_entry) + len);\n \thashcpy(entry->sha1, sha1);\n \thashclr(entry->peeled);\n \tif (check_name &&\n-\t    check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))\n-\t\tdie(\"Reference has invalid format: '%s'\", name);\n-\tmemcpy(entry->name, name, len);\n+\t    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))\n+\t\tdie(\"Reference has invalid format: '%s'\", refname);\n+\tmemcpy(entry->name, refname, len);\n \tentry->flag = flag;\n \tif (new_entry)\n \t\t*new_entry = entry;\n@@ -106,20 +106,20 @@ 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 *name)\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 (name == NULL)\n+\tif (refname == NULL)\n \t\treturn NULL;\n \n \tif (!array->nr)\n \t\treturn NULL;\n \n-\tlen = strlen(name) + 1;\n+\tlen = strlen(refname) + 1;\n \te = xmalloc(sizeof(struct ref_entry) + len);\n-\tmemcpy(e->name, name, len);\n+\tmemcpy(e->name, refname, len);\n \n \tr = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp);\n \n@@ -223,7 +223,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \n \twhile (fgets(refline, sizeof(refline), f)) {\n \t\tunsigned char sha1[20];\n-\t\tconst char *name;\n+\t\tconst char *refname;\n \t\tstatic const char header[] = \"# pack-refs with:\";\n \n \t\tif (!strncmp(refline, header, sizeof(header)-1)) {\n@@ -234,9 +234,9 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tname = parse_ref_line(refline, sha1);\n-\t\tif (name) {\n-\t\t\tadd_ref(name, sha1, flag, 1, array, &last);\n+\t\trefname = parse_ref_line(refline, sha1);\n+\t\tif (refname) {\n+\t\t\tadd_ref(refname, sha1, flag, 1, array, &last);\n \t\t\tcontinue;\n \t\t}\n \t\tif (last &&\n@@ -249,9 +249,9 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \tsort_ref_array(array);\n }\n \n-void add_extra_ref(const char *name, const unsigned char *sha1, int flag)\n+void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n {\n-\tadd_ref(name, sha1, flag, 0, &extra_refs, NULL);\n+\tadd_ref(refname, sha1, flag, 0, &extra_refs, NULL);\n }\n \n void clear_extra_refs(void)\n@@ -298,11 +298,11 @@ static void get_ref_dir(const char *submodule, const char *base,\n \tif (dir) {\n \t\tstruct dirent *de;\n \t\tint baselen = strlen(base);\n-\t\tchar *ref = xmalloc(baselen + 257);\n+\t\tchar *refname = xmalloc(baselen + 257);\n \n-\t\tmemcpy(ref, base, baselen);\n+\t\tmemcpy(refname, base, baselen);\n \t\tif (baselen && base[baselen-1] != '/')\n-\t\t\tref[baselen++] = '/';\n+\t\t\trefname[baselen++] = '/';\n \n \t\twhile ((de = readdir(dir)) != NULL) {\n \t\t\tunsigned char sha1[20];\n@@ -318,30 +318,30 @@ static void get_ref_dir(const char *submodule, 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(ref + baselen, de->d_name, namelen+1);\n+\t\t\tmemcpy(refname + baselen, de->d_name, namelen+1);\n \t\t\trefdir = submodule\n-\t\t\t\t? git_path_submodule(submodule, \"%s\", ref)\n-\t\t\t\t: git_path(\"%s\", ref);\n+\t\t\t\t? git_path_submodule(submodule, \"%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(submodule, ref, array);\n+\t\t\t\tget_ref_dir(submodule, refname, array);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (submodule) {\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag = 0;\n-\t\t\t\tif (resolve_gitlink_ref(submodule, ref, sha1) < 0) {\n+\t\t\t\tif (resolve_gitlink_ref(submodule, 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 if (read_ref_full(ref, sha1, 1, &flag)) {\n+\t\t\t} else if (read_ref_full(refname, sha1, 1, &flag)) {\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t}\n-\t\t\tadd_ref(ref, sha1, flag, 1, array, NULL);\n+\t\t\tadd_ref(refname, sha1, flag, 1, array, NULL);\n \t\t}\n-\t\tfree(ref);\n+\t\tfree(refname);\n \t\tclosedir(dir);\n \t}\n }\n@@ -401,7 +401,8 @@ static struct ref_array *get_loose_refs(const char *submodule)\n  * from \"name\", which is \"module/.git/<refname>\". Find <refname> in\n  * the packed-refs file for the submodule.\n  */\n-static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)\n+static int resolve_gitlink_packed_ref(char *name, int pathlen,\n+\t\t\t\t      const char *refname, unsigned char *result)\n {\n \tint retval = -1;\n \tstruct ref_entry *ref;\n@@ -420,7 +421,9 @@ static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refna\n \treturn retval;\n }\n \n-static int resolve_gitlink_ref_recursive(char *name, int pathlen, const char *refname, unsigned char *result, int recursion)\n+static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n+\t\t\t\t\t const char *refname, unsigned char *result,\n+\t\t\t\t\t int recursion)\n {\n \tint fd, len = strlen(refname);\n \tchar buffer[128], *p;\n@@ -487,10 +490,10 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re\n  * Try to read ref from the packed references.  On success, set sha1\n  * and return 0; otherwise, return -1.\n  */\n-static int get_packed_ref(const char *ref, unsigned char *sha1)\n+static int get_packed_ref(const char *refname, unsigned char *sha1)\n {\n \tstruct ref_array *packed = get_packed_refs(NULL);\n-\tstruct ref_entry *entry = search_ref_array(packed, ref);\n+\tstruct ref_entry *entry = search_ref_array(packed, refname);\n \tif (entry) {\n \t\thashcpy(sha1, entry->sha1);\n \t\treturn 0;\n@@ -498,17 +501,17 @@ static int get_packed_ref(const char *ref, unsigned char *sha1)\n \treturn -1;\n }\n \n-const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)\n+const char *resolve_ref(const char *refname, unsigned char *sha1, int reading, int *flag)\n {\n \tint depth = MAXDEPTH;\n \tssize_t len;\n \tchar buffer[256];\n-\tstatic char ref_buffer[256];\n+\tstatic char refname_buffer[256];\n \n \tif (flag)\n \t\t*flag = 0;\n \n-\tif (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))\n+\tif (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))\n \t\treturn NULL;\n \n \tfor (;;) {\n@@ -520,7 +523,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *\n \t\tif (--depth < 0)\n \t\t\treturn NULL;\n \n-\t\tgit_snpath(path, sizeof(path), \"%s\", ref);\n+\t\tgit_snpath(path, sizeof(path), \"%s\", refname);\n \n \t\tif (lstat(path, &st) < 0) {\n \t\t\tif (errno != ENOENT)\n@@ -529,17 +532,17 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *\n \t\t\t * The loose reference file does not exist;\n \t\t\t * check for a packed reference.\n \t\t\t */\n-\t\t\tif (!get_packed_ref(ref, sha1)) {\n+\t\t\tif (!get_packed_ref(refname, sha1)) {\n \t\t\t\tif (flag)\n \t\t\t\t\t*flag |= REF_ISPACKED;\n-\t\t\t\treturn ref;\n+\t\t\t\treturn refname;\n \t\t\t}\n \t\t\t/* The reference is not a packed reference, either. */\n \t\t\tif (reading) {\n \t\t\t\treturn NULL;\n \t\t\t} else {\n \t\t\t\thashclr(sha1);\n-\t\t\t\treturn ref;\n+\t\t\t\treturn refname;\n \t\t\t}\n \t\t}\n \n@@ -551,8 +554,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *\n \t\t\tbuffer[len] = 0;\n \t\t\tif (!prefixcmp(buffer, \"refs/\") &&\n \t\t\t\t\t!check_refname_format(buffer, 0)) {\n-\t\t\t\tstrcpy(ref_buffer, buffer);\n-\t\t\t\tref = ref_buffer;\n+\t\t\t\tstrcpy(refname_buffer, buffer);\n+\t\t\t\trefname = refname_buffer;\n \t\t\t\tif (flag)\n \t\t\t\t\t*flag |= REF_ISSYMREF;\n \t\t\t\tcontinue;\n@@ -595,7 +598,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *\n \t\t\t\t*flag |= REF_ISBROKEN;\n \t\t\treturn NULL;\n \t\t}\n-\t\tref = strcpy(ref_buffer, buf);\n+\t\trefname = strcpy(refname_buffer, buf);\n \t}\n \t/* Please note that FETCH_HEAD has a second line containing other data. */\n \tif (get_sha1_hex(buffer, sha1) || (buffer[40] != '\\0' && !isspace(buffer[40]))) {\n@@ -603,7 +606,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *\n \t\t\t*flag |= REF_ISBROKEN;\n \t\treturn NULL;\n \t}\n-\treturn ref;\n+\treturn refname;\n }\n \n /* The argument to filter_refs */\n@@ -613,16 +616,16 @@ struct ref_filter {\n \tvoid *cb_data;\n };\n \n-int read_ref_full(const char *ref, unsigned char *sha1, int reading, int *flags)\n+int read_ref_full(const char *refname, unsigned char *sha1, int reading, int *flags)\n {\n-\tif (resolve_ref(ref, sha1, reading, flags))\n+\tif (resolve_ref(refname, sha1, reading, flags))\n \t\treturn 0;\n \treturn -1;\n }\n \n-int read_ref(const char *ref, unsigned char *sha1)\n+int read_ref(const char *refname, unsigned char *sha1)\n {\n-\treturn read_ref_full(ref, sha1, 1, NULL);\n+\treturn read_ref_full(refname, sha1, 1, NULL);\n }\n \n #define DO_FOR_EACH_INCLUDE_BROKEN 01\n@@ -644,23 +647,23 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,\n \treturn fn(entry->name + trim, entry->sha1, entry->flag, cb_data);\n }\n \n-static int filter_refs(const char *ref, const unsigned char *sha, int flags,\n-\tvoid *data)\n+static int filter_refs(const char *refname, const unsigned char *sha, int flags,\n+\t\t       void *data)\n {\n \tstruct ref_filter *filter = (struct ref_filter *)data;\n-\tif (fnmatch(filter->pattern, ref, 0))\n+\tif (fnmatch(filter->pattern, refname, 0))\n \t\treturn 0;\n-\treturn filter->fn(ref, sha, flags, filter->cb_data);\n+\treturn filter->fn(refname, sha, flags, filter->cb_data);\n }\n \n-int peel_ref(const char *ref, unsigned char *sha1)\n+int peel_ref(const char *refname, unsigned char *sha1)\n {\n \tint flag;\n \tunsigned char base[20];\n \tstruct object *o;\n \n-\tif (current_ref && (current_ref->name == ref\n-\t\t|| !strcmp(current_ref->name, ref))) {\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\treturn 0;\n@@ -669,12 +672,12 @@ int peel_ref(const char *ref, unsigned char *sha1)\n \t\tgoto fallback;\n \t}\n \n-\tif (read_ref_full(ref, base, 1, &flag))\n+\tif (read_ref_full(refname, base, 1, &flag))\n \t\treturn -1;\n \n \tif ((flag & REF_ISPACKED)) {\n \t\tstruct ref_array *array = get_packed_refs(NULL);\n-\t\tstruct ref_entry *r = search_ref_array(array, ref);\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@@ -685,7 +688,7 @@ int peel_ref(const char *ref, unsigned char *sha1)\n fallback:\n \to = parse_object(base);\n \tif (o && o->type == OBJ_TAG) {\n-\t\to = deref_tag(o, ref, 0);\n+\t\to = deref_tag(o, refname, 0);\n \t\tif (o) {\n \t\t\thashcpy(sha1, o->sha1);\n \t\t\treturn 0;\n@@ -915,16 +918,16 @@ static inline int bad_ref_char(int ch)\n }\n \n /*\n- * Try to read one refname component from the front of ref.  Return\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 *ref, int flags)\n+static int check_refname_component(const char *refname, int flags)\n {\n \tconst char *cp;\n \tchar last = '\\0';\n \n-\tfor (cp = ref; ; cp++) {\n+\tfor (cp = refname; ; cp++) {\n \t\tchar ch = *cp;\n \t\tif (ch == '\\0' || ch == '/')\n \t\t\tbreak;\n@@ -936,34 +939,34 @@ static int check_refname_component(const char *ref, int flags)\n \t\t\treturn -1; /* Refname contains \"@{\". */\n \t\tlast = ch;\n \t}\n-\tif (cp == ref)\n+\tif (cp == refname)\n \t\treturn -1; /* Component has zero length. */\n-\tif (ref[0] == '.') {\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 (ref[1] == '\\0')\n+\t\tif (refname[1] == '\\0')\n \t\t\treturn -1; /* Component equals \".\". */\n \t}\n-\tif (cp - ref >= 5 && !memcmp(cp - 5, \".lock\", 5))\n+\tif (cp - refname >= 5 && !memcmp(cp - 5, \".lock\", 5))\n \t\treturn -1; /* Refname ends with \".lock\". */\n-\treturn cp - ref;\n+\treturn cp - refname;\n }\n \n-int check_refname_format(const char *ref, int flags)\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(ref, flags);\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\tref[0] == '*' &&\n-\t\t\t\t\t(ref[1] == '\\0' || ref[1] == '/')) {\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@@ -972,13 +975,13 @@ int check_refname_format(const char *ref, int flags)\n \t\t\t}\n \t\t}\n \t\tcomponent_count++;\n-\t\tif (ref[component_len] == '\\0')\n+\t\tif (refname[component_len] == '\\0')\n \t\t\tbreak;\n \t\t/* Skip to next component. */\n-\t\tref += component_len + 1;\n+\t\trefname += component_len + 1;\n \t}\n \n-\tif (ref[component_len - 1] == '.')\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@@ -1054,22 +1057,22 @@ static int remove_empty_directories(const char *file)\n \treturn result;\n }\n \n-static int is_refname_available(const char *ref, const char *oldref,\n+static int is_refname_available(const char *refname, const char *oldrefname,\n \t\t\t\tstruct ref_array *array, int quiet)\n {\n-\tint i, namlen = strlen(ref); /* e.g. 'foo/bar' */\n+\tint i, namlen = strlen(refname); /* e.g. 'foo/bar' */\n \tfor (i = 0; i < array->nr; i++ ) {\n \t\tstruct ref_entry *entry = array->refs[i];\n \t\t/* entry->name could be 'foo' or 'foo/bar/baz' */\n-\t\tif (!oldref || strcmp(oldref, entry->name)) {\n+\t\tif (!oldrefname || strcmp(oldrefname, entry->name)) {\n \t\t\tint len = strlen(entry->name);\n \t\t\tint cmplen = (namlen < len) ? namlen : len;\n-\t\t\tconst char *lead = (namlen < len) ? entry->name : ref;\n-\t\t\tif (!strncmp(ref, entry->name, cmplen) &&\n+\t\t\tconst char *lead = (namlen < len) ? entry->name : refname;\n+\t\t\tif (!strncmp(refname, entry->name, cmplen) &&\n \t\t\t    lead[cmplen] == '/') {\n \t\t\t\tif (!quiet)\n \t\t\t\t\terror(\"'%s' exists; cannot create '%s'\",\n-\t\t\t\t\t      entry->name, ref);\n+\t\t\t\t\t      entry->name, refname);\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t}\n@@ -1165,10 +1168,12 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)\n \treturn logs_found;\n }\n \n-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)\n+static struct ref_lock *lock_ref_sha1_basic(const char *refname,\n+\t\t\t\t\t    const unsigned char *old_sha1,\n+\t\t\t\t\t    int flags, int *type_p)\n {\n \tchar *ref_file;\n-\tconst char *orig_ref = ref;\n+\tconst char *orig_refname = refname;\n \tstruct ref_lock *lock;\n \tint last_errno = 0;\n \tint type, lflags;\n@@ -1178,27 +1183,27 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char\n \tlock = xcalloc(1, sizeof(struct ref_lock));\n \tlock->lock_fd = -1;\n \n-\tref = resolve_ref(ref, lock->old_sha1, mustexist, &type);\n-\tif (!ref && errno == EISDIR) {\n+\trefname = resolve_ref(refname, lock->old_sha1, mustexist, &type);\n+\tif (!refname && errno == EISDIR) {\n \t\t/* we are trying to lock foo but we used to\n \t\t * have foo/bar which now does not exist;\n \t\t * it is normal for the empty directory 'foo'\n \t\t * to remain.\n \t\t */\n-\t\tref_file = git_path(\"%s\", orig_ref);\n+\t\tref_file = git_path(\"%s\", orig_refname);\n \t\tif (remove_empty_directories(ref_file)) {\n \t\t\tlast_errno = errno;\n-\t\t\terror(\"there are still refs under '%s'\", orig_ref);\n+\t\t\terror(\"there are still refs under '%s'\", orig_refname);\n \t\t\tgoto error_return;\n \t\t}\n-\t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);\n+\t\trefname = resolve_ref(orig_refname, lock->old_sha1, mustexist, &type);\n \t}\n \tif (type_p)\n \t    *type_p = type;\n-\tif (!ref) {\n+\tif (!refname) {\n \t\tlast_errno = errno;\n \t\terror(\"unable to resolve reference %s: %s\",\n-\t\t\torig_ref, strerror(errno));\n+\t\t\torig_refname, strerror(errno));\n \t\tgoto error_return;\n \t}\n \tmissing = is_null_sha1(lock->old_sha1);\n@@ -1208,7 +1213,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char\n \t * name is a proper prefix of our refname.\n \t */\n \tif (missing &&\n-\t     !is_refname_available(ref, NULL, get_packed_refs(NULL), 0)) {\n+\t     !is_refname_available(refname, NULL, get_packed_refs(NULL), 0)) {\n \t\tlast_errno = ENOTDIR;\n \t\tgoto error_return;\n \t}\n@@ -1217,12 +1222,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char\n \n \tlflags = LOCK_DIE_ON_ERROR;\n \tif (flags & REF_NODEREF) {\n-\t\tref = orig_ref;\n+\t\trefname = orig_refname;\n \t\tlflags |= LOCK_NODEREF;\n \t}\n-\tlock->ref_name = xstrdup(ref);\n-\tlock->orig_ref_name = xstrdup(orig_ref);\n-\tref_file = git_path(\"%s\", ref);\n+\tlock->ref_name = xstrdup(refname);\n+\tlock->orig_ref_name = xstrdup(orig_refname);\n+\tref_file = git_path(\"%s\", refname);\n \tif (missing)\n \t\tlock->force_write = 1;\n \tif ((flags & REF_NODEREF) && (type & REF_ISSYMREF))\n@@ -1243,20 +1248,21 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char\n \treturn NULL;\n }\n \n-struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)\n+struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1)\n {\n \tchar refpath[PATH_MAX];\n-\tif (check_refname_format(ref, 0))\n+\tif (check_refname_format(refname, 0))\n \t\treturn NULL;\n-\tstrcpy(refpath, mkpath(\"refs/%s\", ref));\n+\tstrcpy(refpath, mkpath(\"refs/%s\", refname));\n \treturn lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);\n }\n \n-struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)\n+struct ref_lock *lock_any_ref_for_update(const char *refname,\n+\t\t\t\t\t const unsigned char *old_sha1, int flags)\n {\n-\tif (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))\n+\tif (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))\n \t\treturn NULL;\n-\treturn lock_ref_sha1_basic(ref, old_sha1, flags, NULL);\n+\treturn lock_ref_sha1_basic(refname, old_sha1, flags, NULL);\n }\n \n static struct lock_file packlock;\n@@ -1342,97 +1348,97 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n  */\n #define TMP_RENAMED_LOG  \"logs/refs/.tmp-renamed-log\"\n \n-int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n+int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)\n {\n \tunsigned char sha1[20], orig_sha1[20];\n \tint flag = 0, logmoved = 0;\n \tstruct ref_lock *lock;\n \tstruct stat loginfo;\n-\tint log = !lstat(git_path(\"logs/%s\", oldref), &loginfo);\n+\tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n \tconst char *symref = NULL;\n \n \tif (log && S_ISLNK(loginfo.st_mode))\n-\t\treturn error(\"reflog for %s is a symlink\", oldref);\n+\t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n \n-\tsymref = resolve_ref(oldref, orig_sha1, 1, &flag);\n+\tsymref = resolve_ref(oldrefname, orig_sha1, 1, &flag);\n \tif (flag & REF_ISSYMREF)\n \t\treturn error(\"refname %s is a symbolic ref, renaming it is not supported\",\n-\t\t\toldref);\n+\t\t\toldrefname);\n \tif (!symref)\n-\t\treturn error(\"refname %s not found\", oldref);\n+\t\treturn error(\"refname %s not found\", oldrefname);\n \n-\tif (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0))\n+\tif (!is_refname_available(newrefname, oldrefname, get_packed_refs(NULL), 0))\n \t\treturn 1;\n \n-\tif (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0))\n+\tif (!is_refname_available(newrefname, oldrefname, get_loose_refs(NULL), 0))\n \t\treturn 1;\n \n-\tif (log && rename(git_path(\"logs/%s\", oldref), git_path(TMP_RENAMED_LOG)))\n+\tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n \t\treturn error(\"unable to move logfile logs/%s to \"TMP_RENAMED_LOG\": %s\",\n-\t\t\toldref, strerror(errno));\n+\t\t\toldrefname, strerror(errno));\n \n-\tif (delete_ref(oldref, orig_sha1, REF_NODEREF)) {\n-\t\terror(\"unable to delete old %s\", oldref);\n+\tif (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {\n+\t\terror(\"unable to delete old %s\", oldrefname);\n \t\tgoto rollback;\n \t}\n \n-\tif (!read_ref_full(newref, sha1, 1, &flag) &&\n-\t    delete_ref(newref, sha1, REF_NODEREF)) {\n+\tif (!read_ref_full(newrefname, sha1, 1, &flag) &&\n+\t    delete_ref(newrefname, sha1, REF_NODEREF)) {\n \t\tif (errno==EISDIR) {\n-\t\t\tif (remove_empty_directories(git_path(\"%s\", newref))) {\n-\t\t\t\terror(\"Directory not empty: %s\", newref);\n+\t\t\tif (remove_empty_directories(git_path(\"%s\", newrefname))) {\n+\t\t\t\terror(\"Directory not empty: %s\", newrefname);\n \t\t\t\tgoto rollback;\n \t\t\t}\n \t\t} else {\n-\t\t\terror(\"unable to delete existing %s\", newref);\n+\t\t\terror(\"unable to delete existing %s\", newrefname);\n \t\t\tgoto rollback;\n \t\t}\n \t}\n \n-\tif (log && safe_create_leading_directories(git_path(\"logs/%s\", newref))) {\n-\t\terror(\"unable to create directory for %s\", newref);\n+\tif (log && safe_create_leading_directories(git_path(\"logs/%s\", newrefname))) {\n+\t\terror(\"unable to create directory for %s\", newrefname);\n \t\tgoto rollback;\n \t}\n \n  retry:\n-\tif (log && rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", newref))) {\n+\tif (log && rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", newrefname))) {\n \t\tif (errno==EISDIR || errno==ENOTDIR) {\n \t\t\t/*\n \t\t\t * rename(a, b) when b is an existing\n \t\t\t * directory ought to result in ISDIR, but\n \t\t\t * Solaris 5.8 gives ENOTDIR.  Sheesh.\n \t\t\t */\n-\t\t\tif (remove_empty_directories(git_path(\"logs/%s\", newref))) {\n-\t\t\t\terror(\"Directory not empty: logs/%s\", newref);\n+\t\t\tif (remove_empty_directories(git_path(\"logs/%s\", newrefname))) {\n+\t\t\t\terror(\"Directory not empty: logs/%s\", newrefname);\n \t\t\t\tgoto rollback;\n \t\t\t}\n \t\t\tgoto retry;\n \t\t} else {\n \t\t\terror(\"unable to move logfile \"TMP_RENAMED_LOG\" to logs/%s: %s\",\n-\t\t\t\tnewref, strerror(errno));\n+\t\t\t\tnewrefname, strerror(errno));\n \t\t\tgoto rollback;\n \t\t}\n \t}\n \tlogmoved = log;\n \n-\tlock = lock_ref_sha1_basic(newref, NULL, 0, NULL);\n+\tlock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);\n \tif (!lock) {\n-\t\terror(\"unable to lock %s for update\", newref);\n+\t\terror(\"unable to lock %s for update\", newrefname);\n \t\tgoto rollback;\n \t}\n \tlock->force_write = 1;\n \thashcpy(lock->old_sha1, orig_sha1);\n \tif (write_ref_sha1(lock, orig_sha1, logmsg)) {\n-\t\terror(\"unable to write current sha1 into %s\", newref);\n+\t\terror(\"unable to write current sha1 into %s\", newrefname);\n \t\tgoto rollback;\n \t}\n \n \treturn 0;\n \n  rollback:\n-\tlock = lock_ref_sha1_basic(oldref, NULL, 0, NULL);\n+\tlock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);\n \tif (!lock) {\n-\t\terror(\"unable to lock %s for rollback\", oldref);\n+\t\terror(\"unable to lock %s for rollback\", oldrefname);\n \t\tgoto rollbacklog;\n \t}\n \n@@ -1440,17 +1446,17 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)\n \tflag = log_all_ref_updates;\n \tlog_all_ref_updates = 0;\n \tif (write_ref_sha1(lock, orig_sha1, NULL))\n-\t\terror(\"unable to write current sha1 into %s\", oldref);\n+\t\terror(\"unable to write current sha1 into %s\", oldrefname);\n \tlog_all_ref_updates = flag;\n \n  rollbacklog:\n-\tif (logmoved && rename(git_path(\"logs/%s\", newref), git_path(\"logs/%s\", oldref)))\n+\tif (logmoved && rename(git_path(\"logs/%s\", newrefname), git_path(\"logs/%s\", oldrefname)))\n \t\terror(\"unable to restore logfile %s from %s: %s\",\n-\t\t\toldref, newref, strerror(errno));\n+\t\t\toldrefname, newrefname, strerror(errno));\n \tif (!logmoved && log &&\n-\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldref)))\n+\t    rename(git_path(TMP_RENAMED_LOG), git_path(\"logs/%s\", oldrefname)))\n \t\terror(\"unable to restore logfile %s from \"TMP_RENAMED_LOG\": %s\",\n-\t\t\toldref, strerror(errno));\n+\t\t\toldrefname, strerror(errno));\n \n \treturn 1;\n }\n@@ -1507,16 +1513,16 @@ static int copy_msg(char *buf, const char *msg)\n \treturn cp - buf;\n }\n \n-int log_ref_setup(const char *ref_name, char *logfile, int bufsize)\n+int log_ref_setup(const char *refname, char *logfile, int bufsize)\n {\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n \n-\tgit_snpath(logfile, bufsize, \"logs/%s\", ref_name);\n+\tgit_snpath(logfile, bufsize, \"logs/%s\", refname);\n \tif (log_all_ref_updates &&\n-\t    (!prefixcmp(ref_name, \"refs/heads/\") ||\n-\t     !prefixcmp(ref_name, \"refs/remotes/\") ||\n-\t     !prefixcmp(ref_name, \"refs/notes/\") ||\n-\t     !strcmp(ref_name, \"HEAD\"))) {\n+\t    (!prefixcmp(refname, \"refs/heads/\") ||\n+\t     !prefixcmp(refname, \"refs/remotes/\") ||\n+\t     !prefixcmp(refname, \"refs/notes/\") ||\n+\t     !strcmp(refname, \"HEAD\"))) {\n \t\tif (safe_create_leading_directories(logfile) < 0)\n \t\t\treturn error(\"unable to create directory for %s\",\n \t\t\t\t     logfile);\n@@ -1546,7 +1552,7 @@ int log_ref_setup(const char *ref_name, char *logfile, int bufsize)\n \treturn 0;\n }\n \n-static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,\n+static int log_ref_write(const char *refname, const unsigned char *old_sha1,\n \t\t\t const unsigned char *new_sha1, const char *msg)\n {\n \tint logfd, result, written, oflags = O_APPEND | O_WRONLY;\n@@ -1559,7 +1565,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,\n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n \n-\tresult = log_ref_setup(ref_name, log_file, sizeof(log_file));\n+\tresult = log_ref_setup(refname, log_file, sizeof(log_file));\n \tif (result)\n \t\treturn result;\n \n@@ -1730,7 +1736,9 @@ static char *ref_msg(const char *line, const char *endp)\n \treturn xmemdupz(line, ep - line);\n }\n \n-int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)\n+int read_ref_at(const char *refname, unsigned long at_time, int cnt,\n+\t\tunsigned char *sha1, char **msg,\n+\t\tunsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)\n {\n \tconst char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;\n \tchar *tz_c;\n@@ -1741,7 +1749,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *\n \tvoid *log_mapped;\n \tsize_t mapsz;\n \n-\tlogfile = git_path(\"logs/%s\", ref);\n+\tlogfile = git_path(\"logs/%s\", refname);\n \tlogfd = open(logfile, O_RDONLY, 0);\n \tif (logfd < 0)\n \t\tdie_errno(\"Unable to read log '%s'\", logfile);\n@@ -1834,14 +1842,14 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *\n \treturn 1;\n }\n \n-int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data)\n+int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long ofs, void *cb_data)\n {\n \tconst char *logfile;\n \tFILE *logfp;\n \tstruct strbuf sb = STRBUF_INIT;\n \tint ret = 0;\n \n-\tlogfile = git_path(\"logs/%s\", ref);\n+\tlogfile = git_path(\"logs/%s\", refname);\n \tlogfp = fopen(logfile, \"r\");\n \tif (!logfp)\n \t\treturn -1;\n@@ -1892,9 +1900,9 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,\n \treturn ret;\n }\n \n-int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)\n+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data)\n {\n-\treturn for_each_recent_reflog_ent(ref, fn, 0, cb_data);\n+\treturn for_each_recent_reflog_ent(refname, fn, 0, cb_data);\n }\n \n static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)\n@@ -2014,7 +2022,7 @@ static void gen_scanf_fmt(char *scanf_fmt, const char *rule)\n \treturn;\n }\n \n-char *shorten_unambiguous_ref(const char *ref, int strict)\n+char *shorten_unambiguous_ref(const char *refname, int strict)\n {\n \tint i;\n \tstatic char **scanf_fmts;\n@@ -2043,10 +2051,10 @@ char *shorten_unambiguous_ref(const char *ref, int strict)\n \n \t/* bail out if there are no rules */\n \tif (!nr_rules)\n-\t\treturn xstrdup(ref);\n+\t\treturn xstrdup(refname);\n \n-\t/* buffer for scanf result, at most ref must fit */\n-\tshort_name = xstrdup(ref);\n+\t/* buffer for scanf result, at most refname must fit */\n+\tshort_name = xstrdup(refname);\n \n \t/* skip first rule, it will always match */\n \tfor (i = nr_rules - 1; i > 0 ; --i) {\n@@ -2054,7 +2062,7 @@ char *shorten_unambiguous_ref(const char *ref, int strict)\n \t\tint rules_to_fail = i;\n \t\tint short_name_len;\n \n-\t\tif (1 != sscanf(ref, scanf_fmts[i], short_name))\n+\t\tif (1 != sscanf(refname, scanf_fmts[i], short_name))\n \t\t\tcontinue;\n \n \t\tshort_name_len = strlen(short_name);\n@@ -2098,5 +2106,5 @@ char *shorten_unambiguous_ref(const char *ref, int strict)\n \t}\n \n \tfree(short_name);\n-\treturn xstrdup(ref);\n+\treturn xstrdup(refname);\n }\ndiff --git a/refs.h b/refs.h\nindex 3fd5536..e36bd03 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -60,14 +60,16 @@ extern void add_extra_ref(const char *refname, const unsigned char *sha1, int fl\n extern void clear_extra_refs(void);\n extern int ref_exists(const char *);\n \n-extern int peel_ref(const char *, unsigned char *);\n+extern int peel_ref(const char *refname, unsigned char *sha1);\n \n /** Locks a \"refs/\" ref returning the lock on success and NULL on failure. **/\n-extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);\n+extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);\n \n /** Locks any ref (for 'HEAD' type refs). */\n #define REF_NODEREF\t0x01\n-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags);\n+extern struct ref_lock *lock_any_ref_for_update(const char *refname,\n+\t\t\t\t\t\tconst unsigned char *old_sha1,\n+\t\t\t\t\t\tint flags);\n \n /** Close the file descriptor owned by a lock and return the status */\n extern int close_ref(struct ref_lock *lock);\n@@ -93,12 +95,14 @@ extern void invalidate_ref_cache(const char *submodule);\n int log_ref_setup(const char *ref_name, char *logfile, int bufsize);\n \n /** Reads log for the value of ref during at_time. **/\n-extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);\n+extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,\n+\t\t       unsigned char *sha1, char **msg,\n+\t\t       unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);\n \n /* iterate over reflog entries */\n typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);\n-int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);\n-int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data);\n+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);\n+int for_each_recent_reflog_ent(const char *refname, each_reflog_ent_fn fn, long, void *cb_data);\n \n /*\n  * Calls the specified function for each reflog file until it returns nonzero,\n@@ -111,9 +115,9 @@ extern int for_each_reflog(each_ref_fn, void *);\n #define REFNAME_DOT_COMPONENT 4\n \n /*\n- * Return 0 iff ref has the correct format for a refname according to\n- * the rules described in Documentation/git-check-ref-format.txt.  If\n- * REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level\n+ * Return 0 iff refname has the correct format for a refname according\n+ * to the rules described in Documentation/git-check-ref-format.txt.\n+ * If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level\n  * reference names.  If REFNAME_REFSPEC_PATTERN is set in flags, then\n  * allow a \"*\" wildcard character in place of one of the name\n  * components.  No leading or repeated slashes are accepted.  If\n@@ -121,10 +125,10 @@ extern int for_each_reflog(each_ref_fn, void *);\n  * components to start with \".\" (but not a whole component equal to\n  * \".\" or \"..\").\n  */\n-extern int check_refname_format(const char *ref, int flags);\n+extern int check_refname_format(const char *refname, int flags);\n \n extern const char *prettify_refname(const char *refname);\n-extern char *shorten_unambiguous_ref(const char *ref, int strict);\n+extern char *shorten_unambiguous_ref(const char *refname, int strict);\n \n /** rename ref, return 0 on success **/\n extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n-- \n1.7.8\n"},{"id":"180844","messageId":"1323668338-1764-4-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 03/51] refs: rename parameters result -> sha1","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:10Z","receivedAt":"2011-12-12T05:38:10Z","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\nTry consistently to use the name \"sha1\" for parameters to which a SHA1\nwill be stored.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   20 ++++++++++----------\n refs.h |    2 +-\n 2 files changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 8ddd874..9f7a5ec 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -402,7 +402,7 @@ static struct ref_array *get_loose_refs(const char *submodule)\n  * the packed-refs file for the submodule.\n  */\n static int resolve_gitlink_packed_ref(char *name, int pathlen,\n-\t\t\t\t      const char *refname, unsigned char *result)\n+\t\t\t\t      const char *refname, unsigned char *sha1)\n {\n \tint retval = -1;\n \tstruct ref_entry *ref;\n@@ -415,14 +415,14 @@ static int resolve_gitlink_packed_ref(char *name, int pathlen,\n \tarray = get_packed_refs(name);\n \tref = search_ref_array(array, refname);\n \tif (ref != NULL) {\n-\t\tmemcpy(result, ref->sha1, 20);\n+\t\tmemcpy(sha1, ref->sha1, 20);\n \t\tretval = 0;\n \t}\n \treturn retval;\n }\n \n static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n-\t\t\t\t\t const char *refname, unsigned char *result,\n+\t\t\t\t\t const char *refname, unsigned char *sha1,\n \t\t\t\t\t int recursion)\n {\n \tint fd, len = strlen(refname);\n@@ -433,7 +433,7 @@ static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n \tmemcpy(name + pathlen, refname, len+1);\n \tfd = open(name, O_RDONLY);\n \tif (fd < 0)\n-\t\treturn resolve_gitlink_packed_ref(name, pathlen, refname, result);\n+\t\treturn resolve_gitlink_packed_ref(name, pathlen, refname, sha1);\n \n \tlen = read(fd, buffer, sizeof(buffer)-1);\n \tclose(fd);\n@@ -444,7 +444,7 @@ static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n \tbuffer[len] = 0;\n \n \t/* Was it a detached head or an old-fashioned symlink? */\n-\tif (!get_sha1_hex(buffer, result))\n+\tif (!get_sha1_hex(buffer, sha1))\n \t\treturn 0;\n \n \t/* Symref? */\n@@ -454,10 +454,10 @@ static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n \twhile (isspace(*p))\n \t\tp++;\n \n-\treturn resolve_gitlink_ref_recursive(name, pathlen, p, result, recursion+1);\n+\treturn resolve_gitlink_ref_recursive(name, pathlen, p, sha1, recursion+1);\n }\n \n-int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *result)\n+int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)\n {\n \tint len = strlen(path), retval;\n \tchar *gitdir;\n@@ -481,7 +481,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re\n \t}\n \tgitdir[len] = '/';\n \tgitdir[++len] = '\\0';\n-\tretval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);\n+\tretval = resolve_gitlink_ref_recursive(gitdir, len, refname, sha1, 0);\n \tfree(gitdir);\n \treturn retval;\n }\n@@ -647,13 +647,13 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,\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+static int filter_refs(const char *refname, const unsigned char *sha1, int flags,\n \t\t       void *data)\n {\n \tstruct ref_filter *filter = (struct ref_filter *)data;\n \tif (fnmatch(filter->pattern, refname, 0))\n \t\treturn 0;\n-\treturn filter->fn(refname, sha, flags, filter->cb_data);\n+\treturn filter->fn(refname, sha1, flags, filter->cb_data);\n }\n \n int peel_ref(const char *refname, unsigned char *sha1)\ndiff --git a/refs.h b/refs.h\nindex e36bd03..4c5d570 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -134,7 +134,7 @@ extern char *shorten_unambiguous_ref(const char *refname, int strict);\n extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n \n /** resolve ref in nested \"gitlink\" repository */\n-extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);\n+extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *sha1);\n \n /** lock a ref and then write its file */\n enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };\n-- \n1.7.8\n"},{"id":"180845","messageId":"1323668338-1764-5-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 04/51] clear_ref_array(): rename from free_ref_array()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:11Z","receivedAt":"2011-12-12T05:38:11Z","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 the function since it doesn't actually free the array object\nthat is passed to it.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    8 ++++----\n 1 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 9f7a5ec..7be91d1 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -149,7 +149,7 @@ static struct ref_entry *current_ref;\n \n static struct ref_array extra_refs;\n \n-static void free_ref_array(struct ref_array *array)\n+static void clear_ref_array(struct ref_array *array)\n {\n \tint i;\n \tfor (i = 0; i < array->nr; i++)\n@@ -162,14 +162,14 @@ static void free_ref_array(struct ref_array *array)\n static void clear_packed_ref_cache(struct ref_cache *refs)\n {\n \tif (refs->did_packed)\n-\t\tfree_ref_array(&refs->packed);\n+\t\tclear_ref_array(&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\tfree_ref_array(&refs->loose);\n+\t\tclear_ref_array(&refs->loose);\n \trefs->did_loose = 0;\n }\n \n@@ -256,7 +256,7 @@ void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n \n void clear_extra_refs(void)\n {\n-\tfree_ref_array(&extra_refs);\n+\tclear_ref_array(&extra_refs);\n }\n \n static struct ref_array *get_packed_refs(const char *submodule)\n-- \n1.7.8\n"},{"id":"180848","messageId":"1323668338-1764-6-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 05/51] is_refname_available(): remove the \"quiet\" argument","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:12Z","receivedAt":"2011-12-12T05:38:12Z","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\nquiet was always set to 0, so get rid of it.  Add a function docstring\nfor good measure.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   20 +++++++++++++-------\n 1 files changed, 13 insertions(+), 7 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 7be91d1..d7d422f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1057,8 +1057,15 @@ static int remove_empty_directories(const char *file)\n \treturn result;\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, int quiet)\n+\t\t\t\tstruct ref_array *array)\n {\n \tint i, namlen = strlen(refname); /* e.g. 'foo/bar' */\n \tfor (i = 0; i < array->nr; i++ ) {\n@@ -1070,9 +1077,8 @@ static int is_refname_available(const char *refname, const char *oldrefname,\n \t\t\tconst char *lead = (namlen < len) ? entry->name : refname;\n \t\t\tif (!strncmp(refname, entry->name, cmplen) &&\n \t\t\t    lead[cmplen] == '/') {\n-\t\t\t\tif (!quiet)\n-\t\t\t\t\terror(\"'%s' exists; cannot create '%s'\",\n-\t\t\t\t\t      entry->name, refname);\n+\t\t\t\terror(\"'%s' exists; cannot create '%s'\",\n+\t\t\t\t      entry->name, refname);\n \t\t\t\treturn 0;\n \t\t\t}\n \t\t}\n@@ -1213,7 +1219,7 @@ 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(NULL), 0)) {\n+\t     !is_refname_available(refname, NULL, get_packed_refs(NULL))) {\n \t\tlast_errno = ENOTDIR;\n \t\tgoto error_return;\n \t}\n@@ -1367,10 +1373,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(NULL), 0))\n+\tif (!is_refname_available(newrefname, oldrefname, get_packed_refs(NULL)))\n \t\treturn 1;\n \n-\tif (!is_refname_available(newrefname, oldrefname, get_loose_refs(NULL), 0))\n+\tif (!is_refname_available(newrefname, oldrefname, get_loose_refs(NULL)))\n \t\treturn 1;\n \n \tif (log && rename(git_path(\"logs/%s\", oldrefname), git_path(TMP_RENAMED_LOG)))\n-- \n1.7.8\n"},{"id":"180846","messageId":"1323668338-1764-7-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 06/51] parse_ref_line(): add docstring","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:13Z","receivedAt":"2011-12-12T05:38: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\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    5 +++++\n 1 files changed, 5 insertions(+), 0 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex d7d422f..1975792 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -20,6 +20,11 @@ struct ref_array {\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-- \n1.7.8\n"},{"id":"180852","messageId":"1323668338-1764-8-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 07/51] add_ref(): add docstring","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:14Z","receivedAt":"2011-12-12T05:38: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\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    1 +\n 1 files changed, 1 insertions(+), 0 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 1975792..2252c66 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -53,6 +53,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)\n \treturn line;\n }\n \n+/* Add a ref_entry to the end of the ref_array (unsorted). */\n static void add_ref(const char *refname, const unsigned char *sha1,\n \t\t    int flag, int check_name, struct ref_array *refs,\n \t\t    struct ref_entry **new_entry)\n-- \n1.7.8\n"},{"id":"180849","messageId":"1323668338-1764-9-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:15Z","receivedAt":"2011-12-12T05:38: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\nGiving the function a name makes the code easier to understand.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   37 +++++++++++++++++++++++++------------\n 1 files changed, 25 insertions(+), 12 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2252c66..ea058c1 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -84,9 +84,28 @@ static int ref_entry_cmp(const void *a, const void *b)\n \treturn strcmp(one->name, two->name);\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->sha1, ref2->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+\t\treturn 0;\n+\t}\n+}\n+\n static void sort_ref_array(struct ref_array *array)\n {\n-\tint i = 0, j = 1;\n+\tint i, j;\n \n \t/* Nothing to sort unless there are at least two entries */\n \tif (array->nr < 2)\n@@ -95,19 +114,13 @@ static void sort_ref_array(struct ref_array *array)\n \tqsort(array->refs, array->nr, sizeof(*array->refs), 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\tif (!strcmp(a->name, b->name)) {\n-\t\t\tif (hashcmp(a->sha1, b->sha1))\n-\t\t\t\tdie(\"Duplicated ref, and SHA1s don't match: %s\",\n-\t\t\t\t    a->name);\n-\t\t\twarning(\"Duplicated ref: %s\", a->name);\n-\t\t\tfree(b);\n+\ti = 0;\n+\tfor (j = 1; j < array->nr; j++) {\n+\t\tif (is_dup_ref(array->refs[i], array->refs[j])) {\n+\t\t\tfree(array->refs[j]);\n \t\t\tcontinue;\n \t\t}\n-\t\ti++;\n-\t\tarray->refs[i] = array->refs[j];\n+\t\tarray->refs[++i] = array->refs[j];\n \t}\n \tarray->nr = i + 1;\n }\n-- \n1.7.8\n"},{"id":"180850","messageId":"1323668338-1764-10-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 09/51] refs: change signatures of get_packed_refs() and get_loose_refs()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:16Z","receivedAt":"2011-12-12T05:38: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\nChange get_packed_refs() and get_loose_refs() to take a (struct\nref_cache *) instead of the name of the submodule.\n\nChange get_ref_dir() to take a submodule name (i.e., \"\" for the main\nmodule) rather than a submodule pointer (i.e., NULL for the main\nmodule) so that refs->name can be used as its argument.  (In a moment\nthis function will also be changed to take a (struct ref_cache *),\ntoo.)\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   36 +++++++++++++++++-------------------\n 1 files changed, 17 insertions(+), 19 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex ea058c1..c62c682 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -278,16 +278,14 @@ void clear_extra_refs(void)\n \tclear_ref_array(&extra_refs);\n }\n \n-static struct ref_array *get_packed_refs(const char *submodule)\n+static struct ref_array *get_packed_refs(struct ref_cache *refs)\n {\n-\tstruct ref_cache *refs = get_ref_cache(submodule);\n-\n \tif (!refs->did_packed) {\n \t\tconst char *packed_refs_file;\n \t\tFILE *f;\n \n-\t\tif (submodule)\n-\t\t\tpacked_refs_file = git_path_submodule(submodule, \"packed-refs\");\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@@ -306,7 +304,7 @@ static void get_ref_dir(const char *submodule, const char *base,\n \tDIR *dir;\n \tconst char *path;\n \n-\tif (submodule)\n+\tif (*submodule)\n \t\tpath = git_path_submodule(submodule, \"%s\", base);\n \telse\n \t\tpath = git_path(\"%s\", base);\n@@ -399,12 +397,10 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)\n \tfor_each_rawref(warn_if_dangling_symref, &data);\n }\n \n-static struct ref_array *get_loose_refs(const char *submodule)\n+static struct ref_array *get_loose_refs(struct ref_cache *refs)\n {\n-\tstruct ref_cache *refs = get_ref_cache(submodule);\n-\n \tif (!refs->did_loose) {\n-\t\tget_ref_dir(submodule, \"refs\", &refs->loose);\n+\t\tget_ref_dir(refs->name, \"refs\", &refs->loose);\n \t\tsort_ref_array(&refs->loose);\n \t\trefs->did_loose = 1;\n \t}\n@@ -431,7 +427,7 @@ static int resolve_gitlink_packed_ref(char *name, int pathlen,\n \tif (pathlen < 6 || memcmp(name + pathlen - 6, \"/.git/\", 6))\n \t\tdie(\"Oops\");\n \tname[pathlen - 6] = '\\0'; /* make it path to the submodule */\n-\tarray = get_packed_refs(name);\n+\tarray = get_packed_refs(get_ref_cache(name));\n \tref = search_ref_array(array, refname);\n \tif (ref != NULL) {\n \t\tmemcpy(sha1, ref->sha1, 20);\n@@ -511,7 +507,7 @@ 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(NULL);\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@@ -695,7 +691,7 @@ 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(NULL);\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 \n \t\tif (r != NULL && r->flag & REF_KNOWS_PEELED) {\n@@ -720,8 +716,9 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \t\t\t   int trim, int flags, void *cb_data)\n {\n \tint retval = 0, i, p = 0, l = 0;\n-\tstruct ref_array *packed = get_packed_refs(submodule);\n-\tstruct ref_array *loose = get_loose_refs(submodule);\n+\tstruct ref_cache *refs = get_ref_cache(submodule);\n+\tstruct ref_array *packed = get_packed_refs(refs);\n+\tstruct ref_array *loose = get_loose_refs(refs);\n \n \tstruct ref_array *extra = &extra_refs;\n \n@@ -1238,7 +1235,7 @@ 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(NULL))) {\n+\t     !is_refname_available(refname, NULL, get_packed_refs(get_ref_cache(NULL)))) {\n \t\tlast_errno = ENOTDIR;\n \t\tgoto error_return;\n \t}\n@@ -1298,7 +1295,7 @@ static int repack_without_ref(const char *refname)\n \tstruct ref_entry *ref;\n \tint fd, i;\n \n-\tpacked = get_packed_refs(NULL);\n+\tpacked = get_packed_refs(get_ref_cache(NULL));\n \tref = search_ref_array(packed, refname);\n \tif (ref == NULL)\n \t\treturn 0;\n@@ -1381,6 +1378,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms\n \tstruct stat loginfo;\n \tint log = !lstat(git_path(\"logs/%s\", oldrefname), &loginfo);\n \tconst char *symref = NULL;\n+\tstruct ref_cache *refs = get_ref_cache(NULL);\n \n \tif (log && S_ISLNK(loginfo.st_mode))\n \t\treturn error(\"reflog for %s is a symlink\", oldrefname);\n@@ -1392,10 +1390,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(NULL)))\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(NULL)))\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.8\n"},{"id":"180854","messageId":"1323668338-1764-11-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 10/51] get_ref_dir(): change signature","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:17Z","receivedAt":"2011-12-12T05:38: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\nChange get_ref_dir() to take a (struct ref_cache *) in place of the\nsubmodule name.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   18 +++++++++---------\n 1 files changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c62c682..91ec395 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -298,14 +298,14 @@ static struct ref_array *get_packed_refs(struct ref_cache *refs)\n \treturn &refs->packed;\n }\n \n-static void get_ref_dir(const char *submodule, const char *base,\n+static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\tstruct ref_array *array)\n {\n \tDIR *dir;\n \tconst char *path;\n \n-\tif (*submodule)\n-\t\tpath = git_path_submodule(submodule, \"%s\", base);\n+\tif (*refs->name)\n+\t\tpath = git_path_submodule(refs->name, \"%s\", base);\n \telse\n \t\tpath = git_path(\"%s\", base);\n \n@@ -336,19 +336,19 @@ static void get_ref_dir(const char *submodule, const char *base,\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\trefdir = submodule\n-\t\t\t\t? git_path_submodule(submodule, \"%s\", refname)\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(submodule, refname, array);\n+\t\t\t\tget_ref_dir(refs, refname, array);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tif (submodule) {\n+\t\t\tif (*refs->name) {\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag = 0;\n-\t\t\t\tif (resolve_gitlink_ref(submodule, refname, sha1) < 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@@ -400,7 +400,7 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)\n static struct ref_array *get_loose_refs(struct ref_cache *refs)\n {\n \tif (!refs->did_loose) {\n-\t\tget_ref_dir(refs->name, \"refs\", &refs->loose);\n+\t\tget_ref_dir(refs, \"refs\", &refs->loose);\n \t\tsort_ref_array(&refs->loose);\n \t\trefs->did_loose = 1;\n \t}\n-- \n1.7.8\n"},{"id":"180851","messageId":"1323668338-1764-12-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 11/51] resolve_gitlink_ref(): improve docstring","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:18Z","receivedAt":"2011-12-12T05:38: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\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.h |    8 ++++++--\n 1 files changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/refs.h b/refs.h\nindex 4c5d570..d498291 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -133,8 +133,12 @@ extern char *shorten_unambiguous_ref(const char *refname, int strict);\n /** rename ref, return 0 on success **/\n extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);\n \n-/** resolve ref in nested \"gitlink\" repository */\n-extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *sha1);\n+/**\n+ * Resolve refname in the nested \"gitlink\" repository that is located\n+ * at path.  If the resolution is successful, return 0 and set sha1 to\n+ * the name of the object; otherwise, return a non-zero value.\n+ */\n+extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);\n \n /** lock a ref and then write its file */\n enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };\n-- \n1.7.8\n"},{"id":"180853","messageId":"1323668338-1764-13-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 12/51] Pass a (ref_cache *) to the resolve_gitlink_*() helper functions","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:19Z","receivedAt":"2011-12-12T05:38: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\nAnd remove some redundant arguments from resolve_gitlink_packed_ref().\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   40 ++++++++++++++++++++--------------------\n 1 files changed, 20 insertions(+), 20 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 91ec395..bf1f164 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -413,30 +413,25 @@ static struct ref_array *get_loose_refs(struct ref_cache *refs)\n \n /*\n  * Called by resolve_gitlink_ref_recursive() after it failed to read\n- * from \"name\", which is \"module/.git/<refname>\". Find <refname> in\n- * the packed-refs file for the submodule.\n+ * from the loose refs in ref_cache refs. Find <refname> in the\n+ * packed-refs file for the submodule.\n  */\n-static int resolve_gitlink_packed_ref(char *name, int pathlen,\n+static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n \t\t\t\t      const char *refname, unsigned char *sha1)\n {\n-\tint retval = -1;\n \tstruct ref_entry *ref;\n-\tstruct ref_array *array;\n+\tstruct ref_array *array = get_packed_refs(refs);\n \n-\t/* being defensive: resolve_gitlink_ref() did this for us */\n-\tif (pathlen < 6 || memcmp(name + pathlen - 6, \"/.git/\", 6))\n-\t\tdie(\"Oops\");\n-\tname[pathlen - 6] = '\\0'; /* make it path to the submodule */\n-\tarray = get_packed_refs(get_ref_cache(name));\n \tref = search_ref_array(array, refname);\n-\tif (ref != NULL) {\n-\t\tmemcpy(sha1, ref->sha1, 20);\n-\t\tretval = 0;\n-\t}\n-\treturn retval;\n+\tif (ref == NULL)\n+\t\treturn -1;\n+\n+\tmemcpy(sha1, ref->sha1, 20);\n+\treturn 0;\n }\n \n-static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n+static int resolve_gitlink_ref_recursive(struct ref_cache *refs,\n+\t\t\t\t\t char *name, int pathlen,\n \t\t\t\t\t const char *refname, unsigned char *sha1,\n \t\t\t\t\t int recursion)\n {\n@@ -448,7 +443,7 @@ static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n \tmemcpy(name + pathlen, refname, len+1);\n \tfd = open(name, O_RDONLY);\n \tif (fd < 0)\n-\t\treturn resolve_gitlink_packed_ref(name, pathlen, refname, sha1);\n+\t\treturn resolve_gitlink_packed_ref(refs, refname, sha1);\n \n \tlen = read(fd, buffer, sizeof(buffer)-1);\n \tclose(fd);\n@@ -469,19 +464,24 @@ static int resolve_gitlink_ref_recursive(char *name, int pathlen,\n \twhile (isspace(*p))\n \t\tp++;\n \n-\treturn resolve_gitlink_ref_recursive(name, pathlen, p, sha1, recursion+1);\n+\treturn resolve_gitlink_ref_recursive(refs, name, pathlen, p, sha1, recursion+1);\n }\n \n int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)\n {\n \tint len = strlen(path), retval;\n-\tchar *gitdir;\n+\tchar *submodule, *gitdir;\n+\tstruct ref_cache *refs;\n \tconst char *tmp;\n \n \twhile (len && path[len-1] == '/')\n \t\tlen--;\n \tif (!len)\n \t\treturn -1;\n+\tsubmodule = xstrndup(path, len);\n+\trefs = get_ref_cache(submodule);\n+\tfree(submodule);\n+\n \tgitdir = xmalloc(len + MAXREFLEN + 8);\n \tmemcpy(gitdir, path, len);\n \tmemcpy(gitdir + len, \"/.git\", 6);\n@@ -496,7 +496,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh\n \t}\n \tgitdir[len] = '/';\n \tgitdir[++len] = '\\0';\n-\tretval = resolve_gitlink_ref_recursive(gitdir, len, refname, sha1, 0);\n+\tretval = resolve_gitlink_ref_recursive(refs, gitdir, len, refname, sha1, 0);\n \tfree(gitdir);\n \treturn retval;\n }\n-- \n1.7.8\n"},{"id":"180862","messageId":"1323668338-1764-14-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 13/51] resolve_gitlink_ref_recursive(): change to work with struct ref_cache","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:20Z","receivedAt":"2011-12-12T05:38: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\nresolve_gitlink_ref() and resolve_gitlink_ref_recursive(), together,\nbasically duplicated the code in git_path_submodule().  So use that\nfunction instead.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   34 ++++++++++------------------------\n 1 files changed, 10 insertions(+), 24 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex bf1f164..ba7a8b0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -431,17 +431,19 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs,\n }\n \n static int resolve_gitlink_ref_recursive(struct ref_cache *refs,\n-\t\t\t\t\t char *name, int pathlen,\n \t\t\t\t\t const char *refname, unsigned char *sha1,\n \t\t\t\t\t int recursion)\n {\n-\tint fd, len = strlen(refname);\n+\tint fd, len;\n \tchar buffer[128], *p;\n+\tchar *path;\n \n-\tif (recursion > MAXDEPTH || len > MAXREFLEN)\n+\tif (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)\n \t\treturn -1;\n-\tmemcpy(name + pathlen, refname, len+1);\n-\tfd = open(name, O_RDONLY);\n+\tpath = *refs->name\n+\t\t? git_path_submodule(refs->name, \"%s\", refname)\n+\t\t: git_path(\"%s\", refname);\n+\tfd = open(path, O_RDONLY);\n \tif (fd < 0)\n \t\treturn resolve_gitlink_packed_ref(refs, refname, sha1);\n \n@@ -464,15 +466,14 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,\n \twhile (isspace(*p))\n \t\tp++;\n \n-\treturn resolve_gitlink_ref_recursive(refs, name, pathlen, p, sha1, recursion+1);\n+\treturn resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);\n }\n \n int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)\n {\n \tint len = strlen(path), retval;\n-\tchar *submodule, *gitdir;\n+\tchar *submodule;\n \tstruct ref_cache *refs;\n-\tconst char *tmp;\n \n \twhile (len && path[len-1] == '/')\n \t\tlen--;\n@@ -482,22 +483,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh\n \trefs = get_ref_cache(submodule);\n \tfree(submodule);\n \n-\tgitdir = xmalloc(len + MAXREFLEN + 8);\n-\tmemcpy(gitdir, path, len);\n-\tmemcpy(gitdir + len, \"/.git\", 6);\n-\tlen += 5;\n-\n-\ttmp = read_gitfile(gitdir);\n-\tif (tmp) {\n-\t\tfree(gitdir);\n-\t\tlen = strlen(tmp);\n-\t\tgitdir = xmalloc(len + MAXREFLEN + 3);\n-\t\tmemcpy(gitdir, tmp, len);\n-\t}\n-\tgitdir[len] = '/';\n-\tgitdir[++len] = '\\0';\n-\tretval = resolve_gitlink_ref_recursive(refs, gitdir, len, refname, sha1, 0);\n-\tfree(gitdir);\n+\tretval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);\n \treturn retval;\n }\n \n-- \n1.7.8\n"},{"id":"180893","messageId":"1323668338-1764-15-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 14/51] repack_without_ref(): remove temporary","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:21Z","receivedAt":"2011-12-12T05:38: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\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    7 ++-----\n 1 files changed, 2 insertions(+), 5 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex ba7a8b0..2e7bc0c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1278,12 +1278,10 @@ static struct lock_file packlock;\n static int repack_without_ref(const char *refname)\n {\n \tstruct ref_array *packed;\n-\tstruct ref_entry *ref;\n \tint fd, i;\n \n \tpacked = get_packed_refs(get_ref_cache(NULL));\n-\tref = search_ref_array(packed, refname);\n-\tif (ref == NULL)\n+\tif (search_ref_array(packed, refname) == NULL)\n \t\treturn 0;\n \tfd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n \tif (fd < 0) {\n@@ -1294,8 +1292,7 @@ static int repack_without_ref(const char *refname)\n \tfor (i = 0; i < packed->nr; i++) {\n \t\tchar line[PATH_MAX + 100];\n \t\tint len;\n-\n-\t\tref = packed->refs[i];\n+\t\tstruct ref_entry *ref = packed->refs[i];\n \n \t\tif (!strcmp(refname, ref->name))\n \t\t\tcontinue;\n-- \n1.7.8\n"},{"id":"180856","messageId":"1323668338-1764-16-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 15/51] create_ref_entry(): extract function from add_ref()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:22Z","receivedAt":"2011-12-12T05:38: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\nSeparate the creation of the ref_entry from its addition to a ref_array.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   37 ++++++++++++++++++++++---------------\n 1 files changed, 22 insertions(+), 15 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2e7bc0c..442b87c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -53,28 +53,35 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)\n \treturn line;\n }\n \n-/* Add a ref_entry to the end of the ref_array (unsorted). */\n-static void add_ref(const char *refname, const unsigned char *sha1,\n-\t\t    int flag, int check_name, struct ref_array *refs,\n-\t\t    struct ref_entry **new_entry)\n+static struct ref_entry *create_ref_entry(const char *refname,\n+\t\t\t\t\t  const unsigned char *sha1, int flag,\n+\t\t\t\t\t  int check_name)\n {\n \tint len;\n-\tstruct ref_entry *entry;\n+\tstruct ref_entry *ref;\n \n-\t/* Allocate it and add it in.. */\n-\tlen = strlen(refname) + 1;\n-\tentry = xmalloc(sizeof(struct ref_entry) + len);\n-\thashcpy(entry->sha1, sha1);\n-\thashclr(entry->peeled);\n \tif (check_name &&\n \t    check_refname_format(refname, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))\n \t\tdie(\"Reference has invalid format: '%s'\", refname);\n-\tmemcpy(entry->name, refname, len);\n-\tentry->flag = flag;\n-\tif (new_entry)\n-\t\t*new_entry = entry;\n+\tlen = strlen(refname) + 1;\n+\tref = xmalloc(sizeof(struct ref_entry) + len);\n+\thashcpy(ref->sha1, sha1);\n+\thashclr(ref->peeled);\n+\tmemcpy(ref->name, refname, len);\n+\tref->flag = flag;\n+\treturn ref;\n+}\n+\n+/* Add a ref_entry to the end of the ref_array (unsorted). */\n+static void add_ref(const char *refname, const unsigned char *sha1,\n+\t\t    int flag, int check_name, struct ref_array *refs,\n+\t\t    struct ref_entry **new_ref)\n+{\n+\tstruct ref_entry *ref = create_ref_entry(refname, sha1, flag, check_name);\n+\tif (new_ref)\n+\t\t*new_ref = ref;\n \tALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc);\n-\trefs->refs[refs->nr++] = entry;\n+\trefs->refs[refs->nr++] = ref;\n }\n \n static int ref_entry_cmp(const void *a, const void *b)\n-- \n1.7.8\n"},{"id":"180858","messageId":"1323668338-1764-17-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 16/51] add_ref(): take a (struct ref_entry *) parameter","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:23Z","receivedAt":"2011-12-12T05:38: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\nTake a pointer to the ref_entry to add to the array, rather than\ncreating the ref_entry within the function.  This opens the way to\nhaving multiple kinds of ref_entries.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   14 +++++---------\n 1 files changed, 5 insertions(+), 9 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 442b87c..579e4c3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -73,13 +73,8 @@ static struct ref_entry *create_ref_entry(const char *refname,\n }\n \n /* Add a ref_entry to the end of the ref_array (unsorted). */\n-static void add_ref(const char *refname, const unsigned char *sha1,\n-\t\t    int flag, int check_name, struct ref_array *refs,\n-\t\t    struct ref_entry **new_ref)\n+static void add_ref(struct ref_array *refs, struct ref_entry *ref)\n {\n-\tstruct ref_entry *ref = create_ref_entry(refname, sha1, flag, check_name);\n-\tif (new_ref)\n-\t\t*new_ref = ref;\n \tALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc);\n \trefs->refs[refs->nr++] = ref;\n }\n@@ -262,7 +257,8 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \n \t\trefname = parse_ref_line(refline, sha1);\n \t\tif (refname) {\n-\t\t\tadd_ref(refname, sha1, flag, 1, array, &last);\n+\t\t\tlast = create_ref_entry(refname, sha1, flag, 1);\n+\t\t\tadd_ref(array, last);\n \t\t\tcontinue;\n \t\t}\n \t\tif (last &&\n@@ -277,7 +273,7 @@ static void read_packed_refs(FILE *f, struct ref_array *array)\n \n void add_extra_ref(const char *refname, const unsigned char *sha1, int flag)\n {\n-\tadd_ref(refname, sha1, flag, 0, &extra_refs, NULL);\n+\tadd_ref(&extra_refs, create_ref_entry(refname, sha1, flag, 0));\n }\n \n void clear_extra_refs(void)\n@@ -363,7 +359,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t}\n-\t\t\tadd_ref(refname, sha1, flag, 1, array, NULL);\n+\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag, 1));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(dir);\n-- \n1.7.8\n"},{"id":"180855","messageId":"1323668338-1764-18-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 17/51] do_for_each_ref(): correctly terminate while processesing extra_refs","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:24Z","receivedAt":"2011-12-12T05:38: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\nIf the user-supplied function returns a nonzero value while processing\nextra_refs, terminate without processing the rest of the list.\n\nThis probably has no practical importance, but makes the handling of\nextra_refs a little bit more consistent with the handling of other\nrefs.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |    5 ++++-\n 1 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 579e4c3..fb6fe84 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -711,8 +711,11 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \n \tstruct ref_array *extra = &extra_refs;\n \n-\tfor (i = 0; i < extra->nr; i++)\n+\tfor (i = 0; i < extra->nr; i++) {\n \t\tretval = do_one_ref(base, fn, trim, flags, cb_data, extra->refs[i]);\n+\t\tif (retval)\n+\t\t\tgoto end_each;\n+\t}\n \n \twhile (p < packed->nr && l < loose->nr) {\n \t\tstruct ref_entry *entry;\n-- \n1.7.8\n"},{"id":"180861","messageId":"1323668338-1764-19-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 18/51] do_for_each_ref_in_array(): new function","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:25Z","receivedAt":"2011-12-12T05:38: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\nExtract function do_for_each_ref_in_array() from do_for_each_ref().\nThe new function will be a useful building block for storing refs\nhierarchically.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   39 +++++++++++++++++++++++----------------\n 1 files changed, 23 insertions(+), 16 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex fb6fe84..bc14437 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -701,21 +701,31 @@ 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+{\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 static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,\n \t\t\t   int trim, int flags, void *cb_data)\n {\n-\tint retval = 0, i, p = 0, l = 0;\n+\tint retval = 0, p = 0, l = 0;\n \tstruct ref_cache *refs = get_ref_cache(submodule);\n \tstruct ref_array *packed = get_packed_refs(refs);\n \tstruct ref_array *loose = get_loose_refs(refs);\n \n-\tstruct ref_array *extra = &extra_refs;\n-\n-\tfor (i = 0; i < extra->nr; i++) {\n-\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, extra->refs[i]);\n-\t\tif (retval)\n-\t\t\tgoto end_each;\n-\t}\n+\tretval = do_for_each_ref_in_array(&extra_refs, 0,\n+\t\t\t\t\t  base, fn, trim, flags, cb_data);\n+\tif (retval)\n+\t\tgoto end_each;\n \n \twhile (p < packed->nr && l < loose->nr) {\n \t\tstruct ref_entry *entry;\n@@ -735,14 +745,11 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n \t}\n \n \tif (l < loose->nr) {\n-\t\tp = l;\n-\t\tpacked = loose;\n-\t}\n-\n-\tfor (; p < packed->nr; p++) {\n-\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, packed->refs[p]);\n-\t\tif (retval)\n-\t\t\tgoto end_each;\n+\t\tretval = do_for_each_ref_in_array(loose, l,\n+\t\t\t\t\t\t  base, fn, trim, flags, cb_data);\n+\t} else {\n+\t\tretval = do_for_each_ref_in_array(packed, p,\n+\t\t\t\t\t\t  base, fn, trim, flags, cb_data);\n \t}\n \n end_each:\n-- \n1.7.8\n"},{"id":"180890","messageId":"1323668338-1764-20-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 19/51] do_for_each_ref_in_arrays(): new function","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:26Z","receivedAt":"2011-12-12T05:38: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\nExtract function do_for_each_ref_in_arrays() from do_for_each_ref().\nThe new function will be a useful building block for storing refs\nhierarchically.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   71 +++++++++++++++++++++++++++++++++++++--------------------------\n 1 files changed, 42 insertions(+), 29 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex bc14437..601665b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -714,45 +714,58 @@ static int do_for_each_ref_in_array(struct ref_array *array, int offset,\n \treturn 0;\n }\n \n-static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,\n-\t\t\t   int trim, int flags, void *cb_data)\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 = 0, p = 0, l = 0;\n-\tstruct ref_cache *refs = get_ref_cache(submodule);\n-\tstruct ref_array *packed = get_packed_refs(refs);\n-\tstruct ref_array *loose = get_loose_refs(refs);\n+\tint retval;\n+\tint i1 = 0, i2 = 0;\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-\tif (retval)\n-\t\tgoto end_each;\n-\n-\twhile (p < packed->nr && l < loose->nr) {\n-\t\tstruct ref_entry *entry;\n-\t\tint cmp = strcmp(packed->refs[p]->name, loose->refs[l]->name);\n-\t\tif (!cmp) {\n-\t\t\tp++;\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\tentry = loose->refs[l++];\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\tentry = packed->refs[p++];\n+\t\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, e2);\n+\t\t\ti2++;\n \t\t}\n-\t\tretval = do_one_ref(base, fn, trim, flags, cb_data, entry);\n \t\tif (retval)\n-\t\t\tgoto end_each;\n+\t\t\treturn retval;\n \t}\n+}\n \n-\tif (l < loose->nr) {\n-\t\tretval = do_for_each_ref_in_array(loose, l,\n-\t\t\t\t\t\t  base, fn, trim, flags, cb_data);\n-\t} else {\n-\t\tretval = do_for_each_ref_in_array(packed, p,\n-\t\t\t\t\t\t  base, fn, trim, flags, cb_data);\n-\t}\n+static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,\n+\t\t\t   int trim, int flags, void *cb_data)\n+{\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+\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 \n-end_each:\n \tcurrent_ref = NULL;\n \treturn retval;\n }\n-- \n1.7.8\n"},{"id":"180883","messageId":"1323668338-1764-21-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 20/51] repack_without_ref(): reimplement using do_for_each_ref_in_array()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:27Z","receivedAt":"2011-12-12T05:38: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\nIt costs a bit of boilerplate, but it means that the function can be\nignorant of how cached refs are stored.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   46 ++++++++++++++++++++++++++++------------------\n 1 files changed, 28 insertions(+), 18 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 601665b..2cdedf8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1299,36 +1299,46 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,\n \treturn lock_ref_sha1_basic(refname, old_sha1, flags, NULL);\n }\n \n+struct repack_without_ref_sb {\n+\tconst char *refname;\n+\tint fd;\n+};\n+\n+static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,\n+\t\t\t\t int flags, void *cb_data)\n+{\n+\tstruct repack_without_ref_sb *data = cb_data;\n+\tchar line[PATH_MAX + 100];\n+\tint len;\n+\n+\tif (!strcmp(data->refname, refname))\n+\t\treturn 0;\n+\tlen = snprintf(line, sizeof(line), \"%s %s\\n\",\n+\t\t       sha1_to_hex(sha1), refname);\n+\t/* this should not happen but just being defensive */\n+\tif (len > sizeof(line))\n+\t\tdie(\"too long a refname '%s'\", refname);\n+\twrite_or_die(data->fd, line, len);\n+\treturn 0;\n+}\n+\n static struct lock_file packlock;\n \n static int repack_without_ref(const char *refname)\n {\n+\tstruct repack_without_ref_sb data;\n \tstruct ref_array *packed;\n-\tint fd, i;\n \n \tpacked = get_packed_refs(get_ref_cache(NULL));\n \tif (search_ref_array(packed, refname) == NULL)\n \t\treturn 0;\n-\tfd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n-\tif (fd < 0) {\n+\tdata.refname = refname;\n+\tdata.fd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n+\tif (data.fd < 0) {\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-\n-\tfor (i = 0; i < packed->nr; i++) {\n-\t\tchar line[PATH_MAX + 100];\n-\t\tint len;\n-\t\tstruct ref_entry *ref = packed->refs[i];\n-\n-\t\tif (!strcmp(refname, ref->name))\n-\t\t\tcontinue;\n-\t\tlen = snprintf(line, sizeof(line), \"%s %s\\n\",\n-\t\t\t       sha1_to_hex(ref->sha1), ref->name);\n-\t\t/* this should not happen but just being defensive */\n-\t\tif (len > sizeof(line))\n-\t\t\tdie(\"too long a refname '%s'\", ref->name);\n-\t\twrite_or_die(fd, line, len);\n-\t}\n+\tdo_for_each_ref_in_array(packed, 0, \"\", repack_without_ref_fn, 0, 0, &data);\n \treturn commit_lock_file(&packlock);\n }\n \n-- \n1.7.8\n"},{"id":"180860","messageId":"1323668338-1764-22-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 21/51] names_conflict(): new function, extracted from is_refname_available()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:28Z","receivedAt":"2011-12-12T05:38: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\nThis costs an extra strlen() in the loop, but even that small price\nwill be clawed back in the next patch.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   43 +++++++++++++++++++++++++++++++------------\n 1 files changed, 31 insertions(+), 12 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2cdedf8..c33e94a 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1086,6 +1086,30 @@ 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+\tint len1 = strlen(refname1);\n+\tint len2 = strlen(refname2);\n+\tint cmplen;\n+\tconst char *lead;\n+\n+\tif (len1 < len2) {\n+\t\tcmplen = len1;\n+\t\tlead = refname2;\n+\t} else {\n+\t\tcmplen = len2;\n+\t\tlead = refname1;\n+\t}\n+\treturn !strncmp(refname1, refname2, cmplen) && lead[cmplen] == '/';\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@@ -1095,20 +1119,15 @@ static int remove_empty_directories(const char *file)\n static int is_refname_available(const char *refname, const char *oldrefname,\n \t\t\t\tstruct ref_array *array)\n {\n-\tint i, namlen = strlen(refname); /* e.g. 'foo/bar' */\n+\tint i;\n \tfor (i = 0; i < array->nr; i++ ) {\n \t\tstruct ref_entry *entry = array->refs[i];\n-\t\t/* entry->name could be 'foo' or 'foo/bar/baz' */\n-\t\tif (!oldrefname || strcmp(oldrefname, entry->name)) {\n-\t\t\tint len = strlen(entry->name);\n-\t\t\tint cmplen = (namlen < len) ? namlen : len;\n-\t\t\tconst char *lead = (namlen < len) ? entry->name : refname;\n-\t\t\tif (!strncmp(refname, entry->name, cmplen) &&\n-\t\t\t    lead[cmplen] == '/') {\n-\t\t\t\terror(\"'%s' exists; cannot create '%s'\",\n-\t\t\t\t      entry->name, refname);\n-\t\t\t\treturn 0;\n-\t\t\t}\n+\t\tif (oldrefname && !strcmp(oldrefname, entry->name))\n+\t\t\tcontinue;\n+\t\tif (names_conflict(refname, entry->name)) {\n+\t\t\terror(\"'%s' exists; cannot create '%s'\",\n+\t\t\t      entry->name, refname);\n+\t\t\treturn 0;\n \t\t}\n \t}\n \treturn 1;\n-- \n1.7.8\n"},{"id":"180891","messageId":"1323668338-1764-23-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 22/51] names_conflict(): simplify implementation","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:29Z","receivedAt":"2011-12-12T05:38: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\nSave a bunch of lines of code and a couple of strlen() calls.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   17 ++++-------------\n 1 files changed, 4 insertions(+), 13 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex c33e94a..12a70c1 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1094,19 +1094,10 @@ static int remove_empty_directories(const char *file)\n  */\n static int names_conflict(const char *refname1, const char *refname2)\n {\n-\tint len1 = strlen(refname1);\n-\tint len2 = strlen(refname2);\n-\tint cmplen;\n-\tconst char *lead;\n-\n-\tif (len1 < len2) {\n-\t\tcmplen = len1;\n-\t\tlead = refname2;\n-\t} else {\n-\t\tcmplen = len2;\n-\t\tlead = refname1;\n-\t}\n-\treturn !strncmp(refname1, refname2, cmplen) && lead[cmplen] == '/';\n+\tfor (; *refname1 && *refname1 == *refname2; refname1++, refname2++)\n+\t\t;\n+\treturn (*refname1 == '\\0' && *refname2 == '/')\n+\t\t|| (*refname1 == '/' && *refname2 == '\\0');\n }\n \n /*\n-- \n1.7.8\n"},{"id":"180857","messageId":"1323668338-1764-24-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 23/51] is_refname_available(): reimplement using do_for_each_ref_in_array()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:30Z","receivedAt":"2011-12-12T05:38: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\nThis implementation will survive upcoming changes to the ref_array\ndata structure.\n\nSigned-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n---\n refs.c |   40 ++++++++++++++++++++++++++++++----------\n 1 files changed, 30 insertions(+), 10 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 12a70c1..064a70f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1100,6 +1100,25 @@ static int names_conflict(const char *refname1, const char *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@@ -1110,16 +1129,17 @@ static int names_conflict(const char *refname1, const char *refname2)\n static int is_refname_available(const char *refname, const char *oldrefname,\n \t\t\t\tstruct ref_array *array)\n {\n-\tint i;\n-\tfor (i = 0; i < array->nr; i++ ) {\n-\t\tstruct ref_entry *entry = array->refs[i];\n-\t\tif (oldrefname && !strcmp(oldrefname, entry->name))\n-\t\t\tcontinue;\n-\t\tif (names_conflict(refname, entry->name)) {\n-\t\t\terror(\"'%s' exists; cannot create '%s'\",\n-\t\t\t      entry->name, refname);\n-\t\t\treturn 0;\n-\t\t}\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-- \n1.7.8\n"},{"id":"180865","messageId":"1323668338-1764-25-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 24/51] refs.c: reorder definitions more logically","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:31Z","receivedAt":"2011-12-12T05:38: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\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 |  655 ++++++++++++++++++++++++++++++++--------------------------------\n 1 files changed, 329 insertions(+), 326 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 064a70f..3cc3e4f 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,44 +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-\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 \t\t\t\t\t  int check_name)\n@@ -79,6 +143,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@@ -86,6 +160,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@@ -127,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@@ -166,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@@ -236,6 +431,39 @@ 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+\treturn line;\n+}\n+\n static void read_packed_refs(FILE *f, struct ref_array *array)\n {\n \tstruct ref_entry *last = NULL;\n@@ -357,47 +585,13 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\t}\n \t\t\t} else if (read_ref_full(refname, sha1, 1, &flag)) {\n \t\t\t\thashclr(sha1);\n-\t\t\t\tflag |= REF_ISBROKEN;\n-\t\t\t}\n-\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag, 1));\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\tflag |= REF_ISBROKEN;\n+\t\t\t}\n+\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag, 1));\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@@ -632,23 +826,10 @@ int read_ref(const char *refname, unsigned char *sha1)\n \treturn read_ref_full(refname, sha1, 1, NULL);\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+int ref_exists(const char *refname)\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+\tunsigned char sha1[20];\n+\treturn !!resolve_ref(refname, sha1, 1, NULL);\n }\n \n static int filter_refs(const char *refname, const unsigned char *sha1, int flags,\n@@ -701,56 +882,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@@ -921,101 +1084,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@@ -1086,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@@ -2057,12 +2066,6 @@ int update_ref(const char *action, const char *refname,\n \treturn 0;\n }\n \n-int ref_exists(const char *refname)\n-{\n-\tunsigned char sha1[20];\n-\treturn !!resolve_ref(refname, sha1, 1, NULL);\n-}\n-\n struct ref *find_ref_by_name(const struct ref *list, const char *name)\n {\n \tfor ( ; list; list = list->next)\n-- \n1.7.8\n"},{"id":"180859","messageId":"1323668338-1764-26-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 25/51] free_ref_entry(): new function","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:32Z","receivedAt":"2011-12-12T05:38: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\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 3cc3e4f..99667fc 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -136,6 +136,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@@ -147,7 +152,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@@ -218,7 +223,7 @@ static void sort_ref_array(struct ref_array *array)\n \ti = 0;\n \tfor (j = 1; j < array->nr; j++) {\n \t\tif (is_dup_ref(array->refs[i], array->refs[j])) {\n-\t\t\tfree(array->refs[j]);\n+\t\t\tfree_ref_entry(array->refs[j]);\n \t\t\tcontinue;\n \t\t}\n \t\tarray->refs[++i] = array->refs[j];\n-- \n1.7.8\n"},{"id":"180863","messageId":"1323668338-1764-27-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 26/51] check_refname_component(): return 0 for zero-length components","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:33Z","receivedAt":"2011-12-12T05:38: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\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 99667fc..e29f76c 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.8\n"},{"id":"180864","messageId":"1323668338-1764-28-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 27/51] struct ref_entry: nest the value part in a union","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:34Z","receivedAt":"2011-12-12T05:38: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\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 e29f76c..fe6d657 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@@ -129,8 +135,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@@ -199,7 +205,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@@ -499,7 +505,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@@ -628,7 +634,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@@ -698,7 +704,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@@ -855,10 +861,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@@ -870,7 +876,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.8\n"},{"id":"180866","messageId":"1323668338-1764-29-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:35Z","receivedAt":"2011-12-12T05:38: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\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 |  166 ++++++++++++++++++++++++++++++++--------------------------------\n 1 files changed, 83 insertions(+), 83 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex fe6d657..b74ef80 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@@ -147,21 +147,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@@ -171,7 +171,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@@ -179,14 +179,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@@ -215,26 +215,26 @@ 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, j;\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 \ti = 0;\n-\tfor (j = 1; j < array->nr; j++) {\n-\t\tif (is_dup_ref(array->refs[i], array->refs[j])) {\n-\t\t\tfree_ref_entry(array->refs[j]);\n+\tfor (j = 1; j < dir->nr; j++) {\n+\t\tif (is_dup_ref(dir->entries[i], dir->entries[j])) {\n+\t\t\tfree_ref_entry(dir->entries[j]);\n \t\t\tcontinue;\n \t\t}\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@@ -475,7 +475,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@@ -497,7 +497,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, 1);\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@@ -507,7 +507,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@@ -517,10 +517,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@@ -541,9 +541,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@@ -552,9 +552,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@@ -563,7 +563,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@@ -584,7 +584,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@@ -598,18 +598,18 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t}\n-\t\t\tadd_ref(array, create_ref_entry(refname, sha1, flag, 1));\n+\t\t\tadd_ref(dir, create_ref_entry(refname, sha1, flag, 1));\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@@ -628,9 +628,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@@ -701,8 +701,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@@ -872,8 +872,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@@ -933,12 +933,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@@ -1999,10 +1999,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@@ -2011,7 +2011,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@@ -2038,7 +2038,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.8\n"},{"id":"180876","messageId":"1323668338-1764-30-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 29/51] refs: store references hierarchically","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:36Z","receivedAt":"2011-12-12T05:38: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\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.\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 |  265 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----------\n 1 files changed, 220 insertions(+), 45 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex b74ef80..ccd2806 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -111,15 +111,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@@ -142,18 +181,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@@ -164,6 +214,21 @@ 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/\") or \"\" for the top-level directory.\n+ */\n+static struct ref_entry *create_dir_entry(const char *dirname)\n+{\n+\tstruct ref_entry *direntry;\n+\tint len = strlen(dirname);\n+\tdirentry = xcalloc(1, sizeof(struct ref_entry) + len + 1);\n+\tmemcpy(direntry->name, dirname, len + 1);\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@@ -171,16 +236,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@@ -197,44 +272,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, 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_array */\n-\ti = 0;\n-\tfor (j = 1; j < dir->nr; j++) {\n-\t\tif (is_dup_ref(dir->entries[i], dir->entries[j])) {\n-\t\t\tfree_ref_entry(dir->entries[j]);\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\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 +412,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 +435,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 +449,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@@ -630,7 +805,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@@ -702,7 +877,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@@ -873,7 +1048,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 +1555,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.8\n"},{"id":"180892","messageId":"1323668338-1764-31-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 30/51] sort_ref_dir(): do not sort if already sorted","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:37Z","receivedAt":"2011-12-12T05:38: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\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 ccd2806..ce141ea 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -108,6 +108,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@@ -210,7 +214,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@@ -252,8 +256,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@@ -358,13 +363,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@@ -381,7 +400,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.8\n"},{"id":"180875","messageId":"1323668338-1764-32-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 31/51] refs: sort ref_dirs lazily","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:38Z","receivedAt":"2011-12-12T05:38: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\nSort ref_dirs lazily, when the ordering is needed: for searching via\nsearch_ref_dir(), and when iterating over the references 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 ce141ea..f01da78 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -256,9 +256,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@@ -364,26 +369,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@@ -393,7 +388,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@@ -430,6 +424,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@@ -453,6 +448,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@@ -701,7 +698,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@@ -803,7 +799,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.8\n"},{"id":"180889","messageId":"1323668338-1764-33-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 32/51] do_for_each_ref(): only iterate over the subtree that was requested","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:39Z","receivedAt":"2011-12-12T05:38: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\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 |   33 +++++++++++++++++++++++++++------\n 1 files changed, 27 insertions(+), 6 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex f01da78..6a11235 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1121,13 +1121,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-\n-\tretval = do_for_each_ref_in_dir(&extra_refs, 0,\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+\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.8\n"},{"id":"180884","messageId":"1323668338-1764-34-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 33/51] get_ref_dir(): keep track of the current ref_dir","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:40Z","receivedAt":"2011-12-12T05:38: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\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 6a11235..6912db3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -730,29 +730,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@@ -767,14 +774,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@@ -788,7 +797,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *base,\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t}\n-\t\t\tadd_ref(dir, create_ref_entry(refname, sha1, flag, 1));\n+\t\t\tadd_entry_to_dir(dir, create_ref_entry(refname, sha1, flag, 1));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(d);\n@@ -798,7 +807,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.8\n"},{"id":"180867","messageId":"1323668338-1764-35-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 34/51] refs: wrap top-level ref_dirs in ref_entries","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:41Z","receivedAt":"2011-12-12T05:38: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\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 |   52 +++++++++++++++++++++++++++++-----------------------\n 1 files changed, 29 insertions(+), 23 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6912db3..b4019e6 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -567,28 +567,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@@ -702,32 +702,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, 0));\n+\tif (!extra_refs)\n+\t\textra_refs = create_dir_entry(\"\");\n+\tadd_ref(&extra_refs->u.subdir, create_ref_entry(refname, sha1, flag, 0));\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@@ -744,7 +749,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@@ -806,11 +811,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@@ -1130,12 +1135,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.8\n"},{"id":"180873","messageId":"1323668338-1764-36-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 35/51] get_packed_refs(): return (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:42Z","receivedAt":"2011-12-12T05:38:42Z","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\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 b4019e6..6a65a21 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -715,7 +715,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@@ -732,7 +732,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@@ -831,9 +831,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@@ -904,8 +904,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@@ -1075,8 +1075,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@@ -1136,7 +1136,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@@ -1522,7 +1523,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@@ -1602,10 +1604,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@@ -1613,7 +1613,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@@ -1684,7 +1685,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.8\n"},{"id":"180872","messageId":"1323668338-1764-37-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 36/51] get_loose_refs(): return (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:43Z","receivedAt":"2011-12-12T05:38:43Z","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\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 6a65a21..918b787 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -809,13 +809,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@@ -1138,7 +1138,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@@ -1688,7 +1689,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.8\n"},{"id":"180869","messageId":"1323668338-1764-38-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 37/51] is_refname_available(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:44Z","receivedAt":"2011-12-12T05:38:44Z","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\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 918b787..6b7d374 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -544,14 +544,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@@ -1525,7 +1527,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@@ -1686,10 +1688,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.8\n"},{"id":"180868","messageId":"1323668338-1764-39-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 38/51] find_ref(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:45Z","receivedAt":"2011-12-12T05:38:45Z","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\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 6b7d374..df7416f 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -321,10 +321,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@@ -835,7 +837,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@@ -907,7 +909,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@@ -1078,7 +1080,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@@ -1608,7 +1610,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.8\n"},{"id":"180870","messageId":"1323668338-1764-40-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 39/51] read_packed_refs(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:46Z","receivedAt":"2011-12-12T05:38:46Z","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\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 df7416f..0d8fdf0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -670,12 +670,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@@ -692,7 +693,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, 1);\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@@ -732,7 +733,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.8\n"},{"id":"180871","messageId":"1323668338-1764-41-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 40/51] add_ref(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:47Z","receivedAt":"2011-12-12T05:38:47Z","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\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 0d8fdf0..439545b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -338,9 +338,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@@ -693,7 +695,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, 1);\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@@ -709,7 +711,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, 0));\n+\tadd_ref(extra_refs, create_ref_entry(refname, sha1, flag, 0));\n }\n \n void clear_extra_refs(void)\n-- \n1.7.8\n"},{"id":"180874","messageId":"1323668338-1764-42-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 41/51] find_containing_direntry(): use (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:48Z","receivedAt":"2011-12-12T05:38:48Z","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 439545b..d89c3d0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -282,38 +282,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@@ -324,12 +326,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@@ -340,12 +341,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@@ -752,11 +752,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@@ -809,7 +809,8 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag |= REF_ISBROKEN;\n \t\t\t}\n-\t\t\tadd_entry_to_dir(dir, create_ref_entry(refname, sha1, flag, 1));\n+\t\t\tadd_entry_to_dir(&direntry->u.subdir,\n+\t\t\t\t\t create_ref_entry(refname, sha1, flag, 1));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(d);\n@@ -1142,35 +1143,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.8\n"},{"id":"180888","messageId":"1323668338-1764-43-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 42/51] search_ref_dir(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:49Z","receivedAt":"2011-12-12T05:38:49Z","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\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 d89c3d0..30ff9b8 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -246,11 +246,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@@ -300,7 +303,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@@ -330,7 +333,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.8\n"},{"id":"180877","messageId":"1323668338-1764-44-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 43/51] add_entry(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:50Z","receivedAt":"2011-12-12T05:38:50Z","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\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 30ff9b8..54f65e3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -199,8 +199,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@@ -310,7 +313,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@@ -348,7 +351,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@@ -812,8 +815,7 @@ static void get_ref_dir(struct ref_cache *refs, const char *dirname)\n \t\t\t\thashclr(sha1);\n \t\t\t\tflag |= REF_ISBROKEN;\n \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, 1));\n+\t\t\tadd_entry(direntry, create_ref_entry(refname, sha1, flag, 1));\n \t\t}\n \t\tfree(refname);\n \t\tclosedir(d);\n-- \n1.7.8\n"},{"id":"180881","messageId":"1323668338-1764-45-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 44/51] do_for_each_ref_in_dir*(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:51Z","receivedAt":"2011-12-12T05:38:51Z","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\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 54f65e3..f6d9fe7 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -429,17 +429,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@@ -450,25 +453,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@@ -478,7 +486,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@@ -501,7 +509,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@@ -563,7 +571,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@@ -1161,21 +1169,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@@ -1626,8 +1633,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.8\n"},{"id":"180878","messageId":"1323668338-1764-46-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 45/51] sort_ref_dir(): take (ref_entry *) instead of (ref_dir *)","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:52Z","receivedAt":"2011-12-12T05:38:52Z","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\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 f6d9fe7..4a3546b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -243,7 +243,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@@ -271,7 +271,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@@ -382,11 +382,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@@ -437,7 +439,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@@ -466,8 +468,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.8\n"},{"id":"180879","messageId":"1323668338-1764-47-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 46/51] struct ref_dir: store a reference to the enclosing ref_cache","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:53Z","receivedAt":"2011-12-12T05:38:53Z","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 4a3546b..4088167 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -106,6 +106,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@@ -113,6 +115,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@@ -226,13 +234,15 @@ 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 \tint len = strlen(dirname);\n \tdirentry = xcalloc(1, sizeof(struct ref_entry) + len + 1);\n \tmemcpy(direntry->name, dirname, len + 1);\n \tdirentry->flag = REF_DIR;\n+\tdirentry->u.subdir.ref_cache = ref_cache;\n \treturn direntry;\n }\n \n@@ -312,7 +322,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@@ -726,7 +737,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, 0));\n }\n \n@@ -744,7 +755,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@@ -835,7 +846,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.8\n"},{"id":"180885","messageId":"1323668338-1764-48-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 47/51] read_loose_refs(): take a (ref_entry *) as argument","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:54Z","receivedAt":"2011-12-12T05:38:54Z","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 4088167..a85a8a5 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -770,21 +770,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@@ -822,7 +821,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@@ -847,7 +848,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.8\n"},{"id":"180886","messageId":"1323668338-1764-49-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 48/51] refs: read loose references lazily","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:55Z","receivedAt":"2011-12-12T05:38:55Z","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 a85a8a5..3cd8e04 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -101,6 +101,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@@ -108,6 +114,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@@ -125,19 +159,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@@ -229,19 +277,21 @@ 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 \tint len = strlen(dirname);\n \tdirentry = xcalloc(1, sizeof(struct ref_entry) + len + 1);\n \tmemcpy(direntry->name, dirname, len + 1);\n-\tdirentry->flag = REF_DIR;\n+\tdirentry->flag = flag;\n \tdirentry->u.subdir.ref_cache = ref_cache;\n \treturn direntry;\n }\n@@ -266,6 +316,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@@ -322,8 +373,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@@ -399,6 +456,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@@ -449,8 +507,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@@ -477,10 +535,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@@ -737,7 +795,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, 0));\n }\n \n@@ -755,7 +813,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@@ -777,11 +835,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@@ -819,11 +880,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@@ -842,13 +904,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.8\n"},{"id":"180880","messageId":"1323668338-1764-50-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 49/51] is_refname_available(): query only possibly-conflicting references","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:56Z","receivedAt":"2011-12-12T05:38:56Z","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\nAlso, simplify name_conflict_fn().  Since it now will only be called\nfor possibly-conflicting references, there is necessarily a conflict\nif it is 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 refs.c |   74 ++++++++++++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 47 insertions(+), 27 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 3cd8e04..312ca3b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -591,23 +591,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@@ -616,31 +600,67 @@ 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-\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  * 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+ * 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 {\n+\tint prefixlen = strlen(refname);\n+\tchar *prefix;\n+\tchar *slash;\n \tstruct name_conflict_cb data;\n-\tdata.refname = refname;\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.oldrefname = oldrefname;\n \tdata.conflicting_refname = NULL;\n \n-\tassert(direntry->flag & REF_DIR);\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-- \n1.7.8\n"},{"id":"180882","messageId":"1323668338-1764-51-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 50/51] read_packed_refs(): keep track of the directory being worked in","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:57Z","receivedAt":"2011-12-12T05:38:57Z","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 312ca3b..92523fd 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -780,6 +780,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@@ -799,8 +800,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, 1);\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.8\n"},{"id":"180887","messageId":"1323668338-1764-52-git-send-email-mhagger@alum.mit.edu","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"[PATCH v2 51/51] repack_without_ref(): call clear_packed_ref_cache()","fromName":"","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T05:38:58Z","receivedAt":"2011-12-12T05:38:58Z","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 92523fd..3541a73 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1736,7 +1736,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@@ -1746,6 +1748,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@@ -1774,6 +1777,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@@ -1782,7 +1786,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.8\n"},{"id":"180907","messageId":"7vmxay6vtx.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-1-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 00/51] ref-api-C and ref-api-D re-roll","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-12T08:24:58Z","receivedAt":"2011-12-12T08:24:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks for a re-roll. Will take a look early this week.\n"},{"id":"180915","messageId":"20111212083345.GA16106@sigill.intra.peff.net","threadId":"29136","inReplyTo":"1323668338-1764-9-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-12T08:33:45Z","receivedAt":"2011-12-12T08:33:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 12, 2011 at 06:38:15AM +0100, mhagger@alum.mit.edu wrote:\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->sha1, ref2->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> +\t\treturn 0;\n> +\t}\n> +}\n\nAs a user, I'm not sure what this message means. If I understand the\ncontext right, this happens when you have duplicate entries in your\npacked-refs file?\n\nThis would indicate a bug in git, so should this perhaps say \"BUG:\" in\nfront, or maybe give some more context so that a user understands it is\nnot their error, or even a random error on this run, but that git has\naccidentally corrupted the packed-refs file (and their best bet is\nprobably to report the bug to us).\n\nThis is obviously not an issue introduced by your patch, as you are\njust moving these error messages around. But maybe while the topic is\nfresh in your mind it is a good time to improve it. I dunno. AFAICT\nnobody has ever actually hit this message, so maybe it doesn't matter.\n:)\n\n-Peff\n"},{"id":"180927","messageId":"4EE5E918.8090104@alum.mit.edu","threadId":"29136","inReplyTo":"20111212083345.GA16106@sigill.intra.peff.net","subject":"Re: [PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-12T11:44:24Z","receivedAt":"2011-12-12T11:44:24Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/12/2011 09:33 AM, Jeff King wrote:\n> On Mon, Dec 12, 2011 at 06:38:15AM +0100, mhagger@alum.mit.edu wrote:\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->sha1, ref2->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>> +\t\treturn 0;\n>> +\t}\n>> +}\n> \n> As a user, I'm not sure what this message means. If I understand the\n> context right, this happens when you have duplicate entries in your\n> packed-refs file?\n\nIt could be caused by dups in any of the three ref caches: packed refs,\nloose refs, or extra refs.\n\nDuplicates in the packed-refs file could be caused by any garden-variety\ndata corruption external to git.  Or they could be caused by a bug in\ngit that caused it to write duplicates.  But the code that writes packed\nrefs iterates over the refs right after they are sorted, and sorting\neliminates duplicates, so this would be an unlikely bug the way the code\nis currently organized.  I suppose they could also be caused by the\nsimultaneous writing of the packed-refs file by another process that\ndoesn't respect git's lock file; perhaps an ill-timed rsync or something.\n\nNormally there should be no duplicate loose refs, because their names\nare taken directly from the filesystem and duplicate filenames are not\nallowed.  (Though I don't know if readdir() guarantees an atomic\nsnapshot of a directory; if not, then a dup could perhaps be created by\nhaving another process add and remove files while git is reading a loose\nref directory.)\n\nExtra refs are created locally by other git code that I am not familiar\nwith, so duplicate extra refs could only be created by a bug in git.\n\nSo in summary, duplicates could be caused by a git bug, by a corrupt\npacked-refs file, or conceivably by a race condition with another\nprocess that is mutating the packed-refs file or the loose reference\ndirectories.\n\n> This would indicate a bug in git, so should this perhaps say \"BUG:\" in\n> front, or maybe give some more context so that a user understands it is\n> not their error, or even a random error on this run, but that git has\n> accidentally corrupted the packed-refs file (and their best bet is\n> probably to report the bug to us).\n> \n> This is obviously not an issue introduced by your patch, as you are\n> just moving these error messages around. But maybe while the topic is\n> fresh in your mind it is a good time to improve it. I dunno. AFAICT\n> nobody has ever actually hit this message, so maybe it doesn't matter.\n> :)\n\nThe first of Google search results for the fatal error message only\nshows one instance where the error was observed [1].  This was a case of\ncloning using the rsync protocol, which I believe is now deprecated\n(probably for exactly this reason).\n\nI think that the gold-plated way to improve the error message would be\nto pass the error back to the caller, who would have more context to\ndecide the most likely cause of the duplicate and give an appropriate\nwarning or error message.  But I think we can afford to wait until more\nerror reports appear before bothering with this.\n\nMichael\n\n[1] http://kerneltrap.org/mailarchive/git/2008/12/17/4438764\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"180936","messageId":"7vaa6x67b9.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"4EE5E918.8090104@alum.mit.edu","subject":"Re: [PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-12T17:14:34Z","receivedAt":"2011-12-12T17:14:34Z","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> Extra refs are created locally by other git code that I am not familiar\n> with, so duplicate extra refs could only be created by a bug in git.\n\nI think we already discussed this, but just in case you forgot.  They are\nused to tell git to treat as if objects in histories leading to certain\ncommits are complete and available, i.e. only their object names matter\nand the refname field is primarily for debugging. It would be a grave\nregression if you errored out seeing duplicate names in them.\n"},{"id":"181001","messageId":"7vvcplxq4r.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-9-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-12T22:33:12Z","receivedAt":"2011-12-12T22:33:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"mhagger@alum.mit.edu writes:\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->sha1, ref2->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> +\t\treturn 0;\n> +\t}\n> +}\n\nAt this step, is_dup_ref() is only called from sort_ref_array() which in\nturn is only called on either a collection of loose or packed refs, so\nwarning is warranted. IOW I do not see anything wrong with _this_ patch.\n\nLater in the series, however, the collection of extra refs seems to be\nshoved into a phoney \"direntry\" and made to go through the same add_ref()\nmachinery that uses find_containing_direntry() which in turn calls\nsearch_ref_dir() that smells like a definite no-no.\n"},{"id":"181004","messageId":"7vpqftxq4p.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-18-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 17/51] do_for_each_ref(): correctly terminate while processesing extra_refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-12T22:41:33Z","receivedAt":"2011-12-12T22:41:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"mhagger@alum.mit.edu writes:\n\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n>\n> If the user-supplied function returns a nonzero value while processing\n> extra_refs, terminate without processing the rest of the list.\n>\n> This probably has no practical importance, but makes the handling of\n> extra_refs a little bit more consistent with the handling of other\n> refs.\n>\n> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n> ---\n>  refs.c |    5 ++++-\n>  1 files changed, 4 insertions(+), 1 deletions(-)\n>\n> diff --git a/refs.c b/refs.c\n> index 579e4c3..fb6fe84 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -711,8 +711,11 @@ static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn\n>  \n>  \tstruct ref_array *extra = &extra_refs;\n>  \n> -\tfor (i = 0; i < extra->nr; i++)\n> +\tfor (i = 0; i < extra->nr; i++) {\n>  \t\tretval = do_one_ref(base, fn, trim, flags, cb_data, extra->refs[i]);\n> +\t\tif (retval)\n> +\t\t\tgoto end_each;\n> +\t}\n\nMakes sense and everything up to this point looks sane.\n\nThanks.\n"},{"id":"181002","messageId":"7vk461xq4n.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-21-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 20/51] repack_without_ref(): reimplement using do_for_each_ref_in_array()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-12T22:44:08Z","receivedAt":"2011-12-12T22:44:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"mhagger@alum.mit.edu writes:\n\n> +static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,\n> +\t\t\t\t int flags, void *cb_data)\n> +{\n> +\tstruct repack_without_ref_sb *data = cb_data;\n> +\tchar line[PATH_MAX + 100];\n> +\tint len;\n> +\n> +\tif (!strcmp(data->refname, refname))\n> +\t\treturn 0;\n> +\tlen = snprintf(line, sizeof(line), \"%s %s\\n\",\n> +\t\t       sha1_to_hex(sha1), refname);\n> +\t/* this should not happen but just being defensive */\n> +\tif (len > sizeof(line))\n> +\t\tdie(\"too long a refname '%s'\", refname);\n\nPerhaps strbuf would be easier to use for things like this.\n"},{"id":"181003","messageId":"7vehw9xq4l.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-31-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 30/51] sort_ref_dir(): do not sort if already sorted","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-12T23:26:57Z","receivedAt":"2011-12-12T23:26:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"mhagger@alum.mit.edu writes:\n\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n>\n> Keep track of how many entries in a ref_dir are already sorted.  In\n> sort_ref_dir(), only call qsort() if the dir contains unsorted\n> entries.\n>\n> We could store a binary \"sorted\" value instead of an integer, but\n> storing the number of sorted entries leaves the way open for a couple\n> of 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\nThe elegance (which I think is the right word for this case as it is both\nsimple and clever) of the above strategy is not fully appreciated unless\nyou explain that your plan for \"lazily add new entries and keep the\ndirectory unsorted\" case is to append them at the end (hence preserving\nthe property that entries[0..sorted] is always sorted).\n\nIf you reword the comment \"How many of the entries...\" to something like\n\"entries at 0 up to this index in the array are sorted\", you could express\nthat idea without changing the log message that much, I guess.\n\n> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n> ---\n>  refs.c |   29 ++++++++++++++++++++++++-----\n>  1 files changed, 24 insertions(+), 5 deletions(-)\n>\n> diff --git a/refs.c b/refs.c\n> index ccd2806..ce141ea 100644\n> --- a/refs.c\n> +++ b/refs.c\n> @@ -108,6 +108,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"},{"id":"181000","messageId":"7v1us9z4qc.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-3-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 02/51] refs: rename \"refname\" variables","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-13T00:37:31Z","receivedAt":"2011-12-13T00:37:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This added otherwise unnecessary conflicts with topics in flight, but the\nconflicts were nothing Git and a human couldn't manage, and overall it is\nnot a bad readability change for the longer-term code health.\n\nThanks.\n"},{"id":"181008","messageId":"7v7h21xps9.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"1323668338-1764-29-git-send-email-mhagger@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-13T00:45:42Z","receivedAt":"2011-12-13T00:45:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"mhagger@alum.mit.edu writes:\n\n> From: Michael Haggerty <mhagger@alum.mit.edu>\n>\n> This purely textual change is in preparation for storing references\n> hierarchically, when the old ref_array structure will represent one\n> \"directory\" of references.  Rename functions that deal with this\n> structure analogously, and also rename the structure's \"refs\" member\n> to \"entries\".\n>\n> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n> ---\n>  refs.c |  166 ++++++++++++++++++++++++++++++++--------------------------------\n>  1 files changed, 83 insertions(+), 83 deletions(-)\n>\n> diff --git a/refs.c b/refs.c\n> index fe6d657..b74ef80 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\nThe s/refs/entries/ renaming is a sane thing to do; on the other hand, I\nsomehow find the s/ref_array/ref_dir/ renaming is a short-sighted change\nand undesirable, as you are essentially declaring that \"if you use this\nstructure, the contents you store there are expected to be named\nhierarchically\", forbidding users that want to use a simple flat array.\n\nBUT. That was an observation before I continued reading the remainder of\nthe series.\n\nI think the above observation primarily come from my worries around the\nextra ref stuff, which by nature does not fit well with the hierarchical\nnaming (they do not even have any meaningful names). Sorting or requiring\nuniqueness among them do not make any sense, let alone cutting their names\nin hierarchical boundaries.\n\nAs an alternative, it _might_ make sense to get rid of \"add_extra_ref()\"\nAPI from refs.c and make it the responsibility for its users to add their\nextra anchor points where they use for_each_ref() to find out all anchor\npoints in the history from the refs.c subsystem. If we go that route, I\nfully agree that \"s/ref_array/ref_dir/\" renaming is the right thing to do,\nas refs.c subsystem will _only_ handle the hierarchical ref namespace and\nnothing else after such a change.\n\nA bit of background refresher.\n\n\"git clone --reference=../another-repo.git $origin\" is a typical user of\nthe add_extra_ref() API. It runs an equivalent of ls-remote against the\nreference repository, and uses add_extra_ref() to cause its later call to\nfor_each_ref() to return the names of the objects at the tips of refs in\nthat reference repository.\n\nDuring the object transfer (i.e. the equivalent of \"git fetch\") from the\norigin, \"clone\" and \"upload-pack\" negotiate what objects need to be sent,\nand this is done by the receiving end telling what it has to the sending\nend, and the sending end uses this information to subtract what the\nreceiving end claims to already have from what is going to be sent.\n\nAnd the enumeration of what the receiving end, especially when there is no\n\"reference\", is done using for_each_ref(), as object transfer considers\neverything reachable from the refs complete. add_extra_ref() is a _hack_\nto cause for_each_ref() to include objects that actually do _not_ sit at\nthe tip of any of our refs.\n\nWhen cloning, we do not have any ref, and after clone is done, we do not\nwant the refs the repository we are borrowing its objects from to be our\nref (after all, we may be using a local copy of linux-3.0 repository as\nour reference but cloning from linux-2.4 history), and that is why this\nhack was invented (in the old scripted version, we tentatively created\nreal refs in our ref namespace and removed them after clone finished).\n\nIn the case of \"clone\", these extra refs are registered with names taken\nfrom the repository we are borrowing from, so they may be unique and\nwithout conflict among them if you are borrowing from one repository, but\nif you borrow from more than one repositories, it is likely that all of\nthem have \"refs/heads/master\" and there is no reason to expect that the\nextra refs added with add_extra_ref() API have unique names. Worse, in\n\"receive-pack\" that runs on the receiving end when you \"git push\" your\nhistory, \"refs\" that exist in repositories that the receiving repository\nis borrowing from (i.e. it has $GIT_DIR/objects/info/alternates) are\nadvertised with \".have\" in their names (not using anything refs/* is to\navoid the pusher from mistakenly think there is such a ref that is\neligible for \"matching push\" logic), and these \".have\" entries obviously\nare not unique. The code also uses the same add_extra_ref() hack to cause\nfor_each_ref() to report them.\n\nThe removal of this hack, taking receive-pack as an example, would be to\nstop using add_extra_ref() but instead to keep a local list of object\nnames that the code currently uses add_extra_ref() to keep track of, and\nthen modify write_head_info() to feed show_ref_cb() with these object\nnames in addition to the current for_each_ref() callback.\n\nIt may make the resulting code cleaner if we go that route, and if we were\nto do so, I think the right place to do so in the series would be either\nat the very beginning of the series (as part of the preliminary clean-up),\nor immediately before \"do_for_each_ref: correctly terminate while\nprocessing extra_refs\" (making that commit unnecessary, as after such a\nfix, refs.c API won't have to worry about the extra_refs hack anymore).\n\nWhat do you think?\n"},{"id":"181018","messageId":"4EE6D61A.2030300@alum.mit.edu","threadId":"29136","inReplyTo":"7vvcplxq4r.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-13T04:35:38Z","receivedAt":"2011-12-13T04:35:38Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/12/2011 11:33 PM, Junio C Hamano wrote:\n> mhagger@alum.mit.edu writes:\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->sha1, ref2->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>> +\t\treturn 0;\n>> +\t}\n>> +}\n> \n> At this step, is_dup_ref() is only called from sort_ref_array() which in\n> turn is only called on either a collection of loose or packed refs, so\n> warning is warranted. IOW I do not see anything wrong with _this_ patch.\n\nI agree.\n\n> Later in the series, however, the collection of extra refs seems to be\n> shoved into a phoney \"direntry\" and made to go through the same add_ref()\n> machinery that uses find_containing_direntry() which in turn calls\n> search_ref_dir() that smells like a definite no-no.\n\nCorrect.\n\nI had remembered your explanation of the purpose of extra refs and also\nthat they have unusual names, but I hadn't allowed for the possibility\nthat multiple extra_refs can have the same name.  The old code never\nsorted the extra refs and therefore never checked or eliminated\nduplicates.  The new code does, as sorting is an intrinsic part of\nlooking up references and reference subdirectories in the hierarchical\ncache.  It is introduced along with the meat of the change in\n\n    [PATCH v2 29/51] refs: store references hierarchically\n\nSo...the new code would die if two extra refs have the same name but\ndifferent SHA1s, and emit a warning if they have the same name and the\nsame SHA1s.  (However, it is no problem for an extra ref to have the\nsame name as a packed or loose ref.)\n\nIs it *really* possible to have multiple extra_refs with the same name,\nor is this more of a hypothetical problem that requires more careful\nanalysis?\n\nIf the former, then I will have to do some work.  My approach would\nprobably be to allow sorting without de-duplication; that way extra_refs\ncan continue to share most of the code used for the other ref caches.\nIt would be relatively easy to add such a fix on top of the patch\nseries, where every ref_entry knows the ref_cache with which it is\nassociated.  It would be quite a bit more painful to massage such a fix\nthrough the whole patch series.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"181019","messageId":"4EE6DBF4.5010409@alum.mit.edu","threadId":"29136","inReplyTo":"4EE6D61A.2030300@alum.mit.edu","subject":"Re: [PATCH v2 08/51] is_dup_ref(): extract function from sort_ref_array()","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-13T05:00:36Z","receivedAt":"2011-12-13T05:00:36Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/13/2011 05:35 AM, Michael Haggerty wrote:\n> Is it *really* possible to have multiple extra_refs with the same name,\n> or is this more of a hypothetical problem that requires more careful\n> analysis?\n\nI see that you already answered this question downthread.  I will reply\nto your analysis there.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"181021","messageId":"4EE6E61F.8080405@alum.mit.edu","threadId":"29136","inReplyTo":"7v7h21xps9.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-13T05:43:59Z","receivedAt":"2011-12-13T05:43:59Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/13/2011 01:45 AM, Junio C Hamano wrote:\n> mhagger@alum.mit.edu writes:\n> \n>> From: Michael Haggerty <mhagger@alum.mit.edu>\n>>\n>> This purely textual change is in preparation for storing references\n>> hierarchically, when the old ref_array structure will represent one\n>> \"directory\" of references.  Rename functions that deal with this\n>> structure analogously, and also rename the structure's \"refs\" member\n>> to \"entries\".\n>>\n>> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>\n>> ---\n>>  refs.c |  166 ++++++++++++++++++++++++++++++++--------------------------------\n>>  1 files changed, 83 insertions(+), 83 deletions(-)\n>>\n>> diff --git a/refs.c b/refs.c\n>> index fe6d657..b74ef80 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> The s/refs/entries/ renaming is a sane thing to do; on the other hand, I\n> somehow find the s/ref_array/ref_dir/ renaming is a short-sighted change\n> and undesirable, as you are essentially declaring that \"if you use this\n> structure, the contents you store there are expected to be named\n> hierarchically\", forbidding users that want to use a simple flat array.\n\nThis data structure is not exposed outside of the module, so it only\ndescribes the de facto current storage scheme rather than making any\npromises to the outside world.\n\n> BUT. That was an observation before I continued reading the remainder of\n> the series.\n\n> I think the above observation primarily come from my worries around the\n> extra ref stuff, which by nature does not fit well with the hierarchical\n> naming (they do not even have any meaningful names). Sorting or requiring\n> uniqueness among them do not make any sense, let alone cutting their names\n> in hierarchical boundaries.\n> \n> As an alternative, it _might_ make sense to get rid of \"add_extra_ref()\"\n> API from refs.c and make it the responsibility for its users to add their\n> extra anchor points where they use for_each_ref() to find out all anchor\n> points in the history from the refs.c subsystem. If we go that route, I\n> fully agree that \"s/ref_array/ref_dir/\" renaming is the right thing to do,\n> as refs.c subsystem will _only_ handle the hierarchical ref namespace and\n> nothing else after such a change.\n\nI absolutely agree; the fact that extra refs are part of the refs module\nhas the foul smell of expedience.  And your suggestion for changing the\nsituation makes sense to me as far as I can follow it.  But I don't\nthink I have the gumption to attack another big part of the code base\nbefore I've even finished the changes that I still have planned for the\nrefs API.\n\nIf somebody else wants to volunteer to make extra_refs redundant, I\nwould be happy to support that person on the refs side.\n\nOtherwise, I propose to just avoid de-duping extra refs so that my patch\nseries doesn't make things worse.  If it is acceptable, I would prefer\nto add the fix as a patch at the end of the series, because after\n\n    struct ref_dir: store a reference to the enclosing ref_cache\n\nref_dir carries around information that can be used to distinguish\nbetween the extra_refs and other ref caches.  I think it is as easy as\nthe attached patch (untested).\n\nApropos testing, it is unsettling that our test suite doesn't show any\nfailures after my changes.  The dir entries in extra_refs are now always\nsorted and de-duped when do_for_each_ref() is called.  Could it be that\nduplicate \".have\" references never come up in the test suite?  It sounds\nlike an important code path is not being tested.  In particular, I won't\nbe able to test whether my fix works :-/\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n\n\ndiff --git a/refs.c b/refs.c\nindex 2d5c827..2fd3db2 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -457,7 +457,6 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2\n  */\n static void sort_ref_dir(struct ref_entry *direntry)\n {\n-\tint i, j;\n \tstruct ref_entry *last = NULL;\n \tstruct ref_dir *dir;\n \tassert(direntry->flag & REF_DIR);\n@@ -468,19 +467,24 @@ static void sort_ref_dir(struct ref_entry *direntry)\n \n \tqsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);\n \n-\t/* Remove any duplicates: */\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\tdir->entries[i++] = entry;\n-\t\t\tlast = NULL;\n-\t\t} else {\n-\t\t\tlast = dir->entries[i++] = entry;\n+\t/* Remove any duplicates, but not for extra_refs: */\n+\tif (dir->ref_cache != NULL) {\n+\t\tint i, j;\n+\t\tfor (i = 0, j = 0; j < dir->nr; j++) {\n+\t\t\tstruct ref_entry *entry = dir->entries[j];\n+\t\t\tif (last && is_dup_ref(last, entry)) {\n+\t\t\t\tfree_ref_entry(entry);\n+\t\t\t} else if (entry->flag & REF_DIR) {\n+\t\t\t\tdir->entries[i++] = entry;\n+\t\t\t\tlast = NULL;\n+\t\t\t} else {\n+\t\t\t\tlast = dir->entries[i++] = entry;\n+\t\t\t}\n \t\t}\n+\t\tdir->nr = i;\n \t}\n-\tdir->sorted = dir->nr = i;\n+\n+\tdir->sorted = dir->nr;\n }\n \n #define DO_FOR_EACH_INCLUDE_BROKEN 01\n"},{"id":"181025","messageId":"7vk461vuy9.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"4EE6E61F.8080405@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-13T06:37:02Z","receivedAt":"2011-12-13T06:37:02Z","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> Apropos testing, it is unsettling that our test suite doesn't show any\n> failures after my changes.  The dir entries in extra_refs are now always\n> sorted and de-duped when do_for_each_ref() is called.  Could it be that\n> duplicate \".have\" references never come up in the test suite?  It sounds\n> like an important code path is not being tested.  In particular, I won't\n> be able to test whether my fix works :-/\n\nI doubt anybody thought of using more than one --reference while cloning\nor having more than one entry in $GIT_DIR/objects/info/alternates in a\nrepository that is being pushed into, even though we might have tests for\nsimpler single --reference and single alternate cases.\n\nAs to the order of the changes, my gut feeling is that it would make it\nharder to debug your series to delay the removal of \"extra_ref\" hackery,\nas it would be a corner case that your \"hierarchical\" structure never has\nto worry about in the end result.\n\nAnother possibility is to keep the extra_ref interface in refs.c, without\nany of your changes (i.e. keep it just as a flat list, with the original\ninterface to append to it without any dedup and other fancy features) and\nalso keep the special casing of it in for_each_ref(). AFAIK, that is the\nonly iterator that should care about the extra refs. Thinking about it a\nbit more, removal of add_extra_ref() API may be unnecessarily complex with\nno real gain. For example, extra refs added in builtin/clone.c is used by\nbuiltin/fetch-pack.c, meaning that the codepath that discovers and\nremembers these extra history anchor points and the codepath that uses\nthem while walking the history are not localized and we would need some\nsort of \"extra ref API\" anyway. I am leaning towards this option.\n"},{"id":"181065","messageId":"4EE7A387.3070400@alum.mit.edu","threadId":"29136","inReplyTo":"7vk461vuy9.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-13T19:12:07Z","receivedAt":"2011-12-13T19:12:07Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/13/2011 07:37 AM, Junio C Hamano wrote:\n> Michael Haggerty <mhagger@alum.mit.edu> writes:\n> \n>> Apropos testing, it is unsettling that our test suite doesn't show any\n>> failures after my changes.  The dir entries in extra_refs are now always\n>> sorted and de-duped when do_for_each_ref() is called.  Could it be that\n>> duplicate \".have\" references never come up in the test suite?  It sounds\n>> like an important code path is not being tested.  In particular, I won't\n>> be able to test whether my fix works :-/\n> \n> I doubt anybody thought of using more than one --reference while cloning\n> or having more than one entry in $GIT_DIR/objects/info/alternates in a\n> repository that is being pushed into, even though we might have tests for\n> simpler single --reference and single alternate cases.\n\nEven with a single alternate database, multiple references are\nadvertised with the same name \".have\".  But the tests never push from a\nrepository with more than one reference in its alternates (verified by\ninstrumenting code).  That is why my changes didn't cause test failures.\n When I, for example, change the setup function in t5519 to push *two*\nreferences to alice-pub, then master works fine, my ref-api branch\nfails, and my fixup patch fixes the failure.\n\n> As to the order of the changes, my gut feeling is that it would make it\n> harder to debug your series to delay the removal of \"extra_ref\" hackery,\n> as it would be a corner case that your \"hierarchical\" structure never has\n> to worry about in the end result.\n> \n> Another possibility is to keep the extra_ref interface in refs.c, without\n> any of your changes (i.e. keep it just as a flat list, with the original\n> interface to append to it without any dedup and other fancy features) and\n> also keep the special casing of it in for_each_ref(). AFAIK, that is the\n> only iterator that should care about the extra refs. Thinking about it a\n> bit more, removal of add_extra_ref() API may be unnecessarily complex with\n> no real gain. For example, extra refs added in builtin/clone.c is used by\n> builtin/fetch-pack.c, meaning that the codepath that discovers and\n> remembers these extra history anchor points and the codepath that uses\n> them while walking the history are not localized and we would need some\n> sort of \"extra ref API\" anyway. I am leaning towards this option.\n\nIn a few minutes I will post a series of patches that store extra_refs\nin a linked list separate from the reference caches, and iterates over\nthem only from for_each_ref().  I could rebase my ref-api-D changes on\ntop of this patch series in such a way that the extra refs are kept in\nnon-hierarchical storage.  But I leave for vacation on Thursday so it is\nquite likely that I won't be able to get it finished before I return in\nthe new year.\n\nAn alternative is to use my one-patch fix on top of the ref-api-D\nchanges (plus another patch to beef up t5519).  The fix is isolated and\nI'm confident that it is safe (though I would inspect and test it better\nbefore formally submitting it).  The advantage is that is less work, so\nit can be ready tomorrow instead of in two weeks.  The disadvantage is\nthat there would be an interval of commits on the feature branch (from\nthe middle of the ref-api-D patch series until the fix) that are\nnon-functional, albeit in a way that the test suite doesn't detect.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"181066","messageId":"7vhb14uvqu.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"4EE7A387.3070400@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-13T19:17:29Z","receivedAt":"2011-12-13T19:17:29Z","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> ...  But I leave for vacation on Thursday so it is quite likely that I\n> won't be able to get it finished before I return in the new year.\n\nThat is perfectly fine. Have fun.\n\nI wasn't expecting the entire series to be ready during this cycle\nanyway, and was planning to queue the clean-up series, up to 16/51\nor so, for the next release.\n"},{"id":"181085","messageId":"4EE7CDF2.3040408@alum.mit.edu","threadId":"29136","inReplyTo":"4EE7A387.3070400@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-13T22:13:06Z","receivedAt":"2011-12-13T22:13:06Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/13/2011 08:12 PM, Michael Haggerty wrote:\n> On 12/13/2011 07:37 AM, Junio C Hamano wrote:\n>> Another possibility is to keep the extra_ref interface in refs.c, without\n>> any of your changes (i.e. keep it just as a flat list, with the original\n>> interface to append to it without any dedup and other fancy features) and\n>> also keep the special casing of it in for_each_ref(). AFAIK, that is the\n>> only iterator that should care about the extra refs. Thinking about it a\n>> bit more, removal of add_extra_ref() API may be unnecessarily complex with\n>> no real gain. For example, extra refs added in builtin/clone.c is used by\n>> builtin/fetch-pack.c, meaning that the codepath that discovers and\n>> remembers these extra history anchor points and the codepath that uses\n>> them while walking the history are not localized and we would need some\n>> sort of \"extra ref API\" anyway. I am leaning towards this option.\n> \n> In a few minutes I will post a series of patches that store extra_refs\n> in a linked list separate from the reference caches, and iterates over\n> them only from for_each_ref().  I could rebase my ref-api-D changes on\n> top of this patch series in such a way that the extra refs are kept in\n> non-hierarchical storage.  But I leave for vacation on Thursday so it is\n> quite likely that I won't be able to get it finished before I return in\n> the new year.\n\nI forgot to mention that I think it would be better to separate the\nhandling of extra refs even more than do the patches\n\n    [PATCH 0/6] Handle extra_refs separately from ref_caches\n\nNamely, we should figure out what code wants to set extra refs and or\nwants to include extra refs in its iteration over references.  The\nsetters don't have to be changed, but the readers should be.  After the\nabove patch series, the readers call for_each_ref(), which always calls\ndo_for_each_extra_ref().  The call to do_for_each_extra_ref() should be\ntaken out of for_each_ref() and called directly by any readers who want\nto support extra_refs.  Other readers should only call for_each_ref().\n(do_for_each_extra_ref(), probably under a different name, would have to\nbecome public.)  That way, for_each_ref() could ignore the extra refs\nentirely, and other callers of for_each_ref() wouldn't risk getting\nextra refs by accident.\n\nToday I spent some time trying to figure out what callers of\nfor_each_ref() could possible want to deal with extra refs, but I got\nlost in the deep call trees and undocumented function declarations.  At\nthe top it is pretty clear that all such code paths start in cmd_clone()\nor cmd_receive_pack().  But there are so many calls to the\nfor_each_ref*() family of functions that I wasn't able to determine\nexactly which should allow for extra refs and which shouldn't.  (I even\nwonder whether extra refs might be used inadvertently in some places\nwhere they aren't wanted, for example in one of the myriad calls to\nsetup_revisions().)  If somebody could put his finger on the\nfor_each_ref() callers who want to support extra refs, that would be\nvery helpful.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"181095","messageId":"7vzkewt5qu.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"4EE7CDF2.3040408@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-13T23:24:25Z","receivedAt":"2011-12-13T23:24:25Z","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>>> .... For example, extra refs added in builtin/clone.c is used by\n>>> builtin/fetch-pack.c, meaning that the codepath that discovers and\n>>> remembers these extra history anchor points and the codepath that uses\n>>> them while walking the history are not localized and we would need some\n>>> sort of \"extra ref API\" anyway. I am leaning towards this option.\n> ...\n> Namely, we should figure out what code wants to set extra refs and or\n> wants to include extra refs in its iteration over references. The\n> setters don't have to be changed, but the readers should be.\n\nYou read me correctly. That is exactly what I meant by \"extra ref API\" in\nmy earlier response.\n\n> ...  But there are so many calls to the\n> for_each_ref*() family of functions that I wasn't able to determine\n> exactly which should allow for extra refs and which shouldn't.\n\nOnly the ones that follow add_extra_ref() in the control flow.\n\nbuiltin/clone.c adds them in setup_reference() to deal with --reference.\nThe objects known to be complete in these repositories we borrow from\nneed to be marked complete on our end (i.e. clone does not have to fetch)\nand transport_fetch_refs() that eventually goes to fetch_refs_via_pack()\nthat calls fetch_pack() uses this information. All three for_each_ref()\ncalls in builtin/fetch-pack.c are about \"what are the objects that we know\nare complete?\" and needs to pay attention to extra refs.\n\nHaving said that, I have a slight suspicion that you might be able to\neliminate this one in clone.  setup_reference() adds the reference\nrepository to the $GIT_DIR/objects/info/alternates, and the fetch logic\nalready knows to account for the refs in alternate repositories via\ninsert_alternate_refs() callchain.\n\nbuiltin/receive-pack.c adds them in add_alternate_refs() to deal with the\nrepositories listed in $GIT_DIR/objects/info/alternates in the receiving\nend of a push. Again, the objects known to be complete in these\nrepositories we borrow from need to be marked complete on our end, and\nwrite_head_info() in that function sends these extras together with real\nrefs we have.\n"},{"id":"181103","messageId":"7vborct37c.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"7vzkewt5qu.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-14T00:19:19Z","receivedAt":"2011-12-14T00:19:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> Namely, we should figure out what code wants to set extra refs and or\n>> wants to include extra refs in its iteration over references. The\n>> setters don't have to be changed, but the readers should be.\n>\n> You read me correctly. That is exactly what I meant by \"extra ref API\" in\n> my earlier response.\n\nActually, I do not think it even needs to be the \"extra *REF* API\". The\nonly thing that matters is that these commits are considered to be extra\nanchor point in the history, in addition to the usual rule of considering\nthat everything reachable from our refs is complete. The data structure to\nhold them does not even have to be a \"struct ref\". Just an array of object\nnames (or \"struct object *\") should suffice.\n"},{"id":"181110","messageId":"20111214023320.GA22141@sigill.intra.peff.net","threadId":"29136","inReplyTo":"7vborct37c.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-14T02:33:20Z","receivedAt":"2011-12-14T02:33:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 13, 2011 at 04:19:19PM -0800, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> >> Namely, we should figure out what code wants to set extra refs and or\n> >> wants to include extra refs in its iteration over references. The\n> >> setters don't have to be changed, but the readers should be.\n> >\n> > You read me correctly. That is exactly what I meant by \"extra ref API\" in\n> > my earlier response.\n> \n> Actually, I do not think it even needs to be the \"extra *REF* API\". The\n> only thing that matters is that these commits are considered to be extra\n> anchor point in the history, in addition to the usual rule of considering\n> that everything reachable from our refs is complete. The data structure to\n> hold them does not even have to be a \"struct ref\". Just an array of object\n> names (or \"struct object *\") should suffice.\n\nSince my cff38a5 (receive-pack: eliminate duplicate .have refs,\n2011-05-19), receive-pack simply has a packed array of binary sha1s (in\na \"struct sha1_array\" object). That might be the simplest thing.\n\n-Peff\n"},{"id":"181215","messageId":"4EE9AD7C.2050605@alum.mit.edu","threadId":"29136","inReplyTo":"20111214023320.GA22141@sigill.intra.peff.net","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2011-12-15T08:19:08Z","receivedAt":"2011-12-15T08:19:08Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/14/2011 03:33 AM, Jeff King wrote:\n> On Tue, Dec 13, 2011 at 04:19:19PM -0800, Junio C Hamano wrote:\n>> Actually, I do not think it even needs to be the \"extra *REF* API\". The\n>> only thing that matters is that these commits are considered to be extra\n>> anchor point in the history, in addition to the usual rule of considering\n>> that everything reachable from our refs is complete. The data structure to\n>> hold them does not even have to be a \"struct ref\". Just an array of object\n>> names (or \"struct object *\") should suffice.\n> \n> Since my cff38a5 (receive-pack: eliminate duplicate .have refs,\n> 2011-05-19), receive-pack simply has a packed array of binary sha1s (in\n> a \"struct sha1_array\" object). That might be the simplest thing.\n\nIt was pretty easy to eliminate the use of extra_refs from receive-pack.\n I'll submit the patches as soon as I can.  The patches will be based\noff of patch 16/51 of the ref-api-D series, since Junio indicated that\nhe wants to queue up those commits (let me know if you have a different\npreference).\n\nNow I'm looking at the uses of extra_refs in git-clone.  One thing it\ndoes is add some extra refs then write them to the packed-refs file.  I\nstill have to dig into it, but this seems strange.  If the refs are\nbeing written to packed-refs, it seems like they must be real (not\nextra) refs, or perhaps are just about to become real refs as part of\nthe clone.  Or is something more egregious is going on?\n\nMoving the pack_refs() function to refs.c was on the agenda anyway, so\nit should be possible to sort this out at the same time.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"181217","messageId":"20111215083732.GA5958@sigill.intra.peff.net","threadId":"29136","inReplyTo":"4EE9AD7C.2050605@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-15T08:37:32Z","receivedAt":"2011-12-15T08:37:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 15, 2011 at 09:19:08AM +0100, Michael Haggerty wrote:\n\n> Now I'm looking at the uses of extra_refs in git-clone.  One thing it\n> does is add some extra refs then write them to the packed-refs file.  I\n> still have to dig into it, but this seems strange.  If the refs are\n> being written to packed-refs, it seems like they must be real (not\n> extra) refs, or perhaps are just about to become real refs as part of\n> the clone.  Or is something more egregious is going on?\n\nI think what is happening is that the extra_refs system is doing double\nduty here. If you do:\n\n  git clone --reference $local $origin\n\nthen the code does something like:\n\n  1. Add all of the refs in $local are added to extra_refs, because we\n     want to advertise them during the pack fetch (see add_one_reference,\n     called through setup_reference at l. 626).\n\n  2. We then get the list of remote refs from $origin and store them in\n     mapped_refs (ll. 657-658). The fetch uses the extra refs from (1).\n\n  3. We call clear_extra_refs to drop those temporary refs (l. 663).\n\n  4. We call write_remote_refs (l. 664), which adds all of the\n     mapped_refs from (2) to extra_refs, then calls pack_refs which will\n     pack all of the existing refs (of which there should be none, I\n     would think), including extras (i.e., all the stuff we just\n     cloned).\n\n     Afterwards we clear_extra_refs again, though I think that is\n     probably unnecessary.\n\nKeeping the \".have\" sha1s out of extra_refs would make this a lot\nclearer.  Arguably it would also be better to have some way to just pass\nthe set of mapped_refs to pack_refs, instead of shoving them in the\nglobal extra_refs and relying on pack_refs to find them there.\n\n-Peff\n"},{"id":"182685","messageId":"4F158E99.2020906@alum.mit.edu","threadId":"29136","inReplyTo":"7vzkewt5qu.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-01-17T15:07:05Z","receivedAt":"2012-01-17T15:07:05Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/14/2011 12:24 AM, Junio C Hamano wrote:\n> Michael Haggerty <mhagger@alum.mit.edu> writes:\n>> ...  But there are so many calls to the\n>> for_each_ref*() family of functions that I wasn't able to determine\n>> exactly which should allow for extra refs and which shouldn't.\n> \n> Only the ones that follow add_extra_ref() in the control flow.\n> \n> builtin/clone.c adds them in setup_reference() to deal with --reference.\n> The objects known to be complete in these repositories we borrow from\n> need to be marked complete on our end (i.e. clone does not have to fetch)\n> and transport_fetch_refs() that eventually goes to fetch_refs_via_pack()\n> that calls fetch_pack() uses this information. All three for_each_ref()\n> calls in builtin/fetch-pack.c are about \"what are the objects that we know\n> are complete?\" and needs to pay attention to extra refs.\n> \n> Having said that, I have a slight suspicion that you might be able to\n> eliminate this one in clone.  setup_reference() adds the reference\n> repository to the $GIT_DIR/objects/info/alternates, and the fetch logic\n> already knows to account for the refs in alternate repositories via\n> insert_alternate_refs() callchain.\n\nIf I comment out the call from add_one_reference() to add_extra_ref()\nthen I get a single failure, in t5700:\n\nnot ok - 8 fetched no objects\n#\t! grep \"^want\" \"$U\"\n\nSo your suspicion does not seem to be borne out (at least not in the\nnaivest form).\n\nStill studying...\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"184356","messageId":"4F352F03.2030104@alum.mit.edu","threadId":"29136","inReplyTo":"4F158E99.2020906@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-02-10T14:51:47Z","receivedAt":"2012-02-10T14:51:47Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 01/17/2012 04:07 PM, Michael Haggerty wrote:\n> On 12/14/2011 12:24 AM, Junio C Hamano wrote:\n>> Michael Haggerty <mhagger@alum.mit.edu> writes:\n>>> ...  But there are so many calls to the\n>>> for_each_ref*() family of functions that I wasn't able to determine\n>>> exactly which should allow for extra refs and which shouldn't.\n>>\n>> Only the ones that follow add_extra_ref() in the control flow.\n>>\n>> builtin/clone.c adds them in setup_reference() to deal with --reference.\n>> The objects known to be complete in these repositories we borrow from\n>> need to be marked complete on our end (i.e. clone does not have to fetch)\n>> and transport_fetch_refs() that eventually goes to fetch_refs_via_pack()\n>> that calls fetch_pack() uses this information. All three for_each_ref()\n>> calls in builtin/fetch-pack.c are about \"what are the objects that we know\n>> are complete?\" and needs to pay attention to extra refs.\n>>\n>> Having said that, I have a slight suspicion that you might be able to\n>> eliminate this one in clone.  setup_reference() adds the reference\n>> repository to the $GIT_DIR/objects/info/alternates, and the fetch logic\n>> already knows to account for the refs in alternate repositories via\n>> insert_alternate_refs() callchain.\n> \n> If I comment out the call from add_one_reference() to add_extra_ref()\n> then I get a single failure, in t5700:\n> \n> not ok - 8 fetched no objects\n> #\t! grep \"^want\" \"$U\"\n> \n> So your suspicion does not seem to be borne out (at least not in the\n> naivest form).\n> \n> Still studying...\n\nI finally had some time to get back to this puzzle.  It took me a while\nto narrow down the problem, and I still don't understand it entirely.\n\nIt seems like Junio should be right that setup_reference() doesn't need\nto add the alternate references to extra_refs.  Indeed, if I remove the\ncall to add_extra_ref(), I see the alternate references being added to\nref_list via insert_one_alternate_ref().  However, in the context of\nt5700, clone nevertheless sends \"want\" lines for one of those references\nand test 8 fails.  Something is screwy.\n\nSo how do the extra_refs prevent the \"want\" lines from being emitted?\nIt turns out that when the alternate refs *are* added as extra_refs,\nthen find_common() is not called at all.  do_fetch_pack() calls\neverything_local(), which returns true, and do_fetch_pack() skips over\nthe call to find_common().\n\nSo ISTM that there are two problems:\n\nFirst problem: everything_local() seems to be either broken or used\nincorrectly.  I can't decide which because I don't know what its\nsemantics are *supposed* to be.\n\nIf everything_local() is trying to check that the references are all in\nthe local repository itself, then it is incorrect for clone to enter\nalternates into extra_refs because everything_local() then mistakes them\nfor local.\n\nIf everything_local() is trying to check that the references are in the\nlocal repository plus alternates, then it is incorrect that\neverything_local() doesn't consider alternate references in its\ndetermination.  My guess is that this is the case, and that something\nlike the following might be the fix:\n\n> ----------------------------- builtin/fetch-pack.c -----------------------------\n> index 9500f35..4257a8d 100644\n> @@ -581,6 +581,11 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)\n>  \t*refs = newlist;\n>  }\n>  \n> +static void mark_alternate_complete(const struct ref *ref, void *unused)\n> +{\n> +\tmark_complete(NULL, ref->old_sha1, 0, NULL);\n> +}\n> +\n>  static int everything_local(struct ref **refs, int nr_match, char **match)\n>  {\n>  \tstruct ref *ref;\n> @@ -609,6 +614,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)\n>  \n>  \tif (!args.depth) {\n>  \t\tfor_each_ref(mark_complete, NULL);\n> +\t\tfor_each_alternate_ref(mark_alternate_complete, NULL);\n>  \t\tif (cutoff)\n>  \t\t\tmark_recent_complete_commits(cutoff);\n>  \t}\n\nWith this patch, then the full test suite passes even if I take out the\ncode that adds the alternate refs to extra_refs.\n\nSecond problem: it seems like the presence of alternate refs is not\nsuppressing the \"want\" lines for those refs in all cases.  Strangely, in\nthe case of t5700, adding the two alternate refs to ref_list\ninsert_one_alternate_ref() causes the \"want\" line for one of them to be\nsuppressed, but not for the other.\n\nSpecifically: (without the above patch) I commented out the call to\nadd_extra_ref() in clone.c:add_one_reference(), then ran t5700 through\nstep 8 then aborted.  insert_one_alternate_ref() was called four times:\n\ninsert_one_alternate_ref(ccc25a1f9655742174c93f48f616bea8ad0bc6ff)\ninsert_one_alternate_ref(ccc25a1f9655742174c93f48f616bea8ad0bc6ff)\ninsert_one_alternate_ref(5355551c5a927a2b6349505ada2da4bb702c0a49)\ninsert_one_alternate_ref(5355551c5a927a2b6349505ada2da4bb702c0a49)\n\n(The duplication here seems strange.)  However, UPLOAD_LOG still\ncontains \"want\" lines (2 of them!) for one of the commits:\n\n#S\n#E\n#S\n#S\n#E\nwant 5355551c5a927a2b6349505ada2da4bb702c0a49 multi_ack_detailed\nside-band-64k thin-pack ofs-delta\nwant 5355551c5a927a2b6349505ada2da4bb702c0a49\n#E\n\nThe \"5355551c\" object corresponds to refs/remotes/origin/master in the\nalternate object store:\n\n$ (cd B; git for-each-ref)\nccc25a1f9655742174c93f48f616bea8ad0bc6ff commit\trefs/heads/master\n5355551c5a927a2b6349505ada2da4bb702c0a49 commit\trefs/remotes/origin/HEAD\n5355551c5a927a2b6349505ada2da4bb702c0a49 commit\trefs/remotes/origin/master\n\nIt seems to me that even in the absence of short-circuiting due to\neverything_local() returning true, the presence of the alternate refs\nshould be suppressing the \"want\" lines for those references.\n\nI have some patches that seem to work (and get rid of extra_refs\nentirely, hurrah!), but I don't want to submit them until I understand\nwhat the correct behavior is *supposed* to be.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"},{"id":"184394","messageId":"20120210204457.GD5504@sigill.intra.peff.net","threadId":"29136","inReplyTo":"4F352F03.2030104@alum.mit.edu","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-02-10T20:44:57Z","receivedAt":"2012-02-10T20:44:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Feb 10, 2012 at 03:51:47PM +0100, Michael Haggerty wrote:\n\n> First problem: everything_local() seems to be either broken or used\n> incorrectly.  I can't decide which because I don't know what its\n> semantics are *supposed* to be.\n> \n> If everything_local() is trying to check that the references are all in\n> the local repository itself, then it is incorrect for clone to enter\n> alternates into extra_refs because everything_local() then mistakes them\n> for local.\n> \n> If everything_local() is trying to check that the references are in the\n> local repository plus alternates, then it is incorrect that\n> everything_local() doesn't consider alternate references in its\n> determination.  My guess is that this is the case, and that something\n> like the following might be the fix:\n\nJunio could answer more authoritatively than I, but I am pretty sure it\nis the latter. The point is to skip the expensive find_common\nnegotiation if we know that there are no objects to fetch. Thus the\n\"local\" here is \"do we have them on this side of the git-protocol\nconnection\", not \"do we have them in our non-alternates repository\".\n\n> > ----------------------------- builtin/fetch-pack.c -----------------------------\n> > index 9500f35..4257a8d 100644\n> > @@ -581,6 +581,11 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)\n> >  \t*refs = newlist;\n> >  }\n> >  \n> > +static void mark_alternate_complete(const struct ref *ref, void *unused)\n> > +{\n> > +\tmark_complete(NULL, ref->old_sha1, 0, NULL);\n> > +}\n> > +\n> >  static int everything_local(struct ref **refs, int nr_match, char **match)\n> >  {\n> >  \tstruct ref *ref;\n> > @@ -609,6 +614,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)\n> >  \n> >  \tif (!args.depth) {\n> >  \t\tfor_each_ref(mark_complete, NULL);\n> > +\t\tfor_each_alternate_ref(mark_alternate_complete, NULL);\n> >  \t\tif (cutoff)\n> >  \t\t\tmark_recent_complete_commits(cutoff);\n> >  \t}\n> \n> With this patch, then the full test suite passes even if I take out the\n> code that adds the alternate refs to extra_refs.\n\nThat looks sane to me.\n\n> Specifically: (without the above patch) I commented out the call to\n> add_extra_ref() in clone.c:add_one_reference(), then ran t5700 through\n> step 8 then aborted.  insert_one_alternate_ref() was called four times:\n> \n> insert_one_alternate_ref(ccc25a1f9655742174c93f48f616bea8ad0bc6ff)\n> insert_one_alternate_ref(ccc25a1f9655742174c93f48f616bea8ad0bc6ff)\n> insert_one_alternate_ref(5355551c5a927a2b6349505ada2da4bb702c0a49)\n> insert_one_alternate_ref(5355551c5a927a2b6349505ada2da4bb702c0a49)\n> \n> (The duplication here seems strange.)\n\nI think the duplication can be explained. The alternate-refs mechanism\ngets the list of refs by running \"git ls-remote\" on each alternate.\nThe symbolic refs appear as refs in that list. So in t5700, for example,\nI get:\n\n  $ git ls-remote B\n  56a2e291e54b1a92180fe2072152e6aa0919fc5f        HEAD\n  56a2e291e54b1a92180fe2072152e6aa0919fc5f        refs/heads/master\n  3ee812e8486c2474e2b03be5c0b42e33092da069        refs/remotes/origin/HEAD\n  3ee812e8486c2474e2b03be5c0b42e33092da069        refs/remotes/origin/master\n\nBecause you don't actually care about the alternate refs themselves, but\nonly about their sha1 values, you could eliminate duplicates early. The\ncode in receive-pack already does this; fetch-pack should probably do\nthe same (it's not _wrong_ not to, but in some pathological cases, the\nduplicates can make performance worse).\n\n> want 5355551c5a927a2b6349505ada2da4bb702c0a49 multi_ack_detailed\n> side-band-64k thin-pack ofs-delta\n> want 5355551c5a927a2b6349505ada2da4bb702c0a49\n> #E\n> \n> The \"5355551c\" object corresponds to refs/remotes/origin/master in the\n> alternate object store:\n> \n> $ (cd B; git for-each-ref)\n> ccc25a1f9655742174c93f48f616bea8ad0bc6ff commit\trefs/heads/master\n> 5355551c5a927a2b6349505ada2da4bb702c0a49 commit\trefs/remotes/origin/HEAD\n> 5355551c5a927a2b6349505ada2da4bb702c0a49 commit\trefs/remotes/origin/master\n> \n> It seems to me that even in the absence of short-circuiting due to\n> everything_local() returning true, the presence of the alternate refs\n> should be suppressing the \"want\" lines for those references.\n\nYeah, that definitely seems like a bug in find_common.\n\n-Peff\n"},{"id":"184396","messageId":"7v62fepew8.fsf@alter.siamese.dyndns.org","threadId":"29136","inReplyTo":"20120210204457.GD5504@sigill.intra.peff.net","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-02-10T21:17:11Z","receivedAt":"2012-02-10T21:17:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> If everything_local() is trying to check that the references are in the\n>> local repository plus alternates, then it is incorrect that\n>> everything_local() doesn't consider alternate references in its\n>> determination.  My guess is that this is the case, and that something\n>> like the following might be the fix:\n>\n> Junio could answer more authoritatively than I, but I am pretty sure it\n> is the latter. The point is to skip the expensive find_common\n> negotiation if we know that there are no objects to fetch. Thus the\n> \"local\" here is \"do we have them on this side of the git-protocol\n> connection\", not \"do we have them in our non-alternates repository\".\n\nCorrect.  The function is about \"do we need to get any object from the\nother side?\" optimization.\n\nI originally thought to go through the rest of your message, but I\nrealized I can just say \"everything you said is correct and I have nothing\nmore to add.\"\n\nThanks.\n"},{"id":"184435","messageId":"4F360BD5.80006@alum.mit.edu","threadId":"29136","inReplyTo":"7v62fepew8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 28/51] refs.c: rename ref_array -> ref_dir","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2012-02-11T06:33:57Z","receivedAt":"2012-02-11T06:33:57Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/10/2012 10:17 PM, Junio C Hamano wrote:\n> Jeff King <peff@peff.net> writes:\n> \n>>> If everything_local() is trying to check that the references are in the\n>>> local repository plus alternates, then it is incorrect that\n>>> everything_local() doesn't consider alternate references in its\n>>> determination.  My guess is that this is the case, and that something\n>>> like the following might be the fix:\n>>\n>> Junio could answer more authoritatively than I, but I am pretty sure it\n>> is the latter. The point is to skip the expensive find_common\n>> negotiation if we know that there are no objects to fetch. Thus the\n>> \"local\" here is \"do we have them on this side of the git-protocol\n>> connection\", not \"do we have them in our non-alternates repository\".\n> \n> Correct.  The function is about \"do we need to get any object from the\n> other side?\" optimization.\n\nThanks for your feedback.  I have just submitted a patch series [1] that\nattempts to fix this problem.\n\nMichael\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/190488\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\nhttp://softwareswirl.blogspot.com/\n"}]}