Subject: [PATCH 4/5] diff: add --dynstat

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Mon, 16 Jan 2012 11:48:00 +0700

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 diff.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 diff.h |    1 +
 2 files changed, 92 insertions(+), 9 deletions(-)

diff --git a/diff.c b/diff.c
index 8c448b5..b945ad6 100644
--- a/diff.c
+++ b/diff.c
@@ -1226,6 +1226,7 @@ struct diffstat_t {
 		unsigned is_renamed:1;
 		uintmax_t added, deleted;
 	} **files;
+	struct strbuf dynstat;
 };
 
 static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
@@ -1779,6 +1780,7 @@ static void free_diffstat_info(struct diffstat_t *diffstat)
 		free(f);
 	}
 	free(diffstat->files);
+	strbuf_release(&diffstat->dynstat);
 }
 
 struct checkdiff_t {
@@ -3332,6 +3334,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 		options->output_format |= DIFF_FORMAT_NUMSTAT;
 	else if (!strcmp(arg, "--shortstat"))
 		options->output_format |= DIFF_FORMAT_SHORTSTAT;
+	else if (!strcmp(arg, "--dynstat"))
+		options->output_format |= DIFF_FORMAT_DYNSTAT;
 	else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
 		return parse_dirstat_opt(options, "");
 	else if (!prefixcmp(arg, "-X"))
@@ -3922,6 +3926,76 @@ static int check_pair_status(struct diff_filepair *p)
 	}
 }
 
+static int diff_flush_dynstat(struct diff_options *o, struct diffstat_t *diffstat)
+{
+	struct diff_queue_struct *q = &diff_queued_diff;
+	int i, adds = 0, dels = 0, summary_lines = 0, printed_lines = 0, total_files;
+
+	memset(diffstat, 0, sizeof(struct diffstat_t));
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		if (check_pair_status(p))
+			diff_flush_stat(p, o, diffstat);
+	}
+
+	total_files = diffstat->nr;
+	if (!total_files)
+		return o->output_format;
+
+	/* how many lines for --stat ? */
+	for (i = 0; i < diffstat->nr; i++) {
+		struct diffstat_file *f = diffstat->files[i];
+		if (!f->is_binary && !f->is_unmerged) {
+			if (!f->is_renamed && (f->added + f->deleted == 0))
+				total_files--;
+			else {
+				adds += f->added;
+				dels += f->deleted;
+			}
+		}
+	}
+
+	/* how many lines for --summary ? */
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		if ((p->status & (DIFF_STATUS_DELETED | DIFF_STATUS_ADDED |
+				 DIFF_STATUS_COPIED | DIFF_STATUS_RENAMED)) ||
+		    p->score)
+			summary_lines++;
+	}
+
+	if (printed_lines + total_files < 50) {
+		o->output_format |= DIFF_FORMAT_DIFFSTAT;
+		printed_lines += total_files;
+		if (printed_lines + summary_lines < 50) {
+			printed_lines += summary_lines;
+			o->output_format |= DIFF_FORMAT_SUMMARY;
+		}
+	} else {
+		const char *s;
+		int nr = 0;
+		show_dirstat(o, &diffstat->dynstat);
+		s = diffstat->dynstat.buf;
+
+		/* how many lines for --dirstat ? */
+		while (s && (s = strchr(s, '\n'))) {
+			nr++;
+			s++;
+		}
+		if (nr < 50)
+			printed_lines += nr;
+		else
+			strbuf_release(&diffstat->dynstat);
+	}
+
+	/* how many lines for --patch ? */
+	if (adds + dels < 10 && printed_lines < 10)
+		o->output_format |= DIFF_FORMAT_PATCH;
+
+	o->output_format |= DIFF_FORMAT_SHORTSTAT;
+	return o->output_format;
+}
+
 static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
 {
 	int fmt = opt->output_format;
@@ -4201,6 +4275,7 @@ void diff_flush(struct diff_options *options)
 	int i, output_format = options->output_format;
 	int separator = 0;
 	int dirstat_by_line = 0;
+	struct diffstat_t diffstat;
 
 	/*
 	 * Order: raw, stat, summary, patch
@@ -4221,6 +4296,19 @@ void diff_flush(struct diff_options *options)
 		separator++;
 	}
 
+	memset(&diffstat, 0, sizeof(struct diffstat_t));
+	strbuf_init(&diffstat.dynstat, 0);
+	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DYNSTAT)) {
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (check_pair_status(p))
+				diff_flush_stat(p, options, &diffstat);
+		}
+	}
+
+	if (output_format & DIFF_FORMAT_DYNSTAT)
+		output_format = diff_flush_dynstat(options, &diffstat);
+
 	if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))
 		dirstat_by_line = 1;
 
@@ -4229,25 +4317,19 @@ void diff_flush(struct diff_options *options)
 
 	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
 	    dirstat_by_line) {
-		struct diffstat_t diffstat;
-
-		memset(&diffstat, 0, sizeof(struct diffstat_t));
-		for (i = 0; i < q->nr; i++) {
-			struct diff_filepair *p = q->queue[i];
-			if (check_pair_status(p))
-				diff_flush_stat(p, options, &diffstat);
-		}
 		if (output_format & DIFF_FORMAT_NUMSTAT)
 			show_numstat(&diffstat, options);
 		if (output_format & DIFF_FORMAT_DIFFSTAT)
 			show_stats(&diffstat, options);
+		if ((output_format & DIFF_FORMAT_DYNSTAT) && diffstat.dynstat.len)
+			fputs(diffstat.dynstat.buf, options->file);
 		if (output_format & DIFF_FORMAT_SHORTSTAT)
 			show_shortstats(&diffstat, options);
 		if (output_format & DIFF_FORMAT_DIRSTAT)
 			show_dirstat_by_line(&diffstat, options);
-		free_diffstat_info(&diffstat);
 		separator++;
 	}
+	free_diffstat_info(&diffstat);
 	if ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)
 		show_dirstat(options, NULL);
 
diff --git a/diff.h b/diff.h
index ae71f4c..6bebad8 100644
--- a/diff.h
+++ b/diff.h
@@ -39,6 +39,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_FORMAT_PATCH	0x0010
 #define DIFF_FORMAT_SHORTSTAT	0x0020
 #define DIFF_FORMAT_DIRSTAT	0x0040
+#define DIFF_FORMAT_DYNSTAT	0x0080
 
 /* These override all above */
 #define DIFF_FORMAT_NAME	0x0100
-- 
1.7.3.1.256.g2539c.dirty