[PATCH 3/2] Enhance --early-output format

Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
From: Linus Torvalds
Date: Sunday, November 4, 2007 - 1:12 pm

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>
---

On Sun, 4 Nov 2007, Linus Torvalds wrote:

It wasn't totally trivial, but it doesn't seem to be excessively subtle 
either. About half the patch is moving around some code to look at whether 
the commit is interesting or not and rewriting the parents, so that it can 
be shared with the revision walker.

 builtin-log.c |   88 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 revision.c    |   63 +++++++++++++++++++++++-----------------
 revision.h    |    8 +++++
 3 files changed, 119 insertions(+), 40 deletions(-)

diff --git a/builtin-log.c b/builtin-log.c
index 707add2..268a7af 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -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)
diff --git a/revision.c b/revision.c
index 26610bb..5d6f208 100644
--- a/revision.c
+++ b/revision.c
@@ -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 -1;
+		default:
+			return commit;
 		}
-		return commit;
 	} while (revs->commits);
 	return NULL;
 }
diff --git a/revision.h b/revision.h
index d8a5a50..2232247 100644
--- a/revision.h
+++ b/revision.h
@@ -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
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
New features in gitk, Paul Mackerras, (Sat Oct 27, 6:39 pm)
Re: New features in gitk, Linus Torvalds, (Sat Oct 27, 10:34 pm)
Re: New features in gitk, Paul Mackerras, (Sun Oct 28, 12:11 am)
Re: New features in gitk, Steffen Prohaska, (Sun Oct 28, 12:36 am)
Re: New features in gitk, Linus Torvalds, (Sun Oct 28, 9:50 am)
Re: New features in gitk, Pierre Habouzit, (Sun Oct 28, 11:32 am)
Re: New features in gitk, Mike Hommey, (Sun Oct 28, 11:38 am)
Re: New features in gitk, Paul Mackerras, (Sun Oct 28, 4:13 pm)
Re: New features in gitk, Pierre Habouzit, (Sun Oct 28, 11:20 pm)
Re: New features in gitk, Pierre Habouzit, (Sun Oct 28, 11:24 pm)
Re: New features in gitk, Jonathan del Strother, (Mon Oct 29, 1:31 am)
Re: New features in gitk, Han-Wen Nienhuys, (Mon Oct 29, 6:30 am)
Re: New features in gitk, Michele Ballabio, (Mon Oct 29, 7:04 am)
Re: New features in gitk, Paul Mackerras, (Thu Nov 1, 3:00 am)
Re: New features in gitk, Paul Mackerras, (Thu Nov 1, 4:37 am)
Re: New features in gitk, Linus Torvalds, (Thu Nov 1, 8:16 am)
Re: New features in gitk, Linus Torvalds, (Thu Nov 1, 8:47 am)
Re: New features in gitk, Linus Torvalds, (Thu Nov 1, 9:21 am)
Re: New features in gitk, Paul Mackerras, (Fri Nov 2, 3:19 am)
Re: New features in gitk, Marco Costalba, (Fri Nov 2, 5:44 am)
Re: New features in gitk, Linus Torvalds, (Fri Nov 2, 8:03 am)
Re: New features in gitk, Linus Torvalds, (Fri Nov 2, 8:42 am)
Re: New features in gitk, Marco Costalba, (Fri Nov 2, 9:50 am)
Re: New features in gitk, Linus Torvalds, (Fri Nov 2, 11:16 am)
Re: New features in gitk, Johannes Schindelin, (Fri Nov 2, 11:17 am)
[PATCH 0/2] History replay support, Linus Torvalds, (Fri Nov 2, 1:31 pm)
[PATCH 1/2] Simplify topo-sort logic, Linus Torvalds, (Fri Nov 2, 1:32 pm)
Re: [PATCH 0/2] History replay support, Linus Torvalds, (Fri Nov 2, 6:40 pm)
Re: [PATCH 0/2] History replay support, Marco Costalba, (Sat Nov 3, 12:56 am)
Re: [PATCH 0/2] History replay support, Paul Mackerras, (Sat Nov 3, 5:32 pm)
[PATCH 3/2] Enhance --early-output format, Linus Torvalds, (Sun Nov 4, 1:12 pm)
Re: [PATCH 3/2] Enhance --early-output format, Junio C Hamano, (Mon Nov 5, 1:24 pm)
Re: [PATCH 3/2] Enhance --early-output format, Linus Torvalds, (Mon Nov 5, 1:47 pm)
Re: [PATCH 3/2] Enhance --early-output format, Linus Torvalds, (Mon Nov 5, 2:22 pm)
Re: [PATCH 3/2] Enhance --early-output format, Linus Torvalds, (Mon Nov 5, 2:35 pm)
[PATCH 4/2] Fix parent rewriting in --early-output, Linus Torvalds, (Mon Nov 12, 9:58 pm)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Junio C Hamano, (Mon Nov 12, 10:43 pm)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Linus Torvalds, (Mon Nov 12, 11:46 pm)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Linus Torvalds, (Tue Nov 13, 12:16 am)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Sven Verdoolaege, (Tue Nov 13, 12:53 am)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Shawn O. Pearce, (Tue Nov 13, 1:01 am)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Junio C Hamano, (Tue Nov 13, 1:24 am)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Junio C Hamano, (Tue Nov 13, 1:48 am)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Paul Mackerras, (Tue Nov 13, 2:59 am)
Re: [PATCH 4/2] Fix parent rewriting in --early-output, Marco Costalba, (Fri Nov 16, 12:30 am)