From 463acbe1c60fc5009dac9d033df6c2b9c5037a91 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 14 Aug 2006 00:58:19 -0400 Subject: [PATCH] Added tree and commit writing to fast-import. The tree of the current commit can be altered by file_change commands before the commit gets written to the pack. The file changes are rather primitive as they simply allow removal of a tree entry or setting/adding a tree entry. Currently trees and commits aren't being deltafied when written to the pack and branch reloading from the current pack doesn't work, so at most 5 branches can be worked with at any one time. Signed-off-by: Shawn O. Pearce --- fast-import.c | 906 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 729 insertions(+), 177 deletions(-) diff --git a/fast-import.c b/fast-import.c index 98c5d1cbd..4605b7469 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1,9 +1,70 @@ +/* +Format of STDIN stream: + + stream ::= cmd*; + + cmd ::= new_blob + | new_commit + | new_branch + | new_tag + ; + + new_blob ::= 'blob' blob_data; + + new_commit ::= 'comt' ref_name author_committer_msg + file_change* + '0'; + + new_branch ::= 'brch' dst_ref_name src_ref_name; + dst_ref_name ::= ref_name; + src_ref_name ::= ref_name | sha1_exp; + + new_tag ::= 'tagg' ref_name tag_name tagger_msg; + + file_change ::= 'M' path_name hexsha1 + | 'D' path_name + ; + + author_committer_msg ::= len32 + 'author' sp name '<' email '>' ts tz lf + 'committer' sp name '<' email '>' ts tz lf + lf + binary_data; + + tagger_msg ::= len32 + 'tagger' sp name '<' email '>' ts tz lf + lf + binary_data; + + blob_data ::= len32 binary_data; # max len is 2^32-1 + path_name ::= len32 path; # max len is PATH_MAX-1 + ref_name ::= len32 ref; # max len is PATH_MAX-1 + tag_name ::= len32 tag; # max len is PATH_MAX-1 + sha1_exp ::= len32 sha1exp; # max len is PATH_MAX-1 + + len32 ::= # unsigned 32 bit value, native format; + binary_data ::= # file content, not interpreted; + sp ::= # ASCII space character; + lf ::= # ASCII newline (LF) character; + path ::= # GIT style file path, e.g. "a/b/c"; + ref ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT"; + tag ::= # GIT tag name, e.g. "FIREFOX_1_5"; + sha1exp ::= # Any valid GIT SHA1 expression; + hexsha1 ::= # SHA1 in hexadecimal format; + name ::= # valid GIT author/committer name; + email ::= # valid GIT author/committer email; + ts ::= # time since the epoch in seconds, ascii decimal; + tz ::= # GIT style timezone; +*/ + #include "builtin.h" #include "cache.h" #include "object.h" #include "blob.h" +#include "tree.h" #include "delta.h" #include "pack.h" +#include "refs.h" #include "csum-file.h" struct object_entry @@ -13,9 +74,9 @@ struct object_entry unsigned char sha1[20]; }; -struct object_entry_block +struct object_entry_pool { - struct object_entry_block *next_block; + struct object_entry_pool *next_pool; struct object_entry *next_free; struct object_entry *end; struct object_entry entries[FLEX_ARRAY]; /* more */ @@ -29,31 +90,55 @@ struct last_object unsigned char sha1[20]; }; -struct tree; +struct mem_pool +{ + struct mem_pool *next_pool; + char *next_free; + char *end; + char space[FLEX_ARRAY]; /* more */ +}; + +struct atom_str +{ + struct atom_str *next_atom; + int str_len; + char str_dat[FLEX_ARRAY]; /* more */ +}; + +struct tree_content; struct tree_entry { - struct tree *tree; - mode_t mode; + struct tree_content *tree; + struct atom_str* name; + unsigned int mode; unsigned char sha1[20]; - char name[FLEX_ARRAY]; /* more */ }; -struct tree +struct tree_content { - struct last_object last_tree; - unsigned long entry_count; - struct tree_entry **entries; + unsigned int entry_capacity; /* must match avail_tree_content */ + unsigned int entry_count; + struct tree_entry *entries[FLEX_ARRAY]; /* more */ +}; + +struct avail_tree_content +{ + unsigned int entry_capacity; /* must match tree_content */ + struct avail_tree_content *next_avail; }; struct branch { - struct branch *next_branch; - struct tree_entry tree; - unsigned char sha1[20]; + struct branch *table_next_branch; + struct branch *active_next_branch; const char *name; + unsigned long last_commit; + struct tree_entry branch_tree; + unsigned char sha1[20]; }; -/* Stats and misc. counters. */ + +/* Stats and misc. counters */ static int max_depth = 10; static unsigned long alloc_count; static unsigned long branch_count; @@ -62,29 +147,50 @@ static unsigned long duplicate_count; static unsigned long object_count_by_type[9]; static unsigned long duplicate_count_by_type[9]; -/* The .pack file */ +/* Memory pools */ +static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool); +static size_t total_allocd; +static struct mem_pool *mem_pool; + +/* atom management */ +static unsigned int atom_table_sz = 4451; +static unsigned int atom_cnt; +static struct atom_str **atom_table; + +/* The .pack file being generated */ static int pack_fd; static unsigned long pack_offset; static unsigned char pack_sha1[20]; /* Table of objects we've written. */ -struct object_entry_block *blocks; -struct object_entry *object_table[1 << 16]; +static unsigned int object_entry_alloc = 1000; +static struct object_entry_pool *blocks; +static struct object_entry *object_table[1 << 16]; /* Our last blob */ -struct last_object last_blob; +static struct last_object last_blob; + +/* Tree management */ +static unsigned int tree_entry_alloc = 1000; +static void *avail_tree_entry; +static unsigned int avail_tree_table_sz = 100; +static struct avail_tree_content **avail_tree_table; /* Branch data */ -struct branch *branches; -struct branch *current_branch; +static unsigned int max_active_branches = 5; +static unsigned int cur_active_branches; +static unsigned int branch_table_sz = 1039; +static struct branch **branch_table; +static struct branch *active_branches; + static void alloc_objects(int cnt) { - struct object_entry_block *b; + struct object_entry_pool *b; - b = xmalloc(sizeof(struct object_entry_block) + b = xmalloc(sizeof(struct object_entry_pool) + cnt * sizeof(struct object_entry)); - b->next_block = blocks; + b->next_pool = blocks; b->next_free = b->entries; b->end = b->entries + cnt; blocks = b; @@ -96,18 +202,28 @@ static struct object_entry* new_object(unsigned char *sha1) struct object_entry *e; if (blocks->next_free == blocks->end) - alloc_objects(1000); + alloc_objects(object_entry_alloc); e = blocks->next_free++; memcpy(e->sha1, sha1, sizeof(e->sha1)); return e; } +static struct object_entry* find_object(unsigned char *sha1) +{ + unsigned int h = sha1[0] << 8 | sha1[1]; + struct object_entry *e; + for (e = object_table[h]; e; e = e->next) + if (!memcmp(sha1, e->sha1, sizeof(e->sha1))) + return e; + return NULL; +} + static struct object_entry* insert_object(unsigned char *sha1) { unsigned int h = sha1[0] << 8 | sha1[1]; struct object_entry *e = object_table[h]; - struct object_entry *p = 0; + struct object_entry *p = NULL; while (e) { if (!memcmp(sha1, e->sha1, sizeof(e->sha1))) @@ -117,7 +233,7 @@ static struct object_entry* insert_object(unsigned char *sha1) } e = new_object(sha1); - e->next = 0; + e->next = NULL; e->offset = 0; if (p) p->next = e; @@ -126,64 +242,240 @@ static struct object_entry* insert_object(unsigned char *sha1) return e; } -static ssize_t yread(int fd, void *buffer, size_t length) +static unsigned int hc_str(const char *s, size_t len) +{ + unsigned int r = 0; + while (len-- > 0) + r = r * 31 + *s++; + return r; +} + +static void* pool_alloc(size_t len) +{ + struct mem_pool *p; + void *r; + + for (p = mem_pool; p; p = p->next_pool) + if ((p->end - p->next_free >= len)) + break; + + if (!p) { + if (len >= (mem_pool_alloc/2)) { + total_allocd += len; + return xmalloc(len); + } + total_allocd += sizeof(struct mem_pool) + mem_pool_alloc; + p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc); + p->next_pool = mem_pool; + p->next_free = p->space; + p->end = p->next_free + mem_pool_alloc; + mem_pool = p; + } + + r = p->next_free; + p->next_free += len; + return r; +} + +static void* pool_calloc(size_t count, size_t size) +{ + size_t len = count * size; + void *r = pool_alloc(len); + memset(r, 0, len); + return r; +} + +static char* pool_strdup(const char *s) +{ + char *r = pool_alloc(strlen(s) + 1); + strcpy(r, s); + return r; +} + +static struct atom_str* to_atom(const char *s, size_t len) +{ + unsigned int hc = hc_str(s, len) % atom_table_sz; + struct atom_str *c; + + for (c = atom_table[hc]; c; c = c->next_atom) + if (c->str_len == len && !strncmp(s, c->str_dat, len)) + return c; + + c = pool_alloc(sizeof(struct atom_str) + len + 1); + c->str_len = len; + strncpy(c->str_dat, s, len); + c->str_dat[len] = 0; + c->next_atom = atom_table[hc]; + atom_table[hc] = c; + atom_cnt++; + return c; +} + +static struct branch* lookup_branch(const char *name) +{ + unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz; + struct branch *b; + + for (b = branch_table[hc]; b; b = b->table_next_branch) + if (!strcmp(name, b->name)) + return b; + return NULL; +} + +static struct branch* new_branch(const char *name) +{ + unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz; + struct branch* b = lookup_branch(name); + + if (b) + die("Invalid attempt to create duplicate branch: %s", name); + + b = pool_calloc(1, sizeof(struct branch)); + b->name = pool_strdup(name); + b->table_next_branch = branch_table[hc]; + branch_table[hc] = b; + branch_count++; + return b; +} + +static unsigned int hc_entries(unsigned int cnt) +{ + cnt = cnt & 7 ? (cnt / 8) + 1 : cnt / 8; + return cnt < avail_tree_table_sz ? cnt : avail_tree_table_sz - 1; +} + +static struct tree_content* new_tree_content(unsigned int cnt) +{ + struct avail_tree_content *f, *l = NULL; + struct tree_content *t; + unsigned int hc = hc_entries(cnt); + + for (f = avail_tree_table[hc]; f; l = f, f = f->next_avail) + if (f->entry_capacity >= cnt) + break; + + if (f) { + if (l) + l->next_avail = f->next_avail; + else + avail_tree_table[hc] = f->next_avail; + } else { + cnt = cnt & 7 ? ((cnt / 8) + 1) * 8 : cnt; + f = pool_alloc(sizeof(*t) + sizeof(t->entries[0]) * cnt); + f->entry_capacity = cnt; + } + + t = (struct tree_content*)f; + t->entry_count = 0; + return t; +} + +static void release_tree_entry(struct tree_entry *e); +static void release_tree_content(struct tree_content *t) +{ + struct avail_tree_content *f = (struct avail_tree_content*)t; + unsigned int hc = hc_entries(f->entry_capacity); + unsigned int i; + for (i = 0; i < t->entry_count; i++) + release_tree_entry(t->entries[i]); + f->next_avail = avail_tree_table[hc]; + avail_tree_table[hc] = f; +} + +static struct tree_content* grow_tree_content( + struct tree_content *t, + int amt) +{ + struct tree_content *r = new_tree_content(t->entry_count + amt); + r->entry_count = t->entry_count; + memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0])); + release_tree_content(t); + return r; +} + +static struct tree_entry* new_tree_entry() +{ + struct tree_entry *e; + + if (!avail_tree_entry) { + unsigned int n = tree_entry_alloc; + avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry)); + while (n--) { + *((void**)e) = e + 1; + e++; + } + } + + e = avail_tree_entry; + avail_tree_entry = *((void**)e); + return e; +} + +static void release_tree_entry(struct tree_entry *e) +{ + if (e->tree) + release_tree_content(e->tree); + *((void**)e) = avail_tree_entry; + avail_tree_entry = e; +} + +static void yread(int fd, void *buffer, size_t length) { ssize_t ret = 0; while (ret < length) { ssize_t size = xread(fd, (char *) buffer + ret, length - ret); - if (size < 0) { - return size; - } - if (size == 0) { - return ret; - } + if (!size) + die("Read from descriptor %i: end of stream", fd); + if (size < 0) + die("Read from descriptor %i: %s", fd, strerror(errno)); + ret += size; + } +} + +static int optional_read(int fd, void *buffer, size_t length) +{ + ssize_t ret = 0; + while (ret < length) { + ssize_t size = xread(fd, (char *) buffer + ret, length - ret); + if (!size && !ret) + return 1; + if (!size) + die("Read from descriptor %i: end of stream", fd); + if (size < 0) + die("Read from descriptor %i: %s", fd, strerror(errno)); ret += size; } - return ret; + return 0; } -static ssize_t ywrite(int fd, void *buffer, size_t length) +static void ywrite(int fd, void *buffer, size_t length) { ssize_t ret = 0; while (ret < length) { ssize_t size = xwrite(fd, (char *) buffer + ret, length - ret); - if (size < 0) { - return size; - } - if (size == 0) { - return ret; - } + if (!size) + die("Write to descriptor %i: end of file", fd); + if (size < 0) + die("Write to descriptor %i: %s", fd, strerror(errno)); ret += size; } - return ret; } -static const char* read_string() +static const char* read_path() { static char sn[PATH_MAX]; unsigned long slen; - if (yread(0, &slen, 4) != 4) - die("Can't obtain string"); + yread(0, &slen, 4); if (!slen) - return 0; + die("Expected string command parameter, didn't find one"); if (slen > (PATH_MAX - 1)) die("Can't handle excessive string length %lu", slen); - - if (yread(0, sn, slen) != slen) - die("Can't obtain string of length %lu", slen); + yread(0, sn, slen); sn[slen] = 0; return sn; } -static const char* read_required_string() -{ - const char *r = read_string(); - if (!r) - die("Expected string command parameter, didn't find one"); - return r; -} - static unsigned long encode_header( enum object_type type, unsigned long size, @@ -234,13 +526,13 @@ static int store_object( if (e->offset) { duplicate_count++; duplicate_count_by_type[type]++; - return 0; + return 1; } e->offset = pack_offset; object_count++; object_count_by_type[type]++; - if (last->data && last->depth < max_depth) + if (last && last->data && last->depth < max_depth) delta = diff_delta(last->data, last->len, dat, datlen, &deltalen, 0); @@ -255,18 +547,16 @@ static int store_object( s.next_in = delta; s.avail_in = deltalen; hdrlen = encode_header(OBJ_DELTA, deltalen, hdr); - if (ywrite(pack_fd, hdr, hdrlen) != hdrlen) - die("Can't write object header: %s", strerror(errno)); - if (ywrite(pack_fd, last->sha1, sizeof(sha1)) != sizeof(sha1)) - die("Can't write object base: %s", strerror(errno)); + ywrite(pack_fd, hdr, hdrlen); + ywrite(pack_fd, last->sha1, sizeof(sha1)); pack_offset += hdrlen + sizeof(sha1); } else { - last->depth = 0; + if (last) + last->depth = 0; s.next_in = dat; s.avail_in = datlen; hdrlen = encode_header(type, datlen, hdr); - if (ywrite(pack_fd, hdr, hdrlen) != hdrlen) - die("Can't write object header: %s", strerror(errno)); + ywrite(pack_fd, hdr, hdrlen); pack_offset += hdrlen; } @@ -276,18 +566,220 @@ static int store_object( /* nothing */; deflateEnd(&s); - if (ywrite(pack_fd, out, s.total_out) != s.total_out) - die("Failed writing compressed data %s", strerror(errno)); + ywrite(pack_fd, out, s.total_out); pack_offset += s.total_out; free(out); if (delta) free(delta); - if (last->data) - free(last->data); - last->data = dat; - last->len = datlen; - memcpy(last->sha1, sha1, sizeof(sha1)); + if (last) { + if (last->data) + free(last->data); + last->data = dat; + last->len = datlen; + memcpy(last->sha1, sha1, sizeof(sha1)); + } + return 0; +} + +static const char *get_mode(const char *str, unsigned int *modep) +{ + unsigned char c; + unsigned int mode = 0; + + while ((c = *str++) != ' ') { + if (c < '0' || c > '7') + return NULL; + mode = (mode << 3) + (c - '0'); + } + *modep = mode; + return str; +} + +static void load_tree(struct tree_entry *root) +{ + struct object_entry *myoe; + struct tree_content *t; + unsigned long size; + char *buf; + const char *c; + char type[20]; + + root->tree = t = new_tree_content(8); + if (!memcmp(root->sha1, null_sha1, 20)) + return; + + myoe = find_object(root->sha1); + if (myoe) { + die("FIXME"); + } else { + buf = read_sha1_file(root->sha1, type, &size); + if (!buf || strcmp(type, tree_type)) + die("Can't load existing tree %s", sha1_to_hex(root->sha1)); + } + + c = buf; + while (c != (buf + size)) { + struct tree_entry *e = new_tree_entry(); + + if (t->entry_count == t->entry_capacity) + root->tree = t = grow_tree_content(t, 8); + t->entries[t->entry_count++] = e; + + e->tree = NULL; + c = get_mode(c, &e->mode); + if (!c) + die("Corrupt mode in %s", sha1_to_hex(root->sha1)); + e->name = to_atom(c, strlen(c)); + c += e->name->str_len + 1; + memcpy(e->sha1, c, sizeof(e->sha1)); + c += 20; + } + free(buf); +} + +static int tecmp (const void *_a, const void *_b) +{ + struct tree_entry *a = *((struct tree_entry**)_a); + struct tree_entry *b = *((struct tree_entry**)_b); + return base_name_compare( + a->name->str_dat, a->name->str_len, a->mode, + b->name->str_dat, b->name->str_len, b->mode); +} + +static void store_tree(struct tree_entry *root) +{ + struct tree_content *t = root->tree; + unsigned int i; + size_t maxlen; + char *buf, *c; + + if (memcmp(root->sha1, null_sha1, 20)) + return; + + maxlen = 0; + for (i = 0; i < t->entry_count; i++) { + maxlen += t->entries[i]->name->str_len + 34; + if (t->entries[i]->tree) + store_tree(t->entries[i]); + } + + qsort(t->entries, t->entry_count, sizeof(t->entries[0]), tecmp); + buf = c = xmalloc(maxlen); + for (i = 0; i < t->entry_count; i++) { + struct tree_entry *e = t->entries[i]; + c += sprintf(c, "%o", e->mode); + *c++ = ' '; + strcpy(c, e->name->str_dat); + c += e->name->str_len + 1; + memcpy(c, e->sha1, 20); + c += 20; + } + store_object(OBJ_TREE, buf, c - buf, NULL, root->sha1); + free(buf); +} + +static int tree_content_set( + struct tree_entry *root, + const char *p, + const unsigned char *sha1, + const unsigned int mode) +{ + struct tree_content *t = root->tree; + const char *slash1; + unsigned int i, n; + struct tree_entry *e; + + slash1 = strchr(p, '/'); + if (slash1) + n = slash1 - p; + else + n = strlen(p); + + for (i = 0; i < t->entry_count; i++) { + e = t->entries[i]; + if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) { + if (!slash1) { + if (e->mode == mode && !memcmp(e->sha1, sha1, 20)) + return 0; + e->mode = mode; + memcpy(e->sha1, sha1, 20); + if (e->tree) { + release_tree_content(e->tree); + e->tree = NULL; + } + memcpy(root->sha1, null_sha1, 20); + return 1; + } + if (!S_ISDIR(e->mode)) { + e->tree = new_tree_content(8); + e->mode = 040000; + } + if (!e->tree) + load_tree(e); + if (tree_content_set(e, slash1 + 1, sha1, mode)) { + memcpy(root->sha1, null_sha1, 20); + return 1; + } + return 0; + } + } + + if (t->entry_count == t->entry_capacity) + root->tree = t = grow_tree_content(t, 8); + e = new_tree_entry(); + e->name = to_atom(p, n); + t->entries[t->entry_count++] = e; + if (slash1) { + e->tree = new_tree_content(8); + e->mode = 040000; + tree_content_set(e, slash1 + 1, sha1, mode); + } else { + e->tree = NULL; + e->mode = mode; + memcpy(e->sha1, sha1, 20); + } + memcpy(root->sha1, null_sha1, 20); + return 1; +} + +static int tree_content_remove(struct tree_entry *root, const char *p) +{ + struct tree_content *t = root->tree; + const char *slash1; + unsigned int i, n; + struct tree_entry *e; + + slash1 = strchr(p, '/'); + if (slash1) + n = slash1 - p; + else + n = strlen(p); + + for (i = 0; i < t->entry_count; i++) { + e = t->entries[i]; + if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) { + if (!slash1 || !S_ISDIR(e->mode)) + goto del_entry; + if (!e->tree) + load_tree(e); + if (tree_content_remove(e, slash1 + 1)) { + if (!e->tree->entry_count) + goto del_entry; + memcpy(root->sha1, null_sha1, 20); + return 1; + } + return 0; + } + } + return 0; + +del_entry: + for (i++; i < t->entry_count; i++) + t->entries[i-1] = t->entries[i]; + t->entry_count--; + release_tree_entry(e); + memcpy(root->sha1, null_sha1, 20); return 1; } @@ -298,13 +790,9 @@ static void init_pack_header() unsigned long zero = 0; version = htonl(version); - - if (ywrite(pack_fd, (char*)magic, 4) != 4) - die("Can't write pack magic: %s", strerror(errno)); - if (ywrite(pack_fd, &version, 4) != 4) - die("Can't write pack version: %s", strerror(errno)); - if (ywrite(pack_fd, &zero, 4) != 4) - die("Can't write 0 object count: %s", strerror(errno)); + ywrite(pack_fd, (char*)magic, 4); + ywrite(pack_fd, &version, 4); + ywrite(pack_fd, &zero, 4); pack_offset = 4 * 3; } @@ -320,14 +808,12 @@ static void fixup_header_footer() die("Failed seeking to start: %s", strerror(errno)); SHA1_Init(&c); - if (yread(pack_fd, hdr, 8) != 8) - die("Failed reading header: %s", strerror(errno)); + yread(pack_fd, hdr, 8); SHA1_Update(&c, hdr, 8); cnt = htonl(object_count); SHA1_Update(&c, &cnt, 4); - if (ywrite(pack_fd, &cnt, 4) != 4) - die("Failed writing object count: %s", strerror(errno)); + ywrite(pack_fd, &cnt, 4); buf = xmalloc(128 * 1024); for (;;) { @@ -339,8 +825,7 @@ static void fixup_header_footer() free(buf); SHA1_Final(pack_sha1, &c); - if (ywrite(pack_fd, pack_sha1, sizeof(pack_sha1)) != sizeof(pack_sha1)) - die("Failed writing pack checksum: %s", strerror(errno)); + ywrite(pack_fd, pack_sha1, sizeof(pack_sha1)); } static int oecmp (const void *_a, const void *_b) @@ -355,14 +840,14 @@ static void write_index(const char *idx_name) struct sha1file *f; struct object_entry **idx, **c, **last; struct object_entry *e; - struct object_entry_block *o; + struct object_entry_pool *o; unsigned int array[256]; int i; /* Build the sorted table of object IDs. */ idx = xmalloc(object_count * sizeof(struct object_entry*)); c = idx; - for (o = blocks; o; o = o->next_block) + for (o = blocks; o; o = o->next_pool) for (e = o->entries; e != o->next_free; e++) *c++ = e; last = idx + object_count; @@ -393,118 +878,175 @@ static void write_index(const char *idx_name) free(idx); } -static void new_blob() +static void dump_branches() +{ + static const char *msg = "fast-import"; + unsigned int i; + struct branch *b; + struct ref_lock *lock; + + for (i = 0; i < branch_table_sz; i++) { + for (b = branch_table[i]; b; b = b->table_next_branch) { + lock = lock_any_ref_for_update(b->name, NULL, 0); + if (!lock || write_ref_sha1(lock, b->sha1, msg) < 0) + die("Can't write %s", b->name); + } + } +} + +static void cmd_new_blob() { unsigned long datlen; + unsigned char sha1[20]; void *dat; - if (yread(0, &datlen, 4) != 4) - die("Can't obtain blob length"); - + yread(0, &datlen, 4); dat = xmalloc(datlen); - if (yread(0, dat, datlen) != datlen) - die("Con't obtain %lu bytes of blob data", datlen); - - if (!store_object(OBJ_BLOB, dat, datlen, &last_blob, 0)) + yread(0, dat, datlen); + if (store_object(OBJ_BLOB, dat, datlen, &last_blob, sha1)) free(dat); } -static struct branch* lookup_branch(const char *name) +static void unload_one_branch() { - struct branch *b; - for (b = branches; b; b = b->next_branch) { - if (!strcmp(name, b->name)) - return b; + while (cur_active_branches >= max_active_branches) { + unsigned long min_commit = ULONG_MAX; + struct branch *e, *l = NULL, *p = NULL; + + for (e = active_branches; e; e = e->active_next_branch) { + if (e->last_commit < min_commit) { + p = l; + min_commit = e->last_commit; + } + l = e; + } + + if (p) { + e = p->active_next_branch; + p->active_next_branch = e->active_next_branch; + } else { + e = active_branches; + active_branches = e->active_next_branch; + } + e->active_next_branch = NULL; + if (e->branch_tree.tree) { + release_tree_content(e->branch_tree.tree); + e->branch_tree.tree = NULL; + } + cur_active_branches--; } - die("No branch named '%s' has been declared", name); } -static struct tree* deep_copy_tree (struct tree *t) +static void load_branch(struct branch *b) { - struct tree *r = xmalloc(sizeof(struct tree)); - unsigned long i; - - if (t->last_tree.data) { - r->last_tree.data = xmalloc(t->last_tree.len); - r->last_tree.len = t->last_tree.len; - r->last_tree.depth = t->last_tree.depth; - memcpy(r->last_tree.data, t->last_tree.data, t->last_tree.len); - memcpy(r->last_tree.sha1, t->last_tree.sha1, sizeof(t->last_tree.sha1)); - } - - r->entry_count = t->entry_count; - r->entries = xmalloc(t->entry_count * sizeof(struct tree_entry*)); - for (i = 0; i < t->entry_count; i++) { - struct tree_entry *a = t->entries[i]; - struct tree_entry *b; - - b = xmalloc(sizeof(struct tree_entry) + strlen(a->name) + 1); - b->tree = a->tree ? deep_copy_tree(a->tree) : 0; - b->mode = a->mode; - memcpy(b->sha1, a->sha1, sizeof(a->sha1)); - strcpy(b->name, a->name); - r->entries[i] = b; - } - - return r; + load_tree(&b->branch_tree); + b->active_next_branch = active_branches; + active_branches = b; + cur_active_branches++; } -static void store_tree (struct tree_entry *e) +static void file_change_m(struct branch *b) { - struct tree *t = e->tree; - unsigned long maxlen, i; - char *buf, *c; + const char *path = read_path(); + char hexsha1[41]; + unsigned char sha1[20]; - if (memcmp(null_sha1, e->sha1, sizeof(e->sha1))) - return; + yread(0, hexsha1, 40); + hexsha1[40] = 0; - maxlen = t->entry_count * 32; - for (i = 0; i < t->entry_count; i++) - maxlen += strlen(t->entries[i]->name); + if (get_sha1_hex(hexsha1, sha1)) + die("Invalid sha1 %s for %s", hexsha1, path); - buf = c = xmalloc(maxlen); - for (i = 0; i < t->entry_count; i++) { - struct tree_entry *e = t->entries[i]; - c += sprintf(c, "%o %s", e->mode, e->name) + 1; - if (e->tree) - store_tree(e); - memcpy(c, e->sha1, sizeof(e->sha1)); - c += sizeof(e->sha1); - } + tree_content_set(&b->branch_tree, path, sha1, 0100644); +} - if (!store_object(OBJ_TREE, buf, c - buf, &t->last_tree, e->sha1)) - free(buf); +static void file_change_d(struct branch *b) +{ + tree_content_remove(&b->branch_tree, read_path()); } -static void new_branch() +static void cmd_new_commit() { - struct branch *nb = xcalloc(1, sizeof(struct branch)); - const char *source_name; + static const unsigned int max_hdr_len = 94; + const char *name = read_path(); + struct branch *b = lookup_branch(name); + unsigned int acmsglen; + char *body, *c; + + if (!b) + die("Branch not declared: %s", name); + if (!b->branch_tree.tree) { + unload_one_branch(); + load_branch(b); + } - nb->name = strdup(read_required_string()); - source_name = read_string(); - if (source_name) { - struct branch *sb = lookup_branch(source_name); - nb->tree.tree = deep_copy_tree(sb->tree.tree); - memcpy(nb->tree.sha1, sb->tree.sha1, sizeof(sb->tree.sha1)); - memcpy(nb->sha1, sb->sha1, sizeof(sb->sha1)); - } else { - nb->tree.tree = xcalloc(1, sizeof(struct tree)); - nb->tree.tree->entries = xmalloc(8*sizeof(struct tree_entry*)); + /* author_committer_msg */ + yread(0, &acmsglen, 4); + body = xmalloc(acmsglen + max_hdr_len); + c = body + max_hdr_len; + yread(0, c, acmsglen); + + /* file_change* */ + for (;;) { + unsigned char cmd; + yread(0, &cmd, 1); + if (cmd == '0') + break; + else if (cmd == 'M') + file_change_m(b); + else if (cmd == 'D') + file_change_d(b); + else + die("Unsupported file_change: %c", cmd); } - nb->next_branch = branches; - branches = nb; - branch_count++; -} -static void set_branch() -{ - current_branch = lookup_branch(read_required_string()); + if (memcmp(b->sha1, null_sha1, 20)) { + sprintf(c - 48, "parent %s", sha1_to_hex(b->sha1)); + *(c - 1) = '\n'; + c -= 48; + } + store_tree(&b->branch_tree); + sprintf(c - 46, "tree %s", sha1_to_hex(b->branch_tree.sha1)); + *(c - 1) = '\n'; + c -= 46; + + store_object(OBJ_COMMIT, + c, (body + max_hdr_len + acmsglen) - c, + NULL, b->sha1); + free(body); + b->last_commit = object_count_by_type[OBJ_COMMIT]; } -static void commit() +static void cmd_new_branch() { - store_tree(¤t_branch->tree); + struct branch *b = new_branch(read_path()); + const char *base = read_path(); + struct branch *s = lookup_branch(base); + + if (!strcmp(b->name, base)) + die("Can't create a branch from itself: %s", base); + else if (s) { + memcpy(b->sha1, s->sha1, 20); + memcpy(b->branch_tree.sha1, s->branch_tree.sha1, 20); + } + else if (!get_sha1(base, b->sha1)) { + if (!memcmp(b->sha1, null_sha1, 20)) + memcpy(b->branch_tree.sha1, null_sha1, 20); + else { + unsigned long size; + char *buf; + + buf = read_object_with_reference(b->sha1, + type_names[OBJ_COMMIT], &size, b->sha1); + if (!buf || size < 46) + die("Not a valid commit: %s", base); + if (memcmp("tree ", buf, 5) + || get_sha1_hex(buf + 5, b->branch_tree.sha1)) + die("The commit %s is corrupt", sha1_to_hex(b->sha1)); + free(buf); + } + } else + die("Not a SHA1 or branch: %s", base); } int main(int argc, const char **argv) @@ -515,6 +1057,9 @@ int main(int argc, const char **argv) char *idx_name; struct stat sb; + setup_ident(); + git_config(git_default_config); + pack_name = xmalloc(strlen(base_name) + 6); sprintf(pack_name, "%s.pack", base_name); idx_name = xmalloc(strlen(base_name) + 5); @@ -525,17 +1070,21 @@ int main(int argc, const char **argv) die("Can't create %s: %s", pack_name, strerror(errno)); alloc_objects(est_obj_cnt); + + atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); + branch_table = xcalloc(branch_table_sz, sizeof(struct branch*)); + avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*)); + init_pack_header(); for (;;) { unsigned long cmd; - if (yread(0, &cmd, 4) != 4) + if (optional_read(0, &cmd, 4)) break; - switch (cmd) { - case 'blob': new_blob(); break; - case 'newb': new_branch(); break; - case 'setb': set_branch(); break; - case 'comt': commit(); break; + switch (ntohl(cmd)) { + case 'blob': cmd_new_blob(); break; + case 'comt': cmd_new_commit(); break; + case 'brch': cmd_new_branch(); break; default: die("Invalid command %lu", cmd); } @@ -543,6 +1092,7 @@ int main(int argc, const char **argv) fixup_header_footer(); close(pack_fd); write_index(idx_name); + dump_branches(); fprintf(stderr, "%s statistics:\n", argv[0]); fprintf(stderr, "---------------------------------------------------\n"); @@ -553,6 +1103,8 @@ int main(int argc, const char **argv) fprintf(stderr, " commits: %10lu (%10lu duplicates)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT]); fprintf(stderr, " tags : %10lu (%10lu duplicates)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG]); fprintf(stderr, "Total branches: %10lu\n", branch_count); + fprintf(stderr, "Total atoms: %10u\n", atom_cnt); + fprintf(stderr, "Memory pools: %10lu MiB\n", total_allocd/(1024*1024)); fprintf(stderr, "---------------------------------------------------\n"); stat(pack_name, &sb); -- 2.26.2