From: Matthias Kestenholz Date: Thu, 3 Aug 2006 15:24:37 +0000 (+0200) Subject: Make git-unpack-objects a builtin X-Git-Tag: v1.4.3-rc1~278^2~7 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=6441363079d85cf17aee21b8925e9745c8abda16;p=git.git Make git-unpack-objects a builtin Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano --- diff --git a/Makefile b/Makefile index 4fd62bcf6..b108a8aa7 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ PROGRAMS = \ git-send-pack$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-unpack-file$X \ - git-unpack-objects$X git-update-server-info$X \ + git-update-server-info$X \ git-upload-pack$X git-verify-pack$X \ git-symbolic-ref$X \ git-pack-redundant$X git-var$X \ @@ -198,7 +198,8 @@ BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \ git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \ git-fmt-merge-msg$X git-prune$X git-mv$X git-prune-packed$X \ - git-repo-config$X git-name-rev$X git-pack-objects$X + git-repo-config$X git-name-rev$X git-pack-objects$X \ + git-unpack-objects$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -256,7 +257,7 @@ BUILTIN_OBJS = \ builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \ builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \ builtin-mv.o builtin-prune-packed.o builtin-repo-config.o \ - builtin-name-rev.o builtin-pack-objects.o + builtin-name-rev.o builtin-pack-objects.o builtin-unpack-objects.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c new file mode 100644 index 000000000..09d264d9f --- /dev/null +++ b/builtin-unpack-objects.c @@ -0,0 +1,310 @@ +#include "builtin.h" +#include "cache.h" +#include "object.h" +#include "delta.h" +#include "pack.h" +#include "blob.h" +#include "commit.h" +#include "tag.h" +#include "tree.h" + +#include + +static int dry_run, quiet; +static const char unpack_usage[] = "git-unpack-objects [-n] [-q] < pack-file"; + +/* We always read in 4kB chunks. */ +static unsigned char buffer[4096]; +static unsigned long offset, len, eof; +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) +{ + if (min <= len) + return buffer + offset; + if (eof) + die("unable to fill input"); + if (min > sizeof(buffer)) + die("cannot fill %d bytes", min); + if (offset) { + SHA1_Update(&ctx, buffer, offset); + memcpy(buffer, buffer + offset, len); + offset = 0; + } + do { + int ret = xread(0, buffer + len, sizeof(buffer) - len); + if (ret <= 0) { + if (!ret) + die("early EOF"); + die("read error on input: %s", strerror(errno)); + } + len += ret; + } while (len < min); + return buffer; +} + +static void use(int bytes) +{ + if (bytes > len) + die("used more bytes than were available"); + len -= bytes; + offset += bytes; +} + +static void *get_data(unsigned long size) +{ + z_stream stream; + void *buf = xmalloc(size); + + memset(&stream, 0, sizeof(stream)); + + stream.next_out = buf; + stream.avail_out = size; + stream.next_in = fill(1); + stream.avail_in = len; + inflateInit(&stream); + + for (;;) { + int ret = inflate(&stream, 0); + use(len - stream.avail_in); + if (stream.total_out == size && ret == Z_STREAM_END) + break; + if (ret != Z_OK) + die("inflate returned %d\n", ret); + stream.next_in = fill(1); + stream.avail_in = len; + } + inflateEnd(&stream); + return buf; +} + +struct delta_info { + unsigned char base_sha1[20]; + 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) +{ + struct delta_info *info = xmalloc(sizeof(*info)); + + memcpy(info->base_sha1, base_sha1, 20); + info->size = size; + info->delta = delta; + info->next = delta_list; + delta_list = info; +} + +static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size); + +static void write_object(void *buf, unsigned long size, const char *type) +{ + unsigned char sha1[20]; + if (write_sha1_file(buf, size, type, sha1) < 0) + die("failed to write object"); + added_object(sha1, type, buf, size); +} + +static int resolve_delta(const char *type, + void *base, unsigned long base_size, + void *delta, unsigned long delta_size) +{ + void *result; + unsigned long result_size; + + result = patch_delta(base, base_size, + delta, delta_size, + &result_size); + if (!result) + die("failed to apply delta"); + free(delta); + write_object(result, result_size, type); + free(result); + return 0; +} + +static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size) +{ + struct delta_info **p = &delta_list; + struct delta_info *info; + + while ((info = *p) != NULL) { + if (!memcmp(info->base_sha1, sha1, 20)) { + *p = info->next; + p = &delta_list; + resolve_delta(type, data, size, info->delta, info->size); + free(info); + continue; + } + p = &info->next; + } +} + +static int unpack_non_delta_entry(enum object_type kind, unsigned long size) +{ + 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) + write_object(buf, size, type); + free(buf); + return 0; +} + +static int unpack_delta_entry(unsigned long delta_size) +{ + void *delta_data, *base; + unsigned long base_size; + char type[20]; + unsigned char base_sha1[20]; + int result; + + memcpy(base_sha1, fill(20), 20); + use(20); + + delta_data = get_data(delta_size); + if (dry_run) { + free(delta_data); + return 0; + } + + if (!has_sha1_file(base_sha1)) { + add_delta_to_list(base_sha1, delta_data, delta_size); + return 0; + } + base = read_sha1_file(base_sha1, type, &base_size); + if (!base) + die("failed to read delta-pack base object %s", sha1_to_hex(base_sha1)); + result = resolve_delta(type, base, base_size, delta_data, delta_size); + free(base); + return result; +} + +static void unpack_one(unsigned nr, unsigned total) +{ + unsigned shift; + unsigned char *pack, c; + unsigned long size; + enum object_type type; + + pack = fill(1); + c = *pack; + use(1); + type = (c >> 4) & 7; + size = (c & 15); + shift = 4; + while (c & 0x80) { + pack = fill(1); + 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); + return; + case OBJ_DELTA: + unpack_delta_entry(size); + return; + default: + die("bad object type %d", type); + } +} + +static void unpack_all(void) +{ + int i; + struct pack_header *hdr = fill(sizeof(struct pack_header)); + unsigned nr_objects = ntohl(hdr->hdr_entries); + + if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE) + 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 (delta_list) + die("unresolved deltas left after unpacking"); +} + +int cmd_unpack_objects(int argc, const char **argv, const char *prefix) +{ + int i; + unsigned char sha1[20]; + + quiet = !isatty(2); + + for (i = 1 ; i < argc; i++) { + const char *arg = argv[i]; + + if (*arg == '-') { + if (!strcmp(arg, "-n")) { + dry_run = 1; + continue; + } + if (!strcmp(arg, "-q")) { + quiet = 1; + continue; + } + usage(unpack_usage); + } + + /* We don't take any non-flag arguments now.. Maybe some day */ + usage(unpack_usage); + } + SHA1_Init(&ctx); + unpack_all(); + SHA1_Update(&ctx, buffer, offset); + SHA1_Final(sha1, &ctx); + if (memcmp(fill(20), sha1, 20)) + die("final sha1 did not match"); + use(20); + + /* Write the last part of the buffer to stdout */ + while (len) { + int ret = xwrite(1, buffer + offset, len); + if (ret <= 0) + break; + len -= ret; + offset += ret; + } + + /* All done */ + if (!quiet) + fprintf(stderr, "\n"); + return 0; +} diff --git a/builtin.h b/builtin.h index bc57a04c0..af73c3c19 100644 --- a/builtin.h +++ b/builtin.h @@ -51,6 +51,7 @@ extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_repo_config(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); +extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix); extern int cmd_write_tree(int argc, const char **argv, const char *prefix); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); diff --git a/git.c b/git.c index 5d58946f4..7c3a7f8fd 100644 --- a/git.c +++ b/git.c @@ -267,6 +267,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "repo-config", cmd_repo_config }, { "name-rev", cmd_name_rev, NEEDS_PREFIX }, { "pack-objects", cmd_pack_objects, NEEDS_PREFIX }, + { "unpack-objects", cmd_unpack_objects, NEEDS_PREFIX }, }; int i; diff --git a/unpack-objects.c b/unpack-objects.c deleted file mode 100644 index 48c1ee796..000000000 --- a/unpack-objects.c +++ /dev/null @@ -1,311 +0,0 @@ -#include "cache.h" -#include "object.h" -#include "delta.h" -#include "pack.h" -#include "blob.h" -#include "commit.h" -#include "tag.h" -#include "tree.h" - -#include - -static int dry_run, quiet; -static const char unpack_usage[] = "git-unpack-objects [-n] [-q] < pack-file"; - -/* We always read in 4kB chunks. */ -static unsigned char buffer[4096]; -static unsigned long offset, len, eof; -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) -{ - if (min <= len) - return buffer + offset; - if (eof) - die("unable to fill input"); - if (min > sizeof(buffer)) - die("cannot fill %d bytes", min); - if (offset) { - SHA1_Update(&ctx, buffer, offset); - memcpy(buffer, buffer + offset, len); - offset = 0; - } - do { - int ret = xread(0, buffer + len, sizeof(buffer) - len); - if (ret <= 0) { - if (!ret) - die("early EOF"); - die("read error on input: %s", strerror(errno)); - } - len += ret; - } while (len < min); - return buffer; -} - -static void use(int bytes) -{ - if (bytes > len) - die("used more bytes than were available"); - len -= bytes; - offset += bytes; -} - -static void *get_data(unsigned long size) -{ - z_stream stream; - void *buf = xmalloc(size); - - memset(&stream, 0, sizeof(stream)); - - stream.next_out = buf; - stream.avail_out = size; - stream.next_in = fill(1); - stream.avail_in = len; - inflateInit(&stream); - - for (;;) { - int ret = inflate(&stream, 0); - use(len - stream.avail_in); - if (stream.total_out == size && ret == Z_STREAM_END) - break; - if (ret != Z_OK) - die("inflate returned %d\n", ret); - stream.next_in = fill(1); - stream.avail_in = len; - } - inflateEnd(&stream); - return buf; -} - -struct delta_info { - unsigned char base_sha1[20]; - 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) -{ - struct delta_info *info = xmalloc(sizeof(*info)); - - memcpy(info->base_sha1, base_sha1, 20); - info->size = size; - info->delta = delta; - info->next = delta_list; - delta_list = info; -} - -static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size); - -static void write_object(void *buf, unsigned long size, const char *type) -{ - unsigned char sha1[20]; - if (write_sha1_file(buf, size, type, sha1) < 0) - die("failed to write object"); - added_object(sha1, type, buf, size); -} - -static int resolve_delta(const char *type, - void *base, unsigned long base_size, - void *delta, unsigned long delta_size) -{ - void *result; - unsigned long result_size; - - result = patch_delta(base, base_size, - delta, delta_size, - &result_size); - if (!result) - die("failed to apply delta"); - free(delta); - write_object(result, result_size, type); - free(result); - return 0; -} - -static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size) -{ - struct delta_info **p = &delta_list; - struct delta_info *info; - - while ((info = *p) != NULL) { - if (!memcmp(info->base_sha1, sha1, 20)) { - *p = info->next; - p = &delta_list; - resolve_delta(type, data, size, info->delta, info->size); - free(info); - continue; - } - p = &info->next; - } -} - -static int unpack_non_delta_entry(enum object_type kind, unsigned long size) -{ - 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) - write_object(buf, size, type); - free(buf); - return 0; -} - -static int unpack_delta_entry(unsigned long delta_size) -{ - void *delta_data, *base; - unsigned long base_size; - char type[20]; - unsigned char base_sha1[20]; - int result; - - memcpy(base_sha1, fill(20), 20); - use(20); - - delta_data = get_data(delta_size); - if (dry_run) { - free(delta_data); - return 0; - } - - if (!has_sha1_file(base_sha1)) { - add_delta_to_list(base_sha1, delta_data, delta_size); - return 0; - } - base = read_sha1_file(base_sha1, type, &base_size); - if (!base) - die("failed to read delta-pack base object %s", sha1_to_hex(base_sha1)); - result = resolve_delta(type, base, base_size, delta_data, delta_size); - free(base); - return result; -} - -static void unpack_one(unsigned nr, unsigned total) -{ - unsigned shift; - unsigned char *pack, c; - unsigned long size; - enum object_type type; - - pack = fill(1); - c = *pack; - use(1); - type = (c >> 4) & 7; - size = (c & 15); - shift = 4; - while (c & 0x80) { - pack = fill(1); - 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); - return; - case OBJ_DELTA: - unpack_delta_entry(size); - return; - default: - die("bad object type %d", type); - } -} - -static void unpack_all(void) -{ - int i; - struct pack_header *hdr = fill(sizeof(struct pack_header)); - unsigned nr_objects = ntohl(hdr->hdr_entries); - - if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE) - 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 (delta_list) - die("unresolved deltas left after unpacking"); -} - -int main(int argc, char **argv) -{ - int i; - unsigned char sha1[20]; - - setup_git_directory(); - - quiet = !isatty(2); - - for (i = 1 ; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg == '-') { - if (!strcmp(arg, "-n")) { - dry_run = 1; - continue; - } - if (!strcmp(arg, "-q")) { - quiet = 1; - continue; - } - usage(unpack_usage); - } - - /* We don't take any non-flag arguments now.. Maybe some day */ - usage(unpack_usage); - } - SHA1_Init(&ctx); - unpack_all(); - SHA1_Update(&ctx, buffer, offset); - SHA1_Final(sha1, &ctx); - if (memcmp(fill(20), sha1, 20)) - die("final sha1 did not match"); - use(20); - - /* Write the last part of the buffer to stdout */ - while (len) { - int ret = xwrite(1, buffer + offset, len); - if (ret <= 0) - break; - len -= ret; - offset += ret; - } - - /* All done */ - if (!quiet) - fprintf(stderr, "\n"); - return 0; -}