Raw
1 /*
2 * Copyright (C) 2005 Junio C Hamano
3 */
4
5 #define USE_THE_REPOSITORY_VARIABLE
6 #define DISABLE_SIGN_COMPARE_WARNINGS
7
8 #include "git-compat-util.h"
9 #include "abspath.h"
10 #include "base85.h"
11 #include "config.h"
12 #include "convert.h"
13 #include "environment.h"
14 #include "gettext.h"
15 #include "tempfile.h"
16 #include "revision.h"
17 #include "quote.h"
18 #include "diff.h"
19 #include "diffcore.h"
20 #include "delta.h"
21 #include "hex.h"
22 #include "xdiff-interface.h"
23 #include "color.h"
24 #include "run-command.h"
25 #include "utf8.h"
26 #include "odb.h"
27 #include "userdiff.h"
28 #include "submodule.h"
29 #include "hashmap.h"
30 #include "mem-pool.h"
31 #include "merge-ll.h"
32 #include "string-list.h"
33 #include "strvec.h"
34 #include "tmp-objdir.h"
35 #include "graph.h"
36 #include "oid-array.h"
37 #include "packfile.h"
38 #include "pager.h"
39 #include "parse-options.h"
40 #include "help.h"
41 #include "promisor-remote.h"
42 #include "dir.h"
43 #include "object-file.h"
44 #include "object-name.h"
45 #include "read-cache-ll.h"
46 #include "setup.h"
47 #include "strmap.h"
48 #include "ws.h"
49
50 #ifdef NO_FAST_WORKING_DIRECTORY
51 #define FAST_WORKING_DIRECTORY 0
52 #else
53 #define FAST_WORKING_DIRECTORY 1
54 #endif
55
56 static int diff_detect_rename_default;
57 static int diff_indent_heuristic = 1;
58 static int diff_rename_limit_default = 1000;
59 static int diff_suppress_blank_empty;
60 static enum git_colorbool diff_use_color_default = GIT_COLOR_UNKNOWN;
61 static int diff_color_moved_default;
62 static int diff_color_moved_ws_default;
63 static unsigned int diff_context_default = 3;
64 static unsigned int diff_interhunk_context_default;
65 static char *diff_word_regex_cfg;
66 static struct external_diff external_diff_cfg;
67 static char *diff_order_file_cfg;
68 int diff_auto_refresh_index = 1;
69 static int diff_mnemonic_prefix;
70 static int diff_no_prefix;
71 static char *diff_src_prefix;
72 static char *diff_dst_prefix;
73 static int diff_relative;
74 static int diff_stat_name_width;
75 static int diff_stat_graph_width;
76 static int diff_dirstat_permille_default = 30;
77 static struct diff_options default_diff_options;
78 static long diff_algorithm;
79 static unsigned ws_error_highlight_default = WSEH_NEW;
80
81 static char diff_colors[][COLOR_MAXLEN] = {
82 GIT_COLOR_RESET,
83 GIT_COLOR_NORMAL, /* CONTEXT */
84 GIT_COLOR_BOLD, /* METAINFO */
85 GIT_COLOR_CYAN, /* FRAGINFO */
86 GIT_COLOR_RED, /* OLD */
87 GIT_COLOR_GREEN, /* NEW */
88 GIT_COLOR_YELLOW, /* COMMIT */
89 GIT_COLOR_BG_RED, /* WHITESPACE */
90 GIT_COLOR_NORMAL, /* FUNCINFO */
91 GIT_COLOR_BOLD_MAGENTA, /* OLD_MOVED */
92 GIT_COLOR_BOLD_BLUE, /* OLD_MOVED ALTERNATIVE */
93 GIT_COLOR_FAINT, /* OLD_MOVED_DIM */
94 GIT_COLOR_FAINT_ITALIC, /* OLD_MOVED_ALTERNATIVE_DIM */
95 GIT_COLOR_BOLD_CYAN, /* NEW_MOVED */
96 GIT_COLOR_BOLD_YELLOW, /* NEW_MOVED ALTERNATIVE */
97 GIT_COLOR_FAINT, /* NEW_MOVED_DIM */
98 GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
99 GIT_COLOR_FAINT, /* CONTEXT_DIM */
100 GIT_COLOR_FAINT_RED, /* OLD_DIM */
101 GIT_COLOR_FAINT_GREEN, /* NEW_DIM */
102 GIT_COLOR_BOLD, /* CONTEXT_BOLD */
103 GIT_COLOR_BOLD_RED, /* OLD_BOLD */
104 GIT_COLOR_BOLD_GREEN, /* NEW_BOLD */
105 };
106
107 static const char *color_diff_slots[] = {
108 [DIFF_CONTEXT] = "context",
109 [DIFF_METAINFO] = "meta",
110 [DIFF_FRAGINFO] = "frag",
111 [DIFF_FILE_OLD] = "old",
112 [DIFF_FILE_NEW] = "new",
113 [DIFF_COMMIT] = "commit",
114 [DIFF_WHITESPACE] = "whitespace",
115 [DIFF_FUNCINFO] = "func",
116 [DIFF_FILE_OLD_MOVED] = "oldMoved",
117 [DIFF_FILE_OLD_MOVED_ALT] = "oldMovedAlternative",
118 [DIFF_FILE_OLD_MOVED_DIM] = "oldMovedDimmed",
119 [DIFF_FILE_OLD_MOVED_ALT_DIM] = "oldMovedAlternativeDimmed",
120 [DIFF_FILE_NEW_MOVED] = "newMoved",
121 [DIFF_FILE_NEW_MOVED_ALT] = "newMovedAlternative",
122 [DIFF_FILE_NEW_MOVED_DIM] = "newMovedDimmed",
123 [DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed",
124 [DIFF_CONTEXT_DIM] = "contextDimmed",
125 [DIFF_FILE_OLD_DIM] = "oldDimmed",
126 [DIFF_FILE_NEW_DIM] = "newDimmed",
127 [DIFF_CONTEXT_BOLD] = "contextBold",
128 [DIFF_FILE_OLD_BOLD] = "oldBold",
129 [DIFF_FILE_NEW_BOLD] = "newBold",
130 };
131
132 define_list_config_array_extra(color_diff_slots, {"plain"});
133
134 static int parse_diff_color_slot(const char *var)
135 {
136 if (!strcasecmp(var, "plain"))
137 return DIFF_CONTEXT;
138 return LOOKUP_CONFIG(color_diff_slots, var);
139 }
140
141 static int parse_dirstat_params(struct diff_options *options, const char *params_string,
142 struct strbuf *errmsg)
143 {
144 char *params_copy = xstrdup(params_string);
145 struct string_list params = STRING_LIST_INIT_NODUP;
146 int ret = 0;
147 int i;
148
149 if (*params_copy)
150 string_list_split_in_place(&params, params_copy, ",", -1);
151 for (i = 0; i < params.nr; i++) {
152 const char *p = params.items[i].string;
153 if (!strcmp(p, "changes")) {
154 options->flags.dirstat_by_line = 0;
155 options->flags.dirstat_by_file = 0;
156 } else if (!strcmp(p, "lines")) {
157 options->flags.dirstat_by_line = 1;
158 options->flags.dirstat_by_file = 0;
159 } else if (!strcmp(p, "files")) {
160 options->flags.dirstat_by_line = 0;
161 options->flags.dirstat_by_file = 1;
162 } else if (!strcmp(p, "noncumulative")) {
163 options->flags.dirstat_cumulative = 0;
164 } else if (!strcmp(p, "cumulative")) {
165 options->flags.dirstat_cumulative = 1;
166 } else if (isdigit(*p)) {
167 char *end;
168 int permille = strtoul(p, &end, 10) * 10;
169 if (*end == '.' && isdigit(*++end)) {
170 /* only use first digit */
171 permille += *end - '0';
172 /* .. and ignore any further digits */
173 while (isdigit(*++end))
174 ; /* nothing */
175 }
176 if (!*end)
177 options->dirstat_permille = permille;
178 else {
179 strbuf_addf(errmsg, _(" Failed to parse dirstat cut-off percentage '%s'\n"),
180 p);
181 ret++;
182 }
183 } else {
184 strbuf_addf(errmsg, _(" Unknown dirstat parameter '%s'\n"), p);
185 ret++;
186 }
187
188 }
189 string_list_clear(&params, 0);
190 free(params_copy);
191 return ret;
192 }
193
194 static int parse_submodule_params(struct diff_options *options, const char *value)
195 {
196 if (!strcmp(value, "log"))
197 options->submodule_format = DIFF_SUBMODULE_LOG;
198 else if (!strcmp(value, "short"))
199 options->submodule_format = DIFF_SUBMODULE_SHORT;
200 else if (!strcmp(value, "diff"))
201 options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
202 /*
203 * Please update $__git_diff_submodule_formats in
204 * git-completion.bash when you add new formats.
205 */
206 else
207 return -1;
208 return 0;
209 }
210
211 int git_config_rename(const char *var, const char *value)
212 {
213 if (!value)
214 return DIFF_DETECT_RENAME;
215 if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy"))
216 return DIFF_DETECT_COPY;
217 return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
218 }
219
220 long parse_algorithm_value(const char *value)
221 {
222 if (!value)
223 return -1;
224 else if (!strcasecmp(value, "myers") || !strcasecmp(value, "default"))
225 return 0;
226 else if (!strcasecmp(value, "minimal"))
227 return XDF_NEED_MINIMAL;
228 else if (!strcasecmp(value, "patience"))
229 return XDF_PATIENCE_DIFF;
230 else if (!strcasecmp(value, "histogram"))
231 return XDF_HISTOGRAM_DIFF;
232 /*
233 * Please update $__git_diff_algorithms in git-completion.bash
234 * when you add new algorithms.
235 */
236 return -1;
237 }
238
239 static int parse_one_token(const char **arg, const char *token)
240 {
241 const char *rest;
242 if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
243 *arg = rest;
244 return 1;
245 }
246 return 0;
247 }
248
249 static int parse_ws_error_highlight(const char *arg)
250 {
251 const char *orig_arg = arg;
252 unsigned val = 0;
253
254 while (*arg) {
255 if (parse_one_token(&arg, "none"))
256 val = 0;
257 else if (parse_one_token(&arg, "default"))
258 val = WSEH_NEW;
259 else if (parse_one_token(&arg, "all"))
260 val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
261 else if (parse_one_token(&arg, "new"))
262 val |= WSEH_NEW;
263 else if (parse_one_token(&arg, "old"))
264 val |= WSEH_OLD;
265 else if (parse_one_token(&arg, "context"))
266 val |= WSEH_CONTEXT;
267 else {
268 return -1 - (int)(arg - orig_arg);
269 }
270 if (*arg)
271 arg++;
272 }
273 return val;
274 }
275
276 /*
277 * These are to give UI layer defaults.
278 * The core-level commands such as git-diff-files should
279 * never be affected by the setting of diff.renames
280 * the user happens to have in the configuration file.
281 */
282 void init_diff_ui_defaults(void)
283 {
284 diff_detect_rename_default = DIFF_DETECT_RENAME;
285 }
286
287 int git_diff_heuristic_config(const char *var, const char *value,
288 void *cb UNUSED)
289 {
290 if (!strcmp(var, "diff.indentheuristic"))
291 diff_indent_heuristic = git_config_bool(var, value);
292 return 0;
293 }
294
295 static int parse_color_moved(const char *arg)
296 {
297 switch (git_parse_maybe_bool(arg)) {
298 case 0:
299 return COLOR_MOVED_NO;
300 case 1:
301 return COLOR_MOVED_DEFAULT;
302 default:
303 break;
304 }
305
306 if (!strcmp(arg, "no"))
307 return COLOR_MOVED_NO;
308 else if (!strcmp(arg, "plain"))
309 return COLOR_MOVED_PLAIN;
310 else if (!strcmp(arg, "blocks"))
311 return COLOR_MOVED_BLOCKS;
312 else if (!strcmp(arg, "zebra"))
313 return COLOR_MOVED_ZEBRA;
314 else if (!strcmp(arg, "default"))
315 return COLOR_MOVED_DEFAULT;
316 else if (!strcmp(arg, "dimmed-zebra"))
317 return COLOR_MOVED_ZEBRA_DIM;
318 else if (!strcmp(arg, "dimmed_zebra"))
319 return COLOR_MOVED_ZEBRA_DIM;
320 else
321 return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'"));
322 }
323
324 static unsigned parse_color_moved_ws(const char *arg)
325 {
326 int ret = 0;
327 struct string_list l = STRING_LIST_INIT_DUP;
328 struct string_list_item *i;
329
330 string_list_split_f(&l, arg, ",", -1, STRING_LIST_SPLIT_TRIM);
331
332 for_each_string_list_item(i, &l) {
333 if (!strcmp(i->string, "no"))
334 ret = 0;
335 else if (!strcmp(i->string, "ignore-space-change"))
336 ret |= XDF_IGNORE_WHITESPACE_CHANGE;
337 else if (!strcmp(i->string, "ignore-space-at-eol"))
338 ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
339 else if (!strcmp(i->string, "ignore-all-space"))
340 ret |= XDF_IGNORE_WHITESPACE;
341 else if (!strcmp(i->string, "allow-indentation-change"))
342 ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
343 else {
344 ret |= COLOR_MOVED_WS_ERROR;
345 error(_("unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', 'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"), i->string);
346 }
347 }
348
349 if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
350 (ret & XDF_WHITESPACE_FLAGS)) {
351 error(_("color-moved-ws: allow-indentation-change cannot be combined with other whitespace modes"));
352 ret |= COLOR_MOVED_WS_ERROR;
353 }
354
355 string_list_clear(&l, 0);
356
357 return ret;
358 }
359
360 int git_diff_ui_config(const char *var, const char *value,
361 const struct config_context *ctx, void *cb)
362 {
363 if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
364 diff_use_color_default = git_config_colorbool(var, value);
365 return 0;
366 }
367 if (!strcmp(var, "diff.colormoved")) {
368 int cm = parse_color_moved(value);
369 if (cm < 0)
370 return -1;
371 diff_color_moved_default = cm;
372 return 0;
373 }
374 if (!strcmp(var, "diff.colormovedws")) {
375 unsigned cm;
376 if (!value)
377 return config_error_nonbool(var);
378 cm = parse_color_moved_ws(value);
379 if (cm & COLOR_MOVED_WS_ERROR)
380 return -1;
381 diff_color_moved_ws_default = cm;
382 return 0;
383 }
384 if (!strcmp(var, "diff.context")) {
385 int val = git_config_int(var, value, ctx->kvi);
386 if (val < 0)
387 return -1;
388 diff_context_default = val;
389 return 0;
390 }
391 if (!strcmp(var, "diff.interhunkcontext")) {
392 int val = git_config_int(var, value, ctx->kvi);
393 if (val < 0)
394 return -1;
395 diff_interhunk_context_default = val;
396 return 0;
397 }
398 if (!strcmp(var, "diff.renames")) {
399 diff_detect_rename_default = git_config_rename(var, value);
400 return 0;
401 }
402 if (!strcmp(var, "diff.autorefreshindex")) {
403 diff_auto_refresh_index = git_config_bool(var, value);
404 return 0;
405 }
406 if (!strcmp(var, "diff.mnemonicprefix")) {
407 diff_mnemonic_prefix = git_config_bool(var, value);
408 return 0;
409 }
410 if (!strcmp(var, "diff.noprefix")) {
411 diff_no_prefix = git_config_bool(var, value);
412 return 0;
413 }
414 if (!strcmp(var, "diff.srcprefix")) {
415 FREE_AND_NULL(diff_src_prefix);
416 return git_config_string(&diff_src_prefix, var, value);
417 }
418 if (!strcmp(var, "diff.dstprefix")) {
419 FREE_AND_NULL(diff_dst_prefix);
420 return git_config_string(&diff_dst_prefix, var, value);
421 }
422 if (!strcmp(var, "diff.relative")) {
423 diff_relative = git_config_bool(var, value);
424 return 0;
425 }
426 if (!strcmp(var, "diff.statnamewidth")) {
427 diff_stat_name_width = git_config_int(var, value, ctx->kvi);
428 return 0;
429 }
430 if (!strcmp(var, "diff.statgraphwidth")) {
431 diff_stat_graph_width = git_config_int(var, value, ctx->kvi);
432 return 0;
433 }
434 if (!strcmp(var, "diff.external"))
435 return git_config_string(&external_diff_cfg.cmd, var, value);
436 if (!strcmp(var, "diff.trustexitcode")) {
437 external_diff_cfg.trust_exit_code = git_config_bool(var, value);
438 return 0;
439 }
440 if (!strcmp(var, "diff.wordregex"))
441 return git_config_string(&diff_word_regex_cfg, var, value);
442 if (!strcmp(var, "diff.orderfile")) {
443 FREE_AND_NULL(diff_order_file_cfg);
444 return git_config_pathname(&diff_order_file_cfg, var, value);
445 }
446
447 if (!strcmp(var, "diff.ignoresubmodules")) {
448 if (!value)
449 return config_error_nonbool(var);
450 handle_ignore_submodules_arg(&default_diff_options, value);
451 }
452
453 if (!strcmp(var, "diff.submodule")) {
454 if (!value)
455 return config_error_nonbool(var);
456 if (parse_submodule_params(&default_diff_options, value))
457 warning(_("Unknown value for 'diff.submodule' config variable: '%s'"),
458 value);
459 return 0;
460 }
461
462 if (!strcmp(var, "diff.algorithm")) {
463 if (!value)
464 return config_error_nonbool(var);
465 diff_algorithm = parse_algorithm_value(value);
466 if (diff_algorithm < 0)
467 return error(_("unknown value for config '%s': %s"),
468 var, value);
469 return 0;
470 }
471
472 if (git_color_config(var, value, cb) < 0)
473 return -1;
474
475 return git_diff_basic_config(var, value, ctx, cb);
476 }
477
478 int git_diff_basic_config(const char *var, const char *value,
479 const struct config_context *ctx, void *cb)
480 {
481 const char *name;
482
483 if (!strcmp(var, "diff.renamelimit")) {
484 diff_rename_limit_default = git_config_int(var, value, ctx->kvi);
485 return 0;
486 }
487
488 if (userdiff_config(var, value) < 0)
489 return -1;
490
491 if (skip_prefix(var, "diff.color.", &name) ||
492 skip_prefix(var, "color.diff.", &name)) {
493 int slot = parse_diff_color_slot(name);
494 if (slot < 0)
495 return 0;
496 if (!value)
497 return config_error_nonbool(var);
498 return color_parse(value, diff_colors[slot]);
499 }
500
501 if (!strcmp(var, "diff.wserrorhighlight")) {
502 int val;
503 if (!value)
504 return config_error_nonbool(var);
505 val = parse_ws_error_highlight(value);
506 if (val < 0)
507 return error(_("unknown value for config '%s': %s"),
508 var, value);
509 ws_error_highlight_default = val;
510 return 0;
511 }
512
513 /* like GNU diff's --suppress-blank-empty option */
514 if (!strcmp(var, "diff.suppressblankempty") ||
515 /* for backwards compatibility */
516 !strcmp(var, "diff.suppress-blank-empty")) {
517 diff_suppress_blank_empty = git_config_bool(var, value);
518 return 0;
519 }
520
521 if (!strcmp(var, "diff.dirstat")) {
522 struct strbuf errmsg = STRBUF_INIT;
523 if (!value)
524 return config_error_nonbool(var);
525 default_diff_options.dirstat_permille = diff_dirstat_permille_default;
526 if (parse_dirstat_params(&default_diff_options, value, &errmsg))
527 warning(_("Found errors in 'diff.dirstat' config variable:\n%s"),
528 errmsg.buf);
529 strbuf_release(&errmsg);
530 diff_dirstat_permille_default = default_diff_options.dirstat_permille;
531 return 0;
532 }
533
534 if (git_diff_heuristic_config(var, value, cb) < 0)
535 return -1;
536
537 return git_default_config(var, value, ctx, cb);
538 }
539
540 static char *quote_two(const char *one, const char *two)
541 {
542 int need_one = quote_c_style(one, NULL, NULL, CQUOTE_NODQ);
543 int need_two = quote_c_style(two, NULL, NULL, CQUOTE_NODQ);
544 struct strbuf res = STRBUF_INIT;
545
546 if (need_one + need_two) {
547 strbuf_addch(&res, '"');
548 quote_c_style(one, &res, NULL, CQUOTE_NODQ);
549 quote_c_style(two, &res, NULL, CQUOTE_NODQ);
550 strbuf_addch(&res, '"');
551 } else {
552 strbuf_addstr(&res, one);
553 strbuf_addstr(&res, two);
554 }
555 return strbuf_detach(&res, NULL);
556 }
557
558 static const struct external_diff *external_diff(void)
559 {
560 static struct external_diff external_diff_env, *external_diff_ptr;
561 static int done_preparing = 0;
562
563 if (done_preparing)
564 return external_diff_ptr;
565 external_diff_env.cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF"));
566 if (git_env_bool("GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE", 0))
567 external_diff_env.trust_exit_code = 1;
568 if (external_diff_env.cmd)
569 external_diff_ptr = &external_diff_env;
570 else if (external_diff_cfg.cmd)
571 external_diff_ptr = &external_diff_cfg;
572 done_preparing = 1;
573 return external_diff_ptr;
574 }
575
576 /*
577 * Keep track of files used for diffing. Sometimes such an entry
578 * refers to a temporary file, sometimes to an existing file, and
579 * sometimes to "/dev/null".
580 */
581 static struct diff_tempfile {
582 /*
583 * filename external diff should read from, or NULL if this
584 * entry is currently not in use:
585 */
586 const char *name;
587
588 char hex[GIT_MAX_HEXSZ + 1];
589 char mode[10];
590
591 /*
592 * If this diff_tempfile instance refers to a temporary file,
593 * this tempfile object is used to manage its lifetime.
594 */
595 struct tempfile *tempfile;
596 } diff_temp[2];
597
598 struct emit_callback {
599 int color_diff;
600 unsigned ws_rule;
601 int blank_at_eof_in_preimage;
602 int blank_at_eof_in_postimage;
603 int lno_in_preimage;
604 int lno_in_postimage;
605 int last_line_kind;
606 const char **label_path;
607 struct diff_words_data *diff_words;
608 struct diff_options *opt;
609 struct strbuf *header;
610 };
611
612 /*
613 * Line-range filter: scopes "git log -L" output to the tracked ranges.
614 *
615 * It sits between xdi_diff_outf() and an output callback (fn_out_consume,
616 * diffstat_consume, checkdiff_consume). xdiff produces a normal diff; the
617 * filter forwards only the lines inside the requested ranges, collecting
618 * contiguous in-range lines into a "range hunk" emitted with a synthetic
619 * @@ header so the callback sees well-formed unified-diff fragments.
620 *
621 * A diff describes the change from a pre-image to a post-image. Each
622 * line is context (' ', in both), a removal ('-', pre-image only), or
623 * an addition ('+', post-image only). -L tracks ranges in the
624 * post-image, so a line is in range by its post-image position.
625 *
626 * Two 1-based cursors track the next line in each image, named as in
627 * struct emit_callback and seeded from the xdiff hunk header:
628 *
629 * lno_in_postimage advances on '+' and ' ' (lines in the post-image)
630 * lno_in_preimage advances on '-' and ' ' (lines in the pre-image)
631 *
632 * Ranges are 0-based half-open [start, end), so a line is tested at the
633 * 0-based index idx_in_postimage = lno_in_postimage - 1.
634 *
635 * A '-' is not present in the post-image, so it has no post-image line
636 * number of its own. Since it does not advance lno_in_postimage, it is
637 * classified at the idx_in_postimage that the following '+'/' ' will
638 * occupy. xdiff emits a change's removals before its additions, so that
639 * index is already known when the '-' arrives.
640 *
641 * The synthetic "@@ -<old> +<new> @@" header has two sides, old (the
642 * pre-image) and new (the post-image), matching the xdiff_emit_hunk_fn
643 * callback; the hunk.old_begin / hunk.new_begin fields below hold those
644 * begins, and flush_range_hunk() derives the counts from the buffered
645 * lines.
646 *
647 * Example, tracking post-image line 2 (range [1, 2)) of:
648 *
649 * pre-image post-image
650 * 1 a 1 a
651 * 2 b 2 X (b -> X)
652 * 3 c 3 c
653 *
654 * classify each line by idx_in_postimage. The pre and post columns
655 * are each cursor's value while that line is classified, i.e. before
656 * the line advances them (pre = lno_in_preimage,
657 * post = lno_in_postimage, idx = idx_in_postimage):
658 * ' a' pre 1 post 1 idx 0 -> before start, skip
659 * '-b' pre 2 post 2 idx 1 -> keep (removal)
660 * '+X' pre 3 post 2 idx 1 -> keep (addition)
661 * ' c' pre 3 post 3 idx 2 -> past end, flush
662 *
663 * -b and +X share idx = 1 because -b did not advance lno_in_postimage;
664 * both land in the range hunk, flushed when ' c' crosses the range end.
665 */
666 struct line_range_filter {
667 xdiff_emit_line_fn orig_line_fn;
668 /*
669 * Optional; consumers that report file line numbers (e.g.
670 * checkdiff) need the synthetic hunk header to set their
671 * post-image position before in-range lines are replayed.
672 */
673 xdiff_emit_hunk_fn orig_hunk_fn;
674 void *orig_cb_data;
675 const struct range_set *ranges; /* 0-based [start, end) */
676 unsigned int cur_range; /* index into the range_set */
677
678 /* Post/pre-image line counters (1-based, set from hunk headers) */
679 long lno_in_postimage;
680 long lno_in_preimage;
681
682 /*
683 * Function name from most recent xdiff hunk header;
684 * size matches struct func_line.buf in xdiff/xemit.c.
685 */
686 char func[80];
687 long funclen;
688
689 /*
690 * The range hunk being accumulated. At most one is live at a time:
691 * it is flushed and reset as the cursor leaves each range (and once
692 * more at end of diff), then reused for the next range.
693 */
694 struct {
695 struct strbuf lines; /* buffered in-range diff lines */
696 long old_begin;
697 long new_begin;
698 int active;
699 } hunk;
700
701 int ret; /* latched error from orig_line_fn */
702 };
703
704 static int count_lines(const char *data, int size)
705 {
706 int count, ch, completely_empty = 1, nl_just_seen = 0;
707 count = 0;
708 while (0 < size--) {
709 ch = *data++;
710 if (ch == '\n') {
711 count++;
712 nl_just_seen = 1;
713 completely_empty = 0;
714 }
715 else {
716 nl_just_seen = 0;
717 completely_empty = 0;
718 }
719 }
720 if (completely_empty)
721 return 0;
722 if (!nl_just_seen)
723 count++; /* no trailing newline */
724 return count;
725 }
726
727 static int fill_mmfile(struct repository *r, mmfile_t *mf,
728 struct diff_filespec *one)
729 {
730 if (!DIFF_FILE_VALID(one)) {
731 mf->ptr = (char *)""; /* does not matter */
732 mf->size = 0;
733 return 0;
734 }
735 else if (diff_populate_filespec(r, one, NULL))
736 return -1;
737
738 mf->ptr = one->data;
739 mf->size = one->size;
740 return 0;
741 }
742
743 /* like fill_mmfile, but only for size, so we can avoid retrieving blob */
744 static unsigned long diff_filespec_size(struct repository *r,
745 struct diff_filespec *one)
746 {
747 struct diff_populate_filespec_options dpf_options = {
748 .check_size_only = 1,
749 };
750
751 if (!DIFF_FILE_VALID(one))
752 return 0;
753 diff_populate_filespec(r, one, &dpf_options);
754 return one->size;
755 }
756
757 static int count_trailing_blank(mmfile_t *mf)
758 {
759 char *ptr = mf->ptr;
760 long size = mf->size;
761 int cnt = 0;
762
763 if (!size)
764 return cnt;
765 ptr += size - 1; /* pointing at the very end */
766 if (*ptr != '\n')
767 ; /* incomplete line */
768 else
769 ptr--; /* skip the last LF */
770 while (mf->ptr < ptr) {
771 char *prev_eol;
772 for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
773 if (*prev_eol == '\n')
774 break;
775 if (!ws_blank_line(prev_eol + 1, ptr - prev_eol))
776 break;
777 cnt++;
778 ptr = prev_eol - 1;
779 }
780 return cnt;
781 }
782
783 static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
784 struct emit_callback *ecbdata)
785 {
786 int l1, l2, at;
787 l1 = count_trailing_blank(mf1);
788 l2 = count_trailing_blank(mf2);
789 if (l2 <= l1) {
790 ecbdata->blank_at_eof_in_preimage = 0;
791 ecbdata->blank_at_eof_in_postimage = 0;
792 return;
793 }
794 at = count_lines(mf1->ptr, mf1->size);
795 ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
796
797 at = count_lines(mf2->ptr, mf2->size);
798 ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
799 }
800
801 static void emit_line_0(struct diff_options *o,
802 const char *set_sign, const char *set, unsigned reverse, const char *reset,
803 int first, const char *line, int len)
804 {
805 int has_trailing_newline, has_trailing_carriage_return;
806 int needs_reset = 0; /* at the end of the line */
807 FILE *file = o->file;
808
809 fputs(diff_line_prefix(o), file);
810
811 has_trailing_newline = (len > 0 && line[len-1] == '\n');
812 if (has_trailing_newline)
813 len--;
814
815 has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
816 if (has_trailing_carriage_return)
817 len--;
818
819 if (!len && !first)
820 goto end_of_line;
821
822 if (reverse && want_color(o->use_color)) {
823 fputs(GIT_COLOR_REVERSE, file);
824 needs_reset = 1;
825 }
826
827 if (set_sign) {
828 fputs(set_sign, file);
829 needs_reset = 1;
830 }
831
832 if (first)
833 fputc(first, file);
834
835 if (!len)
836 goto end_of_line;
837
838 if (set) {
839 if (set_sign && set != set_sign)
840 fputs(reset, file);
841 fputs(set, file);
842 needs_reset = 1;
843 }
844 fwrite(line, len, 1, file);
845 needs_reset = 1; /* 'line' may contain color codes. */
846
847 end_of_line:
848 if (needs_reset)
849 fputs(reset, file);
850 if (has_trailing_carriage_return)
851 fputc('\r', file);
852 if (has_trailing_newline)
853 fputc('\n', file);
854 }
855
856 static void emit_line(struct diff_options *o, const char *set, const char *reset,
857 const char *line, int len)
858 {
859 emit_line_0(o, set, NULL, 0, reset, 0, line, len);
860 }
861
862 enum diff_symbol {
863 DIFF_SYMBOL_BINARY_DIFF_HEADER,
864 DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
865 DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
866 DIFF_SYMBOL_BINARY_DIFF_BODY,
867 DIFF_SYMBOL_BINARY_DIFF_FOOTER,
868 DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
869 DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
870 DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
871 DIFF_SYMBOL_STATS_LINE,
872 DIFF_SYMBOL_WORD_DIFF,
873 DIFF_SYMBOL_STAT_SEP,
874 DIFF_SYMBOL_SUMMARY,
875 DIFF_SYMBOL_SUBMODULE_ADD,
876 DIFF_SYMBOL_SUBMODULE_DEL,
877 DIFF_SYMBOL_SUBMODULE_UNTRACKED,
878 DIFF_SYMBOL_SUBMODULE_MODIFIED,
879 DIFF_SYMBOL_SUBMODULE_HEADER,
880 DIFF_SYMBOL_SUBMODULE_ERROR,
881 DIFF_SYMBOL_SUBMODULE_PIPETHROUGH,
882 DIFF_SYMBOL_REWRITE_DIFF,
883 DIFF_SYMBOL_BINARY_FILES,
884 DIFF_SYMBOL_HEADER,
885 DIFF_SYMBOL_FILEPAIR_PLUS,
886 DIFF_SYMBOL_FILEPAIR_MINUS,
887 DIFF_SYMBOL_WORDS_PORCELAIN,
888 DIFF_SYMBOL_WORDS,
889 DIFF_SYMBOL_CONTEXT,
890 DIFF_SYMBOL_CONTEXT_INCOMPLETE,
891 DIFF_SYMBOL_PLUS,
892 DIFF_SYMBOL_MINUS,
893 DIFF_SYMBOL_CONTEXT_FRAGINFO,
894 DIFF_SYMBOL_CONTEXT_MARKER,
895 DIFF_SYMBOL_SEPARATOR
896 };
897
898 /*
899 * Flags for content lines:
900 * 0..15 are whitespace rules (see ws.h)
901 * 16..18 are WSEH_NEW | WSEH_CONTEXT | WSEH_OLD
902 * 19 is marking if the line is blank at EOF
903 * 20..22 are used for color-moved.
904 */
905 #define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<19)
906 #define DIFF_SYMBOL_MOVED_LINE (1<<20)
907 #define DIFF_SYMBOL_MOVED_LINE_ALT (1<<21)
908 #define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<22)
909
910 #define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)
911
912 /*
913 * This struct is used when we need to buffer the output of the diff output.
914 *
915 * NEEDSWORK: Instead of storing a copy of the line, add an offset pointer
916 * into the pre/post image file. This pointer could be a union with the
917 * line pointer. By storing an offset into the file instead of the literal line,
918 * we can decrease the memory footprint for the buffered output. At first we
919 * may want to only have indirection for the content lines, but we could also
920 * enhance the state for emitting prefabricated lines, e.g. the similarity
921 * score line or hunk/file headers would only need to store a number or path
922 * and then the output can be constructed later on depending on state.
923 */
924 struct emitted_diff_symbol {
925 const char *line;
926 int len;
927 int flags;
928 int indent_off; /* Offset to first non-whitespace character */
929 int indent_width; /* The visual width of the indentation */
930 unsigned id;
931 enum diff_symbol s;
932 };
933 #define EMITTED_DIFF_SYMBOL_INIT { 0 }
934
935 struct emitted_diff_symbols {
936 struct emitted_diff_symbol *buf;
937 int nr, alloc;
938 };
939 #define EMITTED_DIFF_SYMBOLS_INIT { 0 }
940
941 static void append_emitted_diff_symbol(struct diff_options *o,
942 struct emitted_diff_symbol *e)
943 {
944 struct emitted_diff_symbol *f;
945
946 ALLOC_GROW(o->emitted_symbols->buf,
947 o->emitted_symbols->nr + 1,
948 o->emitted_symbols->alloc);
949 f = &o->emitted_symbols->buf[o->emitted_symbols->nr++];
950
951 memcpy(f, e, sizeof(struct emitted_diff_symbol));
952 f->line = e->line ? xmemdupz(e->line, e->len) : NULL;
953 }
954
955 static void free_emitted_diff_symbols(struct emitted_diff_symbols *e)
956 {
957 if (!e)
958 return;
959 free(e->buf);
960 free(e);
961 }
962
963 struct moved_entry {
964 const struct emitted_diff_symbol *es;
965 struct moved_entry *next_line;
966 struct moved_entry *next_match;
967 };
968
969 struct moved_block {
970 struct moved_entry *match;
971 int wsd; /* The whitespace delta of this block */
972 };
973
974 #define INDENT_BLANKLINE INT_MIN
975
976 static void fill_es_indent_data(struct emitted_diff_symbol *es)
977 {
978 unsigned int off = 0, i;
979 int width = 0, tab_width = es->flags & WS_TAB_WIDTH_MASK;
980 const char *s = es->line;
981 const int len = es->len;
982
983 /* skip any \v \f \r at start of indentation */
984 while (s[off] == '\f' || s[off] == '\v' ||
985 (off < len - 1 && s[off] == '\r'))
986 off++;
987
988 /* calculate the visual width of indentation */
989 while(1) {
990 if (s[off] == ' ') {
991 width++;
992 off++;
993 } else if (s[off] == '\t') {
994 width += tab_width - (width % tab_width);
995 while (s[++off] == '\t')
996 width += tab_width;
997 } else {
998 break;
999 }
1000 }
1001
1002 /* check if this line is blank */
1003 for (i = off; i < len; i++)
1004 if (!isspace(s[i]))
1005 break;
1006
1007 if (i == len) {
1008 es->indent_width = INDENT_BLANKLINE;
1009 es->indent_off = len;
1010 } else {
1011 es->indent_off = off;
1012 es->indent_width = width;
1013 }
1014 }
1015
1016 static int compute_ws_delta(const struct emitted_diff_symbol *a,
1017 const struct emitted_diff_symbol *b)
1018 {
1019 int a_width = a->indent_width,
1020 b_width = b->indent_width;
1021
1022 if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE)
1023 return INDENT_BLANKLINE;
1024
1025 return a_width - b_width;
1026 }
1027
1028 static int cmp_in_block_with_wsd(const struct moved_entry *cur,
1029 const struct emitted_diff_symbol *l,
1030 struct moved_block *pmb)
1031 {
1032 int a_width = cur->es->indent_width, b_width = l->indent_width;
1033 int delta;
1034
1035 /* The text of each line must match */
1036 if (cur->es->id != l->id)
1037 return 1;
1038
1039 /*
1040 * If 'l' and 'cur' are both blank then we don't need to check the
1041 * indent. We only need to check cur as we know the strings match.
1042 * */
1043 if (a_width == INDENT_BLANKLINE)
1044 return 0;
1045
1046 /*
1047 * The indent changes of the block are known and stored in pmb->wsd;
1048 * however we need to check if the indent changes of the current line
1049 * match those of the current block.
1050 */
1051 delta = b_width - a_width;
1052
1053 /*
1054 * If the previous lines of this block were all blank then set its
1055 * whitespace delta.
1056 */
1057 if (pmb->wsd == INDENT_BLANKLINE)
1058 pmb->wsd = delta;
1059
1060 return delta != pmb->wsd;
1061 }
1062
1063 struct interned_diff_symbol {
1064 struct hashmap_entry ent;
1065 struct emitted_diff_symbol *es;
1066 };
1067
1068 static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data,
1069 const struct hashmap_entry *eptr,
1070 const struct hashmap_entry *entry_or_key,
1071 const void *keydata UNUSED)
1072 {
1073 const struct diff_options *diffopt = hashmap_cmp_fn_data;
1074 const struct emitted_diff_symbol *a, *b;
1075 unsigned flags = diffopt->color_moved_ws_handling
1076 & XDF_WHITESPACE_FLAGS;
1077
1078 a = container_of(eptr, const struct interned_diff_symbol, ent)->es;
1079 b = container_of(entry_or_key, const struct interned_diff_symbol, ent)->es;
1080
1081 return !xdiff_compare_lines(a->line + a->indent_off,
1082 a->len - a->indent_off,
1083 b->line + b->indent_off,
1084 b->len - b->indent_off, flags);
1085 }
1086
1087 static void prepare_entry(struct diff_options *o, struct emitted_diff_symbol *l,
1088 struct interned_diff_symbol *s)
1089 {
1090 unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
1091 unsigned int hash = xdiff_hash_string(l->line + l->indent_off,
1092 l->len - l->indent_off, flags);
1093
1094 hashmap_entry_init(&s->ent, hash);
1095 s->es = l;
1096 }
1097
1098 struct moved_entry_list {
1099 struct moved_entry *add, *del;
1100 };
1101
1102 static struct moved_entry_list *add_lines_to_move_detection(struct diff_options *o,
1103 struct mem_pool *entry_mem_pool)
1104 {
1105 struct moved_entry *prev_line = NULL;
1106 struct mem_pool interned_pool;
1107 struct hashmap interned_map;
1108 struct moved_entry_list *entry_list = NULL;
1109 size_t entry_list_alloc = 0;
1110 unsigned id = 0;
1111 int n;
1112
1113 hashmap_init(&interned_map, interned_diff_symbol_cmp, o, 8096);
1114 mem_pool_init(&interned_pool, 1024 * 1024);
1115
1116 for (n = 0; n < o->emitted_symbols->nr; n++) {
1117 struct interned_diff_symbol key;
1118 struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
1119 struct interned_diff_symbol *s;
1120 struct moved_entry *entry;
1121
1122 if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS) {
1123 prev_line = NULL;
1124 continue;
1125 }
1126
1127 if (o->color_moved_ws_handling &
1128 COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
1129 fill_es_indent_data(l);
1130
1131 prepare_entry(o, l, &key);
1132 s = hashmap_get_entry(&interned_map, &key, ent, &key.ent);
1133 if (s) {
1134 l->id = s->es->id;
1135 } else {
1136 l->id = id;
1137 ALLOC_GROW_BY(entry_list, id, 1, entry_list_alloc);
1138 hashmap_add(&interned_map,
1139 memcpy(mem_pool_alloc(&interned_pool,
1140 sizeof(key)),
1141 &key, sizeof(key)));
1142 }
1143 entry = mem_pool_alloc(entry_mem_pool, sizeof(*entry));
1144 entry->es = l;
1145 entry->next_line = NULL;
1146 if (prev_line && prev_line->es->s == l->s)
1147 prev_line->next_line = entry;
1148 prev_line = entry;
1149 if (l->s == DIFF_SYMBOL_PLUS) {
1150 entry->next_match = entry_list[l->id].add;
1151 entry_list[l->id].add = entry;
1152 } else {
1153 entry->next_match = entry_list[l->id].del;
1154 entry_list[l->id].del = entry;
1155 }
1156 }
1157
1158 hashmap_clear(&interned_map);
1159 mem_pool_discard(&interned_pool, 0);
1160
1161 return entry_list;
1162 }
1163
1164 static void pmb_advance_or_null(struct diff_options *o,
1165 struct emitted_diff_symbol *l,
1166 struct moved_block *pmb,
1167 int *pmb_nr)
1168 {
1169 int i, j;
1170
1171 for (i = 0, j = 0; i < *pmb_nr; i++) {
1172 int match;
1173 struct moved_entry *prev = pmb[i].match;
1174 struct moved_entry *cur = (prev && prev->next_line) ?
1175 prev->next_line : NULL;
1176
1177 if (o->color_moved_ws_handling &
1178 COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
1179 match = cur &&
1180 !cmp_in_block_with_wsd(cur, l, &pmb[i]);
1181 else
1182 match = cur && cur->es->id == l->id;
1183
1184 if (match) {
1185 pmb[j] = pmb[i];
1186 pmb[j++].match = cur;
1187 }
1188 }
1189 *pmb_nr = j;
1190 }
1191
1192 static void fill_potential_moved_blocks(struct diff_options *o,
1193 struct moved_entry *match,
1194 struct emitted_diff_symbol *l,
1195 struct moved_block **pmb_p,
1196 int *pmb_alloc_p, int *pmb_nr_p)
1197
1198 {
1199 struct moved_block *pmb = *pmb_p;
1200 int pmb_alloc = *pmb_alloc_p, pmb_nr = *pmb_nr_p;
1201
1202 /*
1203 * The current line is the start of a new block.
1204 * Setup the set of potential blocks.
1205 */
1206 for (; match; match = match->next_match) {
1207 ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
1208 if (o->color_moved_ws_handling &
1209 COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
1210 pmb[pmb_nr].wsd = compute_ws_delta(l, match->es);
1211 else
1212 pmb[pmb_nr].wsd = 0;
1213 pmb[pmb_nr++].match = match;
1214 }
1215
1216 *pmb_p = pmb;
1217 *pmb_alloc_p = pmb_alloc;
1218 *pmb_nr_p = pmb_nr;
1219 }
1220
1221 /*
1222 * If o->color_moved is COLOR_MOVED_PLAIN, this function does nothing.
1223 *
1224 * Otherwise, if the last block has fewer alphanumeric characters than
1225 * COLOR_MOVED_MIN_ALNUM_COUNT, unset DIFF_SYMBOL_MOVED_LINE on all lines in
1226 * that block.
1227 *
1228 * The last block consists of the (n - block_length)'th line up to but not
1229 * including the nth line.
1230 *
1231 * Returns 0 if the last block is empty or is unset by this function, non zero
1232 * otherwise.
1233 *
1234 * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c.
1235 * Think of a way to unify them.
1236 */
1237 #define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \
1238 (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT)
1239 static int adjust_last_block(struct diff_options *o, int n, int block_length)
1240 {
1241 int i, alnum_count = 0;
1242 if (o->color_moved == COLOR_MOVED_PLAIN)
1243 return block_length;
1244 for (i = 1; i < block_length + 1; i++) {
1245 const char *c = o->emitted_symbols->buf[n - i].line;
1246 for (; *c; c++) {
1247 if (!isalnum(*c))
1248 continue;
1249 alnum_count++;
1250 if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT)
1251 return 1;
1252 }
1253 }
1254 for (i = 1; i < block_length + 1; i++)
1255 o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK;
1256 return 0;
1257 }
1258
1259 /* Find blocks of moved code, delegate actual coloring decision to helper */
1260 static void mark_color_as_moved(struct diff_options *o,
1261 struct moved_entry_list *entry_list)
1262 {
1263 struct moved_block *pmb = NULL; /* potentially moved blocks */
1264 int pmb_nr = 0, pmb_alloc = 0;
1265 int n, flipped_block = 0, block_length = 0;
1266 enum diff_symbol moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
1267
1268
1269 for (n = 0; n < o->emitted_symbols->nr; n++) {
1270 struct moved_entry *match = NULL;
1271 struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
1272
1273 switch (l->s) {
1274 case DIFF_SYMBOL_PLUS:
1275 match = entry_list[l->id].del;
1276 break;
1277 case DIFF_SYMBOL_MINUS:
1278 match = entry_list[l->id].add;
1279 break;
1280 default:
1281 flipped_block = 0;
1282 }
1283
1284 if (pmb_nr && (!match || l->s != moved_symbol)) {
1285 if (!adjust_last_block(o, n, block_length) &&
1286 block_length > 1) {
1287 /*
1288 * Rewind in case there is another match
1289 * starting at the second line of the block
1290 */
1291 match = NULL;
1292 n -= block_length;
1293 }
1294 pmb_nr = 0;
1295 block_length = 0;
1296 flipped_block = 0;
1297 }
1298 if (!match) {
1299 moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
1300 continue;
1301 }
1302
1303 if (o->color_moved == COLOR_MOVED_PLAIN) {
1304 l->flags |= DIFF_SYMBOL_MOVED_LINE;
1305 continue;
1306 }
1307
1308 pmb_advance_or_null(o, l, pmb, &pmb_nr);
1309
1310 if (pmb_nr == 0) {
1311 int contiguous = adjust_last_block(o, n, block_length);
1312
1313 if (!contiguous && block_length > 1)
1314 /*
1315 * Rewind in case there is another match
1316 * starting at the second line of the block
1317 */
1318 n -= block_length;
1319 else
1320 fill_potential_moved_blocks(o, match, l,
1321 &pmb, &pmb_alloc,
1322 &pmb_nr);
1323
1324 if (contiguous && pmb_nr && moved_symbol == l->s)
1325 flipped_block = (flipped_block + 1) % 2;
1326 else
1327 flipped_block = 0;
1328
1329 if (pmb_nr)
1330 moved_symbol = l->s;
1331 else
1332 moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER;
1333
1334 block_length = 0;
1335 }
1336
1337 if (pmb_nr) {
1338 block_length++;
1339 l->flags |= DIFF_SYMBOL_MOVED_LINE;
1340 if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
1341 l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
1342 }
1343 }
1344 adjust_last_block(o, n, block_length);
1345
1346 free(pmb);
1347 }
1348
1349 static void dim_moved_lines(struct diff_options *o)
1350 {
1351 int n;
1352 for (n = 0; n < o->emitted_symbols->nr; n++) {
1353 struct emitted_diff_symbol *prev = (n != 0) ?
1354 &o->emitted_symbols->buf[n - 1] : NULL;
1355 struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
1356 struct emitted_diff_symbol *next =
1357 (n < o->emitted_symbols->nr - 1) ?
1358 &o->emitted_symbols->buf[n + 1] : NULL;
1359
1360 /* Not a plus or minus line? */
1361 if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS)
1362 continue;
1363
1364 /* Not a moved line? */
1365 if (!(l->flags & DIFF_SYMBOL_MOVED_LINE))
1366 continue;
1367
1368 /*
1369 * If prev or next are not a plus or minus line,
1370 * pretend they don't exist
1371 */
1372 if (prev && prev->s != DIFF_SYMBOL_PLUS &&
1373 prev->s != DIFF_SYMBOL_MINUS)
1374 prev = NULL;
1375 if (next && next->s != DIFF_SYMBOL_PLUS &&
1376 next->s != DIFF_SYMBOL_MINUS)
1377 next = NULL;
1378
1379 /* Inside a block? */
1380 if ((prev &&
1381 (prev->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
1382 (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK)) &&
1383 (next &&
1384 (next->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) ==
1385 (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK))) {
1386 l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
1387 continue;
1388 }
1389
1390 /* Check if we are at an interesting bound: */
1391 if (prev && (prev->flags & DIFF_SYMBOL_MOVED_LINE) &&
1392 (prev->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
1393 (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
1394 continue;
1395 if (next && (next->flags & DIFF_SYMBOL_MOVED_LINE) &&
1396 (next->flags & DIFF_SYMBOL_MOVED_LINE_ALT) !=
1397 (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT))
1398 continue;
1399
1400 /*
1401 * The boundary to prev and next are not interesting,
1402 * so this line is not interesting as a whole
1403 */
1404 l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING;
1405 }
1406 }
1407
1408 static void emit_line_ws_markup(struct diff_options *o,
1409 const char *set_sign, const char *set,
1410 const char *reset,
1411 int sign_index, const char *line, int len,
1412 unsigned ws_rule, int blank_at_eof)
1413 {
1414 const char *ws = NULL;
1415 int sign = o->output_indicators[sign_index];
1416
1417 if (diff_suppress_blank_empty &&
1418 sign_index == OUTPUT_INDICATOR_CONTEXT &&
1419 len == 1 && line[0] == '\n')
1420 sign = 0;
1421
1422 if (o->ws_error_highlight & ws_rule) {
1423 ws = diff_get_color_opt(o, DIFF_WHITESPACE);
1424 if (!*ws)
1425 ws = NULL;
1426 }
1427
1428 if (!ws && !set_sign) {
1429 emit_line_0(o, set, NULL, 0, reset, sign, line, len);
1430 } else if (!ws) {
1431 emit_line_0(o, set_sign, set, !!set_sign, reset, sign, line, len);
1432 } else if (blank_at_eof) {
1433 /* Blank line at EOF - paint '+' as well */
1434 emit_line_0(o, ws, NULL, 0, reset, sign, line, len);
1435 } else {
1436 /* Emit just the prefix, then the rest. */
1437 emit_line_0(o, set_sign ? set_sign : set, NULL, !!set_sign, reset,
1438 sign, "", 0);
1439 ws_check_emit(line, len, ws_rule,
1440 o->file, set, reset, ws);
1441 }
1442 }
1443
1444 static void emit_diff_symbol_from_struct(struct diff_options *o,
1445 struct emitted_diff_symbol *eds)
1446 {
1447 const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
1448
1449 enum diff_symbol s = eds->s;
1450 const char *line = eds->line;
1451 int len = eds->len;
1452 unsigned flags = eds->flags;
1453
1454 if (!o->file)
1455 return;
1456
1457 switch (s) {
1458 case DIFF_SYMBOL_SUBMODULE_HEADER:
1459 case DIFF_SYMBOL_SUBMODULE_ERROR:
1460 case DIFF_SYMBOL_SUBMODULE_PIPETHROUGH:
1461 case DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES:
1462 case DIFF_SYMBOL_SUMMARY:
1463 case DIFF_SYMBOL_STATS_LINE:
1464 case DIFF_SYMBOL_BINARY_DIFF_BODY:
1465 case DIFF_SYMBOL_CONTEXT_FRAGINFO:
1466 emit_line(o, "", "", line, len);
1467 break;
1468 case DIFF_SYMBOL_CONTEXT_INCOMPLETE:
1469 if ((flags & WS_INCOMPLETE_LINE) &&
1470 (flags & o->ws_error_highlight))
1471 set = diff_get_color_opt(o, DIFF_WHITESPACE);
1472 else
1473 set = diff_get_color_opt(o, DIFF_CONTEXT);
1474 reset = diff_get_color_opt(o, DIFF_RESET);
1475 emit_line(o, set, reset, line, len);
1476 break;
1477 case DIFF_SYMBOL_CONTEXT_MARKER:
1478 context = diff_get_color_opt(o, DIFF_CONTEXT);
1479 reset = diff_get_color_opt(o, DIFF_RESET);
1480 emit_line(o, context, reset, line, len);
1481 break;
1482 case DIFF_SYMBOL_SEPARATOR:
1483 fprintf(o->file, "%s%c",
1484 diff_line_prefix(o),
1485 o->line_termination);
1486 break;
1487 case DIFF_SYMBOL_CONTEXT:
1488 set = diff_get_color_opt(o, DIFF_CONTEXT);
1489 reset = diff_get_color_opt(o, DIFF_RESET);
1490 set_sign = NULL;
1491 if (o->flags.dual_color_diffed_diffs) {
1492 char c = !len ? 0 : line[0];
1493
1494 if (c == '+')
1495 set = diff_get_color_opt(o, DIFF_FILE_NEW);
1496 else if (c == '@')
1497 set = diff_get_color_opt(o, DIFF_FRAGINFO);
1498 else if (c == '-')
1499 set = diff_get_color_opt(o, DIFF_FILE_OLD);
1500 }
1501 emit_line_ws_markup(o, set_sign, set, reset,
1502 OUTPUT_INDICATOR_CONTEXT, line, len,
1503 flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
1504 break;
1505 case DIFF_SYMBOL_PLUS:
1506 switch (flags & (DIFF_SYMBOL_MOVED_LINE |
1507 DIFF_SYMBOL_MOVED_LINE_ALT |
1508 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
1509 case DIFF_SYMBOL_MOVED_LINE |
1510 DIFF_SYMBOL_MOVED_LINE_ALT |
1511 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
1512 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT_DIM);
1513 break;
1514 case DIFF_SYMBOL_MOVED_LINE |
1515 DIFF_SYMBOL_MOVED_LINE_ALT:
1516 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT);
1517 break;
1518 case DIFF_SYMBOL_MOVED_LINE |
1519 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
1520 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_DIM);
1521 break;
1522 case DIFF_SYMBOL_MOVED_LINE:
1523 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED);
1524 break;
1525 default:
1526 set = diff_get_color_opt(o, DIFF_FILE_NEW);
1527 }
1528 reset = diff_get_color_opt(o, DIFF_RESET);
1529 if (!o->flags.dual_color_diffed_diffs)
1530 set_sign = NULL;
1531 else {
1532 char c = !len ? 0 : line[0];
1533
1534 set_sign = set;
1535 if (c == '-')
1536 set = diff_get_color_opt(o, DIFF_FILE_OLD_BOLD);
1537 else if (c == '@')
1538 set = diff_get_color_opt(o, DIFF_FRAGINFO);
1539 else if (c == '+')
1540 set = diff_get_color_opt(o, DIFF_FILE_NEW_BOLD);
1541 else
1542 set = diff_get_color_opt(o, DIFF_CONTEXT_BOLD);
1543 flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
1544 }
1545 emit_line_ws_markup(o, set_sign, set, reset,
1546 OUTPUT_INDICATOR_NEW, line, len,
1547 flags & DIFF_SYMBOL_CONTENT_WS_MASK,
1548 flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
1549 break;
1550 case DIFF_SYMBOL_MINUS:
1551 switch (flags & (DIFF_SYMBOL_MOVED_LINE |
1552 DIFF_SYMBOL_MOVED_LINE_ALT |
1553 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) {
1554 case DIFF_SYMBOL_MOVED_LINE |
1555 DIFF_SYMBOL_MOVED_LINE_ALT |
1556 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
1557 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT_DIM);
1558 break;
1559 case DIFF_SYMBOL_MOVED_LINE |
1560 DIFF_SYMBOL_MOVED_LINE_ALT:
1561 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT);
1562 break;
1563 case DIFF_SYMBOL_MOVED_LINE |
1564 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING:
1565 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_DIM);
1566 break;
1567 case DIFF_SYMBOL_MOVED_LINE:
1568 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED);
1569 break;
1570 default:
1571 set = diff_get_color_opt(o, DIFF_FILE_OLD);
1572 }
1573 reset = diff_get_color_opt(o, DIFF_RESET);
1574 if (!o->flags.dual_color_diffed_diffs)
1575 set_sign = NULL;
1576 else {
1577 char c = !len ? 0 : line[0];
1578
1579 set_sign = set;
1580 if (c == '+')
1581 set = diff_get_color_opt(o, DIFF_FILE_NEW_DIM);
1582 else if (c == '@')
1583 set = diff_get_color_opt(o, DIFF_FRAGINFO);
1584 else if (c == '-')
1585 set = diff_get_color_opt(o, DIFF_FILE_OLD_DIM);
1586 else
1587 set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
1588 }
1589 emit_line_ws_markup(o, set_sign, set, reset,
1590 OUTPUT_INDICATOR_OLD, line, len,
1591 flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
1592 break;
1593 case DIFF_SYMBOL_WORDS_PORCELAIN:
1594 context = diff_get_color_opt(o, DIFF_CONTEXT);
1595 reset = diff_get_color_opt(o, DIFF_RESET);
1596 emit_line(o, context, reset, line, len);
1597 fputs("~\n", o->file);
1598 break;
1599 case DIFF_SYMBOL_WORDS:
1600 context = diff_get_color_opt(o, DIFF_CONTEXT);
1601 reset = diff_get_color_opt(o, DIFF_RESET);
1602
1603 /* Skip the prefix character */
1604 line++; len--;
1605 emit_line(o, context, reset, line, len);
1606 break;
1607 case DIFF_SYMBOL_FILEPAIR_PLUS:
1608 meta = diff_get_color_opt(o, DIFF_METAINFO);
1609 reset = diff_get_color_opt(o, DIFF_RESET);
1610 fprintf(o->file, "%s%s+++ %s%s%s\n", diff_line_prefix(o), meta,
1611 line, reset,
1612 strchr(line, ' ') ? "\t" : "");
1613 break;
1614 case DIFF_SYMBOL_FILEPAIR_MINUS:
1615 meta = diff_get_color_opt(o, DIFF_METAINFO);
1616 reset = diff_get_color_opt(o, DIFF_RESET);
1617 fprintf(o->file, "%s%s--- %s%s%s\n", diff_line_prefix(o), meta,
1618 line, reset,
1619 strchr(line, ' ') ? "\t" : "");
1620 break;
1621 case DIFF_SYMBOL_BINARY_FILES:
1622 case DIFF_SYMBOL_HEADER:
1623 fprintf(o->file, "%s", line);
1624 break;
1625 case DIFF_SYMBOL_BINARY_DIFF_HEADER:
1626 fprintf(o->file, "%sGIT binary patch\n", diff_line_prefix(o));
1627 break;
1628 case DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA:
1629 fprintf(o->file, "%sdelta %s\n", diff_line_prefix(o), line);
1630 break;
1631 case DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL:
1632 fprintf(o->file, "%sliteral %s\n", diff_line_prefix(o), line);
1633 break;
1634 case DIFF_SYMBOL_BINARY_DIFF_FOOTER:
1635 fputs(diff_line_prefix(o), o->file);
1636 fputc('\n', o->file);
1637 break;
1638 case DIFF_SYMBOL_REWRITE_DIFF:
1639 fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO);
1640 reset = diff_get_color_opt(o, DIFF_RESET);
1641 emit_line(o, fraginfo, reset, line, len);
1642 break;
1643 case DIFF_SYMBOL_SUBMODULE_ADD:
1644 set = diff_get_color_opt(o, DIFF_FILE_NEW);
1645 reset = diff_get_color_opt(o, DIFF_RESET);
1646 emit_line(o, set, reset, line, len);
1647 break;
1648 case DIFF_SYMBOL_SUBMODULE_DEL:
1649 set = diff_get_color_opt(o, DIFF_FILE_OLD);
1650 reset = diff_get_color_opt(o, DIFF_RESET);
1651 emit_line(o, set, reset, line, len);
1652 break;
1653 case DIFF_SYMBOL_SUBMODULE_UNTRACKED:
1654 fprintf(o->file, "%sSubmodule %s contains untracked content\n",
1655 diff_line_prefix(o), line);
1656 break;
1657 case DIFF_SYMBOL_SUBMODULE_MODIFIED:
1658 fprintf(o->file, "%sSubmodule %s contains modified content\n",
1659 diff_line_prefix(o), line);
1660 break;
1661 case DIFF_SYMBOL_STATS_SUMMARY_NO_FILES:
1662 emit_line(o, "", "", " 0 files changed\n",
1663 strlen(" 0 files changed\n"));
1664 break;
1665 case DIFF_SYMBOL_STATS_SUMMARY_ABBREV:
1666 emit_line(o, "", "", " ...\n", strlen(" ...\n"));
1667 break;
1668 case DIFF_SYMBOL_WORD_DIFF:
1669 fprintf(o->file, "%.*s", len, line);
1670 break;
1671 case DIFF_SYMBOL_STAT_SEP:
1672 fputs(o->stat_sep, o->file);
1673 break;
1674 default:
1675 BUG("unknown diff symbol");
1676 }
1677 }
1678
1679 static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s,
1680 const char *line, int len, unsigned flags)
1681 {
1682 struct emitted_diff_symbol e = {
1683 .line = line, .len = len, .flags = flags, .s = s
1684 };
1685
1686 if (o->emitted_symbols)
1687 append_emitted_diff_symbol(o, &e);
1688 else
1689 emit_diff_symbol_from_struct(o, &e);
1690 }
1691
1692 void diff_emit_submodule_del(struct diff_options *o, const char *line)
1693 {
1694 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_DEL, line, strlen(line), 0);
1695 }
1696
1697 void diff_emit_submodule_add(struct diff_options *o, const char *line)
1698 {
1699 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ADD, line, strlen(line), 0);
1700 }
1701
1702 void diff_emit_submodule_untracked(struct diff_options *o, const char *path)
1703 {
1704 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_UNTRACKED,
1705 path, strlen(path), 0);
1706 }
1707
1708 void diff_emit_submodule_modified(struct diff_options *o, const char *path)
1709 {
1710 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_MODIFIED,
1711 path, strlen(path), 0);
1712 }
1713
1714 void diff_emit_submodule_header(struct diff_options *o, const char *header)
1715 {
1716 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_HEADER,
1717 header, strlen(header), 0);
1718 }
1719
1720 void diff_emit_submodule_error(struct diff_options *o, const char *err)
1721 {
1722 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ERROR, err, strlen(err), 0);
1723 }
1724
1725 void diff_emit_submodule_pipethrough(struct diff_options *o,
1726 const char *line, int len)
1727 {
1728 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_PIPETHROUGH, line, len, 0);
1729 }
1730
1731 static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
1732 {
1733 if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
1734 ecbdata->blank_at_eof_in_preimage &&
1735 ecbdata->blank_at_eof_in_postimage &&
1736 ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
1737 ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
1738 return 0;
1739 return ws_blank_line(line, len);
1740 }
1741
1742 static void emit_add_line(struct emit_callback *ecbdata,
1743 const char *line, int len)
1744 {
1745 unsigned flags = WSEH_NEW | ecbdata->ws_rule;
1746 if (new_blank_line_at_eof(ecbdata, line, len))
1747 flags |= DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF;
1748
1749 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags);
1750 }
1751
1752 static void emit_del_line(struct emit_callback *ecbdata,
1753 const char *line, int len)
1754 {
1755 unsigned flags = WSEH_OLD | ecbdata->ws_rule;
1756 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags);
1757 }
1758
1759 static void emit_context_line(struct emit_callback *ecbdata,
1760 const char *line, int len)
1761 {
1762 unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule;
1763 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags);
1764 }
1765
1766 static void emit_incomplete_line_marker(struct emit_callback *ecbdata,
1767 const char *line, int len)
1768 {
1769 int last_line_kind = ecbdata->last_line_kind;
1770 unsigned flags = (last_line_kind == '+'
1771 ? WSEH_NEW
1772 : last_line_kind == '-'
1773 ? WSEH_OLD
1774 : WSEH_CONTEXT) | ecbdata->ws_rule;
1775 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT_INCOMPLETE,
1776 line, len, flags);
1777 }
1778
1779 static void emit_hunk_header(struct emit_callback *ecbdata,
1780 const char *line, int len)
1781 {
1782 const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
1783 const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
1784 const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
1785 const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
1786 const char *reverse = want_color(ecbdata->color_diff) ? GIT_COLOR_REVERSE : "";
1787 static const char atat[2] = { '@', '@' };
1788 const char *cp, *ep;
1789 struct strbuf msgbuf = STRBUF_INIT;
1790 int org_len = len;
1791 int i = 1;
1792
1793 /*
1794 * As a hunk header must begin with "@@ -<old>, +<new> @@",
1795 * it always is at least 10 bytes long.
1796 */
1797 if (len < 10 ||
1798 memcmp(line, atat, 2) ||
1799 !(ep = memmem(line + 2, len - 2, atat, 2))) {
1800 emit_diff_symbol(ecbdata->opt,
1801 DIFF_SYMBOL_CONTEXT_MARKER, line, len, 0);
1802 return;
1803 }
1804 ep += 2; /* skip over @@ */
1805
1806 /* The hunk header in fraginfo color */
1807 if (ecbdata->opt->flags.dual_color_diffed_diffs)
1808 strbuf_addstr(&msgbuf, reverse);
1809 strbuf_addstr(&msgbuf, frag);
1810 if (ecbdata->opt->flags.suppress_hunk_header_line_count)
1811 strbuf_add(&msgbuf, atat, sizeof(atat));
1812 else
1813 strbuf_add(&msgbuf, line, ep - line);
1814 strbuf_addstr(&msgbuf, reset);
1815
1816 /*
1817 * trailing "\r\n"
1818 */
1819 for ( ; i < 3; i++)
1820 if (line[len - i] == '\r' || line[len - i] == '\n')
1821 len--;
1822
1823 /* blank before the func header */
1824 for (cp = ep; ep - line < len; ep++)
1825 if (*ep != ' ' && *ep != '\t')
1826 break;
1827 if (ep != cp) {
1828 strbuf_addstr(&msgbuf, context);
1829 strbuf_add(&msgbuf, cp, ep - cp);
1830 strbuf_addstr(&msgbuf, reset);
1831 }
1832
1833 if (ep < line + len) {
1834 strbuf_addstr(&msgbuf, func);
1835 strbuf_add(&msgbuf, ep, line + len - ep);
1836 strbuf_addstr(&msgbuf, reset);
1837 }
1838
1839 strbuf_add(&msgbuf, line + len, org_len - len);
1840 strbuf_complete_line(&msgbuf);
1841 emit_diff_symbol(ecbdata->opt,
1842 DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len, 0);
1843 strbuf_release(&msgbuf);
1844 }
1845
1846 static struct diff_tempfile *claim_diff_tempfile(void)
1847 {
1848 int i;
1849 for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
1850 if (!diff_temp[i].name)
1851 return diff_temp + i;
1852 BUG("diff is failing to clean up its tempfiles");
1853 }
1854
1855 static void remove_tempfile(void)
1856 {
1857 int i;
1858 for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
1859 if (is_tempfile_active(diff_temp[i].tempfile))
1860 delete_tempfile(&diff_temp[i].tempfile);
1861 diff_temp[i].name = NULL;
1862 }
1863 }
1864
1865 static void add_line_count(struct strbuf *out, int count)
1866 {
1867 switch (count) {
1868 case 0:
1869 strbuf_addstr(out, "0,0");
1870 break;
1871 case 1:
1872 strbuf_addstr(out, "1");
1873 break;
1874 default:
1875 strbuf_addf(out, "1,%d", count);
1876 break;
1877 }
1878 }
1879
1880 static void emit_rewrite_lines(struct emit_callback *ecbdata,
1881 int prefix, const char *data, int size)
1882 {
1883 const char *endp = NULL;
1884
1885 while (0 < size) {
1886 int len, plen;
1887 char *pdata = NULL;
1888
1889 endp = memchr(data, '\n', size);
1890
1891 if (endp) {
1892 len = endp - data + 1;
1893 plen = len;
1894 } else {
1895 len = size;
1896 plen = len + 1;
1897 pdata = xmalloc(plen + 2);
1898 memcpy(pdata, data, len);
1899 pdata[len] = '\n';
1900 pdata[len + 1] = '\0';
1901 }
1902 if (prefix != '+') {
1903 ecbdata->lno_in_preimage++;
1904 emit_del_line(ecbdata, pdata ? pdata : data, plen);
1905 } else {
1906 ecbdata->lno_in_postimage++;
1907 emit_add_line(ecbdata, pdata ? pdata : data, plen);
1908 }
1909 free(pdata);
1910 size -= len;
1911 data += len;
1912 }
1913 if (!endp) {
1914 static const char nneof[] = "\\ No newline at end of file\n";
1915 ecbdata->last_line_kind = prefix;
1916 emit_incomplete_line_marker(ecbdata, nneof, sizeof(nneof) - 1);
1917 }
1918 }
1919
1920 static void emit_rewrite_diff(const char *name_a,
1921 const char *name_b,
1922 struct diff_filespec *one,
1923 struct diff_filespec *two,
1924 struct userdiff_driver *textconv_one,
1925 struct userdiff_driver *textconv_two,
1926 struct diff_options *o)
1927 {
1928 int lc_a, lc_b;
1929 static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
1930 const char *a_prefix, *b_prefix;
1931 char *data_one, *data_two;
1932 size_t size_one, size_two;
1933 unsigned ws_rule;
1934 struct emit_callback ecbdata;
1935 struct strbuf out = STRBUF_INIT;
1936
1937 if (diff_mnemonic_prefix && o->flags.reverse_diff) {
1938 a_prefix = o->b_prefix;
1939 b_prefix = o->a_prefix;
1940 } else {
1941 a_prefix = o->a_prefix;
1942 b_prefix = o->b_prefix;
1943 }
1944
1945 name_a += (*name_a == '/');
1946 name_b += (*name_b == '/');
1947
1948 strbuf_reset(&a_name);
1949 strbuf_reset(&b_name);
1950 quote_two_c_style(&a_name, a_prefix, name_a, 0);
1951 quote_two_c_style(&b_name, b_prefix, name_b, 0);
1952
1953 size_one = fill_textconv(o->repo, textconv_one, one, &data_one);
1954 size_two = fill_textconv(o->repo, textconv_two, two, &data_two);
1955
1956 ws_rule = whitespace_rule(o->repo->index, name_b);
1957
1958 /* symlink being an incomplete line is not a news */
1959 if (DIFF_FILE_VALID(two) && S_ISLNK(two->mode))
1960 ws_rule &= ~WS_INCOMPLETE_LINE;
1961
1962 memset(&ecbdata, 0, sizeof(ecbdata));
1963 ecbdata.color_diff = o->use_color;
1964 ecbdata.ws_rule = ws_rule;
1965 ecbdata.opt = o;
1966 if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
1967 mmfile_t mf1, mf2;
1968 mf1.ptr = (char *)data_one;
1969 mf2.ptr = (char *)data_two;
1970 mf1.size = size_one;
1971 mf2.size = size_two;
1972 check_blank_at_eof(&mf1, &mf2, &ecbdata);
1973 }
1974 ecbdata.lno_in_preimage = 1;
1975 ecbdata.lno_in_postimage = 1;
1976
1977 lc_a = count_lines(data_one, size_one);
1978 lc_b = count_lines(data_two, size_two);
1979
1980 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
1981 a_name.buf, a_name.len, 0);
1982 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
1983 b_name.buf, b_name.len, 0);
1984
1985 strbuf_addstr(&out, "@@ -");
1986 if (!o->irreversible_delete)
1987 add_line_count(&out, lc_a);
1988 else
1989 strbuf_addstr(&out, "?,?");
1990 strbuf_addstr(&out, " +");
1991 add_line_count(&out, lc_b);
1992 strbuf_addstr(&out, " @@\n");
1993 emit_diff_symbol(o, DIFF_SYMBOL_REWRITE_DIFF, out.buf, out.len, 0);
1994 strbuf_release(&out);
1995
1996 if (lc_a && !o->irreversible_delete)
1997 emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
1998 if (lc_b)
1999 emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
2000 if (textconv_one)
2001 free((char *)data_one);
2002 if (textconv_two)
2003 free((char *)data_two);
2004 }
2005
2006 struct diff_words_buffer {
2007 mmfile_t text;
2008 unsigned long alloc;
2009 struct diff_words_orig {
2010 const char *begin, *end;
2011 } *orig;
2012 int orig_nr, orig_alloc;
2013 };
2014
2015 static void diff_words_append(char *line, unsigned long len,
2016 struct diff_words_buffer *buffer)
2017 {
2018 ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
2019 line++;
2020 len--;
2021 memcpy(buffer->text.ptr + buffer->text.size, line, len);
2022 buffer->text.size += len;
2023 buffer->text.ptr[buffer->text.size] = '\0';
2024 }
2025
2026 struct diff_words_style_elem {
2027 const char *prefix;
2028 const char *suffix;
2029 const char *color; /* NULL; filled in by the setup code if
2030 * color is enabled */
2031 };
2032
2033 struct diff_words_style {
2034 enum diff_words_type type;
2035 struct diff_words_style_elem new_word, old_word, ctx;
2036 const char *newline;
2037 };
2038
2039 static struct diff_words_style diff_words_styles[] = {
2040 { DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" },
2041 { DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" },
2042 { DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" }
2043 };
2044
2045 struct diff_words_data {
2046 struct diff_words_buffer minus, plus;
2047 const char *current_plus;
2048 int last_minus;
2049 struct diff_options *opt;
2050 regex_t *word_regex;
2051 enum diff_words_type type;
2052 struct diff_words_style *style;
2053 };
2054
2055 static int fn_out_diff_words_write_helper(struct diff_options *o,
2056 struct diff_words_style_elem *st_el,
2057 const char *newline,
2058 size_t count, const char *buf)
2059 {
2060 int print = 0;
2061 struct strbuf sb = STRBUF_INIT;
2062
2063 while (count) {
2064 const char *p = memchr(buf, '\n', count);
2065 if (print)
2066 strbuf_addstr(&sb, diff_line_prefix(o));
2067
2068 if (p != buf) {
2069 const char *reset = st_el->color && *st_el->color ?
2070 GIT_COLOR_RESET : NULL;
2071 if (st_el->color && *st_el->color)
2072 strbuf_addstr(&sb, st_el->color);
2073 strbuf_addstr(&sb, st_el->prefix);
2074 strbuf_add(&sb, buf, p ? p - buf : count);
2075 strbuf_addstr(&sb, st_el->suffix);
2076 if (reset)
2077 strbuf_addstr(&sb, reset);
2078 }
2079 if (!p)
2080 goto out;
2081
2082 strbuf_addstr(&sb, newline);
2083 count -= p + 1 - buf;
2084 buf = p + 1;
2085 print = 1;
2086 if (count) {
2087 emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
2088 sb.buf, sb.len, 0);
2089 strbuf_reset(&sb);
2090 }
2091 }
2092
2093 out:
2094 if (sb.len)
2095 emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF,
2096 sb.buf, sb.len, 0);
2097 strbuf_release(&sb);
2098 return 0;
2099 }
2100
2101 /*
2102 * '--color-words' algorithm can be described as:
2103 *
2104 * 1. collect the minus/plus lines of a diff hunk, divided into
2105 * minus-lines and plus-lines;
2106 *
2107 * 2. break both minus-lines and plus-lines into words and
2108 * place them into two mmfile_t with one word for each line;
2109 *
2110 * 3. use xdiff to run diff on the two mmfile_t to get the words level diff;
2111 *
2112 * And for the common parts of the both file, we output the plus side text.
2113 * diff_words->current_plus is used to trace the current position of the plus file
2114 * which printed. diff_words->last_minus is used to trace the last minus word
2115 * printed.
2116 *
2117 * For '--graph' to work with '--color-words', we need to output the graph prefix
2118 * on each line of color words output. Generally, there are two conditions on
2119 * which we should output the prefix.
2120 *
2121 * 1. diff_words->last_minus == 0 &&
2122 * diff_words->current_plus == diff_words->plus.text.ptr
2123 *
2124 * that is: the plus text must start as a new line, and if there is no minus
2125 * word printed, a graph prefix must be printed.
2126 *
2127 * 2. diff_words->current_plus > diff_words->plus.text.ptr &&
2128 * *(diff_words->current_plus - 1) == '\n'
2129 *
2130 * that is: a graph prefix must be printed following a '\n'
2131 */
2132 static int color_words_output_graph_prefix(struct diff_words_data *diff_words)
2133 {
2134 if ((diff_words->last_minus == 0 &&
2135 diff_words->current_plus == diff_words->plus.text.ptr) ||
2136 (diff_words->current_plus > diff_words->plus.text.ptr &&
2137 *(diff_words->current_plus - 1) == '\n')) {
2138 return 1;
2139 } else {
2140 return 0;
2141 }
2142 }
2143
2144 static void fn_out_diff_words_aux(void *priv,
2145 long minus_first, long minus_len,
2146 long plus_first, long plus_len,
2147 const char *func UNUSED, long funclen UNUSED)
2148 {
2149 struct diff_words_data *diff_words = priv;
2150 struct diff_words_style *style = diff_words->style;
2151 const char *minus_begin, *minus_end, *plus_begin, *plus_end;
2152 struct diff_options *opt = diff_words->opt;
2153 const char *line_prefix;
2154
2155 assert(opt);
2156 line_prefix = diff_line_prefix(opt);
2157
2158 /* POSIX requires that first be decremented by one if len == 0... */
2159 if (minus_len) {
2160 minus_begin = diff_words->minus.orig[minus_first].begin;
2161 minus_end =
2162 diff_words->minus.orig[minus_first + minus_len - 1].end;
2163 } else
2164 minus_begin = minus_end =
2165 diff_words->minus.orig[minus_first].end;
2166
2167 if (plus_len) {
2168 plus_begin = diff_words->plus.orig[plus_first].begin;
2169 plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end;
2170 } else
2171 plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
2172
2173 if (color_words_output_graph_prefix(diff_words)) {
2174 fputs(line_prefix, diff_words->opt->file);
2175 }
2176 if (diff_words->current_plus != plus_begin) {
2177 fn_out_diff_words_write_helper(diff_words->opt,
2178 &style->ctx, style->newline,
2179 plus_begin - diff_words->current_plus,
2180 diff_words->current_plus);
2181 }
2182 if (minus_begin != minus_end) {
2183 fn_out_diff_words_write_helper(diff_words->opt,
2184 &style->old_word, style->newline,
2185 minus_end - minus_begin, minus_begin);
2186 }
2187 if (plus_begin != plus_end) {
2188 fn_out_diff_words_write_helper(diff_words->opt,
2189 &style->new_word, style->newline,
2190 plus_end - plus_begin, plus_begin);
2191 }
2192
2193 diff_words->current_plus = plus_end;
2194 diff_words->last_minus = minus_first;
2195 }
2196
2197 /* This function starts looking at *begin, and returns 0 iff a word was found. */
2198 static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
2199 int *begin, int *end)
2200 {
2201 while (word_regex && *begin < buffer->size) {
2202 regmatch_t match[1];
2203 if (!regexec_buf(word_regex, buffer->ptr + *begin,
2204 buffer->size - *begin, 1, match, 0)) {
2205 char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
2206 '\n', match[0].rm_eo - match[0].rm_so);
2207 *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
2208 *begin += match[0].rm_so;
2209 if (*begin == *end)
2210 (*begin)++;
2211 else
2212 return *begin > *end;
2213 } else {
2214 return -1;
2215 }
2216 }
2217
2218 /* find the next word */
2219 while (*begin < buffer->size && isspace(buffer->ptr[*begin]))
2220 (*begin)++;
2221 if (*begin >= buffer->size)
2222 return -1;
2223
2224 /* find the end of the word */
2225 *end = *begin + 1;
2226 while (*end < buffer->size && !isspace(buffer->ptr[*end]))
2227 (*end)++;
2228
2229 return 0;
2230 }
2231
2232 /*
2233 * This function splits the words in buffer->text, stores the list with
2234 * newline separator into out, and saves the offsets of the original words
2235 * in buffer->orig.
2236 */
2237 static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
2238 regex_t *word_regex)
2239 {
2240 int i, j;
2241 long alloc = 0;
2242
2243 out->size = 0;
2244 out->ptr = NULL;
2245
2246 /* fake an empty "0th" word */
2247 ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
2248 buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr;
2249 buffer->orig_nr = 1;
2250
2251 for (i = 0; i < buffer->text.size; i++) {
2252 if (find_word_boundaries(&buffer->text, word_regex, &i, &j))
2253 return;
2254
2255 /* store original boundaries */
2256 ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
2257 buffer->orig_alloc);
2258 buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i;
2259 buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j;
2260 buffer->orig_nr++;
2261
2262 /* store one word */
2263 ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc);
2264 memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
2265 out->ptr[out->size + j - i] = '\n';
2266 out->size += j - i + 1;
2267
2268 i = j - 1;
2269 }
2270 }
2271
2272 /* this executes the word diff on the accumulated buffers */
2273 static void diff_words_show(struct diff_words_data *diff_words)
2274 {
2275 xpparam_t xpp;
2276 xdemitconf_t xecfg;
2277 mmfile_t minus, plus;
2278 struct diff_words_style *style = diff_words->style;
2279
2280 struct diff_options *opt = diff_words->opt;
2281 const char *line_prefix;
2282
2283 assert(opt);
2284 line_prefix = diff_line_prefix(opt);
2285
2286 /* special case: only removal */
2287 if (!diff_words->plus.text.size) {
2288 emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
2289 line_prefix, strlen(line_prefix), 0);
2290 fn_out_diff_words_write_helper(diff_words->opt,
2291 &style->old_word, style->newline,
2292 diff_words->minus.text.size,
2293 diff_words->minus.text.ptr);
2294 diff_words->minus.text.size = 0;
2295 return;
2296 }
2297
2298 diff_words->current_plus = diff_words->plus.text.ptr;
2299 diff_words->last_minus = 0;
2300
2301 memset(&xpp, 0, sizeof(xpp));
2302 memset(&xecfg, 0, sizeof(xecfg));
2303 diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
2304 diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
2305 xpp.flags = 0;
2306 /* as only the hunk header will be parsed, we need a 0-context */
2307 xecfg.ctxlen = 0;
2308 if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, NULL,
2309 diff_words, &xpp, &xecfg))
2310 die("unable to generate word diff");
2311 free(minus.ptr);
2312 free(plus.ptr);
2313 if (diff_words->current_plus != diff_words->plus.text.ptr +
2314 diff_words->plus.text.size) {
2315 if (color_words_output_graph_prefix(diff_words))
2316 emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF,
2317 line_prefix, strlen(line_prefix), 0);
2318 fn_out_diff_words_write_helper(diff_words->opt,
2319 &style->ctx, style->newline,
2320 diff_words->plus.text.ptr + diff_words->plus.text.size
2321 - diff_words->current_plus, diff_words->current_plus);
2322 }
2323 diff_words->minus.text.size = diff_words->plus.text.size = 0;
2324 }
2325
2326 /* In "color-words" mode, show word-diff of words accumulated in the buffer */
2327 static void diff_words_flush(struct emit_callback *ecbdata)
2328 {
2329 struct diff_options *wo = ecbdata->diff_words->opt;
2330
2331 if (ecbdata->diff_words->minus.text.size ||
2332 ecbdata->diff_words->plus.text.size)
2333 diff_words_show(ecbdata->diff_words);
2334
2335 if (wo->emitted_symbols) {
2336 struct diff_options *o = ecbdata->opt;
2337 struct emitted_diff_symbols *wol = wo->emitted_symbols;
2338 int i;
2339
2340 /*
2341 * NEEDSWORK:
2342 * Instead of appending each, concat all words to a line?
2343 */
2344 for (i = 0; i < wol->nr; i++)
2345 append_emitted_diff_symbol(o, &wol->buf[i]);
2346
2347 for (i = 0; i < wol->nr; i++)
2348 free((void *)wol->buf[i].line);
2349
2350 wol->nr = 0;
2351 }
2352 }
2353
2354 static void diff_filespec_load_driver(struct diff_filespec *one,
2355 struct index_state *istate)
2356 {
2357 /* Use already-loaded driver */
2358 if (one->driver)
2359 return;
2360
2361 if (S_ISREG(one->mode))
2362 one->driver = userdiff_find_by_path(istate, one->path);
2363
2364 /* Fallback to default settings */
2365 if (!one->driver)
2366 one->driver = userdiff_find_by_name("default");
2367 }
2368
2369 static const char *userdiff_word_regex(struct diff_filespec *one,
2370 struct index_state *istate)
2371 {
2372 diff_filespec_load_driver(one, istate);
2373 return one->driver->word_regex;
2374 }
2375
2376 static void init_diff_words_data(struct emit_callback *ecbdata,
2377 struct diff_options *orig_opts,
2378 struct diff_filespec *one,
2379 struct diff_filespec *two)
2380 {
2381 int i;
2382 struct diff_options *o = xmalloc(sizeof(struct diff_options));
2383 memcpy(o, orig_opts, sizeof(struct diff_options));
2384
2385 CALLOC_ARRAY(ecbdata->diff_words, 1);
2386 ecbdata->diff_words->type = o->word_diff;
2387 ecbdata->diff_words->opt = o;
2388
2389 if (orig_opts->emitted_symbols)
2390 CALLOC_ARRAY(o->emitted_symbols, 1);
2391
2392 if (!o->word_regex)
2393 o->word_regex = userdiff_word_regex(one, o->repo->index);
2394 if (!o->word_regex)
2395 o->word_regex = userdiff_word_regex(two, o->repo->index);
2396 if (!o->word_regex)
2397 o->word_regex = diff_word_regex_cfg;
2398 if (o->word_regex) {
2399 ecbdata->diff_words->word_regex = (regex_t *)
2400 xmalloc(sizeof(regex_t));
2401 if (regcomp(ecbdata->diff_words->word_regex,
2402 o->word_regex,
2403 REG_EXTENDED | REG_NEWLINE))
2404 die("invalid regular expression: %s",
2405 o->word_regex);
2406 }
2407 for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
2408 if (o->word_diff == diff_words_styles[i].type) {
2409 ecbdata->diff_words->style =
2410 &diff_words_styles[i];
2411 break;
2412 }
2413 }
2414 if (want_color(o->use_color)) {
2415 struct diff_words_style *st = ecbdata->diff_words->style;
2416 st->old_word.color = diff_get_color_opt(o, DIFF_FILE_OLD);
2417 st->new_word.color = diff_get_color_opt(o, DIFF_FILE_NEW);
2418 st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT);
2419 }
2420 }
2421
2422 static void free_diff_words_data(struct emit_callback *ecbdata)
2423 {
2424 if (ecbdata->diff_words) {
2425 diff_words_flush(ecbdata);
2426 free_emitted_diff_symbols(ecbdata->diff_words->opt->emitted_symbols);
2427 free (ecbdata->diff_words->opt);
2428 free (ecbdata->diff_words->minus.text.ptr);
2429 free (ecbdata->diff_words->minus.orig);
2430 free (ecbdata->diff_words->plus.text.ptr);
2431 free (ecbdata->diff_words->plus.orig);
2432 if (ecbdata->diff_words->word_regex) {
2433 regfree(ecbdata->diff_words->word_regex);
2434 free(ecbdata->diff_words->word_regex);
2435 }
2436 FREE_AND_NULL(ecbdata->diff_words);
2437 }
2438 }
2439
2440 const char *diff_get_color(enum git_colorbool diff_use_color, enum color_diff ix)
2441 {
2442 if (want_color(diff_use_color))
2443 return diff_colors[ix];
2444 return "";
2445 }
2446
2447 const char *diff_line_prefix(struct diff_options *opt)
2448 {
2449 return opt->output_prefix ?
2450 opt->output_prefix(opt, opt->output_prefix_data) :
2451 "";
2452 }
2453
2454 static unsigned long sane_truncate_line(char *line, unsigned long len)
2455 {
2456 const char *cp;
2457 unsigned long allot;
2458 size_t l = len;
2459
2460 cp = line;
2461 allot = l;
2462 while (0 < l) {
2463 (void) utf8_width(&cp, &l);
2464 if (!cp)
2465 break; /* truncated in the middle? */
2466 }
2467 return allot - l;
2468 }
2469
2470 static void find_lno(const char *line, struct emit_callback *ecbdata)
2471 {
2472 const char *p;
2473 ecbdata->lno_in_preimage = 0;
2474 ecbdata->lno_in_postimage = 0;
2475 p = strchr(line, '-');
2476 if (!p)
2477 return; /* cannot happen */
2478 ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
2479 p = strchr(p, '+');
2480 if (!p)
2481 return; /* cannot happen */
2482 ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
2483 }
2484
2485 static int fn_out_consume(void *priv, char *line, unsigned long len)
2486 {
2487 struct emit_callback *ecbdata = priv;
2488 struct diff_options *o = ecbdata->opt;
2489
2490 o->found_changes = 1;
2491
2492 if (ecbdata->header) {
2493 emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
2494 ecbdata->header->buf, ecbdata->header->len, 0);
2495 strbuf_reset(ecbdata->header);
2496 ecbdata->header = NULL;
2497 }
2498
2499 if (ecbdata->label_path[0]) {
2500 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS,
2501 ecbdata->label_path[0],
2502 strlen(ecbdata->label_path[0]), 0);
2503 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS,
2504 ecbdata->label_path[1],
2505 strlen(ecbdata->label_path[1]), 0);
2506 ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
2507 }
2508
2509 if (line[0] == '@') {
2510 if (ecbdata->diff_words)
2511 diff_words_flush(ecbdata);
2512 len = sane_truncate_line(line, len);
2513 find_lno(line, ecbdata);
2514 emit_hunk_header(ecbdata, line, len);
2515 return 0;
2516 }
2517
2518 if (ecbdata->diff_words) {
2519 enum diff_symbol s =
2520 ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN ?
2521 DIFF_SYMBOL_WORDS_PORCELAIN : DIFF_SYMBOL_WORDS;
2522 if (line[0] == '-') {
2523 diff_words_append(line, len,
2524 &ecbdata->diff_words->minus);
2525 return 0;
2526 } else if (line[0] == '+') {
2527 diff_words_append(line, len,
2528 &ecbdata->diff_words->plus);
2529 return 0;
2530 } else if (starts_with(line, "\\ ")) {
2531 /*
2532 * Eat the "no newline at eof" marker as if we
2533 * saw a "+" or "-" line with nothing on it,
2534 * and return without diff_words_flush() to
2535 * defer processing. If this is the end of
2536 * preimage, more "+" lines may come after it.
2537 */
2538 return 0;
2539 }
2540 diff_words_flush(ecbdata);
2541 emit_diff_symbol(o, s, line, len, 0);
2542 return 0;
2543 }
2544
2545 switch (line[0]) {
2546 case '+':
2547 ecbdata->lno_in_postimage++;
2548 emit_add_line(ecbdata, line + 1, len - 1);
2549 break;
2550 case '-':
2551 ecbdata->lno_in_preimage++;
2552 emit_del_line(ecbdata, line + 1, len - 1);
2553 break;
2554 case ' ':
2555 ecbdata->lno_in_postimage++;
2556 ecbdata->lno_in_preimage++;
2557 emit_context_line(ecbdata, line + 1, len - 1);
2558 break;
2559 case '\\':
2560 /* incomplete line at the end */
2561 switch (ecbdata->last_line_kind) {
2562 case '+':
2563 case '-':
2564 case ' ':
2565 break;
2566 default:
2567 BUG("fn_out_consume: '\\No newline' after unknown line (%c)",
2568 ecbdata->last_line_kind);
2569 }
2570 ecbdata->lno_in_preimage++;
2571 emit_incomplete_line_marker(ecbdata, line, len);
2572 break;
2573 default:
2574 BUG("fn_out_consume: unknown line '%s'", line);
2575 }
2576 ecbdata->last_line_kind = line[0];
2577 return 0;
2578 }
2579
2580 static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED)
2581 {
2582 struct emit_callback *ecbdata = priv;
2583 struct diff_options *o = ecbdata->opt;
2584
2585 o->found_changes = 1;
2586 return 1;
2587 }
2588
2589 static void line_range_filter_init(struct line_range_filter *filter,
2590 const struct range_set *ranges,
2591 xdiff_emit_line_fn line_fn,
2592 void *cb_data)
2593 {
2594 memset(filter, 0, sizeof(*filter));
2595 filter->orig_line_fn = line_fn;
2596 filter->orig_cb_data = cb_data;
2597 filter->ranges = ranges;
2598 strbuf_init(&filter->hunk.lines, 0);
2599 }
2600
2601 /*
2602 * Begin a range hunk at the first in-range line. Its position fixes the
2603 * hunk's begins, taken from the two image cursors before they advance:
2604 * new_begin from the post-image, old_begin from the pre-image. The line
2605 * counts are not tracked here; flush_range_hunk() derives them from the
2606 * buffered lines.
2607 */
2608 static void begin_range_hunk(struct line_range_filter *filter)
2609 {
2610 filter->hunk.active = 1;
2611 filter->hunk.new_begin = filter->lno_in_postimage;
2612 filter->hunk.old_begin = filter->lno_in_preimage;
2613 strbuf_reset(&filter->hunk.lines);
2614 }
2615
2616 static void flush_range_hunk(struct line_range_filter *filter)
2617 {
2618 struct strbuf hdr = STRBUF_INIT;
2619 const char *p, *end;
2620 long old_count = 0, new_count = 0;
2621 int has_changes = 0;
2622
2623 if (!filter->hunk.active || filter->ret)
2624 return;
2625
2626 /*
2627 * Derive the hunk's geometry from the buffered lines: a ' '
2628 * counts on both sides, a '-' on the old side, a '+' on the new.
2629 * A '-' or '+' marks a real change; the "\ No newline at end of
2630 * file" marker (line[0] == '\\') counts on neither side.
2631 */
2632 p = filter->hunk.lines.buf;
2633 end = p + filter->hunk.lines.len;
2634 while (p < end) {
2635 const char *eol = memchr(p, '\n', end - p);
2636 if (*p == ' ' || *p == '-')
2637 old_count++;
2638 if (*p == ' ' || *p == '+')
2639 new_count++;
2640 if (*p == '-' || *p == '+')
2641 has_changes = 1;
2642 p = eol ? eol + 1 : end;
2643 }
2644
2645 /*
2646 * Suppress context-only hunks: they contain no actual changes
2647 * and would just be noise. This can happen when the inflated
2648 * ctxlen causes xdiff to emit context covering a range that
2649 * has no changes in this commit.
2650 */
2651 if (!has_changes) {
2652 filter->hunk.active = 0;
2653 strbuf_reset(&filter->hunk.lines);
2654 return;
2655 }
2656
2657 xdiff_emit_hunk_header(&hdr, filter->hunk.old_begin, old_count,
2658 filter->hunk.new_begin, new_count,
2659 filter->func, filter->funclen);
2660
2661 /*
2662 * Inform a line-numbering consumer of the post-image position
2663 * before replaying lines, mirroring the hunk callback xdiff
2664 * would have issued for a non-scoped diff.
2665 */
2666 if (filter->orig_hunk_fn)
2667 filter->orig_hunk_fn(filter->orig_cb_data,
2668 filter->hunk.old_begin, old_count,
2669 filter->hunk.new_begin, new_count,
2670 filter->func, filter->funclen);
2671
2672 filter->ret = filter->orig_line_fn(filter->orig_cb_data, hdr.buf, hdr.len);
2673 strbuf_release(&hdr);
2674
2675 /*
2676 * Replay buffered lines one at a time through fn_out_consume.
2677 * The cast discards const because xdiff_emit_line_fn takes
2678 * char *, though fn_out_consume does not modify the buffer.
2679 */
2680 p = filter->hunk.lines.buf;
2681 end = p + filter->hunk.lines.len;
2682 while (!filter->ret && p < end) {
2683 const char *eol = memchr(p, '\n', end - p);
2684 unsigned long line_len = eol ? (unsigned long)(eol - p + 1)
2685 : (unsigned long)(end - p);
2686 filter->ret = filter->orig_line_fn(filter->orig_cb_data, (char *)p, line_len);
2687 p += line_len;
2688 }
2689
2690 filter->hunk.active = 0;
2691 strbuf_reset(&filter->hunk.lines);
2692 }
2693
2694 static void line_range_hunk_fn(void *data,
2695 long old_begin, long old_nr,
2696 long new_begin, long new_nr,
2697 const char *func, long funclen)
2698 {
2699 struct line_range_filter *filter = data;
2700
2701 /*
2702 * Seed the per-image line cursors from the hunk header's begins. For
2703 * a side with no lines (count 0), xdiff's callback has already moved
2704 * its begin to the line before the change, so add one back to recover
2705 * the true 1-based start. xdiff_emit_hunk_header() reapplies that -1
2706 * when the clipped hunk is emitted.
2707 */
2708 filter->lno_in_postimage = new_nr ? new_begin : new_begin + 1;
2709 filter->lno_in_preimage = old_nr ? old_begin : old_begin + 1;
2710
2711 if (funclen > 0) {
2712 if (funclen > (long)sizeof(filter->func))
2713 funclen = sizeof(filter->func);
2714 memcpy(filter->func, func, funclen);
2715 }
2716 filter->funclen = funclen;
2717 }
2718
2719 static int line_range_line_fn(void *priv, char *line, unsigned long len)
2720 {
2721 struct line_range_filter *filter = priv;
2722 long idx_in_postimage;
2723 int in_range;
2724
2725 if (filter->ret)
2726 return filter->ret;
2727
2728 if (line[0] == '\\') {
2729 if (filter->hunk.active)
2730 strbuf_add(&filter->hunk.lines, line, len);
2731 return filter->ret;
2732 }
2733
2734 if (line[0] != '+' && line[0] != ' ' && line[0] != '-')
2735 BUG("unexpected diff line type '%c'", line[0]);
2736
2737 /*
2738 * idx_in_postimage is this line's 0-based post-image index (see the model on
2739 * struct line_range_filter). The cursors are advanced only after
2740 * the line is classified, so a '-' is tested at the same idx_in_postimage as
2741 * the '+'/' ' that follows it.
2742 */
2743 idx_in_postimage = filter->lno_in_postimage - 1;
2744
2745 /* Retire ranges we have passed, flushing the one we leave. */
2746 while (filter->cur_range < filter->ranges->nr &&
2747 idx_in_postimage >= filter->ranges->ranges[filter->cur_range].end) {
2748 if (filter->hunk.active)
2749 flush_range_hunk(filter);
2750 filter->cur_range++;
2751 }
2752
2753 in_range = filter->cur_range < filter->ranges->nr &&
2754 idx_in_postimage >= filter->ranges->ranges[filter->cur_range].start &&
2755 idx_in_postimage < filter->ranges->ranges[filter->cur_range].end;
2756
2757 if (in_range) {
2758 if (!filter->hunk.active)
2759 begin_range_hunk(filter);
2760
2761 strbuf_add(&filter->hunk.lines, line, len);
2762 }
2763
2764 /*
2765 * Advance each image's cursor: a line present in that image (see
2766 * the model) consumes one of its line numbers.
2767 */
2768 if (line[0] != '-')
2769 filter->lno_in_postimage++;
2770 if (line[0] != '+')
2771 filter->lno_in_preimage++;
2772
2773 return filter->ret;
2774 }
2775
2776 /*
2777 * Run an xdiff pass through an initialized line-range filter, flush the
2778 * final range hunk, and release the filter. Inflates ctxlen to the largest
2779 * range span first, so that every change within a single range lands in one
2780 * xdiff hunk and the inter-change context is emitted; the filter then clips
2781 * back to range boundaries. The optimal ctxlen depends on where changes fall
2782 * within the range, which is only known after xdiff runs, so the max span is
2783 * the upper bound that guarantees correctness in a single pass. Every
2784 * consumer (patch, diffstat, check) relies on one xdiff hunk per range, so
2785 * this lives here rather than at each call site. Also clears
2786 * XDL_EMIT_NO_HUNK_HDR: the filter seeds its per-image position from the hunk
2787 * headers, so a consumer that otherwise suppresses them (diffstat) still gets
2788 * them here. Returns non-zero if xdiff or any forwarded callback failed.
2789 */
2790 static int line_range_filter_diff(struct line_range_filter *filter,
2791 mmfile_t *mf1, mmfile_t *mf2,
2792 xpparam_t *xpp, xdemitconf_t *xecfg)
2793 {
2794 const struct range_set *ranges = filter->ranges;
2795 long max_span = 0;
2796 unsigned int i;
2797 int ret;
2798
2799 for (i = 0; i < ranges->nr; i++) {
2800 long span = ranges->ranges[i].end - ranges->ranges[i].start;
2801 if (span > max_span)
2802 max_span = span;
2803 }
2804 if (max_span > xecfg->ctxlen)
2805 xecfg->ctxlen = max_span;
2806
2807 /* the filter seeds its per-image position from hunk headers */
2808 xecfg->flags &= ~XDL_EMIT_NO_HUNK_HDR;
2809
2810 ret = xdi_diff_outf(mf1, mf2, line_range_hunk_fn,
2811 line_range_line_fn, filter, xpp, xecfg);
2812 if (!ret) {
2813 flush_range_hunk(filter);
2814 ret = filter->ret;
2815 }
2816 strbuf_release(&filter->hunk.lines);
2817 return ret;
2818 }
2819
2820 /*
2821 * Expose the in-file line-range filter to callers outside diff.c (e.g.
2822 * pickaxe -G); see xdiff-interface.h for the contract.
2823 */
2824 int diff_emit_line_ranges(mmfile_t *one, mmfile_t *two,
2825 const struct range_set *ranges,
2826 xdiff_emit_line_fn line_fn, void *cb_data,
2827 xpparam_t *xpp, xdemitconf_t *xecfg)
2828 {
2829 struct line_range_filter filter;
2830
2831 line_range_filter_init(&filter, ranges, line_fn, cb_data);
2832 return line_range_filter_diff(&filter, one, two, xpp, xecfg);
2833 }
2834
2835 static void pprint_rename(struct strbuf *name, const char *a, const char *b)
2836 {
2837 const char *old_name = a;
2838 const char *new_name = b;
2839 int pfx_length, sfx_length;
2840 int pfx_adjust_for_slash;
2841 int len_a = strlen(a);
2842 int len_b = strlen(b);
2843 int a_midlen, b_midlen;
2844 int qlen_a = quote_c_style(a, NULL, NULL, 0);
2845 int qlen_b = quote_c_style(b, NULL, NULL, 0);
2846
2847 if (qlen_a || qlen_b) {
2848 quote_c_style(a, name, NULL, 0);
2849 strbuf_addstr(name, " => ");
2850 quote_c_style(b, name, NULL, 0);
2851 return;
2852 }
2853
2854 /* Find common prefix */
2855 pfx_length = 0;
2856 while (*old_name && *new_name && *old_name == *new_name) {
2857 if (*old_name == '/')
2858 pfx_length = old_name - a + 1;
2859 old_name++;
2860 new_name++;
2861 }
2862
2863 /* Find common suffix */
2864 old_name = a + len_a;
2865 new_name = b + len_b;
2866 sfx_length = 0;
2867 /*
2868 * If there is a common prefix, it must end in a slash. In
2869 * that case we let this loop run 1 into the prefix to see the
2870 * same slash.
2871 *
2872 * If there is no common prefix, we cannot do this as it would
2873 * underrun the input strings.
2874 */
2875 pfx_adjust_for_slash = (pfx_length ? 1 : 0);
2876 while (a + pfx_length - pfx_adjust_for_slash <= old_name &&
2877 b + pfx_length - pfx_adjust_for_slash <= new_name &&
2878 *old_name == *new_name) {
2879 if (*old_name == '/')
2880 sfx_length = len_a - (old_name - a);
2881 old_name--;
2882 new_name--;
2883 }
2884
2885 /*
2886 * pfx{mid-a => mid-b}sfx
2887 * {pfx-a => pfx-b}sfx
2888 * pfx{sfx-a => sfx-b}
2889 * name-a => name-b
2890 */
2891 a_midlen = len_a - pfx_length - sfx_length;
2892 b_midlen = len_b - pfx_length - sfx_length;
2893 if (a_midlen < 0)
2894 a_midlen = 0;
2895 if (b_midlen < 0)
2896 b_midlen = 0;
2897
2898 strbuf_grow(name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
2899 if (pfx_length + sfx_length) {
2900 strbuf_add(name, a, pfx_length);
2901 strbuf_addch(name, '{');
2902 }
2903 strbuf_add(name, a + pfx_length, a_midlen);
2904 strbuf_addstr(name, " => ");
2905 strbuf_add(name, b + pfx_length, b_midlen);
2906 if (pfx_length + sfx_length) {
2907 strbuf_addch(name, '}');
2908 strbuf_add(name, a + len_a - sfx_length, sfx_length);
2909 }
2910 }
2911
2912 static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
2913 const char *name_a,
2914 const char *name_b)
2915 {
2916 struct diffstat_file *x;
2917 CALLOC_ARRAY(x, 1);
2918 ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc);
2919 diffstat->files[diffstat->nr++] = x;
2920 if (name_b) {
2921 x->from_name = xstrdup(name_a);
2922 x->name = xstrdup(name_b);
2923 x->is_renamed = 1;
2924 }
2925 else {
2926 x->from_name = NULL;
2927 x->name = xstrdup(name_a);
2928 }
2929 return x;
2930 }
2931
2932 static int diffstat_consume(void *priv, char *line, unsigned long len)
2933 {
2934 struct diffstat_t *diffstat = priv;
2935 struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
2936
2937 if (!len)
2938 BUG("xdiff fed us an empty line");
2939
2940 if (line[0] == '+')
2941 x->added++;
2942 else if (line[0] == '-')
2943 x->deleted++;
2944 return 0;
2945 }
2946
2947 const char mime_boundary_leader[] = "------------";
2948
2949 static int scale_linear(int it, int width, int max_change)
2950 {
2951 if (!it)
2952 return 0;
2953 /*
2954 * make sure that at least one '-' or '+' is printed if
2955 * there is any change to this path. The easiest way is to
2956 * scale linearly as if the allotted width is one column shorter
2957 * than it is, and then add 1 to the result.
2958 */
2959 return 1 + (it * (width - 1) / max_change);
2960 }
2961
2962 static void show_graph(struct strbuf *out, char ch, int cnt,
2963 const char *set, const char *reset)
2964 {
2965 if (cnt <= 0)
2966 return;
2967 strbuf_addstr(out, set);
2968 strbuf_addchars(out, ch, cnt);
2969 strbuf_addstr(out, reset);
2970 }
2971
2972 static void fill_print_name(struct diffstat_file *file)
2973 {
2974 struct strbuf pname = STRBUF_INIT;
2975
2976 if (file->print_name)
2977 return;
2978
2979 if (file->is_renamed)
2980 pprint_rename(&pname, file->from_name, file->name);
2981 else
2982 quote_c_style(file->name, &pname, NULL, 0);
2983
2984 if (file->comments)
2985 strbuf_addf(&pname, " (%s)", file->comments);
2986
2987 file->print_name = strbuf_detach(&pname, NULL);
2988 }
2989
2990 static void print_stat_summary_inserts_deletes(struct diff_options *options,
2991 int files, int insertions, int deletions)
2992 {
2993 struct strbuf sb = STRBUF_INIT;
2994
2995 if (!files) {
2996 assert(insertions == 0 && deletions == 0);
2997 emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_NO_FILES,
2998 NULL, 0, 0);
2999 return;
3000 }
3001
3002 strbuf_addf(&sb,
3003 (files == 1) ? " %d file changed" : " %d files changed",
3004 files);
3005
3006 /*
3007 * For binary diff, the caller may want to print "x files
3008 * changed" with insertions == 0 && deletions == 0.
3009 *
3010 * Not omitting "0 insertions(+), 0 deletions(-)" in this case
3011 * is probably less confusing (i.e skip over "2 files changed
3012 * but nothing about added/removed lines? Is this a bug in Git?").
3013 */
3014 if (insertions || deletions == 0) {
3015 strbuf_addf(&sb,
3016 (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)",
3017 insertions);
3018 }
3019
3020 if (deletions || insertions == 0) {
3021 strbuf_addf(&sb,
3022 (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)",
3023 deletions);
3024 }
3025 strbuf_addch(&sb, '\n');
3026 emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES,
3027 sb.buf, sb.len, 0);
3028 strbuf_release(&sb);
3029 }
3030
3031 void print_stat_summary(FILE *fp, int files,
3032 int insertions, int deletions)
3033 {
3034 struct diff_options o;
3035 memset(&o, 0, sizeof(o));
3036 o.file = fp;
3037
3038 print_stat_summary_inserts_deletes(&o, files, insertions, deletions);
3039 }
3040
3041 /*
3042 * Like utf8_width(), but guaranteed safe for use in loops that subtract
3043 * per-character widths:
3044 *
3045 * - utf8_width() sets *start to NULL on invalid UTF-8 and returns 0;
3046 * we restore the pointer and advance by one byte, returning width 1
3047 * (matching the strlen()-based fallback in utf8_strwidth()).
3048 *
3049 * - utf8_width() returns -1 for control characters; we return 0
3050 * (matching utf8_strnwidth() which skips them).
3051 */
3052 static int utf8_ish_width(const char **start)
3053 {
3054 const char *old = *start;
3055 int w = utf8_width(start, NULL);
3056 if (!*start) {
3057 *start = old + 1;
3058 return 1;
3059 }
3060 return (w < 0) ? 0 : w;
3061 }
3062
3063 static void show_stats(struct diffstat_t *data, struct diff_options *options)
3064 {
3065 int i, len, add, del, adds = 0, dels = 0;
3066 uintmax_t max_change = 0, max_len = 0;
3067 int total_files = data->nr, count;
3068 int width, name_width, graph_width, number_width = 0, bin_width = 0;
3069 const char *reset, *add_c, *del_c;
3070 int extra_shown = 0;
3071 const char *line_prefix = diff_line_prefix(options);
3072 struct strbuf out = STRBUF_INIT;
3073
3074 if (data->nr == 0)
3075 return;
3076
3077 count = options->stat_count ? options->stat_count : data->nr;
3078
3079 reset = diff_get_color_opt(options, DIFF_RESET);
3080 add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
3081 del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
3082
3083 /*
3084 * Find the longest filename and max number of changes
3085 */
3086 for (i = 0; (i < count) && (i < data->nr); i++) {
3087 struct diffstat_file *file = data->files[i];
3088 uintmax_t change = file->added + file->deleted;
3089
3090 if (!file->is_interesting && (change == 0)) {
3091 count++; /* not shown == room for one more */
3092 continue;
3093 }
3094 fill_print_name(file);
3095 len = utf8_strwidth(file->print_name);
3096 if (max_len < len)
3097 max_len = len;
3098
3099 if (file->is_unmerged) {
3100 /* "Unmerged" is 8 characters */
3101 bin_width = bin_width < 8 ? 8 : bin_width;
3102 continue;
3103 }
3104 if (file->is_binary) {
3105 /* "Bin XXX -> YYY bytes" */
3106 int w = 14 + decimal_width(file->added)
3107 + decimal_width(file->deleted);
3108 bin_width = bin_width < w ? w : bin_width;
3109 /* Display change counts aligned with "Bin" */
3110 number_width = 3;
3111 continue;
3112 }
3113
3114 if (max_change < change)
3115 max_change = change;
3116 }
3117 count = i; /* where we can stop scanning in data->files[] */
3118
3119 /*
3120 * We have width = stat_width or term_columns() columns total minus the
3121 * length of line_prefix skipping ANSI escape codes to get the display
3122 * width (e.g., skip ANSI-colored strings in "log --graph --stat").
3123 * We want a maximum of min(max_len, stat_name_width) for the name part.
3124 * We want a maximum of min(max_change, stat_graph_width) for the +- part.
3125 * We also need 1 for " " and 4 + decimal_width(max_change)
3126 * for " | NNNN " and one the empty column at the end, altogether
3127 * 6 + decimal_width(max_change).
3128 *
3129 * If there's not enough space, we will use the smaller of
3130 * stat_name_width (if set) and 5/8*width for the filename,
3131 * and the rest for constant elements + graph part, but no more
3132 * than stat_graph_width for the graph part.
3133 * (5/8 gives 50 for filename and 30 for the constant parts + graph
3134 * for the standard terminal size).
3135 *
3136 * In other words: stat_width limits the maximum width, and
3137 * stat_name_width fixes the maximum width of the filename,
3138 * and is also used to divide available columns if there
3139 * aren't enough.
3140 *
3141 * Binary files are displayed with "Bin XXX -> YYY bytes"
3142 * instead of the change count and graph. This part is treated
3143 * similarly to the graph part, except that it is not
3144 * "scaled". If total width is too small to accommodate the
3145 * guaranteed minimum width of the filename part and the
3146 * separators and this message, this message will "overflow"
3147 * making the line longer than the maximum width.
3148 */
3149 if (options->stat_width == -1)
3150 width = term_columns() - utf8_strnwidth(line_prefix, strlen(line_prefix), 1);
3151 else
3152 width = options->stat_width ? options->stat_width : 80;
3153 number_width = decimal_width(max_change) > number_width ?
3154 decimal_width(max_change) : number_width;
3155
3156 if (options->stat_name_width == -1)
3157 options->stat_name_width = diff_stat_name_width;
3158 if (options->stat_graph_width == -1)
3159 options->stat_graph_width = diff_stat_graph_width;
3160
3161 /*
3162 * Guarantee 3/8*16 == 6 for the graph part
3163 * and 5/8*16 == 10 for the filename part
3164 */
3165 if (width < 16 + 6 + number_width)
3166 width = 16 + 6 + number_width;
3167
3168 /*
3169 * First assign sizes that are wanted, ignoring available width.
3170 * strlen("Bin XXX -> YYY bytes") == bin_width, and the part
3171 * starting from "XXX" should fit in graph_width.
3172 */
3173 graph_width = max_change + 4 > bin_width ? max_change : bin_width - 4;
3174 if (options->stat_graph_width &&
3175 options->stat_graph_width < graph_width)
3176 graph_width = options->stat_graph_width;
3177
3178 name_width = (options->stat_name_width > 0 &&
3179 options->stat_name_width < max_len) ?
3180 options->stat_name_width : max_len;
3181
3182 /*
3183 * Adjust adjustable widths not to exceed maximum width
3184 */
3185 if (name_width + number_width + 6 + graph_width > width) {
3186 if (graph_width > width * 3/8 - number_width - 6) {
3187 graph_width = width * 3/8 - number_width - 6;
3188 if (graph_width < 6)
3189 graph_width = 6;
3190 }
3191
3192 if (options->stat_graph_width &&
3193 graph_width > options->stat_graph_width)
3194 graph_width = options->stat_graph_width;
3195 if (name_width > width - number_width - 6 - graph_width)
3196 name_width = width - number_width - 6 - graph_width;
3197 else
3198 graph_width = width - number_width - 6 - name_width;
3199 }
3200
3201 /*
3202 * From here name_width is the width of the name area,
3203 * and graph_width is the width of the graph area.
3204 * max_change is used to scale graph properly.
3205 */
3206 for (i = 0; i < count; i++) {
3207 const char *prefix = "";
3208 struct diffstat_file *file = data->files[i];
3209 char *name = file->print_name;
3210 uintmax_t added = file->added;
3211 uintmax_t deleted = file->deleted;
3212 int name_len, padding;
3213
3214 if (!file->is_interesting && (added + deleted == 0))
3215 continue;
3216
3217 /*
3218 * "scale" the filename
3219 */
3220 len = name_width;
3221 name_len = utf8_strwidth(name);
3222 if (name_width < name_len) {
3223 char *slash;
3224 prefix = "...";
3225 len -= 3;
3226 if (len < 0)
3227 len = 0;
3228
3229 while (name_len > len && *name)
3230 name_len -= utf8_ish_width((const char**)&name);
3231
3232 slash = strchr(name, '/');
3233 if (slash)
3234 name = slash;
3235 }
3236 padding = len - utf8_strwidth(name);
3237 if (padding < 0)
3238 padding = 0;
3239
3240 if (file->is_binary) {
3241 strbuf_addf(&out, " %s%s%*s | %*s",
3242 prefix, name, padding, "",
3243 number_width, "Bin");
3244 if (!added && !deleted) {
3245 strbuf_addch(&out, '\n');
3246 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
3247 out.buf, out.len, 0);
3248 strbuf_reset(&out);
3249 continue;
3250 }
3251 strbuf_addf(&out, " %s%"PRIuMAX"%s",
3252 del_c, deleted, reset);
3253 strbuf_addstr(&out, " -> ");
3254 strbuf_addf(&out, "%s%"PRIuMAX"%s",
3255 add_c, added, reset);
3256 strbuf_addstr(&out, " bytes\n");
3257 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
3258 out.buf, out.len, 0);
3259 strbuf_reset(&out);
3260 continue;
3261 }
3262 else if (file->is_unmerged) {
3263 strbuf_addf(&out, " %s%s%*s | %*s",
3264 prefix, name, padding, "",
3265 number_width, "Unmerged\n");
3266 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
3267 out.buf, out.len, 0);
3268 strbuf_reset(&out);
3269 continue;
3270 }
3271
3272 /*
3273 * scale the add/delete
3274 */
3275 add = added;
3276 del = deleted;
3277
3278 if (graph_width <= max_change) {
3279 int total = scale_linear(add + del, graph_width, max_change);
3280 if (total < 2 && add && del)
3281 /* width >= 2 due to the sanity check */
3282 total = 2;
3283 if (add < del) {
3284 add = scale_linear(add, graph_width, max_change);
3285 del = total - add;
3286 } else {
3287 del = scale_linear(del, graph_width, max_change);
3288 add = total - del;
3289 }
3290 }
3291 strbuf_addf(&out, " %s%s%*s | %*"PRIuMAX"%s",
3292 prefix, name, padding, "",
3293 number_width, added + deleted,
3294 added + deleted ? " " : "");
3295 show_graph(&out, '+', add, add_c, reset);
3296 show_graph(&out, '-', del, del_c, reset);
3297 strbuf_addch(&out, '\n');
3298 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE,
3299 out.buf, out.len, 0);
3300 strbuf_reset(&out);
3301 }
3302
3303 for (i = 0; i < data->nr; i++) {
3304 struct diffstat_file *file = data->files[i];
3305 uintmax_t added = file->added;
3306 uintmax_t deleted = file->deleted;
3307
3308 if (file->is_unmerged ||
3309 (!file->is_interesting && (added + deleted == 0))) {
3310 total_files--;
3311 continue;
3312 }
3313
3314 if (!file->is_binary) {
3315 adds += added;
3316 dels += deleted;
3317 }
3318 if (i < count)
3319 continue;
3320 if (!extra_shown)
3321 emit_diff_symbol(options,
3322 DIFF_SYMBOL_STATS_SUMMARY_ABBREV,
3323 NULL, 0, 0);
3324 extra_shown = 1;
3325 }
3326
3327 print_stat_summary_inserts_deletes(options, total_files, adds, dels);
3328 strbuf_release(&out);
3329 }
3330
3331 static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
3332 {
3333 int i, adds = 0, dels = 0, total_files = data->nr;
3334
3335 if (data->nr == 0)
3336 return;
3337
3338 for (i = 0; i < data->nr; i++) {
3339 int added = data->files[i]->added;
3340 int deleted = data->files[i]->deleted;
3341
3342 if (data->files[i]->is_unmerged ||
3343 (!data->files[i]->is_interesting && (added + deleted == 0))) {
3344 total_files--;
3345 } else if (!data->files[i]->is_binary) { /* don't count bytes */
3346 adds += added;
3347 dels += deleted;
3348 }
3349 }
3350 print_stat_summary_inserts_deletes(options, total_files, adds, dels);
3351 }
3352
3353 static void show_numstat(struct diffstat_t *data, struct diff_options *options)
3354 {
3355 int i;
3356
3357 if (data->nr == 0)
3358 return;
3359
3360 for (i = 0; i < data->nr; i++) {
3361 struct diffstat_file *file = data->files[i];
3362
3363 fprintf(options->file, "%s", diff_line_prefix(options));
3364
3365 if (file->is_binary)
3366 fprintf(options->file, "-\t-\t");
3367 else
3368 fprintf(options->file,
3369 "%"PRIuMAX"\t%"PRIuMAX"\t",
3370 file->added, file->deleted);
3371 if (options->line_termination) {
3372 fill_print_name(file);
3373 if (!file->is_renamed)
3374 write_name_quoted(file->name, options->file,
3375 options->line_termination);
3376 else {
3377 fputs(file->print_name, options->file);
3378 putc(options->line_termination, options->file);
3379 }
3380 } else {
3381 if (file->is_renamed) {
3382 putc('\0', options->file);
3383 write_name_quoted(file->from_name, options->file, '\0');
3384 }
3385 write_name_quoted(file->name, options->file, '\0');
3386 }
3387 }
3388 }
3389
3390 struct dirstat_file {
3391 const char *name;
3392 unsigned long changed;
3393 };
3394
3395 struct dirstat_dir {
3396 struct dirstat_file *files;
3397 int alloc, nr, permille, cumulative;
3398 };
3399
3400 static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
3401 unsigned long changed, const char *base, int baselen)
3402 {
3403 unsigned long sum_changes = 0;
3404 unsigned int sources = 0;
3405 const char *line_prefix = diff_line_prefix(opt);
3406
3407 while (dir->nr) {
3408 struct dirstat_file *f = dir->files;
3409 int namelen = strlen(f->name);
3410 unsigned long changes;
3411 const char *slash;
3412
3413 if (namelen < baselen)
3414 break;
3415 if (memcmp(f->name, base, baselen))
3416 break;
3417 slash = strchr(f->name + baselen, '/');
3418 if (slash) {
3419 int newbaselen = slash + 1 - f->name;
3420 changes = gather_dirstat(opt, dir, changed, f->name, newbaselen);
3421 sources++;
3422 } else {
3423 changes = f->changed;
3424 dir->files++;
3425 dir->nr--;
3426 sources += 2;
3427 }
3428 sum_changes += changes;
3429 }
3430
3431 /*
3432 * We don't report dirstat's for
3433 * - the top level
3434 * - or cases where everything came from a single directory
3435 * under this directory (sources == 1).
3436 */
3437 if (baselen && sources != 1) {
3438 if (sum_changes) {
3439 int permille = sum_changes * 1000 / changed;
3440 if (permille >= dir->permille) {
3441 fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix,
3442 permille / 10, permille % 10, baselen, base);
3443 if (!dir->cumulative)
3444 return 0;
3445 }
3446 }
3447 }
3448 return sum_changes;
3449 }
3450
3451 static int dirstat_compare(const void *_a, const void *_b)
3452 {
3453 const struct dirstat_file *a = _a;
3454 const struct dirstat_file *b = _b;
3455 return strcmp(a->name, b->name);
3456 }
3457
3458 static void conclude_dirstat(struct diff_options *options,
3459 struct dirstat_dir *dir,
3460 unsigned long changed)
3461 {
3462 struct dirstat_file *to_free = dir->files;
3463
3464 if (!changed) {
3465 /* This can happen even with many files, if everything was renames */
3466 ;
3467 } else {
3468 /* Show all directories with more than x% of the changes */
3469 QSORT(dir->files, dir->nr, dirstat_compare);
3470 gather_dirstat(options, dir, changed, "", 0);
3471 }
3472
3473 free(to_free);
3474 }
3475
3476 static void show_dirstat(struct diff_options *options)
3477 {
3478 int i;
3479 unsigned long changed;
3480 struct dirstat_dir dir;
3481 struct diff_queue_struct *q = &diff_queued_diff;
3482
3483 dir.files = NULL;
3484 dir.alloc = 0;
3485 dir.nr = 0;
3486 dir.permille = options->dirstat_permille;
3487 dir.cumulative = options->flags.dirstat_cumulative;
3488
3489 changed = 0;
3490 for (i = 0; i < q->nr; i++) {
3491 struct diff_filepair *p = q->queue[i];
3492 const char *name;
3493 unsigned long copied, added, damage;
3494 struct diff_populate_filespec_options dpf_options = {
3495 .check_size_only = 1,
3496 };
3497
3498 name = p->two->path ? p->two->path : p->one->path;
3499
3500 if (p->one->oid_valid && p->two->oid_valid &&
3501 oideq(&p->one->oid, &p->two->oid)) {
3502 /*
3503 * The SHA1 has not changed, so pre-/post-content is
3504 * identical. We can therefore skip looking at the
3505 * file contents altogether.
3506 */
3507 damage = 0;
3508 goto found_damage;
3509 }
3510
3511 if (options->flags.dirstat_by_file) {
3512 /*
3513 * In --dirstat-by-file mode, we don't really need to
3514 * look at the actual file contents at all.
3515 * The fact that the SHA1 changed is enough for us to
3516 * add this file to the list of results
3517 * (with each file contributing equal damage).
3518 */
3519 damage = 1;
3520 goto found_damage;
3521 }
3522
3523 if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
3524 diff_populate_filespec(options->repo, p->one, NULL);
3525 diff_populate_filespec(options->repo, p->two, NULL);
3526 diffcore_count_changes(options->repo,
3527 p->one, p->two, NULL, NULL,
3528 &copied, &added);
3529 diff_free_filespec_data(p->one);
3530 diff_free_filespec_data(p->two);
3531 } else if (DIFF_FILE_VALID(p->one)) {
3532 diff_populate_filespec(options->repo, p->one, &dpf_options);
3533 copied = added = 0;
3534 diff_free_filespec_data(p->one);
3535 } else if (DIFF_FILE_VALID(p->two)) {
3536 diff_populate_filespec(options->repo, p->two, &dpf_options);
3537 copied = 0;
3538 added = p->two->size;
3539 diff_free_filespec_data(p->two);
3540 } else
3541 continue;
3542
3543 /*
3544 * Original minus copied is the removed material,
3545 * added is the new material. They are both damages
3546 * made to the preimage.
3547 * If the resulting damage is zero, we know that
3548 * diffcore_count_changes() considers the two entries to
3549 * be identical, but since the oid changed, we
3550 * know that there must have been _some_ kind of change,
3551 * so we force all entries to have damage > 0.
3552 */
3553 damage = (p->one->size - copied) + added;
3554 if (!damage)
3555 damage = 1;
3556
3557 found_damage:
3558 ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
3559 dir.files[dir.nr].name = name;
3560 dir.files[dir.nr].changed = damage;
3561 changed += damage;
3562 dir.nr++;
3563 }
3564
3565 conclude_dirstat(options, &dir, changed);
3566 }
3567
3568 static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)
3569 {
3570 int i;
3571 unsigned long changed;
3572 struct dirstat_dir dir;
3573
3574 if (data->nr == 0)
3575 return;
3576
3577 dir.files = NULL;
3578 dir.alloc = 0;
3579 dir.nr = 0;
3580 dir.permille = options->dirstat_permille;
3581 dir.cumulative = options->flags.dirstat_cumulative;
3582
3583 changed = 0;
3584 for (i = 0; i < data->nr; i++) {
3585 struct diffstat_file *file = data->files[i];
3586 unsigned long damage = file->added + file->deleted;
3587 if (file->is_binary)
3588 /*
3589 * binary files counts bytes, not lines. Must find some
3590 * way to normalize binary bytes vs. textual lines.
3591 * The following heuristic assumes that there are 64
3592 * bytes per "line".
3593 * This is stupid and ugly, but very cheap...
3594 */
3595 damage = DIV_ROUND_UP(damage, 64);
3596 ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
3597 dir.files[dir.nr].name = file->name;
3598 dir.files[dir.nr].changed = damage;
3599 changed += damage;
3600 dir.nr++;
3601 }
3602
3603 conclude_dirstat(options, &dir, changed);
3604 }
3605
3606 static void free_diffstat_file(struct diffstat_file *f)
3607 {
3608 free(f->print_name);
3609 free(f->name);
3610 free(f->from_name);
3611 free(f);
3612 }
3613
3614 void free_diffstat_info(struct diffstat_t *diffstat)
3615 {
3616 int i;
3617 for (i = 0; i < diffstat->nr; i++)
3618 free_diffstat_file(diffstat->files[i]);
3619 free(diffstat->files);
3620 }
3621
3622 struct checkdiff_t {
3623 const char *filename;
3624 int lineno;
3625 int conflict_marker_size;
3626 struct diff_options *o;
3627 unsigned ws_rule;
3628 unsigned status;
3629 int last_line_kind;
3630 };
3631
3632 static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
3633 {
3634 char firstchar;
3635 int cnt;
3636
3637 if (len < marker_size + 1)
3638 return 0;
3639 firstchar = line[0];
3640 switch (firstchar) {
3641 case '=': case '>': case '<': case '|':
3642 break;
3643 default:
3644 return 0;
3645 }
3646 for (cnt = 1; cnt < marker_size; cnt++)
3647 if (line[cnt] != firstchar)
3648 return 0;
3649 /* line[1] through line[marker_size-1] are same as firstchar */
3650 if (len < marker_size + 1 || !isspace(line[marker_size]))
3651 return 0;
3652 return 1;
3653 }
3654
3655 static void checkdiff_consume_hunk(void *priv,
3656 long ob UNUSED, long on UNUSED,
3657 long nb, long nn UNUSED,
3658 const char *func UNUSED, long funclen UNUSED)
3659
3660 {
3661 struct checkdiff_t *data = priv;
3662 data->lineno = nb - 1;
3663 }
3664
3665 static int checkdiff_consume(void *priv, char *line, unsigned long len)
3666 {
3667 struct checkdiff_t *data = priv;
3668 int last_line_kind;
3669 int marker_size = data->conflict_marker_size;
3670 const char *ws = diff_get_color(data->o->use_color, DIFF_WHITESPACE);
3671 const char *reset = diff_get_color(data->o->use_color, DIFF_RESET);
3672 const char *set = diff_get_color(data->o->use_color, DIFF_FILE_NEW);
3673 char *err;
3674 const char *line_prefix;
3675
3676 assert(data->o);
3677 line_prefix = diff_line_prefix(data->o);
3678
3679 last_line_kind = data->last_line_kind;
3680 data->last_line_kind = line[0];
3681 if (line[0] == '+') {
3682 unsigned bad;
3683 data->lineno++;
3684 if (is_conflict_marker(line + 1, marker_size, len - 1)) {
3685 data->status |= 1;
3686 fprintf(data->o->file,
3687 "%s%s:%d: leftover conflict marker\n",
3688 line_prefix, data->filename, data->lineno);
3689 }
3690 bad = ws_check(line + 1, len - 1, data->ws_rule);
3691 if (!bad)
3692 return 0;
3693 data->status |= bad;
3694 err = whitespace_error_string(bad);
3695 fprintf(data->o->file, "%s%s:%d: %s.\n",
3696 line_prefix, data->filename, data->lineno, err);
3697 free(err);
3698 emit_line(data->o, set, reset, line, 1);
3699 ws_check_emit(line + 1, len - 1, data->ws_rule,
3700 data->o->file, set, reset, ws);
3701 } else if (line[0] == ' ') {
3702 data->lineno++;
3703 } else if (line[0] == '\\') {
3704 /* no newline at the end of the line */
3705 if ((data->ws_rule & WS_INCOMPLETE_LINE) &&
3706 (last_line_kind == '+')) {
3707 unsigned bad = WS_INCOMPLETE_LINE;
3708 data->status |= bad;
3709 err = whitespace_error_string(bad);
3710 fprintf(data->o->file, "%s%s:%d: %s.\n",
3711 line_prefix, data->filename, data->lineno, err);
3712 free(err);
3713 }
3714 }
3715 return 0;
3716 }
3717
3718 static unsigned char *deflate_it(char *data,
3719 unsigned long size,
3720 unsigned long *result_size)
3721 {
3722 size_t bound;
3723 unsigned char *deflated;
3724 git_zstream stream;
3725 struct repo_config_values *cfg = repo_config_values(the_repository);
3726
3727 git_deflate_init(&stream, cfg->zlib_compression_level);
3728 bound = git_deflate_bound(&stream, size);
3729 deflated = xmalloc(bound);
3730 stream.next_out = deflated;
3731 stream.avail_out = bound;
3732
3733 stream.next_in = (unsigned char *)data;
3734 stream.avail_in = size;
3735 while (git_deflate(&stream, Z_FINISH) == Z_OK)
3736 ; /* nothing */
3737 git_deflate_end(&stream);
3738 *result_size = stream.total_out;
3739 return deflated;
3740 }
3741
3742 static void emit_binary_diff_body(struct diff_options *o,
3743 mmfile_t *one, mmfile_t *two)
3744 {
3745 void *cp;
3746 void *delta;
3747 void *deflated;
3748 void *data;
3749 unsigned long orig_size;
3750 unsigned long delta_size;
3751 unsigned long deflate_size;
3752 unsigned long data_size;
3753
3754 /* We could do deflated delta, or we could do just deflated two,
3755 * whichever is smaller.
3756 */
3757 delta = NULL;
3758 deflated = deflate_it(two->ptr, two->size, &deflate_size);
3759 if (one->size && two->size) {
3760 size_t delta_size_st = 0;
3761 delta = diff_delta(one->ptr, one->size,
3762 two->ptr, two->size,
3763 &delta_size_st, deflate_size);
3764 delta_size = cast_size_t_to_ulong(delta_size_st);
3765 if (delta) {
3766 void *to_free = delta;
3767 orig_size = delta_size;
3768 delta = deflate_it(delta, delta_size, &delta_size);
3769 free(to_free);
3770 }
3771 }
3772
3773 if (delta && delta_size < deflate_size) {
3774 char *s = xstrfmt("%"PRIuMAX , (uintmax_t)orig_size);
3775 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA,
3776 s, strlen(s), 0);
3777 free(s);
3778 free(deflated);
3779 data = delta;
3780 data_size = delta_size;
3781 } else {
3782 char *s = xstrfmt("%lu", two->size);
3783 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL,
3784 s, strlen(s), 0);
3785 free(s);
3786 free(delta);
3787 data = deflated;
3788 data_size = deflate_size;
3789 }
3790
3791 /* emit data encoded in base85 */
3792 cp = data;
3793 while (data_size) {
3794 int len;
3795 int bytes = (52 < data_size) ? 52 : data_size;
3796 char line[71];
3797 data_size -= bytes;
3798 if (bytes <= 26)
3799 line[0] = bytes + 'A' - 1;
3800 else
3801 line[0] = bytes - 26 + 'a' - 1;
3802 encode_85(line + 1, cp, bytes);
3803 cp = (char *) cp + bytes;
3804
3805 len = strlen(line);
3806 line[len++] = '\n';
3807 line[len] = '\0';
3808
3809 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_BODY,
3810 line, len, 0);
3811 }
3812 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_FOOTER, NULL, 0, 0);
3813 free(data);
3814 }
3815
3816 static void emit_binary_diff(struct diff_options *o,
3817 mmfile_t *one, mmfile_t *two)
3818 {
3819 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER, NULL, 0, 0);
3820 emit_binary_diff_body(o, one, two);
3821 emit_binary_diff_body(o, two, one);
3822 }
3823
3824 int diff_filespec_is_binary(struct repository *r,
3825 struct diff_filespec *one)
3826 {
3827 struct diff_populate_filespec_options dpf_options = {
3828 .check_binary = 1,
3829 };
3830
3831 if (one->is_binary == -1) {
3832 diff_filespec_load_driver(one, r->index);
3833 if (one->driver->binary != -1)
3834 one->is_binary = one->driver->binary;
3835 else {
3836 if (!one->data && DIFF_FILE_VALID(one))
3837 diff_populate_filespec(r, one, &dpf_options);
3838 if (one->is_binary == -1 && one->data)
3839 one->is_binary = buffer_is_binary(one->data,
3840 one->size);
3841 if (one->is_binary == -1)
3842 one->is_binary = 0;
3843 }
3844 }
3845 return one->is_binary;
3846 }
3847
3848 static const struct userdiff_funcname *
3849 diff_funcname_pattern(struct diff_options *o, struct diff_filespec *one)
3850 {
3851 diff_filespec_load_driver(one, o->repo->index);
3852 return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
3853 }
3854
3855 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
3856 {
3857 if (!options->a_prefix)
3858 options->a_prefix = a;
3859 if (!options->b_prefix)
3860 options->b_prefix = b;
3861 }
3862
3863 void diff_set_noprefix(struct diff_options *options)
3864 {
3865 options->a_prefix = options->b_prefix = "";
3866 }
3867
3868 void diff_set_default_prefix(struct diff_options *options)
3869 {
3870 options->a_prefix = diff_src_prefix ? diff_src_prefix : "a/";
3871 options->b_prefix = diff_dst_prefix ? diff_dst_prefix : "b/";
3872 }
3873
3874 struct userdiff_driver *get_textconv(struct repository *r,
3875 struct diff_filespec *one)
3876 {
3877 if (!DIFF_FILE_VALID(one))
3878 return NULL;
3879
3880 diff_filespec_load_driver(one, r->index);
3881 return userdiff_get_textconv(r, one->driver);
3882 }
3883
3884 static struct string_list *additional_headers(struct diff_options *o,
3885 const char *path)
3886 {
3887 if (!o->additional_path_headers)
3888 return NULL;
3889 return strmap_get(o->additional_path_headers, path);
3890 }
3891
3892 static void add_formatted_header(struct strbuf *msg,
3893 const char *header,
3894 const char *line_prefix,
3895 const char *meta,
3896 const char *reset)
3897 {
3898 const char *next, *newline;
3899
3900 for (next = header; *next; next = newline) {
3901 newline = strchrnul(next, '\n');
3902 strbuf_addf(msg, "%s%s%.*s%s\n", line_prefix, meta,
3903 (int)(newline - next), next, reset);
3904 if (*newline)
3905 newline++;
3906 }
3907 }
3908
3909 static void add_formatted_headers(struct strbuf *msg,
3910 struct string_list *more_headers,
3911 const char *line_prefix,
3912 const char *meta,
3913 const char *reset)
3914 {
3915 int i;
3916
3917 for (i = 0; i < more_headers->nr; i++)
3918 add_formatted_header(msg, more_headers->items[i].string,
3919 line_prefix, meta, reset);
3920 }
3921
3922 static int diff_filepair_is_phoney(struct diff_filespec *one,
3923 struct diff_filespec *two)
3924 {
3925 /*
3926 * This function specifically looks for pairs injected by
3927 * create_filepairs_for_header_only_notifications(). Such
3928 * pairs are "phoney" in that they do not represent any
3929 * content or even mode difference, but were inserted because
3930 * diff_queued_diff previously had no pair associated with
3931 * that path but we needed some pair to avoid losing the
3932 * "remerge CONFLICT" header associated with the path.
3933 */
3934 return !DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two);
3935 }
3936
3937 static int set_diff_algorithm(struct diff_options *opts,
3938 const char *alg)
3939 {
3940 long value = parse_algorithm_value(alg);
3941
3942 if (value < 0)
3943 return -1;
3944
3945 /* clear out previous settings */
3946 opts->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
3947 opts->xdl_opts |= value;
3948
3949 return 0;
3950 }
3951
3952 static void builtin_diff(const char *name_a,
3953 const char *name_b,
3954 struct diff_filespec *one,
3955 struct diff_filespec *two,
3956 const char *xfrm_msg,
3957 int must_show_header,
3958 struct diff_options *o,
3959 int complete_rewrite,
3960 const struct range_set *line_ranges)
3961 {
3962 mmfile_t mf1, mf2;
3963 const char *lbl[2];
3964 char *a_one, *b_two;
3965 const char *meta = diff_get_color_opt(o, DIFF_METAINFO);
3966 const char *reset = diff_get_color_opt(o, DIFF_RESET);
3967 const char *a_prefix, *b_prefix;
3968 struct userdiff_driver *textconv_one = NULL;
3969 struct userdiff_driver *textconv_two = NULL;
3970 struct strbuf header = STRBUF_INIT;
3971 const char *line_prefix = diff_line_prefix(o);
3972
3973 diff_set_mnemonic_prefix(o, "a/", "b/");
3974 if (o->flags.reverse_diff) {
3975 a_prefix = o->b_prefix;
3976 b_prefix = o->a_prefix;
3977 } else {
3978 a_prefix = o->a_prefix;
3979 b_prefix = o->b_prefix;
3980 }
3981
3982 if (o->submodule_format == DIFF_SUBMODULE_LOG &&
3983 (!one->mode || S_ISGITLINK(one->mode)) &&
3984 (!two->mode || S_ISGITLINK(two->mode)) &&
3985 (!diff_filepair_is_phoney(one, two))) {
3986 show_submodule_diff_summary(o, one->path ? one->path : two->path,
3987 &one->oid, &two->oid,
3988 two->dirty_submodule);
3989 o->found_changes = 1;
3990 return;
3991 } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
3992 (!one->mode || S_ISGITLINK(one->mode)) &&
3993 (!two->mode || S_ISGITLINK(two->mode)) &&
3994 (!diff_filepair_is_phoney(one, two))) {
3995 show_submodule_inline_diff(o, one->path ? one->path : two->path,
3996 &one->oid, &two->oid,
3997 two->dirty_submodule);
3998 o->found_changes = 1;
3999 return;
4000 }
4001
4002 if (o->flags.allow_textconv) {
4003 textconv_one = get_textconv(o->repo, one);
4004 textconv_two = get_textconv(o->repo, two);
4005 }
4006
4007 /* Never use a non-valid filename anywhere if at all possible */
4008 name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
4009 name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
4010
4011 a_one = quote_two(a_prefix, name_a + (*name_a == '/'));
4012 b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
4013 lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
4014 lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
4015 if (diff_filepair_is_phoney(one, two)) {
4016 /*
4017 * We should only reach this point for pairs generated from
4018 * create_filepairs_for_header_only_notifications(). For
4019 * these, we want to avoid the "/dev/null" special casing
4020 * above, because we do not want such pairs shown as either
4021 * "new file" or "deleted file" below.
4022 */
4023 lbl[0] = a_one;
4024 lbl[1] = b_two;
4025 }
4026 strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset);
4027 if (lbl[0][0] == '/') {
4028 /* /dev/null */
4029 strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset);
4030 if (xfrm_msg)
4031 strbuf_addstr(&header, xfrm_msg);
4032 o->found_changes = 1;
4033 must_show_header = 1;
4034 }
4035 else if (lbl[1][0] == '/') {
4036 strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset);
4037 if (xfrm_msg)
4038 strbuf_addstr(&header, xfrm_msg);
4039 o->found_changes = 1;
4040 must_show_header = 1;
4041 }
4042 else {
4043 if (one->mode != two->mode) {
4044 strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset);
4045 strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset);
4046 o->found_changes = 1;
4047 must_show_header = 1;
4048 }
4049 if (xfrm_msg)
4050 strbuf_addstr(&header, xfrm_msg);
4051
4052 /*
4053 * we do not run diff between different kind
4054 * of objects.
4055 */
4056 if ((one->mode ^ two->mode) & S_IFMT)
4057 goto free_ab_and_return;
4058 if (complete_rewrite &&
4059 (textconv_one || !diff_filespec_is_binary(o->repo, one)) &&
4060 (textconv_two || !diff_filespec_is_binary(o->repo, two))) {
4061 emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
4062 header.buf, header.len, 0);
4063 strbuf_reset(&header);
4064 emit_rewrite_diff(name_a, name_b, one, two,
4065 textconv_one, textconv_two, o);
4066 o->found_changes = 1;
4067 goto free_ab_and_return;
4068 }
4069 }
4070
4071 if (o->irreversible_delete && lbl[1][0] == '/') {
4072 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf,
4073 header.len, 0);
4074 strbuf_reset(&header);
4075 goto free_ab_and_return;
4076 } else if (!o->flags.text &&
4077 ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) ||
4078 (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) {
4079 struct strbuf sb = STRBUF_INIT;
4080 if (!one->data && !two->data &&
4081 S_ISREG(one->mode) && S_ISREG(two->mode) &&
4082 !o->flags.binary) {
4083 if (oideq(&one->oid, &two->oid)) {
4084 if (must_show_header)
4085 emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
4086 header.buf, header.len,
4087 0);
4088 goto free_ab_and_return;
4089 }
4090 emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
4091 header.buf, header.len, 0);
4092 strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
4093 diff_line_prefix(o), lbl[0], lbl[1]);
4094 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
4095 sb.buf, sb.len, 0);
4096 strbuf_release(&sb);
4097 o->found_changes = 1;
4098 goto free_ab_and_return;
4099 }
4100 if (fill_mmfile(o->repo, &mf1, one) < 0 ||
4101 fill_mmfile(o->repo, &mf2, two) < 0)
4102 die("unable to read files to diff");
4103 /* Quite common confusing case */
4104 if (mf1.size == mf2.size &&
4105 !memcmp(mf1.ptr, mf2.ptr, mf1.size)) {
4106 if (must_show_header)
4107 emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
4108 header.buf, header.len, 0);
4109 goto free_ab_and_return;
4110 }
4111 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
4112 strbuf_reset(&header);
4113 if (o->flags.binary)
4114 emit_binary_diff(o, &mf1, &mf2);
4115 else {
4116 strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
4117 diff_line_prefix(o), lbl[0], lbl[1]);
4118 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES,
4119 sb.buf, sb.len, 0);
4120 strbuf_release(&sb);
4121 }
4122 o->found_changes = 1;
4123 } else {
4124 /* Crazy xdl interfaces.. */
4125 const char *diffopts;
4126 const char *v;
4127 xpparam_t xpp;
4128 xdemitconf_t xecfg;
4129 struct emit_callback ecbdata;
4130 unsigned ws_rule;
4131 const struct userdiff_funcname *pe;
4132
4133 if (must_show_header) {
4134 emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
4135 header.buf, header.len, 0);
4136 strbuf_reset(&header);
4137 }
4138
4139 mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr);
4140 mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr);
4141
4142 ws_rule = whitespace_rule(o->repo->index, name_b);
4143
4144 /* symlink being an incomplete line is not a news */
4145 if (DIFF_FILE_VALID(two) && S_ISLNK(two->mode))
4146 ws_rule &= ~WS_INCOMPLETE_LINE;
4147
4148 pe = diff_funcname_pattern(o, one);
4149 if (!pe)
4150 pe = diff_funcname_pattern(o, two);
4151
4152 memset(&xpp, 0, sizeof(xpp));
4153 memset(&xecfg, 0, sizeof(xecfg));
4154 memset(&ecbdata, 0, sizeof(ecbdata));
4155 if (o->flags.suppress_diff_headers)
4156 lbl[0] = NULL;
4157 ecbdata.label_path = lbl;
4158 ecbdata.color_diff = o->use_color;
4159 ecbdata.ws_rule = ws_rule;
4160 if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
4161 check_blank_at_eof(&mf1, &mf2, &ecbdata);
4162 ecbdata.opt = o;
4163 if (header.len && !o->flags.suppress_diff_headers)
4164 ecbdata.header = &header;
4165 xpp.flags = o->xdl_opts;
4166 xpp.ignore_regex = o->ignore_regex;
4167 xpp.ignore_regex_nr = o->ignore_regex_nr;
4168 xpp.anchors = o->anchors;
4169 xpp.anchors_nr = o->anchors_nr;
4170 xecfg.ctxlen = o->context;
4171 xecfg.interhunkctxlen = o->interhunkcontext;
4172 xecfg.flags = XDL_EMIT_FUNCNAMES;
4173 if (o->flags.funccontext)
4174 xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
4175 if (pe)
4176 xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
4177
4178 diffopts = getenv("GIT_DIFF_OPTS");
4179 if (!diffopts)
4180 ;
4181 else if (skip_prefix(diffopts, "--unified=", &v))
4182 xecfg.ctxlen = strtoul(v, NULL, 10);
4183 else if (skip_prefix(diffopts, "-u", &v))
4184 xecfg.ctxlen = strtoul(v, NULL, 10);
4185
4186 if (o->word_diff)
4187 init_diff_words_data(&ecbdata, o, one, two);
4188 if (!o->file) {
4189 /*
4190 * Unlike the normal output case, we need to ignore the
4191 * return value from xdi_diff_outf() here, because
4192 * xdi_diff_outf() takes non-zero return from its
4193 * callback function as a sign of error and returns
4194 * early (which is why we return non-zero from our
4195 * callback, quick_consume()). Unfortunately,
4196 * xdi_diff_outf() signals an error by returning
4197 * non-zero.
4198 */
4199 xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
4200 &ecbdata, &xpp, &xecfg);
4201 } else if (line_ranges) {
4202 struct line_range_filter lr_filter;
4203
4204 line_range_filter_init(&lr_filter, line_ranges,
4205 fn_out_consume, &ecbdata);
4206
4207 if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
4208 &xpp, &xecfg))
4209 die("unable to generate diff for %s",
4210 one->path);
4211 } else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
4212 &ecbdata, &xpp, &xecfg))
4213 die("unable to generate diff for %s", one->path);
4214 if (o->word_diff)
4215 free_diff_words_data(&ecbdata);
4216 if (textconv_one)
4217 free(mf1.ptr);
4218 if (textconv_two)
4219 free(mf2.ptr);
4220 xdiff_clear_find_func(&xecfg);
4221 }
4222
4223 free_ab_and_return:
4224 strbuf_release(&header);
4225 diff_free_filespec_data(one);
4226 diff_free_filespec_data(two);
4227 free(a_one);
4228 free(b_two);
4229 return;
4230 }
4231
4232 static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
4233 {
4234 if (!is_renamed) {
4235 if (p->status == DIFF_STATUS_ADDED) {
4236 if (S_ISLNK(p->two->mode))
4237 return "new +l";
4238 else if ((p->two->mode & 0777) == 0755)
4239 return "new +x";
4240 else
4241 return "new";
4242 } else if (p->status == DIFF_STATUS_DELETED)
4243 return "gone";
4244 }
4245 if (S_ISLNK(p->one->mode) && !S_ISLNK(p->two->mode))
4246 return "mode -l";
4247 else if (!S_ISLNK(p->one->mode) && S_ISLNK(p->two->mode))
4248 return "mode +l";
4249 else if ((p->one->mode & 0777) == 0644 &&
4250 (p->two->mode & 0777) == 0755)
4251 return "mode +x";
4252 else if ((p->one->mode & 0777) == 0755 &&
4253 (p->two->mode & 0777) == 0644)
4254 return "mode -x";
4255 return NULL;
4256 }
4257
4258 static void builtin_diffstat(const char *name_a, const char *name_b,
4259 struct diff_filespec *one,
4260 struct diff_filespec *two,
4261 struct diffstat_t *diffstat,
4262 struct diff_options *o,
4263 struct diff_filepair *p)
4264 {
4265 mmfile_t mf1, mf2;
4266 struct diffstat_file *data;
4267 int may_differ;
4268 int complete_rewrite = 0;
4269
4270 if (!DIFF_PAIR_UNMERGED(p)) {
4271 if (p->status == DIFF_STATUS_MODIFIED && p->score)
4272 complete_rewrite = 1;
4273 }
4274
4275 data = diffstat_add(diffstat, name_a, name_b);
4276 data->is_interesting = p->status != DIFF_STATUS_UNKNOWN;
4277 if (o->flags.stat_with_summary)
4278 data->comments = get_compact_summary(p, data->is_renamed);
4279
4280 if (!one || !two) {
4281 data->is_unmerged = 1;
4282 return;
4283 }
4284
4285 /* saves some reads if true, not a guarantee of diff outcome */
4286 may_differ = !(one->oid_valid && two->oid_valid &&
4287 oideq(&one->oid, &two->oid));
4288
4289 if (diff_filespec_is_binary(o->repo, one) ||
4290 diff_filespec_is_binary(o->repo, two)) {
4291 data->is_binary = 1;
4292 if (!may_differ) {
4293 data->added = 0;
4294 data->deleted = 0;
4295 } else {
4296 data->added = diff_filespec_size(o->repo, two);
4297 data->deleted = diff_filespec_size(o->repo, one);
4298 }
4299 }
4300
4301 else if (complete_rewrite) {
4302 diff_populate_filespec(o->repo, one, NULL);
4303 diff_populate_filespec(o->repo, two, NULL);
4304 data->deleted = count_lines(one->data, one->size);
4305 data->added = count_lines(two->data, two->size);
4306 }
4307
4308 else if (may_differ) {
4309 /* Crazy xdl interfaces.. */
4310 xpparam_t xpp;
4311 xdemitconf_t xecfg;
4312
4313 if (fill_mmfile(o->repo, &mf1, one) < 0 ||
4314 fill_mmfile(o->repo, &mf2, two) < 0)
4315 die("unable to read files to diff");
4316
4317 memset(&xpp, 0, sizeof(xpp));
4318 memset(&xecfg, 0, sizeof(xecfg));
4319 xpp.flags = o->xdl_opts;
4320 xpp.ignore_regex = o->ignore_regex;
4321 xpp.ignore_regex_nr = o->ignore_regex_nr;
4322 xpp.anchors = o->anchors;
4323 xpp.anchors_nr = o->anchors_nr;
4324 xecfg.ctxlen = o->context;
4325 xecfg.interhunkctxlen = o->interhunkcontext;
4326 xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
4327
4328 if (p->line_ranges) {
4329 struct line_range_filter lr_filter;
4330
4331 line_range_filter_init(&lr_filter, p->line_ranges,
4332 diffstat_consume, diffstat);
4333
4334 if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
4335 &xpp, &xecfg))
4336 die("unable to generate diffstat for %s",
4337 one->path);
4338 } else if (xdi_diff_outf(&mf1, &mf2, NULL,
4339 diffstat_consume, diffstat, &xpp, &xecfg))
4340 die("unable to generate diffstat for %s", one->path);
4341
4342 if (DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two)) {
4343 struct diffstat_file *file =
4344 diffstat->files[diffstat->nr - 1];
4345 /*
4346 * Omit diffstats of modified files where nothing changed.
4347 * Even if may_differ, this might be the case due to
4348 * ignoring whitespace changes, etc.
4349 *
4350 * But note that we special-case additions, deletions,
4351 * renames, and mode changes as adding an empty file,
4352 * for example is still of interest.
4353 */
4354 if ((p->status == DIFF_STATUS_MODIFIED)
4355 && !file->added
4356 && !file->deleted
4357 && one->mode == two->mode) {
4358 free_diffstat_file(file);
4359 diffstat->nr--;
4360 }
4361 }
4362 }
4363
4364 diff_free_filespec_data(one);
4365 diff_free_filespec_data(two);
4366 }
4367
4368 /*
4369 * Is the 0-based line index within any of the tracked ranges?
4370 * (range_set ranges are 0-based, half-open [start, end).) This is a
4371 * one-shot query for a single line and scans; the streaming filter
4372 * (line_range_line_fn) uses a forward cursor instead.
4373 */
4374 static int idx_in_ranges(const struct range_set *ranges, long idx)
4375 {
4376 unsigned int i;
4377
4378 for (i = 0; i < ranges->nr; i++)
4379 if (idx >= ranges->ranges[i].start &&
4380 idx < ranges->ranges[i].end)
4381 return 1;
4382 return 0;
4383 }
4384
4385 static void builtin_checkdiff(const char *name_a, const char *name_b,
4386 const char *attr_path,
4387 struct diff_filespec *one,
4388 struct diff_filespec *two,
4389 struct diff_options *o,
4390 const struct range_set *line_ranges)
4391 {
4392 mmfile_t mf1, mf2;
4393 struct checkdiff_t data;
4394
4395 if (!two)
4396 return;
4397
4398 memset(&data, 0, sizeof(data));
4399 data.filename = name_b ? name_b : name_a;
4400 data.lineno = 0;
4401 data.o = o;
4402 data.ws_rule = whitespace_rule(o->repo->index, attr_path);
4403 data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path);
4404
4405 /* symlink being an incomplete line is not a news */
4406 if (DIFF_FILE_VALID(two) && S_ISLNK(two->mode))
4407 data.ws_rule &= ~WS_INCOMPLETE_LINE;
4408
4409 if (fill_mmfile(o->repo, &mf1, one) < 0 ||
4410 fill_mmfile(o->repo, &mf2, two) < 0)
4411 die("unable to read files to diff");
4412
4413 /*
4414 * All the other codepaths check both sides, but not checking
4415 * the "old" side here is deliberate. We are checking the newly
4416 * introduced changes, and as long as the "new" side is text, we
4417 * can and should check what it introduces.
4418 */
4419 if (diff_filespec_is_binary(o->repo, two))
4420 goto free_and_return;
4421 else {
4422 /* Crazy xdl interfaces.. */
4423 xpparam_t xpp;
4424 xdemitconf_t xecfg;
4425
4426 memset(&xpp, 0, sizeof(xpp));
4427 memset(&xecfg, 0, sizeof(xecfg));
4428 xecfg.ctxlen = 1; /* at least one context line */
4429 xpp.flags = 0;
4430
4431 if (line_ranges) {
4432 struct line_range_filter lr_filter;
4433
4434 line_range_filter_init(&lr_filter, line_ranges,
4435 checkdiff_consume, &data);
4436 lr_filter.orig_hunk_fn = checkdiff_consume_hunk;
4437
4438 if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
4439 &xpp, &xecfg))
4440 die("unable to generate checkdiff for %s",
4441 one->path);
4442 } else if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume_hunk,
4443 checkdiff_consume, &data,
4444 &xpp, &xecfg))
4445 die("unable to generate checkdiff for %s", one->path);
4446
4447 if (data.ws_rule & WS_BLANK_AT_EOF) {
4448 struct emit_callback ecbdata;
4449 int blank_at_eof;
4450
4451 ecbdata.ws_rule = data.ws_rule;
4452 check_blank_at_eof(&mf1, &mf2, &ecbdata);
4453 blank_at_eof = ecbdata.blank_at_eof_in_postimage;
4454
4455 /*
4456 * check_blank_at_eof() scans the whole file; with -L,
4457 * keep the report only when its line is in a tracked
4458 * range. The error's location is the first trailing
4459 * blank line (blank_at_eof, 1-based; ranges 0-based), so
4460 * we scope by that line.
4461 */
4462 if (blank_at_eof && line_ranges &&
4463 !idx_in_ranges(line_ranges, blank_at_eof - 1))
4464 blank_at_eof = 0;
4465
4466 if (blank_at_eof) {
4467 static char *err;
4468 if (!err)
4469 err = whitespace_error_string(WS_BLANK_AT_EOF);
4470 fprintf(o->file, "%s:%d: %s.\n",
4471 data.filename, blank_at_eof, err);
4472 data.status = 1; /* report errors */
4473 }
4474 }
4475 }
4476 free_and_return:
4477 diff_free_filespec_data(one);
4478 diff_free_filespec_data(two);
4479 if (data.status)
4480 o->flags.check_failed = 1;
4481 }
4482
4483 struct diff_filespec *alloc_filespec(const char *path)
4484 {
4485 struct diff_filespec *spec;
4486
4487 FLEXPTR_ALLOC_STR(spec, path, path);
4488 spec->count = 1;
4489 spec->is_binary = -1;
4490 return spec;
4491 }
4492
4493 void free_filespec(struct diff_filespec *spec)
4494 {
4495 if (!--spec->count) {
4496 diff_free_filespec_data(spec);
4497 free(spec);
4498 }
4499 }
4500
4501 void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,
4502 int oid_valid, unsigned short mode)
4503 {
4504 if (mode) {
4505 spec->mode = canon_mode(mode);
4506 oidcpy(&spec->oid, oid);
4507 spec->oid_valid = oid_valid;
4508 }
4509 }
4510
4511 /*
4512 * Given a name and sha1 pair, if the index tells us the file in
4513 * the work tree has that object contents, return true, so that
4514 * prepare_temp_file() does not have to inflate and extract.
4515 */
4516 static int reuse_worktree_file(struct index_state *istate,
4517 const char *name,
4518 const struct object_id *oid,
4519 int want_file)
4520 {
4521 const struct cache_entry *ce;
4522 struct stat st;
4523 int pos, len;
4524
4525 /*
4526 * We do not read the cache ourselves here, because the
4527 * benchmark with my previous version that always reads cache
4528 * shows that it makes things worse for diff-tree comparing
4529 * two linux-2.6 kernel trees in an already checked out work
4530 * tree. This is because most diff-tree comparisons deal with
4531 * only a small number of files, while reading the cache is
4532 * expensive for a large project, and its cost outweighs the
4533 * savings we get by not inflating the object to a temporary
4534 * file. Practically, this code only helps when we are used
4535 * by diff-cache --cached, which does read the cache before
4536 * calling us.
4537 */
4538 if (!istate->cache)
4539 return 0;
4540
4541 /* We want to avoid the working directory if our caller
4542 * doesn't need the data in a normal file, this system
4543 * is rather slow with its stat/open/mmap/close syscalls,
4544 * and the object is contained in a pack file. The pack
4545 * is probably already open and will be faster to obtain
4546 * the data through than the working directory. Loose
4547 * objects however would tend to be slower as they need
4548 * to be individually opened and inflated.
4549 */
4550 if (!FAST_WORKING_DIRECTORY && !want_file &&
4551 has_object_pack(istate->repo, oid))
4552 return 0;
4553
4554 /*
4555 * Similarly, if we'd have to convert the file contents anyway, that
4556 * makes the optimization not worthwhile.
4557 */
4558 if (!want_file && would_convert_to_git(istate, name))
4559 return 0;
4560
4561 /*
4562 * If this path does not match our sparse-checkout definition,
4563 * then the file will not be in the working directory.
4564 */
4565 if (!path_in_sparse_checkout(name, istate))
4566 return 0;
4567
4568 len = strlen(name);
4569 pos = index_name_pos(istate, name, len);
4570 if (pos < 0)
4571 return 0;
4572 ce = istate->cache[pos];
4573
4574 /*
4575 * This is not the sha1 we are looking for, or
4576 * unreusable because it is not a regular file.
4577 */
4578 if (!oideq(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
4579 return 0;
4580
4581 /*
4582 * If ce is marked as "assume unchanged", there is no
4583 * guarantee that work tree matches what we are looking for.
4584 */
4585 if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
4586 return 0;
4587
4588 /*
4589 * If ce matches the file in the work tree, we can reuse it.
4590 */
4591 if (ce_uptodate(ce) ||
4592 (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0)))
4593 return 1;
4594
4595 return 0;
4596 }
4597
4598 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
4599 {
4600 struct strbuf buf = STRBUF_INIT;
4601 const char *dirty = "";
4602
4603 /* Are we looking at the work tree? */
4604 if (s->dirty_submodule)
4605 dirty = "-dirty";
4606
4607 strbuf_addf(&buf, "Subproject commit %s%s\n",
4608 oid_to_hex(&s->oid), dirty);
4609 s->size = buf.len;
4610 if (size_only) {
4611 s->data = NULL;
4612 strbuf_release(&buf);
4613 } else {
4614 s->data = strbuf_detach(&buf, NULL);
4615 s->should_free = 1;
4616 }
4617 return 0;
4618 }
4619
4620 /*
4621 * While doing rename detection and pickaxe operation, we may need to
4622 * grab the data for the blob (or file) for our own in-core comparison.
4623 * diff_filespec has data and size fields for this purpose.
4624 */
4625 int diff_populate_filespec(struct repository *r,
4626 struct diff_filespec *s,
4627 const struct diff_populate_filespec_options *options)
4628 {
4629 int size_only = options ? options->check_size_only : 0;
4630 int check_binary = options ? options->check_binary : 0;
4631 int err = 0;
4632 int conv_flags = global_conv_flags_eol;
4633 /*
4634 * demote FAIL to WARN to allow inspecting the situation
4635 * instead of refusing.
4636 */
4637 if (conv_flags & CONV_EOL_RNDTRP_DIE)
4638 conv_flags = CONV_EOL_RNDTRP_WARN;
4639
4640 if (!DIFF_FILE_VALID(s))
4641 die("internal error: asking to populate invalid file.");
4642 if (S_ISDIR(s->mode))
4643 return -1;
4644
4645 if (s->data)
4646 return 0;
4647
4648 if (size_only && 0 < s->size)
4649 return 0;
4650
4651 if (S_ISGITLINK(s->mode))
4652 return diff_populate_gitlink(s, size_only);
4653
4654 if (!s->oid_valid ||
4655 reuse_worktree_file(r->index, s->path, &s->oid, 0)) {
4656 struct strbuf buf = STRBUF_INIT;
4657 struct stat st;
4658 int fd;
4659
4660 if (lstat(s->path, &st) < 0) {
4661 err_empty:
4662 err = -1;
4663 empty:
4664 s->data = (char *)"";
4665 s->size = 0;
4666 return err;
4667 }
4668 s->size = xsize_t(st.st_size);
4669 if (!s->size)
4670 goto empty;
4671 if (S_ISLNK(st.st_mode)) {
4672 struct strbuf sb = STRBUF_INIT;
4673
4674 if (strbuf_readlink(&sb, s->path, s->size))
4675 goto err_empty;
4676 s->size = sb.len;
4677 s->data = strbuf_detach(&sb, NULL);
4678 s->should_free = 1;
4679 return 0;
4680 }
4681
4682 /*
4683 * Even if the caller would be happy with getting
4684 * only the size, we cannot return early at this
4685 * point if the path requires us to run the content
4686 * conversion.
4687 */
4688 if (size_only && !would_convert_to_git(r->index, s->path))
4689 return 0;
4690
4691 /*
4692 * Note: this check uses xsize_t(st.st_size) that may
4693 * not be the true size of the blob after it goes
4694 * through convert_to_git(). This may not strictly be
4695 * correct, but the whole point of big_file_threshold
4696 * and is_binary check being that we want to avoid
4697 * opening the file and inspecting the contents, this
4698 * is probably fine.
4699 */
4700 if (check_binary &&
4701 s->size > repo_settings_get_big_file_threshold(the_repository) &&
4702 s->is_binary == -1) {
4703 s->is_binary = 1;
4704 return 0;
4705 }
4706 fd = open(s->path, O_RDONLY);
4707 if (fd < 0)
4708 goto err_empty;
4709 s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
4710 close(fd);
4711 s->should_munmap = 1;
4712
4713 /*
4714 * Convert from working tree format to canonical git format
4715 */
4716 if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) {
4717 size_t size = 0;
4718 munmap(s->data, s->size);
4719 s->should_munmap = 0;
4720 s->data = strbuf_detach(&buf, &size);
4721 s->size = size;
4722 s->should_free = 1;
4723 }
4724 }
4725 else {
4726 size_t size_st = 0;
4727 struct object_info info = {
4728 .sizep = &size_st
4729 };
4730
4731 if (!(size_only || check_binary))
4732 /*
4733 * Set contentp, since there is no chance that merely
4734 * the size is sufficient.
4735 */
4736 info.contentp = &s->data;
4737
4738 if (options && options->missing_object_cb) {
4739 if (!odb_read_object_info_extended(r->objects, &s->oid, &info,
4740 OBJECT_INFO_LOOKUP_REPLACE |
4741 OBJECT_INFO_SKIP_FETCH_OBJECT))
4742 goto object_read;
4743 options->missing_object_cb(options->missing_object_data);
4744 }
4745 if (odb_read_object_info_extended(r->objects, &s->oid, &info,
4746 OBJECT_INFO_LOOKUP_REPLACE))
4747 die("unable to read %s", oid_to_hex(&s->oid));
4748
4749 object_read:
4750 s->size = cast_size_t_to_ulong(size_st);
4751 if (size_only || check_binary) {
4752 if (size_only)
4753 return 0;
4754 if (s->size > repo_settings_get_big_file_threshold(the_repository) &&
4755 s->is_binary == -1) {
4756 s->is_binary = 1;
4757 return 0;
4758 }
4759 }
4760 if (!info.contentp) {
4761 info.contentp = &s->data;
4762 if (odb_read_object_info_extended(r->objects, &s->oid, &info,
4763 OBJECT_INFO_LOOKUP_REPLACE))
4764 die("unable to read %s", oid_to_hex(&s->oid));
4765 s->size = cast_size_t_to_ulong(size_st);
4766 }
4767 s->should_free = 1;
4768 }
4769 return 0;
4770 }
4771
4772 void diff_free_filespec_blob(struct diff_filespec *s)
4773 {
4774 if (s->should_free)
4775 free(s->data);
4776 else if (s->should_munmap)
4777 munmap(s->data, s->size);
4778
4779 if (s->should_free || s->should_munmap) {
4780 s->should_free = s->should_munmap = 0;
4781 s->data = NULL;
4782 }
4783 }
4784
4785 void diff_free_filespec_data(struct diff_filespec *s)
4786 {
4787 if (!s)
4788 return;
4789
4790 diff_free_filespec_blob(s);
4791 FREE_AND_NULL(s->cnt_data);
4792 }
4793
4794 static void prep_temp_blob(struct index_state *istate,
4795 const char *path, struct diff_tempfile *temp,
4796 void *blob,
4797 unsigned long size,
4798 const struct object_id *oid,
4799 int mode)
4800 {
4801 struct strbuf buf = STRBUF_INIT;
4802 char *path_dup = xstrdup(path);
4803 const char *base = basename(path_dup);
4804 struct checkout_metadata meta;
4805
4806 init_checkout_metadata(&meta, NULL, NULL, oid);
4807
4808 temp->tempfile = mks_tempfile_dt("git-blob-XXXXXX", base);
4809 if (!temp->tempfile)
4810 die_errno("unable to create temp-file");
4811 if (convert_to_working_tree(istate, path,
4812 (const char *)blob, (size_t)size, &buf, &meta)) {
4813 blob = buf.buf;
4814 size = buf.len;
4815 }
4816 if (write_in_full(temp->tempfile->fd, blob, size) < 0 ||
4817 close_tempfile_gently(temp->tempfile))
4818 die_errno("unable to write temp-file");
4819 temp->name = get_tempfile_path(temp->tempfile);
4820 oid_to_hex_r(temp->hex, oid);
4821 xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
4822 strbuf_release(&buf);
4823 free(path_dup);
4824 }
4825
4826 static struct diff_tempfile *prepare_temp_file(struct repository *r,
4827 struct diff_filespec *one)
4828 {
4829 struct diff_tempfile *temp = claim_diff_tempfile();
4830
4831 if (!DIFF_FILE_VALID(one)) {
4832 not_a_valid_file:
4833 /* A '-' entry produces this for file-2, and
4834 * a '+' entry produces this for file-1.
4835 */
4836 temp->name = "/dev/null";
4837 xsnprintf(temp->hex, sizeof(temp->hex), ".");
4838 xsnprintf(temp->mode, sizeof(temp->mode), ".");
4839 return temp;
4840 }
4841
4842 if (!S_ISGITLINK(one->mode) &&
4843 (!one->oid_valid ||
4844 reuse_worktree_file(r->index, one->path, &one->oid, 1))) {
4845 struct stat st;
4846 if (lstat(one->path, &st) < 0) {
4847 if (errno == ENOENT)
4848 goto not_a_valid_file;
4849 die_errno("stat(%s)", one->path);
4850 }
4851 if (S_ISLNK(st.st_mode)) {
4852 struct strbuf sb = STRBUF_INIT;
4853 if (strbuf_readlink(&sb, one->path, st.st_size) < 0)
4854 die_errno("readlink(%s)", one->path);
4855 prep_temp_blob(r->index, one->path, temp, sb.buf, sb.len,
4856 (one->oid_valid ?
4857 &one->oid : null_oid(the_hash_algo)),
4858 (one->oid_valid ?
4859 one->mode : S_IFLNK));
4860 strbuf_release(&sb);
4861 }
4862 else {
4863 /* we can borrow from the file in the work tree */
4864 temp->name = one->path;
4865 if (!one->oid_valid)
4866 oid_to_hex_r(temp->hex, null_oid(the_hash_algo));
4867 else
4868 oid_to_hex_r(temp->hex, &one->oid);
4869 /* Even though we may sometimes borrow the
4870 * contents from the work tree, we always want
4871 * one->mode. mode is trustworthy even when
4872 * !(one->oid_valid), as long as
4873 * DIFF_FILE_VALID(one).
4874 */
4875 xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode);
4876 }
4877 return temp;
4878 }
4879 else {
4880 if (diff_populate_filespec(r, one, NULL))
4881 die("cannot read data blob for %s", one->path);
4882 prep_temp_blob(r->index, one->path, temp,
4883 one->data, one->size,
4884 &one->oid, one->mode);
4885 }
4886 return temp;
4887 }
4888
4889 static void add_external_diff_name(struct repository *r,
4890 struct strvec *argv,
4891 struct diff_filespec *df)
4892 {
4893 struct diff_tempfile *temp = prepare_temp_file(r, df);
4894 strvec_push(argv, temp->name);
4895 strvec_push(argv, temp->hex);
4896 strvec_push(argv, temp->mode);
4897 }
4898
4899 /* An external diff command takes:
4900 *
4901 * diff-cmd name infile1 infile1-sha1 infile1-mode \
4902 * infile2 infile2-sha1 infile2-mode [ rename-to ]
4903 *
4904 */
4905 static void run_external_diff(const struct external_diff *pgm,
4906 const char *name,
4907 const char *other,
4908 struct diff_filespec *one,
4909 struct diff_filespec *two,
4910 const char *xfrm_msg,
4911 struct diff_options *o)
4912 {
4913 struct child_process cmd = CHILD_PROCESS_INIT;
4914 struct diff_queue_struct *q = &diff_queued_diff;
4915 int rc;
4916
4917 /*
4918 * Trivial equality is handled by diff_unmodified_pair() before
4919 * we get here. If we don't need to show the diff and the
4920 * external diff program lacks the ability to tell us whether
4921 * it's empty then we consider it non-empty without even asking.
4922 */
4923 if (!pgm->trust_exit_code && !o->file) {
4924 o->found_changes = 1;
4925 return;
4926 }
4927
4928 strvec_push(&cmd.args, pgm->cmd);
4929 strvec_push(&cmd.args, name);
4930
4931 if (one && two) {
4932 add_external_diff_name(o->repo, &cmd.args, one);
4933 add_external_diff_name(o->repo, &cmd.args, two);
4934 if (other) {
4935 strvec_push(&cmd.args, other);
4936 if (xfrm_msg)
4937 strvec_push(&cmd.args, xfrm_msg);
4938 }
4939 }
4940
4941 strvec_pushf(&cmd.env, "GIT_DIFF_PATH_COUNTER=%d",
4942 ++o->diff_path_counter);
4943 strvec_pushf(&cmd.env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
4944
4945 diff_free_filespec_data(one);
4946 diff_free_filespec_data(two);
4947 cmd.use_shell = 1;
4948 if (!o->file)
4949 cmd.no_stdout = 1;
4950 else if (o->file != stdout)
4951 cmd.out = xdup(fileno(o->file));
4952 rc = run_command(&cmd);
4953 if (!pgm->trust_exit_code && rc == 0)
4954 o->found_changes = 1;
4955 else if (pgm->trust_exit_code && rc == 0)
4956 ; /* nothing */
4957 else if (pgm->trust_exit_code && rc == 1)
4958 o->found_changes = 1;
4959 else
4960 die(_("external diff died, stopping at %s"), name);
4961
4962 remove_tempfile();
4963 }
4964
4965 static int similarity_index(struct diff_filepair *p)
4966 {
4967 return p->score * 100 / MAX_SCORE;
4968 }
4969
4970 static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
4971 {
4972 if (startup_info->have_repository)
4973 return repo_find_unique_abbrev(the_repository, oid, abbrev);
4974 else {
4975 char *hex = oid_to_hex(oid);
4976 if (abbrev < 0)
4977 abbrev = FALLBACK_DEFAULT_ABBREV;
4978 if (abbrev > the_hash_algo->hexsz)
4979 BUG("oid abbreviation out of range: %d", abbrev);
4980 if (abbrev)
4981 hex[abbrev] = '\0';
4982 return hex;
4983 }
4984 }
4985
4986 static void fill_metainfo(struct strbuf *msg,
4987 const char *name,
4988 const char *other,
4989 struct diff_filespec *one,
4990 struct diff_filespec *two,
4991 struct diff_options *o,
4992 struct diff_filepair *p,
4993 int *must_show_header,
4994 enum git_colorbool use_color)
4995 {
4996 const char *set = diff_get_color(use_color, DIFF_METAINFO);
4997 const char *reset = diff_get_color(use_color, DIFF_RESET);
4998 const char *line_prefix = diff_line_prefix(o);
4999 struct string_list *more_headers = NULL;
5000
Showing first 5,000 of 8,010 lines. View raw