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