X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=index-pack.c;h=8331d99a62a457cb341a834792aedf5de9c5625f;hb=af6feeb229110a0fa77a179c2655fca08e72f879;hp=9086bbf154e0e8a43c530abf2c4d1b083bb59ed4;hpb=636171cb80255682bdfc9bf5a98c9e66d4c0444a;p=git.git diff --git a/index-pack.c b/index-pack.c index 9086bbf15..8331d99a6 100644 --- a/index-pack.c +++ b/index-pack.c @@ -6,9 +6,11 @@ #include "commit.h" #include "tag.h" #include "tree.h" +#include +#include static const char index_pack_usage[] = -"git-index-pack [-o ] { | --stdin [--fix-thin] [] }"; +"git-index-pack [-v] [-o ] [{ ---keep | --keep= }] { | --stdin [--fix-thin] [] }"; struct object_entry { @@ -44,6 +46,42 @@ static int nr_deltas; static int nr_resolved_deltas; static int from_stdin; +static int verbose; + +static volatile sig_atomic_t progress_update; + +static void progress_interval(int signum) +{ + progress_update = 1; +} + +static void setup_progress_signal(void) +{ + struct sigaction sa; + struct itimerval v; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = progress_interval; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + + v.it_interval.tv_sec = 1; + v.it_interval.tv_usec = 0; + v.it_value = v.it_interval; + setitimer(ITIMER_REAL, &v, NULL); + +} + +static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc) +{ + unsigned percent = n * 100 / total; + if (percent != last_pc || progress_update) { + fprintf(stderr, "%4u%% (%u/%u) done\r", percent, n, total); + progress_update = 0; + } + return percent; +} /* We always read in 4kB chunks. */ static unsigned char input_buffer[4096]; @@ -52,7 +90,7 @@ static SHA_CTX input_ctx; static int input_fd, output_fd, mmap_fd; /* Discard current buffer used content. */ -static void flush() +static void flush(void) { if (input_offset) { if (output_fd >= 0) @@ -67,7 +105,7 @@ static void flush() * 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 <= input_len) return input_buffer + input_offset; @@ -96,7 +134,7 @@ static void use(int bytes) consumed_bytes += bytes; } -static const char * open_pack_file(const char *pack_name) +static const char *open_pack_file(const char *pack_name) { if (from_stdin) { input_fd = 0; @@ -135,7 +173,6 @@ static void parse_pack_header(void) nr_objects = ntohl(hdr->hdr_entries); use(sizeof(struct pack_header)); - /*fprintf(stderr, "Indexing %d objects\n", nr_objects);*/ } static void bad_object(unsigned long offset, const char *format, @@ -238,7 +275,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_ return unpack_entry_data(obj->offset, obj->size); } -static void * get_data_from_pack(struct object_entry *obj) +static void *get_data_from_pack(struct object_entry *obj) { unsigned long from = obj[0].offset + obj[0].hdr_size; unsigned long len = obj[1].offset - from; @@ -287,8 +324,8 @@ static int find_delta(const union delta_base *base) return -first-1; } -static int find_delta_childs(const union delta_base *base, - int *first_index, int *last_index) +static int find_delta_children(const union delta_base *base, + int *first_index, int *last_index) { int first = find_delta(base); int last = first; @@ -352,7 +389,7 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data, nr_resolved_deltas++; hashcpy(delta_base.sha1, delta_obj->sha1); - if (!find_delta_childs(&delta_base, &first, &last)) { + if (!find_delta_children(&delta_base, &first, &last)) { for (j = first; j <= last; j++) { struct object_entry *child = objects + deltas[j].obj_no; if (child->real_type == OBJ_REF_DELTA) @@ -362,7 +399,7 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data, memset(&delta_base, 0, sizeof(delta_base)); delta_base.offset = delta_obj->offset; - if (!find_delta_childs(&delta_base, &first, &last)) { + if (!find_delta_children(&delta_base, &first, &last)) { for (j = first; j <= last; j++) { struct object_entry *child = objects + deltas[j].obj_no; if (child->real_type == OBJ_OFS_DELTA) @@ -383,7 +420,7 @@ static int compare_delta_entry(const void *a, const void *b) /* Parse all objects and return the pack content SHA1 hash */ static void parse_pack_objects(unsigned char *sha1) { - int i; + int i, percent = -1; struct delta_entry *delta = deltas; void *data; struct stat st; @@ -392,8 +429,10 @@ static void parse_pack_objects(unsigned char *sha1) * First pass: * - find locations of all objects; * - calculate SHA1 of all non-delta objects; - * - remember base SHA1 for all deltas. + * - remember base (SHA1 or offset) for all deltas. */ + if (verbose) + fprintf(stderr, "Indexing %d objects.\n", nr_objects); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; data = unpack_raw_entry(obj, &delta->base); @@ -405,21 +444,29 @@ static void parse_pack_objects(unsigned char *sha1) } else sha1_object(data, obj->size, obj->type, obj->sha1); free(data); + if (verbose) + percent = display_progress(i+1, nr_objects, percent); } objects[i].offset = consumed_bytes; + if (verbose) + fputc('\n', stderr); /* Check pack integrity */ flush(); SHA1_Final(sha1, &input_ctx); if (hashcmp(fill(20), sha1)) die("pack is corrupted (SHA1 mismatch)"); + use(20); /* If input_fd is a file, we should have reached its end now. */ if (fstat(input_fd, &st)) die("cannot fstat packfile: %s", strerror(errno)); - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes + 20) + if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes) die("pack has junk at the end"); + if (!nr_deltas) + return; + /* Sort deltas by base SHA1/offset for fast searching */ qsort(deltas, nr_deltas, sizeof(struct delta_entry), compare_delta_entry); @@ -432,6 +479,8 @@ static void parse_pack_objects(unsigned char *sha1) * recursively checking if the resulting object is used as a base * for some more deltas. */ + if (verbose) + fprintf(stderr, "Resolving %d deltas.\n", nr_deltas); for (i = 0; i < nr_objects; i++) { struct object_entry *obj = &objects[i]; union delta_base base; @@ -440,10 +489,10 @@ static void parse_pack_objects(unsigned char *sha1) if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) continue; hashcpy(base.sha1, obj->sha1); - ref = !find_delta_childs(&base, &ref_first, &ref_last); + ref = !find_delta_children(&base, &ref_first, &ref_last); memset(&base, 0, sizeof(base)); base.offset = obj->offset; - ofs = !find_delta_childs(&base, &ofs_first, &ofs_last); + ofs = !find_delta_children(&base, &ofs_first, &ofs_last); if (!ref && !ofs) continue; data = get_data_from_pack(obj); @@ -462,7 +511,12 @@ static void parse_pack_objects(unsigned char *sha1) obj->size, obj->type); } free(data); + if (verbose) + percent = display_progress(nr_resolved_deltas, + nr_deltas, percent); } + if (verbose && nr_resolved_deltas == nr_deltas) + fputc('\n', stderr); } static int write_compressed(int fd, void *in, unsigned int size) @@ -521,7 +575,7 @@ static int delta_pos_compare(const void *_a, const void *_b) static void fix_unresolved_deltas(int nr_unresolved) { struct delta_entry **sorted_by_pos; - int i, n = 0; + int i, n = 0, percent = -1; /* * Since many unresolved deltas may well be themselves base objects @@ -561,7 +615,7 @@ static void fix_unresolved_deltas(int nr_unresolved) else die("base object %s is of type '%s'", sha1_to_hex(d->base.sha1), type); - find_delta_childs(&d->base, &first, &last); + find_delta_children(&d->base, &first, &last); for (j = first; j <= last; j++) { struct object_entry *child = objects + deltas[j].obj_no; if (child->real_type == OBJ_REF_DELTA) @@ -570,8 +624,13 @@ static void fix_unresolved_deltas(int nr_unresolved) append_obj_to_pack(data, size, obj_type); free(data); + if (verbose) + percent = display_progress(nr_resolved_deltas, + nr_deltas, percent); } free(sorted_by_pos); + if (verbose) + fputc('\n', stderr); } static void readjust_pack_header_and_sha1(unsigned char *sha1) @@ -616,7 +675,7 @@ static int sha1_compare(const void *_a, const void *_b) * On entry *sha1 contains the pack content SHA1 hash, on exit it is * the SHA1 hash of sorted object names. */ -static const char * write_index_file(const char *index_name, unsigned char *sha1) +static const char *write_index_file(const char *index_name, unsigned char *sha1) { struct sha1file *f; struct object_entry **sorted_by_sha, **list, **last; @@ -695,8 +754,10 @@ static const char * write_index_file(const char *index_name, unsigned char *sha1 static void final(const char *final_pack_name, const char *curr_pack_name, const char *final_index_name, const char *curr_index_name, + const char *keep_name, const char *keep_msg, unsigned char *sha1) { + char *report = "pack"; char name[PATH_MAX]; int err; @@ -709,6 +770,27 @@ static void final(const char *final_pack_name, const char *curr_pack_name, chmod(curr_pack_name, 0444); } + if (keep_msg) { + int keep_fd, keep_msg_len = strlen(keep_msg); + if (!keep_name) { + snprintf(name, sizeof(name), "%s/pack/pack-%s.keep", + get_object_directory(), sha1_to_hex(sha1)); + keep_name = name; + } + keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600); + if (keep_fd < 0) { + if (errno != EEXIST) + die("cannot write keep file"); + } else { + if (keep_msg_len > 0) { + write_or_die(keep_fd, keep_msg, keep_msg_len); + write_or_die(keep_fd, "\n", 1); + } + close(keep_fd); + report = "keep"; + } + } + if (final_pack_name != curr_pack_name) { if (!final_pack_name) { snprintf(name, sizeof(name), "%s/pack/pack-%s.pack", @@ -729,6 +811,27 @@ static void final(const char *final_pack_name, const char *curr_pack_name, if (move_temp_to_file(curr_index_name, final_index_name)) die("cannot store index file"); } + + if (!from_stdin) { + printf("%s\n", sha1_to_hex(sha1)); + } else { + char buf[48]; + int len = snprintf(buf, sizeof(buf), "%s\t%s\n", + report, sha1_to_hex(sha1)); + xwrite(1, buf, len); + + /* + * Let's just mimic git-unpack-objects here and write + * the last part of the input buffer to stdout. + */ + while (input_len) { + err = xwrite(1, input_buffer + input_offset, input_len); + if (err <= 0) + break; + input_len -= err; + input_offset += err; + } + } } int main(int argc, char **argv) @@ -736,7 +839,8 @@ int main(int argc, char **argv) int i, fix_thin_pack = 0; const char *curr_pack, *pack_name = NULL; const char *curr_index, *index_name = NULL; - char *index_name_buf = NULL; + const char *keep_name = NULL, *keep_msg = NULL; + char *index_name_buf = NULL, *keep_name_buf = NULL; unsigned char sha1[20]; for (i = 1; i < argc; i++) { @@ -747,6 +851,25 @@ int main(int argc, char **argv) from_stdin = 1; } else if (!strcmp(arg, "--fix-thin")) { fix_thin_pack = 1; + } else if (!strcmp(arg, "--keep")) { + keep_msg = ""; + } else if (!strncmp(arg, "--keep=", 7)) { + keep_msg = arg + 7; + } else if (!strncmp(arg, "--pack_header=", 14)) { + struct pack_header *hdr; + char *c; + + hdr = (struct pack_header *)input_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); + input_len = sizeof(*hdr); + } else if (!strcmp(arg, "-v")) { + verbose = 1; } else if (!strcmp(arg, "-o")) { if (index_name || (i+1) >= argc) usage(index_pack_usage); @@ -775,21 +898,37 @@ int main(int argc, char **argv) strcpy(index_name_buf + len - 5, ".idx"); index_name = index_name_buf; } + if (keep_msg && !keep_name && pack_name) { + int len = strlen(pack_name); + if (!has_extension(pack_name, ".pack")) + die("packfile name '%s' does not end with '.pack'", + pack_name); + keep_name_buf = xmalloc(len); + memcpy(keep_name_buf, pack_name, len - 5); + strcpy(keep_name_buf + len - 5, ".keep"); + keep_name = keep_name_buf; + } curr_pack = open_pack_file(pack_name); parse_pack_header(); objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry)); deltas = xmalloc(nr_objects * sizeof(struct delta_entry)); + if (verbose) + setup_progress_signal(); parse_pack_objects(sha1); if (nr_deltas != nr_resolved_deltas) { if (fix_thin_pack) { int nr_unresolved = nr_deltas - nr_resolved_deltas; + int nr_objects_initial = nr_objects; if (nr_unresolved <= 0) die("confusion beyond insanity"); objects = xrealloc(objects, (nr_objects + nr_unresolved + 1) * sizeof(*objects)); fix_unresolved_deltas(nr_unresolved); + if (verbose) + fprintf(stderr, "%d objects were added to complete this thin pack.\n", + nr_objects - nr_objects_initial); readjust_pack_header_and_sha1(sha1); } if (nr_deltas != nr_resolved_deltas) @@ -797,16 +936,17 @@ int main(int argc, char **argv) nr_deltas - nr_resolved_deltas); } else { /* Flush remaining pack final 20-byte SHA1. */ - use(20); flush(); } free(deltas); curr_index = write_index_file(index_name, sha1); - final(pack_name, curr_pack, index_name, curr_index, sha1); + final(pack_name, curr_pack, + index_name, curr_index, + keep_name, keep_msg, + sha1); free(objects); free(index_name_buf); - - printf("%s\n", sha1_to_hex(sha1)); + free(keep_name_buf); return 0; }