#include "commit.h"
#include "tag.h"
#include "tree.h"
-
-#include <sys/time.h>
+#include "progress.h"
static int dry_run, quiet, recover, has_errors;
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
/* We always read in 4kB chunks. */
static unsigned char buffer[4096];
-static unsigned long offset, len;
+static unsigned int offset, len;
+static off_t consumed_bytes;
static SHA_CTX ctx;
/*
* Make sure at least "min" bytes are available in the buffer, and
* return the pointer to the buffer.
*/
-static void * fill(int min)
+static void *fill(int min)
{
if (min <= len)
return buffer + offset;
die("cannot fill %d bytes", min);
if (offset) {
SHA1_Update(&ctx, buffer, offset);
- memcpy(buffer, buffer + offset, len);
+ memmove(buffer, buffer + offset, len);
offset = 0;
}
do {
- int ret = xread(0, buffer + len, sizeof(buffer) - len);
+ ssize_t ret = xread(0, buffer + len, sizeof(buffer) - len);
if (ret <= 0) {
if (!ret)
die("early EOF");
die("used more bytes than were available");
len -= bytes;
offset += bytes;
+
+ /* make sure off_t is sufficiently large not to wrap */
+ if (consumed_bytes > consumed_bytes + bytes)
+ die("pack too large for current definition of off_t");
+ consumed_bytes += bytes;
}
static void *get_data(unsigned long size)
struct delta_info {
unsigned char base_sha1[20];
+ unsigned nr;
+ off_t base_offset;
unsigned long size;
void *delta;
struct delta_info *next;
static struct delta_info *delta_list;
-static void add_delta_to_list(unsigned char *base_sha1, void *delta, unsigned long size)
+static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
+ off_t base_offset,
+ void *delta, unsigned long size)
{
struct delta_info *info = xmalloc(sizeof(*info));
hashcpy(info->base_sha1, base_sha1);
+ info->base_offset = base_offset;
info->size = size;
info->delta = delta;
+ info->nr = nr;
info->next = delta_list;
delta_list = info;
}
-static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size);
+struct obj_info {
+ off_t offset;
+ unsigned char sha1[20];
+};
+
+static struct obj_info *obj_list;
-static void write_object(void *buf, unsigned long size, const char *type)
+static void added_object(unsigned nr, enum object_type type,
+ void *data, unsigned long size);
+
+static void write_object(unsigned nr, enum object_type type,
+ void *buf, unsigned long size)
{
- unsigned char sha1[20];
- if (write_sha1_file(buf, size, type, sha1) < 0)
+ if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
die("failed to write object");
- added_object(sha1, type, buf, size);
+ added_object(nr, type, buf, size);
}
-static void resolve_delta(const char *type,
+static void resolve_delta(unsigned nr, enum object_type type,
void *base, unsigned long base_size,
void *delta, unsigned long delta_size)
{
if (!result)
die("failed to apply delta");
free(delta);
- write_object(result, result_size, type);
+ write_object(nr, type, result, result_size);
free(result);
}
-static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size)
+static void added_object(unsigned nr, enum object_type type,
+ void *data, unsigned long size)
{
struct delta_info **p = &delta_list;
struct delta_info *info;
while ((info = *p) != NULL) {
- if (!hashcmp(info->base_sha1, sha1)) {
+ if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
+ info->base_offset == obj_list[nr].offset) {
*p = info->next;
p = &delta_list;
- resolve_delta(type, data, size, info->delta, info->size);
+ resolve_delta(info->nr, type, data, size,
+ info->delta, info->size);
free(info);
continue;
}
}
}
-static void unpack_non_delta_entry(enum object_type kind, unsigned long size)
+static void unpack_non_delta_entry(enum object_type type, unsigned long size,
+ unsigned nr)
{
void *buf = get_data(size);
- const char *type;
-
- switch (kind) {
- case OBJ_COMMIT: type = commit_type; break;
- case OBJ_TREE: type = tree_type; break;
- case OBJ_BLOB: type = blob_type; break;
- case OBJ_TAG: type = tag_type; break;
- default: die("bad type %d", kind);
- }
+
if (!dry_run && buf)
- write_object(buf, size, type);
+ write_object(nr, type, buf, size);
free(buf);
}
-static void unpack_delta_entry(unsigned long delta_size)
+static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
+ unsigned nr)
{
void *delta_data, *base;
unsigned long base_size;
- char type[20];
unsigned char base_sha1[20];
- hashcpy(base_sha1, fill(20));
- use(20);
+ if (type == OBJ_REF_DELTA) {
+ hashcpy(base_sha1, fill(20));
+ use(20);
+ delta_data = get_data(delta_size);
+ if (dry_run || !delta_data) {
+ free(delta_data);
+ return;
+ }
+ if (!has_sha1_file(base_sha1)) {
+ hashcpy(obj_list[nr].sha1, null_sha1);
+ add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
+ return;
+ }
+ } else {
+ unsigned base_found = 0;
+ unsigned char *pack, c;
+ off_t base_offset;
+ unsigned lo, mid, hi;
- delta_data = get_data(delta_size);
- if (dry_run || !delta_data) {
- free(delta_data);
- return;
- }
+ pack = fill(1);
+ c = *pack;
+ use(1);
+ base_offset = c & 127;
+ while (c & 128) {
+ base_offset += 1;
+ if (!base_offset || MSB(base_offset, 7))
+ die("offset value overflow for delta base object");
+ pack = fill(1);
+ c = *pack;
+ use(1);
+ base_offset = (base_offset << 7) + (c & 127);
+ }
+ base_offset = obj_list[nr].offset - base_offset;
- if (!has_sha1_file(base_sha1)) {
- add_delta_to_list(base_sha1, delta_data, delta_size);
- return;
+ delta_data = get_data(delta_size);
+ if (dry_run || !delta_data) {
+ free(delta_data);
+ return;
+ }
+ lo = 0;
+ hi = nr;
+ while (lo < hi) {
+ mid = (lo + hi)/2;
+ if (base_offset < obj_list[mid].offset) {
+ hi = mid;
+ } else if (base_offset > obj_list[mid].offset) {
+ lo = mid + 1;
+ } else {
+ hashcpy(base_sha1, obj_list[mid].sha1);
+ base_found = !is_null_sha1(base_sha1);
+ break;
+ }
+ }
+ if (!base_found) {
+ /* The delta base object is itself a delta that
+ has not been resolved yet. */
+ hashcpy(obj_list[nr].sha1, null_sha1);
+ add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
+ return;
+ }
}
- base = read_sha1_file(base_sha1, type, &base_size);
+
+ base = read_sha1_file(base_sha1, &type, &base_size);
if (!base) {
error("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));
has_errors = 1;
return;
}
- resolve_delta(type, base, base_size, delta_data, delta_size);
+ resolve_delta(nr, type, base, base_size, delta_data, delta_size);
free(base);
}
-static void unpack_one(unsigned nr, unsigned total)
+static void unpack_one(unsigned nr)
{
unsigned shift;
unsigned char *pack, c;
unsigned long size;
enum object_type type;
+ obj_list[nr].offset = consumed_bytes;
+
pack = fill(1);
c = *pack;
use(1);
shift = 4;
while (c & 0x80) {
pack = fill(1);
- c = *pack++;
+ c = *pack;
use(1);
size += (c & 0x7f) << shift;
shift += 7;
}
- if (!quiet) {
- static unsigned long last_sec;
- static unsigned last_percent;
- struct timeval now;
- unsigned percentage = (nr * 100) / total;
-
- gettimeofday(&now, NULL);
- if (percentage != last_percent || now.tv_sec != last_sec) {
- last_sec = now.tv_sec;
- last_percent = percentage;
- fprintf(stderr, "%4u%% (%u/%u) done\r", percentage, nr, total);
- }
- }
+
switch (type) {
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
- unpack_non_delta_entry(type, size);
+ unpack_non_delta_entry(type, size, nr);
return;
- case OBJ_DELTA:
- unpack_delta_entry(size);
+ case OBJ_REF_DELTA:
+ case OBJ_OFS_DELTA:
+ unpack_delta_entry(type, size, nr);
return;
default:
error("bad object type %d", type);
static void unpack_all(void)
{
int i;
+ struct progress progress;
struct pack_header *hdr = fill(sizeof(struct pack_header));
unsigned nr_objects = ntohl(hdr->hdr_entries);
die("bad pack file");
if (!pack_version_ok(hdr->hdr_version))
die("unknown pack file version %d", ntohl(hdr->hdr_version));
- fprintf(stderr, "Unpacking %d objects\n", nr_objects);
-
use(sizeof(struct pack_header));
- for (i = 0; i < nr_objects; i++)
- unpack_one(i+1, nr_objects);
+
+ if (!quiet)
+ start_progress(&progress, "Unpacking %u objects...", "", nr_objects);
+ obj_list = xmalloc(nr_objects * sizeof(*obj_list));
+ for (i = 0; i < nr_objects; i++) {
+ unpack_one(i);
+ if (!quiet)
+ display_progress(&progress, i + 1);
+ }
+ if (!quiet)
+ stop_progress(&progress);
+
if (delta_list)
die("unresolved deltas left after unpacking");
}
recover = 1;
continue;
}
+ if (!prefixcmp(arg, "--pack_header=")) {
+ struct pack_header *hdr;
+ char *c;
+
+ hdr = (struct pack_header *)buffer;
+ hdr->hdr_signature = htonl(PACK_SIGNATURE);
+ hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
+ if (*c != ',')
+ die("bad %s", arg);
+ hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
+ if (*c)
+ die("bad %s", arg);
+ len = sizeof(*hdr);
+ continue;
+ }
usage(unpack_usage);
}
}
/* All done */
- if (!quiet)
- fprintf(stderr, "\n");
return has_errors;
}