Skip to content

Commit ac83261

Browse files
committed
diff: add an internal option to dual-color diffs of diffs
When diffing diffs, it can be quite daunting to figure out what the heck is going on, as there are nested +/- signs. Let's make this easier by adding a flag in diff_options that allows color-coding the outer diff sign with inverted colors, so that the preimage and postimage is colored like the diff it is. Of course, this really only makes sense when the preimage and postimage *are* diffs. So let's not expose this flag via a command-line option for now. This is a feature that was invented by git-tbdiff, and it will be used by `git range-diff` in the next commit, by offering it via a new option: `--dual-color`. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 4844e5e commit ac83261

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

diff.c

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
570570
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
571571
}
572572

573-
static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
573+
static void emit_line_0(struct diff_options *o,
574+
const char *set, unsigned reverse, const char *reset,
574575
int first, const char *line, int len)
575576
{
576577
int has_trailing_newline, has_trailing_carriage_return;
577578
int nofirst;
578579
FILE *file = o->file;
579580

580-
fputs(diff_line_prefix(o), file);
581+
if (first)
582+
fputs(diff_line_prefix(o), file);
583+
else if (!len)
584+
return;
581585

582586
if (len == 0) {
583587
has_trailing_newline = (first == '\n');
@@ -595,8 +599,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
595599
}
596600

597601
if (len || !nofirst) {
602+
if (reverse && want_color(o->use_color))
603+
fputs(GIT_COLOR_REVERSE, file);
598604
fputs(set, file);
599-
if (!nofirst)
605+
if (first && !nofirst)
600606
fputc(first, file);
601607
fwrite(line, len, 1, file);
602608
fputs(reset, file);
@@ -610,7 +616,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
610616
static void emit_line(struct diff_options *o, const char *set, const char *reset,
611617
const char *line, int len)
612618
{
613-
emit_line_0(o, set, reset, line[0], line+1, len-1);
619+
emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
614620
}
615621

616622
enum diff_symbol {
@@ -970,7 +976,8 @@ static void dim_moved_lines(struct diff_options *o)
970976

971977
static void emit_line_ws_markup(struct diff_options *o,
972978
const char *set, const char *reset,
973-
const char *line, int len, char sign,
979+
const char *line, int len,
980+
const char *set_sign, char sign,
974981
unsigned ws_rule, int blank_at_eof)
975982
{
976983
const char *ws = NULL;
@@ -981,14 +988,20 @@ static void emit_line_ws_markup(struct diff_options *o,
981988
ws = NULL;
982989
}
983990

984-
if (!ws)
985-
emit_line_0(o, set, reset, sign, line, len);
986-
else if (blank_at_eof)
991+
if (!ws && !set_sign)
992+
emit_line_0(o, set, 0, reset, sign, line, len);
993+
else if (!ws) {
994+
/* Emit just the prefix, then the rest. */
995+
emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
996+
sign, "", 0);
997+
emit_line_0(o, set, 0, reset, 0, line, len);
998+
} else if (blank_at_eof)
987999
/* Blank line at EOF - paint '+' as well */
988-
emit_line_0(o, ws, reset, sign, line, len);
1000+
emit_line_0(o, ws, 0, reset, sign, line, len);
9891001
else {
9901002
/* Emit just the prefix, then the rest. */
991-
emit_line_0(o, set, reset, sign, "", 0);
1003+
emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
1004+
sign, "", 0);
9921005
ws_check_emit(line, len, ws_rule,
9931006
o->file, set, reset, ws);
9941007
}
@@ -998,7 +1011,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
9981011
struct emitted_diff_symbol *eds)
9991012
{
10001013
static const char *nneof = " No newline at end of file\n";
1001-
const char *context, *reset, *set, *meta, *fraginfo;
1014+
const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
10021015
struct strbuf sb = STRBUF_INIT;
10031016

10041017
enum diff_symbol s = eds->s;
@@ -1011,7 +1024,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10111024
context = diff_get_color_opt(o, DIFF_CONTEXT);
10121025
reset = diff_get_color_opt(o, DIFF_RESET);
10131026
putc('\n', o->file);
1014-
emit_line_0(o, context, reset, '\\',
1027+
emit_line_0(o, context, 0, reset, '\\',
10151028
nneof, strlen(nneof));
10161029
break;
10171030
case DIFF_SYMBOL_SUBMODULE_HEADER:
@@ -1038,7 +1051,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10381051
case DIFF_SYMBOL_CONTEXT:
10391052
set = diff_get_color_opt(o, DIFF_CONTEXT);
10401053
reset = diff_get_color_opt(o, DIFF_RESET);
1041-
emit_line_ws_markup(o, set, reset, line, len, ' ',
1054+
set_sign = NULL;
1055+
if (o->flags.dual_color_diffed_diffs) {
1056+
char c = !len ? 0 : line[0];
1057+
1058+
if (c == '+')
1059+
set = diff_get_color_opt(o, DIFF_FILE_NEW);
1060+
else if (c == '@')
1061+
set = diff_get_color_opt(o, DIFF_FRAGINFO);
1062+
else if (c == '-')
1063+
set = diff_get_color_opt(o, DIFF_FILE_OLD);
1064+
}
1065+
emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
10421066
flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
10431067
break;
10441068
case DIFF_SYMBOL_PLUS:
@@ -1065,7 +1089,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10651089
set = diff_get_color_opt(o, DIFF_FILE_NEW);
10661090
}
10671091
reset = diff_get_color_opt(o, DIFF_RESET);
1068-
emit_line_ws_markup(o, set, reset, line, len, '+',
1092+
if (!o->flags.dual_color_diffed_diffs)
1093+
set_sign = NULL;
1094+
else {
1095+
char c = !len ? 0 : line[0];
1096+
1097+
set_sign = set;
1098+
if (c == '-')
1099+
set = diff_get_color_opt(o, DIFF_FILE_OLD);
1100+
else if (c == '@')
1101+
set = diff_get_color_opt(o, DIFF_FRAGINFO);
1102+
else if (c != '+')
1103+
set = diff_get_color_opt(o, DIFF_CONTEXT);
1104+
}
1105+
emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
10691106
flags & DIFF_SYMBOL_CONTENT_WS_MASK,
10701107
flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
10711108
break;
@@ -1093,7 +1130,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10931130
set = diff_get_color_opt(o, DIFF_FILE_OLD);
10941131
}
10951132
reset = diff_get_color_opt(o, DIFF_RESET);
1096-
emit_line_ws_markup(o, set, reset, line, len, '-',
1133+
if (!o->flags.dual_color_diffed_diffs)
1134+
set_sign = NULL;
1135+
else {
1136+
char c = !len ? 0 : line[0];
1137+
1138+
set_sign = set;
1139+
if (c == '+')
1140+
set = diff_get_color_opt(o, DIFF_FILE_NEW);
1141+
else if (c == '@')
1142+
set = diff_get_color_opt(o, DIFF_FRAGINFO);
1143+
else if (c != '-')
1144+
set = diff_get_color_opt(o, DIFF_CONTEXT);
1145+
}
1146+
emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
10971147
flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
10981148
break;
10991149
case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -1284,6 +1334,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
12841334
const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
12851335
const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
12861336
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
1337+
const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
12871338
static const char atat[2] = { '@', '@' };
12881339
const char *cp, *ep;
12891340
struct strbuf msgbuf = STRBUF_INIT;
@@ -1304,6 +1355,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
13041355
ep += 2; /* skip over @@ */
13051356

13061357
/* The hunk header in fraginfo color */
1358+
if (ecbdata->opt->flags.dual_color_diffed_diffs)
1359+
strbuf_addstr(&msgbuf, reverse);
13071360
strbuf_addstr(&msgbuf, frag);
13081361
strbuf_add(&msgbuf, line, ep - line);
13091362
strbuf_addstr(&msgbuf, reset);

diff.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ struct diff_flags {
9595
unsigned default_follow_renames:1;
9696
unsigned stat_with_summary:1;
9797
unsigned suppress_diff_headers:1;
98+
unsigned dual_color_diffed_diffs:1;
9899
};
99100

100101
static inline void diff_flags_or(struct diff_flags *a,

0 commit comments

Comments
 (0)