Git development
 help / color / mirror / Atom feed
* [PATCH] diff-options: add --stat
@ 2006-04-13  1:02 Johannes Schindelin
  2006-04-13  1:15 ` Johannes Schindelin
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Johannes Schindelin @ 2006-04-13  1:02 UTC (permalink / raw)
  To: git, junkio


Now you can say "git diff --stat" (to get an idea how many changes are
uncommitted), or "git log --stat" (to get an idea how many changes were
committed).

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>

---

	Probably it would be nicer to pass down the diff_options to
	builtin_diff(), run_diff() and run_diff_cmd(), but I am *way*
	too tired to change that now.

	It would have been nice to not just rip out and modify the diffstat
	code from apply.c, but I did not want to fake patch structures.
	Maybe someone more intelligent than me wants to fix this...

 Documentation/diff-options.txt |    3 
 diff.c                         |  250 ++++++++++++++++++++++++++++++++++------
 diff.h                         |    3 
 git-diff.sh                    |    6 +
 4 files changed, 222 insertions(+), 40 deletions(-)

c706aa10bc5e2e2e22fb07aeaff1418f3d4caee0
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 338014c..447e522 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -7,6 +7,9 @@
 --patch-with-raw::
 	Generate patch but keep also the default raw diff output.
 
+--stat::
+	Generate a diffstat instead of a patch.
+
 -z::
 	\0 line termination on output
 
diff --git a/diff.c b/diff.c
index a14e664..7f2b652 100644
--- a/diff.c
+++ b/diff.c
@@ -177,6 +177,7 @@ static int fill_mmfile(mmfile_t *mf, str
 
 struct emit_callback {
 	const char **label_path;
+	struct diffstat_t *diffstat;
 };
 
 static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
@@ -195,6 +196,150 @@ static int fn_out(void *priv, mmbuffer_t
 	return 0;
 }
 
+struct diffstat_t {
+	int nr;
+	int alloc;
+	struct diffstat_file {
+		char *name;
+		unsigned int added, deleted;
+	} **files;
+};
+
+static int fn_diffstat(void *priv, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+	struct emit_callback *data = priv;
+	struct diffstat_file *x;
+	struct diffstat_t *diffstat = data->diffstat;
+
+	if (data->label_path[0]) {
+		x = xcalloc(sizeof (*x), 1);
+		if (diffstat->nr == diffstat->alloc) {
+			diffstat->alloc = alloc_nr(diffstat->alloc);
+			diffstat->files = xrealloc(diffstat->files,
+					diffstat->alloc * sizeof(x));
+		}
+		diffstat->files[diffstat->nr++] = x;
+		x->name = strdup(data->label_path[0] + 2);
+		data->label_path[0] = data->label_path[1] = NULL;
+	} else
+		x = diffstat->files[diffstat->nr - 1];
+
+	for (i = 0; i < nbuf; i++)
+		if (mb[i].ptr[0] == '+')
+			x->added++;
+		else if (mb[i].ptr[0] == '-')
+			x->deleted++;
+	return 0;
+}
+
+static void diffstat_binary(struct diffstat_t *diffstat, const char *name)
+{
+	struct diffstat_file *x = xcalloc(sizeof (*x), 1);
+	if (diffstat->nr == diffstat->alloc) {
+		diffstat->alloc = alloc_nr(diffstat->alloc);
+		diffstat->files = xrealloc(diffstat->files,
+				diffstat->alloc * sizeof(x));
+	}
+	diffstat->files[diffstat->nr++] = x;
+	x->name = strdup(name + 2);
+	x->added = -1;
+}
+
+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct diffstat_t* data)
+{
+	char *prefix = "";
+	int i, len, add, del, total, adds = 0, dels = 0;
+	int max, max_change = 0, max_len = 0;
+	int total_files = data->nr;
+
+	if (data->nr == 0)
+		return;
+
+	printf("---\n");
+
+	for (i = 0; i < data->nr; i++) {
+		struct diffstat_file *file = data->files[i];
+		
+		if (max_change < file->added + file->deleted)
+			max_change = file->added + file->deleted;
+		len = strlen(file->name);
+		if (max_len < len)
+			max_len = len;
+	}
+
+	for (i = 0; i < data->nr; i++) {
+		char *name = data->files[i]->name;
+		int added = data->files[i]->added;
+		int deleted = data->files[i]->deleted;
+
+		if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+			char *qname = xmalloc(len + 1);
+			quote_c_style(name, qname, NULL, 0);
+			free(name);
+			name = qname;
+		}
+
+		/*
+		 * "scale" the filename
+		 */
+		len = strlen(name);
+		max = max_len;
+		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;
+
+		if (added < 0) {
+			/* binary file */
+			printf(" %s%-*s |  Bin\n", prefix, len, name);
+			continue;
+		} else if (added + deleted == 0) {
+			total_files--;
+			continue;
+		}
+
+		add = added;
+		del = deleted;
+		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;
+		}
+		/* TODO: binary */
+		printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+				len, name, added + deleted,
+				add, pluses, del, minuses);
+		free(name);
+		free(data->files[i]);
+	}
+	free(data->files);
+	printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+			total_files, adds, dels);
+}
+
 #define FIRST_FEW_BYTES 8000
 static int mmfile_is_binary(mmfile_t *mf)
 {
@@ -211,7 +356,8 @@ static void builtin_diff(const char *nam
 			 struct diff_filespec *one,
 			 struct diff_filespec *two,
 			 const char *xfrm_msg,
-			 int complete_rewrite)
+			 int complete_rewrite,
+			 struct diffstat_t* diffstat)
 {
 	mmfile_t mf1, mf2;
 	const char *lbl[2];
@@ -221,43 +367,49 @@ static void builtin_diff(const char *nam
 	b_two = quote_two("b/", name_b);
 	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
 	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-	printf("diff --git %s %s\n", a_one, b_two);
-	if (lbl[0][0] == '/') {
-		/* /dev/null */
-		printf("new file mode %06o\n", two->mode);
-		if (xfrm_msg && xfrm_msg[0])
-			puts(xfrm_msg);
-	}
-	else if (lbl[1][0] == '/') {
-		printf("deleted file mode %06o\n", one->mode);
-		if (xfrm_msg && xfrm_msg[0])
-			puts(xfrm_msg);
-	}
-	else {
-		if (one->mode != two->mode) {
-			printf("old mode %06o\n", one->mode);
-			printf("new mode %06o\n", two->mode);
+	if (!diffstat) {
+		printf("diff --git %s %s\n", a_one, b_two);
+		if (lbl[0][0] == '/') {
+			/* /dev/null */
+			printf("new file mode %06o\n", two->mode);
+			if (xfrm_msg && xfrm_msg[0])
+				puts(xfrm_msg);
 		}
-		if (xfrm_msg && xfrm_msg[0])
-			puts(xfrm_msg);
-		/*
-		 * we do not run diff between different kind
-		 * of objects.
-		 */
-		if ((one->mode ^ two->mode) & S_IFMT)
-			goto free_ab_and_return;
-		if (complete_rewrite) {
-			emit_rewrite_diff(name_a, name_b, one, two);
-			goto free_ab_and_return;
+		else if (lbl[1][0] == '/') {
+			printf("deleted file mode %06o\n", one->mode);
+			if (xfrm_msg && xfrm_msg[0])
+				puts(xfrm_msg);
 		}
+		else {
+			if (one->mode != two->mode) {
+				printf("old mode %06o\n", one->mode);
+				printf("new mode %06o\n", two->mode);
+			}
+			if (xfrm_msg && xfrm_msg[0])
+				puts(xfrm_msg);
+			/*
+			 * we do not run diff between different kind
+			 * of objects.
+			 */
+			if ((one->mode ^ two->mode) & S_IFMT)
+				goto free_ab_and_return;
+			if (complete_rewrite) {
+				emit_rewrite_diff(name_a, name_b, one, two);
+				goto free_ab_and_return;
+			}
+		}
 	}
 
 	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 		die("unable to read files to diff");
 
-	if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
-		printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
-	else {
+	if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+		if (diffstat)
+			diffstat_binary(diffstat, lbl[0]);
+		else
+			printf("Binary files %s and %s differ\n",
+					lbl[0], lbl[1]);
+	} else {
 		/* Crazy xdl interfaces.. */
 		const char *diffopts = getenv("GIT_DIFF_OPTS");
 		xpparam_t xpp;
@@ -266,6 +418,7 @@ static void builtin_diff(const char *nam
 		struct emit_callback ecbdata;
 
 		ecbdata.label_path = lbl;
+		ecbdata.diffstat = diffstat;
 		xpp.flags = XDF_NEED_MINIMAL;
 		xecfg.ctxlen = 3;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -275,7 +428,7 @@ static void builtin_diff(const char *nam
 			xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 		else if (!strncmp(diffopts, "-u", 2))
 			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-		ecb.outf = fn_out;
+		ecb.outf = diffstat ? fn_diffstat : fn_out;
 		ecb.priv = &ecbdata;
 		xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 	}
@@ -690,16 +843,19 @@ static void run_diff_cmd(const char *pgm
 			 struct diff_filespec *one,
 			 struct diff_filespec *two,
 			 const char *xfrm_msg,
-			 int complete_rewrite)
+			 int complete_rewrite,
+			 struct diffstat_t *diffstat)
 {
 	if (pgm) {
+		if (diffstat)
+			die ("Cannot use diffstat with external diff");
 		run_external_diff(pgm, name, other, one, two, xfrm_msg,
 				  complete_rewrite);
 		return;
 	}
 	if (one && two)
 		builtin_diff(name, other ? other : name,
-			     one, two, xfrm_msg, complete_rewrite);
+			     one, two, xfrm_msg, complete_rewrite, diffstat);
 	else
 		printf("* Unmerged path %s\n", name);
 }
@@ -733,7 +889,8 @@ static void run_diff(struct diff_filepai
 
 	if (DIFF_PAIR_UNMERGED(p)) {
 		/* unmerged */
-		run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+		run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0,
+			o->diffstat);
 		return;
 	}
 
@@ -805,15 +962,17 @@ static void run_diff(struct diff_filepai
 		 * needs to be split into deletion and creation.
 		 */
 		struct diff_filespec *null = alloc_filespec(two->path);
-		run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+		run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0,
+			o->diffstat);
 		free(null);
 		null = alloc_filespec(one->path);
-		run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+		run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0,
+			o->diffstat);
 		free(null);
 	}
 	else
 		run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
-			     complete_rewrite);
+			     complete_rewrite, o->diffstat);
 
 	free(name_munged);
 	free(other_munged);
@@ -866,6 +1025,8 @@ int diff_opt_parse(struct diff_options *
 		options->output_format = DIFF_FORMAT_PATCH;
 		options->with_raw = 1;
 	}
+	else if (!strcmp(arg, "--stat"))
+		options->output_format = DIFF_FORMAT_DIFFSTAT;
 	else if (!strcmp(arg, "-z"))
 		options->line_termination = 0;
 	else if (!strncmp(arg, "-l", 2))
@@ -1291,6 +1452,7 @@ static void flush_one_pair(struct diff_f
 		break;
 	default:
 		switch (diff_output_format) {
+		case DIFF_FORMAT_DIFFSTAT:
 		case DIFF_FORMAT_PATCH:
 			diff_flush_patch(p, options);
 			break;
@@ -1316,6 +1478,12 @@ void diff_flush(struct diff_options *opt
 	struct diff_queue_struct *q = &diff_queued_diff;
 	int i;
 	int diff_output_format = options->output_format;
+	struct diffstat_t *diffstat = NULL;
+
+	if (diff_output_format == DIFF_FORMAT_DIFFSTAT) {
+		diffstat = xcalloc(sizeof (struct diffstat_t), 1);
+		options->diffstat = diffstat;
+	}
 
 	if (options->with_raw) {
 		for (i = 0; i < q->nr; i++) {
@@ -1329,6 +1497,12 @@ void diff_flush(struct diff_options *opt
 		flush_one_pair(p, diff_output_format, options);
 		diff_free_filepair(p);
 	}
+
+	if (diffstat) {
+		show_stats(diffstat);
+		free(diffstat);
+	}
+
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
diff --git a/diff.h b/diff.h
index 236095f..5c1526c 100644
--- a/diff.h
+++ b/diff.h
@@ -44,6 +44,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 *);
@@ -119,6 +120,7 @@ #define COMMON_DIFF_OPTIONS_HELP \
 "  -u            synonym for -p.\n" \
 "  --patch-with-raw\n" \
 "                output both a patch and the diff-raw format.\n" \
+"  --stat        show diffstat instead of patch.\n" \
 "  --name-only   show only names of changed files.\n" \
 "  --name-status show names and status of changed files.\n" \
 "  --full-index  show full object name on index lines.\n" \
@@ -142,6 +144,7 @@ #define DIFF_FORMAT_PATCH	2
 #define DIFF_FORMAT_NO_OUTPUT	3
 #define DIFF_FORMAT_NAME	4
 #define DIFF_FORMAT_NAME_STATUS	5
+#define DIFF_FORMAT_DIFFSTAT	6
 
 extern void diff_flush(struct diff_options*);
 
diff --git a/git-diff.sh b/git-diff.sh
index dc0dd31..0fe6770 100755
--- a/git-diff.sh
+++ b/git-diff.sh
@@ -30,9 +30,11 @@ case " $flags " in
 	cc_or_p=--cc ;;
 esac
 
-# If we do not have --name-status, --name-only, -r, or -c default to --cc.
+# If we do not have --name-status, --name-only, -r, -c or --stat,
+# default to --cc.
 case " $flags " in
-*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* )
+*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \
+*" '--stat' "*)
 	;;
 *)
 	flags="$flags'$cc_or_p' " ;;
-- 
1.2.0.gd507-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  1:02 [PATCH] diff-options: add --stat Johannes Schindelin
@ 2006-04-13  1:15 ` Johannes Schindelin
  2006-04-13  1:32   ` Johannes Schindelin
  2006-04-13  4:54 ` Junio C Hamano
  2006-04-13 18:26 ` Junio C Hamano
  2 siblings, 1 reply; 13+ messages in thread
From: Johannes Schindelin @ 2006-04-13  1:15 UTC (permalink / raw)
  To: git, junkio

Hi,

I just realized that something is wrong with this patch: the first file 
of the patchset seems to be ignored. I'll fix it tomorrow.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  1:15 ` Johannes Schindelin
@ 2006-04-13  1:32   ` Johannes Schindelin
  0 siblings, 0 replies; 13+ messages in thread
From: Johannes Schindelin @ 2006-04-13  1:32 UTC (permalink / raw)
  To: git, junkio

Hi,

On Thu, 13 Apr 2006, Johannes Schindelin wrote:

> I just realized that something is wrong with this patch: the first file 
> of the patchset seems to be ignored. I'll fix it tomorrow.

Okay, so I lied: I fixed it today.

diff --git a/diff-tree.c b/diff-tree.c
index 2b79dd0..536da8e 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -117,7 +117,8 @@ int main(int argc, const char **argv)
 	if (opt->dense_combined_merges)
 		opt->diffopt.output_format = DIFF_FORMAT_PATCH;
 
-	if (opt->diffopt.output_format == DIFF_FORMAT_PATCH)
+	if (opt->diffopt.output_format == DIFF_FORMAT_DIFFSTAT ||
+			opt->diffopt.output_format == DIFF_FORMAT_PATCH)
 		opt->diffopt.recursive = 1;
 
 	diff_tree_setup_paths(get_pathspec(prefix, argv), opt);
diff --git a/git.c b/git.c
index 5cb0d32..e4fcf92 100644
--- a/git.c
+++ b/git.c
@@ -344,7 +344,8 @@ static int cmd_log(int argc, const char 
 			opt.ignore_merges = 0;
 		if (opt.dense_combined_merges)
 			opt.diffopt.output_format = DIFF_FORMAT_PATCH;
-		if (opt.diffopt.output_format == DIFF_FORMAT_PATCH)
+		if (opt.diffopt.output_format == DIFF_FORMAT_DIFFSTAT ||
+				opt.diffopt.output_format == DIFF_FORMAT_PATCH)
 			opt.diffopt.recursive = 1;
 		if (!full_diff && rev.prune_data)
 			diff_tree_setup_paths(rev.prune_data, &opt.diffopt);

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  1:02 [PATCH] diff-options: add --stat Johannes Schindelin
  2006-04-13  1:15 ` Johannes Schindelin
