Enhance --early-output format
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 4 Nov 2007 20:12:05 +0000 (12:12 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 5 Nov 2007 22:28:53 +0000 (14:28 -0800)
This makes --early-output a bit more advanced, and actually makes it
generate multiple "Final output:" headers as it updates things
asynchronously. I realize that the "Final output:" line is now illogical,
since it's not really final until it also says "done", but

It now _always_ generates a "Final output:" header in front of any commit
list, and that output header gives you a *guess* at the maximum number of
commits available. However, it should be noted that the guess can be
completely off: I do a reasonable job estimating it, but it is not meant
to be exact.

So what happens is that you may get output like this:

- at 0.1 seconds:

Final output: 2 incomplete
.. 2 commits listed ..

- half a second later:

Final output: 33 incomplete
.. 33 commits listed ..

- another half a second after that:

Final output: 71 incomplete
.. 71 commits listed ..

- another half second later:

Final output: 136 incomplete
.. 100 commits listed: we hit the --early-output limit, and
.. will only output 100 commits, and after this you'll not
.. see an "incomplete" report any more since you got as much
.. early output as you asked for!

- .. and then finally:

Final output: 73106 done
.. all the commits ..

The above is a real-life scenario on my current kernel tree after having
flushed all the caches.

Tested with the experimental gitk patch that Paul sent out, and by looking
at the actual log output (and verifying that my commit count guesses
actually match real life fairly well).

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-log.c
revision.c
revision.h
index 4e9d0cb69ca52ed3a695daa03f9451af6d732bbf..981f38872c15606ebee66509dd04b6c4de3de583 100644 (file)
@@ -77,17 +77,85 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
        }
 }
 
+/*
+ * This gives a rough estimate for how many commits we
+ * will print out in the list.
+ */
+static int estimate_commit_count(struct rev_info *rev, struct commit_list *list)
+{
+       int n = 0;
+
+       while (list) {
+               struct commit *commit = list->item;
+               unsigned int flags = commit->object.flags;
+
+               list = list->next;
+               if (flags & UNINTERESTING)
+                       continue;
+               if (rev->prune_fn && rev->dense && !(flags & TREECHANGE)) {
+                       if (commit->parents && !commit->parents->next)
+                               continue;
+               }
+               n++;
+       }
+       return n;
+}
+
+static void show_early_header(struct rev_info *rev, const char *stage, int nr)
+{
+       if (rev->shown_one) {
+               rev->shown_one = 0;
+               if (rev->commit_format != CMIT_FMT_ONELINE)
+                       putchar(rev->diffopt.line_termination);
+       }
+       printf("Final output: %d %s\n", nr, stage);
+}
+
+struct itimerval early_output_timer;
+
 static void log_show_early(struct rev_info *revs, struct commit_list *list)
 {
        int i = revs->early_output;
+       int show_header = 1;
 
        sort_in_topological_order(&list, revs->lifo);
        while (list && i) {
                struct commit *commit = list->item;
-               log_tree_commit(revs, commit);
+               switch (simplify_commit(revs, commit)) {
+               case commit_show:
+                       if (show_header) {
+                               int n = estimate_commit_count(revs, list);
+                               show_early_header(revs, "incomplete", n);
+                               show_header = 0;
+                       }
+                       log_tree_commit(revs, commit);
+                       i--;
+                       break;
+               case commit_ignore:
+                       break;
+               case commit_error:
+                       return;
+               }
                list = list->next;
-               i--;
        }
+
+       /* Did we already get enough commits for the early output? */
+       if (!i)
+               return;
+
+       /*
+        * ..if no, then repeat it twice a second until we
+        * do.
+        *
+        * NOTE! We don't use "it_interval", because if the
+        * reader isn't listening, we want our output to be
+        * throttled by the writing, and not have the timer
+        * trigger every second even if we're blocked on a
+        * reader!
+        */
+       early_output_timer.it_value.tv_sec = 0;
+       early_output_timer.it_value.tv_usec = 500000;
+       setitimer(ITIMER_REAL, &early_output_timer, NULL);
 }
 
 static void early_output(int signal)
