[WIP/PATCH] Combined diffstat

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

 



I ended up implementing both proposals for merge diffstats:

Junio)

 pack-objects.c |  923 ++++++++----- ++++++++++++++++++++++++++++------ +++--
 rev-list.c     |    5
 send-pack.c    |   86               +-                                 +
 upload-pack.c  |    7
 4 files changed, 786 insertions(+), 235 deletions(-)

Marco)

 pack-objects.c |   22 ++++++++++++++--------
 rev-list.c     |    0
 send-pack.c    |    0
 upload-pack.c  |    0
 4 files changed, 156 insertions(+), 86 deletions(-)

 pack-objects.c |   56 +++++++++++++++++++++++++++++++++++++++++++++++---------
 send-pack.c    |    4 +++-
 upload-pack.c  |    0
 3 files changed, 532 insertions(+), 103 deletions(-)

 pack-objects.c |    9 ++++++---
 rev-list.c     |    0
 send-pack.c    |    4 +++-
 upload-pack.c  |    0
 4 files changed, 98 insertions(+), 46 deletions(-)

For the moment, I like Junio's better, if only because it was more difficult
to implement.

If you want to try it, I suggest using this as a test case:

	git log --stat f0b0af1b04

To get the output with separate diffstats for each parent, just define the
environment variable NO_COMBINED_DIFFSTAT before calling git.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@xxxxxx>

---

 combine-diff.c |  271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 diff.h         |    1 
 log-tree.c     |    6 +
 3 files changed, 278 insertions(+), 0 deletions(-)

9e802df5d029d44938568060d4d567a4fd130d79
diff --git a/combine-diff.c b/combine-diff.c
index 9bd27f8..b82ee8c 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -148,6 +148,15 @@ static void append_lost(struct sline *sl
 	sline->lost_tail = &lline->next;
 }
 
+struct diffstat_t {
+        struct diffstat_file {
+                char *name;
+                struct diffstat_parent {
+			unsigned int added, deleted;
+		} *parent;
+        } *files, *current;
+};
+
 struct combine_diff_state {
 	struct xdiff_emit_state xm;
 
@@ -557,6 +566,35 @@ static void dump_sline(struct sline *sli
 	}
 }
 