@ 2006-04-13  4:54 ` Junio C Hamano
  2006-04-13  6:53   ` Junio C Hamano
  2006-04-13  6:59   ` Johannes Schindelin
  2006-04-13 18:26 ` Junio C Hamano
  2 siblings, 2 replies; 13+ messages in thread
From: Junio C Hamano @ 2006-04-13  4:54 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Interesting.

I wonder if you can also make this an independent option that
prepends diffstat in front of the patch, just like the way the
new flag --patch-with-raw flag prepends raw output in front of
the patch.

By the way, I've been wondering if anybody uses the
GIT_EXTERNAL_DIFF interface.  Does anybody miss it if we did so?

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  4:54 ` Junio C Hamano
@ 2006-04-13  6:53   ` Junio C Hamano
  2006-04-13  8:29     ` Johannes Schindelin
  2006-04-13 17:47     ` Marco Costalba
  2006-04-13  6:59   ` Johannes Schindelin
  1 sibling, 2 replies; 13+ messages in thread
From: Junio C Hamano @ 2006-04-13  6:53 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Junio C Hamano <junkio@cox.net> writes:

> I wonder if you can also make this an independent option that
> prepends diffstat in front of the patch, just like the way the
> new flag --patch-with-raw flag prepends raw output in front of
> the patch.

Clarification.

Traditionally, we had diff-raw and diff-patch formats.
We can think of --name-status and --name-only variants of
diff-raw (just like different --abbrev settings give different
visuals for diff-raw).  Until very recently, these were either-or
output formats, but for Cogito we added an option to show both.

We could reorganize the output format options to:

	- diff-raw and its name variants
	- diff-stat
        - diff-patch

and have (internally) three bools to specify which ones to
output, in the above order.  The recent --patch-with-raw would
flip bit #0 (show raw) and bit #2 (show patch) on.  It is very
likely that diff-stat followed by diff-patch would be a popular
format (that is what git-format-patch does), and it also is
conceivable that diff-raw with diff-stat but without diff-patch
might turn out to be useful for some people.

Also, I forgot to mention, but would it be useful to have a
diffstat to show --cc?  It is unclear, without much thinking,
what the numbers would mean, though...

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  4:54 ` Junio C Hamano
  2006-04-13  6:53   ` Junio C Hamano
@ 2006-04-13  6:59   ` Johannes Schindelin
  1 sibling, 0 replies; 13+ messages in thread
