{"thread":{"id":"7727","subject":"[PATCH] common progress display support","startedAt":"2007-04-18T18:27:45Z","lastAt":"2007-04-19T09:03:06Z","messageCount":4,"participants":["Nicolas Pitre","Shawn O. Pearce","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"39802","messageId":"alpine.LFD.0.98.0704181422050.4504@xanadu.home","threadId":"7727","inReplyTo":null,"subject":"[PATCH] common progress display support","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-04-18T18:27:45Z","receivedAt":"2007-04-18T18:27:45Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"Instead of having this code duplicated in multiple places, let's have\na common interface for progress display.  If someday someone wishes to\ndisplay a cheezy progress bar instead then only one file will have to\nbe changed.\n\nNote: I left merge-recursive.c out since it has a strange notion of\nprogress as it apparently increase the expected total number as it goes.\nSomeone with more intimate knowledge of what that is supposed to mean\nmight look at converting it to the common progress interface.\n\nSigned-off-by: Nicolas Pitre <nico@cam.org>\n---\n Makefile                 |    4 +-\n builtin-pack-objects.c   |   83 +++++++++++----------------------------------\n builtin-unpack-objects.c |   38 +++++++++-----------\n index-pack.c             |   78 +++++++++++++------------------------------\n progress.c               |   68 +++++++++++++++++++++++++++++++++++++\n progress.h               |   14 ++++++++\n unpack-trees.c           |   54 +++++-------------------------\n 7 files changed, 154 insertions(+), 185 deletions(-)\n create mode 100644 progress.c\n create mode 100644 progress.h\n\ndiff --git a/Makefile b/Makefile\nindex 596089e..f463149 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -283,7 +283,7 @@ LIB_H = \\\n \tdiff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n \ttree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \\\n-\tutf8.h reflog-walk.h patch-ids.h attr.h decorate.h\n+\tutf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h\n \n DIFF_OBJS = \\\n \tdiff.o diff-lib.o diffcore-break.o diffcore-order.o \\\n@@ -305,7 +305,7 @@ LIB_OBJS = \\\n \twrite_or_die.o trace.o list-objects.o grep.o match-trees.o \\\n \talloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \\\n \tcolor.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \\\n-\tconvert.o attr.o decorate.o\n+\tconvert.o attr.o decorate.o progress.o\n \n BUILTIN_OBJS = \\\n \tbuiltin-add.o \\\ndiff --git a/builtin-pack-objects.c b/builtin-pack-objects.c\nindex 19fae4c..150f56c 100644\n--- a/builtin-pack-objects.c\n+++ b/builtin-pack-objects.c\n@@ -12,6 +12,7 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"list-objects.h\"\n+#include \"progress.h\"\n \n static const char pack_usage[] = \"\\\n git-pack-objects [{ -q | --progress | --all-progress }] \\n\\\n@@ -69,10 +70,10 @@ static const char *pack_tmp_name, *idx_tmp_name;\n static char tmpname[PATH_MAX];\n static unsigned char pack_file_sha1[20];\n static int progress = 1;\n-static volatile sig_atomic_t progress_update;\n static int window = 10;\n static int pack_to_stdout;\n static int num_preferred_base;\n+static struct progress progress_state;\n \n /*\n  * The object names in objects array are hashed with this hashtable,\n@@ -572,7 +573,6 @@ static off_t write_pack_file(void)\n \tstruct sha1file *f;\n \toff_t offset, last_obj_offset = 0;\n \tstruct pack_header hdr;\n-\tunsigned last_percent = 999;\n \tint do_progress = progress;\n \n \tif (pack_to_stdout) {\n@@ -588,8 +588,10 @@ static off_t write_pack_file(void)\n \t\tf = sha1fd(fd, pack_tmp_name);\n \t}\n \n-\tif (do_progress)\n+\tif (do_progress) {\n \t\tfprintf(stderr, \"Writing %u objects.\\n\", nr_result);\n+\t\tstart_progress(&progress_state, \"\", nr_result);\n+\t}\n \n \thdr.hdr_signature = htonl(PACK_SIGNATURE);\n \thdr.hdr_version = htonl(PACK_VERSION);\n@@ -601,18 +603,11 @@ static off_t write_pack_file(void)\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tlast_obj_offset = offset;\n \t\toffset = write_one(f, objects + i, offset);\n-\t\tif (do_progress) {\n-\t\t\tunsigned percent = written * 100 / nr_result;\n-\t\t\tif (progress_update || percent != last_percent) {\n-\t\t\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\",\n-\t\t\t\t\tpercent, written, nr_result);\n-\t\t\t\tprogress_update = 0;\n-\t\t\t\tlast_percent = percent;\n-\t\t\t}\n-\t\t}\n+\t\tif (do_progress)\n+\t\t\tdisplay_progress(&progress_state, written);\n \t}\n \tif (do_progress)\n-\t\tfputc('\\n', stderr);\n+\t\tstop_progress(&progress_state);\n  done:\n \tif (written != nr_result)\n \t\tdie(\"wrote %u objects while expecting %u\", written, nr_result);\n@@ -873,10 +868,8 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,\n \telse\n \t\tobject_ix[-1 - ix] = nr_objects;\n \n-\tif (progress_update) {\n-\t\tfprintf(stderr, \"Counting objects...%u\\r\", nr_objects);\n-\t\tprogress_update = 0;\n-\t}\n+\tif (progress)\n+\t\tdisplay_progress(&progress_state, nr_objects);\n \n \treturn 1;\n }\n@@ -1398,15 +1391,16 @@ static void find_deltas(struct object_entry **list, int window, int depth)\n \tuint32_t i = nr_objects, idx = 0, processed = 0;\n \tunsigned int array_size = window * sizeof(struct unpacked);\n \tstruct unpacked *array;\n-\tunsigned last_percent = 999;\n \tint max_depth;\n \n \tif (!nr_objects)\n \t\treturn;\n \tarray = xmalloc(array_size);\n \tmemset(array, 0, array_size);\n-\tif (progress)\n+\tif (progress) {\n \t\tfprintf(stderr, \"Deltifying %u objects.\\n\", nr_result);\n+\t\tstart_progress(&progress_state, \"\", nr_result);\n+\t}\n \n \tdo {\n \t\tstruct object_entry *entry = list[--i];\n@@ -1416,15 +1410,8 @@ static void find_deltas(struct object_entry **list, int window, int depth)\n \t\tif (!entry->preferred_base)\n \t\t\tprocessed++;\n \n-\t\tif (progress) {\n-\t\t\tunsigned percent = processed * 100 / nr_result;\n-\t\t\tif (percent != last_percent || progress_update) {\n-\t\t\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\",\n-\t\t\t\t\tpercent, processed, nr_result);\n-\t\t\t\tprogress_update = 0;\n-\t\t\t\tlast_percent = percent;\n-\t\t\t}\n-\t\t}\n+\t\tif (progress)\n+\t\t\tdisplay_progress(&progress_state, processed);\n \n \t\tif (entry->delta)\n \t\t\t/* This happens if we decided to reuse existing\n@@ -1479,7 +1466,7 @@ static void find_deltas(struct object_entry **list, int window, int depth)\n \t} while (i > 0);\n \n \tif (progress)\n-\t\tfputc('\\n', stderr);\n+\t\tstop_progress(&progress_state);\n \n \tfor (i = 0; i < window; ++i) {\n \t\tfree_delta_index(array[i].index);\n@@ -1506,28 +1493,6 @@ static void prepare_pack(int window, int depth)\n \tfree(delta_list);\n }\n \n-static void progress_interval(int signum)\n-{\n-\tprogress_update = 1;\n-}\n-\n-static void setup_progress_signal(void)\n-{\n-\tstruct sigaction sa;\n-\tstruct itimerval v;\n-\n-\tmemset(&sa, 0, sizeof(sa));\n-\tsa.sa_handler = progress_interval;\n-\tsigemptyset(&sa.sa_mask);\n-\tsa.sa_flags = SA_RESTART;\n-\tsigaction(SIGALRM, &sa, NULL);\n-\n-\tv.it_interval.tv_sec = 1;\n-\tv.it_interval.tv_usec = 0;\n-\tv.it_value = v.it_interval;\n-\tsetitimer(ITIMER_REAL, &v, NULL);\n-}\n-\n static int git_pack_config(const char *k, const char *v)\n {\n \tif(!strcmp(k, \"pack.window\")) {\n@@ -1760,31 +1725,25 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \n \tif (progress) {\n \t\tfprintf(stderr, \"Generating pack...\\n\");\n-\t\tsetup_progress_signal();\n+\t\tstart_progress(&progress_state, \"Counting objects: \", 0);\n \t}\n-\n \tif (!use_internal_rev_list)\n \t\tread_object_list_from_stdin();\n \telse {\n \t\trp_av[rp_ac] = NULL;\n \t\tget_object_list(rp_ac, rp_av);\n \t}\n-\n-\tif (progress)\n+\tif (progress) {\n+\t\tstop_progress(&progress_state);\n \t\tfprintf(stderr, \"Done counting %u objects.\\n\", nr_objects);\n+\t}\n+\n \tif (non_empty && !nr_result)\n \t\treturn 0;\n \tif (progress && (nr_objects != nr_result))\n \t\tfprintf(stderr, \"Result has %u objects.\\n\", nr_result);\n \tif (nr_result)\n \t\tprepare_pack(window, depth);\n-\tif (progress == 1 && pack_to_stdout) {\n-\t\t/* the other end usually displays progress itself */\n-\t\tstruct itimerval v = {{0,},};\n-\t\tsetitimer(ITIMER_REAL, &v, NULL);\n-\t\tsignal(SIGALRM, SIG_IGN );\n-\t\tprogress_update = 0;\n-\t}\n \tlast_obj_offset = write_pack_file();\n \tif (!pack_to_stdout) {\n \t\tunsigned char object_list_sha1[20];\ndiff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c\nindex f821906..c370c7f 100644\n--- a/builtin-unpack-objects.c\n+++ b/builtin-unpack-objects.c\n@@ -7,6 +7,7 @@\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"tree.h\"\n+#include \"progress.h\"\n \n static int dry_run, quiet, recover, has_errors;\n static const char unpack_usage[] = \"git-unpack-objects [-n] [-q] [-r] < pack-file\";\n@@ -264,7 +265,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,\n \tfree(base);\n }\n \n-static void unpack_one(unsigned nr, unsigned total)\n+static void unpack_one(unsigned nr)\n {\n \tunsigned shift;\n \tunsigned char *pack, c;\n@@ -286,20 +287,7 @@ static void unpack_one(unsigned nr, unsigned total)\n \t\tsize += (c & 0x7f) << shift;\n \t\tshift += 7;\n \t}\n-\tif (!quiet) {\n-\t\tstatic unsigned long last_sec;\n-\t\tstatic unsigned last_percent;\n-\t\tstruct timeval now;\n-\t\tunsigned percentage = ((nr+1) * 100) / total;\n-\n-\t\tgettimeofday(&now, NULL);\n-\t\tif (percentage != last_percent || now.tv_sec != last_sec) {\n-\t\t\tlast_sec = now.tv_sec;\n-\t\t\tlast_percent = percentage;\n-\t\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\",\n-\t\t\t\t\tpercentage, (nr+1), total);\n-\t\t}\n-\t}\n+\n \tswitch (type) {\n \tcase OBJ_COMMIT:\n \tcase OBJ_TREE:\n@@ -323,6 +311,7 @@ static void unpack_one(unsigned nr, unsigned total)\n static void unpack_all(void)\n {\n \tint i;\n+\tstruct progress progress;\n \tstruct pack_header *hdr = fill(sizeof(struct pack_header));\n \tunsigned nr_objects = ntohl(hdr->hdr_entries);\n \n@@ -330,12 +319,21 @@ static void unpack_all(void)\n \t\tdie(\"bad pack file\");\n \tif (!pack_version_ok(hdr->hdr_version))\n \t\tdie(\"unknown pack file version %d\", ntohl(hdr->hdr_version));\n-\tfprintf(stderr, \"Unpacking %d objects\\n\", nr_objects);\n+\tuse(sizeof(struct pack_header));\n \n+\tif (!quiet) {\n+\t\tfprintf(stderr, \"Unpacking %d objects\\n\", nr_objects);\n+\t\tstart_progress(&progress, \"\", nr_objects);\n+\t}\n \tobj_list = xmalloc(nr_objects * sizeof(*obj_list));\n-\tuse(sizeof(struct pack_header));\n-\tfor (i = 0; i < nr_objects; i++)\n-\t\tunpack_one(i, nr_objects);\n+\tfor (i = 0; i < nr_objects; i++) {\n+\t\tunpack_one(i);\n+\t\tif (!quiet)\n+\t\t\tdisplay_progress(&progress, i + 1);\n+\t}\n+\tif (!quiet)\n+\t\tstop_progress(&progress);\n+\n \tif (delta_list)\n \t\tdie(\"unresolved deltas left after unpacking\");\n }\n@@ -404,7 +402,5 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t}\n \n \t/* All done */\n-\tif (!quiet)\n-\t\tfprintf(stderr, \"\\n\");\n \treturn has_errors;\n }\ndiff --git a/index-pack.c b/index-pack.c\nindex 7aad261..a49d03d 100644\n--- a/index-pack.c\n+++ b/index-pack.c\n@@ -6,6 +6,7 @@\n #include \"commit.h\"\n #include \"tag.h\"\n #include \"tree.h\"\n+#include \"progress.h\"\n \n static const char index_pack_usage[] =\n \"git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }\";\n@@ -47,40 +48,7 @@ static int nr_resolved_deltas;\n static int from_stdin;\n static int verbose;\n \n-static volatile sig_atomic_t progress_update;\n-\n-static void progress_interval(int signum)\n-{\n-\tprogress_update = 1;\n-}\n-\n-static void setup_progress_signal(void)\n-{\n-\tstruct sigaction sa;\n-\tstruct itimerval v;\n-\n-\tmemset(&sa, 0, sizeof(sa));\n-\tsa.sa_handler = progress_interval;\n-\tsigemptyset(&sa.sa_mask);\n-\tsa.sa_flags = SA_RESTART;\n-\tsigaction(SIGALRM, &sa, NULL);\n-\n-\tv.it_interval.tv_sec = 1;\n-\tv.it_interval.tv_usec = 0;\n-\tv.it_value = v.it_interval;\n-\tsetitimer(ITIMER_REAL, &v, NULL);\n-\n-}\n-\n-static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc)\n-{\n-\tunsigned percent = n * 100 / total;\n-\tif (percent != last_pc || progress_update) {\n-\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\", percent, n, total);\n-\t\tprogress_update = 0;\n-\t}\n-\treturn percent;\n-}\n+static struct progress progress;\n \n /* We always read in 4kB chunks. */\n static unsigned char input_buffer[4096];\n@@ -428,7 +396,7 @@ static int compare_delta_entry(const void *a, const void *b)\n /* Parse all objects and return the pack content SHA1 hash */\n static void parse_pack_objects(unsigned char *sha1)\n {\n-\tint i, percent = -1;\n+\tint i;\n \tstruct delta_entry *delta = deltas;\n \tvoid *data;\n \tstruct stat st;\n@@ -439,8 +407,10 @@ static void parse_pack_objects(unsigned char *sha1)\n \t * - calculate SHA1 of all non-delta objects;\n \t * - remember base (SHA1 or offset) for all deltas.\n \t */\n-\tif (verbose)\n+\tif (verbose) {\n \t\tfprintf(stderr, \"Indexing %d objects.\\n\", nr_objects);\n+\t\tstart_progress(&progress, \"\", nr_objects);\n+\t}\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tstruct object_entry *obj = &objects[i];\n \t\tdata = unpack_raw_entry(obj, &delta->base);\n@@ -453,11 +423,11 @@ static void parse_pack_objects(unsigned char *sha1)\n \t\t\tsha1_object(data, obj->size, obj->type, obj->sha1);\n \t\tfree(data);\n \t\tif (verbose)\n-\t\t\tpercent = display_progress(i+1, nr_objects, percent);\n+\t\t\tdisplay_progress(&progress, i+1);\n \t}\n \tobjects[i].offset = consumed_bytes;\n \tif (verbose)\n-\t\tfputc('\\n', stderr);\n+\t\tstop_progress(&progress);\n \n \t/* Check pack integrity */\n \tflush();\n@@ -488,8 +458,10 @@ static void parse_pack_objects(unsigned char *sha1)\n \t *   recursively checking if the resulting object is used as a base\n \t *   for some more deltas.\n \t */\n-\tif (verbose)\n+\tif (verbose) {\n \t\tfprintf(stderr, \"Resolving %d deltas.\\n\", nr_deltas);\n+\t\tstart_progress(&progress, \"\", nr_deltas);\n+\t}\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tstruct object_entry *obj = &objects[i];\n \t\tunion delta_base base;\n@@ -521,11 +493,8 @@ static void parse_pack_objects(unsigned char *sha1)\n \t\t\t}\n \t\tfree(data);\n \t\tif (verbose)\n-\t\t\tpercent = display_progress(nr_resolved_deltas,\n-\t\t\t\t\t\t   nr_deltas, percent);\n+\t\t\tdisplay_progress(&progress, nr_resolved_deltas);\n \t}\n-\tif (verbose && nr_resolved_deltas == nr_deltas)\n-\t\tfputc('\\n', stderr);\n }\n \n static int write_compressed(int fd, void *in, unsigned int size, uint32_t *obj_crc)\n@@ -587,7 +556,7 @@ static int delta_pos_compare(const void *_a, const void *_b)\n static void fix_unresolved_deltas(int nr_unresolved)\n {\n \tstruct delta_entry **sorted_by_pos;\n-\tint i, n = 0, percent = -1;\n+\tint i, n = 0;\n \n \t/*\n \t * Since many unresolved deltas may well be themselves base objects\n@@ -632,12 +601,9 @@ static void fix_unresolved_deltas(int nr_unresolved)\n \t\tappend_obj_to_pack(d->base.sha1, data, size, type);\n \t\tfree(data);\n \t\tif (verbose)\n-\t\t\tpercent = display_progress(nr_resolved_deltas,\n-\t\t\t\t\t\t   nr_deltas, percent);\n+\t\t\tdisplay_progress(&progress, nr_resolved_deltas);\n \t}\n \tfree(sorted_by_pos);\n-\tif (verbose)\n-\t\tfputc('\\n', stderr);\n }\n \n static void readjust_pack_header_and_sha1(unsigned char *sha1)\n@@ -980,10 +946,13 @@ int main(int argc, char **argv)\n \tparse_pack_header();\n \tobjects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));\n \tdeltas = xmalloc(nr_objects * sizeof(struct delta_entry));\n-\tif (verbose)\n-\t\tsetup_progress_signal();\n \tparse_pack_objects(sha1);\n-\tif (nr_deltas != nr_resolved_deltas) {\n+\tif (nr_deltas == nr_resolved_deltas) {\n+\t\tif (verbose)\n+\t\t\tstop_progress(&progress);\n+\t\t/* Flush remaining pack final 20-byte SHA1. */\n+\t\tflush();\n+\t} else {\n \t\tif (fix_thin_pack) {\n \t\t\tint nr_unresolved = nr_deltas - nr_resolved_deltas;\n \t\t\tint nr_objects_initial = nr_objects;\n@@ -993,17 +962,16 @@ int main(int argc, char **argv)\n \t\t\t\t\t   (nr_objects + nr_unresolved + 1)\n \t\t\t\t\t   * sizeof(*objects));\n \t\t\tfix_unresolved_deltas(nr_unresolved);\n-\t\t\tif (verbose)\n+\t\t\tif (verbose) {\n+\t\t\t\tstop_progress(&progress);\n \t\t\t\tfprintf(stderr, \"%d objects were added to complete this thin pack.\\n\",\n \t\t\t\t\tnr_objects - nr_objects_initial);\n+\t\t\t}\n \t\t\treadjust_pack_header_and_sha1(sha1);\n \t\t}\n \t\tif (nr_deltas != nr_resolved_deltas)\n \t\t\tdie(\"pack has %d unresolved deltas\",\n \t\t\t    nr_deltas - nr_resolved_deltas);\n-\t} else {\n-\t\t/* Flush remaining pack final 20-byte SHA1. */\n-\t\tflush();\n \t}\n \tfree(deltas);\n \tcurr_index = write_index_file(index_name, sha1);\ndiff --git a/progress.c b/progress.c\nnew file mode 100644\nindex 0000000..702e116\n--- /dev/null\n+++ b/progress.c\n@@ -0,0 +1,68 @@\n+#include \"git-compat-util.h\"\n+#include \"progress.h\"\n+\n+static volatile sig_atomic_t progress_update;\n+\n+static void progress_interval(int signum)\n+{\n+\tprogress_update = 1;\n+}\n+\n+static void set_progress_signal(void)\n+{\n+\tstruct sigaction sa;\n+\tstruct itimerval v;\n+\n+\tmemset(&sa, 0, sizeof(sa));\n+\tsa.sa_handler = progress_interval;\n+\tsigemptyset(&sa.sa_mask);\n+\tsa.sa_flags = SA_RESTART;\n+\tsigaction(SIGALRM, &sa, NULL);\n+\n+\tv.it_interval.tv_sec = 1;\n+\tv.it_interval.tv_usec = 0;\n+\tv.it_value = v.it_interval;\n+\tsetitimer(ITIMER_REAL, &v, NULL);\n+}\n+\n+static void clear_progress_signal(void)\n+{\n+\tstruct itimerval v = {{0,},};\n+\tsetitimer(ITIMER_REAL, &v, NULL);\n+\tsignal(SIGALRM, SIG_IGN);\n+\tprogress_update = 0;\n+}\n+\n+int display_progress(struct progress *progress, unsigned n)\n+{\n+\tif (progress->total) {\n+\t\tunsigned percent = n * 100 / progress->total;\n+\t\tif (percent != progress->last_percent || progress_update) {\n+\t\t\tprogress->last_percent = percent;\n+\t\t\tfprintf(stderr, \"%s%4u%% (%u/%u) done\\r\",\n+\t\t\t\tprogress->msg, percent, n, progress->total);\n+\t\t\tprogress_update = 0;\n+\t\t\treturn 1;\n+\t\t}\n+\t} else if (progress_update) {\n+\t\tfprintf(stderr, \"%s%u\\r\", progress->msg, n);\n+\t\tprogress_update = 0;\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+void start_progress(struct progress *progress, const char *msg, unsigned total)\n+{\n+\tprogress->msg = msg;\n+\tprogress->total = total;\n+\tprogress->last_percent = -1;\n+\tset_progress_signal();\n+}\n+\n+void stop_progress(struct progress *progress)\n+{\n+\tclear_progress_signal();\n+\tif (progress->total)\n+\t\tfputc('\\n', stderr);\n+}\ndiff --git a/progress.h b/progress.h\nnew file mode 100644\nindex 0000000..5fa4948\n--- /dev/null\n+++ b/progress.h\n@@ -0,0 +1,14 @@\n+#ifndef __progress_h__\n+#define __progress_h__\n+\n+struct progress {\n+\tconst char *msg;\n+\tunsigned total;\n+\tunsigned last_percent;\n+};\n+\n+int display_progress(struct progress *progress, unsigned n);\n+void start_progress(struct progress *progress, const char *msg, unsigned total);\n+void stop_progress(struct progress *progress);\n+\n+#endif\ndiff --git a/unpack-trees.c b/unpack-trees.c\nindex 5139481..8a11622 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -4,6 +4,7 @@\n #include \"tree-walk.h\"\n #include \"cache-tree.h\"\n #include \"unpack-trees.h\"\n+#include \"progress.h\"\n \n #define DBRT_DEBUG 1\n \n@@ -288,36 +289,13 @@ static void unlink_entry(char *name)\n \t}\n }\n \n-static volatile sig_atomic_t progress_update;\n-\n-static void progress_interval(int signum)\n-{\n-\tprogress_update = 1;\n-}\n-\n-static void setup_progress_signal(void)\n-{\n-\tstruct sigaction sa;\n-\tstruct itimerval v;\n-\n-\tmemset(&sa, 0, sizeof(sa));\n-\tsa.sa_handler = progress_interval;\n-\tsigemptyset(&sa.sa_mask);\n-\tsa.sa_flags = SA_RESTART;\n-\tsigaction(SIGALRM, &sa, NULL);\n-\n-\tv.it_interval.tv_sec = 1;\n-\tv.it_interval.tv_usec = 0;\n-\tv.it_value = v.it_interval;\n-\tsetitimer(ITIMER_REAL, &v, NULL);\n-}\n-\n static struct checkout state;\n static void check_updates(struct cache_entry **src, int nr,\n \t\tstruct unpack_trees_options *o)\n {\n \tunsigned short mask = htons(CE_UPDATE);\n-\tunsigned last_percent = 200, cnt = 0, total = 0;\n+\tunsigned cnt = 0, total = 0;\n+\tstruct progress progress;\n \n \tif (o->update && o->verbose_update) {\n \t\tfor (total = cnt = 0; cnt < nr; cnt++) {\n@@ -332,8 +310,7 @@ static void check_updates(struct cache_entry **src, int nr,\n \n \t\tif (total) {\n \t\t\tfprintf(stderr, \"Checking files out...\\n\");\n-\t\t\tsetup_progress_signal();\n-\t\t\tprogress_update = 1;\n+\t\t\tstart_progress(&progress, \"\", total);\n \t\t}\n \t\tcnt = 0;\n \t}\n@@ -341,20 +318,9 @@ static void check_updates(struct cache_entry **src, int nr,\n \twhile (nr--) {\n \t\tstruct cache_entry *ce = *src++;\n \n-\t\tif (total) {\n-\t\t\tif (!ce->ce_mode || ce->ce_flags & mask) {\n-\t\t\t\tunsigned percent;\n-\t\t\t\tcnt++;\n-\t\t\t\tpercent = (cnt * 100) / total;\n-\t\t\t\tif (percent != last_percent ||\n-\t\t\t\t    progress_update) {\n-\t\t\t\t\tfprintf(stderr, \"%4u%% (%u/%u) done\\r\",\n-\t\t\t\t\t\tpercent, cnt, total);\n-\t\t\t\t\tlast_percent = percent;\n-\t\t\t\t\tprogress_update = 0;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t}\n+\t\tif (total)\n+\t\t\tif (!ce->ce_mode || ce->ce_flags & mask)\n+\t\t\t\tdisplay_progress(&progress, ++cnt);\n \t\tif (!ce->ce_mode) {\n \t\t\tif (o->update)\n \t\t\t\tunlink_entry(ce->name);\n@@ -366,10 +332,8 @@ static void check_updates(struct cache_entry **src, int nr,\n \t\t\t\tcheckout_entry(ce, &state, NULL);\n \t\t}\n \t}\n-\tif (total) {\n-\t\tsignal(SIGALRM, SIG_IGN);\n-\t\tfputc('\\n', stderr);\n-\t}\n+\tif (total)\n+\t\tstop_progress(&progress);;\n }\n \n int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)\n-- \n1.5.1.1.873.g1ddf-dirty\n"},{"id":"39808","messageId":"20070418185649.GB18328@spearce.org","threadId":"7727","inReplyTo":"alpine.LFD.0.98.0704181422050.4504@xanadu.home","subject":"Re: [PATCH] common progress display support","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-04-18T18:56:49Z","receivedAt":"2007-04-18T18:56:49Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Nicolas Pitre <nico@cam.org> wrote:\n> Instead of having this code duplicated in multiple places, let's have\n> a common interface for progress display.  If someday someone wishes to\n> display a cheezy progress bar instead then only one file will have to\n> be changed.\n> \n> Note: I left merge-recursive.c out since it has a strange notion of\n> progress as it apparently increase the expected total number as it goes.\n> Someone with more intimate knowledge of what that is supposed to mean\n> might look at converting it to the common progress interface.\n\nThat disaster in merge-recursive.c is my fault.  The progress\nmeter there doesn't mean much of anything.  Even on my slow as\ndirt Windows system I see the progress meter jump to the end as\nsoon as we discover the number of files we have to chug though.\nSo its always at 99%.\n\nAbout the only value it has is saying \"Oh; I just found out there\nare 6,000 files deleted/renamed between the two branches, and I\nfinished processing them.  Oh; I just found out that was just\nan inner recursive merge, now there are actually 12,000 files\ndeleted/renamed when you add in the final merge.  OK, I'm done.\"\n\nI've been thinking about getting rid of it.\n\n-- \nShawn.\n"},{"id":"39810","messageId":"alpine.LFD.0.98.0704181517360.4504@xanadu.home","threadId":"7727","inReplyTo":"20070418185649.GB18328@spearce.org","subject":"Re: [PATCH] common progress display support","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-04-18T19:20:28Z","receivedAt":"2007-04-18T19:20:28Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Wed, 18 Apr 2007, Shawn O. Pearce wrote:\n\n> Nicolas Pitre <nico@cam.org> wrote:\n> > Note: I left merge-recursive.c out since it has a strange notion of\n> > progress as it apparently increase the expected total number as it goes.\n> > Someone with more intimate knowledge of what that is supposed to mean\n> > might look at converting it to the common progress interface.\n> \n> That disaster in merge-recursive.c is my fault.  The progress\n> meter there doesn't mean much of anything.\n> \n> I've been thinking about getting rid of it.\n\nWell... be my guest.  ;-)\n\n\nNicolas\n"},{"id":"39877","messageId":"Pine.LNX.4.64.0704191053590.8822@racer.site","threadId":"7727","inReplyTo":"alpine.LFD.0.98.0704181422050.4504@xanadu.home","subject":"Re: [PATCH] common progress display support","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-04-19T09:03:06Z","receivedAt":"2007-04-19T09:03:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 18 Apr 2007, Nicolas Pitre wrote:\n\n> Instead of having this code duplicated in multiple places, let's have a \n> common interface for progress display.\n\nI like that!\n\n> diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c\n> index 19fae4c..150f56c 100644\n> --- a/builtin-pack-objects.c\n> +++ b/builtin-pack-objects.c\n> @@ -588,8 +588,10 @@ static off_t write_pack_file(void)\n>  \t\tf = sha1fd(fd, pack_tmp_name);\n>  \t}\n>  \n> -\tif (do_progress)\n> +\tif (do_progress) {\n>  \t\tfprintf(stderr, \"Writing %u objects.\\n\", nr_result);\n> +\t\tstart_progress(&progress_state, \"\", nr_result);\n> +\t}\n\nIt seems like it would be a good idea to change the signature of \nstart_progress() to take an optional message, like this:\n\n\tstart_progress(&progress_state, \"Writing %u object.s\\n\", \n\t\t\"\", nr_result);\n\nIt would make the added/deleted ratio even more impressive.\n\nI only skimmed the patch, but like all of your patches, it looks good to \nme.\n\nCiao,\nDscho\n"}]}