[REPLACEMENT PATCH 2/2] Add "--early-output" log flag for interactive GUI use

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This adds support for "--early-output[=n]" as a flag to the "git log"
family of commands.  This allows GUI programs to state that they want to
get some output early, in order to be able to show at least something
quickly, even if the full output may take longer to generate.

If no count is specified, a default count of a hundred commits will be
used, although the actual numbr of commits output may be smaller
depending on how many commits were actually found in the first tenth of
a second (or if *everything* was found before that, in which case no
early output will be provided, and only the final list is made
available).

When the full list is generated, there will be a "Final output:" string
prepended to it, regardless of whether any early commits were shown or
not, so that the consumer can always know the difference between early
output and the final list.

Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
---

This replaces my 2/2 patch from yesterday, but still relies on the 
topo-sorting cleanups in 1/2 (which are really totally independent, and 
probably could go in regardless of any of this series).

Instead of having a generic replay mechanism, this one just does *one* 
replay, and discards even that if it can generate all the commits really 
quickly.

The timeout right now is set to a pretty aggressive one-tenth-of-a- 
second, and realistically that could/should probably be longer, but making 
it short makes sure that we actually trigger this in normal use, so I 
think this is the right approach for the initial implementation.

Try it out, with

	git log --early-output=2

and look at what happens: if it cannot generate all the commits in the 
first 0.1 seconds, it will take what it *could* generate, sort it 
topologically (you can add "--date-order" if you want), and show the first 
two commits.

When it then generates the rest, it will add a "Final output:" line, and 
then re-generate it all.

The intent is for a GUI front-end to be able to use the first one-hundred 
or so commits to populate the commit log, and show the window, and not 
have to worry about the fact that the rest of the data (how-ever many 
hundred thousand commits there are) may take much more time to arrive.

So not only is this patch pretty straightforward in itself, I think usage 
should be pretty straightforward too.

 builtin-log.c |   67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 revision.c    |   22 ++++++++++++++++++
 revision.h    |    4 +++
 3 files changed, 93 insertions(+), 0 deletions(-)

diff --git a/builtin-log.c b/builtin-log.c
index e8b982d..707add2 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -77,11 +77,78 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	}
 }
 
+static void log_show_early(struct rev_info *revs, struct commit_list *list)
+{
+	int i = revs->early_output;
+
+	sort_in_topological_order(&list, revs->lifo);
+	while (list && i) {
+		struct commit *commit = list->item;
+		log_tree_commit(revs, commit);
+		list = list->next;
+		i--;
+	}
+}
+
+static void early_output(int signal)
+{
+	show_early_output = log_show_early;
+}
+
+static void setup_early_output(struct rev_info *rev)
+{
+	struct sigaction sa;
+	struct itimerval v;
+
+	/*
+	 * Set up the signal handler, minimally intrusively:
+	 * we only set a single volatile integer word (not
+	 * using sigatomic_t - trying to avoid unnecessary
+	 * system dependencies and headers), and using
+	 * SA_RESTART.
+	 */
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = early_output;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGALRM, &sa, NULL);
+
+	/*
+	 * If we can get the whole output in less than a
+	 * tenth of a second, don't even bother doing the
+	 * early-output thing..
+	 *
+	 * 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);
+}
+
+static void finish_early_output(struct rev_info *rev)
+{
+	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");
+}
+
 static int cmd_log_walk(struct rev_info *rev)
 {
 	struct commit *commit;
 
+	if (rev->early_output)
+		setup_early_output(rev);
+
 	prepare_revision_walk(rev);
+
+	if (rev->early_output)
+		finish_early_output(rev);
+
 	while ((commit = get_revision(rev)) != NULL) {
 		log_tree_commit(rev, commit);
 		if (!rev->reflog_info) {
diff --git a/revision.c b/revision.c
index e85b4af..26610bb 100644
--- a/revision.c
+++ b/revision.c
@@ -10,6 +10,8 @@
 #include "reflog-walk.h"
 #include "patch-ids.h"
 
+volatile show_early_output_fn_t show_early_output;
+
 static char *path_name(struct name_path *path, const char *name)
 {
 	struct name_path *p;
@@ -533,6 +535,7 @@ static int limit_list(struct rev_info *revs)
 		struct commit_list *entry = list;
 		struct commit *commit = list->item;
 		struct object *obj = &commit->object;
+		show_early_output_fn_t show;
 
 		list = list->next;
 		free(entry);
@@ -550,6 +553,13 @@ static int limit_list(struct rev_info *revs)
 		if (revs->min_age != -1 && (commit->date > revs->min_age))
 			continue;
 		p = &commit_list_insert(commit, p)->next;
+
+		show = show_early_output;
+		if (!show)
+			continue;
+
+		show(revs, newlist);
+		show_early_output = NULL;
 	}
 	if (revs->cherry_pick)
 		cherry_pick_list(newlist, revs);
@@ -991,6 +1001,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				revs->topo_order = 1;
 				continue;
 			}
+			if (!prefixcmp(arg, "--early-output")) {
+				int count = 100;
+				switch (arg[14]) {
+				case '=':
+					count = atoi(arg+15);
+					/* Fallthrough */
+				case 0:
+					revs->topo_order = 1;
+					revs->early_output = count;
+					continue;
+				}
+			}
 			if (!strcmp(arg, "--parents")) {
 				revs->parents = 1;
 				continue;
diff --git a/revision.h b/revision.h
index 1f64576..d8a5a50 100644
--- a/revision.h
+++ b/revision.h
@@ -30,6 +30,8 @@ struct rev_info {
 	void *prune_data;
 	prune_fn_t *prune_fn;
 
+	unsigned int early_output;
+
 	/* Traversal flags */
 	unsigned int	dense:1,
 			no_merges:1,
@@ -105,6 +107,8 @@ struct rev_info {
 #define REV_TREE_DIFFERENT	2
 
 /* revision.c */
+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
+volatile show_early_output_fn_t show_early_output;
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
 extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux