Skip to content

Commit 6be4baf

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 96a3073 commit 6be4baf

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
@@ -562,14 +562,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
562562
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
563563
}
564564

565-
static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
565+
static void emit_line_0(struct diff_options *o,
566+
const char *set, unsigned reverse, const char *reset,
566567
int first, const char *line, int len)
567568
{
568569
int has_trailing_newline, has_trailing_carriage_return;
569570
int nofirst;
570571
FILE *file = o->file;
571572

572-
fputs(diff_line_prefix(o), file);
573+
if (first)
574+
fputs(diff_line_prefix(o), file);
575+
else if (!len)
576+
return;
573577

574578
if (len == 0) {
575579
has_trailing_newline = (first == '\n');
@@ -587,8 +591,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
587591
}
588592

589593
if (len || !nofirst) {
594+
if (reverse && want_color(o->use_color))
595+
fputs(GIT_COLOR_REVERSE, file);
590596
fputs(set, file);
591-
if (!nofirst)
597+
if (first && !nofirst)
592598
fputc(first, file);
593599
fwrite(line, len, 1, file);
594600
fputs(reset, file);
@@ -602,7 +608,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res
602608
static void emit_line(struct diff_options *o, const char *set, const char *reset,
603609
const char *line, int len)
604610
{
605-
emit_line_0(o, set, reset, line[0], line+1, len-1);
611+
emit_line_0(o, set, 0, reset, line[0], line+1, len-1);
606612
}
607613

608614
enum diff_symbol {
@@ -962,7 +968,8 @@ static void dim_moved_lines(struct diff_options *o)
962968

963969
static void emit_line_ws_markup(struct diff_options *o,
964970
const char *set, const char *reset,
965-
const char *line, int len, char sign,
971+
const char *line, int len,
972+
const char *set_sign, char sign,
966973
unsigned ws_rule, int blank_at_eof)
967974
{
968975
const char *ws = NULL;
@@ -973,14 +980,20 @@ static void emit_line_ws_markup(struct diff_options *o,
973980
ws = NULL;
974981
}
975982

976-
if (!ws)
977-
emit_line_0(o, set, reset, sign, line, len);
978-
else if (blank_at_eof)
983+
if (!ws && !set_sign)
984+
emit_line_0(o, set, 0, reset, sign, line, len);
985+
else if (!ws) {
986+
/* Emit just the prefix, then the rest. */
987+
emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
988+
sign, "", 0);
989+
emit_line_0(o, set, 0, reset, 0, line, len);
990+
} else if (blank_at_eof)
979991
/* Blank line at EOF - paint '+' as well */
980-
emit_line_0(o, ws, reset, sign, line, len);
992+
emit_line_0(o, ws, 0, reset, sign, line, len);
981993
else {
982994
/* Emit just the prefix, then the rest. */
983-
emit_line_0(o, set, reset, sign, "", 0);
995+
emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
996+
sign, "", 0);
984997
ws_check_emit(line, len, ws_rule,
985998
o->file, set, reset, ws);
986999
}
@@ -990,7 +1003,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
9901003
struct emitted_diff_symbol *eds)
9911004
{
9921005
static const char *nneof = " No newline at end of file\n";
993-
const char *context, *reset, *set, *meta, *fraginfo;
1006+
const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
9941007
struct strbuf sb = STRBUF_INIT;
9951008

9961009
enum diff_symbol s = eds->s;
@@ -1003,7 +1016,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10031016
context = diff_get_color_opt(o, DIFF_CONTEXT);
10041017
reset = diff_get_color_opt(o, DIFF_RESET);
10051018
putc('\n', o->file);
1006-
emit_line_0(o, context, reset, '\\',
1019+
emit_line_0(o, context, 0, reset, '\\',
10071020
nneof, strlen(nneof));
10081021
break;
10091022
case DIFF_SYMBOL_SUBMODULE_HEADER:
@@ -1030,7 +1043,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10301043
case DIFF_SYMBOL_CONTEXT:
10311044
set = diff_get_color_opt(o, DIFF_CONTEXT);
10321045
reset = diff_get_color_opt(o, DIFF_RESET);
1033-
emit_line_ws_markup(o, set, reset, line, len, ' ',
1046+
set_sign = NULL;
1047+
if (o->flags.dual_color_diffed_diffs) {
1048+
char c = !len ? 0 : line[0];
1049+
1050+
if (c == '+')
1051+
set = diff_get_color_opt(o, DIFF_FILE_NEW);
1052+
else if (c == '@')
1053+
set = diff_get_color_opt(o, DIFF_FRAGINFO);
1054+
else if (c == '-')
1055+
set = diff_get_color_opt(o, DIFF_FILE_OLD);
1056+
}
1057+
emit_line_ws_markup(o, set, reset, line, len, set_sign, ' ',
10341058
flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
10351059
break;
10361060
case DIFF_SYMBOL_PLUS:
@@ -1057,7 +1081,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10571081
set = diff_get_color_opt(o, DIFF_FILE_NEW);
10581082
}
10591083
reset = diff_get_color_opt(o, DIFF_RESET);
1060-
emit_line_ws_markup(o, set, reset, line, len, '+',
1084+
if (!o->flags.dual_color_diffed_diffs)
1085+
set_sign = NULL;
1086+
else {
1087+
char c = !len ? 0 : line[0];
1088+
1089+
set_sign = set;
1090+
if (c == '-')
1091+
set = diff_get_color_opt(o, DIFF_FILE_OLD);
1092+
else if (c == '@')
1093+
set = diff_get_color_opt(o, DIFF_FRAGINFO);
1094+
else if (c != '+')
1095+
set = diff_get_color_opt(o, DIFF_CONTEXT);
1096+
}
1097+
emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
10611098
flags & DIFF_SYMBOL_CONTENT_WS_MASK,
10621099
flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
10631100
break;
@@ -1085,7 +1122,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
10851122
set = diff_get_color_opt(o, DIFF_FILE_OLD);
10861123
}
10871124
reset = diff_get_color_opt(o, DIFF_RESET);
1088-
emit_line_ws_markup(o, set, reset, line, len, '-',
1125+
if (!o->flags.dual_color_diffed_diffs)
1126+
set_sign = NULL;
1127+
else {
1128+
char c = !len ? 0 : line[0];
1129+
1130+
set_sign = set;
1131+
if (c == '+')
1132+
set = diff_get_color_opt(o, DIFF_FILE_NEW);
1133+
else if (c == '@')
1134+
set = diff_get_color_opt(o, DIFF_FRAGINFO);
1135+
else if (c != '-')
1136+
set = diff_get_color_opt(o, DIFF_CONTEXT);
1137+
}
1138+
emit_line_ws_markup(o, set, reset, line, len, set_sign, '-',
10891139
flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
10901140
break;
10911141
case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -1276,6 +1326,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
12761326
const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
12771327
const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
12781328
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
1329+
const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
12791330
static const char atat[2] = { '@', '@' };
12801331
const char *cp, *ep;
12811332
struct strbuf msgbuf = STRBUF_INIT;
@@ -1296,6 +1347,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata,
12961347
ep += 2; /* skip over @@ */
12971348

12981349
/* The hunk header in fraginfo color */
1350+
if (ecbdata->opt->flags.dual_color_diffed_diffs)
1351+
strbuf_addstr(&msgbuf, reverse);
12991352
strbuf_addstr(&msgbuf, frag);
13001353
strbuf_add(&msgbuf, line, ep - line);
13011354
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)