rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
for (i = 0; i < num_ent; i++) {
- long hl = *((long *)(index + 24 * i));
+ uint32_t hl = *((uint32_t *)(index + 24 * i));
rix->revindex[i] = ntohl(hl);
}
/* This knows the pack format -- the 20-byte trailer
struct unpacked {
struct object_entry *entry;
void *data;
+ struct delta_index *index;
};
/*
* more importantly, the bigger file is likely the more recent
* one.
*/
-static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_depth)
+static int try_delta(struct unpacked *trg, struct unpacked *src,
+ struct delta_index *src_index, unsigned max_depth)
{
- struct object_entry *cur_entry = cur->entry;
- struct object_entry *old_entry = old->entry;
- unsigned long size, oldsize, delta_size, sizediff;
- long max_size;
+ struct object_entry *trg_entry = trg->entry;
+ struct object_entry *src_entry = src->entry;
+ unsigned long size, src_size, delta_size, sizediff, max_size;
void *delta_buf;
/* Don't bother doing diffs between different types */
- if (cur_entry->type != old_entry->type)
+ if (trg_entry->type != src_entry->type)
return -1;
/* We do not compute delta to *create* objects we are not
* going to pack.
*/
- if (cur_entry->preferred_base)
+ if (trg_entry->preferred_base)
return -1;
- /* If the current object is at pack edge, take the depth the
+ /*
+ * If the current object is at pack edge, take the depth the
* objects that depend on the current object into account --
* otherwise they would become too deep.
*/
- if (cur_entry->delta_child) {
- if (max_depth <= cur_entry->delta_limit)
+ if (trg_entry->delta_child) {
+ if (max_depth <= trg_entry->delta_limit)
return 0;
- max_depth -= cur_entry->delta_limit;
+ max_depth -= trg_entry->delta_limit;
}
-
- size = cur_entry->size;
- oldsize = old_entry->size;
- sizediff = oldsize > size ? oldsize - size : size - oldsize;
-
- if (size < 50)
- return -1;
- if (old_entry->depth >= max_depth)
+ if (src_entry->depth >= max_depth)
return 0;
- /*
- * NOTE!
- *
- * We always delta from the bigger to the smaller, since that's
- * more space-efficient (deletes don't have to say _what_ they
- * delete).
- */
+ /* Now some size filtering euristics. */
+ size = trg_entry->size;
max_size = size / 2 - 20;
- if (cur_entry->delta)
- max_size = cur_entry->delta_size-1;
+ if (trg_entry->delta)
+ max_size = trg_entry->delta_size-1;
+ src_size = src_entry->size;
+ sizediff = src_size < size ? size - src_size : 0;
if (sizediff >= max_size)
return 0;
- delta_buf = diff_delta(old->data, oldsize,
- cur->data, size, &delta_size, max_size);
+
+ delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size);
if (!delta_buf)
return 0;
- cur_entry->delta = old_entry;
- cur_entry->delta_size = delta_size;
- cur_entry->depth = old_entry->depth + 1;
+
+ trg_entry->delta = src_entry;
+ trg_entry->delta_size = delta_size;
+ trg_entry->depth = src_entry->depth + 1;
free(delta_buf);
- return 0;
+ return 1;
}
static void progress_interval(int signum)
*/
continue;
+ if (entry->size < 50)
+ continue;
+ if (n->index)
+ free_delta_index(n->index);
free(n->data);
n->entry = entry;
n->data = read_sha1_file(entry->sha1, type, &size);
if (size != entry->size)
- die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size);
+ die("object %s inconsistent object length (%lu vs %lu)",
+ sha1_to_hex(entry->sha1), size, entry->size);
+ n->index = create_delta_index(n->data, size);
+ if (!n->index)
+ die("out of memory");
j = window;
while (--j > 0) {
m = array + other_idx;
if (!m->entry)
break;
- if (try_delta(n, m, depth) < 0)
+ if (try_delta(n, m, m->index, depth) < 0)
break;
}
#if 0
if (progress)
fputc('\n', stderr);
- for (i = 0; i < window; ++i)
+ for (i = 0; i < window; ++i) {
+ if (array[i].index)
+ free_delta_index(array[i].index);
free(array[i].data);
+ }
free(array);
}
setup_git_directory();
+ progress = isatty(2);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
usage(pack_usage);
continue;
}
+ if (!strcmp("--progress", arg)) {
+ progress = 1;
+ continue;
+ }
if (!strcmp("-q", arg)) {
progress = 0;
continue;
char * sha1_to_hex(const unsigned char *sha1)
{
- static char buffer[50];
+ static int bufno;
+ static char hexbuffer[4][50];
static const char hex[] = "0123456789abcdef";
- char *buf = buffer;
+ char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
int i;
for (i = 0; i < 20; i++) {
struct alternate_object_database *alt_odb_list;
static struct alternate_object_database **alt_odb_tail;
+static void read_info_alternates(const char * alternates, int depth);
+
/*
* Prepare alternate object database registry.
*
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
- const char *relative_base)
+static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
{
- const char *cp, *last;
- struct alternate_object_database *ent;
+ struct stat st;
const char *objdir = get_object_directory();
+ struct alternate_object_database *ent;
+ struct alternate_object_database *alt;
+ /* 43 = 40-byte + 2 '/' + terminating NUL */
+ int pfxlen = len;
+ int entlen = pfxlen + 43;
int base_len = -1;
+ if (*entry != '/' && relative_base) {
+ /* Relative alt-odb */
+ if (base_len < 0)
+ base_len = strlen(relative_base) + 1;
+ entlen += base_len;
+ pfxlen += base_len;
+ }
+ ent = xmalloc(sizeof(*ent) + entlen);
+
+ if (*entry != '/' && relative_base) {
+ memcpy(ent->base, relative_base, base_len - 1);
+ ent->base[base_len - 1] = '/';
+ memcpy(ent->base + base_len, entry, len);
+ }
+ else
+ memcpy(ent->base, entry, pfxlen);
+
+ ent->name = ent->base + pfxlen + 1;
+ ent->base[pfxlen + 3] = '/';
+ ent->base[pfxlen] = ent->base[entlen-1] = 0;
+
+ /* Detect cases where alternate disappeared */
+ if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
+ error("object directory %s does not exist; "
+ "check .git/objects/info/alternates.",
+ ent->base);
+ free(ent);
+ return -1;
+ }
+
+ /* Prevent the common mistake of listing the same
+ * thing twice, or object directory itself.
+ */
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ if (!memcmp(ent->base, alt->base, pfxlen)) {
+ free(ent);
+ return -1;
+ }
+ }
+ if (!memcmp(ent->base, objdir, pfxlen)) {
+ free(ent);
+ return -1;
+ }
+
+ /* add the alternate entry */
+ *alt_odb_tail = ent;
+ alt_odb_tail = &(ent->next);
+ ent->next = NULL;
+
+ /* recursively add alternates */
+ read_info_alternates(ent->base, depth + 1);
+
+ ent->base[pfxlen] = '/';
+
+ return 0;
+}
+
+static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
+ const char *relative_base, int depth)
+{
+ const char *cp, *last;
+
+ if (depth > 5) {
+ error("%s: ignoring alternate object stores, nesting too deep.",
+ relative_base);
+ return;
+ }
+
last = alt;
while (last < ep) {
cp = last;
last = cp + 1;
continue;
}
- for ( ; cp < ep && *cp != sep; cp++)
- ;
+ while (cp < ep && *cp != sep)
+ cp++;
if (last != cp) {
- struct stat st;
- struct alternate_object_database *alt;
- /* 43 = 40-byte + 2 '/' + terminating NUL */
- int pfxlen = cp - last;
- int entlen = pfxlen + 43;
-
- if (*last != '/' && relative_base) {
- /* Relative alt-odb */
- if (base_len < 0)
- base_len = strlen(relative_base) + 1;
- entlen += base_len;
- pfxlen += base_len;
- }
- ent = xmalloc(sizeof(*ent) + entlen);
-
- if (*last != '/' && relative_base) {
- memcpy(ent->base, relative_base, base_len - 1);
- ent->base[base_len - 1] = '/';
- memcpy(ent->base + base_len,
- last, cp - last);
- }
- else
- memcpy(ent->base, last, pfxlen);
-
- ent->name = ent->base + pfxlen + 1;
- ent->base[pfxlen + 3] = '/';
- ent->base[pfxlen] = ent->base[entlen-1] = 0;
-
- /* Detect cases where alternate disappeared */
- if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
- error("object directory %s does not exist; "
- "check .git/objects/info/alternates.",
- ent->base);
- goto bad;
- }
- ent->base[pfxlen] = '/';
-
- /* Prevent the common mistake of listing the same
- * thing twice, or object directory itself.
- */
- for (alt = alt_odb_list; alt; alt = alt->next)
- if (!memcmp(ent->base, alt->base, pfxlen))
- goto bad;
- if (!memcmp(ent->base, objdir, pfxlen)) {
- bad:
- free(ent);
- }
- else {
- *alt_odb_tail = ent;
- alt_odb_tail = &(ent->next);
- ent->next = NULL;
+ if ((*last != '/') && depth) {
+ error("%s: ignoring relative alternate object store %s",
+ relative_base, last);
+ } else {
+ link_alt_odb_entry(last, cp - last,
+ relative_base, depth);
}
}
while (cp < ep && *cp == sep)
}
}
-void prepare_alt_odb(void)
+static void read_info_alternates(const char * relative_base, int depth)
{
- char path[PATH_MAX];
char *map;
- int fd;
struct stat st;
- char *alt;
-
- alt = getenv(ALTERNATE_DB_ENVIRONMENT);
- if (!alt) alt = "";
-
- if (alt_odb_tail)
- return;
- alt_odb_tail = &alt_odb_list;
- link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL);
+ char path[PATH_MAX];
+ int fd;
- sprintf(path, "%s/info/alternates", get_object_directory());
+ sprintf(path, "%s/info/alternates", relative_base);
fd = open(path, O_RDONLY);
if (fd < 0)
return;
if (map == MAP_FAILED)
return;
- link_alt_odb_entries(map, map + st.st_size, '\n',
- get_object_directory());
+ link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
+
munmap(map, st.st_size);
}
+void prepare_alt_odb(void)
+{
+ char *alt;
+
+ alt = getenv(ALTERNATE_DB_ENVIRONMENT);
+ if (!alt) alt = "";
+
+ if (alt_odb_tail)
+ return;
+ alt_odb_tail = &alt_odb_list;
+ link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
+
+ read_info_alternates(get_object_directory(), 0);
+}
+
static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
{
char *name = sha1_file_name(sha1);
int mi = (lo + hi) / 2;
int cmp = memcmp(index + 24 * mi + 4, sha1, 20);
if (!cmp) {
- e->offset = ntohl(*((int*)(index + 24 * mi)));
+ e->offset = ntohl(*((uint32_t *)(index + 24 * mi)));
memcpy(e->sha1, sha1, 20);
e->p = p;
return 1;