+struct alternate_object_database *alt_odb;
+
+/*
+ * Prepare alternate object database registry.
+ * alt_odb points at an array of struct alternate_object_database.
+ * This array is terminated with an element that has both its base
+ * and name set to NULL. alt_odb[n] comes from n'th non-empty
+ * element from colon separated ALTERNATE_DB_ENVIRONMENT environment
+ * variable, and its base points at a statically allocated buffer
+ * that contains "/the/directory/corresponding/to/.git/objects/...",
+ * while its name points just after the slash at the end of
+ * ".git/objects/" in the example above, and has enough space to hold
+ * 40-byte hex SHA1, an extra slash for the first level indirection,
+ * and the terminating NUL.
+ * This function allocates the alt_odb array and all the strings
+ * pointed by base fields of the array elements with one xmalloc();
+ * the string pool immediately follows the array.
+ */
+void prepare_alt_odb(void)
+{
+ int pass, totlen, i;
+ const char *cp, *last;
+ char *op = NULL;
+ const char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : "";
+
+ if (alt_odb)
+ return;
+ /* The first pass counts how large an area to allocate to
+ * hold the entire alt_odb structure, including array of
+ * structs and path buffers for them. The second pass fills
+ * the structure and prepares the path buffers for use by
+ * fill_sha1_path().
+ */
+ for (totlen = pass = 0; pass < 2; pass++) {
+ last = alt;
+ i = 0;
+ do {
+ cp = strchr(last, ':') ? : last + strlen(last);
+ if (last != cp) {
+ /* 43 = 40-byte + 2 '/' + terminating NUL */
+ int pfxlen = cp - last;
+ int entlen = pfxlen + 43;
+ if (pass == 0)
+ totlen += entlen;
+ else {
+ alt_odb[i].base = op;
+ alt_odb[i].name = op + pfxlen + 1;
+ memcpy(op, last, pfxlen);
+ op[pfxlen] = op[pfxlen + 3] = '/';
+ op[entlen-1] = 0;
+ op += entlen;
+ }
+ i++;
+ }
+ while (*cp && *cp == ':')
+ cp++;
+ last = cp;
+ } while (*cp);
+ if (pass)
+ break;
+ alt_odb = xmalloc(sizeof(*alt_odb) * (i + 1) + totlen);
+ alt_odb[i].base = alt_odb[i].name = NULL;
+ op = (char*)(&alt_odb[i+1]);
+ }
+}
+
+static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
+{
+ int i;
+ char *name = sha1_file_name(sha1);
+
+ if (!stat(name, st))
+ return name;
+ prepare_alt_odb();
+ for (i = 0; (name = alt_odb[i].name) != NULL; i++) {
+ fill_sha1_path(name, sha1);
+ if (!stat(alt_odb[i].base, st))
+ return alt_odb[i].base;
+ }
+ return NULL;
+}
+
+#define PACK_MAX_SZ (1<<26)
+static int pack_used_ctr;
+static unsigned long pack_mapped;
+struct packed_git *packed_git;
+
+static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
+ void **idx_map_)
+{
+ void *idx_map;
+ unsigned int *index;
+ unsigned long idx_size;
+ int nr, i;
+ int fd = open(path, O_RDONLY);
+ struct stat st;
+ if (fd < 0)
+ return -1;
+ if (fstat(fd, &st)) {
+ close(fd);
+ return -1;
+ }
+ idx_size = st.st_size;
+ idx_map = mmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (idx_map == MAP_FAILED)
+ return -1;
+
+ index = idx_map;
+ *idx_map_ = idx_map;
+ *idx_size_ = idx_size;
+
+ /* check index map */
+ if (idx_size < 4*256 + 20 + 20)
+ return error("index file too small");
+ nr = 0;
+ for (i = 0; i < 256; i++) {
+ unsigned int n = ntohl(index[i]);
+ if (n < nr)
+ return error("non-monotonic index");
+ nr = n;
+ }
+
+ /*
+ * Total size:
+ * - 256 index entries 4 bytes each
+ * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ */
+ if (idx_size != 4*256 + nr * 24 + 20 + 20)
+ return error("wrong index file size");
+
+ return 0;
+}
+
+static int unuse_one_packed_git(void)
+{
+ struct packed_git *p, *lru = NULL;
+
+ for (p = packed_git; p; p = p->next) {
+ if (p->pack_use_cnt || !p->pack_base)
+ continue;
+ if (!lru || p->pack_last_used < lru->pack_last_used)
+ lru = p;
+ }
+ if (!lru)
+ return 0;
+ munmap(lru->pack_base, lru->pack_size);
+ lru->pack_base = NULL;
+ return 1;
+}
+
+void unuse_packed_git(struct packed_git *p)
+{
+ p->pack_use_cnt--;
+}
+
+int use_packed_git(struct packed_git *p)
+{
+ if (!p->pack_base) {
+ int fd;
+ struct stat st;
+ void *map;
+
+ pack_mapped += p->pack_size;
+ while (PACK_MAX_SZ < pack_mapped && unuse_one_packed_git())
+ ; /* nothing */
+ fd = open(p->pack_name, O_RDONLY);
+ if (fd < 0)
+ die("packfile %s cannot be opened", p->pack_name);
+ if (fstat(fd, &st)) {
+ close(fd);
+ die("packfile %s cannot be opened", p->pack_name);
+ }
+ if (st.st_size != p->pack_size)
+ die("packfile %s size mismatch.", p->pack_name);
+ map = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (map == MAP_FAILED)
+ die("packfile %s cannot be mapped.", p->pack_name);
+ p->pack_base = map;
+
+ /* Check if the pack file matches with the index file.
+ * this is cheap.
+ */
+ if (memcmp((char*)(p->index_base) + p->index_size - 40,
+ p->pack_base + p->pack_size - 20, 20))
+ die("packfile %s does not match index.", p->pack_name);
+ }
+ p->pack_last_used = pack_used_ctr++;
+ p->pack_use_cnt++;
+ return 0;
+}
+
+struct packed_git *add_packed_git(char *path, int path_len)
+{
+ struct stat st;
+ struct packed_git *p;
+ unsigned long idx_size;
+ void *idx_map;
+
+ if (check_packed_git_idx(path, &idx_size, &idx_map))
+ return NULL;
+
+ /* do we have a corresponding .pack file? */
+ strcpy(path + path_len - 4, ".pack");
+ if (stat(path, &st) || !S_ISREG(st.st_mode)) {
+ munmap(idx_map, idx_size);
+ return NULL;
+ }
+ /* ok, it looks sane as far as we can check without
+ * actually mapping the pack file.
+ */
+ p = xmalloc(sizeof(*p) + path_len + 2);
+ strcpy(p->pack_name, path);
+ p->index_size = idx_size;
+ p->pack_size = st.st_size;
+ p->index_base = idx_map;
+ p->next = NULL;
+ p->pack_base = NULL;
+ p->pack_last_used = 0;
+ p->pack_use_cnt = 0;
+ return p;
+}
+
+static void prepare_packed_git_one(char *objdir)