From: Johannes Schindelin @ 2006-04-13  6:59 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Wed, 12 Apr 2006, Junio C Hamano wrote:

> By the way, I've been wondering if anybody uses the
> GIT_EXTERNAL_DIFF interface.  Does anybody miss it if we did so?

Yes. I use it regularly with a patched wdiff (word-by-word diff).

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  6:53   ` Junio C Hamano
@ 2006-04-13  8:29     ` Johannes Schindelin
  2006-04-13 17:47     ` Marco Costalba
  1 sibling, 0 replies; 13+ messages in thread
From: Johannes Schindelin @ 2006-04-13  8:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Wed, 12 Apr 2006, Junio C Hamano wrote:

> Junio C Hamano <junkio@cox.net> writes:
> 
> > I wonder if you can also make this an independent option that
> > prepends diffstat in front of the patch, just like the way the
> > new flag --patch-with-raw flag prepends raw output in front of
> > the patch.
> 
> Clarification.
> 
> Traditionally, we had diff-raw and diff-patch formats.
> We can think of --name-status and --name-only variants of
> diff-raw (just like different --abbrev settings give different
> visuals for diff-raw).  Until very recently, these were either-or
> output formats, but for Cogito we added an option to show both.
> 
> We could reorganize the output format options to:
> 
> 	- diff-raw and its name variants
> 	- diff-stat
>         - diff-patch
> 
> and have (internally) three bools to specify which ones to
> output, in the above order.  The recent --patch-with-raw would
> flip bit #0 (show raw) and bit #2 (show patch) on.  It is very
> likely that diff-stat followed by diff-patch would be a popular
> format (that is what git-format-patch does), and it also is
> conceivable that diff-raw with diff-stat but without diff-patch
> might turn out to be useful for some people.

That sounds plausible. Note that if diff-stat and diff-patch are turned 
on, the patch generating code will be called twice. I do not think it is 
sensible (or robust, for that matter) to cache the patch for this case.

> Also, I forgot to mention, but would it be useful to have a
> diffstat to show --cc?  It is unclear, without much thinking,
> what the numbers would mean, though...

I thought long and hard about that. But how would you display it? You can 
have lines starting with "+ ", " +", "--", etc. The only half-way 
reasonable approach I found was to display the diffstat against each 
parent individually. Since it would be a bit involved to implement that, I 
wanted to think about it a bit longer, if it really makes sense.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  6:53   ` Junio C Hamano
  2006-04-13  8:29     ` Johannes Schindelin
@ 2006-04-13 17:47     ` Marco Costalba
  1 sibling, 0 replies; 13+ messages in thread
