Display a progress meter during merge-recursive.
authorShawn O. Pearce <spearce@spearce.org>
Sun, 14 Jan 2007 05:28:58 +0000 (00:28 -0500)
committerJunio C Hamano <junkio@cox.net>
Sun, 14 Jan 2007 20:20:39 +0000 (12:20 -0800)
Because large merges on slow systems can take up to a minute to
execute we should try to keep the user entertained with a progress
meter to let them know how far we have progressed through the
current merge.

The progress meter considers each entry in the in-memory index to
be a unit, which means a single recursive merge will double the
number of units in the progress meter.  Files which are unmerged
after the 3-way tree merge are also considered a unit within the
progress meter.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
merge-recursive.c

index 9237a57f8e11e323d6f4b267e09583b3a6c9103b..966d8e987fe015e3759066b9713ca1df09ca2291 100644 (file)
@@ -79,6 +79,11 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1};
 static int call_depth = 0;
 static int verbosity = 2;
 static int buffer_output = 1;
+static int do_progress = 1;
+static unsigned last_percent;
+static unsigned merged_cnt;
+static unsigned total_cnt;
+static volatile sig_atomic_t progress_update;
 static struct output_buffer *output_list, *output_end;
 
 static int show (int v)
@@ -153,6 +158,39 @@ static void output_commit_title(struct commit *commit)
        }
 }
 
+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 void display_progress()
+{
+       unsigned percent = total_cnt ? merged_cnt * 100 / total_cnt : 0;
+       if (progress_update || percent != last_percent) {
+               fprintf(stderr, "%4u%% (%u/%u) done\r",
+                       percent, merged_cnt, total_cnt);
+               progress_update = 0;
+               last_percent = percent;
+       }
+}
+
 static struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage, int refresh)
 {
@@ -315,11 +353,14 @@ static struct path_list *get_unmerged(void)
        int i;
 
        unmerged->strdup_paths = 1;
+       total_cnt += active_nr;
 
-       for (i = 0; i < active_nr; i++) {
+       for (i = 0; i < active_nr; i++, merged_cnt++) {
                struct path_list_item *item;
                struct stage_data *e;
                struct cache_entry *ce = active_cache[i];
+               if (do_progress)
+                       display_progress();
                if (!ce_stage(ce))
                        continue;
 
@@ -1096,13 +1137,15 @@ static int merge_trees(struct tree *head,
                re_merge = get_renames(merge, common, head, merge, entries);
                clean = process_renames(re_head, re_merge,
                                branch1, branch2);
-               for (i = 0; i < entries->nr; i++) {
+               total_cnt += entries->nr;
+               for (i = 0; i < entries->nr; i++, merged_cnt++) {
                        const char *path = entries->items[i].path;
                        struct stage_data *e = entries->items[i].util;
-                       if (e->processed)
-                               continue;
-                       if (!process_entry(path, e, branch1, branch2))
+                       if (!e->processed
+                               && !process_entry(path, e, branch1, branch2))
                                clean = 0;
+                       if (do_progress)
+                               display_progress();
                }
 
                path_list_clear(re_merge, 0);
@@ -1210,6 +1253,15 @@ static int merge(struct commit *h1,
                commit_list_insert(h1, &(*result)->parents);
                commit_list_insert(h2, &(*result)->parents->next);
        }
+       if (!call_depth && do_progress) {
+               /* Make sure we end at 100% */
+               if (!total_cnt)
+                       total_cnt = 1;
+               merged_cnt = total_cnt;
+               progress_update = 1;
+               display_progress();
+               fputc('\n', stderr);
+       }
        flush_output();
        return clean;
 }
@@ -1279,6 +1331,12 @@ int main(int argc, char *argv[])
        }
        if (argc - i != 3) /* "--" "<head>" "<remote>" */
                die("Not handling anything other than two heads merge.");
+       if (verbosity >= 5) {
+               buffer_output = 0;
+               do_progress = 0;
+       }
+       else
+               do_progress = isatty(1);
 
        branch1 = argv[++i];
        branch2 = argv[++i];
@@ -1288,8 +1346,9 @@ int main(int argc, char *argv[])
 
        branch1 = better_branch_name(branch1);
        branch2 = better_branch_name(branch2);
-       if (verbosity >= 5)
-               buffer_output = 0;
+
+       if (do_progress)
+               setup_progress_signal();
        if (show(3))
                printf("Merging %s with %s\n", branch1, branch2);