提交 f59a59e2 编写于 作者: J Johannes Schindelin 提交者: Junio C Hamano

Add the --color-words option to the diff options family

With this option, the changed words are shown inline. For example,
if a file containing "This is foo" is changed to "This is bar", the diff
will now show "This is " in plain text, "foo" in red, and "bar" in green.
Signed-off-by: NJohannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: NJunio C Hamano <junkio@cox.net>
上级 65cdb5f1
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
Turn off colored diff, even when the configuration file Turn off colored diff, even when the configuration file
gives the default to color output. gives the default to color output.
--color-words::
Show colored word diff, i.e. color words which have changed.
--no-renames:: --no-renames::
Turn off rename detection, even when the configuration Turn off rename detection, even when the configuration
file gives the default to do so. file gives the default to do so.
......
...@@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) ...@@ -358,12 +358,152 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
return 0; return 0;
} }
struct diff_words_buffer {
mmfile_t text;
long alloc;
long current; /* output pointer */
int suppressed_newline;
};
static void diff_words_append(char *line, unsigned long len,
struct diff_words_buffer *buffer)
{
if (buffer->text.size + len > buffer->alloc) {
buffer->alloc = (buffer->text.size + len) * 3 / 2;
buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
}
line++;
len--;
memcpy(buffer->text.ptr + buffer->text.size, line, len);
buffer->text.size += len;
}
struct diff_words_data {
struct xdiff_emit_state xm;
struct diff_words_buffer minus, plus;
};
static void print_word(struct diff_words_buffer *buffer, int len, int color,
int suppress_newline)
{
const char *ptr;
int eol = 0;
if (len == 0)
return;
ptr = buffer->text.ptr + buffer->current;
buffer->current += len;
if (ptr[len - 1] == '\n') {
eol = 1;
len--;
}
fputs(diff_get_color(1, color), stdout);
fwrite(ptr, len, 1, stdout);
fputs(diff_get_color(1, DIFF_RESET), stdout);
if (eol) {
if (suppress_newline)
buffer->suppressed_newline = 1;
else
putchar('\n');
}
}
static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
struct diff_words_data *diff_words = priv;
if (diff_words->minus.suppressed_newline) {
if (line[0] != '+')
putchar('\n');
diff_words->minus.suppressed_newline = 0;
}
len--;
switch (line[0]) {
case '-':
print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
break;
case '+':
print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
break;
case ' ':
print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
diff_words->minus.current += len;
break;
}
}
/* this executes the word diff on the accumulated buffers */
static void diff_words_show(struct diff_words_data *diff_words)
{
xpparam_t xpp;
xdemitconf_t xecfg;
xdemitcb_t ecb;
mmfile_t minus, plus;
int i;
minus.size = diff_words->minus.text.size;
minus.ptr = xmalloc(minus.size);
memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
for (i = 0; i < minus.size; i++)
if (isspace(minus.ptr[i]))
minus.ptr[i] = '\n';
diff_words->minus.current = 0;
plus.size = diff_words->plus.text.size;
plus.ptr = xmalloc(plus.size);
memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
for (i = 0; i < plus.size; i++)
if (isspace(plus.ptr[i]))
plus.ptr[i] = '\n';
diff_words->plus.current = 0;
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
xecfg.flags = 0;
ecb.outf = xdiff_outf;
ecb.priv = diff_words;
diff_words->xm.consume = fn_out_diff_words_aux;
xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
free(minus.ptr);
free(plus.ptr);
diff_words->minus.text.size = diff_words->plus.text.size = 0;
if (diff_words->minus.suppressed_newline) {
putchar('\n');
diff_words->minus.suppressed_newline = 0;
}
}
struct emit_callback { struct emit_callback {
struct xdiff_emit_state xm; struct xdiff_emit_state xm;
int nparents, color_diff; int nparents, color_diff;
const char **label_path; const char **label_path;
struct diff_words_data *diff_words;
}; };
static void free_diff_words_data(struct emit_callback *ecbdata)
{
if (ecbdata->diff_words) {
/* flush buffers */
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
if (ecbdata->diff_words->minus.text.ptr)
free (ecbdata->diff_words->minus.text.ptr);
if (ecbdata->diff_words->plus.text.ptr)
free (ecbdata->diff_words->plus.text.ptr);
free(ecbdata->diff_words);
ecbdata->diff_words = NULL;
}
}
const char *diff_get_color(int diff_use_color, enum color_diff ix) const char *diff_get_color(int diff_use_color, enum color_diff ix)
{ {
if (diff_use_color) if (diff_use_color)
...@@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) ...@@ -398,12 +538,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
else { else {
int nparents = ecbdata->nparents; int nparents = ecbdata->nparents;
int color = DIFF_PLAIN; int color = DIFF_PLAIN;
for (i = 0; i < nparents && len; i++) { if (ecbdata->diff_words && nparents != 1)
if (line[i] == '-') /* fall back to normal diff */
color = DIFF_FILE_OLD; free_diff_words_data(ecbdata);
else if (line[i] == '+') if (ecbdata->diff_words) {
color = DIFF_FILE_NEW; if (line[0] == '-') {
} diff_words_append(line, len,
&ecbdata->diff_words->minus);
return;
} else if (line[0] == '+') {
diff_words_append(line, len,
&ecbdata->diff_words->plus);
return;
}
if (ecbdata->diff_words->minus.text.size ||
ecbdata->diff_words->plus.text.size)
diff_words_show(ecbdata->diff_words);
line++;
len--;
} else
for (i = 0; i < nparents && len; i++) {
if (line[i] == '-')
color = DIFF_FILE_OLD;
else if (line[i] == '+')
color = DIFF_FILE_NEW;
}
set = diff_get_color(ecbdata->color_diff, color); set = diff_get_color(ecbdata->color_diff, color);
} }
if (len > 0 && line[len-1] == '\n') if (len > 0 && line[len-1] == '\n')
...@@ -836,7 +995,12 @@ static void builtin_diff(const char *name_a, ...@@ -836,7 +995,12 @@ static void builtin_diff(const char *name_a,
ecb.outf = xdiff_outf; ecb.outf = xdiff_outf;
ecb.priv = &ecbdata; ecb.priv = &ecbdata;
ecbdata.xm.consume = fn_out_consume; ecbdata.xm.consume = fn_out_consume;
if (o->color_diff_words)
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
if (o->color_diff_words)
free_diff_words_data(&ecbdata);
} }
free_ab_and_return: free_ab_and_return:
...@@ -1697,6 +1861,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) ...@@ -1697,6 +1861,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->xdl_opts |= XDF_IGNORE_WHITESPACE; options->xdl_opts |= XDF_IGNORE_WHITESPACE;
else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(arg, "--color-words"))
options->color_diff = options->color_diff_words = 1;
else if (!strcmp(arg, "--no-renames")) else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0; options->detect_rename = 0;
else else
......
...@@ -46,7 +46,8 @@ struct diff_options { ...@@ -46,7 +46,8 @@ struct diff_options {
full_index:1, full_index:1,
silent_on_remove:1, silent_on_remove:1,
find_copies_harder:1, find_copies_harder:1,
color_diff:1; color_diff:1,
color_diff_words:1;
int context; int context;
int break_opt; int break_opt;
int detect_rename; int detect_rename;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册