Alternate object pool mechanism updates.
authorJunio C Hamano <junkio@cox.net>
Mon, 15 Aug 2005 00:25:57 +0000 (17:25 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 15 Aug 2005 10:00:20 +0000 (03:00 -0700)
It was a mistake to use GIT_ALTERNATE_OBJECT_DIRECTORIES
environment variable to specify what alternate object pools to
look for missing objects when working with an object database.
It is not a property of the process running the git commands,
but a property of the object database that is partial and needs
other object pools to complete the set of objects it lacks.

This patch allows you to have $GIT_OBJECT_DIRECTORY/info/alternates
whose contents is in exactly the same format as the environment
variable, to let an object database name alternate object pools
it depends on.

Signed-off-by: Junio C Hamano <junkio@cox.net>
cache.h
fsck-cache.c
sha1_file.c

diff --git a/cache.h b/cache.h
index efd2a2c595aa6253ec70d0c45289345dd6043003..dbde95a0b687fcc528aa32629bbf6f4d4ba488df 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -278,9 +278,10 @@ struct checkout {
 extern int checkout_entry(struct cache_entry *ce, struct checkout *state);
 
 extern struct alternate_object_database {
-       char *base;
+       struct alternate_object_database *next;
        char *name;
-} *alt_odb;
+       char base[0]; /* more */
+} *alt_odb_list;
 extern void prepare_alt_odb(void);
 
 extern struct packed_git {
index e40c64332f2ca8b01b12d1b3f050165f61836b3b..8091780193b94c9855855656538c64f4a707a212 100644 (file)
@@ -456,13 +456,13 @@ int main(int argc, char **argv)
        fsck_head_link();
        fsck_object_dir(get_object_directory());
        if (check_full) {
-               int j;
+               struct alternate_object_database *alt;
                struct packed_git *p;
                prepare_alt_odb();
-               for (j = 0; alt_odb[j].base; j++) {
+               for (alt = alt_odb_list; alt; alt = alt->next) {
                        char namebuf[PATH_MAX];
-                       int namelen = alt_odb[j].name - alt_odb[j].base;
-                       memcpy(namebuf, alt_odb[j].base, namelen);
+                       int namelen = alt->name - alt->base;
+                       memcpy(namebuf, alt->base, namelen);
                        namebuf[namelen - 1] = 0;
                        fsck_object_dir(namebuf);
                }
index 8d189d4919f83c243c306973285cefb5b55e2f19..2d109f928bef6fe74f2a27af1419d9e82a7b9b33 100644 (file)
@@ -222,84 +222,100 @@ char *sha1_pack_index_name(const unsigned char *sha1)
        return base;
 }
 
-struct alternate_object_database *alt_odb;
+struct alternate_object_database *alt_odb_list;
+static struct alternate_object_database **alt_odb_tail;
 
 /*
  * 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.
+ *
+ * The variable alt_odb_list points at the list of struct
+ * alternate_object_database.  The elements on this list come from
+ * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
+ * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
+ * whose contents is exactly in the same format as that environment
+ * variable.  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.
  */
-void prepare_alt_odb(void)
+static void link_alt_odb_entries(const char *alt, const char *ep)
 {
-       int pass, totlen, i;
        const char *cp, *last;
-       char *op = NULL;
-       const char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : "";
+       struct alternate_object_database *ent;
+
+       last = alt;
+       do {
+               for (cp = last; cp < ep && *cp != ':'; cp++)
+                       ;
+               if (last != cp) {
+                       /* 43 = 40-byte + 2 '/' + terminating NUL */
+                       int pfxlen = cp - last;
+                       int entlen = pfxlen + 43;
+
+                       ent = xmalloc(sizeof(*ent) + entlen);
+                       *alt_odb_tail = ent;
+                       alt_odb_tail = &(ent->next);
+                       ent->next = NULL;
+
+                       memcpy(ent->base, last, pfxlen);
+                       ent->name = ent->base + pfxlen + 1;
+                       ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
+                       ent->base[entlen-1] = 0;
+               }
+               while (cp < ep && *cp == ':')
+                       cp++;
+               last = cp;
+       } while (cp < ep);
+}
 
-       if (alt_odb)
+void prepare_alt_odb(void)
+{
+       char path[PATH_MAX];
+       char *map, *ep;
+       int fd;
+       struct stat st;
+       char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : "";
+
+       sprintf(path, "%s/info/alternates", get_object_directory());
+       if (alt_odb_tail)
+               return;
+       alt_odb_tail = &alt_odb_list;
+       link_alt_odb_entries(alt, alt + strlen(alt));
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return;
+       if (fstat(fd, &st) || (st.st_size == 0)) {
+               close(fd);
                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]);
        }
+       map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       close(fd);
+       if (map == MAP_FAILED)
+               return;
+
+       /* Remove the trailing newline */
+       for (ep = map + st.st_size - 1; map < ep && ep[-1] == '\n'; ep--)
+               ;
+       link_alt_odb_entries(map, ep);
+       munmap(map, st.st_size);
 }
 
 static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
 {
-       int i;
        char *name = sha1_file_name(sha1);
+       struct alternate_object_database *alt;
 
        if (!stat(name, st))
                return name;
        prepare_alt_odb();
-       for (i = 0; (name = alt_odb[i].name) != NULL; i++) {
+       for (alt = alt_odb_list; alt; alt = alt->next) {
+               name = alt->name;
                fill_sha1_path(name, sha1);
-               if (!stat(alt_odb[i].base, st))
-                       return alt_odb[i].base;
+               if (!stat(alt->base, st))
+                       return alt->base;
        }
        return NULL;
 }
@@ -522,18 +538,18 @@ static void prepare_packed_git_one(char *objdir)
 
 void prepare_packed_git(void)
 {
-       int i;
        static int run_once = 0;
+       struct alternate_object_database *alt;
 
-       if (run_once++)
+       if (run_once)
                return;
-
        prepare_packed_git_one(get_object_directory());
        prepare_alt_odb();
-       for (i = 0; alt_odb[i].base != NULL; i++) {
-               alt_odb[i].name[0] = 0;
-               prepare_packed_git_one(alt_odb[i].base);
+       for (alt = alt_odb_list; alt; alt = alt->next) {
+               alt->name[0] = 0;
+               prepare_packed_git_one(alt->base);
        }
+       run_once = 1;
 }
 
 int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)