From: Marco Costalba @ 2006-04-13 17:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git

On 4/13/06, Junio C Hamano <junkio@cox.net> wrote:
> Junio C Hamano <junkio@cox.net> writes:
>
> It is very
> likely that diff-stat followed by diff-patch would be a popular
> format (that is what git-format-patch does),

Yes, it is very likely the new output format (stat+patch) will be the
default in qgit diff window viewer ;-)

Marco

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13  1:02 [PATCH] diff-options: add --stat Johannes Schindelin
  2006-04-13  1:15 ` Johannes Schindelin
  2006-04-13  4:54 ` Junio C Hamano
@ 2006-04-13 18:26 ` Junio C Hamano
  2006-04-13 18:39   ` Linus Torvalds
  2006-04-13 20:00   ` Johannes Schindelin
  2 siblings, 2 replies; 13+ messages in thread
From: Junio C Hamano @ 2006-04-13 18:26 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Now you can say "git diff --stat" (to get an idea how many changes are
> uncommitted), or "git log --stat" (to get an idea how many changes were
> committed).

I like what this tries to do (as I already said), but there are
issues with the way it does it; here are some comments.

> +static int fn_diffstat(void *priv, mmbuffer_t *mb, int nbuf)
> +{
> +...
> +	for (i = 0; i < nbuf; i++)
> +		if (mb[i].ptr[0] == '+')
> +			x->added++;
> +		else if (mb[i].ptr[0] == '-')
> +			x->deleted++;
> +	return 0;
> +}

This is broken if you have a hunk that adds, deletes or
leaves a line that happens to begin with a plus or a minus.

A demonstration.

        : siamese; ./git-diff-files -p
        diff --git a/Makefile b/Makefile
        index 1130af4..389143e 100644
        --- a/Makefile
        +++ b/Makefile
        @@ -1,3 +1,4 @@
        +-this new line begins with a minus
         # The default target of this Makefile is...
         all:
         
        : siamese; ./git-diff-files --stat
        ---
         Makefile |    2 +-
         1 files changed, 1 insertions(+), 1 deletions(-)

For an added line, xdl_emit_diffrec(rec, size, " ", 1, ecb) is
called, which gives mb[0].ptr = " " and mb[1].ptr = <the
contents of the line>; fn_diffstat() is called with (nbuf == 2).
You are counting the addition mark '+', but you are counting the
minus at the beginning of '-this new line...'.

A fragile workaround would be to make fn_diffstat() aware of
what the caller does and look only at mb[0], with a note to the
poor soul who needs to debug future breakage if xdiff side ever
changes.

A less efficient but more futureproof alternative is to use
xdiff-interface.c::xdiff_outf() to make sure your callback is
fed one line at a time (an example of how this can be done is
found in combine-diff.c::combine_diff()).

For the original "emit everything to stdout" code Linus did,
this was not a problem, because that usage does not care about
the record boundary.  For this "counting '+' and '-' at the
beginning of each hunk data", you do.

> @@ -221,43 +367,49 @@ static void builtin_diff(const char *nam
>  	b_two = quote_two("b/", name_b);
>  	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
>  	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
>...
> +	if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
> +		if (diffstat)
> +			diffstat_binary(diffstat, lbl[0]);
> +		else
> +			printf("Binary files %s and %s differ\n",
> +					lbl[0], lbl[1]);
> +	} else {
>...
> @@ -690,16 +843,19 @@ static void run_diff_cmd(const char *pgm
>  			 struct diff_filespec *one,
>  			 struct diff_filespec *two,
>  			 const char *xfrm_msg,
> -			 int complete_rewrite)
> +			 int complete_rewrite,
> +			 struct diffstat_t *diffstat)
>  {
>  	if (pgm) {
> +		if (diffstat)
> +			die ("Cannot use diffstat with external diff");
>  		run_external_diff(pgm, name, other, one, two, xfrm_msg,
>  				  complete_rewrite);
>  		return;
>  	}

Instead of driving diffstat code from run_diff(),
run_diff_cmd(), and builtin_diff(), I think it would be much
cleaner to define diff_flush_stat() as a sibling to
diff_flush_raw() and diff_flush_patch(), and bypass the
run_diff() chain.  There will be some stuff you can reuse from
builtin_diff() (mostly how it interfaces with xdiff library), so
split that out as a separate function, and call it from both
builtin_diff() and diff_flush_stat().  Then you do not have to
say "Cannot use diffstat with external diff"; even when you
usually use your wdiff based external diff, you would want an
option to get diffstat, no?  This is even more true if we are to
have three bools to toggle which format to enable like I said in
my previous message with --with-raw --with-stat --with-patch
options.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13 18:26 ` Junio C Hamano
@ 2006-04-13 18:39   ` Linus Torvalds
  2006-04-13 18:55     ` Junio C Hamano
  2006-04-13 20:00   ` Johannes Schindelin
  1 sibling, 1 reply; 13+ messages in thread
From: Linus Torvalds @ 2006-04-13 18:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git



On Thu, 13 Apr 2006, Junio C Hamano wrote:
> 
> I like what this tries to do (as I already said), but there are
> issues with the way it does it; here are some comments.
> 
> > +static int fn_diffstat(void *priv, mmbuffer_t *mb, int nbuf)
> > +{
> > +...
> > +	for (i = 0; i < nbuf; i++)
> > +		if (mb[i].ptr[0] == '+')
> > +			x->added++;
> > +		else if (mb[i].ptr[0] == '-')
> > +			x->deleted++;
> > +	return 0;
> > +}
> 
> This is broken if you have a hunk that adds, deletes or
> leaves a line that happens to begin with a plus or a minus.

I think you can just depend on it being in mb[0].ptr[0], no?

xdiff always gives full lines at a time, afaik, and mb[0] always ends up 
being available.

		Linus

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13 18:39   ` Linus Torvalds
@ 2006-04-13 18:55     ` Junio C Hamano
  2006-04-13 19:18       ` Linus Torvalds
  0 siblings, 1 reply; 13+ messages in thread
