{"thread":{"id":"23326","subject":"[PATCH 0/2] gitk --color-words","startedAt":"2010-04-04T13:46:37Z","lastAt":"2010-04-13T09:27:52Z","messageCount":15,"participants":["Thomas Rast","Junio C Hamano","Miles Bader"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"138536","messageId":"cover.1270388195.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"cover.1269996525.git.trast@student.ethz","subject":"[PATCH 0/2] gitk --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-04T13:46:37Z","receivedAt":"2010-04-04T13:46:37Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Miles Bader wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> > How readable can you make this for human consumption while still keeping\n> > it machine readable?  The answer could be it already is human readble.\n> >\n> > Two reasons I ask the above question are that I find the feature quite\n> > interesting, and would want to see if it can be also fed to humans, and\n> > that the combination of this new option and the existing --color-words is\n> > misnamed.\n> \n> There's the format used by the \"wdiff\" program, which is more like\n> traditional diff output in that it doesn't use color, but is human\n> friendly, and also seems to be somewhat machine-parseable:\n> \n>    $ echo 'This is a test' > /tmp/a\n>    $ echo 'This is funky test' > /tmp/b\n>    $ wdiff /tmp/a /tmp/b\n>    This is [-a-] {+funky+} test\n> \n> [I say \"somewhat\" because wdiff itself doesn't appear to escape potentially\n> ambiguous content, e.g., if there's actually a \"{+\" in the file....]\n\nJunio C Hamano wrote:\n> If you call this --word-diff, then it would become more clear that\n> --color-words perhaps should have been called --word-diff=color or\n> something.\n\nExcellent ideas!  I don't have anything to add ;-)\n\nThis makes [1/2] a rather new patch though, I moved the whole\nprefix/suffix handing further out to accommodate different styles.\n\nThere's a little change in [2/2] apart from the obvious option\nrenaming, too: it interprets --color-words and --word-diff as an\ninitial setting for the checkbox.\n\n\nThomas Rast (2):\n  diff: add --word-diff option that generalizes --color-words\n  gitk: add the equivalent of diff --color-words\n"},{"id":"138538","messageId":"a1e9ef6a4eb1d7930f69e9ac4b63dc3152ebc98c.1270388195.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"cover.1270388195.git.trast@student.ethz.ch","subject":"[PATCH 1/2] diff: add --word-diff option that generalizes --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-04T13:46:38Z","receivedAt":"2010-04-04T13:46:38Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This teaches the --color-words engine a more general interface that\nsupports two new modes:\n\n* --word-diff=plain, inspired by the 'wdiff' utility (most similar to\n  'wdiff -n <old> <new>'): uses delimiters [-removed-] and {+added+}\n\n* --word-diff=porcelain, which generates an ad-hoc machine readable\n  format:\n  - each diff unit is prefixed by [-+ ] and terminated by newline as\n    in unified diff\n  - newlines in the input are output as a line consisting only of a\n    tilde '~'\n\n--color-words becomes a synonym for --word-diff=color.  Also adds some\ncompatibility/convenience options.\n\nThanks to Junio C Hamano and Miles Bader for good ideas.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/diff-options.txt  |   39 +++++++++-\n Documentation/gitattributes.txt |    2 +-\n color.c                         |   28 -------\n color.h                         |    1 -\n diff.c                          |  148 ++++++++++++++++++++++++++++++++++-----\n diff.h                          |   11 +++-\n t/t4034-diff-words.sh           |   70 ++++++++++++++++++\n 7 files changed, 245 insertions(+), 54 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 60e922e..e51fc9a 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -126,11 +126,38 @@ any of those replacements occurred.\n \tgives the default to color output.\n \tSame as `--color=never`.\n \n---color-words[=<regex>]::\n-\tShow colored word diff, i.e., color words which have changed.\n-\tBy default, words are separated by whitespace.\n+--word-diff[=<mode>]::\n+\tShow a word diff, using the <mode> to delimit changed words.\n+\tBy default, words are delimited by whitespace; see\n+\t`--word-diff-regex` below.  The <mode> defaults to 'auto', and\n+\tmust be one of:\n++\n+--\n+color::\n+\tHighlight changed words with colors.  Implies `--color`.\n+plain::\n+\tShow words as `[-removed-]` and `{+added+}`.  Makes no\n+\tattempts to escape the delimiters if they appear in the input,\n+\tso the output may be ambiguous.\n+porcelain::\n+\tUse a special line-based format intended for script\n+\tconsumption.  Added/removed/unchanged runs are printed in the\n+\tusual unified diff format, starting with a `+`/`-`/` `\n+\tcharacter at the beginning of the line and extending to the\n+\tend of the line.  Newlines in the input are represented by a\n+\ttilde `~` on a line of its own.\n+auto::\n+\t'color' if color is enabled, 'plain' otherwise.\n+none::\n+\tDisable word diff again.\n+--\n+\n+--word-diff-regex=<regex>::\n+\tUse <regex> to decide what a word is, instead of considering\n+\truns of non-whitespace to be a word.  Also implies\n+\t`--word-diff=auto` unless it was already enabled.\n +\n-When a <regex> is specified, every non-overlapping match of the\n+Every non-overlapping match of the\n <regex> is considered a word.  Anything between these matches is\n considered whitespace and ignored(!) for the purposes of finding\n differences.  You may want to append `|[^[:space:]]` to your regular\n@@ -142,6 +169,10 @@ The regex can also be set via a diff driver or configuration option, see\n linkgit:gitattributes[1] or linkgit:git-config[1].  Giving it explicitly\n overrides any diff driver or configuration setting.  Diff drivers\n override configuration settings.\n+\n+--color-words[=<regex>]::\n+\tEquivalent to `--word-diff=color` plus (if a regex was\n+\tspecified) `--word-diff-regex=<regex>`.\n endif::git-format-patch[]\n \n --no-renames::\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex d892e64..7554fcd 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -360,7 +360,7 @@ patterns are available:\n Customizing word diff\n ^^^^^^^^^^^^^^^^^^^^^\n \n-You can customize the rules that `git diff --color-words` uses to\n+You can customize the rules that `git diff --word-diff` uses to\n split words in a line, by specifying an appropriate regular expression\n in the \"diff.*.wordRegex\" configuration variable.  For example, in TeX\n a backslash followed by a sequence of letters forms a command, but\ndiff --git a/color.c b/color.c\nindex bcf4e2c..1b00554 100644\n--- a/color.c\n+++ b/color.c\n@@ -211,31 +211,3 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)\n \tva_end(args);\n \treturn r;\n }\n-\n-/*\n- * This function splits the buffer by newlines and colors the lines individually.\n- *\n- * Returns 0 on success.\n- */\n-int color_fwrite_lines(FILE *fp, const char *color,\n-\t\tsize_t count, const char *buf)\n-{\n-\tif (!*color)\n-\t\treturn fwrite(buf, count, 1, fp) != 1;\n-\twhile (count) {\n-\t\tchar *p = memchr(buf, '\\n', count);\n-\t\tif (p != buf && (fputs(color, fp) < 0 ||\n-\t\t\t\tfwrite(buf, p ? p - buf : count, 1, fp) != 1 ||\n-\t\t\t\tfputs(GIT_COLOR_RESET, fp) < 0))\n-\t\t\treturn -1;\n-\t\tif (!p)\n-\t\t\treturn 0;\n-\t\tif (fputc('\\n', fp) < 0)\n-\t\t\treturn -1;\n-\t\tcount -= p + 1 - buf;\n-\t\tbuf = p + 1;\n-\t}\n-\treturn 0;\n-}\n-\n-\ndiff --git a/color.h b/color.h\nindex 5c264b0..03ca064 100644\n--- a/color.h\n+++ b/color.h\n@@ -61,6 +61,5 @@\n int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);\n __attribute__((format (printf, 3, 4)))\n int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);\n-int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);\n \n #endif /* COLOR_H */\ndiff --git a/diff.c b/diff.c\nindex f5d93e9..dca310e 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -572,16 +572,60 @@ static void diff_words_append(char *line, unsigned long len,\n \tbuffer->text.ptr[buffer->text.size] = '\\0';\n }\n \n+struct diff_words_style\n+{\n+\tenum diff_words_type type;\n+\tint is_color;\n+\tconst char *new_prefix;\n+\tconst char *new_suffix;\n+\tconst char *old_prefix;\n+\tconst char *old_suffix;\n+\tconst char *ctx_prefix;\n+\tconst char *ctx_suffix;\n+\tconst char *newline;\n+};\n+\n+struct diff_words_style diff_words_styles[] = {\n+\t{ DIFF_WORDS_PORCELAIN, 0, \"+\", \"\\n\", \"-\", \"\\n\", \" \", \"\\n\", \"~\\n\" },\n+\t{ DIFF_WORDS_PLAIN, 0, \"{+\", \"+}\", \"[-\", \"-]\", \"\", \"\", \"\\n\" },\n+\t{ DIFF_WORDS_COLOR, 1, NULL, NULL, NULL, NULL, NULL, NULL, \"\\n\" }\n+};\n+\n struct diff_words_data {\n \tstruct diff_words_buffer minus, plus;\n \tconst char *current_plus;\n \tFILE *file;\n \tregex_t *word_regex;\n+\tenum diff_words_type type;\n+\tstruct diff_words_style *style;\n };\n \n+static int fn_out_diff_words_write_helper(FILE *fp,\n+\t\t\t\t\t  const char *prefix,\n+\t\t\t\t\t  const char *suffix,\n+\t\t\t\t\t  const char *newline,\n+\t\t\t\t\t  size_t count, const char *buf)\n+{\n+\twhile (count) {\n+\t\tchar *p = memchr(buf, '\\n', count);\n+\t\tif (p != buf && (fputs(prefix, fp) < 0 ||\n+\t\t\t\t fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||\n+\t\t\t\t fputs(suffix, fp) < 0))\n+\t\t\treturn -1;\n+\t\tif (!p)\n+\t\t\treturn 0;\n+\t\tif (fputs(newline, fp) < 0)\n+\t\t\treturn -1;\n+\t\tcount -= p + 1 - buf;\n+\t\tbuf = p + 1;\n+\t}\n+\treturn 0;\n+}\n+\n static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n {\n \tstruct diff_words_data *diff_words = priv;\n+\tstruct diff_words_style *style = diff_words->style;\n \tint minus_first, minus_len, plus_first, plus_len;\n \tconst char *minus_begin, *minus_end, *plus_begin, *plus_end;\n \n@@ -605,16 +649,20 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n \t\tplus_begin = plus_end = diff_words->plus.orig[plus_first].end;\n \n \tif (diff_words->current_plus != plus_begin)\n-\t\tfwrite(diff_words->current_plus,\n-\t\t\t\tplus_begin - diff_words->current_plus, 1,\n-\t\t\t\tdiff_words->file);\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\t\tstyle->ctx_prefix, style->ctx_suffix,\n+\t\t\t\tstyle->newline,\n+\t\t\t\tplus_begin - diff_words->current_plus,\n+\t\t\t\tdiff_words->current_plus);\n \tif (minus_begin != minus_end)\n-\t\tcolor_fwrite_lines(diff_words->file,\n-\t\t\t\tdiff_get_color(1, DIFF_FILE_OLD),\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\t\tstyle->old_prefix, style->old_suffix,\n+\t\t\t\tstyle->newline,\n \t\t\t\tminus_end - minus_begin, minus_begin);\n \tif (plus_begin != plus_end)\n-\t\tcolor_fwrite_lines(diff_words->file,\n-\t\t\t\tdiff_get_color(1, DIFF_FILE_NEW),\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\t\tstyle->new_prefix, style->new_suffix,\n+\t\t\t\tstyle->newline,\n \t\t\t\tplus_end - plus_begin, plus_begin);\n \n \tdiff_words->current_plus = plus_end;\n@@ -697,11 +745,13 @@ static void diff_words_show(struct diff_words_data *diff_words)\n \txdemitconf_t xecfg;\n \txdemitcb_t ecb;\n \tmmfile_t minus, plus;\n+\tstruct diff_words_style *style = diff_words->style;\n \n \t/* special case: only removal */\n \tif (!diff_words->plus.text.size) {\n-\t\tcolor_fwrite_lines(diff_words->file,\n-\t\t\tdiff_get_color(1, DIFF_FILE_OLD),\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\tstyle->old_prefix, style->old_suffix,\n+\t\t\tstyle->newline,\n \t\t\tdiff_words->minus.text.size, diff_words->minus.text.ptr);\n \t\tdiff_words->minus.text.size = 0;\n \t\treturn;\n@@ -722,10 +772,11 @@ static void diff_words_show(struct diff_words_data *diff_words)\n \tfree(plus.ptr);\n \tif (diff_words->current_plus != diff_words->plus.text.ptr +\n \t\t\tdiff_words->plus.text.size)\n-\t\tfwrite(diff_words->current_plus,\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\tstyle->ctx_prefix, style->ctx_suffix,\n+\t\t\tstyle->newline,\n \t\t\tdiff_words->plus.text.ptr + diff_words->plus.text.size\n-\t\t\t- diff_words->current_plus, 1,\n-\t\t\tdiff_words->file);\n+\t\t\t- diff_words->current_plus, diff_words->current_plus);\n \tdiff_words->minus.text.size = diff_words->plus.text.size = 0;\n }\n \n@@ -837,6 +888,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \n \tif (len < 1) {\n \t\temit_line(ecbdata->file, reset, reset, line, len);\n+\t\tif (ecbdata->diff_words\n+\t\t    && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)\n+\t\t\tfputs(\"~\\n\", ecbdata->file);\n \t\treturn;\n \t}\n \n@@ -851,9 +905,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\t\treturn;\n \t\t}\n \t\tdiff_words_flush(ecbdata);\n-\t\tline++;\n-\t\tlen--;\n-\t\temit_line(ecbdata->file, plain, reset, line, len);\n+\t\tif (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {\n+\t\t\temit_line(ecbdata->file, plain, reset, line, len);\n+\t\t\tfputs(\"~\\n\", ecbdata->file);\n+\t\t} else {\n+\t\t\t/* don't print the prefix character */\n+\t\t\temit_line(ecbdata->file, plain, reset, line+1, len-1);\n+\t\t}\n \t\treturn;\n \t}\n \n@@ -1755,10 +1813,19 @@ static void builtin_diff(const char *name_a,\n \t\t\txecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);\n \t\telse if (!prefixcmp(diffopts, \"-u\"))\n \t\t\txecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);\n-\t\tif (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {\n+\t\tif (o->word_diff) {\n+\t\t\tint i;\n+\n+\t\t\tif (o->word_diff == DIFF_WORDS_AUTO) {\n+\t\t\t\tif (DIFF_OPT_TST(o, COLOR_DIFF))\n+\t\t\t\t\to->word_diff = DIFF_WORDS_COLOR;\n+\t\t\t\telse\n+\t\t\t\t\to->word_diff = DIFF_WORDS_PLAIN;\n+\t\t\t}\n \t\t\tecbdata.diff_words =\n \t\t\t\txcalloc(1, sizeof(struct diff_words_data));\n \t\t\tecbdata.diff_words->file = o->file;\n+\t\t\tecbdata.diff_words->type = o->word_diff;\n \t\t\tif (!o->word_regex)\n \t\t\t\to->word_regex = userdiff_word_regex(one);\n \t\t\tif (!o->word_regex)\n@@ -1774,10 +1841,27 @@ static void builtin_diff(const char *name_a,\n \t\t\t\t\tdie (\"Invalid regular expression: %s\",\n \t\t\t\t\t\t\to->word_regex);\n \t\t\t}\n+\t\t\tfor (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {\n+\t\t\t\tif (o->word_diff == diff_words_styles[i].type) {\n+\t\t\t\t\tecbdata.diff_words->style =\n+\t\t\t\t\t\t&diff_words_styles[i];\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (ecbdata.diff_words->style->is_color) {\n+\t\t\t\tstruct diff_words_style *s\n+\t\t\t\t\t= ecbdata.diff_words->style;\n+\t\t\t\ts->new_prefix = diff_get_color_opt(o, DIFF_FILE_NEW);\n+\t\t\t\ts->old_prefix = diff_get_color_opt(o, DIFF_FILE_OLD);\n+\t\t\t\ts->ctx_prefix = diff_get_color_opt(o, DIFF_PLAIN);\n+\t\t\t\ts->new_suffix = s->old_suffix\n+\t\t\t\t\t= diff_get_color_opt(o, DIFF_RESET);\n+\t\t\t\ts->ctx_suffix = \"\";\n+\t\t\t}\n \t\t}\n \t\txdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,\n \t\t\t      &xpp, &xecfg, &ecb);\n-\t\tif (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))\n+\t\tif (o->word_diff)\n \t\t\tfree_diff_words_data(&ecbdata);\n \t\tif (textconv_one)\n \t\t\tfree(mf1.ptr);\n@@ -2845,13 +2929,39 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\tDIFF_OPT_CLR(options, COLOR_DIFF);\n \telse if (!strcmp(arg, \"--color-words\")) {\n \t\tDIFF_OPT_SET(options, COLOR_DIFF);\n-\t\tDIFF_OPT_SET(options, COLOR_DIFF_WORDS);\n+\t\toptions->word_diff = DIFF_WORDS_COLOR;\n \t}\n \telse if (!prefixcmp(arg, \"--color-words=\")) {\n \t\tDIFF_OPT_SET(options, COLOR_DIFF);\n-\t\tDIFF_OPT_SET(options, COLOR_DIFF_WORDS);\n+\t\toptions->word_diff = DIFF_WORDS_COLOR;\n \t\toptions->word_regex = arg + 14;\n \t}\n+\telse if (!strcmp(arg, \"--word-diff\")) {\n+\t\tif (options->word_diff == DIFF_WORDS_NONE)\n+\t\t\toptions->word_diff = DIFF_WORDS_AUTO;\n+\t}\n+\telse if (!prefixcmp(arg, \"--word-diff=\")) {\n+\t\tconst char *type = arg + 12;\n+\t\tif (!strcmp(type, \"auto\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_AUTO;\n+\t\telse if (!strcmp(type, \"plain\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_PLAIN;\n+\t\telse if (!strcmp(type, \"color\")) {\n+\t\t\tDIFF_OPT_SET(options, COLOR_DIFF);\n+\t\t\toptions->word_diff = DIFF_WORDS_COLOR;\n+\t\t}\n+\t\telse if (!strcmp(type, \"porcelain\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_PORCELAIN;\n+\t\telse if (!strcmp(type, \"none\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_NONE;\n+\t\telse\n+\t\t\tdie(\"bad --word-diff argument: %s\", type);\n+\t}\n+\telse if (!prefixcmp(arg, \"--word-diff-regex=\")) {\n+\t\tif (options->word_diff == DIFF_WORDS_NONE)\n+\t\t\toptions->word_diff = DIFF_WORDS_AUTO;\n+\t\toptions->word_regex = arg + 18;\n+\t}\n \telse if (!strcmp(arg, \"--exit-code\"))\n \t\tDIFF_OPT_SET(options, EXIT_WITH_STATUS);\n \telse if (!strcmp(arg, \"--quiet\"))\ndiff --git a/diff.h b/diff.h\nindex 6a71013..7c8e3ff 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -54,7 +54,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)\n #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)\n #define DIFF_OPT_COLOR_DIFF          (1 <<  8)\n-#define DIFF_OPT_COLOR_DIFF_WORDS    (1 <<  9)\n+/* (1 <<  9) unused */\n #define DIFF_OPT_HAS_CHANGES         (1 << 10)\n #define DIFF_OPT_QUICK               (1 << 11)\n #define DIFF_OPT_NO_INDEX            (1 << 12)\n@@ -79,6 +79,14 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)\n #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)\n \n+enum diff_words_type {\n+\tDIFF_WORDS_NONE = 0,\n+\tDIFF_WORDS_AUTO,\n+\tDIFF_WORDS_PORCELAIN,\n+\tDIFF_WORDS_PLAIN,\n+\tDIFF_WORDS_COLOR\n+};\n+\n struct diff_options {\n \tconst char *filter;\n \tconst char *orderfile;\n@@ -108,6 +116,7 @@ struct diff_options {\n \tint stat_width;\n \tint stat_name_width;\n \tconst char *word_regex;\n+\tenum diff_words_type word_diff;\n \n \t/* this is set by diffcore for DIFF_FORMAT_PATCH */\n \tint found_changes;\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex 2e2e103..764368d 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -55,6 +55,72 @@ test_expect_success 'word diff with runs of whitespace' '\n \n '\n \n+test_expect_success '--word-diff=color' '\n+\n+\tword_diff --word-diff=color\n+\n+'\n+\n+test_expect_success '--color --word-diff=color' '\n+\n+\tword_diff --color --word-diff=auto\n+\n+'\n+\n+sed 's/#.*$//' > expect <<EOF\n+diff --git a/pre b/post\n+index 330b04f..5ed8eff 100644\n+--- a/pre\n++++ b/post\n+@@ -1,3 +1,7 @@\n+-h(4)\n++h(4),hh[44]\n+~\n+ # significant space\n+~\n+ a = b + c\n+~\n+~\n++aa = a\n+~\n+~\n++aeff = aeff * ( aaa )\n+~\n+EOF\n+\n+test_expect_success '--word-diff=porcelain' '\n+\n+\tword_diff --word-diff=porcelain\n+\n+'\n+\n+cat > expect <<EOF\n+diff --git a/pre b/post\n+index 330b04f..5ed8eff 100644\n+--- a/pre\n++++ b/post\n+@@ -1,3 +1,7 @@\n+[-h(4)-]{+h(4),hh[44]+}\n+\n+a = b + c\n+\n+{+aa = a+}\n+\n+{+aeff = aeff * ( aaa )+}\n+EOF\n+\n+test_expect_success '--word-diff=plain' '\n+\n+\tword_diff --word-diff=plain\n+\n+'\n+\n+test_expect_success '--no-color --word-diff=color' '\n+\n+\tword_diff --no-color --word-diff=auto\n+\n+'\n+\n cat > expect <<\\EOF\n <WHITE>diff --git a/pre b/post<RESET>\n <WHITE>index 330b04f..5ed8eff 100644<RESET>\n@@ -143,6 +209,10 @@ test_expect_success 'command-line overrides config' '\n \tword_diff --color-words=\"[a-z]+\"\n '\n \n+test_expect_success 'command-line overrides config: --word-diff-regex' '\n+\tword_diff --color --word-diff-regex=\"[a-z]+\"\n+'\n+\n cp expect.non-whitespace-is-word expect\n \n test_expect_success '.gitattributes override config' '\n-- \n1.7.0.3.521.ga1e9e\n"},{"id":"138537","messageId":"d144dbcdc950d54837009b740ad1fa791b5e479a.1270388195.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"cover.1270388195.git.trast@student.ethz.ch","subject":"[PATCH 2/2] gitk: add the equivalent of diff --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-04T13:46:39Z","receivedAt":"2010-04-04T13:46:39Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Use the newly added 'diff --word-diff=porcelain' to teach gitk a\ncolor-words mode, triggered by a checkbox.\n\nAs an extra twist, automatically enable this word-diff support when\nthe user mentions a word-diff related option on the command line.\nThese options were previously ignored because they would break diff\nparsing.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n gitk |   37 ++++++++++++++++++++++++++++++++++---\n 1 files changed, 34 insertions(+), 3 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 1f36a3e..05ed053 100755\n--- a/gitk\n+++ b/gitk\n@@ -131,6 +131,7 @@ proc unmerged_files {files} {\n \n proc parseviewargs {n arglist} {\n     global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env\n+    global worddiff\n \n     set vdatemode($n) 0\n     set vmergeonly($n) 0\n@@ -168,7 +169,7 @@ proc parseviewargs {n arglist} {\n \t\tlappend diffargs $arg\n \t    }\n \t    \"--raw\" - \"--patch-with-raw\" - \"--patch-with-stat\" -\n-\t    \"--name-only\" - \"--name-status\" - \"--color\" - \"--color-words\" -\n+\t    \"--name-only\" - \"--name-status\" - \"--color\" -\n \t    \"--log-size\" - \"--pretty=*\" - \"--decorate\" - \"--abbrev-commit\" -\n \t    \"--cc\" - \"-z\" - \"--header\" - \"--parents\" - \"--boundary\" -\n \t    \"--no-color\" - \"-g\" - \"--walk-reflogs\" - \"--no-walk\" -\n@@ -177,6 +178,11 @@ proc parseviewargs {n arglist} {\n \t\t# These cause our parsing of git log's output to fail, or else\n \t\t# they're options we want to set ourselves, so ignore them.\n \t    }\n+\t    \"--color-words*\" - \"--word-diff*\" {\n+\t\t# These trigger a word diff in the console interface,\n+\t\t# so help the user by enabling our own support\n+\t\tset worddiff 1\n+\t    }\n \t    \"--stat=*\" - \"--numstat\" - \"--shortstat\" - \"--summary\" -\n \t    \"--check\" - \"--exit-code\" - \"--quiet\" - \"--topo-order\" -\n \t    \"--full-history\" - \"--dense\" - \"--sparse\" -\n@@ -2240,6 +2246,9 @@ proc makewindow {} {\n     ${NS}::checkbutton .bleft.mid.ignspace -text [mc \"Ignore space change\"] \\\n \t-command changeignorespace -variable ignorespace\n     pack .bleft.mid.ignspace -side left -padx 5\n+    ${NS}::checkbutton .bleft.mid.worddiff -text [mc \"Word diff\"] \\\n+\t-command changeworddiff -variable worddiff\n+    pack .bleft.mid.worddiff -side left -padx 5\n     set ctext .bleft.bottom.ctext\n     text $ctext -background $bgcolor -foreground $fgcolor \\\n \t-state disabled -font textfont \\\n@@ -7494,11 +7503,16 @@ proc changeignorespace {} {\n     reselectline\n }\n \n+proc changeworddiff {} {\n+    reselectline\n+}\n+\n proc getblobdiffs {ids} {\n     global blobdifffd diffids env\n     global diffinhdr treediffs\n     global diffcontext\n     global ignorespace\n+    global worddiff\n     global limitdiffs vfilelimit curview\n     global diffencoding targetline diffnparents\n     global git_version\n@@ -7515,6 +7529,9 @@ proc getblobdiffs {ids} {\n     if {$ignorespace} {\n \tappend cmd \" -w\"\n     }\n+    if {$worddiff} {\n+\tappend cmd \" --word-diff=porcelain\"\n+    }\n     if {$limitdiffs && $vfilelimit($curview) ne {}} {\n \tset cmd [concat $cmd -- $vfilelimit($curview)]\n     }\n@@ -7599,6 +7616,7 @@ proc getblobdiffline {bdf ids} {\n     global ctext_file_names ctext_file_lines\n     global diffinhdr treediffs mergemax diffnparents\n     global diffencoding jump_to_here targetline diffline\n+    global worddiff\n \n     set nr 0\n     $ctext conf -state normal\n@@ -7727,15 +7745,22 @@ proc getblobdiffline {bdf ids} {\n \t    # parse the prefix - one ' ', '-' or '+' for each parent\n \t    set prefix [string range $line 0 [expr {$diffnparents - 1}]]\n \t    set tag [expr {$diffnparents > 1? \"m\": \"d\"}]\n+\t    set dowords [expr {$worddiff && $diffnparents == 1}]\n \t    if {[string trim $prefix \" -+\"] eq {}} {\n \t\t# prefix only has \" \", \"-\" and \"+\" in it: normal diff line\n \t\tset num [string first \"-\" $prefix]\n+\t\tif {$dowords} {\n+\t\t    set line [string range $line 1 end]\n+\t\t}\n \t\tif {$num >= 0} {\n \t\t    # removed line, first parent with line is $num\n \t\t    if {$num >= $mergemax} {\n \t\t\tset num \"max\"\n \t\t    }\n-\t\t    $ctext insert end \"$line\\n\" $tag$num\n+\t\t    $ctext insert end \"$line\" $tag$num\n+\t\t    if {!$dowords} {\n+\t\t\t$ctext insert end \"\\n\" $tag$num\n+\t\t    }\n \t\t} else {\n \t\t    set tags {}\n \t\t    if {[string first \"+\" $prefix] >= 0} {\n@@ -7759,8 +7784,13 @@ proc getblobdiffline {bdf ids} {\n \t\t\t    incr diffline\n \t\t\t}\n \t\t    }\n-\t\t    $ctext insert end \"$line\\n\" $tags\n+\t\t    $ctext insert end \"$line\" $tags\n+\t\t    if {!$dowords} {\n+\t\t\t$ctext insert end \"\\n\" $tags\n+\t\t    }\n \t\t}\n+\t    } elseif {$dowords && $prefix eq \"~\"} {\n+\t\t$ctext insert end \"\\n\" {}\n \t    } else {\n \t\t# \"\\ No newline at end of file\",\n \t\t# or something else we don't recognize\n@@ -11379,6 +11409,7 @@ if {[tk windowingsystem] eq \"win32\"} {\n set diffcolors {red \"#00a000\" blue}\n set diffcontext 3\n set ignorespace 0\n+set worddiff 0\n set markbgcolor \"#e0e0ff\"\n \n set circlecolors {white blue gray blue blue}\n-- \n1.7.0.3.521.ga1e9e\n"},{"id":"138574","messageId":"7v39zay7or.fsf@alter.siamese.dyndns.org","threadId":"23326","inReplyTo":"a1e9ef6a4eb1d7930f69e9ac4b63dc3152ebc98c.1270388195.git.trast@student.ethz.ch","subject":"Re: [PATCH 1/2] diff: add --word-diff option that generalizes --color-words","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-05T02:06:12Z","receivedAt":"2010-04-05T02:06:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> This teaches the --color-words engine a more general interface that\n> supports two new modes:\n>\n> * --word-diff=plain, inspired by the 'wdiff' utility (most similar to\n>   'wdiff -n <old> <new>'): uses delimiters [-removed-] and {+added+}\n>\n> * --word-diff=porcelain, which generates an ad-hoc machine readable\n>   format:\n>   - each diff unit is prefixed by [-+ ] and terminated by newline as\n>     in unified diff\n>   - newlines in the input are output as a line consisting only of a\n>     tilde '~'\n\nThanks.  This was a fun feature to look at.\n\nI think it is a bug that \"git show --word-diff\" gives the colored format\noutput when I have \"color.ui\" configuration.\n\nEven though I may have \"color.ui = auto\" in the configuration, I am\ntelling the command to do a --word-diff, not --color-words, from the\ncommand line, and that should override color.ui settings.\n\nSo I think a request for --word-diff that is not --word-diff=color should\nnever use the --color-words _unless_ there is --color on the command line:\n\n\tgit show --word-diff\n\tgit show --word-diff=plain\n\tgit show --word-diff=porcelain\n\nAll of the above may paint hunk headers and metainfo the usual way when I\nhave \"color.ui\" set, but I do not want them to be painting the diff part\nlike --color-words does.  I am fine if \"porcelain\" did not to paint the\nmetainfo, but I see this feature as three different output types of how\nword diff is presented, so in that sense, it probably is better to force\nscripts to explicitly ask for no-color, i.e.\n\n\tgit show --no-color --word-diff=porcelain\n\nif they want to read and interpret metainfo.\n\nWhen the command line asks for color explicitly, then we should see the\ngood old --color-words:\n\n\tgit show --color-words\n\tgit show --word-diff=color\n\nI am not sure what the following two should do.  One could argue that\nthese default to --color-words; or --color should apply only to the\ncoloring of metainfo but not the diff part:\n\n        git show --color --word-diff\n        git show --word-diff --color\n\nI am slightly in favor of doing the same as --color-words, but people may\nhave different opinions.\n\nThe following of course should show the plain word diff but the metainfo\nand friends colored:\n\n        git show --word-diff=plain --color\n        git show --color --word-diff=plain\n\nIt might be just the matter of defaulting --word-diff without \"=<type>\"\nnot to \"auto\" but to \"plain\".  I haven't looked at the code closely yet.\n"},{"id":"138585","messageId":"201004051220.47400.trast@student.ethz.ch","threadId":"23326","inReplyTo":"7v39zay7or.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/2] diff: add --word-diff option that generalizes --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-05T10:20:46Z","receivedAt":"2010-04-05T10:20:46Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Junio C Hamano wrote:\n> \n> I think it is a bug that \"git show --word-diff\" gives the colored format\n> output when I have \"color.ui\" configuration.\n> \n> Even though I may have \"color.ui = auto\" in the configuration, I am\n> telling the command to do a --word-diff, not --color-words, from the\n> command line, and that should override color.ui settings.\n\nI don't really see why the user would forfeit the convenience and\nreadability of a color-words diff when the terminal supports colors.\nGranted, this is probably the first instance where the colors actually\nchange the output format instead of just making easier to read, but\nstill?\n\nBut:\n\n> It might be just the matter of defaulting --word-diff without \"=<type>\"\n> not to \"auto\" but to \"plain\".  I haven't looked at the code closely yet.\n\nYes.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"138651","messageId":"7v6345pwjw.fsf@alter.siamese.dyndns.org","threadId":"23326","inReplyTo":"201004051220.47400.trast@student.ethz.ch","subject":"Re: [PATCH 1/2] diff: add --word-diff option that generalizes --color-words","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-05T18:46:11Z","receivedAt":"2010-04-05T18:46:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n>> I think it is a bug that \"git show --word-diff\" gives the colored format\n>> output when I have \"color.ui\" configuration.\n>> \n>> Even though I may have \"color.ui = auto\" in the configuration, I am\n>> telling the command to do a --word-diff, not --color-words, from the\n>> command line, and that should override color.ui settings.\n>\n> I don't really see why the user would forfeit the convenience and\n> readability of a color-words diff when the terminal supports colors.\n\nThere is one difference between other uses of colors and color-words, but\nI can imagine that ordinary people may not have even realized nor thought\nabout it.\n\nTo people who are somewhat but not completely color-challenged (like\nmyself), it still helps to paint hunk headers in a color that is different\nfrom the body text to make the boundary of each hunk more visible.  Even\nwithout the perception of exact color/hue, the contrast alone helps in\nthat case.\n\nThe body of the diff is painted in deleted and added colors; the color is\nused only as a way to additionally enhance the output, while still keeping\nthe plus/minus/SP at the leftmost column.  Even to somebody who is\ncompletely color challenged, the necessary information is still there.\n\nCompare --color-words with these.  The output uses color as the _only_ way\nto say which words appear before and after.  The contrast among the three\ncolors used for the plain body, deleted and added text may still help, or\nit may not, to a partially color challenged person.\n\nI also happen to see \"--word-diff=color\" more as just a customization to\nthe \"plain\" output formatting to say \"use these byte sequences that happen\nto be ANSI color codes, instead of '{+', '+}', etc.\" than as a part of\nwhat the ui.color switch wants to specify, which is \"are various parts of\nthe output colored to further help distinguishing them visually?\"\n\nSo color me somewhat biased, but there is a case where the suggestion in\nthe message you are responding to makes a difference.\n\nI also think --color --word-diff=plain could show \"{+new+}\" in green and\n\"[-old-]\" in red and that may make things more consistent.\n"},{"id":"138657","messageId":"s2kfc339e4a1004051153yc950b858lb919740f0de80e9@mail.gmail.com","threadId":"23326","inReplyTo":"7v6345pwjw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/2] diff: add --word-diff option that generalizes --color-words","fromName":"Miles Bader","fromEmail":"miles@gnu.org","sentAt":"2010-04-05T18:53:41Z","receivedAt":"2010-04-05T18:53:41Z","isPatch":true,"sender":{"key":"miles@gnu.org","avatar":"https://gravatar.com/avatar/01069b69593af7bff28e2f97afeb3644ae6fe2f5f56cb3a8cf34c5fb8c36efe5?d=mp&s=160"},"body":"On Tue, Apr 6, 2010 at 3:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> I also think --color --word-diff=plain could show \"{+new+}\" in green and\n> \"[-old-]\" in red and that may make things more consistent.\n\nI agree.\n\nFor some reason, even though I can see the red and green well enough,\nI find the current --color-words output hard to parse.\nThe use of _only_ color to distinguish old/new somehow doesn't jive\nwith my brain...\n\nI find _highlighting_ using color very useful, but I think using\nsyntax and color together is better than using just color.\n\n-Miles\n\n-- \nDo not taunt Happy Fun Ball.\n"},{"id":"138730","messageId":"201004061120.04060.trast@student.ethz.ch","threadId":"23326","inReplyTo":"7v6345pwjw.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 1/2] diff: add --word-diff option that generalizes --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-06T09:20:03Z","receivedAt":"2010-04-06T09:20:03Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Junio C Hamano wrote:\n> \n> There is one difference between other uses of colors and color-words, but\n> I can imagine that ordinary people may not have even realized nor thought\n> about it.\n> \n> To people who are somewhat but not completely color-challenged (like\n> myself), it still helps to paint hunk headers in a color that is different\n> from the body text to make the boundary of each hunk more visible.  Even\n> without the perception of exact color/hue, the contrast alone helps in\n> that case.\n\nI see now.  My apologies for not thinking of that case!\n\nI'll make a new patch.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"139326","messageId":"cover.1271077334.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"s2kfc339e4a1004051153yc950b858lb919740f0de80e9@mail.gmail.com","subject":"[PATCH v3 0/2] gitk --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-12T13:07:43Z","receivedAt":"2010-04-12T13:07:43Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"During development an unrelated gitk diff parsing bug hit me, which is\nthe new 2/3.  I think it should go in maint if the patch turns out to\nbe correct, but I'd like to have an ACK from Jens first.\n\nJunio C Hamano wrote:\n> I also think --color --word-diff=plain could show \"{+new+}\" in green and\n> \"[-old-]\" in red and that may make things more consistent.\n\nSo here's a patch that does that.  I may be overdoing the \"generic\ndiff style\" code a bit, but it makes for slightly nicer code.\n\nI similarly extended the support in gitk to have two different modes.\nMy use of the dropdown is again sheer voodoo and works merely by\naccident...\n\nI also taught gitk to not show the option and not parse the\ncommand-line options unless the git version is at least v1.7.2, which\nI expect will be the version that has the underlying diff support.\n\nBTW:\n\nMiles Bader wrote:\n> For some reason, even though I can see the red and green well enough,\n> I find the current --color-words output hard to parse.\n> The use of _only_ color to distinguish old/new somehow doesn't jive\n> with my brain...\n\nI set my diff colors to bold red/blue which makes the changed words\nvery visible even in light conditions where green is very hard to tell\nfrom black (sitting outside in the sun or so).\n\n> I find _highlighting_ using color very useful, but I think using\n> syntax and color together is better than using just color.\n\nWell, I for one find the extra markup *very* confusing because I need\nto mentally untangle it from the actual content...\n\n\nThomas Rast (3):\n  diff: add --word-diff option that generalizes --color-words\n  gitk: do not parse \"  >\" context as submodule change\n  gitk: add the equivalent of diff --color-words\n"},{"id":"139327","messageId":"09bfc1bfe19d47267167b264ed03a677ce20013f.1271077334.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"cover.1271077334.git.trast@student.ethz.ch","subject":"[PATCH v3 1/3] diff: add --word-diff option that generalizes --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-12T13:07:44Z","receivedAt":"2010-04-12T13:07:44Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"This teaches the --color-words engine a more general interface that\nsupports two new modes:\n\n* --word-diff=plain, inspired by the 'wdiff' utility (most similar to\n  'wdiff -n <old> <new>'): uses delimiters [-removed-] and {+added+}\n\n* --word-diff=porcelain, which generates an ad-hoc machine readable\n  format:\n  - each diff unit is prefixed by [-+ ] and terminated by newline as\n    in unified diff\n  - newlines in the input are output as a line consisting only of a\n    tilde '~'\n\nBoth of these formats still support color if it is enabled, using it\nto highlight the differences.  --color-words becomes a synonym for\n--word-diff=color, which is the color-only format.  Also adds some\ncompatibility/convenience options.\n\nThanks to Junio C Hamano and Miles Bader for good ideas.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n Documentation/diff-options.txt  |   40 +++++++++-\n Documentation/gitattributes.txt |    2 +-\n color.c                         |   28 -------\n color.h                         |    1 -\n diff.c                          |  154 ++++++++++++++++++++++++++++++++++-----\n diff.h                          |   10 ++-\n t/t4034-diff-words.sh           |  122 +++++++++++++++++++++++++++++++\n 7 files changed, 303 insertions(+), 54 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 60e922e..a616ca5 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -126,11 +126,39 @@ any of those replacements occurred.\n \tgives the default to color output.\n \tSame as `--color=never`.\n \n---color-words[=<regex>]::\n-\tShow colored word diff, i.e., color words which have changed.\n-\tBy default, words are separated by whitespace.\n+--word-diff[=<mode>]::\n+\tShow a word diff, using the <mode> to delimit changed words.\n+\tBy default, words are delimited by whitespace; see\n+\t`--word-diff-regex` below.  The <mode> defaults to 'plain', and\n+\tmust be one of:\n++\n+--\n+color::\n+\tHighlight changed words using only colors.  Implies `--color`.\n+plain::\n+\tShow words as `[-removed-]` and `{+added+}`.  Makes no\n+\tattempts to escape the delimiters if they appear in the input,\n+\tso the output may be ambiguous.\n+porcelain::\n+\tUse a special line-based format intended for script\n+\tconsumption.  Added/removed/unchanged runs are printed in the\n+\tusual unified diff format, starting with a `+`/`-`/` `\n+\tcharacter at the beginning of the line and extending to the\n+\tend of the line.  Newlines in the input are represented by a\n+\ttilde `~` on a line of its own.\n+none::\n+\tDisable word diff again.\n+--\n++\n+Note that despite the name of the first mode, color is used to\n+highlight the changed parts in all modes if enabled.\n+\n+--word-diff-regex=<regex>::\n+\tUse <regex> to decide what a word is, instead of considering\n+\truns of non-whitespace to be a word.  Also implies\n+\t`--word-diff` unless it was already enabled.\n +\n-When a <regex> is specified, every non-overlapping match of the\n+Every non-overlapping match of the\n <regex> is considered a word.  Anything between these matches is\n considered whitespace and ignored(!) for the purposes of finding\n differences.  You may want to append `|[^[:space:]]` to your regular\n@@ -142,6 +170,10 @@ The regex can also be set via a diff driver or configuration option, see\n linkgit:gitattributes[1] or linkgit:git-config[1].  Giving it explicitly\n overrides any diff driver or configuration setting.  Diff drivers\n override configuration settings.\n+\n+--color-words[=<regex>]::\n+\tEquivalent to `--word-diff=color` plus (if a regex was\n+\tspecified) `--word-diff-regex=<regex>`.\n endif::git-format-patch[]\n \n --no-renames::\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex a8500d1..0523a57 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -360,7 +360,7 @@ patterns are available:\n Customizing word diff\n ^^^^^^^^^^^^^^^^^^^^^\n \n-You can customize the rules that `git diff --color-words` uses to\n+You can customize the rules that `git diff --word-diff` uses to\n split words in a line, by specifying an appropriate regular expression\n in the \"diff.*.wordRegex\" configuration variable.  For example, in TeX\n a backslash followed by a sequence of letters forms a command, but\ndiff --git a/color.c b/color.c\nindex bcf4e2c..1b00554 100644\n--- a/color.c\n+++ b/color.c\n@@ -211,31 +211,3 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)\n \tva_end(args);\n \treturn r;\n }\n-\n-/*\n- * This function splits the buffer by newlines and colors the lines individually.\n- *\n- * Returns 0 on success.\n- */\n-int color_fwrite_lines(FILE *fp, const char *color,\n-\t\tsize_t count, const char *buf)\n-{\n-\tif (!*color)\n-\t\treturn fwrite(buf, count, 1, fp) != 1;\n-\twhile (count) {\n-\t\tchar *p = memchr(buf, '\\n', count);\n-\t\tif (p != buf && (fputs(color, fp) < 0 ||\n-\t\t\t\tfwrite(buf, p ? p - buf : count, 1, fp) != 1 ||\n-\t\t\t\tfputs(GIT_COLOR_RESET, fp) < 0))\n-\t\t\treturn -1;\n-\t\tif (!p)\n-\t\t\treturn 0;\n-\t\tif (fputc('\\n', fp) < 0)\n-\t\t\treturn -1;\n-\t\tcount -= p + 1 - buf;\n-\t\tbuf = p + 1;\n-\t}\n-\treturn 0;\n-}\n-\n-\ndiff --git a/color.h b/color.h\nindex 5c264b0..03ca064 100644\n--- a/color.h\n+++ b/color.h\n@@ -61,6 +61,5 @@\n int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);\n __attribute__((format (printf, 3, 4)))\n int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);\n-int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);\n \n #endif /* COLOR_H */\ndiff --git a/diff.c b/diff.c\nindex 7effdac..ca054d4 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -560,16 +560,83 @@ static void diff_words_append(char *line, unsigned long len,\n \tbuffer->text.ptr[buffer->text.size] = '\\0';\n }\n \n+struct diff_words_style_elem\n+{\n+\tconst char *prefix;\n+\tconst char *suffix;\n+\tconst char *color; /* NULL; filled in by the setup code if\n+\t\t\t    * color is enabled */\n+};\n+\n+struct diff_words_style\n+{\n+\tenum diff_words_type type;\n+\tstruct diff_words_style_elem new, old, ctx;\n+\tconst char *newline;\n+};\n+\n+struct diff_words_style diff_words_styles[] = {\n+\t{ DIFF_WORDS_PORCELAIN,\n+\t  {\"+\", \"\\n\", NULL},\n+\t  {\"-\", \"\\n\", NULL},\n+\t  {\" \", \"\\n\", NULL},\n+\t  \"~\\n\"\n+\t},\n+\t{ DIFF_WORDS_PLAIN,\n+\t  {\"{+\", \"+}\", NULL},\n+\t  {\"[-\", \"-]\", NULL},\n+\t  {\"\", \"\", NULL},\n+\t  \"\\n\"\n+\t},\n+\t{ DIFF_WORDS_COLOR,\n+\t  {\"\", \"\", NULL},\n+\t  {\"\", \"\", NULL},\n+\t  {\"\", \"\", NULL},\n+\t  \"\\n\"\n+\t}\n+};\n+\n struct diff_words_data {\n \tstruct diff_words_buffer minus, plus;\n \tconst char *current_plus;\n \tFILE *file;\n \tregex_t *word_regex;\n+\tenum diff_words_type type;\n+\tstruct diff_words_style *style;\n };\n \n+static int fn_out_diff_words_write_helper(FILE *fp,\n+\t\t\t\t\t  struct diff_words_style_elem st_el,\n+\t\t\t\t\t  const char *newline,\n+\t\t\t\t\t  size_t count, const char *buf)\n+{\n+\twhile (count) {\n+\t\tchar *p = memchr(buf, '\\n', count);\n+\t\tif (p != buf) {\n+\t\t\tif (st_el.color && fputs(st_el.color, fp) < 0)\n+\t\t\t\treturn -1;\n+\t\t\tif (fputs(st_el.prefix, fp) < 0 ||\n+\t\t\t    fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||\n+\t\t\t    fputs(st_el.suffix, fp) < 0)\n+\t\t\t\treturn -1;\n+\t\t\tif (st_el.color && strlen(st_el.color)\n+\t\t\t    && fputs(GIT_COLOR_RESET, fp) < 0)\n+\t\t\t\treturn -1;\n+\t\t}\n+\t\tif (!p)\n+\t\t\treturn 0;\n+\t\tif (fputs(newline, fp) < 0)\n+\t\t\treturn -1;\n+\t\tcount -= p + 1 - buf;\n+\t\tbuf = p + 1;\n+\t}\n+\treturn 0;\n+}\n+\n static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n {\n \tstruct diff_words_data *diff_words = priv;\n+\tstruct diff_words_style *style = diff_words->style;\n \tint minus_first, minus_len, plus_first, plus_len;\n \tconst char *minus_begin, *minus_end, *plus_begin, *plus_end;\n \n@@ -593,16 +660,17 @@ static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)\n \t\tplus_begin = plus_end = diff_words->plus.orig[plus_first].end;\n \n \tif (diff_words->current_plus != plus_begin)\n-\t\tfwrite(diff_words->current_plus,\n-\t\t\t\tplus_begin - diff_words->current_plus, 1,\n-\t\t\t\tdiff_words->file);\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\t\tstyle->ctx, style->newline,\n+\t\t\t\tplus_begin - diff_words->current_plus,\n+\t\t\t\tdiff_words->current_plus);\n \tif (minus_begin != minus_end)\n-\t\tcolor_fwrite_lines(diff_words->file,\n-\t\t\t\tdiff_get_color(1, DIFF_FILE_OLD),\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\t\tstyle->old, style->newline,\n \t\t\t\tminus_end - minus_begin, minus_begin);\n \tif (plus_begin != plus_end)\n-\t\tcolor_fwrite_lines(diff_words->file,\n-\t\t\t\tdiff_get_color(1, DIFF_FILE_NEW),\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\t\tstyle->new, style->newline,\n \t\t\t\tplus_end - plus_begin, plus_begin);\n \n \tdiff_words->current_plus = plus_end;\n@@ -685,11 +753,12 @@ static void diff_words_show(struct diff_words_data *diff_words)\n \txdemitconf_t xecfg;\n \txdemitcb_t ecb;\n \tmmfile_t minus, plus;\n+\tstruct diff_words_style *style = diff_words->style;\n \n \t/* special case: only removal */\n \tif (!diff_words->plus.text.size) {\n-\t\tcolor_fwrite_lines(diff_words->file,\n-\t\t\tdiff_get_color(1, DIFF_FILE_OLD),\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\tstyle->old, style->newline,\n \t\t\tdiff_words->minus.text.size, diff_words->minus.text.ptr);\n \t\tdiff_words->minus.text.size = 0;\n \t\treturn;\n@@ -710,10 +779,10 @@ static void diff_words_show(struct diff_words_data *diff_words)\n \tfree(plus.ptr);\n \tif (diff_words->current_plus != diff_words->plus.text.ptr +\n \t\t\tdiff_words->plus.text.size)\n-\t\tfwrite(diff_words->current_plus,\n+\t\tfn_out_diff_words_write_helper(diff_words->file,\n+\t\t\tstyle->ctx, style->newline,\n \t\t\tdiff_words->plus.text.ptr + diff_words->plus.text.size\n-\t\t\t- diff_words->current_plus, 1,\n-\t\t\tdiff_words->file);\n+\t\t\t- diff_words->current_plus, diff_words->current_plus);\n \tdiff_words->minus.text.size = diff_words->plus.text.size = 0;\n }\n \n@@ -825,6 +894,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \n \tif (len < 1) {\n \t\temit_line(ecbdata->file, reset, reset, line, len);\n+\t\tif (ecbdata->diff_words\n+\t\t    && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)\n+\t\t\tfputs(\"~\\n\", ecbdata->file);\n \t\treturn;\n \t}\n \n@@ -839,9 +911,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)\n \t\t\treturn;\n \t\t}\n \t\tdiff_words_flush(ecbdata);\n-\t\tline++;\n-\t\tlen--;\n-\t\temit_line(ecbdata->file, plain, reset, line, len);\n+\t\tif (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {\n+\t\t\temit_line(ecbdata->file, plain, reset, line, len);\n+\t\t\tfputs(\"~\\n\", ecbdata->file);\n+\t\t} else {\n+\t\t\t/* don't print the prefix character */\n+\t\t\temit_line(ecbdata->file, plain, reset, line+1, len-1);\n+\t\t}\n \t\treturn;\n \t}\n \n@@ -1739,10 +1815,13 @@ static void builtin_diff(const char *name_a,\n \t\t\txecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);\n \t\telse if (!prefixcmp(diffopts, \"-u\"))\n \t\t\txecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);\n-\t\tif (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {\n+\t\tif (o->word_diff) {\n+\t\t\tint i;\n+\n \t\t\tecbdata.diff_words =\n \t\t\t\txcalloc(1, sizeof(struct diff_words_data));\n \t\t\tecbdata.diff_words->file = o->file;\n+\t\t\tecbdata.diff_words->type = o->word_diff;\n \t\t\tif (!o->word_regex)\n \t\t\t\to->word_regex = userdiff_word_regex(one);\n \t\t\tif (!o->word_regex)\n@@ -1758,10 +1837,23 @@ static void builtin_diff(const char *name_a,\n \t\t\t\t\tdie (\"Invalid regular expression: %s\",\n \t\t\t\t\t\t\to->word_regex);\n \t\t\t}\n+\t\t\tfor (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {\n+\t\t\t\tif (o->word_diff == diff_words_styles[i].type) {\n+\t\t\t\t\tecbdata.diff_words->style =\n+\t\t\t\t\t\t&diff_words_styles[i];\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (DIFF_OPT_TST(o, COLOR_DIFF)) {\n+\t\t\t\tstruct diff_words_style *st = ecbdata.diff_words->style;\n+\t\t\t\tst->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);\n+\t\t\t\tst->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);\n+\t\t\t\tst->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);\n+\t\t\t}\n \t\t}\n \t\txdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,\n \t\t\t      &xpp, &xecfg, &ecb);\n-\t\tif (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))\n+\t\tif (o->word_diff)\n \t\t\tfree_diff_words_data(&ecbdata);\n \t\tif (textconv_one)\n \t\t\tfree(mf1.ptr);\n@@ -2830,13 +2922,37 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\tDIFF_OPT_CLR(options, COLOR_DIFF);\n \telse if (!strcmp(arg, \"--color-words\")) {\n \t\tDIFF_OPT_SET(options, COLOR_DIFF);\n-\t\tDIFF_OPT_SET(options, COLOR_DIFF_WORDS);\n+\t\toptions->word_diff = DIFF_WORDS_COLOR;\n \t}\n \telse if (!prefixcmp(arg, \"--color-words=\")) {\n \t\tDIFF_OPT_SET(options, COLOR_DIFF);\n-\t\tDIFF_OPT_SET(options, COLOR_DIFF_WORDS);\n+\t\toptions->word_diff = DIFF_WORDS_COLOR;\n \t\toptions->word_regex = arg + 14;\n \t}\n+\telse if (!strcmp(arg, \"--word-diff\")) {\n+\t\tif (options->word_diff == DIFF_WORDS_NONE)\n+\t\t\toptions->word_diff = DIFF_WORDS_PLAIN;\n+\t}\n+\telse if (!prefixcmp(arg, \"--word-diff=\")) {\n+\t\tconst char *type = arg + 12;\n+\t\tif (!strcmp(type, \"plain\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_PLAIN;\n+\t\telse if (!strcmp(type, \"color\")) {\n+\t\t\tDIFF_OPT_SET(options, COLOR_DIFF);\n+\t\t\toptions->word_diff = DIFF_WORDS_COLOR;\n+\t\t}\n+\t\telse if (!strcmp(type, \"porcelain\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_PORCELAIN;\n+\t\telse if (!strcmp(type, \"none\"))\n+\t\t\toptions->word_diff = DIFF_WORDS_NONE;\n+\t\telse\n+\t\t\tdie(\"bad --word-diff argument: %s\", type);\n+\t}\n+\telse if (!prefixcmp(arg, \"--word-diff-regex=\")) {\n+\t\tif (options->word_diff == DIFF_WORDS_NONE)\n+\t\t\toptions->word_diff = DIFF_WORDS_PLAIN;\n+\t\toptions->word_regex = arg + 18;\n+\t}\n \telse if (!strcmp(arg, \"--exit-code\"))\n \t\tDIFF_OPT_SET(options, EXIT_WITH_STATUS);\n \telse if (!strcmp(arg, \"--quiet\"))\ndiff --git a/diff.h b/diff.h\nindex 6a71013..9ace08c 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -54,7 +54,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)\n #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)\n #define DIFF_OPT_COLOR_DIFF          (1 <<  8)\n-#define DIFF_OPT_COLOR_DIFF_WORDS    (1 <<  9)\n+/* (1 <<  9) unused */\n #define DIFF_OPT_HAS_CHANGES         (1 << 10)\n #define DIFF_OPT_QUICK               (1 << 11)\n #define DIFF_OPT_NO_INDEX            (1 << 12)\n@@ -79,6 +79,13 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)\n #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)\n \n+enum diff_words_type {\n+\tDIFF_WORDS_NONE = 0,\n+\tDIFF_WORDS_PORCELAIN,\n+\tDIFF_WORDS_PLAIN,\n+\tDIFF_WORDS_COLOR\n+};\n+\n struct diff_options {\n \tconst char *filter;\n \tconst char *orderfile;\n@@ -108,6 +115,7 @@ struct diff_options {\n \tint stat_width;\n \tint stat_name_width;\n \tconst char *word_regex;\n+\tenum diff_words_type word_diff;\n \n \t/* this is set by diffcore for DIFF_FORMAT_PATCH */\n \tint found_changes;\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex 2e2e103..6f7548c 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -55,6 +55,93 @@ test_expect_success 'word diff with runs of whitespace' '\n \n '\n \n+test_expect_success '--word-diff=color' '\n+\n+\tword_diff --word-diff=color\n+\n+'\n+\n+test_expect_success '--color --word-diff=color' '\n+\n+\tword_diff --color --word-diff=color\n+\n+'\n+\n+sed 's/#.*$//' > expect <<EOF\n+diff --git a/pre b/post\n+index 330b04f..5ed8eff 100644\n+--- a/pre\n++++ b/post\n+@@ -1,3 +1,7 @@\n+-h(4)\n++h(4),hh[44]\n+~\n+ # significant space\n+~\n+ a = b + c\n+~\n+~\n++aa = a\n+~\n+~\n++aeff = aeff * ( aaa )\n+~\n+EOF\n+\n+test_expect_success '--word-diff=porcelain' '\n+\n+\tword_diff --word-diff=porcelain\n+\n+'\n+\n+cat > expect <<EOF\n+diff --git a/pre b/post\n+index 330b04f..5ed8eff 100644\n+--- a/pre\n++++ b/post\n+@@ -1,3 +1,7 @@\n+[-h(4)-]{+h(4),hh[44]+}\n+\n+a = b + c\n+\n+{+aa = a+}\n+\n+{+aeff = aeff * ( aaa )+}\n+EOF\n+\n+test_expect_success '--word-diff=plain' '\n+\n+\tword_diff --word-diff=plain\n+\n+'\n+\n+test_expect_success '--word-diff=plain --no-color' '\n+\n+\tword_diff --word-diff=plain --no-color\n+\n+'\n+\n+cat > expect <<EOF\n+<WHITE>diff --git a/pre b/post<RESET>\n+<WHITE>index 330b04f..5ed8eff 100644<RESET>\n+<WHITE>--- a/pre<RESET>\n+<WHITE>+++ b/post<RESET>\n+<CYAN>@@ -1,3 +1,7 @@<RESET>\n+<RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>\n+\n+a = b + c<RESET>\n+\n+<GREEN>{+aa = a+}<RESET>\n+\n+<GREEN>{+aeff = aeff * ( aaa )+}<RESET>\n+EOF\n+\n+test_expect_success '--word-diff=plain --color' '\n+\n+\tword_diff --word-diff=plain --color\n+\n+'\n+\n cat > expect <<\\EOF\n <WHITE>diff --git a/pre b/post<RESET>\n <WHITE>index 330b04f..5ed8eff 100644<RESET>\n@@ -143,6 +230,25 @@ test_expect_success 'command-line overrides config' '\n \tword_diff --color-words=\"[a-z]+\"\n '\n \n+cat > expect <<\\EOF\n+<WHITE>diff --git a/pre b/post<RESET>\n+<WHITE>index 330b04f..5ed8eff 100644<RESET>\n+<WHITE>--- a/pre<RESET>\n+<WHITE>+++ b/post<RESET>\n+<CYAN>@@ -1,3 +1,7 @@<RESET>\n+h(4),<GREEN>{+hh+}<RESET>[44]\n+\n+a = b + c<RESET>\n+\n+<GREEN>{+aa = a+}<RESET>\n+\n+<GREEN>{+aeff = aeff * ( aaa+}<RESET> )\n+EOF\n+\n+test_expect_success 'command-line overrides config: --word-diff-regex' '\n+\tword_diff --color --word-diff-regex=\"[a-z]+\"\n+'\n+\n cp expect.non-whitespace-is-word expect\n \n test_expect_success '.gitattributes override config' '\n@@ -209,4 +315,20 @@ test_expect_success 'test when words are only removed at the end' '\n \n '\n \n+cat > expect <<\\EOF\n+diff --git a/pre b/post\n+index 289cb9d..2d06f37 100644\n+--- a/pre\n++++ b/post\n+@@ -1 +1 @@\n+-(:\n++(\n+EOF\n+\n+test_expect_success '--word-diff=none' '\n+\n+\tword_diff --word-diff=plain --word-diff=none\n+\n+'\n+\n test_done\n-- \n1.7.1.rc0.262.gff1a3\n"},{"id":"139329","messageId":"ba83d579ded263a44455508ad89ff142272626c6.1271077334.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"cover.1271077334.git.trast@student.ethz.ch","subject":"[PATCH v3 2/3] gitk: do not parse \" >\" context as submodule change","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-12T13:07:45Z","receivedAt":"2010-04-12T13:07:45Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Since 5c838d2 (gitk: Use the --submodule option for displaying diffs\nwhen available, 2009-10-28) gitk erroneously matches \"  >\" and \"  <\"\nat the beginning of a line in the submodule code even if we're in the\ndiff text section and the lines should be treated as context.\n\nFix by (ab)using the $diffinhdr variable also in the 'Submodule...'\ncase, and move the \"  >\"/\"  <\" specific code inside the $diffinhdr\ntest.  The existing code will set $diffinhdr to 0 when it hits a\n\"+++\", so that it is always 0 when we can hit a context line.\n---\n gitk |   14 ++++++++------\n 1 files changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 1f36a3e..e9e284e 100755\n--- a/gitk\n+++ b/gitk\n@@ -7688,12 +7688,8 @@ proc getblobdiffline {bdf ids} {\n \t    lappend ctext_file_lines $fname\n \t    makediffhdr $fname $ids\n \t    $ctext insert end \"\\n$line\\n\" filesep\n-\t} elseif {![string compare -length 3 \"  >\" $line]} {\n-\t    set line [encoding convertfrom $diffencoding $line]\n-\t    $ctext insert end \"$line\\n\" dresult\n-\t} elseif {![string compare -length 3 \"  <\" $line]} {\n-\t    set line [encoding convertfrom $diffencoding $line]\n-\t    $ctext insert end \"$line\\n\" d0\n+\t    # pretend we're in a file header to correctly parse \"  [><]\"\n+\t    set diffinhdr 1\n \t} elseif {$diffinhdr} {\n \t    if {![string compare -length 12 \"rename from \" $line]} {\n \t\tset fname [string range $line [expr 6 + [string first \" from \" $line] ] end]\n@@ -7712,6 +7708,12 @@ proc getblobdiffline {bdf ids} {\n \t\t    set fname [lindex $fname 0]\n \t\t}\n \t\tmakediffhdr $fname $ids\n+\t    } elseif {![string compare -length 3 \"  >\" $line]} {\n+\t\tset line [encoding convertfrom $diffencoding $line]\n+\t\t$ctext insert end \"$line\\n\" dresult\n+\t    } elseif {![string compare -length 3 \"  <\" $line]} {\n+\t\tset line [encoding convertfrom $diffencoding $line]\n+\t\t$ctext insert end \"$line\\n\" d0\n \t    } elseif {[string compare -length 3 $line \"---\"] == 0} {\n \t\t# do nothing\n \t\tcontinue\n-- \n1.7.1.rc0.262.gff1a3\n"},{"id":"139328","messageId":"c6a5b441d481dee8861c8b9b2898cce10b7a1f75.1271077334.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"cover.1271077334.git.trast@student.ethz.ch","subject":"[PATCH v3 3/3] gitk: add the equivalent of diff --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-12T13:07:46Z","receivedAt":"2010-04-12T13:07:46Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Use the newly added 'diff --word-diff=porcelain' to teach gitk a\ncolor-words mode, with two different modes analogous to the\n--word-diff=plain and --word-diff=color settings.  These are selected\nby a dropdown box.\n\nAs an extra twist, automatically enable this word-diff support when\nthe user mentions a word-diff related option on the command line.\nThese options were previously ignored because they would break diff\nparsing.\n\nBoth of these features are only enabled if we have a version of git\nthat supports --word-diff=porcelain, tentatively set to 1.7.2.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n gitk |   64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 files changed, 61 insertions(+), 3 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex e9e284e..466c328 100755\n--- a/gitk\n+++ b/gitk\n@@ -131,6 +131,7 @@ proc unmerged_files {files} {\n \n proc parseviewargs {n arglist} {\n     global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env\n+    global worddiff git_version\n \n     set vdatemode($n) 0\n     set vmergeonly($n) 0\n@@ -168,7 +169,7 @@ proc parseviewargs {n arglist} {\n \t\tlappend diffargs $arg\n \t    }\n \t    \"--raw\" - \"--patch-with-raw\" - \"--patch-with-stat\" -\n-\t    \"--name-only\" - \"--name-status\" - \"--color\" - \"--color-words\" -\n+\t    \"--name-only\" - \"--name-status\" - \"--color\" -\n \t    \"--log-size\" - \"--pretty=*\" - \"--decorate\" - \"--abbrev-commit\" -\n \t    \"--cc\" - \"-z\" - \"--header\" - \"--parents\" - \"--boundary\" -\n \t    \"--no-color\" - \"-g\" - \"--walk-reflogs\" - \"--no-walk\" -\n@@ -177,6 +178,18 @@ proc parseviewargs {n arglist} {\n \t\t# These cause our parsing of git log's output to fail, or else\n \t\t# they're options we want to set ourselves, so ignore them.\n \t    }\n+\t    \"--color-words*\" - \"--word-diff=color\" {\n+\t\t# These trigger a word diff in the console interface,\n+\t\t# so help the user by enabling our own support\n+\t\tif {[package vcompare $git_version \"1.7.2\"] >= 0} {\n+\t\t    set worddiff [mc \"Color words\"]\n+\t\t}\n+\t    }\n+\t    \"--word-diff*\" {\n+\t\tif {[package vcompare $git_version \"1.7.2\"] >= 0} {\n+\t\t    set worddiff [mc \"Markup words\"]\n+\t\t}\n+\t    }\n \t    \"--stat=*\" - \"--numstat\" - \"--shortstat\" - \"--summary\" -\n \t    \"--check\" - \"--exit-code\" - \"--quiet\" - \"--topo-order\" -\n \t    \"--full-history\" - \"--dense\" - \"--sparse\" -\n@@ -1967,6 +1980,8 @@ proc makewindow {} {\n     global fprogitem fprogcoord lastprogupdate progupdatepending\n     global rprogitem rprogcoord rownumsel numcommits\n     global have_tk85 use_ttk NS\n+    global git_version\n+    global worddiff\n \n     # The \"mc\" arguments here are purely so that xgettext\n     # sees the following string as needing to be translated\n@@ -2240,6 +2255,15 @@ proc makewindow {} {\n     ${NS}::checkbutton .bleft.mid.ignspace -text [mc \"Ignore space change\"] \\\n \t-command changeignorespace -variable ignorespace\n     pack .bleft.mid.ignspace -side left -padx 5\n+\n+    set worddiff [mc \"Line diff\"]\n+    if {[package vcompare $git_version \"1.7.2\"] >= 0} {\n+\tmakedroplist .bleft.mid.worddiff worddiff [mc \"Line diff\"] \\\n+\t    [mc \"Markup words\"] [mc \"Color words\"]\n+\ttrace add variable worddiff write changeworddiff\n+\tpack .bleft.mid.worddiff -side left -padx 5\n+    }\n+\n     set ctext .bleft.bottom.ctext\n     text $ctext -background $bgcolor -foreground $fgcolor \\\n \t-state disabled -font textfont \\\n@@ -7494,11 +7518,16 @@ proc changeignorespace {} {\n     reselectline\n }\n \n+proc changeworddiff {name ix op} {\n+    reselectline\n+}\n+\n proc getblobdiffs {ids} {\n     global blobdifffd diffids env\n     global diffinhdr treediffs\n     global diffcontext\n     global ignorespace\n+    global worddiff\n     global limitdiffs vfilelimit curview\n     global diffencoding targetline diffnparents\n     global git_version\n@@ -7515,6 +7544,9 @@ proc getblobdiffs {ids} {\n     if {$ignorespace} {\n \tappend cmd \" -w\"\n     }\n+    if {$worddiff ne [mc \"Line diff\"]} {\n+\tappend cmd \" --word-diff=porcelain\"\n+    }\n     if {$limitdiffs && $vfilelimit($curview) ne {}} {\n \tset cmd [concat $cmd -- $vfilelimit($curview)]\n     }\n@@ -7599,6 +7631,7 @@ proc getblobdiffline {bdf ids} {\n     global ctext_file_names ctext_file_lines\n     global diffinhdr treediffs mergemax diffnparents\n     global diffencoding jump_to_here targetline diffline\n+    global worddiff\n \n     set nr 0\n     $ctext conf -state normal\n@@ -7729,15 +7762,28 @@ proc getblobdiffline {bdf ids} {\n \t    # parse the prefix - one ' ', '-' or '+' for each parent\n \t    set prefix [string range $line 0 [expr {$diffnparents - 1}]]\n \t    set tag [expr {$diffnparents > 1? \"m\": \"d\"}]\n+\t    set dowords [expr {$worddiff ne [mc \"Line diff\"] && $diffnparents == 1}]\n+\t    set words_pre_markup \"\"\n+\t    set words_post_markup \"\"\n \t    if {[string trim $prefix \" -+\"] eq {}} {\n \t\t# prefix only has \" \", \"-\" and \"+\" in it: normal diff line\n \t\tset num [string first \"-\" $prefix]\n+\t\tif {$dowords} {\n+\t\t    set line [string range $line 1 end]\n+\t\t}\n \t\tif {$num >= 0} {\n \t\t    # removed line, first parent with line is $num\n \t\t    if {$num >= $mergemax} {\n \t\t\tset num \"max\"\n \t\t    }\n-\t\t    $ctext insert end \"$line\\n\" $tag$num\n+\t\t    if {$dowords && $worddiff eq [mc \"Markup words\"]} {\n+\t\t\t$ctext insert end \"\\[-$line-\\]\" $tag$num\n+\t\t    } else {\n+\t\t\t$ctext insert end \"$line\" $tag$num\n+\t\t    }\n+\t\t    if {!$dowords} {\n+\t\t\t$ctext insert end \"\\n\" $tag$num\n+\t\t    }\n \t\t} else {\n \t\t    set tags {}\n \t\t    if {[string first \"+\" $prefix] >= 0} {\n@@ -7752,6 +7798,8 @@ proc getblobdiffline {bdf ids} {\n \t\t\t\tlappend tags m$num\n \t\t\t    }\n \t\t\t}\n+\t\t\tset words_pre_markup \"{+\"\n+\t\t\tset words_post_markup \"+}\"\n \t\t    }\n \t\t    if {$targetline ne {}} {\n \t\t\tif {$diffline == $targetline} {\n@@ -7761,8 +7809,17 @@ proc getblobdiffline {bdf ids} {\n \t\t\t    incr diffline\n \t\t\t}\n \t\t    }\n-\t\t    $ctext insert end \"$line\\n\" $tags\n+\t\t    if {$dowords && $worddiff eq [mc \"Markup words\"]} {\n+\t\t\t$ctext insert end \"$words_pre_markup$line$words_post_markup\" $tags\n+\t\t    } else {\n+\t\t\t$ctext insert end \"$line\" $tags\n+\t\t    }\n+\t\t    if {!$dowords} {\n+\t\t\t$ctext insert end \"\\n\" $tags\n+\t\t    }\n \t\t}\n+\t    } elseif {$dowords && $prefix eq \"~\"} {\n+\t\t$ctext insert end \"\\n\" {}\n \t    } else {\n \t\t# \"\\ No newline at end of file\",\n \t\t# or something else we don't recognize\n@@ -11381,6 +11438,7 @@ if {[tk windowingsystem] eq \"win32\"} {\n set diffcolors {red \"#00a000\" blue}\n set diffcontext 3\n set ignorespace 0\n+set worddiff \"\"\n set markbgcolor \"#e0e0ff\"\n \n set circlecolors {white blue gray blue blue}\n-- \n1.7.1.rc0.262.gff1a3\n"},{"id":"139331","messageId":"5531510bfb94997f729a894a0b5a3158177a9add.1271078624.git.trast@student.ethz.ch","threadId":"23326","inReplyTo":"ba83d579ded263a44455508ad89ff142272626c6.1271077334.git.trast@student.ethz.ch","subject":"[PATCH v3.1] gitk: do not parse \" >\" context as submodule change","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-12T13:25:53Z","receivedAt":"2010-04-12T13:25:53Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Since 5c838d2 (gitk: Use the --submodule option for displaying diffs\nwhen available, 2009-10-28) gitk erroneously matches \"  >\" and \"  <\"\nat the beginning of a line in the submodule code even if we're in the\ndiff text section and the lines should be treated as context.\n\nFix by (ab)using the $diffinhdr variable also in the 'Submodule...'\ncase, and move the \"  >\"/\"  <\" specific code inside the $diffinhdr\ntest.  The existing code will set $diffinhdr to 0 when it hits a\n\"+++\", so that it is always 0 when we can hit a context line.\n---\n\nI just noticed that I forgot to add the 'continue' that prevents the\nfinal 'insert' in the $diffinhdr block from kicking in, which resulted\nin doubling of the lines.  This one has them.  Sorry for the noise.\n\n\n gitk |   16 ++++++++++------\n 1 files changed, 10 insertions(+), 6 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 1f36a3e..7b8799e 100755\n--- a/gitk\n+++ b/gitk\n@@ -7688,12 +7688,8 @@ proc getblobdiffline {bdf ids} {\n \t    lappend ctext_file_lines $fname\n \t    makediffhdr $fname $ids\n \t    $ctext insert end \"\\n$line\\n\" filesep\n-\t} elseif {![string compare -length 3 \"  >\" $line]} {\n-\t    set line [encoding convertfrom $diffencoding $line]\n-\t    $ctext insert end \"$line\\n\" dresult\n-\t} elseif {![string compare -length 3 \"  <\" $line]} {\n-\t    set line [encoding convertfrom $diffencoding $line]\n-\t    $ctext insert end \"$line\\n\" d0\n+\t    # pretend we're in a file header to correctly parse \"  [><]\"\n+\t    set diffinhdr 1\n \t} elseif {$diffinhdr} {\n \t    if {![string compare -length 12 \"rename from \" $line]} {\n \t\tset fname [string range $line [expr 6 + [string first \" from \" $line] ] end]\n@@ -7712,6 +7708,14 @@ proc getblobdiffline {bdf ids} {\n \t\t    set fname [lindex $fname 0]\n \t\t}\n \t\tmakediffhdr $fname $ids\n+\t    } elseif {![string compare -length 3 \"  >\" $line]} {\n+\t\tset line [encoding convertfrom $diffencoding $line]\n+\t\t$ctext insert end \"$line\\n\" dresult\n+\t\tcontinue\n+\t    } elseif {![string compare -length 3 \"  <\" $line]} {\n+\t\tset line [encoding convertfrom $diffencoding $line]\n+\t\t$ctext insert end \"$line\\n\" d0\n+\t\tcontinue\n \t    } elseif {[string compare -length 3 $line \"---\"] == 0} {\n \t\t# do nothing\n \t\tcontinue\n-- \n1.7.1.rc0.260.g2b919\n"},{"id":"139357","messageId":"7v8w8sljjr.fsf@alter.siamese.dyndns.org","threadId":"23326","inReplyTo":"09bfc1bfe19d47267167b264ed03a677ce20013f.1271077334.git.trast@student.ethz.ch","subject":"Re: [PATCH v3 1/3] diff: add --word-diff option that generalizes --color-words","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-12T16:31:04Z","receivedAt":"2010-04-12T16:31:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> diff --git a/diff.c b/diff.c\n> index 7effdac..ca054d4 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -560,16 +560,83 @@ static void diff_words_append(char *line, unsigned long len,\n>  \tbuffer->text.ptr[buffer->text.size] = '\\0';\n>  }\n>  \n> +struct diff_words_style_elem\n> +{\n> +\tconst char *prefix;\n> +\tconst char *suffix;\n> +\tconst char *color; /* NULL; filled in by the setup code if\n> +\t\t\t    * color is enabled */\n> +};\n\nThe reader makes a mental note that this is a 12- to 24-byte structure...\n\n> +struct diff_words_style\n> +{\n> +\tenum diff_words_type type;\n> +\tstruct diff_words_style_elem new, old, ctx;\n> +\tconst char *newline;\n> +};\n> +\n> +struct diff_words_style diff_words_styles[] = {\n> +\t{ DIFF_WORDS_PORCELAIN,\n> +\t  {\"+\", \"\\n\", NULL},\n> +\t  {\"-\", \"\\n\", NULL},\n> +\t  {\" \", \"\\n\", NULL},\n> +\t  \"~\\n\"\n> +\t},\n> +\t{ DIFF_WORDS_PLAIN,\n> +\t  {\"{+\", \"+}\", NULL},\n> +\t  {\"[-\", \"-]\", NULL},\n> +\t  {\"\", \"\", NULL},\n> +\t  \"\\n\"\n> +\t},\n> +\t{ DIFF_WORDS_COLOR,\n> +\t  {\"\", \"\", NULL},\n> +\t  {\"\", \"\", NULL},\n> +\t  {\"\", \"\", NULL},\n> +\t  \"\\n\"\n> +\t}\n> +};\n\nBeautiful.\n\nThe style might be a bit iffy.  Shouldn't an opening \"{\", unless closed on\nthe same line with a matching \"}\", stand on its own line?\n\n> +static int fn_out_diff_words_write_helper(FILE *fp,\n> +\t\t\t\t\t  struct diff_words_style_elem st_el,\n\nDo you need to pass this by value?\n\n> +\t\t\t\t\t  const char *newline,\n> +\t\t\t\t\t  size_t count, const char *buf)\n> +{\n> +\twhile (count) {\n> +\t\tchar *p = memchr(buf, '\\n', count);\n> +\t\tif (p != buf) {\n> +\t\t\tif (st_el.color && fputs(st_el.color, fp) < 0)\n> +\t\t\t\treturn -1;\n> +\t\t\tif (fputs(st_el.prefix, fp) < 0 ||\n> +\t\t\t    fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||\n> +\t\t\t    fputs(st_el.suffix, fp) < 0)\n> +\t\t\t\treturn -1;\n> +\t\t\tif (st_el.color && strlen(st_el.color)\n\nI would avoid strlen() only for boolean result here---the compilers may\nnot be as clever as you are.\n\n\tif (st_elp->color && *st_elp->color\n"},{"id":"139404","messageId":"201004131127.52669.trast@student.ethz.ch","threadId":"23326","inReplyTo":"7v8w8sljjr.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v3 1/3] diff: add --word-diff option that generalizes --color-words","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2010-04-13T09:27:52Z","receivedAt":"2010-04-13T09:27:52Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Junio C Hamano wrote:\n> Thomas Rast <trast@student.ethz.ch> writes:\n[...]\n> > +\t{ DIFF_WORDS_COLOR,\n> > +\t  {\"\", \"\", NULL},\n> > +\t  {\"\", \"\", NULL},\n> > +\t  {\"\", \"\", NULL},\n> > +\t  \"\\n\"\n> > +\t}\n> > +};\n> \n> Beautiful.\n> \n> The style might be a bit iffy.  Shouldn't an opening \"{\", unless closed on\n> the same line with a matching \"}\", stand on its own line?\n\nPerhaps.  OTOH I just noticed I could also drop the NULL (and let the\nimplicit 0-padding take over) which makes every style fit on a single\nline.  That should make it a bit easier to read.\n\n> > +static int fn_out_diff_words_write_helper(FILE *fp,\n> > +\t\t\t\t\t  struct diff_words_style_elem st_el,\n> \n> Do you need to pass this by value?\n\nNo, thanks for catching this.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"}]}