+static void diffstat_sline(struct diffstat_t *diffstat,
+		struct sline *sline, unsigned long cnt, int num_parent)
+{
+	struct diffstat_parent *data = diffstat->current->parent;
+	unsigned long interesting = (1UL<<num_parent);
+	int lno;
+	for (lno = 0; lno <= cnt; lno++) {
+		struct sline *sl = sline + lno;
+		struct lline *ll;
+		int j;
+
+		if (!(sl->flag & interesting))
+			continue;
+
+		
+		for (ll = sl->lost_head; ll; ll = ll->next)
+			for (j = 0; j < num_parent; j++)
+				if (ll->parent_map & (1UL<<j))
+					data[j].deleted++;
+
+		if (cnt < lno)
+			break;
+
+		for (j = 0; j < num_parent; j++)
+			if (sl->flag & (1UL<<j))
+				data[j].added++;
+	}
+}
+
 static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
 			       int i, int j)
 {
@@ -596,6 +634,7 @@ static int show_patch_diff(struct combin
 	int working_tree_file = !memcmp(elem->sha1, null_sha1, 20);
 	int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
 	mmfile_t result_file;
+	struct diffstat_t *diffstat = opt->diffstat;
 
 	/* Read the result of merge first */
 	if (!working_tree_file)
@@ -686,6 +725,12 @@ static int show_patch_diff(struct combin
 
 	show_hunks = make_hunks(sline, cnt, num_parent, dense);
 
+	if (diffstat) {
+		diffstat->current->name = strdup(elem->path);
+		diffstat_sline(diffstat, sline, cnt, num_parent);
+		show_hunks = 0;
+	}
+
 	if (show_hunks || mode_differs || working_tree_file) {
 		const char *abb;
 
@@ -826,11 +871,213 @@ int show_combined_diff(struct combine_di
 		return 1;
 
 	default:
+	case DIFF_FORMAT_DIFFSTAT:
 	case DIFF_FORMAT_PATCH:
 		return show_patch_diff(p, num_parent, dense, header, opt);
 	}
 }
 
+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct diffstat_t *diffstat,
+		int num_paths, int num_parent)
+{
+	int show_combined_diffstat = (getenv("NO_COMBINED_DIFFSTAT") == NULL);
+	int i, j;
+	int max_length = 0, max_change = 0;
+	int *parent_max_change;
+	char *prefix = "";
+	int len, add, del, total, adds, dels, total_files, max;
+
+	if (num_paths == 0)
+		return;
+
+	parent_max_change = xcalloc(sizeof(int), num_parent);
+
+	for (i = 0; i < num_paths; i++) {
+		int len;
+		char *name = diffstat->files[i].name;
+		if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+			char *qname = xmalloc(len + 1);
+			quote_c_style(name, qname, NULL, 0);
+			free(name);
+			diffstat->files[i].name = name = qname;
+		} else
+			len = strlen(name);
+
+		if (max_length < len)
+			max_length = len;
+
+		for (j = 0; j < num_parent; j++) {
+			struct diffstat_parent *data =
+				diffstat->files[i].parent + j;
+			int change = data->added + data->deleted;
+			if (parent_max_change[j] < change) {
+				parent_max_change[j] = change;
+				if (max_change < change)
+					max_change = change;
+			}
+		}
+	}
+
+	if (max_change == 0)
+		return;
+
+	printf("---\n");
+
+	if (show_combined_diffstat) {
+		char buffer[80];
+		max_change = 0;
+		for (j = 0; j < num_parent; j++)
+			max_change += parent_max_change[j];
+		
+		prefix = "";
+		total_files = num_paths;
+		adds = dels = 0;
+
+		for (i = 0; i < num_paths; i++) {
+			int offset;
+			char *name = diffstat->files[i].name;
+
+			/*
+			 * "scale" the filename
+			 */
+			len = strlen(name);
+			max = max_length;
+			if (max > 50)
+				max = 50;
+			if (len > max) {
+				char *slash;
+				prefix = "...";
+				max -= 3;
+				name += len - max;
+				slash = strchr(name, '/');
+				if (slash)
+					name = slash;
+			}
+			len = max;
+
+			/*
+			 * scale the add/delete
+			 */
+			max = max_change + num_parent - 1;
+			if (max + len > 70)
+				max = 70 - len;
+
+			memset(buffer, ' ', 70);
+			buffer[max + 1] = 0;
+
+			max -= num_parent - 1;
+			offset = add = del = 0;
+			for (j = 0; j < num_parent; j++) {
+				struct diffstat_parent *data =
+					diffstat->files[i].parent + j;
+				int a, d, size;
+
+				size = max * parent_max_change[j] / max_change;
+				if (size == 0)
+					size = 1;
+
+				a = data->added;
+				d = a + data->deleted;
+
+				a = a * size / parent_max_change[j];
+				d = d * size / parent_max_change[j];
+
+				memset(buffer + offset, '+', a);
+				memset(buffer + offset + a, '-', d - a);
+
+				add += data->added;
+				del += data->deleted;
+
+				offset += size + 1;
+			}
+
+			if (add + del == 0) {
+				total_files--;
+				continue;
+			}
+
+			adds += add;
+			dels += del;
+
+			printf(" %s%-*s |%5d %s\n", prefix, len, name,
+					add + del, buffer);
+		}
+		printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+				total_files, adds, dels);
+	} else {
+		for (j = 0; j < num_parent; j++) {
+			prefix = "";
+			total_files = num_paths;
+			adds = dels = 0;
+
+			for (i = 0; i < num_paths; i++) {
+				char *name = diffstat->files[i].name;
+				struct diffstat_parent *data =
+					diffstat->files[i].parent + j;
+
+				/*
+				 * "scale" the filename
+				 */
+				len = strlen(name);
+				max = max_length;
+				if (max > 50)
+					max = 50;
+				if (len > max) {
+					char *slash;
+					prefix = "...";
+					max -= 3;
+					name += len - max;
+					slash = strchr(name, '/');
+					if (slash)
+						name = slash;
+				}
+				len = max;
+
+				/*
+				 * scale the add/delete
+				 */
+				max = max_change;
+				if (max + len > 70)
+					max = 70 - len;
+
+				add = data->added;
+				del = data->deleted;
+
+				if (add + del == 0) {
+					total_files--;
+					continue;
+				}
+
+				total = add + del;
+				adds += add;
+				dels += del;
+
+				if (max_change > 0) {
+					total = (total * max + max_change / 2) / max_change;
+					add = (add * max + max_change / 2) / max_change;
+					del = total - add;
+				}
+				printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+						len, name, add + del,
+						add, pluses, del, minuses);
+			}
+			printf(" %d files changed, %d insertions(+), %d deletions(-)\n\n",
+					total_files, adds, dels);
+		}
+	}
+
+	for (i = 0; i < num_paths; i++) {
+		free(diffstat->files[i].parent);
+		free(diffstat->files[i].name);
+	}
+	free(diffstat->files);
+	diffstat->files = NULL;
+	free(parent_max_change);
+}
+
 const char *diff_tree_combined_merge(const unsigned char *sha1,
 			     const char *header, int dense,
 			     struct diff_options *opt)
