#include "refs.h" #include "cache.h" #include "rev-cache.h" struct rev_cache **rev_cache; int nr_revs, alloc_revs; struct rev_list_elem *rle_free; #define BATCH_SIZE 512 int find_rev_cache(const unsigned char *sha1) { int lo = 0, hi = nr_revs; while (lo < hi) { int mi = (lo + hi) / 2; struct rev_cache *ri = rev_cache[mi]; int cmp = memcmp(sha1, ri->sha1, 20); if (!cmp) return mi; if (cmp < 0) hi = mi; else lo = mi + 1; } return -lo - 1; } static struct rev_list_elem *alloc_list_elem(void) { struct rev_list_elem *rle; if (!rle_free) { int i; rle = xmalloc(sizeof(*rle) * BATCH_SIZE); for (i = 0; i < BATCH_SIZE - 1; i++) { rle[i].ri = NULL; rle[i].next = &rle[i + 1]; } rle[BATCH_SIZE - 1].ri = NULL; rle[BATCH_SIZE - 1].next = NULL; rle_free = rle; } rle = rle_free; rle_free = rle->next; return rle; } static struct rev_cache *create_rev_cache(const unsigned char *sha1) { struct rev_cache *ri; int pos = find_rev_cache(sha1); if (0 <= pos) return rev_cache[pos]; pos = -pos - 1; if (alloc_revs <= ++nr_revs) { alloc_revs = alloc_nr(alloc_revs); rev_cache = xrealloc(rev_cache, sizeof(ri) * alloc_revs); } if (pos < nr_revs) memmove(rev_cache + pos + 1, rev_cache + pos, (nr_revs - pos - 1) * sizeof(ri)); ri = xcalloc(1, sizeof(*ri)); memcpy(ri->sha1, sha1, 20); rev_cache[pos] = ri; return ri; } static unsigned char last_sha1[20]; static void write_one_rev_cache(FILE *rev_cache_file, struct rev_cache *ri) { unsigned char flag; struct rev_list_elem *rle; if (ri->written) return; if (ri->parsed) { /* We use last_sha1 compression only for the first parent; * otherwise the resulting rev-cache would lose the parent * order information. */ if (ri->parents && !memcmp(ri->parents->ri->sha1, last_sha1, 20)) flag = (ri->num_parents - 1) | 0x80; else flag = ri->num_parents; fwrite(ri->sha1, 20, 1, rev_cache_file); fwrite(&flag, 1, 1, rev_cache_file); for (rle = ri->parents; rle; rle = rle->next) { if (flag & 0x80 && rle == ri->parents) continue; fwrite(rle->ri->sha1, 20, 1, rev_cache_file); } memcpy(last_sha1, ri->sha1, 20); ri->written = 1; } /* recursively write children depth first */ for (rle = ri->children; rle; rle = rle->next) write_one_rev_cache(rev_cache_file, rle->ri); } void write_rev_cache(const char *newpath, const char *oldpath) { /* write the following commit ancestry information in * $GIT_DIR/info/rev-cache. * * The format is: * 20-byte SHA1 (commit ID) * 1-byte flag: * - bit 0-6 records "number of parent commit SHA1s to * follow" (i.e. up to 127 children can be listed). * - when the bit 7 is on, then "the entry immediately * before this entry is one of the parents of this * commit". * N x 20-byte SHA1 (parent commit IDs) */ FILE *rev_cache_file; int i; struct rev_cache *ri; if (!strcmp(newpath, oldpath)) { /* If we are doing it in place */ rev_cache_file = fopen(newpath, "a"); } else { char buf[8096]; size_t sz; FILE *oldfp = fopen(oldpath, "r"); rev_cache_file = fopen(newpath, "w"); if (oldfp) { while (1) { sz = fread(buf, 1, sizeof(buf), oldfp); if (sz == 0) break; fwrite(buf, 1, sz, rev_cache_file); } fclose(oldfp); } } memset(last_sha1, 0, 20); /* Go through available rev_cache structures, starting from * parentless ones first, so that we would get most out of * last_sha1 optimization by the depth first behaviour of * write_one_rev_cache(). */ for (i = 0; i < nr_revs; i++) { ri = rev_cache[i]; if (ri->num_parents) continue; write_one_rev_cache(rev_cache_file, ri); } /* Then the rest */ for (i = 0; i < nr_revs; i++) { ri = rev_cache[i]; write_one_rev_cache(rev_cache_file, ri); } fclose(rev_cache_file); } static void add_parent(struct rev_cache *child, const unsigned char *parent_sha1) { struct rev_cache *parent = create_rev_cache(parent_sha1); struct rev_list_elem *e = alloc_list_elem(); /* Keep the parent list ordered in the same way the commit * object records them. */ e->ri = parent; e->next = NULL; if (!child->parents_tail) child->parents = e; else child->parents_tail->next = e; child->parents_tail = e; child->num_parents++; /* There is no inherent order of the children so we just * LIFO them together. */ e = alloc_list_elem(); e->next = parent->children; parent->children = e; e->ri = child; parent->num_children++; } int read_rev_cache(const char *path, FILE *dumpfile, int dry_run) { unsigned char *map; int fd; struct stat st; unsigned long ofs, len; struct rev_cache *ri = NULL; fd = open(path, O_RDONLY); if (fd < 0) { if (dry_run) return error("cannot open %s", path); if (errno == ENOENT) return 0; return -1; } if (fstat(fd, &st)) { close(fd); return -1; } map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (map == MAP_FAILED) return -1; memset(last_sha1, 0, 20); ofs = 0; len = st.st_size; while (ofs < len) { unsigned char sha1[20]; int flag, cnt, i; if (len < ofs + 21) die("rev-cache too short"); memcpy(sha1, map + ofs, 20); flag = map[ofs + 20]; ofs += 21; cnt = (flag & 0x7f) + ((flag & 0x80) != 0); if (len < ofs + (flag & 0x7f) * 20) die("rev-cache too short to have %d more parents", (flag & 0x7f)); if (dumpfile) fprintf(dumpfile, "%s", sha1_to_hex(sha1)); if (!dry_run) { ri = create_rev_cache(sha1); if (!ri) die("cannot create rev-cache for %s", sha1_to_hex(sha1)); ri->written = ri->parsed = 1; } i = 0; if (flag & 0x80) { if (!dry_run) add_parent(ri, last_sha1); if (dumpfile) fprintf(dumpfile, " %s", sha1_to_hex(last_sha1)); i++; } while (i++ < cnt) { if (!dry_run) add_parent(ri, map + ofs); if (dumpfile) fprintf(dumpfile, " %s", sha1_to_hex(last_sha1)); ofs += 20; } if (dumpfile) fprintf(dumpfile, "\n"); memcpy(last_sha1, sha1, 20); } if (ofs != len) die("rev-cache truncated?"); munmap(map, len); return 0; } int record_rev_cache(const unsigned char *sha1, FILE *dumpfile) { unsigned char parent[20]; char type[20]; unsigned long size, ofs; unsigned int cnt, i; void *buf; struct rev_cache *ri; buf = read_sha1_file(sha1, type, &size); if (!buf) return error("%s: not found", sha1_to_hex(sha1)); if (strcmp(type, "commit")) { free(buf); return error("%s: not a commit but a %s", sha1_to_hex(sha1), type); } ri = create_rev_cache(sha1); if (ri->parsed) return 0; if (dumpfile) fprintf(dumpfile, "commit %s\n", sha1_to_hex(sha1)); cnt = 0; ofs = 46; /* "tree " + hex-sha1 + "\n" */ while (!memcmp(buf + ofs, "parent ", 7) && !get_sha1_hex(buf + ofs + 7, parent)) { ofs += 48; cnt++; } if (cnt * 48 + 46 != ofs) { free(buf); die("internal error in record_rev_cache"); } ri = create_rev_cache(sha1); ri->parsed = 1; for (i = 0; i < cnt; i++) { unsigned char parent_sha1[20]; ofs = 46 + i * 48 + 7; get_sha1_hex(buf + ofs, parent_sha1); add_parent(ri, parent_sha1); record_rev_cache(parent_sha1, dumpfile); } free(buf); return 0; }