{"thread":{"id":"3864","subject":"[PATCH] diff-options: add --stat (take 2)","startedAt":"2006-04-13T22:15:30Z","lastAt":"2006-04-14T04:37:44Z","messageCount":3,"participants":["Johannes Schindelin","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"18628","messageId":"Pine.LNX.4.63.0604140012560.10924@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3864","inReplyTo":null,"subject":"[PATCH] diff-options: add --stat (take 2)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-04-13T22:15:30Z","receivedAt":"2006-04-13T22:15:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nNow, you can say \"git diff --stat\" (to get an idea how many changes are\nuncommitted), or \"git log --stat\".\n    \nSigned-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\n---\n\n\tThanks to Junio's comments, this looks much better now; I am\n\treasonably happy about it (it even lost some pounds: 31 lines).\n\n\tI still did not find a way to share code with git-apply's diffstat\n\tcode, though. But then, it is only one function (show_stats).\n\n Documentation/diff-options.txt |    3 +\n diff.c                         |  220 +++++++++++++++++++++++++++++++++++++++-\n diff.h                         |    2 \n git-diff.sh                    |    6 +\n 4 files changed, 225 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 338014c..447e522 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -7,6 +7,9 @@\n --patch-with-raw::\n \tGenerate patch but keep also the default raw diff output.\n \n+--stat::\n+\tGenerate a diffstat instead of a patch.\n+\n -z::\n \t\\0 line termination on output\n \ndiff --git a/diff.c b/diff.c\nindex a14e664..ad8478b 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -8,7 +8,7 @@ #include \"cache.h\"\n #include \"quote.h\"\n #include \"diff.h\"\n #include \"diffcore.h\"\n-#include \"xdiff/xdiff.h\"\n+#include \"xdiff-interface.h\"\n \n static int use_size_cache;\n \n@@ -195,6 +195,137 @@ static int fn_out(void *priv, mmbuffer_t\n \treturn 0;\n }\n \n+struct diffstat_t {\n+\tstruct xdiff_emit_state xm;\n+\n+\tint nr;\n+\tint alloc;\n+\tstruct diffstat_file {\n+\t\tchar *name;\n+\t\tunsigned int added, deleted;\n+\t} **files;\n+};\n+\n+static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,\n+\t\tconst char *name)\n+{\n+\tstruct diffstat_file *x;\n+\tx = xcalloc(sizeof (*x), 1);\n+\tif (diffstat->nr == diffstat->alloc) {\n+\t\tdiffstat->alloc = alloc_nr(diffstat->alloc);\n+\t\tdiffstat->files = xrealloc(diffstat->files,\n+\t\t\t\tdiffstat->alloc * sizeof(x));\n+\t}\n+\tdiffstat->files[diffstat->nr++] = x;\n+\tx->name = strdup(name);\n+\treturn x;\n+}\n+\n+static void diffstat_consume(void *priv, char *line, unsigned long len)\n+{\n+\tstruct diffstat_t *diffstat = priv;\n+\tstruct diffstat_file *x = diffstat->files[diffstat->nr - 1];\n+\n+\tif (line[0] == '+')\n+\t\tx->added++;\n+\telse if (line[0] == '-')\n+\t\tx->deleted++;\n+}\n+\n+static const char pluses[] = \"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\";\n+static const char minuses[]= \"----------------------------------------------------------------------\";\n+\n+static void show_stats(struct diffstat_t* data)\n+{\n+\tchar *prefix = \"\";\n+\tint i, len, add, del, total, adds = 0, dels = 0;\n+\tint max, max_change = 0, max_len = 0;\n+\tint total_files = data->nr;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tprintf(\"---\\n\");\n+\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\t\n+\t\tif (max_change < file->added + file->deleted)\n+\t\t\tmax_change = file->added + file->deleted;\n+\t\tlen = strlen(file->name);\n+\t\tif (max_len < len)\n+\t\t\tmax_len = len;\n+\t}\n+\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tchar *name = data->files[i]->name;\n+\t\tint added = data->files[i]->added;\n+\t\tint deleted = data->files[i]->deleted;\n+\n+\t\tif (0 < (len = quote_c_style(name, NULL, NULL, 0))) {\n+\t\t\tchar *qname = xmalloc(len + 1);\n+\t\t\tquote_c_style(name, qname, NULL, 0);\n+\t\t\tfree(name);\n+\t\t\tname = qname;\n+\t\t}\n+\n+\t\t/*\n+\t\t * \"scale\" the filename\n+\t\t */\n+\t\tlen = strlen(name);\n+\t\tmax = max_len;\n+\t\tif (max > 50)\n+\t\t\tmax = 50;\n+\t\tif (len > max) {\n+\t\t\tchar *slash;\n+\t\t\tprefix = \"...\";\n+\t\t\tmax -= 3;\n+\t\t\tname += len - max;\n+\t\t\tslash = strchr(name, '/');\n+\t\t\tif (slash)\n+\t\t\t\tname = slash;\n+\t\t}\n+\t\tlen = max;\n+\n+\t\t/*\n+\t\t * scale the add/delete\n+\t\t */\n+\t\tmax = max_change;\n+\t\tif (max + len > 70)\n+\t\t\tmax = 70 - len;\n+\n+\t\tif (added < 0) {\n+\t\t\t/* binary file */\n+\t\t\tprintf(\" %s%-*s |  Bin\\n\", prefix, len, name);\n+\t\t\tcontinue;\n+\t\t} else if (added + deleted == 0) {\n+\t\t\ttotal_files--;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tadd = added;\n+\t\tdel = deleted;\n+\t\ttotal = add + del;\n+\t\tadds += add;\n+\t\tdels += del;\n+\n+\t\tif (max_change > 0) {\n+\t\t\ttotal = (total * max + max_change / 2) / max_change;\n+\t\t\tadd = (add * max + max_change / 2) / max_change;\n+\t\t\tdel = total - add;\n+\t\t}\n+\t\t/* TODO: binary */\n+\t\tprintf(\" %s%-*s |%5d %.*s%.*s\\n\", prefix,\n+\t\t\t\tlen, name, added + deleted,\n+\t\t\t\tadd, pluses, del, minuses);\n+\t\tfree(name);\n+\t\tfree(data->files[i]);\n+\t}\n+\tfree(data->files);\n+\tprintf(\" %d files changed, %d insertions(+), %d deletions(-)\\n\",\n+\t\t\ttotal_files, adds, dels);\n+}\n+\n #define FIRST_FEW_BYTES 8000\n static int mmfile_is_binary(mmfile_t *mf)\n {\n@@ -285,7 +416,36 @@ static void builtin_diff(const char *nam\n \tfree(b_two);\n \treturn;\n }\n+\n+static void builtin_diffstat(const char *name_a, const char *name_b,\n+\t\tstruct diff_filespec *one, struct diff_filespec *two,\n+\t\tstruct diffstat_t *diffstat)\n+{\n+\tmmfile_t mf1, mf2;\n+\tstruct diffstat_file *data;\n \n+\tdata = diffstat_add(diffstat, name_a ? name_a : name_b);\n+\n+\tif (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)\n+\t\tdie(\"unable to read files to diff\");\n+\n+\tif (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))\n+\t\tdata->added = -1;\n+\telse {\n+\t\t/* Crazy xdl interfaces.. */\n+\t\txpparam_t xpp;\n+\t\txdemitconf_t xecfg;\n+\t\txdemitcb_t ecb;\n+\n+\t\txpp.flags = XDF_NEED_MINIMAL;\n+\t\txecfg.ctxlen = 3;\n+\t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n+\t\tecb.outf = xdiff_outf;\n+\t\tecb.priv = diffstat;\n+\t\txdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);\n+\t}\n+}\n+\n struct diff_filespec *alloc_filespec(const char *path)\n {\n \tint namelen = strlen(path);\n@@ -818,7 +978,28 @@ static void run_diff(struct diff_filepai\n \tfree(name_munged);\n \tfree(other_munged);\n }\n+\n+static void run_diffstat(struct diff_filepair *p, struct diff_options *o,\n+\t\tstruct diffstat_t *diffstat)\n+{\n+\tconst char *name;\n+\tconst char *other;\n \n+\tif (DIFF_PAIR_UNMERGED(p)) {\n+\t\t/* unmerged */\n+\t\tbuiltin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);\n+\t\treturn;\n+\t}\n+\n+\tname = p->one->path;\n+\tother = (strcmp(name, p->two->path) ? p->two->path : NULL);\n+\n+\tdiff_fill_sha1_info(p->one);\n+\tdiff_fill_sha1_info(p->two);\n+\n+\tbuiltin_diffstat(name, other, p->one, p->two, diffstat);\n+}\n+\n void diff_setup(struct diff_options *options)\n {\n \tmemset(options, 0, sizeof(*options));\n@@ -866,6 +1047,8 @@ int diff_opt_parse(struct diff_options *\n \t\toptions->output_format = DIFF_FORMAT_PATCH;\n \t\toptions->with_raw = 1;\n \t}\n+\telse if (!strcmp(arg, \"--stat\"))\n+\t\toptions->output_format = DIFF_FORMAT_DIFFSTAT;\n \telse if (!strcmp(arg, \"-z\"))\n \t\toptions->line_termination = 0;\n \telse if (!strncmp(arg, \"-l\", 2))\n@@ -1163,6 +1346,19 @@ static void diff_flush_patch(struct diff\n \t\treturn; /* no tree diffs in patch format */ \n \n \trun_diff(p, o);\n+}\n+\n+static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,\n+\t\tstruct diffstat_t *diffstat)\n+{\n+\tif (diff_unmodified_pair(p))\n+\t\treturn;\n+\n+\tif ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||\n+\t    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))\n+\t\treturn; /* no tree diffs in patch format */ \n+\n+\trun_diffstat(p, o, diffstat);\n }\n \n int diff_queue_is_empty(void)\n@@ -1276,7 +1472,8 @@ static void diff_resolve_rename_copy(voi\n \n static void flush_one_pair(struct diff_filepair *p,\n \t\t\t   int diff_output_format,\n-\t\t\t   struct diff_options *options)\n+\t\t\t   struct diff_options *options,\n+\t\t\t   struct diffstat_t *diffstat)\n {\n \tint inter_name_termination = '\\t';\n \tint line_termination = options->line_termination;\n@@ -1291,6 +1488,9 @@ static void flush_one_pair(struct diff_f\n \t\tbreak;\n \tdefault:\n \t\tswitch (diff_output_format) {\n+\t\tcase DIFF_FORMAT_DIFFSTAT:\n+\t\t\tdiff_flush_stat(p, options, diffstat);\n+\t\t\tbreak;\n \t\tcase DIFF_FORMAT_PATCH:\n \t\t\tdiff_flush_patch(p, options);\n \t\t\tbreak;\n@@ -1316,19 +1516,31 @@ void diff_flush(struct diff_options *opt\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i;\n \tint diff_output_format = options->output_format;\n+\tstruct diffstat_t *diffstat = NULL;\n \n+\tif (diff_output_format == DIFF_FORMAT_DIFFSTAT) {\n+\t\tdiffstat = xcalloc(sizeof (struct diffstat_t), 1);\n+\t\tdiffstat->xm.consume = diffstat_consume;\n+\t}\n+\n \tif (options->with_raw) {\n \t\tfor (i = 0; i < q->nr; i++) {\n \t\t\tstruct diff_filepair *p = q->queue[i];\n-\t\t\tflush_one_pair(p, DIFF_FORMAT_RAW, options);\n+\t\t\tflush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);\n \t\t}\n \t\tputchar(options->line_termination);\n \t}\n \tfor (i = 0; i < q->nr; i++) {\n \t\tstruct diff_filepair *p = q->queue[i];\n-\t\tflush_one_pair(p, diff_output_format, options);\n+\t\tflush_one_pair(p, diff_output_format, options, diffstat);\n \t\tdiff_free_filepair(p);\n \t}\n+\n+\tif (diffstat) {\n+\t\tshow_stats(diffstat);\n+\t\tfree(diffstat);\n+\t}\n+\n \tfree(q->queue);\n \tq->queue = NULL;\n \tq->nr = q->alloc = 0;\ndiff --git a/diff.h b/diff.h\nindex 236095f..2f8aff2 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -119,6 +119,7 @@ #define COMMON_DIFF_OPTIONS_HELP \\\n \"  -u            synonym for -p.\\n\" \\\n \"  --patch-with-raw\\n\" \\\n \"                output both a patch and the diff-raw format.\\n\" \\\n+\"  --stat        show diffstat instead of patch.\\n\" \\\n \"  --name-only   show only names of changed files.\\n\" \\\n \"  --name-status show names and status of changed files.\\n\" \\\n \"  --full-index  show full object name on index lines.\\n\" \\\n@@ -142,6 +143,7 @@ #define DIFF_FORMAT_PATCH\t2\n #define DIFF_FORMAT_NO_OUTPUT\t3\n #define DIFF_FORMAT_NAME\t4\n #define DIFF_FORMAT_NAME_STATUS\t5\n+#define DIFF_FORMAT_DIFFSTAT\t6\n \n extern void diff_flush(struct diff_options*);\n \ndiff --git a/git-diff.sh b/git-diff.sh\nindex dc0dd31..0fe6770 100755\n--- a/git-diff.sh\n+++ b/git-diff.sh\n@@ -30,9 +30,11 @@ case \" $flags \" in\n \tcc_or_p=--cc ;;\n esac\n \n-# If we do not have --name-status, --name-only, -r, or -c default to --cc.\n+# If we do not have --name-status, --name-only, -r, -c or --stat,\n+# default to --cc.\n case \" $flags \" in\n-*\" '--name-status' \"* | *\" '--name-only' \"* | *\" '-r' \"* | *\" '-c' \"* )\n+*\" '--name-status' \"* | *\" '--name-only' \"* | *\" '-r' \"* | *\" '-c' \"* | \\\n+*\" '--stat' \"*)\n \t;;\n *)\n \tflags=\"$flags'$cc_or_p' \" ;;\n"},{"id":"18629","messageId":"Pine.LNX.4.63.0604140108280.25088@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"3864","inReplyTo":"Pine.LNX.4.63.0604140012560.10924@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] diff-options: add --stat (take 2)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-04-13T23:09:48Z","receivedAt":"2006-04-13T23:09:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"... and a fix for an invalid free():\n\n---\n\n diff.c |   10 +++++-----\n 1 files changed, 5 insertions(+), 5 deletions(-)\n\n14d8e3c7cda1e2aaff62375fe34db2458d302173\ndiff --git a/diff.c b/diff.c\nindex ad8478b..2968153 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -266,7 +266,7 @@ static void show_stats(struct diffstat_t\n \t\t\tchar *qname = xmalloc(len + 1);\n \t\t\tquote_c_style(name, qname, NULL, 0);\n \t\t\tfree(name);\n-\t\t\tname = qname;\n+\t\t\tdata->files[i]->name = name = qname;\n \t\t}\n \n \t\t/*\n@@ -297,10 +297,10 @@ static void show_stats(struct diffstat_t\n \t\tif (added < 0) {\n \t\t\t/* binary file */\n \t\t\tprintf(\" %s%-*s |  Bin\\n\", prefix, len, name);\n-\t\t\tcontinue;\n+\t\t\tgoto free_diffstat_file;\n \t\t} else if (added + deleted == 0) {\n \t\t\ttotal_files--;\n-\t\t\tcontinue;\n+\t\t\tgoto free_diffstat_file;\n \t\t}\n \n \t\tadd = added;\n@@ -314,11 +314,11 @@ static void show_stats(struct diffstat_t\n \t\t\tadd = (add * max + max_change / 2) / max_change;\n \t\t\tdel = total - add;\n \t\t}\n-\t\t/* TODO: binary */\n \t\tprintf(\" %s%-*s |%5d %.*s%.*s\\n\", prefix,\n \t\t\t\tlen, name, added + deleted,\n \t\t\t\tadd, pluses, del, minuses);\n-\t\tfree(name);\n+\tfree_diffstat_file:\n+\t\tfree(data->files[i]->name);\n \t\tfree(data->files[i]);\n \t}\n \tfree(data->files);\n-- \n1.3.0.rc3.g9813\n"},{"id":"18634","messageId":"7vzmiovkkn.fsf@assigned-by-dhcp.cox.net","threadId":"3864","inReplyTo":"Pine.LNX.4.63.0604140012560.10924@wbgn013.biozentrum.uni-wuerzburg.de","subject":"[PATCH] diff --stat: no need to ask funcnames nor context.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-14T04:37:44Z","receivedAt":"2006-04-14T04:37:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Now, you can say \"git diff --stat\" (to get an idea how many changes are\n> uncommitted), or \"git log --stat\".\n>     \n> Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n\nNice.\n\n-- >8 --\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n diff.c |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\n84981f9ad963f050abf4fe33ac07d36b4ea90c6d\ndiff --git a/diff.c b/diff.c\nindex c120239..f1b672d 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -438,8 +438,8 @@ static void builtin_diffstat(const char \n \t\txdemitcb_t ecb;\n \n \t\txpp.flags = XDF_NEED_MINIMAL;\n-\t\txecfg.ctxlen = 3;\n-\t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n+\t\txecfg.ctxlen = 0;\n+\t\txecfg.flags = 0;\n \t\tecb.outf = xdiff_outf;\n \t\tecb.priv = diffstat;\n \t\txdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);\n-- \n1.3.0.rc3.g9306\n"}]}