@@ -98,7 +166,6 @@ static void early_output(int signal)
 static void setup_early_output(struct rev_info *rev)
 {
        struct sigaction sa;
-       struct itimerval v;
 
        /*
         * Set up the signal handler, minimally intrusively:
@@ -120,21 +187,16 @@ static void setup_early_output(struct rev_info *rev)
         *
         * This is a one-time-only trigger.
         */
-       memset(&v, 0, sizeof(v));
-       v.it_value.tv_sec = 0;
-       v.it_value.tv_usec = 100000;
-       setitimer(ITIMER_REAL, &v, NULL);
+       early_output_timer.it_value.tv_sec = 0;
+       early_output_timer.it_value.tv_usec = 100000;
+       setitimer(ITIMER_REAL, &early_output_timer, NULL);
 }
 
 static void finish_early_output(struct rev_info *rev)
 {
+       int n = estimate_commit_count(rev, rev->commits);
        signal(SIGALRM, SIG_IGN);
-       if (rev->shown_one) {
-               rev->shown_one = 0;
-               if (rev->commit_format != CMIT_FMT_ONELINE)
-                       putchar(rev->diffopt.line_termination);
-       }
-       printf("Final output:\n");
+       show_early_header(rev, "done", n);
 }
 
 static int cmd_log_walk(struct rev_info *rev)
index 26610bb42d1c2359ac27af3e8ad4b92c888f3b16..2e6121fa1b92b158d6fdc39e0414bd03acf4a58a 100644 (file)
@@ -1398,6 +1398,36 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                           commit->buffer, strlen(commit->buffer));
 }
 
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+       if (commit->object.flags & SHOWN)
+               return commit_ignore;
+       if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
+               return commit_ignore;
+       if (commit->object.flags & UNINTERESTING)
+               return commit_ignore;
+       if (revs->min_age != -1 && (commit->date > revs->min_age))
+               return commit_ignore;
+       if (revs->no_merges && commit->parents && commit->parents->next)
+               return commit_ignore;
+       if (!commit_match(commit, revs))
+               return commit_ignore;
+       if (revs->prune_fn && revs->dense) {
+               /* Commit without changes? */
+               if (!(commit->object.flags & TREECHANGE)) {
+                       /* drop merges unless we want parenthood */
+                       if (!revs->parents)
+                               return commit_ignore;
+                       /* non-merge - always ignore it */
+                       if (!commit->parents || !commit->parents->next)
+                               return commit_ignore;
+               }
+               if (revs->parents && rewrite_parents(revs, commit) < 0)
+                       return commit_error;
+       }
+       return commit_show;
+}
+
 static struct commit *get_revision_1(struct rev_info *revs)
 {
        if (!revs->commits)
@@ -1425,36 +1455,15 @@ static struct commit *get_revision_1(struct rev_info *revs)
                        if (add_parents_to_list(revs, commit, &revs->commits) < 0)
                                return NULL;
                }
-               if (commit->object.flags & SHOWN)
-                       continue;
-
-               if (revs->unpacked && has_sha1_pack(commit->object.sha1,
-                                                   revs->ignore_packed))
-                   continue;
 
-               if (commit->object.flags & UNINTERESTING)
-                       continue;
-               if (revs->min_age != -1 && (commit->date > revs->min_age))
-                       continue;
-               if (revs->no_merges &&
-                   commit->parents && commit->parents->next)
-                       continue;
-               if (!commit_match(commit, revs))
+               switch (simplify_commit(revs, commit)) {
+               case commit_ignore:
                        continue;
-               if (revs->prune_fn && revs->dense) {
-                       /* Commit without changes? */
-                       if (!(commit->object.flags & TREECHANGE)) {
-                               /* drop merges unless we want parenthood */
-                               if (!revs->parents)
-                                       continue;
-                               /* non-merge - always ignore it */
-                               if (!commit->parents || !commit->parents->next)
-                                       continue;
-                       }
-                       if (revs->parents && rewrite_parents(revs, commit) < 0)
-                               return NULL;
+               case commit_error:
+                       return NULL;
+               default:
+                       return commit;
                }
-               return commit;
        } while (revs->commits);
        return NULL;
 }
index d8a5a5021f1e066b34b1d0602d7e44649e0aa4cf..2232247be4949305ea7b742ca3bdd45cf008d335 100644 (file)
@@ -133,4 +133,12 @@ extern void add_object(struct object *obj,
 
 extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
 
+enum commit_action {
+       commit_ignore,
+       commit_show,
+       commit_error
+};
+
+extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
+
 #endif