{"thread":{"id":"227","subject":"[PATCH 01-19/19] All of the above combined","startedAt":"2005-04-21T18:39:37Z","lastAt":"2005-04-21T18:39:37Z","messageCount":1,"participants":["Brad Roberts"],"isPatch":true,"patchVersion":1,"patchTotal":19},"messages":[{"id":"1176","messageId":"200504211839.j3LIdbV2027938@gameboy2.puremagic.com","threadId":"227","inReplyTo":null,"subject":"[PATCH 01-19/19] All of the above combined","fromName":"Brad Roberts","fromEmail":"braddr@gameboy2.puremagic.com","sentAt":"2005-04-21T18:39:37Z","receivedAt":"2005-04-21T18:39:37Z","isPatch":true,"sender":{"key":"braddr@gameboy2.puremagic.com","avatar":null},"body":"Make the cache management code behave more like a library.  There are no\nlonger any global variables in read-cache.c.  Nothing ever uses more than\none cache yet, but I can see how it might simplify some of the merge code.\n\nSigned-off-by: Brad Roberts <braddr@puremagic.com>\n---\n \n cache.h          |   36 +++------\n check-files.c    |   12 +--\n checkout-cache.c |   22 +++---\n diff-cache.c     |   36 ++++-----\n merge-cache.c    |   29 ++++---\n read-cache.c     |  200 ++++++++++++++++++++++++++++++++++++-------------------\n read-tree.c      |   71 +++++++++++--------\n show-diff.c      |   19 +++--\n show-files.c     |   27 +++----\n update-cache.c   |   39 +++++-----\n write-tree.c     |   24 +++---\n11 files changed, 292 insertions(+), 223 deletions(-)\n\nIndex: cache.h\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/cache.h  (mode:100644 sha1:828d660ab82bb35a1ca632a2ba4620dc483889bd)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/cache.h  (mode:100644 sha1:d8ade9f4b9bd9b6045f97b4df5bef8356c767d46)\n@@ -17,21 +17,6 @@\n #include <zlib.h>\n \n /*\n- * Basic data structures for the directory cache\n- *\n- * NOTE NOTE NOTE! This is all in the native CPU byte format. It's\n- * not even trying to be portable. It's trying to be efficient. It's\n- * just a cache, after all.\n- */\n-\n-#define CACHE_SIGNATURE 0x44495243\t/* \"DIRC\" */\n-struct cache_header {\n-\tunsigned int hdr_signature;\n-\tunsigned int hdr_version;\n-\tunsigned int hdr_entries;\n-};\n-\n-/*\n  * The \"cache_time\" is just the low 32 bits of the\n  * time. It doesn't matter if it overflows - we only\n  * check it for equality in the 32 bits we save.\n@@ -67,6 +52,8 @@\n #define CE_STAGEMASK (0x3000)\n #define CE_STAGESHIFT 12\n \n+extern int ce_match_stat(struct cache_entry *ce, struct stat *st);\n+\n #define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))\n #define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))\n #define ce_size(ce) cache_entry_size(ce_namelen(ce))\n@@ -78,8 +65,6 @@\n #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)\n \n const char *sha1_file_directory;\n-struct cache_entry **active_cache;\n-unsigned int active_nr, active_alloc;\n \n #define DB_ENVIRONMENT \"SHA1_FILE_DIRECTORY\"\n #define DEFAULT_DB_ENVIRONMENT \".git/objects\"\n@@ -87,12 +72,17 @@\n #define alloc_nr(x) (((x)+16)*3/2)\n \n /* Initialize and use the cache information */\n-extern int read_cache(void);\n-extern int write_cache(int newfd, struct cache_entry **cache, int entries);\n-extern int cache_name_pos(const char *name, int namelen);\n-extern int add_cache_entry(struct cache_entry *ce, int ok_to_add);\n-extern int remove_file_from_cache(char *path);\n-extern int cache_match_stat(struct cache_entry *ce, struct stat *st);\n+extern struct cache *new_cache(void);\n+extern struct cache *read_cache(void);\n+extern int write_cache(struct cache *cache, int newfd);\n+extern void free_cache(struct cache *cache);\n+extern int cache_name_pos(struct cache *cache, const char *name, int namelen);\n+extern int add_cache_entry(struct cache *cache, struct cache_entry *ce, int ok_to_add);\n+extern int remove_file_from_cache(struct cache *cache, char *path);\n+extern int get_num_cache_entries(struct cache *cache);\n+extern struct cache_entry * get_cache_entry(struct cache *cache, int pos);\n+extern void set_cache_entry(struct cache *cache, struct cache_entry *ce, int pos);\n+extern int remove_cache_entry_at(struct cache *cache, int pos);\n \n #define MTIME_CHANGED\t0x0001\n #define CTIME_CHANGED\t0x0002\nIndex: check-files.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/check-files.c  (mode:100644 sha1:7d16691aa9d51b5b4670d5837b3527ee7c7da79c)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/check-files.c  (mode:100644 sha1:4de6d39e4997d29f13261c21eeb378f74b3f8a8f)\n@@ -8,7 +8,7 @@\n  */\n #include \"cache.h\"\n \n-static void check_file(const char *path)\n+static void check_file(struct cache *cache, const char *path)\n {\n \tint fd = open(path, O_RDONLY);\n \tstruct cache_entry *ce;\n@@ -23,15 +23,15 @@\n \t}\n \n \t/* Exists but is not in the cache is not fine */\n-\tpos = cache_name_pos(path, strlen(path));\n+\tpos = cache_name_pos(cache, path, strlen(path));\n \tif (pos < 0)\n \t\tdie(\"preparing to update existing file '%s' not in cache\", path);\n-\tce = active_cache[pos];\n+\tce = get_cache_entry(cache, pos);\n \n \tif (fstat(fd, &st) < 0)\n \t\tdie(\"fstat(%s): %s\", path, strerror(errno));\n \n-\tchanged = cache_match_stat(ce, &st);\n+\tchanged = ce_match_stat(ce, &st);\n \tif (changed)\n \t\tdie(\"preparing to update file '%s' not uptodate in cache\", path);\n }\n@@ -39,9 +39,9 @@\n int main(int argc, char **argv)\n {\n \tint i;\n+\tstruct cache *cache = read_cache();\n \n-\tread_cache();\n \tfor (i = 1; i < argc ; i++)\n-\t\tcheck_file(argv[i]);\n+\t\tcheck_file(cache, argv[i]);\n \treturn 0;\n }\nIndex: checkout-cache.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/checkout-cache.c  (mode:100644 sha1:8bf86016b5d5fd88a52ce694fc59bb9ecb550d22)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/checkout-cache.c  (mode:100644 sha1:2e8c61323a72f6052d8c9ef76a4eef05aa5ac0f9)\n@@ -100,7 +100,7 @@\n \tstruct stat st;\n \n \tif (!stat(ce->name, &st)) {\n-\t\tunsigned changed = cache_match_stat(ce, &st);\n+\t\tunsigned changed = ce_match_stat(ce, &st);\n \t\tif (!changed)\n \t\t\treturn 0;\n \t\tif (!force) {\n@@ -120,23 +120,23 @@\n \treturn write_entry(ce);\n }\n \n-static int checkout_file(const char *name)\n+static int checkout_file(struct cache *cache, const char *name)\n {\n-\tint pos = cache_name_pos(name, strlen(name));\n+\tint pos = cache_name_pos(cache, name, strlen(name));\n \tif (pos < 0) {\n \t\tif (!quiet)\n \t\t\tfprintf(stderr, \"checkout-cache: %s is not in the cache\\n\", name);\n \t\treturn -1;\n \t}\n-\treturn checkout_entry(active_cache[pos]);\n+\treturn checkout_entry(get_cache_entry(cache, pos));\n }\n \n-static int checkout_all(void)\n+static int checkout_all(struct cache *cache)\n {\n \tint i;\n \n-\tfor (i = 0; i < active_nr ; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n+\tfor (i = 0; i < get_num_cache_entries(cache) ; i++) {\n+\t\tstruct cache_entry *ce = get_cache_entry(cache, i);\n \t\tif (ce_stage(ce))\n \t\t\tcontinue;\n \t\tif (checkout_entry(ce) < 0)\n@@ -149,15 +149,15 @@\n {\n \tint i, force_filename = 0;\n \n-\tif (read_cache() < 0) {\n+\tstruct cache * cache = read_cache();\n+\tif (!cache)\n \t\tdie(\"invalid cache\");\n-\t}\n \n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n \t\tif (!force_filename) {\n \t\t\tif (!strcmp(arg, \"-a\")) {\n-\t\t\t\tcheckout_all();\n+\t\t\t\tcheckout_all(cache);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--\")) {\n@@ -173,7 +173,7 @@\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t}\n-\t\tcheckout_file(arg);\n+\t\tcheckout_file(cache, arg);\n \t}\n \treturn 0;\n }\nIndex: diff-cache.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/diff-cache.c  (mode:100644 sha1:fcbc4900d32f4ca24f67bb8f0fe344c6c5642ac9)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/diff-cache.c  (mode:100644 sha1:1d39ca1f79d841e363a4be57871a5c1282d441e1)\n@@ -4,7 +4,7 @@\n static int recursive = 0;\n static int line_termination = '\\n';\n \n-static int diff_cache(void *tree, unsigned long size, struct cache_entry **ac, int entries, const char *base);\n+static int diff_cache(void *tree, unsigned long size, struct cache *cache, int pos, const char *base);\n \n static void update_tree_entry(void **bufp, unsigned long *sizep)\n {\n@@ -82,10 +82,10 @@\n }\n \n static int compare_tree_entry(const char *path1, unsigned int mode1, const unsigned char *sha1,\n-\t\t\t      struct cache_entry **ac, int *entries, const char *base)\n+\t\t\t      struct cache *cache, int *pos, const char *base)\n {\n \tint baselen = strlen(base);\n-\tstruct cache_entry *ce = *ac;\n+\tstruct cache_entry *ce = get_cache_entry(cache, *pos);\n \tconst char *path2 = ce->name + baselen;\n \tunsigned int mode2 = ntohl(ce->ce_mode);\n \tconst unsigned char *sha2 = ce->sha1;\n@@ -107,7 +107,7 @@\n \t\t\tmemcpy(newbase + baselen + pathlen1, \"/\", 2);\n \t\t\tif (!tree || strcmp(type, \"tree\"))\n \t\t\t\tdie(\"unable to read tree object %s\", sha1_to_hex(sha1));\n-\t\t\t*entries = diff_cache(tree, size, ac, *entries, newbase);\n+\t\t\t*pos = diff_cache(tree, size, cache, *pos, newbase);\n \t\t\tfree(newbase);\n \t\t\tfree(tree);\n \t\t\treturn -1;\n@@ -125,7 +125,7 @@\n \t\t\tshow_file(\"-\", path1, mode1, sha1, base);\n \t\t\treturn -1;\n \t\t}\n-\t\tchanged = cache_match_stat(ce, &st);\n+\t\tchanged = ce_match_stat(ce, &st);\n \t\tclose(fd);\n \t\tif (changed) {\n \t\t\tmode2 = st.st_mode;\n@@ -158,7 +158,7 @@\n \treturn 0;\n }\n \n-static int diff_cache(void *tree, unsigned long size, struct cache_entry **ac, int entries, const char *base)\n+static int diff_cache(void *tree, unsigned long size, struct cache *cache, int pos, const char *base)\n {\n \tint baselen = strlen(base);\n \n@@ -167,15 +167,16 @@\n \t\tunsigned int mode;\n \t\tconst char *path;\n \t\tconst unsigned char *sha1;\n-\t\tint left;\n \n \t\t/*\n \t\t * No entries in the cache (with this base)?\n \t\t * Output the tree contents.\n \t\t */\n-\t\tif (!entries || ce_namelen(ce = *ac) < baselen || memcmp(ce->name, base, baselen)) {\n+\t\tif ((pos == get_num_cache_entries(cache)) ||\n+\t\t    ce_namelen(ce = get_cache_entry(cache, pos)) < baselen ||\n+\t\t    memcmp(ce->name, base, baselen)) {\n \t\t\tif (!size)\n-\t\t\t\treturn entries;\n+\t\t\t\treturn pos;\n \t\t\tsha1 = extract(tree, size, &path, &mode);\n \t\t\tshow_file(\"-\", path, mode, sha1, base);\n \t\t\tupdate_tree_entry(&tree, &size);\n@@ -187,27 +188,20 @@\n \t\t */\n \t\tif (!size) {\n \t\t\tshow_file(\"+\", ce->name, ntohl(ce->ce_mode), ce->sha1, \"\");\n-\t\t\tac++;\n-\t\t\tentries--;\n+\t\t\tpos++;\n \t\t\tcontinue;\n \t\t}\n \n \t\tsha1 = extract(tree, size, &path, &mode);\n-\t\tleft = entries;\n-\t\tswitch (compare_tree_entry(path, mode, sha1, ac, &left, base)) {\n+\t\tswitch (compare_tree_entry(path, mode, sha1, cache, &pos, base)) {\n \t\tcase -1:\n \t\t\tupdate_tree_entry(&tree, &size);\n-\t\t\tif (left < entries) {\n-\t\t\t\tac += (entries - left);\n-\t\t\t\tentries = left;\n-\t\t\t}\n \t\t\tcontinue;\n \t\tcase 0:\n \t\t\tupdate_tree_entry(&tree, &size);\n \t\t\t/* Fallthrough */\n \t\tcase 1:\n-\t\t\tac++;\n-\t\t\tentries--;\n+\t\t\tpos++;\n \t\t\tcontinue;\n \t\t}\n \t\tdie(\"diff-cache: internal error\");\n@@ -221,8 +215,8 @@\n \tvoid *tree;\n \tunsigned long size;\n \tchar type[20];\n+\tstruct cache *cache = read_cache();\n \n-\tread_cache();\n \twhile (argc > 2) {\n \t\tchar *arg = argv[1];\n \t\targv++;\n@@ -263,5 +257,5 @@\n \tif (strcmp(type, \"tree\"))\n \t\tdie(\"bad tree object %s (%s)\", sha1_to_hex(tree_sha1), type);\n \n-\treturn diff_cache(tree, size, active_cache, active_nr, \"\");\n+\treturn diff_cache(tree, size, cache, 0, \"\");\n }\nIndex: merge-cache.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/merge-cache.c  (mode:100644 sha1:35a0d588178aa5371399458b1a15519cffd645b8)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/merge-cache.c  (mode:100644 sha1:440d4d6e98d1387c5055ba5539b829e7557d9d4a)\n@@ -26,11 +26,11 @@\n \t\terr = 1;\n }\n \n-static int merge_entry(int pos, const char *path)\n+static int merge_entry(struct cache *cache, int pos, const char *path)\n {\n \tint found;\n \t\n-\tif (pos >= active_nr)\n+\tif (pos >= get_num_cache_entries(cache))\n \t\tdie(\"merge-cache: %s not in the cache\", path);\n \targuments[0] = pgm;\n \targuments[1] = \"\";\n@@ -40,7 +40,7 @@\n \tfound = 0;\n \tdo {\n \t\tstatic char hexbuf[4][60];\n-\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\tstruct cache_entry *ce = get_cache_entry(cache, pos);\n \t\tint stage = ce_stage(ce);\n \n \t\tif (strcmp(ce->name, path))\n@@ -48,44 +48,45 @@\n \t\tfound++;\n \t\tstrcpy(hexbuf[stage], sha1_to_hex(ce->sha1));\n \t\targuments[stage] = hexbuf[stage];\n-\t} while (++pos < active_nr);\n+\t} while (++pos < get_num_cache_entries(cache));\n \tif (!found)\n \t\tdie(\"merge-cache: %s not in the cache\", path);\n \trun_program();\n \treturn found;\n }\n \n-static void merge_file(const char *path)\n+static void merge_file(struct cache *cache, const char *path)\n {\n-\tint pos = cache_name_pos(path, strlen(path));\n+\tint pos = cache_name_pos(cache, path, strlen(path));\n \n \t/*\n \t * If it already exists in the cache as stage0, it's\n \t * already merged and there is nothing to do.\n \t */\n \tif (pos < 0)\n-\t\tmerge_entry(-pos-1, path);\n+\t\tmerge_entry(cache, -pos-1, path);\n }\n \n-static void merge_all(void)\n+static void merge_all(struct cache *cache)\n {\n \tint i;\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n+\tfor (i = 0; i < get_num_cache_entries(cache); i++) {\n+\t\tstruct cache_entry *ce = get_cache_entry(cache, i);\n \t\tif (!ce_stage(ce))\n \t\t\tcontinue;\n-\t\ti += merge_entry(i, ce->name)-1;\n+\t\ti += merge_entry(cache, i, ce->name)-1;\n \t}\n }\n \n int main(int argc, char **argv)\n {\n \tint i, force_file = 0;\n+\tstruct cache *cache;\n \n \tif (argc < 3)\n \t\tusage(\"merge-cache <merge-program> (-a | <filename>*)\");\n \n-\tread_cache();\n+\tcache = read_cache();\n \n \tpgm = argv[1];\n \tfor (i = 2; i < argc; i++) {\n@@ -96,12 +97,12 @@\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"-a\")) {\n-\t\t\t\tmerge_all();\n+\t\t\t\tmerge_all(cache);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tdie(\"merge-cache: unknown option %s\", arg);\n \t\t}\n-\t\tmerge_file(arg);\n+\t\tmerge_file(cache, arg);\n \t}\n \tif (err)\n \t\tdie(\"merge program failed\");\nIndex: read-cache.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/read-cache.c  (mode:100644 sha1:2f6a4aa18d48865db80459a3459ac4384b0b16c8)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/read-cache.c  (mode:100644 sha1:7084fcdf771ddc5bfac38b8778a5904d779de3a4)\n@@ -6,10 +6,42 @@\n #include <stdarg.h>\n #include \"cache.h\"\n \n-struct cache_entry **active_cache = NULL;\n-unsigned int active_nr = 0, active_alloc = 0;\n+/*\n+ * Basic data structures for the directory cache\n+ */\n \n-int cache_match_stat(struct cache_entry *ce, struct stat *st)\n+#define CACHE_SIGNATURE 0x44495243\t/* \"DIRC\" */\n+struct cache_header {\n+\tunsigned int hdr_signature;\n+\tunsigned int hdr_version;\n+\tunsigned int hdr_entries;\n+};\n+\n+struct mmap_holder {\n+\tvoid * ptr;\n+\tsize_t size;\n+};\n+\n+struct cache {\n+\tstruct mmap_holder   map;\n+\tstruct cache_header *header;\n+\tstruct cache_entry **entries;\n+\tunsigned int num_entries;\n+\tunsigned int allocated_entries;\n+};\n+\n+struct cache * new_cache()\n+{\n+\treturn (struct cache*)calloc(1, sizeof(struct cache));\n+}\n+\n+void free_cache(struct cache *cache)\n+{\n+\tmunmap(cache->map.ptr, cache->map.size);\n+\tfree(cache);\n+}\n+\n+int ce_match_stat(struct cache_entry *ce, struct stat *st)\n {\n \tunsigned int changed = 0;\n \n@@ -65,15 +97,15 @@\n \treturn 0;\n }\n \n-int cache_name_pos(const char *name, int namelen)\n+int cache_name_pos(struct cache *cache, const char *name, int namelen)\n {\n \tint first, last;\n \n \tfirst = 0;\n-\tlast = active_nr;\n+\tlast = cache->num_entries;\n \twhile (last > first) {\n \t\tint next = (last + first) >> 1;\n-\t\tstruct cache_entry *ce = active_cache[next];\n+\t\tstruct cache_entry *ce = cache->entries[next];\n \t\tint cmp = cache_name_compare(name, namelen, ce->name, htons(ce->ce_flags));\n \t\tif (!cmp)\n \t\t\treturn next;\n@@ -87,20 +119,20 @@\n }\n \n /* Remove entry, return true if there are more entries to go.. */\n-static int remove_entry_at(int pos)\n+int remove_cache_entry_at(struct cache *cache, int pos)\n {\n-\tactive_nr--;\n-\tif (pos >= active_nr)\n+\tcache->num_entries--;\n+\tif (pos >= cache->num_entries)\n \t\treturn 0;\n-\tmemmove(active_cache + pos, active_cache + pos + 1, (active_nr - pos) * sizeof(struct cache_entry *));\n+\tmemmove(cache->entries + pos, cache->entries + pos + 1, (cache->num_entries - pos) * sizeof(struct cache_entry *));\n \treturn 1;\n }\n \n-int remove_file_from_cache(char *path)\n+int remove_file_from_cache(struct cache *cache, char *path)\n {\n-\tint pos = cache_name_pos(path, strlen(path));\n+\tint pos = cache_name_pos(cache, path, strlen(path));\n \tif (pos >= 0)\n-\t\tremove_entry_at(pos);\n+\t\tremove_cache_entry_at(cache, pos);\n \treturn 0;\n }\n \n@@ -110,15 +142,33 @@\n \treturn ce_namelen(b) == len && !memcmp(a->name, b->name, len);\n }\n \n-int add_cache_entry(struct cache_entry *ce, int ok_to_add)\n+int get_num_cache_entries(struct cache *cache)\n+{\n+\treturn cache->num_entries;\n+}\n+\n+struct cache_entry * get_cache_entry(struct cache *cache, int pos)\n+{\n+\treturn cache->entries[pos];\n+}\n+\n+void set_cache_entry(struct cache *cache, struct cache_entry *ce, int pos)\n+{\n+\t/* You can NOT just free cache->entries[i] here, since it\n+\t * might not be necessarily malloc()ed but can also come\n+\t * from mmap(). */\n+\tcache->entries[pos] = ce;\n+}\n+\n+int add_cache_entry(struct cache *cache, struct cache_entry *ce, int ok_to_add)\n {\n \tint pos;\n \n-\tpos = cache_name_pos(ce->name, htons(ce->ce_flags));\n+\tpos = cache_name_pos(cache, ce->name, htons(ce->ce_flags));\n \n \t/* existing match? Just replace it */\n \tif (pos >= 0) {\n-\t\tactive_cache[pos] = ce;\n+\t\tcache->entries[pos] = ce;\n \t\treturn 0;\n \t}\n \tpos = -pos-1;\n@@ -127,10 +177,10 @@\n \t * Inserting a merged entry (\"stage 0\") into the index\n \t * will always replace all non-merged entries..\n \t */\n-\tif (pos < active_nr && ce_stage(ce) == 0) {\n-\t\twhile (same_name(active_cache[pos], ce)) {\n+\tif (pos < cache->num_entries && ce_stage(ce) == 0) {\n+\t\twhile (same_name(cache->entries[pos], ce)) {\n \t\t\tok_to_add = 1;\n-\t\t\tif (!remove_entry_at(pos))\n+\t\t\tif (!remove_cache_entry_at(cache, pos))\n \t\t\t\tbreak;\n \t\t}\n \t}\n@@ -139,16 +189,16 @@\n \t\treturn -1;\n \n \t/* Make sure the array is big enough .. */\n-\tif (active_nr == active_alloc) {\n-\t\tactive_alloc = alloc_nr(active_alloc);\n-\t\tactive_cache = realloc(active_cache, active_alloc * sizeof(struct cache_entry *));\n+\tif (cache->num_entries == cache->allocated_entries) {\n+\t\tcache->allocated_entries = alloc_nr(cache->allocated_entries);\n+\t\tcache->entries = realloc(cache->entries, cache->allocated_entries * sizeof(struct cache_entry *));\n \t}\n \n \t/* Add it in.. */\n-\tactive_nr++;\n-\tif (active_nr > pos)\n-\t\tmemmove(active_cache + pos + 1, active_cache + pos, (active_nr - pos - 1) * sizeof(ce));\n-\tactive_cache[pos] = ce;\n+\tcache->num_entries++;\n+\tif (cache->num_entries > pos)\n+\t\tmemmove(cache->entries + pos + 1, cache->entries + pos, (cache->num_entries - pos - 1) * sizeof(ce));\n+\tcache->entries[pos] = ce;\n \treturn 0;\n }\n \n@@ -169,59 +219,75 @@\n \treturn 0;\n }\n \n-int read_cache(void)\n+struct cache *read_cache(void)\n {\n \tint fd, i;\n \tstruct stat st;\n-\tunsigned long size, offset;\n-\tvoid *map;\n-\tstruct cache_header *hdr;\n-\n-\terrno = EBUSY;\n-\tif (active_cache)\n-\t\treturn error(\"more than one cachefile\");\n+\tunsigned long offset;\n+\tstruct cache *cache;\n+\n+\tcache = new_cache();\n+\tif (!cache) {\n+\t\terrno = ENOMEM;\n+\t\terror(\"unable to allocate cache\");\n+\t\treturn NULL;\n+\t}\n+\n \terrno = ENOENT;\n \tsha1_file_directory = getenv(DB_ENVIRONMENT);\n \tif (!sha1_file_directory)\n \t\tsha1_file_directory = DEFAULT_DB_ENVIRONMENT;\n-\tif (access(sha1_file_directory, X_OK) < 0)\n-\t\treturn error(\"no access to SHA1 file directory\");\n+\tif (access(sha1_file_directory, X_OK) < 0) {\n+\t\terror(\"no access to SHA1 file directory\");\n+\t\tfree(cache);\n+\t\treturn NULL;\n+\t}\n \tfd = open(\".git/index\", O_RDONLY);\n-\tif (fd < 0)\n-\t\treturn (errno == ENOENT) ? 0 : error(\"open failed\");\n+\tif (fd < 0) {\n+\t\t/* TODO: Why special case this?  If we can't get to the data, what's the point? */\n+\t\tif (errno == ENOENT)\n+\t\t\treturn cache;\n+\t\telse {\n+\t\t\terror(\"open failed\");\n+\t\t\tfree(cache);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n \n-\tsize = 0; /* avoid gcc warning */\n-\tmap = (void *)-1;\n+\tcache->map.size = 0; /* avoid gcc warning */\n+\tcache->map.ptr = (void *)-1;\n \tif (!fstat(fd, &st)) {\n-\t\tsize = st.st_size;\n+\t\tcache->map.size = st.st_size;\n \t\terrno = EINVAL;\n-\t\tif (size >= sizeof(struct cache_header) + 20)\n-\t\t\tmap = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n+\t\tif (cache->map.size >= sizeof(struct cache_header) + 20)\n+\t\t\tcache->map.ptr = mmap(NULL, cache->map.size, PROT_READ, MAP_PRIVATE, fd, 0);\n \t}\n \tclose(fd);\n-\tif (-1 == (int)(long)map)\n-\t\treturn error(\"mmap failed\");\n+\tif (-1 == (int)(long)cache->map.ptr) {\n+\t\terror(\"mmap failed\");\n+\t\tfree(cache);\n+\t\treturn NULL;\n+\t}\n \n-\thdr = map;\n-\tif (verify_hdr(hdr, size) < 0)\n-\t\tgoto unmap;\n-\n-\tactive_nr = ntohl(hdr->hdr_entries);\n-\tactive_alloc = alloc_nr(active_nr);\n-\tactive_cache = calloc(active_alloc, sizeof(struct cache_entry *));\n-\n-\toffset = sizeof(*hdr);\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = map + offset;\n-\t\toffset = offset + ce_size(ce);\n-\t\tactive_cache[i] = ce;\n+\tcache->header = cache->map.ptr;\n+\tif (verify_hdr(cache->header, cache->map.size) < 0) {\n+\t\tfree_cache(cache);\n+\t\terrno = EINVAL;\n+\t\terror(\"verify header failed\");\n+\t\treturn NULL;\n \t}\n-\treturn active_nr;\n \n-unmap:\n-\tmunmap(map, size);\n-\terrno = EINVAL;\n-\treturn error(\"verify header failed\");\n+\tcache->num_entries = ntohl(cache->header->hdr_entries);\n+\tcache->allocated_entries = alloc_nr(cache->num_entries);\n+\tcache->entries = calloc(cache->allocated_entries, sizeof(struct cache_entry *));\n+\n+\toffset = sizeof(*cache->header);\n+\tfor (i = 0; i < cache->num_entries; i++) {\n+\t\tstruct cache_entry *ce = cache->map.ptr + offset;\n+\t\toffset = offset + ce_size(ce);\n+\t\tcache->entries[i] = ce;\n+\t}\n+\treturn cache;\n }\n \n #define WRITE_BUFFER_SIZE 8192\n@@ -267,7 +333,7 @@\n \treturn 0;\n }\n \n-int write_cache(int newfd, struct cache_entry **cache, int entries)\n+int write_cache(struct cache *cache, int newfd)\n {\n \tSHA_CTX c;\n \tstruct cache_header hdr;\n@@ -275,14 +341,14 @@\n \n \thdr.hdr_signature = htonl(CACHE_SIGNATURE);\n \thdr.hdr_version = htonl(2);\n-\thdr.hdr_entries = htonl(entries);\n+\thdr.hdr_entries = htonl(cache->num_entries);\n \n \tSHA1_Init(&c);\n \tif (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)\n \t\treturn -1;\n \n-\tfor (i = 0; i < entries; i++) {\n-\t\tstruct cache_entry *ce = cache[i];\n+\tfor (i = 0; i < cache->num_entries; i++) {\n+\t\tstruct cache_entry *ce = cache->entries[i];\n \t\tif (ce_write(&c, newfd, ce, ce_size(ce)) < 0)\n \t\t\treturn -1;\n \t}\nIndex: read-tree.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/read-tree.c  (mode:100644 sha1:620f3f74eb56366fca8be4d28d7b04875c0fa90c)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/read-tree.c  (mode:100644 sha1:a683b7f60e58514d36218a7b2c2ace2d3ec9f984)\n@@ -7,7 +7,7 @@\n \n static int stage = 0;\n \n-static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode)\n+static int read_one_entry(struct cache *cache, unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode)\n {\n \tint len = strlen(pathname);\n \tunsigned int size = cache_entry_size(baselen + len);\n@@ -20,10 +20,10 @@\n \tmemcpy(ce->name, base, baselen);\n \tmemcpy(ce->name + baselen, pathname, len+1);\n \tmemcpy(ce->sha1, sha1, 20);\n-\treturn add_cache_entry(ce, 1);\n+\treturn add_cache_entry(cache, ce, 1);\n }\n \n-static int read_tree(unsigned char *sha1, const char *base, int baselen)\n+static int read_tree(struct cache *cache, unsigned char *sha1, const char *base, int baselen)\n {\n \tvoid *buffer, *bufptr;\n \tunsigned long size;\n@@ -57,7 +57,7 @@\n \t\t\tmemcpy(newbase, base, baselen);\n \t\t\tmemcpy(newbase + baselen, path, pathlen);\n \t\t\tnewbase[baselen + pathlen] = '/';\n-\t\t\tretval = read_tree(sha1, newbase, baselen + pathlen + 1);\n+\t\t\tretval = read_tree(cache, sha1, newbase, baselen + pathlen + 1);\n \t\t\tfree(newbase);\n \t\t\tif (retval) {\n \t\t\t\tfree(buffer);\n@@ -65,7 +65,7 @@\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\tif (read_one_entry(sha1, base, baselen, path, mode) < 0) {\n+\t\tif (read_one_entry(cache, sha1, base, baselen, path, mode) < 0) {\n \t\t\tfree(buffer);\n \t\t\treturn -1;\n \t\t}\n@@ -146,26 +146,30 @@\n \treturn NULL;\n }\n \n-static void trivially_merge_cache(struct cache_entry **src, int nr)\n+/* rather than doing the 'right' thing of deleting entries as we merge,\n+ * walk dst through the cache, overwriting entries as we go and at the\n+ * end truncate the size of the cache */\n+static void trivially_merge_cache(struct cache *cache)\n {\n \tstatic struct cache_entry null_entry;\n-\tstruct cache_entry **dst = src;\n \tstruct cache_entry *old = &null_entry;\n+\tint src = 0, dst = 0, nr = get_num_cache_entries(cache);\n \n-\twhile (nr) {\n+\twhile (src < nr) {\n \t\tstruct cache_entry *ce, *result;\n \n-\t\tce = src[0];\n+\t\tce = get_cache_entry(cache, src);\n \n \t\t/* We throw away original cache entries except for the stat information */\n \t\tif (!ce_stage(ce)) {\n \t\t\told = ce;\n \t\t\tsrc++;\n-\t\t\tnr--;\n-\t\t\tactive_nr--;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (nr > 2 && (result = merge_entries(ce, src[1], src[2])) != NULL) {\n+\t\tif ((src < (nr - 2)) &&\n+\t\t    (result = merge_entries(ce,\n+\t\t\t\t\t    get_cache_entry(cache, src + 1),\n+\t\t\t\t\t    get_cache_entry(cache, src + 2))) != NULL) {\n \t\t\t/*\n \t\t\t * See if we can re-use the old CE directly?\n \t\t\t * That way we get the uptodate stat info.\n@@ -175,40 +179,46 @@\n \t\t\tce = result;\n \t\t\tce->ce_flags &= ~htons(CE_STAGEMASK);\n \t\t\tsrc += 2;\n-\t\t\tnr -= 2;\n-\t\t\tactive_nr -= 2;\n \t\t}\n-\t\t*dst++ = ce;\n+\t\tset_cache_entry(cache, ce, dst);\n+\t\tdst++;\n \t\tsrc++;\n+\t}\n+\t/* this could be replaced by a truncate api */\n+\twhile (nr > dst) {\n \t\tnr--;\n+\t\tremove_cache_entry_at(cache, nr);\n \t}\n }\n \n-static void merge_stat_info(struct cache_entry **src, int nr)\n+static void merge_stat_info(struct cache *cache)\n {\n \tstatic struct cache_entry null_entry;\n-\tstruct cache_entry **dst = src;\n \tstruct cache_entry *old = &null_entry;\n+\tint src = 0, dst = 0, nr = get_num_cache_entries(cache);\n \n-\twhile (nr) {\n+\twhile (src < nr) {\n \t\tstruct cache_entry *ce;\n \n-\t\tce = src[0];\n+\t\tce = get_cache_entry(cache, src);\n \n \t\t/* We throw away original cache entries except for the stat information */\n \t\tif (!ce_stage(ce)) {\n \t\t\told = ce;\n \t\t\tsrc++;\n-\t\t\tnr--;\n-\t\t\tactive_nr--;\n \t\t\tcontinue;\n \t\t}\n \t\tif (path_matches(ce, old) && same(ce, old))\n \t\t\t*ce = *old;\n \t\tce->ce_flags &= ~htons(CE_STAGEMASK);\n-\t\t*dst++ = ce;\n+\t\tset_cache_entry(cache, ce, dst);\n+\t\tdst++;\n \t\tsrc++;\n+\t}\n+\t/* this could be replaced by a truncate api */\n+\twhile (nr > dst) {\n \t\tnr--;\n+\t\tremove_cache_entry_at(cache, nr);\n \t}\n }\n \n@@ -216,6 +226,7 @@\n {\n \tint i, newfd, merge;\n \tunsigned char sha1[20];\n+\tstruct cache *cache = NULL;\n \n \tnewfd = open(\".git/index.lock\", O_RDWR | O_CREAT | O_EXCL, 0600);\n \tif (newfd < 0)\n@@ -232,9 +243,9 @@\n \t\t\tint i;\n \t\t\tif (stage)\n \t\t\t\tusage(\"-m needs to come first\");\n-\t\t\tread_cache();\n-\t\t\tfor (i = 0; i < active_nr; i++) {\n-\t\t\t\tif (ce_stage(active_cache[i]))\n+\t\t\tcache = read_cache();\n+\t\t\tfor (i = 0; i < get_num_cache_entries(cache); i++) {\n+\t\t\t\tif (ce_stage(get_cache_entry(cache, i)))\n \t\t\t\t\tusage(\"you need to resolve your current index first\");\n \t\t\t}\n \t\t\tstage = 1;\n@@ -245,23 +256,25 @@\n \t\t\tusage(\"read-tree [-m] <sha1>\");\n \t\tif (stage > 3)\n \t\t\tusage(\"can't merge more than two trees\");\n-\t\tif (read_tree(sha1, \"\", 0) < 0)\n+\t\tif (!cache)\n+\t\t\tcache = new_cache();\n+\t\tif (read_tree(cache, sha1, \"\", 0) < 0)\n \t\t\tdie(\"failed to unpack tree object %s\", arg);\n \t\tstage++;\n \t}\n \tif (merge) {\n \t\tswitch (stage) {\n \t\tcase 4:\t/* Three-way merge */\n-\t\t\ttrivially_merge_cache(active_cache, active_nr);\n+\t\t\ttrivially_merge_cache(cache);\n \t\t\tbreak;\n \t\tcase 2:\t/* Just read a tree, merge with old cache contents */\n-\t\t\tmerge_stat_info(active_cache, active_nr);\n+\t\t\tmerge_stat_info(cache);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tdie(\"just how do you expect me to merge %d trees?\", stage-1);\n \t\t}\n \t}\n-\tif (write_cache(newfd, active_cache, active_nr) ||\n+\tif (write_cache(cache, newfd) ||\n \t    rename(\".git/index.lock\", \".git/index\"))\n \t\tdie(\"unable to write new index file\");\n \tremove_lock = 0;\nIndex: show-diff.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/show-diff.c  (mode:100644 sha1:da364e26e28823f951a6be1b686a458575f28ea1)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/show-diff.c  (mode:100644 sha1:d61bf6dea8106599c25ac5071743b351f6e000ce)\n@@ -126,9 +126,16 @@\n \tint silent_on_nonexisting_files = 0;\n \tint machine_readable = 0;\n \tint reverse = 0;\n-\tint entries = read_cache();\n+\tint entries;\n \tint matched = 0;\n \tint i;\n+\tstruct cache *cache = read_cache();\n+\n+\tentries = get_num_cache_entries(cache);\n+\tif (entries < 0) {\n+\t\tperror(\"read_cache\");\n+\t\texit(1);\n+\t}\n \n \twhile (1 < argc && argv[1][0] == '-') {\n \t\tif  (!strcmp(argv[1], \"-R\"))\n@@ -147,14 +154,10 @@\n \t/* At this point, if argc == 1, then we are doing everything.\n \t * Otherwise argv[1] .. argv[argc-1] have the explicit paths.\n \t */\n-\tif (entries < 0) {\n-\t\tperror(\"read_cache\");\n-\t\texit(1);\n-\t}\n \tprepare_diff_cmd();\n \tfor (i = 0; i < entries; i++) {\n \t\tstruct stat st;\n-\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tstruct cache_entry *ce = get_cache_entry(cache, i);\n \t\tint changed;\n \t\tunsigned long size;\n \t\tchar type[20];\n@@ -172,7 +175,7 @@\n \t\t\t\tprintf(\"%s: Unmerged\\n\",\n \t\t\t\t       ce->name);\n \t\t\twhile (i < entries &&\n-\t\t\t       !strcmp(ce->name, active_cache[i]->name))\n+\t\t\t       !strcmp(ce->name, get_cache_entry(cache, i)->name))\n \t\t\t\ti++;\n \t\t\ti--; /* compensate for loop control increments */\n \t\t\tcontinue;\n@@ -190,7 +193,7 @@\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\tchanged = cache_match_stat(ce, &st);\n+\t\tchanged = ce_match_stat(ce, &st);\n \t\tif (!changed)\n \t\t\tcontinue;\n \t\tif (!machine_readable)\nIndex: show-files.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/show-files.c  (mode:100644 sha1:0b49ca051de413e7182445dd8fb9144125716974)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/show-files.c  (mode:100644 sha1:c8dc21d0dd3f5db3f7016323859c58449968d800)\n@@ -27,11 +27,11 @@\n static int nr_dir;\n static int dir_alloc;\n \n-static void add_name(const char *pathname, int len)\n+static void add_name(struct cache *cache, const char *pathname, int len)\n {\n \tchar *name;\n \n-\tif (cache_name_pos(pathname, len) >= 0)\n+\tif (cache_name_pos(cache, pathname, len) >= 0)\n \t\treturn;\n \n \tif (nr_dir == dir_alloc) {\n@@ -51,7 +51,7 @@\n  * Also, we currently ignore all names starting with a dot.\n  * That likely will not change.\n  */\n-static void read_directory(const char *path, const char *base, int baselen)\n+static void read_directory(struct cache *cache, const char *path, const char *base, int baselen)\n {\n \tDIR *dir = opendir(path);\n \n@@ -82,12 +82,12 @@\n \t\t\t\t/* fallthrough */\n \t\t\tcase DT_DIR:\n \t\t\t\tmemcpy(fullname + baselen + len, \"/\", 2);\n-\t\t\t\tread_directory(fullname, fullname, baselen + len + 1);\n+\t\t\t\tread_directory(cache, fullname, fullname, baselen + len + 1);\n \t\t\t\tcontinue;\n \t\t\tcase DT_REG:\n \t\t\t\tbreak;\n \t\t\t}\n-\t\t\tadd_name(fullname, baselen + len);\n+\t\t\tadd_name(cache, fullname, baselen + len);\n \t\t}\n \t\tclosedir(dir);\n \t}\n@@ -102,13 +102,13 @@\n \treturn cache_name_compare(n1, l1, n2, l2);\n }\n \n-static void show_files(void)\n+static void show_files(struct cache *cache)\n {\n \tint i;\n \n \t/* For cached/deleted files we don't need to even do the readdir */\n \tif (show_others | show_ignored) {\n-\t\tread_directory(\".\", \"\", 0);\n+\t\tread_directory(cache, \".\", \"\", 0);\n \t\tqsort(dir, nr_dir, sizeof(char *), cmp_name);\n \t}\n \tif (show_others) {\n@@ -116,8 +116,8 @@\n \t\t\tprintf(\"%s%s%c\", tag_other, dir[i], line_terminator);\n \t}\n \tif (show_cached | show_stage) {\n-\t\tfor (i = 0; i < active_nr; i++) {\n-\t\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tfor (i = 0; i < get_num_cache_entries(cache); i++) {\n+\t\t\tstruct cache_entry *ce = get_cache_entry(cache, i);\n \t\t\tif (show_unmerged && !ce_stage(ce))\n \t\t\t\tcontinue;\n \t\t\tif (!show_stage)\n@@ -136,8 +136,8 @@\n \t\t}\n \t}\n \tif (show_deleted) {\n-\t\tfor (i = 0; i < active_nr; i++) {\n-\t\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tfor (i = 0; i < get_num_cache_entries(cache); i++) {\n+\t\t\tstruct cache_entry *ce = get_cache_entry(cache, i);\n \t\t\tstruct stat st;\n \t\t\tif (!stat(ce->name, &st))\n \t\t\t\tcontinue;\n@@ -152,6 +152,7 @@\n int main(int argc, char **argv)\n {\n \tint i;\n+\tstruct cache *cache;\n \n \tfor (i = 1; i < argc; i++) {\n \t\tchar *arg = argv[i];\n@@ -202,7 +203,7 @@\n \tif (!(show_stage | show_deleted | show_others | show_ignored | show_unmerged))\n \t\tshow_cached = 1;\n \n-\tread_cache();\n-\tshow_files();\n+\tcache = read_cache();\n+\tshow_files(cache);\n \treturn 0;\n }\nIndex: update-cache.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/update-cache.c  (mode:100644 sha1:a09883541c745c76413c62109a80f40df4b7a7fb)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/update-cache.c  (mode:100644 sha1:565638acd2380023ea69e82316a7ab77d95d8ee7)\n@@ -85,7 +85,7 @@\n \tce->ce_size = htonl(st->st_size);\n }\n \n-static int add_file_to_cache(char *path)\n+static int add_file_to_cache(struct cache *cache, char *path)\n {\n \tint size, namelen;\n \tstruct cache_entry *ce;\n@@ -96,7 +96,7 @@\n \tif (fd < 0) {\n \t\tif (errno == ENOENT) {\n \t\t\tif (allow_remove)\n-\t\t\t\treturn remove_file_from_cache(path);\n+\t\t\t\treturn remove_file_from_cache(cache, path);\n \t\t}\n \t\treturn -1;\n \t}\n@@ -117,7 +117,7 @@\n \t\tfree(ce);\n \t\treturn -1;\n \t}\n-\tif (add_cache_entry(ce, allow_add)) {\n+\tif (add_cache_entry(cache, ce, allow_add)) {\n \t\tfree(ce);\n \t\treturn -1;\n \t}\n@@ -179,7 +179,7 @@\n \tif (stat(ce->name, &st) < 0)\n \t\treturn NULL;\n \n-\tchanged = cache_match_stat(ce, &st);\n+\tchanged = ce_match_stat(ce, &st);\n \tif (!changed)\n \t\treturn ce;\n \n@@ -200,17 +200,17 @@\n \treturn updated;\n }\n \n-static void refresh_cache(void)\n+static void refresh_cache(struct cache *cache)\n {\n \tint i;\n \n-\tfor (i = 0; i < active_nr; i++) {\n+\tfor (i = 0; i < get_num_cache_entries(cache); i++) {\n \t\tstruct cache_entry *ce, *new;\n-\t\tce = active_cache[i];\n+\t\tce = get_cache_entry(cache, i);\n \t\tif (ce_stage(ce)) {\n \t\t\tprintf(\"%s: needs merge\\n\", ce->name);\n-\t\t\twhile ((i < active_nr) &&\n-\t\t\t       ! strcmp(active_cache[i]->name, ce->name))\n+\t\t\twhile ((i < get_num_cache_entries(cache)) &&\n+\t\t\t       ! strcmp(get_cache_entry(cache, i)->name, ce->name))\n \t\t\t\ti++;\n \t\t\ti--;\n \t\t\tcontinue;\n@@ -221,10 +221,7 @@\n \t\t\tprintf(\"%s: needs update\\n\", ce->name);\n \t\t\tcontinue;\n \t\t}\n-\t\t/* You can NOT just free active_cache[i] here, since it\n-\t\t * might not be necessarily malloc()ed but can also come\n-\t\t * from mmap(). */\n-\t\tactive_cache[i] = new;\n+\t\tset_cache_entry(cache, new, i);\n \t}\n }\n \n@@ -256,7 +253,7 @@\n \t}\n }\n \n-static int add_cacheinfo(char *arg1, char *arg2, char *arg3)\n+static int add_cacheinfo(struct cache *cache, char *arg1, char *arg2, char *arg3)\n {\n \tint size, len;\n \tunsigned int mode;\n@@ -279,7 +276,7 @@\n \tmemcpy(ce->name, arg3, len);\n \tce->ce_flags = htons(len);\n \tce->ce_mode = create_ce_mode(mode);\n-\treturn add_cache_entry(ce, allow_add);\n+\treturn add_cache_entry(cache, ce, allow_add);\n }\n \n static int remove_lock = 0;\n@@ -294,6 +291,7 @@\n {\n \tint i, newfd, entries;\n \tint allow_options = 1;\n+\tstruct cache *cache = NULL;\n \n \tnewfd = open(\".git/index.lock\", O_RDWR | O_CREAT | O_EXCL, 0600);\n \tif (newfd < 0)\n@@ -302,7 +300,8 @@\n \tatexit(remove_lock_file);\n \tremove_lock = 1;\n \n-\tentries = read_cache();\n+\tcache = read_cache();\n+\tentries = get_num_cache_entries(cache);\n \tif (entries < 0)\n \t\tdie(\"cache corrupted\");\n \n@@ -323,11 +322,11 @@\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--refresh\")) {\n-\t\t\t\trefresh_cache();\n+\t\t\t\trefresh_cache(cache);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(path, \"--cacheinfo\")) {\n-\t\t\t\tif (i+3 >= argc || add_cacheinfo(argv[i+1], argv[i+2], argv[i+3]))\n+\t\t\t\tif (i+3 >= argc || add_cacheinfo(cache, argv[i+1], argv[i+2], argv[i+3]))\n \t\t\t\t\tdie(\"update-cache: --cacheinfo <mode> <sha1> <path>\");\n \t\t\t\ti += 3;\n \t\t\t\tcontinue;\n@@ -338,10 +337,10 @@\n \t\t\tfprintf(stderr, \"Ignoring path %s\\n\", argv[i]);\n \t\t\tcontinue;\n \t\t}\n-\t\tif (add_file_to_cache(path))\n+\t\tif (add_file_to_cache(cache, path))\n \t\t\tdie(\"Unable to add %s to database\", path);\n \t}\n-\tif (write_cache(newfd, active_cache, active_nr) ||\n+\tif (write_cache(cache, newfd) ||\n \t    rename(\".git/index.lock\", \".git/index\"))\n \t\tdie(\"Unable to write new cachefile\");\n \nIndex: write-tree.c\n===================================================================\n--- c0260bfb82da04aeff4e598ced5295d6ae2e262d/write-tree.c  (mode:100644 sha1:827809dbddbff6dd8cf842641f6db5ad2f3ae07a)\n+++ 38adb888a4c1adfe083f24d4ec51018e0b5a8335/write-tree.c  (mode:100644 sha1:ad148b422ffa85d7ecf515e55538c1afa13f17d6)\n@@ -29,7 +29,7 @@\n \n #define ORIG_OFFSET (40)\t/* Enough space to add the header of \"tree <size>\\0\" */\n \n-static int write_tree(struct cache_entry **cachep, int maxentries, const char *base, int baselen, unsigned char *returnsha1)\n+static int write_tree(struct cache *cache, int start_pos, const char *base, int baselen, unsigned char *returnsha1)\n {\n \tunsigned char subdir_sha1[20];\n \tunsigned long size, offset;\n@@ -43,7 +43,7 @@\n \n \tnr = 0;\n \tdo {\n-\t\tstruct cache_entry *ce = cachep[nr];\n+\t\tstruct cache_entry *ce = get_cache_entry(cache, start_pos + nr);\n \t\tconst char *pathname = ce->name, *filename, *dirname;\n \t\tint pathlen = ce_namelen(ce), entrylen;\n \t\tunsigned char *sha1;\n@@ -53,16 +53,13 @@\n \t\tif (baselen >= pathlen || memcmp(base, pathname, baselen))\n \t\t\tbreak;\n \n-\t\tsha1 = ce->sha1;\n-\t\tmode = ntohl(ce->ce_mode);\n-\n \t\t/* Do we have _further_ subdirectories? */\n \t\tfilename = pathname + baselen;\n \t\tdirname = strchr(filename, '/');\n \t\tif (dirname) {\n \t\t\tint subdir_written;\n \n-\t\t\tsubdir_written = write_tree(cachep + nr, maxentries - nr, pathname, dirname-pathname+1, subdir_sha1);\n+\t\t\tsubdir_written = write_tree(cache, start_pos + nr, pathname, dirname-pathname+1, subdir_sha1);\n \t\t\tnr += subdir_written;\n \n \t\t\t/* Now we need to write out the directory entry into this tree.. */\n@@ -72,6 +69,9 @@\n \t\t\t/* ..but the directory entry doesn't count towards the total count */\n \t\t\tnr--;\n \t\t\tsha1 = subdir_sha1;\n+\t\t} else {\n+\t\t\tsha1 = ce->sha1;\n+\t\t\tmode = ntohl(ce->ce_mode);\n \t\t}\n \n \t\tif (check_valid_sha1(sha1) < 0)\n@@ -87,7 +87,7 @@\n \t\tmemcpy(buffer + offset, sha1, 20);\n \t\toffset += 20;\n \t\tnr++;\n-\t} while (nr < maxentries);\n+\t} while ((start_pos + nr) < get_num_cache_entries(cache));\n \n \ti = prepend_integer(buffer, offset - ORIG_OFFSET, ORIG_OFFSET);\n \ti -= 5;\n@@ -101,16 +101,18 @@\n int main(int argc, char **argv)\n {\n \tint i, unmerged;\n-\tint entries = read_cache();\n \tunsigned char sha1[20];\n+\tstruct cache *cache = read_cache();\n+\tint entries;\n \n-\tif (entries <= 0)\n+\tif (!cache)\n \t\tdie(\"write-tree: no cache contents to write\");\n+\tentries = get_num_cache_entries(cache);\n \n \t/* Verify that the tree is merged */\n \tunmerged = 0;\n \tfor (i = 0; i < entries; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n+\t\tstruct cache_entry *ce = get_cache_entry(cache, i);\n \t\tif (ntohs(ce->ce_flags) & ~CE_NAMEMASK) {\n \t\t\tif (++unmerged > 10) {\n \t\t\t\tfprintf(stderr, \"...\\n\");\n@@ -123,7 +125,7 @@\n \t\tdie(\"write-tree: not able to write tree\");\n \n \t/* Ok, write it out */\n-\tif (write_tree(active_cache, entries, \"\", 0, sha1) != entries)\n+\tif (write_tree(cache, 0, \"\", 0, sha1) != entries)\n \t\tdie(\"write-tree: internal error\");\n \tprintf(\"%s\\n\", sha1_to_hex(sha1));\n \treturn 0;\n"}]}