From: Junio C Hamano @ 2006-04-13 18:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@osdl.org> writes:

> I think you can just depend on it being in mb[0].ptr[0], no?

Yes that happens to be the case _now_.  I just did not want to
worry about future breakage, in case if Davide ever wants to
change how mb[] is prepared for whatever reason.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13 18:55     ` Junio C Hamano
@ 2006-04-13 19:18       ` Linus Torvalds
  0 siblings, 0 replies; 13+ messages in thread
From: Linus Torvalds @ 2006-04-13 19:18 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git



On Thu, 13 Apr 2006, Junio C Hamano wrote:
> 
> Yes that happens to be the case _now_.  I just did not want to
> worry about future breakage, in case if Davide ever wants to
> change how mb[] is prepared for whatever reason.

I'd be worried if we depended on an external version of xdiff, but as it 
is, we'd see all changes to our local xdiff implementation, so...

It's not like either of the statements
 - we always get whole lines
 - the first memory block is always non-empty
is really very controversial ;)

		Linus

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] diff-options: add --stat
  2006-04-13 18:26 ` Junio C Hamano
  2006-04-13 18:39   ` Linus Torvalds
@ 2006-04-13 20:00   ` Johannes Schindelin
  1 sibling, 0 replies; 13+ messages in thread
From: Johannes Schindelin @ 2006-04-13 20:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Thu, 13 Apr 2006, Junio C Hamano wrote:

> For an added line, xdl_emit_diffrec(rec, size, " ", 1, ecb) is
> called, which gives mb[0].ptr = " " and mb[1].ptr = <the
> contents of the line>; fn_diffstat() is called with (nbuf == 2).

Silly me. I did not check that code, but assumed that mb Just Contains 
Whole Lines...

> Instead of driving diffstat code from run_diff(),
> run_diff_cmd(), and builtin_diff(), I think it would be much
> cleaner to define diff_flush_stat() as a sibling to
> diff_flush_raw() and diff_flush_patch(), and bypass the
> run_diff() chain.

I guess you're right. It is also more work :-(

There is another bug: if a file is created, you see "ev/null" as filename. 
Ugly.

I'll try to fix it.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2006-04-13 20:01 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-04-13  1:02 [PATCH] diff-options: add --stat Johannes Schindelin
2006-04-13  1:15 ` Johannes Schindelin
2006-04-13  1:32   ` Johannes Schindelin
2006-04-13  4:54 ` Junio C Hamano
2006-04-13  6:53   ` Junio C Hamano
2006-04-13  8:29     ` Johannes Schindelin
2006-04-13 17:47     ` Marco Costalba
2006-04-13  6:59   ` Johannes Schindelin
2006-04-13 18:26 ` Junio C Hamano
2006-04-13 18:39   ` Linus Torvalds
2006-04-13 18:55     ` Junio C Hamano
2006-04-13 19:18       ` Linus Torvalds
2006-04-13 20:00   ` Johannes Schindelin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox