#define USE_THE_REPOSITORY_VARIABLE #include "test-tool.h" #include "git-compat-util.h" #include "delta.h" #include "git-zlib.h" #include "hash.h" #include "hex.h" #include "pack.h" #include "pack-objects.h" #include "parse-options.h" #include "setup.h" #include "strbuf.h" #include "string-list.h" static const char *usage_str[] = { "test-tool pack-deltas --num-objects ", NULL }; static unsigned long do_compress(void **pptr, unsigned long size) { git_zstream stream; void *in, *out; unsigned long maxsize; git_deflate_init(&stream, 1); maxsize = git_deflate_bound(&stream, size); in = *pptr; out = xmalloc(maxsize); *pptr = out; stream.next_in = in; stream.avail_in = size; stream.next_out = out; stream.avail_out = maxsize; while (git_deflate(&stream, Z_FINISH) == Z_OK) ; /* nothing */ git_deflate_end(&stream); free(in); return stream.total_out; } static void write_ref_delta(struct hashfile *f, struct object_id *oid, struct object_id *base) { unsigned char header[MAX_PACK_OBJECT_HEADER]; unsigned long size, base_size, delta_size, compressed_size, hdrlen; enum object_type type; void *base_buf, *delta_buf; void *buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) die("unable to read %s", oid_to_hex(oid)); base_buf = repo_read_object_file(the_repository, base, &type, &base_size); if (!base_buf) die("unable to read %s", oid_to_hex(base)); delta_buf = diff_delta(base_buf, base_size, buf, size, &delta_size, 0); compressed_size = do_compress(&delta_buf, delta_size); hdrlen = encode_in_pack_object_header(header, sizeof(header), OBJ_REF_DELTA, delta_size); hashwrite(f, header, hdrlen); hashwrite(f, base->hash, the_repository->hash_algo->rawsz); hashwrite(f, delta_buf, compressed_size); free(buf); free(base_buf); free(delta_buf); } int cmd__pack_deltas(int argc, const char **argv) { int num_objects = -1; struct hashfile *f; struct strbuf line = STRBUF_INIT; struct option options[] = { OPT_INTEGER('n', "num-objects", &num_objects, N_("the number of objects to write")), OPT_END() }; argc = parse_options(argc, argv, NULL, options, usage_str, 0); if (argc || num_objects < 0) usage_with_options(usage_str, options); setup_git_directory(); f = hashfd(the_repository->hash_algo, 1, ""); write_pack_header(f, num_objects); /* Read each line from stdin into 'line' */ while (strbuf_getline_lf(&line, stdin) != EOF) { const char *type_str, *content_oid_str, *base_oid_str = NULL; struct object_id content_oid, base_oid; struct string_list items = STRING_LIST_INIT_NODUP; /* * Tokenize into two or three parts: * 1. REF_DELTA, OFS_DELTA, or FULL. * 2. The object ID for the content object. * 3. The object ID for the base object (optional). */ if (string_list_split_in_place(&items, line.buf, " ", 3) < 0) die("invalid input format: %s", line.buf); if (items.nr < 2) die("invalid input format: %s", line.buf); type_str = items.items[0].string; content_oid_str = items.items[1].string; if (get_oid_hex(content_oid_str, &content_oid)) die("invalid object: %s", content_oid_str); if (items.nr >= 3) { base_oid_str = items.items[2].string; if (get_oid_hex(base_oid_str, &base_oid)) die("invalid object: %s", base_oid_str); } string_list_clear(&items, 0); if (!strcmp(type_str, "REF_DELTA")) write_ref_delta(f, &content_oid, &base_oid); else if (!strcmp(type_str, "OFS_DELTA")) die("OFS_DELTA not implemented"); else if (!strcmp(type_str, "FULL")) die("FULL not implemented"); else die("unknown pack type: %s", type_str); } finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE); strbuf_release(&line); return 0; }