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: Johannes Schindelin <Johannes.Schindelin@gmx.de> Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
65cdb5f165
commit
f59a59e22f
@ -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.
|
||||||
|
178
diff.c
178
diff.c
@ -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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
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
|
||||||
|
3
diff.h
3
diff.h
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user