From: Petr Baudis Date: Wed, 11 May 2005 00:00:49 +0000 (+0200) Subject: Merge with http://members.cox.net/junkio/git-jc.git X-Git-Tag: v0.99~603 X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=f1a7eb36b017c62d9a007b6b8660bdeec3f94f97;hp=-c;p=git.git Merge with http://members.cox.net/junkio/git-jc.git --- f1a7eb36b017c62d9a007b6b8660bdeec3f94f97 diff --combined cache.h index f8329aedf,0901fae8a..ff229ee52 --- a/cache.h +++ b/cache.h @@@ -30,6 -30,13 +30,13 @@@ #define DTYPE(de) DT_UNKNOWN #endif + /* + * Environment variables transition. + * We accept older names for now but warn. + */ + extern char *gitenv_bc(const char *); + #define gitenv(e) (getenv(e) ? : gitenv_bc(e)) + /* * Basic data structures for the directory cache * @@@ -96,18 -103,18 +103,18 @@@ static inline unsigned int create_ce_mo #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7) -struct cache_entry **active_cache; -unsigned int active_nr, active_alloc, active_cache_changed; +extern struct cache_entry **active_cache; +extern unsigned int active_nr, active_alloc, active_cache_changed; - #define DB_ENVIRONMENT "SHA1_FILE_DIRECTORY" - #define DEFAULT_DB_ENVIRONMENT ".git/objects" - - #define get_object_directory() (getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT) - + #define GIT_DIR_ENVIRONMENT "GIT_DIR" + #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" + #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY" #define INDEX_ENVIRONMENT "GIT_INDEX_FILE" - #define DEFAULT_INDEX_ENVIRONMENT ".git/index" - #define get_index_file() (getenv(INDEX_ENVIRONMENT) ? : DEFAULT_INDEX_ENVIRONMENT) + extern char *get_object_directory(void); + extern char *get_index_file(void); + + #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" #define alloc_nr(x) (((x)+16)*3/2) @@@ -115,7 -122,9 +122,9 @@@ extern int read_cache(void); extern int write_cache(int newfd, struct cache_entry **cache, int entries); extern int cache_name_pos(const char *name, int namelen); - extern int add_cache_entry(struct cache_entry *ce, int ok_to_add); + #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ + #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ + extern int add_cache_entry(struct cache_entry *ce, int option); extern int remove_entry_at(int pos); extern int remove_file_from_cache(char *path); extern int same_name(struct cache_entry *a, struct cache_entry *b); diff --combined sha1_file.c index e21f4b30d,7ca0a386f..942b673dc --- a/sha1_file.c +++ b/sha1_file.c @@@ -7,6 -7,7 +7,7 @@@ * creation etc. */ #include + #include #include "cache.h" #ifndef O_NOATIME @@@ -58,6 -59,38 +59,38 @@@ int get_sha1_file(const char *path, uns return get_sha1_hex(buffer, result); } + static char *git_dir, *git_object_dir, *git_index_file; + static void setup_git_env(void) + { + git_dir = gitenv(GIT_DIR_ENVIRONMENT); + if (!git_dir) + git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; + git_object_dir = gitenv(DB_ENVIRONMENT); + if (!git_object_dir) { + git_object_dir = xmalloc(strlen(git_dir) + 9); + sprintf(git_object_dir, "%s/objects", git_dir); + } + git_index_file = gitenv(INDEX_ENVIRONMENT); + if (!git_index_file) { + git_index_file = xmalloc(strlen(git_dir) + 7); + sprintf(git_index_file, "%s/index", git_dir); + } + } + + char *get_object_directory(void) + { + if (!git_object_dir) + setup_git_env(); + return git_object_dir; + } + + char *get_index_file(void) + { + if (!git_index_file) + setup_git_env(); + return git_index_file; + } + int get_sha1(const char *str, unsigned char *sha1) { static char pathname[PATH_MAX]; @@@ -69,15 -102,16 +102,16 @@@ "refs/snap", NULL }; - const char *gitdir; const char **p; if (!get_sha1_hex(str, sha1)) return 0; - gitdir = ".git"; + if (!git_dir) + setup_git_env(); for (p = prefix; *p; p++) { - snprintf(pathname, sizeof(pathname), "%s/%s/%s", gitdir, *p, str); + snprintf(pathname, sizeof(pathname), "%s/%s/%s", + git_dir, *p, str); if (!get_sha1_file(pathname, sha1)) return 0; } @@@ -100,18 -134,34 +134,34 @@@ char * sha1_to_hex(const unsigned char return buffer; } + static void fill_sha1_path(char *pathbuf, const unsigned char *sha1) + { + int i; + for (i = 0; i < 20; i++) { + static char hex[] = "0123456789abcdef"; + unsigned int val = sha1[i]; + char *pos = pathbuf + i*2 + (i > 0); + *pos++ = hex[val >> 4]; + *pos = hex[val & 0xf]; + } + } + /* * NOTE! This returns a statically allocated buffer, so you have to be * careful about using it. Do a "strdup()" if you need to save the * filename. + * + * Also note that this returns the location for creating. Reading + * SHA1 file can happen from any alternate directory listed in the + * DB_ENVIRONMENT environment variable if it is not found in + * the primary object database. */ char *sha1_file_name(const unsigned char *sha1) { - int i; static char *name, *base; if (!base) { - char *sha1_file_directory = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT; + const char *sha1_file_directory = get_object_directory(); int len = strlen(sha1_file_directory); base = xmalloc(len + 60); memcpy(base, sha1_file_directory, len); @@@ -120,16 -170,94 +170,94 @@@ base[len+3] = '/'; name = base + len + 1; } - for (i = 0; i < 20; i++) { - static char hex[] = "0123456789abcdef"; - unsigned int val = sha1[i]; - char *pos = name + i*2 + (i > 0); - *pos++ = hex[val >> 4]; - *pos = hex[val & 0xf]; - } + fill_sha1_path(name, sha1); return base; } + static struct alternate_object_database { + char *base; + char *name; + } *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. + */ + static void prepare_alt_odb(void) + { + int pass, totlen, i; + const char *cp, *last; + char *op = 0; + const char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; + + /* 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 = 0; + 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; + if (!alt_odb) + 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; + } + int check_sha1_signature(unsigned char *sha1, void *map, unsigned long size, const char *type) { char header[100]; @@@ -145,10 -273,15 +273,15 @@@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { - char *filename = sha1_file_name(sha1); struct stat st; void *map; int fd; + char *filename = find_sha1_file(sha1, &st); + + if (!filename) { + error("cannot map sha1 file %s", sha1_to_hex(sha1)); + return NULL; + } fd = open(filename, O_RDONLY | sha1_file_open_flag); if (fd < 0) { @@@ -167,10 -300,6 +300,6 @@@ /* If it failed once, it will probably fail again. Stop using O_NOATIME */ sha1_file_open_flag = 0; } - if (fstat(fd, &st) < 0) { - close(fd); - return NULL; - } map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (-1 == (int)(long)map) @@@ -315,6 -444,7 +444,7 @@@ int write_sha1_file(char *buf, unsigne } snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory()); + fd = mkstemp(tmpfile); if (fd < 0) { fprintf(stderr, "unable to create temporary sha1 filename %s: %s", tmpfile, strerror(errno)); @@@ -335,7 -465,7 +465,7 @@@ stream.next_in = hdr; stream.avail_in = hdrlen; while (deflate(&stream, 0) == Z_OK) - /* nothing */ + /* nothing */; /* Then the data itself.. */ stream.next_in = buf; @@@ -349,6 -479,7 +479,7 @@@ die("unable to write file"); fchmod(fd, 0444); close(fd); + free(compressed); ret = link(tmpfile, filename); if (ret < 0) { @@@ -442,12 -573,8 +573,8 @@@ int write_sha1_from_fd(const unsigned c int has_sha1_file(const unsigned char *sha1) { - char *filename = sha1_file_name(sha1); struct stat st; - - if (!stat(filename, &st)) - return 1; - return 0; + return !!find_sha1_file(sha1, &st); } int index_fd(unsigned char *sha1, int fd, struct stat *st) diff --combined write-tree.c index d801d7fbe,19b6ee5ea..e3c0a0231 --- a/write-tree.c +++ b/write-tree.c @@@ -30,7 -30,7 +30,7 @@@ static int write_tree(struct cache_entr offset = 0; nr = 0; - do { + while (nr < maxentries) { struct cache_entry *ce = cachep[nr]; const char *pathname = ce->name, *filename, *dirname; int pathlen = ce_namelen(ce), entrylen; @@@ -75,7 -75,7 +75,7 @@@ memcpy(buffer + offset, sha1, 20); offset += 20; nr++; - } while (nr < maxentries); + } write_sha1_file(buffer, offset, "tree", returnsha1); free(buffer); @@@ -84,26 -84,53 +84,53 @@@ int main(int argc, char **argv) { - int i, unmerged; + int i, funny; int entries = read_cache(); unsigned char sha1[20]; - if (entries <= 0) - die("write-tree: no cache contents to write"); + if (entries < 0) + die("write-tree: error reading cache"); /* Verify that the tree is merged */ - unmerged = 0; + funny = 0; for (i = 0; i < entries; i++) { struct cache_entry *ce = active_cache[i]; if (ntohs(ce->ce_flags) & ~CE_NAMEMASK) { - if (++unmerged > 10) { + if (10 < ++funny) { fprintf(stderr, "...\n"); break; } fprintf(stderr, "%s: unmerged (%s)\n", ce->name, sha1_to_hex(ce->sha1)); } } - if (unmerged) + if (funny) + die("write-tree: not able to write tree"); + + /* Also verify that the cache does not have path and path/file + * at the same time. At this point we know the cache has only + * stage 0 entries. + */ + funny = 0; + for (i = 0; i < entries - 1; i++) { + /* path/file always comes after path because of the way + * the cache is sorted. Also path can appear only once, + * which means conflicting one would immediately follow. + */ + const char *this_name = active_cache[i]->name; + const char *next_name = active_cache[i+1]->name; + int this_len = strlen(this_name); + if (this_len < strlen(next_name) && + strncmp(this_name, next_name, this_len) == 0 && + next_name[this_len] == '/') { + if (10 < ++funny) { + fprintf(stderr, "...\n"); + break; + } + fprintf(stderr, "You have both %s and %s\n", + this_name, next_name); + } + } + if (funny) die("write-tree: not able to write tree"); /* Ok, write it out */