@@ -840,6 +1087,11 @@ const char *diff_tree_combined_merge(con
 	struct commit_list *parents;
 	struct combine_diff_path *p, *paths = NULL;
 	int num_parent, i, num_paths;
+	struct diffstat_t *diffstat = NULL;
+
+	if (opt->output_format == DIFF_FORMAT_DIFFSTAT)
+		opt->diffstat = diffstat =
+			xcalloc(sizeof(struct diffstat_t), 1);
 
 	diffopts = *opt;
 	diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -870,6 +1122,15 @@ const char *diff_tree_combined_merge(con
 			num_paths++;
 	}
 	if (num_paths) {
+		if (diffstat) {
+			diffstat->files = xcalloc(sizeof(struct diffstat_file),
+					num_paths);
+			for (i = 0; i < num_paths; i++)
+				diffstat->files[i].parent =
+					xcalloc(sizeof(struct diffstat_parent),
+							num_parent);
+			diffstat->current = diffstat->files;
+		}
 		if (opt->with_raw) {
 			int saved_format = opt->output_format;
 			opt->output_format = DIFF_FORMAT_RAW;
@@ -882,12 +1143,22 @@ const char *diff_tree_combined_merge(con
 			putchar(opt->line_termination);
 		}
 		for (p = paths; p; p = p->next) {
+			if (p->len == 0)
+				continue;
 			if (show_combined_diff(p, num_parent, dense,
 					       header, opt))
 				header = NULL;
+			if (diffstat)
+				diffstat->current++;
 		}
 	}
 
+	if (diffstat) {
+		show_stats(diffstat, num_paths, num_parent);
+		free(diffstat);
+		opt->diffstat = diffstat = NULL;
+	}
+
 	/* Clean things up */
 	while (paths) {
 		struct combine_diff_path *tmp = paths;
diff --git a/diff.h b/diff.h
index f783bae..26e3c02 100644
--- a/diff.h
+++ b/diff.h
@@ -45,6 +45,7 @@ struct diff_options {
 	int *pathlens;
 	change_fn_t change;
 	add_remove_fn_t add_remove;
+	struct diffstat_t *diffstat;
 };
 
 extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
diff --git a/log-tree.c b/log-tree.c
index cb0d0b1..bb46f06 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -232,6 +232,12 @@ int parse_whatchanged_opt(int ac, const 
 			opt->diffopt.output_format = DIFF_FORMAT_PATCH;
 		if (opt->diffopt.output_format == DIFF_FORMAT_PATCH)
 			opt->diffopt.recursive = 1;
+		if (opt->diffopt.output_format == DIFF_FORMAT_DIFFSTAT) {
+			opt->ignore_merges = 0;
+			opt->combine_merges = 1;
+			opt->dense_combined_merges = 1;
+			opt->diffopt.recursive = 1;
+		}
 		if (!wcopt->full_diff && rev->prune_data)
 			diff_tree_setup_paths(rev->prune_data, &opt->diffopt);
 		diff_setup_done(&opt->diffopt);
-- 
1.3.0.rc4.g667c

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