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 <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Johannes Schindelin committed
Aug 13, 2018 at 04:33 UTC
f7c3b4e2d8c16890c40c7e10e16cabb910f6b3ae
2 files changed
+69
-15
diff.c
+68
-15
@@ -609,14 +609,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
609
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
610
}
611
612
-static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
612
+static void emit_line_0(struct diff_options *o,
613
+ const char *set, unsigned reverse, const char *reset,
614
int first, const char *line, int len)
615
{
616
int has_trailing_newline, has_trailing_carriage_return;
617
int nofirst;
618
FILE *file = o->file;
619
619
- fputs(diff_line_prefix(o), file);
620
+ if (first)
621
+ fputs(diff_line_prefix(o), file);
622
+ else if (!len)
623
+ return;
624
625
if (len == 0) {
626
has_trailing_newline = (first == '\n');
@@ -634,8 +638,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
638
}
639
640
if (len || !nofirst) {
641
+ if (reverse && want_color(o->use_color))
642
+ fputs(GIT_COLOR_REVERSE, file);
643
fputs(set, file);
638
- if (!nofirst)
644
+ if (first && !nofirst)
645
fputc(first, file);
646
fwrite(line, len, 1, file);
647
fputs(reset, file);
@@ -649,7 +655,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
655
static void emit_line(struct diff_options *o, const char *set, const char *reset,
656
const char *line, int len)
657
{
652
- emit_line_0(o, set, reset, line[0], line+1, len-1);
658
+ emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
659
}
660
661
enum diff_symbol {
@@ -1168,7 +1174,8 @@ static void dim_moved_lines(struct diff_options *o)
1174
1175
static void emit_line_ws_markup(struct diff_options *o,
1176
const char *set, const char *reset,
1171
- const char *line, int len, char sign,
1177
+ const char *line, int len,
1178
+ const char *set_sign, char sign,
1179
unsigned ws_rule, int blank_at_eof)
1180
{
1181
const char *ws = NULL;
@@ -1179,14 +1186,20 @@ static void emit_line_ws_markup(struct diff_options *o,
1186
ws = NULL;
1187
}
1188
1182
- if (!ws)
1183
- emit_line_0(o, set, reset, sign, line, len);
1184
- else if (blank_at_eof)
1189
+ if (!ws && !set_sign)
1190
+ emit_line_0(o, set, 0, reset, sign, line, len);
1191
+ else if (!ws) {
1192
+ /* Emit just the prefix, then the rest. */
1193
+ emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
1194
+ sign, "", 0);
1195
+ emit_line_0(o, set, 0, reset, 0, line, len);
1196
+ } else if (blank_at_eof)
1197
/* Blank line at EOF - paint '+' as well */
1186
- emit_line_0(o, ws, reset, sign, line, len);
1198
+ emit_line_0(o, ws, 0, reset, sign, line, len);
1199
else {
1200
/* Emit just the prefix, then the rest. */
1189
- emit_line_0(o, set, reset, sign, "", 0);
1201
+ emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
1202
+ sign, "", 0);
1203
ws_check_emit(line, len, ws_rule,
1204
o->file, set, reset, ws);
1205
}
@@ -1196,7 +1209,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1209
struct emitted_diff_symbol *eds)
1210
{
1211
static const char *nneof = " No newline at end of file\n";
1199
- const char *context, *reset, *set, *meta, *fraginfo;
1212
+ const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
1213
struct strbuf sb = STRBUF_INIT;
1214
1215
enum diff_symbol s = eds->s;
@@ -1209,7 +1222,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1222
context = diff_get_color_opt(o, DIFF_CONTEXT);
1223
reset = diff_get_color_opt(o, DIFF_RESET);
1224
putc('\n', o->file);
1212
- emit_line_0(o, context, reset, '\\',
1225
+ emit_line_0(o, context, 0, reset, '\\',
1226
nneof, strlen(nneof));
1227
break;
1228
case DIFF_SYMBOL_SUBMODULE_HEADER:
@@ -1236,7 +1249,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1249
case DIFF_SYMBOL_CONTEXT:
1250
set = diff_get_color_opt(o, DIFF_CONTEXT);
1251
reset = diff_get_color_opt(o, DIFF_RESET);
1239
- emit_line_ws_markup(o, set, reset, line, len, ' ',
1252
+ set_sign = NULL;
1253
+ if (o->flags.dual_color_diffed_diffs) {
1254
+ char c = !len ? 0 : line[0];
1255
+
1256
+ if (c == '+')
1257
+ set = diff_get_color_opt(o, DIFF_FILE_NEW);
1258
+ else if (c == '@')
1259
+ set = diff_get_color_opt(o, DIFF_FRAGINFO);
1260
+ else if (c == '-')
1261
+ set = diff_get_color_opt(o, DIFF_FILE_OLD);
1262
+ }
1263
+ emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
1264
flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
1265
break;
1266
case DIFF_SYMBOL_PLUS:
@@ -1263,7 +1287,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1287
set = diff_get_color_opt(o, DIFF_FILE_NEW);
1288
}
1289
reset = diff_get_color_opt(o, DIFF_RESET);
1266
- emit_line_ws_markup(o, set, reset, line, len, '+',
1290
+ if (!o->flags.dual_color_diffed_diffs)
1291
+ set_sign = NULL;
1292
+ else {
1293
+ char c = !len ? 0 : line[0];
1294
+
1295
+ set_sign = set;
1296
+ if (c == '-')
1297
+ set = diff_get_color_opt(o, DIFF_FILE_OLD);
1298
+ else if (c == '@')
1299
+ set = diff_get_color_opt(o, DIFF_FRAGINFO);
1300
+ else if (c != '+')
1301
+ set = diff_get_color_opt(o, DIFF_CONTEXT);
1302
+ }
1303
+ emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
1304
flags & DIFF_SYMBOL_CONTENT_WS_MASK,
1305
flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
1306
break;
@@ -1291,7 +1328,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
1328
set = diff_get_color_opt(o, DIFF_FILE_OLD);
1329
}
1330
reset = diff_get_color_opt(o, DIFF_RESET);
1294
- emit_line_ws_markup(o, set, reset, line, len, '-',
1331
+ if (!o->flags.dual_color_diffed_diffs)
1332
+ set_sign = NULL;
1333
+ else {
1334
+ char c = !len ? 0 : line[0];
1335
+
1336
+ set_sign = set;
1337
+ if (c == '+')
1338
+ set = diff_get_color_opt(o, DIFF_FILE_NEW);
1339
+ else if (c == '@')
1340
+ set = diff_get_color_opt(o, DIFF_FRAGINFO);
1341
+ else if (c != '-')
1342
+ set = diff_get_color_opt(o, DIFF_CONTEXT);
1343
+ }
1344
+ emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
1345
flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
1346
break;
1347
case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -1482,6 +1532,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
1532
const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
1533
const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
1534
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
1535
+ const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
1536
static const char atat[2] = { '@', '@' };
1537
const char *cp, *ep;
1538
struct strbuf msgbuf = STRBUF_INIT;
@@ -1502,6 +1553,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
1553
ep += 2; /* skip over @@ */
1554
1555
/* The hunk header in fraginfo color */
1556
+ if (ecbdata->opt->flags.dual_color_diffed_diffs)
1557
+ strbuf_addstr(&msgbuf, reverse);
1558
strbuf_addstr(&msgbuf, frag);
1559
strbuf_add(&msgbuf, line, ep - line);
1560
strbuf_addstr(&msgbuf, reset);
diff.h
+1
@@ -95,6 +95,7 @@ struct diff_flags {
95
unsigned default_follow_renames:1;
96
unsigned stat_with_summary:1;
97
unsigned suppress_diff_headers:1;
98
+ unsigned dual_color_diffed_diffs:1;
99
};
100
101
static inline void diff_flags_or(struct diff_flags *a,