Git fork
at 3da4413dbcdb8df021739ca6f20fe4f0bcd1fd3c 7497 lines 212 kB view raw
1/* 2 * Copyright (C) 2005 Junio C Hamano 3 */ 4 5#define USE_THE_REPOSITORY_VARIABLE 6#define DISABLE_SIGN_COMPARE_WARNINGS 7 8#include "git-compat-util.h" 9#include "abspath.h" 10#include "base85.h" 11#include "config.h" 12#include "convert.h" 13#include "environment.h" 14#include "gettext.h" 15#include "tempfile.h" 16#include "revision.h" 17#include "quote.h" 18#include "diff.h" 19#include "diffcore.h" 20#include "delta.h" 21#include "hex.h" 22#include "xdiff-interface.h" 23#include "color.h" 24#include "run-command.h" 25#include "utf8.h" 26#include "odb.h" 27#include "userdiff.h" 28#include "submodule.h" 29#include "hashmap.h" 30#include "mem-pool.h" 31#include "merge-ll.h" 32#include "string-list.h" 33#include "strvec.h" 34#include "tmp-objdir.h" 35#include "graph.h" 36#include "oid-array.h" 37#include "packfile.h" 38#include "pager.h" 39#include "parse-options.h" 40#include "help.h" 41#include "promisor-remote.h" 42#include "dir.h" 43#include "object-file.h" 44#include "object-name.h" 45#include "read-cache-ll.h" 46#include "setup.h" 47#include "strmap.h" 48#include "ws.h" 49 50#ifdef NO_FAST_WORKING_DIRECTORY 51#define FAST_WORKING_DIRECTORY 0 52#else 53#define FAST_WORKING_DIRECTORY 1 54#endif 55 56static int diff_detect_rename_default; 57static int diff_indent_heuristic = 1; 58static int diff_rename_limit_default = 1000; 59static int diff_suppress_blank_empty; 60static int diff_use_color_default = -1; 61static int diff_color_moved_default; 62static int diff_color_moved_ws_default; 63static int diff_context_default = 3; 64static int diff_interhunk_context_default; 65static char *diff_word_regex_cfg; 66static struct external_diff external_diff_cfg; 67static char *diff_order_file_cfg; 68int diff_auto_refresh_index = 1; 69static int diff_mnemonic_prefix; 70static int diff_no_prefix; 71static char *diff_src_prefix; 72static char *diff_dst_prefix; 73static int diff_relative; 74static int diff_stat_name_width; 75static int diff_stat_graph_width; 76static int diff_dirstat_permille_default = 30; 77static struct diff_options default_diff_options; 78static long diff_algorithm; 79static unsigned ws_error_highlight_default = WSEH_NEW; 80 81static char diff_colors[][COLOR_MAXLEN] = { 82 GIT_COLOR_RESET, 83 GIT_COLOR_NORMAL, /* CONTEXT */ 84 GIT_COLOR_BOLD, /* METAINFO */ 85 GIT_COLOR_CYAN, /* FRAGINFO */ 86 GIT_COLOR_RED, /* OLD */ 87 GIT_COLOR_GREEN, /* NEW */ 88 GIT_COLOR_YELLOW, /* COMMIT */ 89 GIT_COLOR_BG_RED, /* WHITESPACE */ 90 GIT_COLOR_NORMAL, /* FUNCINFO */ 91 GIT_COLOR_BOLD_MAGENTA, /* OLD_MOVED */ 92 GIT_COLOR_BOLD_BLUE, /* OLD_MOVED ALTERNATIVE */ 93 GIT_COLOR_FAINT, /* OLD_MOVED_DIM */ 94 GIT_COLOR_FAINT_ITALIC, /* OLD_MOVED_ALTERNATIVE_DIM */ 95 GIT_COLOR_BOLD_CYAN, /* NEW_MOVED */ 96 GIT_COLOR_BOLD_YELLOW, /* NEW_MOVED ALTERNATIVE */ 97 GIT_COLOR_FAINT, /* NEW_MOVED_DIM */ 98 GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */ 99 GIT_COLOR_FAINT, /* CONTEXT_DIM */ 100 GIT_COLOR_FAINT_RED, /* OLD_DIM */ 101 GIT_COLOR_FAINT_GREEN, /* NEW_DIM */ 102 GIT_COLOR_BOLD, /* CONTEXT_BOLD */ 103 GIT_COLOR_BOLD_RED, /* OLD_BOLD */ 104 GIT_COLOR_BOLD_GREEN, /* NEW_BOLD */ 105}; 106 107static const char *color_diff_slots[] = { 108 [DIFF_CONTEXT] = "context", 109 [DIFF_METAINFO] = "meta", 110 [DIFF_FRAGINFO] = "frag", 111 [DIFF_FILE_OLD] = "old", 112 [DIFF_FILE_NEW] = "new", 113 [DIFF_COMMIT] = "commit", 114 [DIFF_WHITESPACE] = "whitespace", 115 [DIFF_FUNCINFO] = "func", 116 [DIFF_FILE_OLD_MOVED] = "oldMoved", 117 [DIFF_FILE_OLD_MOVED_ALT] = "oldMovedAlternative", 118 [DIFF_FILE_OLD_MOVED_DIM] = "oldMovedDimmed", 119 [DIFF_FILE_OLD_MOVED_ALT_DIM] = "oldMovedAlternativeDimmed", 120 [DIFF_FILE_NEW_MOVED] = "newMoved", 121 [DIFF_FILE_NEW_MOVED_ALT] = "newMovedAlternative", 122 [DIFF_FILE_NEW_MOVED_DIM] = "newMovedDimmed", 123 [DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed", 124 [DIFF_CONTEXT_DIM] = "contextDimmed", 125 [DIFF_FILE_OLD_DIM] = "oldDimmed", 126 [DIFF_FILE_NEW_DIM] = "newDimmed", 127 [DIFF_CONTEXT_BOLD] = "contextBold", 128 [DIFF_FILE_OLD_BOLD] = "oldBold", 129 [DIFF_FILE_NEW_BOLD] = "newBold", 130}; 131 132define_list_config_array_extra(color_diff_slots, {"plain"}); 133 134static int parse_diff_color_slot(const char *var) 135{ 136 if (!strcasecmp(var, "plain")) 137 return DIFF_CONTEXT; 138 return LOOKUP_CONFIG(color_diff_slots, var); 139} 140 141static int parse_dirstat_params(struct diff_options *options, const char *params_string, 142 struct strbuf *errmsg) 143{ 144 char *params_copy = xstrdup(params_string); 145 struct string_list params = STRING_LIST_INIT_NODUP; 146 int ret = 0; 147 int i; 148 149 if (*params_copy) 150 string_list_split_in_place(&params, params_copy, ",", -1); 151 for (i = 0; i < params.nr; i++) { 152 const char *p = params.items[i].string; 153 if (!strcmp(p, "changes")) { 154 options->flags.dirstat_by_line = 0; 155 options->flags.dirstat_by_file = 0; 156 } else if (!strcmp(p, "lines")) { 157 options->flags.dirstat_by_line = 1; 158 options->flags.dirstat_by_file = 0; 159 } else if (!strcmp(p, "files")) { 160 options->flags.dirstat_by_line = 0; 161 options->flags.dirstat_by_file = 1; 162 } else if (!strcmp(p, "noncumulative")) { 163 options->flags.dirstat_cumulative = 0; 164 } else if (!strcmp(p, "cumulative")) { 165 options->flags.dirstat_cumulative = 1; 166 } else if (isdigit(*p)) { 167 char *end; 168 int permille = strtoul(p, &end, 10) * 10; 169 if (*end == '.' && isdigit(*++end)) { 170 /* only use first digit */ 171 permille += *end - '0'; 172 /* .. and ignore any further digits */ 173 while (isdigit(*++end)) 174 ; /* nothing */ 175 } 176 if (!*end) 177 options->dirstat_permille = permille; 178 else { 179 strbuf_addf(errmsg, _(" Failed to parse dirstat cut-off percentage '%s'\n"), 180 p); 181 ret++; 182 } 183 } else { 184 strbuf_addf(errmsg, _(" Unknown dirstat parameter '%s'\n"), p); 185 ret++; 186 } 187 188 } 189 string_list_clear(&params, 0); 190 free(params_copy); 191 return ret; 192} 193 194static int parse_submodule_params(struct diff_options *options, const char *value) 195{ 196 if (!strcmp(value, "log")) 197 options->submodule_format = DIFF_SUBMODULE_LOG; 198 else if (!strcmp(value, "short")) 199 options->submodule_format = DIFF_SUBMODULE_SHORT; 200 else if (!strcmp(value, "diff")) 201 options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF; 202 /* 203 * Please update $__git_diff_submodule_formats in 204 * git-completion.bash when you add new formats. 205 */ 206 else 207 return -1; 208 return 0; 209} 210 211int git_config_rename(const char *var, const char *value) 212{ 213 if (!value) 214 return DIFF_DETECT_RENAME; 215 if (!strcasecmp(value, "copies") || !strcasecmp(value, "copy")) 216 return DIFF_DETECT_COPY; 217 return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0; 218} 219 220long parse_algorithm_value(const char *value) 221{ 222 if (!value) 223 return -1; 224 else if (!strcasecmp(value, "myers") || !strcasecmp(value, "default")) 225 return 0; 226 else if (!strcasecmp(value, "minimal")) 227 return XDF_NEED_MINIMAL; 228 else if (!strcasecmp(value, "patience")) 229 return XDF_PATIENCE_DIFF; 230 else if (!strcasecmp(value, "histogram")) 231 return XDF_HISTOGRAM_DIFF; 232 /* 233 * Please update $__git_diff_algorithms in git-completion.bash 234 * when you add new algorithms. 235 */ 236 return -1; 237} 238 239static int parse_one_token(const char **arg, const char *token) 240{ 241 const char *rest; 242 if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) { 243 *arg = rest; 244 return 1; 245 } 246 return 0; 247} 248 249static int parse_ws_error_highlight(const char *arg) 250{ 251 const char *orig_arg = arg; 252 unsigned val = 0; 253 254 while (*arg) { 255 if (parse_one_token(&arg, "none")) 256 val = 0; 257 else if (parse_one_token(&arg, "default")) 258 val = WSEH_NEW; 259 else if (parse_one_token(&arg, "all")) 260 val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT; 261 else if (parse_one_token(&arg, "new")) 262 val |= WSEH_NEW; 263 else if (parse_one_token(&arg, "old")) 264 val |= WSEH_OLD; 265 else if (parse_one_token(&arg, "context")) 266 val |= WSEH_CONTEXT; 267 else { 268 return -1 - (int)(arg - orig_arg); 269 } 270 if (*arg) 271 arg++; 272 } 273 return val; 274} 275 276/* 277 * These are to give UI layer defaults. 278 * The core-level commands such as git-diff-files should 279 * never be affected by the setting of diff.renames 280 * the user happens to have in the configuration file. 281 */ 282void init_diff_ui_defaults(void) 283{ 284 diff_detect_rename_default = DIFF_DETECT_RENAME; 285} 286 287int git_diff_heuristic_config(const char *var, const char *value, 288 void *cb UNUSED) 289{ 290 if (!strcmp(var, "diff.indentheuristic")) 291 diff_indent_heuristic = git_config_bool(var, value); 292 return 0; 293} 294 295static int parse_color_moved(const char *arg) 296{ 297 switch (git_parse_maybe_bool(arg)) { 298 case 0: 299 return COLOR_MOVED_NO; 300 case 1: 301 return COLOR_MOVED_DEFAULT; 302 default: 303 break; 304 } 305 306 if (!strcmp(arg, "no")) 307 return COLOR_MOVED_NO; 308 else if (!strcmp(arg, "plain")) 309 return COLOR_MOVED_PLAIN; 310 else if (!strcmp(arg, "blocks")) 311 return COLOR_MOVED_BLOCKS; 312 else if (!strcmp(arg, "zebra")) 313 return COLOR_MOVED_ZEBRA; 314 else if (!strcmp(arg, "default")) 315 return COLOR_MOVED_DEFAULT; 316 else if (!strcmp(arg, "dimmed-zebra")) 317 return COLOR_MOVED_ZEBRA_DIM; 318 else if (!strcmp(arg, "dimmed_zebra")) 319 return COLOR_MOVED_ZEBRA_DIM; 320 else 321 return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'")); 322} 323 324static unsigned parse_color_moved_ws(const char *arg) 325{ 326 int ret = 0; 327 struct string_list l = STRING_LIST_INIT_DUP; 328 struct string_list_item *i; 329 330 string_list_split(&l, arg, ',', -1); 331 332 for_each_string_list_item(i, &l) { 333 struct strbuf sb = STRBUF_INIT; 334 strbuf_addstr(&sb, i->string); 335 strbuf_trim(&sb); 336 337 if (!strcmp(sb.buf, "no")) 338 ret = 0; 339 else if (!strcmp(sb.buf, "ignore-space-change")) 340 ret |= XDF_IGNORE_WHITESPACE_CHANGE; 341 else if (!strcmp(sb.buf, "ignore-space-at-eol")) 342 ret |= XDF_IGNORE_WHITESPACE_AT_EOL; 343 else if (!strcmp(sb.buf, "ignore-all-space")) 344 ret |= XDF_IGNORE_WHITESPACE; 345 else if (!strcmp(sb.buf, "allow-indentation-change")) 346 ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE; 347 else { 348 ret |= COLOR_MOVED_WS_ERROR; 349 error(_("unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', 'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"), sb.buf); 350 } 351 352 strbuf_release(&sb); 353 } 354 355 if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) && 356 (ret & XDF_WHITESPACE_FLAGS)) { 357 error(_("color-moved-ws: allow-indentation-change cannot be combined with other whitespace modes")); 358 ret |= COLOR_MOVED_WS_ERROR; 359 } 360 361 string_list_clear(&l, 0); 362 363 return ret; 364} 365 366int git_diff_ui_config(const char *var, const char *value, 367 const struct config_context *ctx, void *cb) 368{ 369 if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { 370 diff_use_color_default = git_config_colorbool(var, value); 371 return 0; 372 } 373 if (!strcmp(var, "diff.colormoved")) { 374 int cm = parse_color_moved(value); 375 if (cm < 0) 376 return -1; 377 diff_color_moved_default = cm; 378 return 0; 379 } 380 if (!strcmp(var, "diff.colormovedws")) { 381 unsigned cm; 382 if (!value) 383 return config_error_nonbool(var); 384 cm = parse_color_moved_ws(value); 385 if (cm & COLOR_MOVED_WS_ERROR) 386 return -1; 387 diff_color_moved_ws_default = cm; 388 return 0; 389 } 390 if (!strcmp(var, "diff.context")) { 391 diff_context_default = git_config_int(var, value, ctx->kvi); 392 if (diff_context_default < 0) 393 return -1; 394 return 0; 395 } 396 if (!strcmp(var, "diff.interhunkcontext")) { 397 diff_interhunk_context_default = git_config_int(var, value, 398 ctx->kvi); 399 if (diff_interhunk_context_default < 0) 400 return -1; 401 return 0; 402 } 403 if (!strcmp(var, "diff.renames")) { 404 diff_detect_rename_default = git_config_rename(var, value); 405 return 0; 406 } 407 if (!strcmp(var, "diff.autorefreshindex")) { 408 diff_auto_refresh_index = git_config_bool(var, value); 409 return 0; 410 } 411 if (!strcmp(var, "diff.mnemonicprefix")) { 412 diff_mnemonic_prefix = git_config_bool(var, value); 413 return 0; 414 } 415 if (!strcmp(var, "diff.noprefix")) { 416 diff_no_prefix = git_config_bool(var, value); 417 return 0; 418 } 419 if (!strcmp(var, "diff.srcprefix")) { 420 FREE_AND_NULL(diff_src_prefix); 421 return git_config_string(&diff_src_prefix, var, value); 422 } 423 if (!strcmp(var, "diff.dstprefix")) { 424 FREE_AND_NULL(diff_dst_prefix); 425 return git_config_string(&diff_dst_prefix, var, value); 426 } 427 if (!strcmp(var, "diff.relative")) { 428 diff_relative = git_config_bool(var, value); 429 return 0; 430 } 431 if (!strcmp(var, "diff.statnamewidth")) { 432 diff_stat_name_width = git_config_int(var, value, ctx->kvi); 433 return 0; 434 } 435 if (!strcmp(var, "diff.statgraphwidth")) { 436 diff_stat_graph_width = git_config_int(var, value, ctx->kvi); 437 return 0; 438 } 439 if (!strcmp(var, "diff.external")) 440 return git_config_string(&external_diff_cfg.cmd, var, value); 441 if (!strcmp(var, "diff.trustexitcode")) { 442 external_diff_cfg.trust_exit_code = git_config_bool(var, value); 443 return 0; 444 } 445 if (!strcmp(var, "diff.wordregex")) 446 return git_config_string(&diff_word_regex_cfg, var, value); 447 if (!strcmp(var, "diff.orderfile")) { 448 FREE_AND_NULL(diff_order_file_cfg); 449 return git_config_pathname(&diff_order_file_cfg, var, value); 450 } 451 452 if (!strcmp(var, "diff.ignoresubmodules")) { 453 if (!value) 454 return config_error_nonbool(var); 455 handle_ignore_submodules_arg(&default_diff_options, value); 456 } 457 458 if (!strcmp(var, "diff.submodule")) { 459 if (!value) 460 return config_error_nonbool(var); 461 if (parse_submodule_params(&default_diff_options, value)) 462 warning(_("Unknown value for 'diff.submodule' config variable: '%s'"), 463 value); 464 return 0; 465 } 466 467 if (!strcmp(var, "diff.algorithm")) { 468 if (!value) 469 return config_error_nonbool(var); 470 diff_algorithm = parse_algorithm_value(value); 471 if (diff_algorithm < 0) 472 return error(_("unknown value for config '%s': %s"), 473 var, value); 474 return 0; 475 } 476 477 if (git_color_config(var, value, cb) < 0) 478 return -1; 479 480 return git_diff_basic_config(var, value, ctx, cb); 481} 482 483int git_diff_basic_config(const char *var, const char *value, 484 const struct config_context *ctx, void *cb) 485{ 486 const char *name; 487 488 if (!strcmp(var, "diff.renamelimit")) { 489 diff_rename_limit_default = git_config_int(var, value, ctx->kvi); 490 return 0; 491 } 492 493 if (userdiff_config(var, value) < 0) 494 return -1; 495 496 if (skip_prefix(var, "diff.color.", &name) || 497 skip_prefix(var, "color.diff.", &name)) { 498 int slot = parse_diff_color_slot(name); 499 if (slot < 0) 500 return 0; 501 if (!value) 502 return config_error_nonbool(var); 503 return color_parse(value, diff_colors[slot]); 504 } 505 506 if (!strcmp(var, "diff.wserrorhighlight")) { 507 int val; 508 if (!value) 509 return config_error_nonbool(var); 510 val = parse_ws_error_highlight(value); 511 if (val < 0) 512 return error(_("unknown value for config '%s': %s"), 513 var, value); 514 ws_error_highlight_default = val; 515 return 0; 516 } 517 518 /* like GNU diff's --suppress-blank-empty option */ 519 if (!strcmp(var, "diff.suppressblankempty") || 520 /* for backwards compatibility */ 521 !strcmp(var, "diff.suppress-blank-empty")) { 522 diff_suppress_blank_empty = git_config_bool(var, value); 523 return 0; 524 } 525 526 if (!strcmp(var, "diff.dirstat")) { 527 struct strbuf errmsg = STRBUF_INIT; 528 if (!value) 529 return config_error_nonbool(var); 530 default_diff_options.dirstat_permille = diff_dirstat_permille_default; 531 if (parse_dirstat_params(&default_diff_options, value, &errmsg)) 532 warning(_("Found errors in 'diff.dirstat' config variable:\n%s"), 533 errmsg.buf); 534 strbuf_release(&errmsg); 535 diff_dirstat_permille_default = default_diff_options.dirstat_permille; 536 return 0; 537 } 538 539 if (git_diff_heuristic_config(var, value, cb) < 0) 540 return -1; 541 542 return git_default_config(var, value, ctx, cb); 543} 544 545static char *quote_two(const char *one, const char *two) 546{ 547 int need_one = quote_c_style(one, NULL, NULL, CQUOTE_NODQ); 548 int need_two = quote_c_style(two, NULL, NULL, CQUOTE_NODQ); 549 struct strbuf res = STRBUF_INIT; 550 551 if (need_one + need_two) { 552 strbuf_addch(&res, '"'); 553 quote_c_style(one, &res, NULL, CQUOTE_NODQ); 554 quote_c_style(two, &res, NULL, CQUOTE_NODQ); 555 strbuf_addch(&res, '"'); 556 } else { 557 strbuf_addstr(&res, one); 558 strbuf_addstr(&res, two); 559 } 560 return strbuf_detach(&res, NULL); 561} 562 563static const struct external_diff *external_diff(void) 564{ 565 static struct external_diff external_diff_env, *external_diff_ptr; 566 static int done_preparing = 0; 567 568 if (done_preparing) 569 return external_diff_ptr; 570 external_diff_env.cmd = xstrdup_or_null(getenv("GIT_EXTERNAL_DIFF")); 571 if (git_env_bool("GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE", 0)) 572 external_diff_env.trust_exit_code = 1; 573 if (external_diff_env.cmd) 574 external_diff_ptr = &external_diff_env; 575 else if (external_diff_cfg.cmd) 576 external_diff_ptr = &external_diff_cfg; 577 done_preparing = 1; 578 return external_diff_ptr; 579} 580 581/* 582 * Keep track of files used for diffing. Sometimes such an entry 583 * refers to a temporary file, sometimes to an existing file, and 584 * sometimes to "/dev/null". 585 */ 586static struct diff_tempfile { 587 /* 588 * filename external diff should read from, or NULL if this 589 * entry is currently not in use: 590 */ 591 const char *name; 592 593 char hex[GIT_MAX_HEXSZ + 1]; 594 char mode[10]; 595 596 /* 597 * If this diff_tempfile instance refers to a temporary file, 598 * this tempfile object is used to manage its lifetime. 599 */ 600 struct tempfile *tempfile; 601} diff_temp[2]; 602 603struct emit_callback { 604 int color_diff; 605 unsigned ws_rule; 606 int blank_at_eof_in_preimage; 607 int blank_at_eof_in_postimage; 608 int lno_in_preimage; 609 int lno_in_postimage; 610 const char **label_path; 611 struct diff_words_data *diff_words; 612 struct diff_options *opt; 613 struct strbuf *header; 614}; 615 616static int count_lines(const char *data, int size) 617{ 618 int count, ch, completely_empty = 1, nl_just_seen = 0; 619 count = 0; 620 while (0 < size--) { 621 ch = *data++; 622 if (ch == '\n') { 623 count++; 624 nl_just_seen = 1; 625 completely_empty = 0; 626 } 627 else { 628 nl_just_seen = 0; 629 completely_empty = 0; 630 } 631 } 632 if (completely_empty) 633 return 0; 634 if (!nl_just_seen) 635 count++; /* no trailing newline */ 636 return count; 637} 638 639static int fill_mmfile(struct repository *r, mmfile_t *mf, 640 struct diff_filespec *one) 641{ 642 if (!DIFF_FILE_VALID(one)) { 643 mf->ptr = (char *)""; /* does not matter */ 644 mf->size = 0; 645 return 0; 646 } 647 else if (diff_populate_filespec(r, one, NULL)) 648 return -1; 649 650 mf->ptr = one->data; 651 mf->size = one->size; 652 return 0; 653} 654 655/* like fill_mmfile, but only for size, so we can avoid retrieving blob */ 656static unsigned long diff_filespec_size(struct repository *r, 657 struct diff_filespec *one) 658{ 659 struct diff_populate_filespec_options dpf_options = { 660 .check_size_only = 1, 661 }; 662 663 if (!DIFF_FILE_VALID(one)) 664 return 0; 665 diff_populate_filespec(r, one, &dpf_options); 666 return one->size; 667} 668 669static int count_trailing_blank(mmfile_t *mf) 670{ 671 char *ptr = mf->ptr; 672 long size = mf->size; 673 int cnt = 0; 674 675 if (!size) 676 return cnt; 677 ptr += size - 1; /* pointing at the very end */ 678 if (*ptr != '\n') 679 ; /* incomplete line */ 680 else 681 ptr--; /* skip the last LF */ 682 while (mf->ptr < ptr) { 683 char *prev_eol; 684 for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--) 685 if (*prev_eol == '\n') 686 break; 687 if (!ws_blank_line(prev_eol + 1, ptr - prev_eol)) 688 break; 689 cnt++; 690 ptr = prev_eol - 1; 691 } 692 return cnt; 693} 694 695static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, 696 struct emit_callback *ecbdata) 697{ 698 int l1, l2, at; 699 l1 = count_trailing_blank(mf1); 700 l2 = count_trailing_blank(mf2); 701 if (l2 <= l1) { 702 ecbdata->blank_at_eof_in_preimage = 0; 703 ecbdata->blank_at_eof_in_postimage = 0; 704 return; 705 } 706 at = count_lines(mf1->ptr, mf1->size); 707 ecbdata->blank_at_eof_in_preimage = (at - l1) + 1; 708 709 at = count_lines(mf2->ptr, mf2->size); 710 ecbdata->blank_at_eof_in_postimage = (at - l2) + 1; 711} 712 713static void emit_line_0(struct diff_options *o, 714 const char *set_sign, const char *set, unsigned reverse, const char *reset, 715 int first, const char *line, int len) 716{ 717 int has_trailing_newline, has_trailing_carriage_return; 718 int needs_reset = 0; /* at the end of the line */ 719 FILE *file = o->file; 720 721 fputs(diff_line_prefix(o), file); 722 723 has_trailing_newline = (len > 0 && line[len-1] == '\n'); 724 if (has_trailing_newline) 725 len--; 726 727 has_trailing_carriage_return = (len > 0 && line[len-1] == '\r'); 728 if (has_trailing_carriage_return) 729 len--; 730 731 if (!len && !first) 732 goto end_of_line; 733 734 if (reverse && want_color(o->use_color)) { 735 fputs(GIT_COLOR_REVERSE, file); 736 needs_reset = 1; 737 } 738 739 if (set_sign) { 740 fputs(set_sign, file); 741 needs_reset = 1; 742 } 743 744 if (first) 745 fputc(first, file); 746 747 if (!len) 748 goto end_of_line; 749 750 if (set) { 751 if (set_sign && set != set_sign) 752 fputs(reset, file); 753 fputs(set, file); 754 needs_reset = 1; 755 } 756 fwrite(line, len, 1, file); 757 needs_reset = 1; /* 'line' may contain color codes. */ 758 759end_of_line: 760 if (needs_reset) 761 fputs(reset, file); 762 if (has_trailing_carriage_return) 763 fputc('\r', file); 764 if (has_trailing_newline) 765 fputc('\n', file); 766} 767 768static void emit_line(struct diff_options *o, const char *set, const char *reset, 769 const char *line, int len) 770{ 771 emit_line_0(o, set, NULL, 0, reset, 0, line, len); 772} 773 774enum diff_symbol { 775 DIFF_SYMBOL_BINARY_DIFF_HEADER, 776 DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA, 777 DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL, 778 DIFF_SYMBOL_BINARY_DIFF_BODY, 779 DIFF_SYMBOL_BINARY_DIFF_FOOTER, 780 DIFF_SYMBOL_STATS_SUMMARY_NO_FILES, 781 DIFF_SYMBOL_STATS_SUMMARY_ABBREV, 782 DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES, 783 DIFF_SYMBOL_STATS_LINE, 784 DIFF_SYMBOL_WORD_DIFF, 785 DIFF_SYMBOL_STAT_SEP, 786 DIFF_SYMBOL_SUMMARY, 787 DIFF_SYMBOL_SUBMODULE_ADD, 788 DIFF_SYMBOL_SUBMODULE_DEL, 789 DIFF_SYMBOL_SUBMODULE_UNTRACKED, 790 DIFF_SYMBOL_SUBMODULE_MODIFIED, 791 DIFF_SYMBOL_SUBMODULE_HEADER, 792 DIFF_SYMBOL_SUBMODULE_ERROR, 793 DIFF_SYMBOL_SUBMODULE_PIPETHROUGH, 794 DIFF_SYMBOL_REWRITE_DIFF, 795 DIFF_SYMBOL_BINARY_FILES, 796 DIFF_SYMBOL_HEADER, 797 DIFF_SYMBOL_FILEPAIR_PLUS, 798 DIFF_SYMBOL_FILEPAIR_MINUS, 799 DIFF_SYMBOL_WORDS_PORCELAIN, 800 DIFF_SYMBOL_WORDS, 801 DIFF_SYMBOL_CONTEXT, 802 DIFF_SYMBOL_CONTEXT_INCOMPLETE, 803 DIFF_SYMBOL_PLUS, 804 DIFF_SYMBOL_MINUS, 805 DIFF_SYMBOL_NO_LF_EOF, 806 DIFF_SYMBOL_CONTEXT_FRAGINFO, 807 DIFF_SYMBOL_CONTEXT_MARKER, 808 DIFF_SYMBOL_SEPARATOR 809}; 810/* 811 * Flags for content lines: 812 * 0..12 are whitespace rules 813 * 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT 814 * 16 is marking if the line is blank at EOF 815 */ 816#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16) 817#define DIFF_SYMBOL_MOVED_LINE (1<<17) 818#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<18) 819#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<19) 820#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK) 821 822/* 823 * This struct is used when we need to buffer the output of the diff output. 824 * 825 * NEEDSWORK: Instead of storing a copy of the line, add an offset pointer 826 * into the pre/post image file. This pointer could be a union with the 827 * line pointer. By storing an offset into the file instead of the literal line, 828 * we can decrease the memory footprint for the buffered output. At first we 829 * may want to only have indirection for the content lines, but we could also 830 * enhance the state for emitting prefabricated lines, e.g. the similarity 831 * score line or hunk/file headers would only need to store a number or path 832 * and then the output can be constructed later on depending on state. 833 */ 834struct emitted_diff_symbol { 835 const char *line; 836 int len; 837 int flags; 838 int indent_off; /* Offset to first non-whitespace character */ 839 int indent_width; /* The visual width of the indentation */ 840 unsigned id; 841 enum diff_symbol s; 842}; 843#define EMITTED_DIFF_SYMBOL_INIT { 0 } 844 845struct emitted_diff_symbols { 846 struct emitted_diff_symbol *buf; 847 int nr, alloc; 848}; 849#define EMITTED_DIFF_SYMBOLS_INIT { 0 } 850 851static void append_emitted_diff_symbol(struct diff_options *o, 852 struct emitted_diff_symbol *e) 853{ 854 struct emitted_diff_symbol *f; 855 856 ALLOC_GROW(o->emitted_symbols->buf, 857 o->emitted_symbols->nr + 1, 858 o->emitted_symbols->alloc); 859 f = &o->emitted_symbols->buf[o->emitted_symbols->nr++]; 860 861 memcpy(f, e, sizeof(struct emitted_diff_symbol)); 862 f->line = e->line ? xmemdupz(e->line, e->len) : NULL; 863} 864 865static void free_emitted_diff_symbols(struct emitted_diff_symbols *e) 866{ 867 if (!e) 868 return; 869 free(e->buf); 870 free(e); 871} 872 873struct moved_entry { 874 const struct emitted_diff_symbol *es; 875 struct moved_entry *next_line; 876 struct moved_entry *next_match; 877}; 878 879struct moved_block { 880 struct moved_entry *match; 881 int wsd; /* The whitespace delta of this block */ 882}; 883 884#define INDENT_BLANKLINE INT_MIN 885 886static void fill_es_indent_data(struct emitted_diff_symbol *es) 887{ 888 unsigned int off = 0, i; 889 int width = 0, tab_width = es->flags & WS_TAB_WIDTH_MASK; 890 const char *s = es->line; 891 const int len = es->len; 892 893 /* skip any \v \f \r at start of indentation */ 894 while (s[off] == '\f' || s[off] == '\v' || 895 (off < len - 1 && s[off] == '\r')) 896 off++; 897 898 /* calculate the visual width of indentation */ 899 while(1) { 900 if (s[off] == ' ') { 901 width++; 902 off++; 903 } else if (s[off] == '\t') { 904 width += tab_width - (width % tab_width); 905 while (s[++off] == '\t') 906 width += tab_width; 907 } else { 908 break; 909 } 910 } 911 912 /* check if this line is blank */ 913 for (i = off; i < len; i++) 914 if (!isspace(s[i])) 915 break; 916 917 if (i == len) { 918 es->indent_width = INDENT_BLANKLINE; 919 es->indent_off = len; 920 } else { 921 es->indent_off = off; 922 es->indent_width = width; 923 } 924} 925 926static int compute_ws_delta(const struct emitted_diff_symbol *a, 927 const struct emitted_diff_symbol *b) 928{ 929 int a_width = a->indent_width, 930 b_width = b->indent_width; 931 932 if (a_width == INDENT_BLANKLINE && b_width == INDENT_BLANKLINE) 933 return INDENT_BLANKLINE; 934 935 return a_width - b_width; 936} 937 938static int cmp_in_block_with_wsd(const struct moved_entry *cur, 939 const struct emitted_diff_symbol *l, 940 struct moved_block *pmb) 941{ 942 int a_width = cur->es->indent_width, b_width = l->indent_width; 943 int delta; 944 945 /* The text of each line must match */ 946 if (cur->es->id != l->id) 947 return 1; 948 949 /* 950 * If 'l' and 'cur' are both blank then we don't need to check the 951 * indent. We only need to check cur as we know the strings match. 952 * */ 953 if (a_width == INDENT_BLANKLINE) 954 return 0; 955 956 /* 957 * The indent changes of the block are known and stored in pmb->wsd; 958 * however we need to check if the indent changes of the current line 959 * match those of the current block. 960 */ 961 delta = b_width - a_width; 962 963 /* 964 * If the previous lines of this block were all blank then set its 965 * whitespace delta. 966 */ 967 if (pmb->wsd == INDENT_BLANKLINE) 968 pmb->wsd = delta; 969 970 return delta != pmb->wsd; 971} 972 973struct interned_diff_symbol { 974 struct hashmap_entry ent; 975 struct emitted_diff_symbol *es; 976}; 977 978static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data, 979 const struct hashmap_entry *eptr, 980 const struct hashmap_entry *entry_or_key, 981 const void *keydata UNUSED) 982{ 983 const struct diff_options *diffopt = hashmap_cmp_fn_data; 984 const struct emitted_diff_symbol *a, *b; 985 unsigned flags = diffopt->color_moved_ws_handling 986 & XDF_WHITESPACE_FLAGS; 987 988 a = container_of(eptr, const struct interned_diff_symbol, ent)->es; 989 b = container_of(entry_or_key, const struct interned_diff_symbol, ent)->es; 990 991 return !xdiff_compare_lines(a->line + a->indent_off, 992 a->len - a->indent_off, 993 b->line + b->indent_off, 994 b->len - b->indent_off, flags); 995} 996 997static void prepare_entry(struct diff_options *o, struct emitted_diff_symbol *l, 998 struct interned_diff_symbol *s) 999{ 1000 unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS; 1001 unsigned int hash = xdiff_hash_string(l->line + l->indent_off, 1002 l->len - l->indent_off, flags); 1003 1004 hashmap_entry_init(&s->ent, hash); 1005 s->es = l; 1006} 1007 1008struct moved_entry_list { 1009 struct moved_entry *add, *del; 1010}; 1011 1012static struct moved_entry_list *add_lines_to_move_detection(struct diff_options *o, 1013 struct mem_pool *entry_mem_pool) 1014{ 1015 struct moved_entry *prev_line = NULL; 1016 struct mem_pool interned_pool; 1017 struct hashmap interned_map; 1018 struct moved_entry_list *entry_list = NULL; 1019 size_t entry_list_alloc = 0; 1020 unsigned id = 0; 1021 int n; 1022 1023 hashmap_init(&interned_map, interned_diff_symbol_cmp, o, 8096); 1024 mem_pool_init(&interned_pool, 1024 * 1024); 1025 1026 for (n = 0; n < o->emitted_symbols->nr; n++) { 1027 struct interned_diff_symbol key; 1028 struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n]; 1029 struct interned_diff_symbol *s; 1030 struct moved_entry *entry; 1031 1032 if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS) { 1033 prev_line = NULL; 1034 continue; 1035 } 1036 1037 if (o->color_moved_ws_handling & 1038 COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) 1039 fill_es_indent_data(l); 1040 1041 prepare_entry(o, l, &key); 1042 s = hashmap_get_entry(&interned_map, &key, ent, &key.ent); 1043 if (s) { 1044 l->id = s->es->id; 1045 } else { 1046 l->id = id; 1047 ALLOC_GROW_BY(entry_list, id, 1, entry_list_alloc); 1048 hashmap_add(&interned_map, 1049 memcpy(mem_pool_alloc(&interned_pool, 1050 sizeof(key)), 1051 &key, sizeof(key))); 1052 } 1053 entry = mem_pool_alloc(entry_mem_pool, sizeof(*entry)); 1054 entry->es = l; 1055 entry->next_line = NULL; 1056 if (prev_line && prev_line->es->s == l->s) 1057 prev_line->next_line = entry; 1058 prev_line = entry; 1059 if (l->s == DIFF_SYMBOL_PLUS) { 1060 entry->next_match = entry_list[l->id].add; 1061 entry_list[l->id].add = entry; 1062 } else { 1063 entry->next_match = entry_list[l->id].del; 1064 entry_list[l->id].del = entry; 1065 } 1066 } 1067 1068 hashmap_clear(&interned_map); 1069 mem_pool_discard(&interned_pool, 0); 1070 1071 return entry_list; 1072} 1073 1074static void pmb_advance_or_null(struct diff_options *o, 1075 struct emitted_diff_symbol *l, 1076 struct moved_block *pmb, 1077 int *pmb_nr) 1078{ 1079 int i, j; 1080 1081 for (i = 0, j = 0; i < *pmb_nr; i++) { 1082 int match; 1083 struct moved_entry *prev = pmb[i].match; 1084 struct moved_entry *cur = (prev && prev->next_line) ? 1085 prev->next_line : NULL; 1086 1087 if (o->color_moved_ws_handling & 1088 COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) 1089 match = cur && 1090 !cmp_in_block_with_wsd(cur, l, &pmb[i]); 1091 else 1092 match = cur && cur->es->id == l->id; 1093 1094 if (match) { 1095 pmb[j] = pmb[i]; 1096 pmb[j++].match = cur; 1097 } 1098 } 1099 *pmb_nr = j; 1100} 1101 1102static void fill_potential_moved_blocks(struct diff_options *o, 1103 struct moved_entry *match, 1104 struct emitted_diff_symbol *l, 1105 struct moved_block **pmb_p, 1106 int *pmb_alloc_p, int *pmb_nr_p) 1107 1108{ 1109 struct moved_block *pmb = *pmb_p; 1110 int pmb_alloc = *pmb_alloc_p, pmb_nr = *pmb_nr_p; 1111 1112 /* 1113 * The current line is the start of a new block. 1114 * Setup the set of potential blocks. 1115 */ 1116 for (; match; match = match->next_match) { 1117 ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc); 1118 if (o->color_moved_ws_handling & 1119 COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) 1120 pmb[pmb_nr].wsd = compute_ws_delta(l, match->es); 1121 else 1122 pmb[pmb_nr].wsd = 0; 1123 pmb[pmb_nr++].match = match; 1124 } 1125 1126 *pmb_p = pmb; 1127 *pmb_alloc_p = pmb_alloc; 1128 *pmb_nr_p = pmb_nr; 1129} 1130 1131/* 1132 * If o->color_moved is COLOR_MOVED_PLAIN, this function does nothing. 1133 * 1134 * Otherwise, if the last block has fewer alphanumeric characters than 1135 * COLOR_MOVED_MIN_ALNUM_COUNT, unset DIFF_SYMBOL_MOVED_LINE on all lines in 1136 * that block. 1137 * 1138 * The last block consists of the (n - block_length)'th line up to but not 1139 * including the nth line. 1140 * 1141 * Returns 0 if the last block is empty or is unset by this function, non zero 1142 * otherwise. 1143 * 1144 * NEEDSWORK: This uses the same heuristic as blame_entry_score() in blame.c. 1145 * Think of a way to unify them. 1146 */ 1147#define DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK \ 1148 (DIFF_SYMBOL_MOVED_LINE | DIFF_SYMBOL_MOVED_LINE_ALT) 1149static int adjust_last_block(struct diff_options *o, int n, int block_length) 1150{ 1151 int i, alnum_count = 0; 1152 if (o->color_moved == COLOR_MOVED_PLAIN) 1153 return block_length; 1154 for (i = 1; i < block_length + 1; i++) { 1155 const char *c = o->emitted_symbols->buf[n - i].line; 1156 for (; *c; c++) { 1157 if (!isalnum(*c)) 1158 continue; 1159 alnum_count++; 1160 if (alnum_count >= COLOR_MOVED_MIN_ALNUM_COUNT) 1161 return 1; 1162 } 1163 } 1164 for (i = 1; i < block_length + 1; i++) 1165 o->emitted_symbols->buf[n - i].flags &= ~DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK; 1166 return 0; 1167} 1168 1169/* Find blocks of moved code, delegate actual coloring decision to helper */ 1170static void mark_color_as_moved(struct diff_options *o, 1171 struct moved_entry_list *entry_list) 1172{ 1173 struct moved_block *pmb = NULL; /* potentially moved blocks */ 1174 int pmb_nr = 0, pmb_alloc = 0; 1175 int n, flipped_block = 0, block_length = 0; 1176 enum diff_symbol moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER; 1177 1178 1179 for (n = 0; n < o->emitted_symbols->nr; n++) { 1180 struct moved_entry *match = NULL; 1181 struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n]; 1182 1183 switch (l->s) { 1184 case DIFF_SYMBOL_PLUS: 1185 match = entry_list[l->id].del; 1186 break; 1187 case DIFF_SYMBOL_MINUS: 1188 match = entry_list[l->id].add; 1189 break; 1190 default: 1191 flipped_block = 0; 1192 } 1193 1194 if (pmb_nr && (!match || l->s != moved_symbol)) { 1195 if (!adjust_last_block(o, n, block_length) && 1196 block_length > 1) { 1197 /* 1198 * Rewind in case there is another match 1199 * starting at the second line of the block 1200 */ 1201 match = NULL; 1202 n -= block_length; 1203 } 1204 pmb_nr = 0; 1205 block_length = 0; 1206 flipped_block = 0; 1207 } 1208 if (!match) { 1209 moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER; 1210 continue; 1211 } 1212 1213 if (o->color_moved == COLOR_MOVED_PLAIN) { 1214 l->flags |= DIFF_SYMBOL_MOVED_LINE; 1215 continue; 1216 } 1217 1218 pmb_advance_or_null(o, l, pmb, &pmb_nr); 1219 1220 if (pmb_nr == 0) { 1221 int contiguous = adjust_last_block(o, n, block_length); 1222 1223 if (!contiguous && block_length > 1) 1224 /* 1225 * Rewind in case there is another match 1226 * starting at the second line of the block 1227 */ 1228 n -= block_length; 1229 else 1230 fill_potential_moved_blocks(o, match, l, 1231 &pmb, &pmb_alloc, 1232 &pmb_nr); 1233 1234 if (contiguous && pmb_nr && moved_symbol == l->s) 1235 flipped_block = (flipped_block + 1) % 2; 1236 else 1237 flipped_block = 0; 1238 1239 if (pmb_nr) 1240 moved_symbol = l->s; 1241 else 1242 moved_symbol = DIFF_SYMBOL_BINARY_DIFF_HEADER; 1243 1244 block_length = 0; 1245 } 1246 1247 if (pmb_nr) { 1248 block_length++; 1249 l->flags |= DIFF_SYMBOL_MOVED_LINE; 1250 if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS) 1251 l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT; 1252 } 1253 } 1254 adjust_last_block(o, n, block_length); 1255 1256 free(pmb); 1257} 1258 1259static void dim_moved_lines(struct diff_options *o) 1260{ 1261 int n; 1262 for (n = 0; n < o->emitted_symbols->nr; n++) { 1263 struct emitted_diff_symbol *prev = (n != 0) ? 1264 &o->emitted_symbols->buf[n - 1] : NULL; 1265 struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n]; 1266 struct emitted_diff_symbol *next = 1267 (n < o->emitted_symbols->nr - 1) ? 1268 &o->emitted_symbols->buf[n + 1] : NULL; 1269 1270 /* Not a plus or minus line? */ 1271 if (l->s != DIFF_SYMBOL_PLUS && l->s != DIFF_SYMBOL_MINUS) 1272 continue; 1273 1274 /* Not a moved line? */ 1275 if (!(l->flags & DIFF_SYMBOL_MOVED_LINE)) 1276 continue; 1277 1278 /* 1279 * If prev or next are not a plus or minus line, 1280 * pretend they don't exist 1281 */ 1282 if (prev && prev->s != DIFF_SYMBOL_PLUS && 1283 prev->s != DIFF_SYMBOL_MINUS) 1284 prev = NULL; 1285 if (next && next->s != DIFF_SYMBOL_PLUS && 1286 next->s != DIFF_SYMBOL_MINUS) 1287 next = NULL; 1288 1289 /* Inside a block? */ 1290 if ((prev && 1291 (prev->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) == 1292 (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK)) && 1293 (next && 1294 (next->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK) == 1295 (l->flags & DIFF_SYMBOL_MOVED_LINE_ZEBRA_MASK))) { 1296 l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING; 1297 continue; 1298 } 1299 1300 /* Check if we are at an interesting bound: */ 1301 if (prev && (prev->flags & DIFF_SYMBOL_MOVED_LINE) && 1302 (prev->flags & DIFF_SYMBOL_MOVED_LINE_ALT) != 1303 (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT)) 1304 continue; 1305 if (next && (next->flags & DIFF_SYMBOL_MOVED_LINE) && 1306 (next->flags & DIFF_SYMBOL_MOVED_LINE_ALT) != 1307 (l->flags & DIFF_SYMBOL_MOVED_LINE_ALT)) 1308 continue; 1309 1310 /* 1311 * The boundary to prev and next are not interesting, 1312 * so this line is not interesting as a whole 1313 */ 1314 l->flags |= DIFF_SYMBOL_MOVED_LINE_UNINTERESTING; 1315 } 1316} 1317 1318static void emit_line_ws_markup(struct diff_options *o, 1319 const char *set_sign, const char *set, 1320 const char *reset, 1321 int sign_index, const char *line, int len, 1322 unsigned ws_rule, int blank_at_eof) 1323{ 1324 const char *ws = NULL; 1325 int sign = o->output_indicators[sign_index]; 1326 1327 if (o->ws_error_highlight & ws_rule) { 1328 ws = diff_get_color_opt(o, DIFF_WHITESPACE); 1329 if (!*ws) 1330 ws = NULL; 1331 } 1332 1333 if (!ws && !set_sign) 1334 emit_line_0(o, set, NULL, 0, reset, sign, line, len); 1335 else if (!ws) { 1336 emit_line_0(o, set_sign, set, !!set_sign, reset, sign, line, len); 1337 } else if (blank_at_eof) 1338 /* Blank line at EOF - paint '+' as well */ 1339 emit_line_0(o, ws, NULL, 0, reset, sign, line, len); 1340 else { 1341 /* Emit just the prefix, then the rest. */ 1342 emit_line_0(o, set_sign ? set_sign : set, NULL, !!set_sign, reset, 1343 sign, "", 0); 1344 ws_check_emit(line, len, ws_rule, 1345 o->file, set, reset, ws); 1346 } 1347} 1348 1349static void emit_diff_symbol_from_struct(struct diff_options *o, 1350 struct emitted_diff_symbol *eds) 1351{ 1352 static const char *nneof = " No newline at end of file\n"; 1353 const char *context, *reset, *set, *set_sign, *meta, *fraginfo; 1354 1355 enum diff_symbol s = eds->s; 1356 const char *line = eds->line; 1357 int len = eds->len; 1358 unsigned flags = eds->flags; 1359 1360 switch (s) { 1361 case DIFF_SYMBOL_NO_LF_EOF: 1362 context = diff_get_color_opt(o, DIFF_CONTEXT); 1363 reset = diff_get_color_opt(o, DIFF_RESET); 1364 putc('\n', o->file); 1365 emit_line_0(o, context, NULL, 0, reset, '\\', 1366 nneof, strlen(nneof)); 1367 break; 1368 case DIFF_SYMBOL_SUBMODULE_HEADER: 1369 case DIFF_SYMBOL_SUBMODULE_ERROR: 1370 case DIFF_SYMBOL_SUBMODULE_PIPETHROUGH: 1371 case DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES: 1372 case DIFF_SYMBOL_SUMMARY: 1373 case DIFF_SYMBOL_STATS_LINE: 1374 case DIFF_SYMBOL_BINARY_DIFF_BODY: 1375 case DIFF_SYMBOL_CONTEXT_FRAGINFO: 1376 emit_line(o, "", "", line, len); 1377 break; 1378 case DIFF_SYMBOL_CONTEXT_INCOMPLETE: 1379 case DIFF_SYMBOL_CONTEXT_MARKER: 1380 context = diff_get_color_opt(o, DIFF_CONTEXT); 1381 reset = diff_get_color_opt(o, DIFF_RESET); 1382 emit_line(o, context, reset, line, len); 1383 break; 1384 case DIFF_SYMBOL_SEPARATOR: 1385 fprintf(o->file, "%s%c", 1386 diff_line_prefix(o), 1387 o->line_termination); 1388 break; 1389 case DIFF_SYMBOL_CONTEXT: 1390 set = diff_get_color_opt(o, DIFF_CONTEXT); 1391 reset = diff_get_color_opt(o, DIFF_RESET); 1392 set_sign = NULL; 1393 if (o->flags.dual_color_diffed_diffs) { 1394 char c = !len ? 0 : line[0]; 1395 1396 if (c == '+') 1397 set = diff_get_color_opt(o, DIFF_FILE_NEW); 1398 else if (c == '@') 1399 set = diff_get_color_opt(o, DIFF_FRAGINFO); 1400 else if (c == '-') 1401 set = diff_get_color_opt(o, DIFF_FILE_OLD); 1402 } 1403 emit_line_ws_markup(o, set_sign, set, reset, 1404 OUTPUT_INDICATOR_CONTEXT, line, len, 1405 flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0); 1406 break; 1407 case DIFF_SYMBOL_PLUS: 1408 switch (flags & (DIFF_SYMBOL_MOVED_LINE | 1409 DIFF_SYMBOL_MOVED_LINE_ALT | 1410 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) { 1411 case DIFF_SYMBOL_MOVED_LINE | 1412 DIFF_SYMBOL_MOVED_LINE_ALT | 1413 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING: 1414 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT_DIM); 1415 break; 1416 case DIFF_SYMBOL_MOVED_LINE | 1417 DIFF_SYMBOL_MOVED_LINE_ALT: 1418 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_ALT); 1419 break; 1420 case DIFF_SYMBOL_MOVED_LINE | 1421 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING: 1422 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED_DIM); 1423 break; 1424 case DIFF_SYMBOL_MOVED_LINE: 1425 set = diff_get_color_opt(o, DIFF_FILE_NEW_MOVED); 1426 break; 1427 default: 1428 set = diff_get_color_opt(o, DIFF_FILE_NEW); 1429 } 1430 reset = diff_get_color_opt(o, DIFF_RESET); 1431 if (!o->flags.dual_color_diffed_diffs) 1432 set_sign = NULL; 1433 else { 1434 char c = !len ? 0 : line[0]; 1435 1436 set_sign = set; 1437 if (c == '-') 1438 set = diff_get_color_opt(o, DIFF_FILE_OLD_BOLD); 1439 else if (c == '@') 1440 set = diff_get_color_opt(o, DIFF_FRAGINFO); 1441 else if (c == '+') 1442 set = diff_get_color_opt(o, DIFF_FILE_NEW_BOLD); 1443 else 1444 set = diff_get_color_opt(o, DIFF_CONTEXT_BOLD); 1445 flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK; 1446 } 1447 emit_line_ws_markup(o, set_sign, set, reset, 1448 OUTPUT_INDICATOR_NEW, line, len, 1449 flags & DIFF_SYMBOL_CONTENT_WS_MASK, 1450 flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF); 1451 break; 1452 case DIFF_SYMBOL_MINUS: 1453 switch (flags & (DIFF_SYMBOL_MOVED_LINE | 1454 DIFF_SYMBOL_MOVED_LINE_ALT | 1455 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING)) { 1456 case DIFF_SYMBOL_MOVED_LINE | 1457 DIFF_SYMBOL_MOVED_LINE_ALT | 1458 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING: 1459 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT_DIM); 1460 break; 1461 case DIFF_SYMBOL_MOVED_LINE | 1462 DIFF_SYMBOL_MOVED_LINE_ALT: 1463 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_ALT); 1464 break; 1465 case DIFF_SYMBOL_MOVED_LINE | 1466 DIFF_SYMBOL_MOVED_LINE_UNINTERESTING: 1467 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED_DIM); 1468 break; 1469 case DIFF_SYMBOL_MOVED_LINE: 1470 set = diff_get_color_opt(o, DIFF_FILE_OLD_MOVED); 1471 break; 1472 default: 1473 set = diff_get_color_opt(o, DIFF_FILE_OLD); 1474 } 1475 reset = diff_get_color_opt(o, DIFF_RESET); 1476 if (!o->flags.dual_color_diffed_diffs) 1477 set_sign = NULL; 1478 else { 1479 char c = !len ? 0 : line[0]; 1480 1481 set_sign = set; 1482 if (c == '+') 1483 set = diff_get_color_opt(o, DIFF_FILE_NEW_DIM); 1484 else if (c == '@') 1485 set = diff_get_color_opt(o, DIFF_FRAGINFO); 1486 else if (c == '-') 1487 set = diff_get_color_opt(o, DIFF_FILE_OLD_DIM); 1488 else 1489 set = diff_get_color_opt(o, DIFF_CONTEXT_DIM); 1490 } 1491 emit_line_ws_markup(o, set_sign, set, reset, 1492 OUTPUT_INDICATOR_OLD, line, len, 1493 flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0); 1494 break; 1495 case DIFF_SYMBOL_WORDS_PORCELAIN: 1496 context = diff_get_color_opt(o, DIFF_CONTEXT); 1497 reset = diff_get_color_opt(o, DIFF_RESET); 1498 emit_line(o, context, reset, line, len); 1499 fputs("~\n", o->file); 1500 break; 1501 case DIFF_SYMBOL_WORDS: 1502 context = diff_get_color_opt(o, DIFF_CONTEXT); 1503 reset = diff_get_color_opt(o, DIFF_RESET); 1504 /* 1505 * Skip the prefix character, if any. With 1506 * diff_suppress_blank_empty, there may be 1507 * none. 1508 */ 1509 if (line[0] != '\n') { 1510 line++; 1511 len--; 1512 } 1513 emit_line(o, context, reset, line, len); 1514 break; 1515 case DIFF_SYMBOL_FILEPAIR_PLUS: 1516 meta = diff_get_color_opt(o, DIFF_METAINFO); 1517 reset = diff_get_color_opt(o, DIFF_RESET); 1518 fprintf(o->file, "%s%s+++ %s%s%s\n", diff_line_prefix(o), meta, 1519 line, reset, 1520 strchr(line, ' ') ? "\t" : ""); 1521 break; 1522 case DIFF_SYMBOL_FILEPAIR_MINUS: 1523 meta = diff_get_color_opt(o, DIFF_METAINFO); 1524 reset = diff_get_color_opt(o, DIFF_RESET); 1525 fprintf(o->file, "%s%s--- %s%s%s\n", diff_line_prefix(o), meta, 1526 line, reset, 1527 strchr(line, ' ') ? "\t" : ""); 1528 break; 1529 case DIFF_SYMBOL_BINARY_FILES: 1530 case DIFF_SYMBOL_HEADER: 1531 fprintf(o->file, "%s", line); 1532 break; 1533 case DIFF_SYMBOL_BINARY_DIFF_HEADER: 1534 fprintf(o->file, "%sGIT binary patch\n", diff_line_prefix(o)); 1535 break; 1536 case DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA: 1537 fprintf(o->file, "%sdelta %s\n", diff_line_prefix(o), line); 1538 break; 1539 case DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL: 1540 fprintf(o->file, "%sliteral %s\n", diff_line_prefix(o), line); 1541 break; 1542 case DIFF_SYMBOL_BINARY_DIFF_FOOTER: 1543 fputs(diff_line_prefix(o), o->file); 1544 fputc('\n', o->file); 1545 break; 1546 case DIFF_SYMBOL_REWRITE_DIFF: 1547 fraginfo = diff_get_color(o->use_color, DIFF_FRAGINFO); 1548 reset = diff_get_color_opt(o, DIFF_RESET); 1549 emit_line(o, fraginfo, reset, line, len); 1550 break; 1551 case DIFF_SYMBOL_SUBMODULE_ADD: 1552 set = diff_get_color_opt(o, DIFF_FILE_NEW); 1553 reset = diff_get_color_opt(o, DIFF_RESET); 1554 emit_line(o, set, reset, line, len); 1555 break; 1556 case DIFF_SYMBOL_SUBMODULE_DEL: 1557 set = diff_get_color_opt(o, DIFF_FILE_OLD); 1558 reset = diff_get_color_opt(o, DIFF_RESET); 1559 emit_line(o, set, reset, line, len); 1560 break; 1561 case DIFF_SYMBOL_SUBMODULE_UNTRACKED: 1562 fprintf(o->file, "%sSubmodule %s contains untracked content\n", 1563 diff_line_prefix(o), line); 1564 break; 1565 case DIFF_SYMBOL_SUBMODULE_MODIFIED: 1566 fprintf(o->file, "%sSubmodule %s contains modified content\n", 1567 diff_line_prefix(o), line); 1568 break; 1569 case DIFF_SYMBOL_STATS_SUMMARY_NO_FILES: 1570 emit_line(o, "", "", " 0 files changed\n", 1571 strlen(" 0 files changed\n")); 1572 break; 1573 case DIFF_SYMBOL_STATS_SUMMARY_ABBREV: 1574 emit_line(o, "", "", " ...\n", strlen(" ...\n")); 1575 break; 1576 case DIFF_SYMBOL_WORD_DIFF: 1577 fprintf(o->file, "%.*s", len, line); 1578 break; 1579 case DIFF_SYMBOL_STAT_SEP: 1580 fputs(o->stat_sep, o->file); 1581 break; 1582 default: 1583 BUG("unknown diff symbol"); 1584 } 1585} 1586 1587static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s, 1588 const char *line, int len, unsigned flags) 1589{ 1590 struct emitted_diff_symbol e = { 1591 .line = line, .len = len, .flags = flags, .s = s 1592 }; 1593 1594 if (o->emitted_symbols) 1595 append_emitted_diff_symbol(o, &e); 1596 else 1597 emit_diff_symbol_from_struct(o, &e); 1598} 1599 1600void diff_emit_submodule_del(struct diff_options *o, const char *line) 1601{ 1602 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_DEL, line, strlen(line), 0); 1603} 1604 1605void diff_emit_submodule_add(struct diff_options *o, const char *line) 1606{ 1607 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ADD, line, strlen(line), 0); 1608} 1609 1610void diff_emit_submodule_untracked(struct diff_options *o, const char *path) 1611{ 1612 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_UNTRACKED, 1613 path, strlen(path), 0); 1614} 1615 1616void diff_emit_submodule_modified(struct diff_options *o, const char *path) 1617{ 1618 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_MODIFIED, 1619 path, strlen(path), 0); 1620} 1621 1622void diff_emit_submodule_header(struct diff_options *o, const char *header) 1623{ 1624 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_HEADER, 1625 header, strlen(header), 0); 1626} 1627 1628void diff_emit_submodule_error(struct diff_options *o, const char *err) 1629{ 1630 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_ERROR, err, strlen(err), 0); 1631} 1632 1633void diff_emit_submodule_pipethrough(struct diff_options *o, 1634 const char *line, int len) 1635{ 1636 emit_diff_symbol(o, DIFF_SYMBOL_SUBMODULE_PIPETHROUGH, line, len, 0); 1637} 1638 1639static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len) 1640{ 1641 if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) && 1642 ecbdata->blank_at_eof_in_preimage && 1643 ecbdata->blank_at_eof_in_postimage && 1644 ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage && 1645 ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage)) 1646 return 0; 1647 return ws_blank_line(line, len); 1648} 1649 1650static void emit_add_line(struct emit_callback *ecbdata, 1651 const char *line, int len) 1652{ 1653 unsigned flags = WSEH_NEW | ecbdata->ws_rule; 1654 if (new_blank_line_at_eof(ecbdata, line, len)) 1655 flags |= DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF; 1656 1657 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags); 1658} 1659 1660static void emit_del_line(struct emit_callback *ecbdata, 1661 const char *line, int len) 1662{ 1663 unsigned flags = WSEH_OLD | ecbdata->ws_rule; 1664 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags); 1665} 1666 1667static void emit_context_line(struct emit_callback *ecbdata, 1668 const char *line, int len) 1669{ 1670 unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule; 1671 emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_CONTEXT, line, len, flags); 1672} 1673 1674static void emit_hunk_header(struct emit_callback *ecbdata, 1675 const char *line, int len) 1676{ 1677 const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT); 1678 const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO); 1679 const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO); 1680 const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET); 1681 const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : ""; 1682 static const char atat[2] = { '@', '@' }; 1683 const char *cp, *ep; 1684 struct strbuf msgbuf = STRBUF_INIT; 1685 int org_len = len; 1686 int i = 1; 1687 1688 /* 1689 * As a hunk header must begin with "@@ -<old>, +<new> @@", 1690 * it always is at least 10 bytes long. 1691 */ 1692 if (len < 10 || 1693 memcmp(line, atat, 2) || 1694 !(ep = memmem(line + 2, len - 2, atat, 2))) { 1695 emit_diff_symbol(ecbdata->opt, 1696 DIFF_SYMBOL_CONTEXT_MARKER, line, len, 0); 1697 return; 1698 } 1699 ep += 2; /* skip over @@ */ 1700 1701 /* The hunk header in fraginfo color */ 1702 if (ecbdata->opt->flags.dual_color_diffed_diffs) 1703 strbuf_addstr(&msgbuf, reverse); 1704 strbuf_addstr(&msgbuf, frag); 1705 if (ecbdata->opt->flags.suppress_hunk_header_line_count) 1706 strbuf_add(&msgbuf, atat, sizeof(atat)); 1707 else 1708 strbuf_add(&msgbuf, line, ep - line); 1709 strbuf_addstr(&msgbuf, reset); 1710 1711 /* 1712 * trailing "\r\n" 1713 */ 1714 for ( ; i < 3; i++) 1715 if (line[len - i] == '\r' || line[len - i] == '\n') 1716 len--; 1717 1718 /* blank before the func header */ 1719 for (cp = ep; ep - line < len; ep++) 1720 if (*ep != ' ' && *ep != '\t') 1721 break; 1722 if (ep != cp) { 1723 strbuf_addstr(&msgbuf, context); 1724 strbuf_add(&msgbuf, cp, ep - cp); 1725 strbuf_addstr(&msgbuf, reset); 1726 } 1727 1728 if (ep < line + len) { 1729 strbuf_addstr(&msgbuf, func); 1730 strbuf_add(&msgbuf, ep, line + len - ep); 1731 strbuf_addstr(&msgbuf, reset); 1732 } 1733 1734 strbuf_add(&msgbuf, line + len, org_len - len); 1735 strbuf_complete_line(&msgbuf); 1736 emit_diff_symbol(ecbdata->opt, 1737 DIFF_SYMBOL_CONTEXT_FRAGINFO, msgbuf.buf, msgbuf.len, 0); 1738 strbuf_release(&msgbuf); 1739} 1740 1741static struct diff_tempfile *claim_diff_tempfile(void) 1742{ 1743 int i; 1744 for (i = 0; i < ARRAY_SIZE(diff_temp); i++) 1745 if (!diff_temp[i].name) 1746 return diff_temp + i; 1747 BUG("diff is failing to clean up its tempfiles"); 1748} 1749 1750static void remove_tempfile(void) 1751{ 1752 int i; 1753 for (i = 0; i < ARRAY_SIZE(diff_temp); i++) { 1754 if (is_tempfile_active(diff_temp[i].tempfile)) 1755 delete_tempfile(&diff_temp[i].tempfile); 1756 diff_temp[i].name = NULL; 1757 } 1758} 1759 1760static void add_line_count(struct strbuf *out, int count) 1761{ 1762 switch (count) { 1763 case 0: 1764 strbuf_addstr(out, "0,0"); 1765 break; 1766 case 1: 1767 strbuf_addstr(out, "1"); 1768 break; 1769 default: 1770 strbuf_addf(out, "1,%d", count); 1771 break; 1772 } 1773} 1774 1775static void emit_rewrite_lines(struct emit_callback *ecb, 1776 int prefix, const char *data, int size) 1777{ 1778 const char *endp = NULL; 1779 1780 while (0 < size) { 1781 int len; 1782 1783 endp = memchr(data, '\n', size); 1784 len = endp ? (endp - data + 1) : size; 1785 if (prefix != '+') { 1786 ecb->lno_in_preimage++; 1787 emit_del_line(ecb, data, len); 1788 } else { 1789 ecb->lno_in_postimage++; 1790 emit_add_line(ecb, data, len); 1791 } 1792 size -= len; 1793 data += len; 1794 } 1795 if (!endp) 1796 emit_diff_symbol(ecb->opt, DIFF_SYMBOL_NO_LF_EOF, NULL, 0, 0); 1797} 1798 1799static void emit_rewrite_diff(const char *name_a, 1800 const char *name_b, 1801 struct diff_filespec *one, 1802 struct diff_filespec *two, 1803 struct userdiff_driver *textconv_one, 1804 struct userdiff_driver *textconv_two, 1805 struct diff_options *o) 1806{ 1807 int lc_a, lc_b; 1808 static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT; 1809 const char *a_prefix, *b_prefix; 1810 char *data_one, *data_two; 1811 size_t size_one, size_two; 1812 struct emit_callback ecbdata; 1813 struct strbuf out = STRBUF_INIT; 1814 1815 if (diff_mnemonic_prefix && o->flags.reverse_diff) { 1816 a_prefix = o->b_prefix; 1817 b_prefix = o->a_prefix; 1818 } else { 1819 a_prefix = o->a_prefix; 1820 b_prefix = o->b_prefix; 1821 } 1822 1823 name_a += (*name_a == '/'); 1824 name_b += (*name_b == '/'); 1825 1826 strbuf_reset(&a_name); 1827 strbuf_reset(&b_name); 1828 quote_two_c_style(&a_name, a_prefix, name_a, 0); 1829 quote_two_c_style(&b_name, b_prefix, name_b, 0); 1830 1831 size_one = fill_textconv(o->repo, textconv_one, one, &data_one); 1832 size_two = fill_textconv(o->repo, textconv_two, two, &data_two); 1833 1834 memset(&ecbdata, 0, sizeof(ecbdata)); 1835 ecbdata.color_diff = want_color(o->use_color); 1836 ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b); 1837 ecbdata.opt = o; 1838 if (ecbdata.ws_rule & WS_BLANK_AT_EOF) { 1839 mmfile_t mf1, mf2; 1840 mf1.ptr = (char *)data_one; 1841 mf2.ptr = (char *)data_two; 1842 mf1.size = size_one; 1843 mf2.size = size_two; 1844 check_blank_at_eof(&mf1, &mf2, &ecbdata); 1845 } 1846 ecbdata.lno_in_preimage = 1; 1847 ecbdata.lno_in_postimage = 1; 1848 1849 lc_a = count_lines(data_one, size_one); 1850 lc_b = count_lines(data_two, size_two); 1851 1852 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS, 1853 a_name.buf, a_name.len, 0); 1854 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS, 1855 b_name.buf, b_name.len, 0); 1856 1857 strbuf_addstr(&out, "@@ -"); 1858 if (!o->irreversible_delete) 1859 add_line_count(&out, lc_a); 1860 else 1861 strbuf_addstr(&out, "?,?"); 1862 strbuf_addstr(&out, " +"); 1863 add_line_count(&out, lc_b); 1864 strbuf_addstr(&out, " @@\n"); 1865 emit_diff_symbol(o, DIFF_SYMBOL_REWRITE_DIFF, out.buf, out.len, 0); 1866 strbuf_release(&out); 1867 1868 if (lc_a && !o->irreversible_delete) 1869 emit_rewrite_lines(&ecbdata, '-', data_one, size_one); 1870 if (lc_b) 1871 emit_rewrite_lines(&ecbdata, '+', data_two, size_two); 1872 if (textconv_one) 1873 free((char *)data_one); 1874 if (textconv_two) 1875 free((char *)data_two); 1876} 1877 1878struct diff_words_buffer { 1879 mmfile_t text; 1880 unsigned long alloc; 1881 struct diff_words_orig { 1882 const char *begin, *end; 1883 } *orig; 1884 int orig_nr, orig_alloc; 1885}; 1886 1887static void diff_words_append(char *line, unsigned long len, 1888 struct diff_words_buffer *buffer) 1889{ 1890 ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc); 1891 line++; 1892 len--; 1893 memcpy(buffer->text.ptr + buffer->text.size, line, len); 1894 buffer->text.size += len; 1895 buffer->text.ptr[buffer->text.size] = '\0'; 1896} 1897 1898struct diff_words_style_elem { 1899 const char *prefix; 1900 const char *suffix; 1901 const char *color; /* NULL; filled in by the setup code if 1902 * color is enabled */ 1903}; 1904 1905struct diff_words_style { 1906 enum diff_words_type type; 1907 struct diff_words_style_elem new_word, old_word, ctx; 1908 const char *newline; 1909}; 1910 1911static struct diff_words_style diff_words_styles[] = { 1912 { DIFF_WORDS_PORCELAIN, {"+", "\n"}, {"-", "\n"}, {" ", "\n"}, "~\n" }, 1913 { DIFF_WORDS_PLAIN, {"{+", "+}"}, {"[-", "-]"}, {"", ""}, "\n" }, 1914 { DIFF_WORDS_COLOR, {"", ""}, {"", ""}, {"", ""}, "\n" } 1915}; 1916 1917struct diff_words_data { 1918 struct diff_words_buffer minus, plus; 1919 const char *current_plus; 1920 int last_minus; 1921 struct diff_options *opt; 1922 regex_t *word_regex; 1923 enum diff_words_type type; 1924 struct diff_words_style *style; 1925}; 1926 1927static int fn_out_diff_words_write_helper(struct diff_options *o, 1928 struct diff_words_style_elem *st_el, 1929 const char *newline, 1930 size_t count, const char *buf) 1931{ 1932 int print = 0; 1933 struct strbuf sb = STRBUF_INIT; 1934 1935 while (count) { 1936 char *p = memchr(buf, '\n', count); 1937 if (print) 1938 strbuf_addstr(&sb, diff_line_prefix(o)); 1939 1940 if (p != buf) { 1941 const char *reset = st_el->color && *st_el->color ? 1942 GIT_COLOR_RESET : NULL; 1943 if (st_el->color && *st_el->color) 1944 strbuf_addstr(&sb, st_el->color); 1945 strbuf_addstr(&sb, st_el->prefix); 1946 strbuf_add(&sb, buf, p ? p - buf : count); 1947 strbuf_addstr(&sb, st_el->suffix); 1948 if (reset) 1949 strbuf_addstr(&sb, reset); 1950 } 1951 if (!p) 1952 goto out; 1953 1954 strbuf_addstr(&sb, newline); 1955 count -= p + 1 - buf; 1956 buf = p + 1; 1957 print = 1; 1958 if (count) { 1959 emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF, 1960 sb.buf, sb.len, 0); 1961 strbuf_reset(&sb); 1962 } 1963 } 1964 1965out: 1966 if (sb.len) 1967 emit_diff_symbol(o, DIFF_SYMBOL_WORD_DIFF, 1968 sb.buf, sb.len, 0); 1969 strbuf_release(&sb); 1970 return 0; 1971} 1972 1973/* 1974 * '--color-words' algorithm can be described as: 1975 * 1976 * 1. collect the minus/plus lines of a diff hunk, divided into 1977 * minus-lines and plus-lines; 1978 * 1979 * 2. break both minus-lines and plus-lines into words and 1980 * place them into two mmfile_t with one word for each line; 1981 * 1982 * 3. use xdiff to run diff on the two mmfile_t to get the words level diff; 1983 * 1984 * And for the common parts of the both file, we output the plus side text. 1985 * diff_words->current_plus is used to trace the current position of the plus file 1986 * which printed. diff_words->last_minus is used to trace the last minus word 1987 * printed. 1988 * 1989 * For '--graph' to work with '--color-words', we need to output the graph prefix 1990 * on each line of color words output. Generally, there are two conditions on 1991 * which we should output the prefix. 1992 * 1993 * 1. diff_words->last_minus == 0 && 1994 * diff_words->current_plus == diff_words->plus.text.ptr 1995 * 1996 * that is: the plus text must start as a new line, and if there is no minus 1997 * word printed, a graph prefix must be printed. 1998 * 1999 * 2. diff_words->current_plus > diff_words->plus.text.ptr && 2000 * *(diff_words->current_plus - 1) == '\n' 2001 * 2002 * that is: a graph prefix must be printed following a '\n' 2003 */ 2004static int color_words_output_graph_prefix(struct diff_words_data *diff_words) 2005{ 2006 if ((diff_words->last_minus == 0 && 2007 diff_words->current_plus == diff_words->plus.text.ptr) || 2008 (diff_words->current_plus > diff_words->plus.text.ptr && 2009 *(diff_words->current_plus - 1) == '\n')) { 2010 return 1; 2011 } else { 2012 return 0; 2013 } 2014} 2015 2016static void fn_out_diff_words_aux(void *priv, 2017 long minus_first, long minus_len, 2018 long plus_first, long plus_len, 2019 const char *func UNUSED, long funclen UNUSED) 2020{ 2021 struct diff_words_data *diff_words = priv; 2022 struct diff_words_style *style = diff_words->style; 2023 const char *minus_begin, *minus_end, *plus_begin, *plus_end; 2024 struct diff_options *opt = diff_words->opt; 2025 const char *line_prefix; 2026 2027 assert(opt); 2028 line_prefix = diff_line_prefix(opt); 2029 2030 /* POSIX requires that first be decremented by one if len == 0... */ 2031 if (minus_len) { 2032 minus_begin = diff_words->minus.orig[minus_first].begin; 2033 minus_end = 2034 diff_words->minus.orig[minus_first + minus_len - 1].end; 2035 } else 2036 minus_begin = minus_end = 2037 diff_words->minus.orig[minus_first].end; 2038 2039 if (plus_len) { 2040 plus_begin = diff_words->plus.orig[plus_first].begin; 2041 plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end; 2042 } else 2043 plus_begin = plus_end = diff_words->plus.orig[plus_first].end; 2044 2045 if (color_words_output_graph_prefix(diff_words)) { 2046 fputs(line_prefix, diff_words->opt->file); 2047 } 2048 if (diff_words->current_plus != plus_begin) { 2049 fn_out_diff_words_write_helper(diff_words->opt, 2050 &style->ctx, style->newline, 2051 plus_begin - diff_words->current_plus, 2052 diff_words->current_plus); 2053 } 2054 if (minus_begin != minus_end) { 2055 fn_out_diff_words_write_helper(diff_words->opt, 2056 &style->old_word, style->newline, 2057 minus_end - minus_begin, minus_begin); 2058 } 2059 if (plus_begin != plus_end) { 2060 fn_out_diff_words_write_helper(diff_words->opt, 2061 &style->new_word, style->newline, 2062 plus_end - plus_begin, plus_begin); 2063 } 2064 2065 diff_words->current_plus = plus_end; 2066 diff_words->last_minus = minus_first; 2067} 2068 2069/* This function starts looking at *begin, and returns 0 iff a word was found. */ 2070static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex, 2071 int *begin, int *end) 2072{ 2073 while (word_regex && *begin < buffer->size) { 2074 regmatch_t match[1]; 2075 if (!regexec_buf(word_regex, buffer->ptr + *begin, 2076 buffer->size - *begin, 1, match, 0)) { 2077 char *p = memchr(buffer->ptr + *begin + match[0].rm_so, 2078 '\n', match[0].rm_eo - match[0].rm_so); 2079 *end = p ? p - buffer->ptr : match[0].rm_eo + *begin; 2080 *begin += match[0].rm_so; 2081 if (*begin == *end) 2082 (*begin)++; 2083 else 2084 return *begin > *end; 2085 } else { 2086 return -1; 2087 } 2088 } 2089 2090 /* find the next word */ 2091 while (*begin < buffer->size && isspace(buffer->ptr[*begin])) 2092 (*begin)++; 2093 if (*begin >= buffer->size) 2094 return -1; 2095 2096 /* find the end of the word */ 2097 *end = *begin + 1; 2098 while (*end < buffer->size && !isspace(buffer->ptr[*end])) 2099 (*end)++; 2100 2101 return 0; 2102} 2103 2104/* 2105 * This function splits the words in buffer->text, stores the list with 2106 * newline separator into out, and saves the offsets of the original words 2107 * in buffer->orig. 2108 */ 2109static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out, 2110 regex_t *word_regex) 2111{ 2112 int i, j; 2113 long alloc = 0; 2114 2115 out->size = 0; 2116 out->ptr = NULL; 2117 2118 /* fake an empty "0th" word */ 2119 ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc); 2120 buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr; 2121 buffer->orig_nr = 1; 2122 2123 for (i = 0; i < buffer->text.size; i++) { 2124 if (find_word_boundaries(&buffer->text, word_regex, &i, &j)) 2125 return; 2126 2127 /* store original boundaries */ 2128 ALLOC_GROW(buffer->orig, buffer->orig_nr + 1, 2129 buffer->orig_alloc); 2130 buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i; 2131 buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j; 2132 buffer->orig_nr++; 2133 2134 /* store one word */ 2135 ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc); 2136 memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i); 2137 out->ptr[out->size + j - i] = '\n'; 2138 out->size += j - i + 1; 2139 2140 i = j - 1; 2141 } 2142} 2143 2144/* this executes the word diff on the accumulated buffers */ 2145static void diff_words_show(struct diff_words_data *diff_words) 2146{ 2147 xpparam_t xpp; 2148 xdemitconf_t xecfg; 2149 mmfile_t minus, plus; 2150 struct diff_words_style *style = diff_words->style; 2151 2152 struct diff_options *opt = diff_words->opt; 2153 const char *line_prefix; 2154 2155 assert(opt); 2156 line_prefix = diff_line_prefix(opt); 2157 2158 /* special case: only removal */ 2159 if (!diff_words->plus.text.size) { 2160 emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF, 2161 line_prefix, strlen(line_prefix), 0); 2162 fn_out_diff_words_write_helper(diff_words->opt, 2163 &style->old_word, style->newline, 2164 diff_words->minus.text.size, 2165 diff_words->minus.text.ptr); 2166 diff_words->minus.text.size = 0; 2167 return; 2168 } 2169 2170 diff_words->current_plus = diff_words->plus.text.ptr; 2171 diff_words->last_minus = 0; 2172 2173 memset(&xpp, 0, sizeof(xpp)); 2174 memset(&xecfg, 0, sizeof(xecfg)); 2175 diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex); 2176 diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex); 2177 xpp.flags = 0; 2178 /* as only the hunk header will be parsed, we need a 0-context */ 2179 xecfg.ctxlen = 0; 2180 if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, NULL, 2181 diff_words, &xpp, &xecfg)) 2182 die("unable to generate word diff"); 2183 free(minus.ptr); 2184 free(plus.ptr); 2185 if (diff_words->current_plus != diff_words->plus.text.ptr + 2186 diff_words->plus.text.size) { 2187 if (color_words_output_graph_prefix(diff_words)) 2188 emit_diff_symbol(diff_words->opt, DIFF_SYMBOL_WORD_DIFF, 2189 line_prefix, strlen(line_prefix), 0); 2190 fn_out_diff_words_write_helper(diff_words->opt, 2191 &style->ctx, style->newline, 2192 diff_words->plus.text.ptr + diff_words->plus.text.size 2193 - diff_words->current_plus, diff_words->current_plus); 2194 } 2195 diff_words->minus.text.size = diff_words->plus.text.size = 0; 2196} 2197 2198/* In "color-words" mode, show word-diff of words accumulated in the buffer */ 2199static void diff_words_flush(struct emit_callback *ecbdata) 2200{ 2201 struct diff_options *wo = ecbdata->diff_words->opt; 2202 2203 if (ecbdata->diff_words->minus.text.size || 2204 ecbdata->diff_words->plus.text.size) 2205 diff_words_show(ecbdata->diff_words); 2206 2207 if (wo->emitted_symbols) { 2208 struct diff_options *o = ecbdata->opt; 2209 struct emitted_diff_symbols *wol = wo->emitted_symbols; 2210 int i; 2211 2212 /* 2213 * NEEDSWORK: 2214 * Instead of appending each, concat all words to a line? 2215 */ 2216 for (i = 0; i < wol->nr; i++) 2217 append_emitted_diff_symbol(o, &wol->buf[i]); 2218 2219 for (i = 0; i < wol->nr; i++) 2220 free((void *)wol->buf[i].line); 2221 2222 wol->nr = 0; 2223 } 2224} 2225 2226static void diff_filespec_load_driver(struct diff_filespec *one, 2227 struct index_state *istate) 2228{ 2229 /* Use already-loaded driver */ 2230 if (one->driver) 2231 return; 2232 2233 if (S_ISREG(one->mode)) 2234 one->driver = userdiff_find_by_path(istate, one->path); 2235 2236 /* Fallback to default settings */ 2237 if (!one->driver) 2238 one->driver = userdiff_find_by_name("default"); 2239} 2240 2241static const char *userdiff_word_regex(struct diff_filespec *one, 2242 struct index_state *istate) 2243{ 2244 diff_filespec_load_driver(one, istate); 2245 return one->driver->word_regex; 2246} 2247 2248static void init_diff_words_data(struct emit_callback *ecbdata, 2249 struct diff_options *orig_opts, 2250 struct diff_filespec *one, 2251 struct diff_filespec *two) 2252{ 2253 int i; 2254 struct diff_options *o = xmalloc(sizeof(struct diff_options)); 2255 memcpy(o, orig_opts, sizeof(struct diff_options)); 2256 2257 CALLOC_ARRAY(ecbdata->diff_words, 1); 2258 ecbdata->diff_words->type = o->word_diff; 2259 ecbdata->diff_words->opt = o; 2260 2261 if (orig_opts->emitted_symbols) 2262 CALLOC_ARRAY(o->emitted_symbols, 1); 2263 2264 if (!o->word_regex) 2265 o->word_regex = userdiff_word_regex(one, o->repo->index); 2266 if (!o->word_regex) 2267 o->word_regex = userdiff_word_regex(two, o->repo->index); 2268 if (!o->word_regex) 2269 o->word_regex = diff_word_regex_cfg; 2270 if (o->word_regex) { 2271 ecbdata->diff_words->word_regex = (regex_t *) 2272 xmalloc(sizeof(regex_t)); 2273 if (regcomp(ecbdata->diff_words->word_regex, 2274 o->word_regex, 2275 REG_EXTENDED | REG_NEWLINE)) 2276 die("invalid regular expression: %s", 2277 o->word_regex); 2278 } 2279 for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) { 2280 if (o->word_diff == diff_words_styles[i].type) { 2281 ecbdata->diff_words->style = 2282 &diff_words_styles[i]; 2283 break; 2284 } 2285 } 2286 if (want_color(o->use_color)) { 2287 struct diff_words_style *st = ecbdata->diff_words->style; 2288 st->old_word.color = diff_get_color_opt(o, DIFF_FILE_OLD); 2289 st->new_word.color = diff_get_color_opt(o, DIFF_FILE_NEW); 2290 st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT); 2291 } 2292} 2293 2294static void free_diff_words_data(struct emit_callback *ecbdata) 2295{ 2296 if (ecbdata->diff_words) { 2297 diff_words_flush(ecbdata); 2298 free_emitted_diff_symbols(ecbdata->diff_words->opt->emitted_symbols); 2299 free (ecbdata->diff_words->opt); 2300 free (ecbdata->diff_words->minus.text.ptr); 2301 free (ecbdata->diff_words->minus.orig); 2302 free (ecbdata->diff_words->plus.text.ptr); 2303 free (ecbdata->diff_words->plus.orig); 2304 if (ecbdata->diff_words->word_regex) { 2305 regfree(ecbdata->diff_words->word_regex); 2306 free(ecbdata->diff_words->word_regex); 2307 } 2308 FREE_AND_NULL(ecbdata->diff_words); 2309 } 2310} 2311 2312const char *diff_get_color(int diff_use_color, enum color_diff ix) 2313{ 2314 if (want_color(diff_use_color)) 2315 return diff_colors[ix]; 2316 return ""; 2317} 2318 2319const char *diff_line_prefix(struct diff_options *opt) 2320{ 2321 return opt->output_prefix ? 2322 opt->output_prefix(opt, opt->output_prefix_data) : 2323 ""; 2324} 2325 2326static unsigned long sane_truncate_line(char *line, unsigned long len) 2327{ 2328 const char *cp; 2329 unsigned long allot; 2330 size_t l = len; 2331 2332 cp = line; 2333 allot = l; 2334 while (0 < l) { 2335 (void) utf8_width(&cp, &l); 2336 if (!cp) 2337 break; /* truncated in the middle? */ 2338 } 2339 return allot - l; 2340} 2341 2342static void find_lno(const char *line, struct emit_callback *ecbdata) 2343{ 2344 const char *p; 2345 ecbdata->lno_in_preimage = 0; 2346 ecbdata->lno_in_postimage = 0; 2347 p = strchr(line, '-'); 2348 if (!p) 2349 return; /* cannot happen */ 2350 ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10); 2351 p = strchr(p, '+'); 2352 if (!p) 2353 return; /* cannot happen */ 2354 ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10); 2355} 2356 2357static int fn_out_consume(void *priv, char *line, unsigned long len) 2358{ 2359 struct emit_callback *ecbdata = priv; 2360 struct diff_options *o = ecbdata->opt; 2361 2362 o->found_changes = 1; 2363 2364 if (ecbdata->header) { 2365 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, 2366 ecbdata->header->buf, ecbdata->header->len, 0); 2367 strbuf_reset(ecbdata->header); 2368 ecbdata->header = NULL; 2369 } 2370 2371 if (ecbdata->label_path[0]) { 2372 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_MINUS, 2373 ecbdata->label_path[0], 2374 strlen(ecbdata->label_path[0]), 0); 2375 emit_diff_symbol(o, DIFF_SYMBOL_FILEPAIR_PLUS, 2376 ecbdata->label_path[1], 2377 strlen(ecbdata->label_path[1]), 0); 2378 ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; 2379 } 2380 2381 if (diff_suppress_blank_empty 2382 && len == 2 && line[0] == ' ' && line[1] == '\n') { 2383 line[0] = '\n'; 2384 len = 1; 2385 } 2386 2387 if (line[0] == '@') { 2388 if (ecbdata->diff_words) 2389 diff_words_flush(ecbdata); 2390 len = sane_truncate_line(line, len); 2391 find_lno(line, ecbdata); 2392 emit_hunk_header(ecbdata, line, len); 2393 return 0; 2394 } 2395 2396 if (ecbdata->diff_words) { 2397 enum diff_symbol s = 2398 ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN ? 2399 DIFF_SYMBOL_WORDS_PORCELAIN : DIFF_SYMBOL_WORDS; 2400 if (line[0] == '-') { 2401 diff_words_append(line, len, 2402 &ecbdata->diff_words->minus); 2403 return 0; 2404 } else if (line[0] == '+') { 2405 diff_words_append(line, len, 2406 &ecbdata->diff_words->plus); 2407 return 0; 2408 } else if (starts_with(line, "\\ ")) { 2409 /* 2410 * Eat the "no newline at eof" marker as if we 2411 * saw a "+" or "-" line with nothing on it, 2412 * and return without diff_words_flush() to 2413 * defer processing. If this is the end of 2414 * preimage, more "+" lines may come after it. 2415 */ 2416 return 0; 2417 } 2418 diff_words_flush(ecbdata); 2419 emit_diff_symbol(o, s, line, len, 0); 2420 return 0; 2421 } 2422 2423 switch (line[0]) { 2424 case '+': 2425 ecbdata->lno_in_postimage++; 2426 emit_add_line(ecbdata, line + 1, len - 1); 2427 break; 2428 case '-': 2429 ecbdata->lno_in_preimage++; 2430 emit_del_line(ecbdata, line + 1, len - 1); 2431 break; 2432 case ' ': 2433 ecbdata->lno_in_postimage++; 2434 ecbdata->lno_in_preimage++; 2435 emit_context_line(ecbdata, line + 1, len - 1); 2436 break; 2437 default: 2438 /* incomplete line at the end */ 2439 ecbdata->lno_in_preimage++; 2440 emit_diff_symbol(o, DIFF_SYMBOL_CONTEXT_INCOMPLETE, 2441 line, len, 0); 2442 break; 2443 } 2444 return 0; 2445} 2446 2447static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED) 2448{ 2449 struct emit_callback *ecbdata = priv; 2450 struct diff_options *o = ecbdata->opt; 2451 2452 o->found_changes = 1; 2453 return 1; 2454} 2455 2456static void pprint_rename(struct strbuf *name, const char *a, const char *b) 2457{ 2458 const char *old_name = a; 2459 const char *new_name = b; 2460 int pfx_length, sfx_length; 2461 int pfx_adjust_for_slash; 2462 int len_a = strlen(a); 2463 int len_b = strlen(b); 2464 int a_midlen, b_midlen; 2465 int qlen_a = quote_c_style(a, NULL, NULL, 0); 2466 int qlen_b = quote_c_style(b, NULL, NULL, 0); 2467 2468 if (qlen_a || qlen_b) { 2469 quote_c_style(a, name, NULL, 0); 2470 strbuf_addstr(name, " => "); 2471 quote_c_style(b, name, NULL, 0); 2472 return; 2473 } 2474 2475 /* Find common prefix */ 2476 pfx_length = 0; 2477 while (*old_name && *new_name && *old_name == *new_name) { 2478 if (*old_name == '/') 2479 pfx_length = old_name - a + 1; 2480 old_name++; 2481 new_name++; 2482 } 2483 2484 /* Find common suffix */ 2485 old_name = a + len_a; 2486 new_name = b + len_b; 2487 sfx_length = 0; 2488 /* 2489 * If there is a common prefix, it must end in a slash. In 2490 * that case we let this loop run 1 into the prefix to see the 2491 * same slash. 2492 * 2493 * If there is no common prefix, we cannot do this as it would 2494 * underrun the input strings. 2495 */ 2496 pfx_adjust_for_slash = (pfx_length ? 1 : 0); 2497 while (a + pfx_length - pfx_adjust_for_slash <= old_name && 2498 b + pfx_length - pfx_adjust_for_slash <= new_name && 2499 *old_name == *new_name) { 2500 if (*old_name == '/') 2501 sfx_length = len_a - (old_name - a); 2502 old_name--; 2503 new_name--; 2504 } 2505 2506 /* 2507 * pfx{mid-a => mid-b}sfx 2508 * {pfx-a => pfx-b}sfx 2509 * pfx{sfx-a => sfx-b} 2510 * name-a => name-b 2511 */ 2512 a_midlen = len_a - pfx_length - sfx_length; 2513 b_midlen = len_b - pfx_length - sfx_length; 2514 if (a_midlen < 0) 2515 a_midlen = 0; 2516 if (b_midlen < 0) 2517 b_midlen = 0; 2518 2519 strbuf_grow(name, pfx_length + a_midlen + b_midlen + sfx_length + 7); 2520 if (pfx_length + sfx_length) { 2521 strbuf_add(name, a, pfx_length); 2522 strbuf_addch(name, '{'); 2523 } 2524 strbuf_add(name, a + pfx_length, a_midlen); 2525 strbuf_addstr(name, " => "); 2526 strbuf_add(name, b + pfx_length, b_midlen); 2527 if (pfx_length + sfx_length) { 2528 strbuf_addch(name, '}'); 2529 strbuf_add(name, a + len_a - sfx_length, sfx_length); 2530 } 2531} 2532 2533static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, 2534 const char *name_a, 2535 const char *name_b) 2536{ 2537 struct diffstat_file *x; 2538 CALLOC_ARRAY(x, 1); 2539 ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc); 2540 diffstat->files[diffstat->nr++] = x; 2541 if (name_b) { 2542 x->from_name = xstrdup(name_a); 2543 x->name = xstrdup(name_b); 2544 x->is_renamed = 1; 2545 } 2546 else { 2547 x->from_name = NULL; 2548 x->name = xstrdup(name_a); 2549 } 2550 return x; 2551} 2552 2553static int diffstat_consume(void *priv, char *line, unsigned long len) 2554{ 2555 struct diffstat_t *diffstat = priv; 2556 struct diffstat_file *x = diffstat->files[diffstat->nr - 1]; 2557 2558 if (!len) 2559 BUG("xdiff fed us an empty line"); 2560 2561 if (line[0] == '+') 2562 x->added++; 2563 else if (line[0] == '-') 2564 x->deleted++; 2565 return 0; 2566} 2567 2568const char mime_boundary_leader[] = "------------"; 2569 2570static int scale_linear(int it, int width, int max_change) 2571{ 2572 if (!it) 2573 return 0; 2574 /* 2575 * make sure that at least one '-' or '+' is printed if 2576 * there is any change to this path. The easiest way is to 2577 * scale linearly as if the allotted width is one column shorter 2578 * than it is, and then add 1 to the result. 2579 */ 2580 return 1 + (it * (width - 1) / max_change); 2581} 2582 2583static void show_graph(struct strbuf *out, char ch, int cnt, 2584 const char *set, const char *reset) 2585{ 2586 if (cnt <= 0) 2587 return; 2588 strbuf_addstr(out, set); 2589 strbuf_addchars(out, ch, cnt); 2590 strbuf_addstr(out, reset); 2591} 2592 2593static void fill_print_name(struct diffstat_file *file) 2594{ 2595 struct strbuf pname = STRBUF_INIT; 2596 2597 if (file->print_name) 2598 return; 2599 2600 if (file->is_renamed) 2601 pprint_rename(&pname, file->from_name, file->name); 2602 else 2603 quote_c_style(file->name, &pname, NULL, 0); 2604 2605 if (file->comments) 2606 strbuf_addf(&pname, " (%s)", file->comments); 2607 2608 file->print_name = strbuf_detach(&pname, NULL); 2609} 2610 2611static void print_stat_summary_inserts_deletes(struct diff_options *options, 2612 int files, int insertions, int deletions) 2613{ 2614 struct strbuf sb = STRBUF_INIT; 2615 2616 if (!files) { 2617 assert(insertions == 0 && deletions == 0); 2618 emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_NO_FILES, 2619 NULL, 0, 0); 2620 return; 2621 } 2622 2623 strbuf_addf(&sb, 2624 (files == 1) ? " %d file changed" : " %d files changed", 2625 files); 2626 2627 /* 2628 * For binary diff, the caller may want to print "x files 2629 * changed" with insertions == 0 && deletions == 0. 2630 * 2631 * Not omitting "0 insertions(+), 0 deletions(-)" in this case 2632 * is probably less confusing (i.e skip over "2 files changed 2633 * but nothing about added/removed lines? Is this a bug in Git?"). 2634 */ 2635 if (insertions || deletions == 0) { 2636 strbuf_addf(&sb, 2637 (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)", 2638 insertions); 2639 } 2640 2641 if (deletions || insertions == 0) { 2642 strbuf_addf(&sb, 2643 (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)", 2644 deletions); 2645 } 2646 strbuf_addch(&sb, '\n'); 2647 emit_diff_symbol(options, DIFF_SYMBOL_STATS_SUMMARY_INSERTS_DELETES, 2648 sb.buf, sb.len, 0); 2649 strbuf_release(&sb); 2650} 2651 2652void print_stat_summary(FILE *fp, int files, 2653 int insertions, int deletions) 2654{ 2655 struct diff_options o; 2656 memset(&o, 0, sizeof(o)); 2657 o.file = fp; 2658 2659 print_stat_summary_inserts_deletes(&o, files, insertions, deletions); 2660} 2661 2662static void show_stats(struct diffstat_t *data, struct diff_options *options) 2663{ 2664 int i, len, add, del, adds = 0, dels = 0; 2665 uintmax_t max_change = 0, max_len = 0; 2666 int total_files = data->nr, count; 2667 int width, name_width, graph_width, number_width = 0, bin_width = 0; 2668 const char *reset, *add_c, *del_c; 2669 int extra_shown = 0; 2670 const char *line_prefix = diff_line_prefix(options); 2671 struct strbuf out = STRBUF_INIT; 2672 2673 if (data->nr == 0) 2674 return; 2675 2676 count = options->stat_count ? options->stat_count : data->nr; 2677 2678 reset = diff_get_color_opt(options, DIFF_RESET); 2679 add_c = diff_get_color_opt(options, DIFF_FILE_NEW); 2680 del_c = diff_get_color_opt(options, DIFF_FILE_OLD); 2681 2682 /* 2683 * Find the longest filename and max number of changes 2684 */ 2685 for (i = 0; (i < count) && (i < data->nr); i++) { 2686 struct diffstat_file *file = data->files[i]; 2687 uintmax_t change = file->added + file->deleted; 2688 2689 if (!file->is_interesting && (change == 0)) { 2690 count++; /* not shown == room for one more */ 2691 continue; 2692 } 2693 fill_print_name(file); 2694 len = utf8_strwidth(file->print_name); 2695 if (max_len < len) 2696 max_len = len; 2697 2698 if (file->is_unmerged) { 2699 /* "Unmerged" is 8 characters */ 2700 bin_width = bin_width < 8 ? 8 : bin_width; 2701 continue; 2702 } 2703 if (file->is_binary) { 2704 /* "Bin XXX -> YYY bytes" */ 2705 int w = 14 + decimal_width(file->added) 2706 + decimal_width(file->deleted); 2707 bin_width = bin_width < w ? w : bin_width; 2708 /* Display change counts aligned with "Bin" */ 2709 number_width = 3; 2710 continue; 2711 } 2712 2713 if (max_change < change) 2714 max_change = change; 2715 } 2716 count = i; /* where we can stop scanning in data->files[] */ 2717 2718 /* 2719 * We have width = stat_width or term_columns() columns total. 2720 * We want a maximum of min(max_len, stat_name_width) for the name part. 2721 * We want a maximum of min(max_change, stat_graph_width) for the +- part. 2722 * We also need 1 for " " and 4 + decimal_width(max_change) 2723 * for " | NNNN " and one the empty column at the end, altogether 2724 * 6 + decimal_width(max_change). 2725 * 2726 * If there's not enough space, we will use the smaller of 2727 * stat_name_width (if set) and 5/8*width for the filename, 2728 * and the rest for constant elements + graph part, but no more 2729 * than stat_graph_width for the graph part. 2730 * (5/8 gives 50 for filename and 30 for the constant parts + graph 2731 * for the standard terminal size). 2732 * 2733 * In other words: stat_width limits the maximum width, and 2734 * stat_name_width fixes the maximum width of the filename, 2735 * and is also used to divide available columns if there 2736 * aren't enough. 2737 * 2738 * Binary files are displayed with "Bin XXX -> YYY bytes" 2739 * instead of the change count and graph. This part is treated 2740 * similarly to the graph part, except that it is not 2741 * "scaled". If total width is too small to accommodate the 2742 * guaranteed minimum width of the filename part and the 2743 * separators and this message, this message will "overflow" 2744 * making the line longer than the maximum width. 2745 */ 2746 2747 /* 2748 * NEEDSWORK: line_prefix is often used for "log --graph" output 2749 * and contains ANSI-colored string. utf8_strnwidth() should be 2750 * used to correctly count the display width instead of strlen(). 2751 */ 2752 if (options->stat_width == -1) 2753 width = term_columns() - strlen(line_prefix); 2754 else 2755 width = options->stat_width ? options->stat_width : 80; 2756 number_width = decimal_width(max_change) > number_width ? 2757 decimal_width(max_change) : number_width; 2758 2759 if (options->stat_name_width == -1) 2760 options->stat_name_width = diff_stat_name_width; 2761 if (options->stat_graph_width == -1) 2762 options->stat_graph_width = diff_stat_graph_width; 2763 2764 /* 2765 * Guarantee 3/8*16 == 6 for the graph part 2766 * and 5/8*16 == 10 for the filename part 2767 */ 2768 if (width < 16 + 6 + number_width) 2769 width = 16 + 6 + number_width; 2770 2771 /* 2772 * First assign sizes that are wanted, ignoring available width. 2773 * strlen("Bin XXX -> YYY bytes") == bin_width, and the part 2774 * starting from "XXX" should fit in graph_width. 2775 */ 2776 graph_width = max_change + 4 > bin_width ? max_change : bin_width - 4; 2777 if (options->stat_graph_width && 2778 options->stat_graph_width < graph_width) 2779 graph_width = options->stat_graph_width; 2780 2781 name_width = (options->stat_name_width > 0 && 2782 options->stat_name_width < max_len) ? 2783 options->stat_name_width : max_len; 2784 2785 /* 2786 * Adjust adjustable widths not to exceed maximum width 2787 */ 2788 if (name_width + number_width + 6 + graph_width > width) { 2789 if (graph_width > width * 3/8 - number_width - 6) { 2790 graph_width = width * 3/8 - number_width - 6; 2791 if (graph_width < 6) 2792 graph_width = 6; 2793 } 2794 2795 if (options->stat_graph_width && 2796 graph_width > options->stat_graph_width) 2797 graph_width = options->stat_graph_width; 2798 if (name_width > width - number_width - 6 - graph_width) 2799 name_width = width - number_width - 6 - graph_width; 2800 else 2801 graph_width = width - number_width - 6 - name_width; 2802 } 2803 2804 /* 2805 * From here name_width is the width of the name area, 2806 * and graph_width is the width of the graph area. 2807 * max_change is used to scale graph properly. 2808 */ 2809 for (i = 0; i < count; i++) { 2810 const char *prefix = ""; 2811 struct diffstat_file *file = data->files[i]; 2812 char *name = file->print_name; 2813 uintmax_t added = file->added; 2814 uintmax_t deleted = file->deleted; 2815 int name_len, padding; 2816 2817 if (!file->is_interesting && (added + deleted == 0)) 2818 continue; 2819 2820 /* 2821 * "scale" the filename 2822 */ 2823 len = name_width; 2824 name_len = utf8_strwidth(name); 2825 if (name_width < name_len) { 2826 char *slash; 2827 prefix = "..."; 2828 len -= 3; 2829 /* 2830 * NEEDSWORK: (name_len - len) counts the display 2831 * width, which would be shorter than the byte 2832 * length of the corresponding substring. 2833 * Advancing "name" by that number of bytes does 2834 * *NOT* skip over that many columns, so it is 2835 * very likely that chomping the pathname at the 2836 * slash we will find starting from "name" will 2837 * leave the resulting string still too long. 2838 */ 2839 name += name_len - len; 2840 slash = strchr(name, '/'); 2841 if (slash) 2842 name = slash; 2843 } 2844 padding = len - utf8_strwidth(name); 2845 if (padding < 0) 2846 padding = 0; 2847 2848 if (file->is_binary) { 2849 strbuf_addf(&out, " %s%s%*s | %*s", 2850 prefix, name, padding, "", 2851 number_width, "Bin"); 2852 if (!added && !deleted) { 2853 strbuf_addch(&out, '\n'); 2854 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE, 2855 out.buf, out.len, 0); 2856 strbuf_reset(&out); 2857 continue; 2858 } 2859 strbuf_addf(&out, " %s%"PRIuMAX"%s", 2860 del_c, deleted, reset); 2861 strbuf_addstr(&out, " -> "); 2862 strbuf_addf(&out, "%s%"PRIuMAX"%s", 2863 add_c, added, reset); 2864 strbuf_addstr(&out, " bytes\n"); 2865 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE, 2866 out.buf, out.len, 0); 2867 strbuf_reset(&out); 2868 continue; 2869 } 2870 else if (file->is_unmerged) { 2871 strbuf_addf(&out, " %s%s%*s | %*s", 2872 prefix, name, padding, "", 2873 number_width, "Unmerged\n"); 2874 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE, 2875 out.buf, out.len, 0); 2876 strbuf_reset(&out); 2877 continue; 2878 } 2879 2880 /* 2881 * scale the add/delete 2882 */ 2883 add = added; 2884 del = deleted; 2885 2886 if (graph_width <= max_change) { 2887 int total = scale_linear(add + del, graph_width, max_change); 2888 if (total < 2 && add && del) 2889 /* width >= 2 due to the sanity check */ 2890 total = 2; 2891 if (add < del) { 2892 add = scale_linear(add, graph_width, max_change); 2893 del = total - add; 2894 } else { 2895 del = scale_linear(del, graph_width, max_change); 2896 add = total - del; 2897 } 2898 } 2899 strbuf_addf(&out, " %s%s%*s | %*"PRIuMAX"%s", 2900 prefix, name, padding, "", 2901 number_width, added + deleted, 2902 added + deleted ? " " : ""); 2903 show_graph(&out, '+', add, add_c, reset); 2904 show_graph(&out, '-', del, del_c, reset); 2905 strbuf_addch(&out, '\n'); 2906 emit_diff_symbol(options, DIFF_SYMBOL_STATS_LINE, 2907 out.buf, out.len, 0); 2908 strbuf_reset(&out); 2909 } 2910 2911 for (i = 0; i < data->nr; i++) { 2912 struct diffstat_file *file = data->files[i]; 2913 uintmax_t added = file->added; 2914 uintmax_t deleted = file->deleted; 2915 2916 if (file->is_unmerged || 2917 (!file->is_interesting && (added + deleted == 0))) { 2918 total_files--; 2919 continue; 2920 } 2921 2922 if (!file->is_binary) { 2923 adds += added; 2924 dels += deleted; 2925 } 2926 if (i < count) 2927 continue; 2928 if (!extra_shown) 2929 emit_diff_symbol(options, 2930 DIFF_SYMBOL_STATS_SUMMARY_ABBREV, 2931 NULL, 0, 0); 2932 extra_shown = 1; 2933 } 2934 2935 print_stat_summary_inserts_deletes(options, total_files, adds, dels); 2936 strbuf_release(&out); 2937} 2938 2939static void show_shortstats(struct diffstat_t *data, struct diff_options *options) 2940{ 2941 int i, adds = 0, dels = 0, total_files = data->nr; 2942 2943 if (data->nr == 0) 2944 return; 2945 2946 for (i = 0; i < data->nr; i++) { 2947 int added = data->files[i]->added; 2948 int deleted = data->files[i]->deleted; 2949 2950 if (data->files[i]->is_unmerged || 2951 (!data->files[i]->is_interesting && (added + deleted == 0))) { 2952 total_files--; 2953 } else if (!data->files[i]->is_binary) { /* don't count bytes */ 2954 adds += added; 2955 dels += deleted; 2956 } 2957 } 2958 print_stat_summary_inserts_deletes(options, total_files, adds, dels); 2959} 2960 2961static void show_numstat(struct diffstat_t *data, struct diff_options *options) 2962{ 2963 int i; 2964 2965 if (data->nr == 0) 2966 return; 2967 2968 for (i = 0; i < data->nr; i++) { 2969 struct diffstat_file *file = data->files[i]; 2970 2971 fprintf(options->file, "%s", diff_line_prefix(options)); 2972 2973 if (file->is_binary) 2974 fprintf(options->file, "-\t-\t"); 2975 else 2976 fprintf(options->file, 2977 "%"PRIuMAX"\t%"PRIuMAX"\t", 2978 file->added, file->deleted); 2979 if (options->line_termination) { 2980 fill_print_name(file); 2981 if (!file->is_renamed) 2982 write_name_quoted(file->name, options->file, 2983 options->line_termination); 2984 else { 2985 fputs(file->print_name, options->file); 2986 putc(options->line_termination, options->file); 2987 } 2988 } else { 2989 if (file->is_renamed) { 2990 putc('\0', options->file); 2991 write_name_quoted(file->from_name, options->file, '\0'); 2992 } 2993 write_name_quoted(file->name, options->file, '\0'); 2994 } 2995 } 2996} 2997 2998struct dirstat_file { 2999 const char *name; 3000 unsigned long changed; 3001}; 3002 3003struct dirstat_dir { 3004 struct dirstat_file *files; 3005 int alloc, nr, permille, cumulative; 3006}; 3007 3008static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir, 3009 unsigned long changed, const char *base, int baselen) 3010{ 3011 unsigned long sum_changes = 0; 3012 unsigned int sources = 0; 3013 const char *line_prefix = diff_line_prefix(opt); 3014 3015 while (dir->nr) { 3016 struct dirstat_file *f = dir->files; 3017 int namelen = strlen(f->name); 3018 unsigned long changes; 3019 char *slash; 3020 3021 if (namelen < baselen) 3022 break; 3023 if (memcmp(f->name, base, baselen)) 3024 break; 3025 slash = strchr(f->name + baselen, '/'); 3026 if (slash) { 3027 int newbaselen = slash + 1 - f->name; 3028 changes = gather_dirstat(opt, dir, changed, f->name, newbaselen); 3029 sources++; 3030 } else { 3031 changes = f->changed; 3032 dir->files++; 3033 dir->nr--; 3034 sources += 2; 3035 } 3036 sum_changes += changes; 3037 } 3038 3039 /* 3040 * We don't report dirstat's for 3041 * - the top level 3042 * - or cases where everything came from a single directory 3043 * under this directory (sources == 1). 3044 */ 3045 if (baselen && sources != 1) { 3046 if (sum_changes) { 3047 int permille = sum_changes * 1000 / changed; 3048 if (permille >= dir->permille) { 3049 fprintf(opt->file, "%s%4d.%01d%% %.*s\n", line_prefix, 3050 permille / 10, permille % 10, baselen, base); 3051 if (!dir->cumulative) 3052 return 0; 3053 } 3054 } 3055 } 3056 return sum_changes; 3057} 3058 3059static int dirstat_compare(const void *_a, const void *_b) 3060{ 3061 const struct dirstat_file *a = _a; 3062 const struct dirstat_file *b = _b; 3063 return strcmp(a->name, b->name); 3064} 3065 3066static void conclude_dirstat(struct diff_options *options, 3067 struct dirstat_dir *dir, 3068 unsigned long changed) 3069{ 3070 struct dirstat_file *to_free = dir->files; 3071 3072 if (!changed) { 3073 /* This can happen even with many files, if everything was renames */ 3074 ; 3075 } else { 3076 /* Show all directories with more than x% of the changes */ 3077 QSORT(dir->files, dir->nr, dirstat_compare); 3078 gather_dirstat(options, dir, changed, "", 0); 3079 } 3080 3081 free(to_free); 3082} 3083 3084static void show_dirstat(struct diff_options *options) 3085{ 3086 int i; 3087 unsigned long changed; 3088 struct dirstat_dir dir; 3089 struct diff_queue_struct *q = &diff_queued_diff; 3090 3091 dir.files = NULL; 3092 dir.alloc = 0; 3093 dir.nr = 0; 3094 dir.permille = options->dirstat_permille; 3095 dir.cumulative = options->flags.dirstat_cumulative; 3096 3097 changed = 0; 3098 for (i = 0; i < q->nr; i++) { 3099 struct diff_filepair *p = q->queue[i]; 3100 const char *name; 3101 unsigned long copied, added, damage; 3102 struct diff_populate_filespec_options dpf_options = { 3103 .check_size_only = 1, 3104 }; 3105 3106 name = p->two->path ? p->two->path : p->one->path; 3107 3108 if (p->one->oid_valid && p->two->oid_valid && 3109 oideq(&p->one->oid, &p->two->oid)) { 3110 /* 3111 * The SHA1 has not changed, so pre-/post-content is 3112 * identical. We can therefore skip looking at the 3113 * file contents altogether. 3114 */ 3115 damage = 0; 3116 goto found_damage; 3117 } 3118 3119 if (options->flags.dirstat_by_file) { 3120 /* 3121 * In --dirstat-by-file mode, we don't really need to 3122 * look at the actual file contents at all. 3123 * The fact that the SHA1 changed is enough for us to 3124 * add this file to the list of results 3125 * (with each file contributing equal damage). 3126 */ 3127 damage = 1; 3128 goto found_damage; 3129 } 3130 3131 if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { 3132 diff_populate_filespec(options->repo, p->one, NULL); 3133 diff_populate_filespec(options->repo, p->two, NULL); 3134 diffcore_count_changes(options->repo, 3135 p->one, p->two, NULL, NULL, 3136 &copied, &added); 3137 diff_free_filespec_data(p->one); 3138 diff_free_filespec_data(p->two); 3139 } else if (DIFF_FILE_VALID(p->one)) { 3140 diff_populate_filespec(options->repo, p->one, &dpf_options); 3141 copied = added = 0; 3142 diff_free_filespec_data(p->one); 3143 } else if (DIFF_FILE_VALID(p->two)) { 3144 diff_populate_filespec(options->repo, p->two, &dpf_options); 3145 copied = 0; 3146 added = p->two->size; 3147 diff_free_filespec_data(p->two); 3148 } else 3149 continue; 3150 3151 /* 3152 * Original minus copied is the removed material, 3153 * added is the new material. They are both damages 3154 * made to the preimage. 3155 * If the resulting damage is zero, we know that 3156 * diffcore_count_changes() considers the two entries to 3157 * be identical, but since the oid changed, we 3158 * know that there must have been _some_ kind of change, 3159 * so we force all entries to have damage > 0. 3160 */ 3161 damage = (p->one->size - copied) + added; 3162 if (!damage) 3163 damage = 1; 3164 3165found_damage: 3166 ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); 3167 dir.files[dir.nr].name = name; 3168 dir.files[dir.nr].changed = damage; 3169 changed += damage; 3170 dir.nr++; 3171 } 3172 3173 conclude_dirstat(options, &dir, changed); 3174} 3175 3176static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options) 3177{ 3178 int i; 3179 unsigned long changed; 3180 struct dirstat_dir dir; 3181 3182 if (data->nr == 0) 3183 return; 3184 3185 dir.files = NULL; 3186 dir.alloc = 0; 3187 dir.nr = 0; 3188 dir.permille = options->dirstat_permille; 3189 dir.cumulative = options->flags.dirstat_cumulative; 3190 3191 changed = 0; 3192 for (i = 0; i < data->nr; i++) { 3193 struct diffstat_file *file = data->files[i]; 3194 unsigned long damage = file->added + file->deleted; 3195 if (file->is_binary) 3196 /* 3197 * binary files counts bytes, not lines. Must find some 3198 * way to normalize binary bytes vs. textual lines. 3199 * The following heuristic assumes that there are 64 3200 * bytes per "line". 3201 * This is stupid and ugly, but very cheap... 3202 */ 3203 damage = DIV_ROUND_UP(damage, 64); 3204 ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); 3205 dir.files[dir.nr].name = file->name; 3206 dir.files[dir.nr].changed = damage; 3207 changed += damage; 3208 dir.nr++; 3209 } 3210 3211 conclude_dirstat(options, &dir, changed); 3212} 3213 3214static void free_diffstat_file(struct diffstat_file *f) 3215{ 3216 free(f->print_name); 3217 free(f->name); 3218 free(f->from_name); 3219 free(f); 3220} 3221 3222void free_diffstat_info(struct diffstat_t *diffstat) 3223{ 3224 int i; 3225 for (i = 0; i < diffstat->nr; i++) 3226 free_diffstat_file(diffstat->files[i]); 3227 free(diffstat->files); 3228} 3229 3230struct checkdiff_t { 3231 const char *filename; 3232 int lineno; 3233 int conflict_marker_size; 3234 struct diff_options *o; 3235 unsigned ws_rule; 3236 unsigned status; 3237}; 3238 3239static int is_conflict_marker(const char *line, int marker_size, unsigned long len) 3240{ 3241 char firstchar; 3242 int cnt; 3243 3244 if (len < marker_size + 1) 3245 return 0; 3246 firstchar = line[0]; 3247 switch (firstchar) { 3248 case '=': case '>': case '<': case '|': 3249 break; 3250 default: 3251 return 0; 3252 } 3253 for (cnt = 1; cnt < marker_size; cnt++) 3254 if (line[cnt] != firstchar) 3255 return 0; 3256 /* line[1] through line[marker_size-1] are same as firstchar */ 3257 if (len < marker_size + 1 || !isspace(line[marker_size])) 3258 return 0; 3259 return 1; 3260} 3261 3262static void checkdiff_consume_hunk(void *priv, 3263 long ob UNUSED, long on UNUSED, 3264 long nb, long nn UNUSED, 3265 const char *func UNUSED, long funclen UNUSED) 3266 3267{ 3268 struct checkdiff_t *data = priv; 3269 data->lineno = nb - 1; 3270} 3271 3272static int checkdiff_consume(void *priv, char *line, unsigned long len) 3273{ 3274 struct checkdiff_t *data = priv; 3275 int marker_size = data->conflict_marker_size; 3276 const char *ws = diff_get_color(data->o->use_color, DIFF_WHITESPACE); 3277 const char *reset = diff_get_color(data->o->use_color, DIFF_RESET); 3278 const char *set = diff_get_color(data->o->use_color, DIFF_FILE_NEW); 3279 char *err; 3280 const char *line_prefix; 3281 3282 assert(data->o); 3283 line_prefix = diff_line_prefix(data->o); 3284 3285 if (line[0] == '+') { 3286 unsigned bad; 3287 data->lineno++; 3288 if (is_conflict_marker(line + 1, marker_size, len - 1)) { 3289 data->status |= 1; 3290 fprintf(data->o->file, 3291 "%s%s:%d: leftover conflict marker\n", 3292 line_prefix, data->filename, data->lineno); 3293 } 3294 bad = ws_check(line + 1, len - 1, data->ws_rule); 3295 if (!bad) 3296 return 0; 3297 data->status |= bad; 3298 err = whitespace_error_string(bad); 3299 fprintf(data->o->file, "%s%s:%d: %s.\n", 3300 line_prefix, data->filename, data->lineno, err); 3301 free(err); 3302 emit_line(data->o, set, reset, line, 1); 3303 ws_check_emit(line + 1, len - 1, data->ws_rule, 3304 data->o->file, set, reset, ws); 3305 } else if (line[0] == ' ') { 3306 data->lineno++; 3307 } 3308 return 0; 3309} 3310 3311static unsigned char *deflate_it(char *data, 3312 unsigned long size, 3313 unsigned long *result_size) 3314{ 3315 int bound; 3316 unsigned char *deflated; 3317 git_zstream stream; 3318 3319 git_deflate_init(&stream, zlib_compression_level); 3320 bound = git_deflate_bound(&stream, size); 3321 deflated = xmalloc(bound); 3322 stream.next_out = deflated; 3323 stream.avail_out = bound; 3324 3325 stream.next_in = (unsigned char *)data; 3326 stream.avail_in = size; 3327 while (git_deflate(&stream, Z_FINISH) == Z_OK) 3328 ; /* nothing */ 3329 git_deflate_end(&stream); 3330 *result_size = stream.total_out; 3331 return deflated; 3332} 3333 3334static void emit_binary_diff_body(struct diff_options *o, 3335 mmfile_t *one, mmfile_t *two) 3336{ 3337 void *cp; 3338 void *delta; 3339 void *deflated; 3340 void *data; 3341 unsigned long orig_size; 3342 unsigned long delta_size; 3343 unsigned long deflate_size; 3344 unsigned long data_size; 3345 3346 /* We could do deflated delta, or we could do just deflated two, 3347 * whichever is smaller. 3348 */ 3349 delta = NULL; 3350 deflated = deflate_it(two->ptr, two->size, &deflate_size); 3351 if (one->size && two->size) { 3352 delta = diff_delta(one->ptr, one->size, 3353 two->ptr, two->size, 3354 &delta_size, deflate_size); 3355 if (delta) { 3356 void *to_free = delta; 3357 orig_size = delta_size; 3358 delta = deflate_it(delta, delta_size, &delta_size); 3359 free(to_free); 3360 } 3361 } 3362 3363 if (delta && delta_size < deflate_size) { 3364 char *s = xstrfmt("%"PRIuMAX , (uintmax_t)orig_size); 3365 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_DELTA, 3366 s, strlen(s), 0); 3367 free(s); 3368 free(deflated); 3369 data = delta; 3370 data_size = delta_size; 3371 } else { 3372 char *s = xstrfmt("%lu", two->size); 3373 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER_LITERAL, 3374 s, strlen(s), 0); 3375 free(s); 3376 free(delta); 3377 data = deflated; 3378 data_size = deflate_size; 3379 } 3380 3381 /* emit data encoded in base85 */ 3382 cp = data; 3383 while (data_size) { 3384 int len; 3385 int bytes = (52 < data_size) ? 52 : data_size; 3386 char line[71]; 3387 data_size -= bytes; 3388 if (bytes <= 26) 3389 line[0] = bytes + 'A' - 1; 3390 else 3391 line[0] = bytes - 26 + 'a' - 1; 3392 encode_85(line + 1, cp, bytes); 3393 cp = (char *) cp + bytes; 3394 3395 len = strlen(line); 3396 line[len++] = '\n'; 3397 line[len] = '\0'; 3398 3399 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_BODY, 3400 line, len, 0); 3401 } 3402 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_FOOTER, NULL, 0, 0); 3403 free(data); 3404} 3405 3406static void emit_binary_diff(struct diff_options *o, 3407 mmfile_t *one, mmfile_t *two) 3408{ 3409 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_DIFF_HEADER, NULL, 0, 0); 3410 emit_binary_diff_body(o, one, two); 3411 emit_binary_diff_body(o, two, one); 3412} 3413 3414int diff_filespec_is_binary(struct repository *r, 3415 struct diff_filespec *one) 3416{ 3417 struct diff_populate_filespec_options dpf_options = { 3418 .check_binary = 1, 3419 }; 3420 3421 if (one->is_binary == -1) { 3422 diff_filespec_load_driver(one, r->index); 3423 if (one->driver->binary != -1) 3424 one->is_binary = one->driver->binary; 3425 else { 3426 if (!one->data && DIFF_FILE_VALID(one)) 3427 diff_populate_filespec(r, one, &dpf_options); 3428 if (one->is_binary == -1 && one->data) 3429 one->is_binary = buffer_is_binary(one->data, 3430 one->size); 3431 if (one->is_binary == -1) 3432 one->is_binary = 0; 3433 } 3434 } 3435 return one->is_binary; 3436} 3437 3438static const struct userdiff_funcname * 3439diff_funcname_pattern(struct diff_options *o, struct diff_filespec *one) 3440{ 3441 diff_filespec_load_driver(one, o->repo->index); 3442 return one->driver->funcname.pattern ? &one->driver->funcname : NULL; 3443} 3444 3445void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b) 3446{ 3447 if (!options->a_prefix) 3448 options->a_prefix = a; 3449 if (!options->b_prefix) 3450 options->b_prefix = b; 3451} 3452 3453void diff_set_noprefix(struct diff_options *options) 3454{ 3455 options->a_prefix = options->b_prefix = ""; 3456} 3457 3458void diff_set_default_prefix(struct diff_options *options) 3459{ 3460 options->a_prefix = diff_src_prefix ? diff_src_prefix : "a/"; 3461 options->b_prefix = diff_dst_prefix ? diff_dst_prefix : "b/"; 3462} 3463 3464struct userdiff_driver *get_textconv(struct repository *r, 3465 struct diff_filespec *one) 3466{ 3467 if (!DIFF_FILE_VALID(one)) 3468 return NULL; 3469 3470 diff_filespec_load_driver(one, r->index); 3471 return userdiff_get_textconv(r, one->driver); 3472} 3473 3474static struct string_list *additional_headers(struct diff_options *o, 3475 const char *path) 3476{ 3477 if (!o->additional_path_headers) 3478 return NULL; 3479 return strmap_get(o->additional_path_headers, path); 3480} 3481 3482static void add_formatted_header(struct strbuf *msg, 3483 const char *header, 3484 const char *line_prefix, 3485 const char *meta, 3486 const char *reset) 3487{ 3488 const char *next, *newline; 3489 3490 for (next = header; *next; next = newline) { 3491 newline = strchrnul(next, '\n'); 3492 strbuf_addf(msg, "%s%s%.*s%s\n", line_prefix, meta, 3493 (int)(newline - next), next, reset); 3494 if (*newline) 3495 newline++; 3496 } 3497} 3498 3499static void add_formatted_headers(struct strbuf *msg, 3500 struct string_list *more_headers, 3501 const char *line_prefix, 3502 const char *meta, 3503 const char *reset) 3504{ 3505 int i; 3506 3507 for (i = 0; i < more_headers->nr; i++) 3508 add_formatted_header(msg, more_headers->items[i].string, 3509 line_prefix, meta, reset); 3510} 3511 3512static int diff_filepair_is_phoney(struct diff_filespec *one, 3513 struct diff_filespec *two) 3514{ 3515 /* 3516 * This function specifically looks for pairs injected by 3517 * create_filepairs_for_header_only_notifications(). Such 3518 * pairs are "phoney" in that they do not represent any 3519 * content or even mode difference, but were inserted because 3520 * diff_queued_diff previously had no pair associated with 3521 * that path but we needed some pair to avoid losing the 3522 * "remerge CONFLICT" header associated with the path. 3523 */ 3524 return !DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two); 3525} 3526 3527static int set_diff_algorithm(struct diff_options *opts, 3528 const char *alg) 3529{ 3530 long value = parse_algorithm_value(alg); 3531 3532 if (value < 0) 3533 return -1; 3534 3535 /* clear out previous settings */ 3536 DIFF_XDL_CLR(opts, NEED_MINIMAL); 3537 opts->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK; 3538 opts->xdl_opts |= value; 3539 3540 return 0; 3541} 3542 3543static void builtin_diff(const char *name_a, 3544 const char *name_b, 3545 struct diff_filespec *one, 3546 struct diff_filespec *two, 3547 const char *xfrm_msg, 3548 int must_show_header, 3549 struct diff_options *o, 3550 int complete_rewrite) 3551{ 3552 mmfile_t mf1, mf2; 3553 const char *lbl[2]; 3554 char *a_one, *b_two; 3555 const char *meta = diff_get_color_opt(o, DIFF_METAINFO); 3556 const char *reset = diff_get_color_opt(o, DIFF_RESET); 3557 const char *a_prefix, *b_prefix; 3558 struct userdiff_driver *textconv_one = NULL; 3559 struct userdiff_driver *textconv_two = NULL; 3560 struct strbuf header = STRBUF_INIT; 3561 const char *line_prefix = diff_line_prefix(o); 3562 3563 diff_set_mnemonic_prefix(o, "a/", "b/"); 3564 if (o->flags.reverse_diff) { 3565 a_prefix = o->b_prefix; 3566 b_prefix = o->a_prefix; 3567 } else { 3568 a_prefix = o->a_prefix; 3569 b_prefix = o->b_prefix; 3570 } 3571 3572 if (o->submodule_format == DIFF_SUBMODULE_LOG && 3573 (!one->mode || S_ISGITLINK(one->mode)) && 3574 (!two->mode || S_ISGITLINK(two->mode)) && 3575 (!diff_filepair_is_phoney(one, two))) { 3576 show_submodule_diff_summary(o, one->path ? one->path : two->path, 3577 &one->oid, &two->oid, 3578 two->dirty_submodule); 3579 o->found_changes = 1; 3580 return; 3581 } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF && 3582 (!one->mode || S_ISGITLINK(one->mode)) && 3583 (!two->mode || S_ISGITLINK(two->mode)) && 3584 (!diff_filepair_is_phoney(one, two))) { 3585 show_submodule_inline_diff(o, one->path ? one->path : two->path, 3586 &one->oid, &two->oid, 3587 two->dirty_submodule); 3588 o->found_changes = 1; 3589 return; 3590 } 3591 3592 if (o->flags.allow_textconv) { 3593 textconv_one = get_textconv(o->repo, one); 3594 textconv_two = get_textconv(o->repo, two); 3595 } 3596 3597 /* Never use a non-valid filename anywhere if at all possible */ 3598 name_a = DIFF_FILE_VALID(one) ? name_a : name_b; 3599 name_b = DIFF_FILE_VALID(two) ? name_b : name_a; 3600 3601 a_one = quote_two(a_prefix, name_a + (*name_a == '/')); 3602 b_two = quote_two(b_prefix, name_b + (*name_b == '/')); 3603 lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; 3604 lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; 3605 if (diff_filepair_is_phoney(one, two)) { 3606 /* 3607 * We should only reach this point for pairs generated from 3608 * create_filepairs_for_header_only_notifications(). For 3609 * these, we want to avoid the "/dev/null" special casing 3610 * above, because we do not want such pairs shown as either 3611 * "new file" or "deleted file" below. 3612 */ 3613 lbl[0] = a_one; 3614 lbl[1] = b_two; 3615 } 3616 strbuf_addf(&header, "%s%sdiff --git %s %s%s\n", line_prefix, meta, a_one, b_two, reset); 3617 if (lbl[0][0] == '/') { 3618 /* /dev/null */ 3619 strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset); 3620 if (xfrm_msg) 3621 strbuf_addstr(&header, xfrm_msg); 3622 o->found_changes = 1; 3623 must_show_header = 1; 3624 } 3625 else if (lbl[1][0] == '/') { 3626 strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset); 3627 if (xfrm_msg) 3628 strbuf_addstr(&header, xfrm_msg); 3629 o->found_changes = 1; 3630 must_show_header = 1; 3631 } 3632 else { 3633 if (one->mode != two->mode) { 3634 strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset); 3635 strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset); 3636 o->found_changes = 1; 3637 must_show_header = 1; 3638 } 3639 if (xfrm_msg) 3640 strbuf_addstr(&header, xfrm_msg); 3641 3642 /* 3643 * we do not run diff between different kind 3644 * of objects. 3645 */ 3646 if ((one->mode ^ two->mode) & S_IFMT) 3647 goto free_ab_and_return; 3648 if (complete_rewrite && 3649 (textconv_one || !diff_filespec_is_binary(o->repo, one)) && 3650 (textconv_two || !diff_filespec_is_binary(o->repo, two))) { 3651 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, 3652 header.buf, header.len, 0); 3653 strbuf_reset(&header); 3654 emit_rewrite_diff(name_a, name_b, one, two, 3655 textconv_one, textconv_two, o); 3656 o->found_changes = 1; 3657 goto free_ab_and_return; 3658 } 3659 } 3660 3661 if (o->irreversible_delete && lbl[1][0] == '/') { 3662 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, 3663 header.len, 0); 3664 strbuf_reset(&header); 3665 goto free_ab_and_return; 3666 } else if (!o->flags.text && 3667 ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) || 3668 (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) { 3669 struct strbuf sb = STRBUF_INIT; 3670 if (!one->data && !two->data && 3671 S_ISREG(one->mode) && S_ISREG(two->mode) && 3672 !o->flags.binary) { 3673 if (oideq(&one->oid, &two->oid)) { 3674 if (must_show_header) 3675 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, 3676 header.buf, header.len, 3677 0); 3678 goto free_ab_and_return; 3679 } 3680 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, 3681 header.buf, header.len, 0); 3682 strbuf_addf(&sb, "%sBinary files %s and %s differ\n", 3683 diff_line_prefix(o), lbl[0], lbl[1]); 3684 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES, 3685 sb.buf, sb.len, 0); 3686 strbuf_release(&sb); 3687 o->found_changes = 1; 3688 goto free_ab_and_return; 3689 } 3690 if (fill_mmfile(o->repo, &mf1, one) < 0 || 3691 fill_mmfile(o->repo, &mf2, two) < 0) 3692 die("unable to read files to diff"); 3693 /* Quite common confusing case */ 3694 if (mf1.size == mf2.size && 3695 !memcmp(mf1.ptr, mf2.ptr, mf1.size)) { 3696 if (must_show_header) 3697 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, 3698 header.buf, header.len, 0); 3699 goto free_ab_and_return; 3700 } 3701 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0); 3702 strbuf_reset(&header); 3703 if (o->flags.binary) 3704 emit_binary_diff(o, &mf1, &mf2); 3705 else { 3706 strbuf_addf(&sb, "%sBinary files %s and %s differ\n", 3707 diff_line_prefix(o), lbl[0], lbl[1]); 3708 emit_diff_symbol(o, DIFF_SYMBOL_BINARY_FILES, 3709 sb.buf, sb.len, 0); 3710 strbuf_release(&sb); 3711 } 3712 o->found_changes = 1; 3713 } else { 3714 /* Crazy xdl interfaces.. */ 3715 const char *diffopts; 3716 const char *v; 3717 xpparam_t xpp; 3718 xdemitconf_t xecfg; 3719 struct emit_callback ecbdata; 3720 const struct userdiff_funcname *pe; 3721 3722 if (must_show_header) { 3723 emit_diff_symbol(o, DIFF_SYMBOL_HEADER, 3724 header.buf, header.len, 0); 3725 strbuf_reset(&header); 3726 } 3727 3728 mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr); 3729 mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr); 3730 3731 pe = diff_funcname_pattern(o, one); 3732 if (!pe) 3733 pe = diff_funcname_pattern(o, two); 3734 3735 memset(&xpp, 0, sizeof(xpp)); 3736 memset(&xecfg, 0, sizeof(xecfg)); 3737 memset(&ecbdata, 0, sizeof(ecbdata)); 3738 if (o->flags.suppress_diff_headers) 3739 lbl[0] = NULL; 3740 ecbdata.label_path = lbl; 3741 ecbdata.color_diff = want_color(o->use_color); 3742 ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b); 3743 if (ecbdata.ws_rule & WS_BLANK_AT_EOF) 3744 check_blank_at_eof(&mf1, &mf2, &ecbdata); 3745 ecbdata.opt = o; 3746 if (header.len && !o->flags.suppress_diff_headers) 3747 ecbdata.header = &header; 3748 xpp.flags = o->xdl_opts; 3749 xpp.ignore_regex = o->ignore_regex; 3750 xpp.ignore_regex_nr = o->ignore_regex_nr; 3751 xpp.anchors = o->anchors; 3752 xpp.anchors_nr = o->anchors_nr; 3753 xecfg.ctxlen = o->context; 3754 xecfg.interhunkctxlen = o->interhunkcontext; 3755 xecfg.flags = XDL_EMIT_FUNCNAMES; 3756 if (o->flags.funccontext) 3757 xecfg.flags |= XDL_EMIT_FUNCCONTEXT; 3758 if (pe) 3759 xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); 3760 3761 diffopts = getenv("GIT_DIFF_OPTS"); 3762 if (!diffopts) 3763 ; 3764 else if (skip_prefix(diffopts, "--unified=", &v)) 3765 xecfg.ctxlen = strtoul(v, NULL, 10); 3766 else if (skip_prefix(diffopts, "-u", &v)) 3767 xecfg.ctxlen = strtoul(v, NULL, 10); 3768 3769 if (o->word_diff) 3770 init_diff_words_data(&ecbdata, o, one, two); 3771 if (o->dry_run) { 3772 /* 3773 * Unlike the !dry_run case, we need to ignore the 3774 * return value from xdi_diff_outf() here, because 3775 * xdi_diff_outf() takes non-zero return from its 3776 * callback function as a sign of error and returns 3777 * early (which is why we return non-zero from our 3778 * callback, quick_consume()). Unfortunately, 3779 * xdi_diff_outf() signals an error by returning 3780 * non-zero. 3781 */ 3782 xdi_diff_outf(&mf1, &mf2, NULL, quick_consume, 3783 &ecbdata, &xpp, &xecfg); 3784 } else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume, 3785 &ecbdata, &xpp, &xecfg)) 3786 die("unable to generate diff for %s", one->path); 3787 if (o->word_diff) 3788 free_diff_words_data(&ecbdata); 3789 if (textconv_one) 3790 free(mf1.ptr); 3791 if (textconv_two) 3792 free(mf2.ptr); 3793 xdiff_clear_find_func(&xecfg); 3794 } 3795 3796 free_ab_and_return: 3797 strbuf_release(&header); 3798 diff_free_filespec_data(one); 3799 diff_free_filespec_data(two); 3800 free(a_one); 3801 free(b_two); 3802 return; 3803} 3804 3805static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed) 3806{ 3807 if (!is_renamed) { 3808 if (p->status == DIFF_STATUS_ADDED) { 3809 if (S_ISLNK(p->two->mode)) 3810 return "new +l"; 3811 else if ((p->two->mode & 0777) == 0755) 3812 return "new +x"; 3813 else 3814 return "new"; 3815 } else if (p->status == DIFF_STATUS_DELETED) 3816 return "gone"; 3817 } 3818 if (S_ISLNK(p->one->mode) && !S_ISLNK(p->two->mode)) 3819 return "mode -l"; 3820 else if (!S_ISLNK(p->one->mode) && S_ISLNK(p->two->mode)) 3821 return "mode +l"; 3822 else if ((p->one->mode & 0777) == 0644 && 3823 (p->two->mode & 0777) == 0755) 3824 return "mode +x"; 3825 else if ((p->one->mode & 0777) == 0755 && 3826 (p->two->mode & 0777) == 0644) 3827 return "mode -x"; 3828 return NULL; 3829} 3830 3831static void builtin_diffstat(const char *name_a, const char *name_b, 3832 struct diff_filespec *one, 3833 struct diff_filespec *two, 3834 struct diffstat_t *diffstat, 3835 struct diff_options *o, 3836 struct diff_filepair *p) 3837{ 3838 mmfile_t mf1, mf2; 3839 struct diffstat_file *data; 3840 int may_differ; 3841 int complete_rewrite = 0; 3842 3843 if (!DIFF_PAIR_UNMERGED(p)) { 3844 if (p->status == DIFF_STATUS_MODIFIED && p->score) 3845 complete_rewrite = 1; 3846 } 3847 3848 data = diffstat_add(diffstat, name_a, name_b); 3849 data->is_interesting = p->status != DIFF_STATUS_UNKNOWN; 3850 if (o->flags.stat_with_summary) 3851 data->comments = get_compact_summary(p, data->is_renamed); 3852 3853 if (!one || !two) { 3854 data->is_unmerged = 1; 3855 return; 3856 } 3857 3858 /* saves some reads if true, not a guarantee of diff outcome */ 3859 may_differ = !(one->oid_valid && two->oid_valid && 3860 oideq(&one->oid, &two->oid)); 3861 3862 if (diff_filespec_is_binary(o->repo, one) || 3863 diff_filespec_is_binary(o->repo, two)) { 3864 data->is_binary = 1; 3865 if (!may_differ) { 3866 data->added = 0; 3867 data->deleted = 0; 3868 } else { 3869 data->added = diff_filespec_size(o->repo, two); 3870 data->deleted = diff_filespec_size(o->repo, one); 3871 } 3872 } 3873 3874 else if (complete_rewrite) { 3875 diff_populate_filespec(o->repo, one, NULL); 3876 diff_populate_filespec(o->repo, two, NULL); 3877 data->deleted = count_lines(one->data, one->size); 3878 data->added = count_lines(two->data, two->size); 3879 } 3880 3881 else if (may_differ) { 3882 /* Crazy xdl interfaces.. */ 3883 xpparam_t xpp; 3884 xdemitconf_t xecfg; 3885 3886 if (fill_mmfile(o->repo, &mf1, one) < 0 || 3887 fill_mmfile(o->repo, &mf2, two) < 0) 3888 die("unable to read files to diff"); 3889 3890 memset(&xpp, 0, sizeof(xpp)); 3891 memset(&xecfg, 0, sizeof(xecfg)); 3892 xpp.flags = o->xdl_opts; 3893 xpp.ignore_regex = o->ignore_regex; 3894 xpp.ignore_regex_nr = o->ignore_regex_nr; 3895 xpp.anchors = o->anchors; 3896 xpp.anchors_nr = o->anchors_nr; 3897 xecfg.ctxlen = o->context; 3898 xecfg.interhunkctxlen = o->interhunkcontext; 3899 xecfg.flags = XDL_EMIT_NO_HUNK_HDR; 3900 if (xdi_diff_outf(&mf1, &mf2, NULL, 3901 diffstat_consume, diffstat, &xpp, &xecfg)) 3902 die("unable to generate diffstat for %s", one->path); 3903 3904 if (DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two)) { 3905 struct diffstat_file *file = 3906 diffstat->files[diffstat->nr - 1]; 3907 /* 3908 * Omit diffstats of modified files where nothing changed. 3909 * Even if may_differ, this might be the case due to 3910 * ignoring whitespace changes, etc. 3911 * 3912 * But note that we special-case additions, deletions, 3913 * renames, and mode changes as adding an empty file, 3914 * for example is still of interest. 3915 */ 3916 if ((p->status == DIFF_STATUS_MODIFIED) 3917 && !file->added 3918 && !file->deleted 3919 && one->mode == two->mode) { 3920 free_diffstat_file(file); 3921 diffstat->nr--; 3922 } 3923 } 3924 } 3925 3926 diff_free_filespec_data(one); 3927 diff_free_filespec_data(two); 3928} 3929 3930static void builtin_checkdiff(const char *name_a, const char *name_b, 3931 const char *attr_path, 3932 struct diff_filespec *one, 3933 struct diff_filespec *two, 3934 struct diff_options *o) 3935{ 3936 mmfile_t mf1, mf2; 3937 struct checkdiff_t data; 3938 3939 if (!two) 3940 return; 3941 3942 memset(&data, 0, sizeof(data)); 3943 data.filename = name_b ? name_b : name_a; 3944 data.lineno = 0; 3945 data.o = o; 3946 data.ws_rule = whitespace_rule(o->repo->index, attr_path); 3947 data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path); 3948 3949 if (fill_mmfile(o->repo, &mf1, one) < 0 || 3950 fill_mmfile(o->repo, &mf2, two) < 0) 3951 die("unable to read files to diff"); 3952 3953 /* 3954 * All the other codepaths check both sides, but not checking 3955 * the "old" side here is deliberate. We are checking the newly 3956 * introduced changes, and as long as the "new" side is text, we 3957 * can and should check what it introduces. 3958 */ 3959 if (diff_filespec_is_binary(o->repo, two)) 3960 goto free_and_return; 3961 else { 3962 /* Crazy xdl interfaces.. */ 3963 xpparam_t xpp; 3964 xdemitconf_t xecfg; 3965 3966 memset(&xpp, 0, sizeof(xpp)); 3967 memset(&xecfg, 0, sizeof(xecfg)); 3968 xecfg.ctxlen = 1; /* at least one context line */ 3969 xpp.flags = 0; 3970 if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume_hunk, 3971 checkdiff_consume, &data, 3972 &xpp, &xecfg)) 3973 die("unable to generate checkdiff for %s", one->path); 3974 3975 if (data.ws_rule & WS_BLANK_AT_EOF) { 3976 struct emit_callback ecbdata; 3977 int blank_at_eof; 3978 3979 ecbdata.ws_rule = data.ws_rule; 3980 check_blank_at_eof(&mf1, &mf2, &ecbdata); 3981 blank_at_eof = ecbdata.blank_at_eof_in_postimage; 3982 3983 if (blank_at_eof) { 3984 static char *err; 3985 if (!err) 3986 err = whitespace_error_string(WS_BLANK_AT_EOF); 3987 fprintf(o->file, "%s:%d: %s.\n", 3988 data.filename, blank_at_eof, err); 3989 data.status = 1; /* report errors */ 3990 } 3991 } 3992 } 3993 free_and_return: 3994 diff_free_filespec_data(one); 3995 diff_free_filespec_data(two); 3996 if (data.status) 3997 o->flags.check_failed = 1; 3998} 3999 4000struct diff_filespec *alloc_filespec(const char *path) 4001{ 4002 struct diff_filespec *spec; 4003 4004 FLEXPTR_ALLOC_STR(spec, path, path); 4005 spec->count = 1; 4006 spec->is_binary = -1; 4007 return spec; 4008} 4009 4010void free_filespec(struct diff_filespec *spec) 4011{ 4012 if (!--spec->count) { 4013 diff_free_filespec_data(spec); 4014 free(spec); 4015 } 4016} 4017 4018void fill_filespec(struct diff_filespec *spec, const struct object_id *oid, 4019 int oid_valid, unsigned short mode) 4020{ 4021 if (mode) { 4022 spec->mode = canon_mode(mode); 4023 oidcpy(&spec->oid, oid); 4024 spec->oid_valid = oid_valid; 4025 } 4026} 4027 4028/* 4029 * Given a name and sha1 pair, if the index tells us the file in 4030 * the work tree has that object contents, return true, so that 4031 * prepare_temp_file() does not have to inflate and extract. 4032 */ 4033static int reuse_worktree_file(struct index_state *istate, 4034 const char *name, 4035 const struct object_id *oid, 4036 int want_file) 4037{ 4038 const struct cache_entry *ce; 4039 struct stat st; 4040 int pos, len; 4041 4042 /* 4043 * We do not read the cache ourselves here, because the 4044 * benchmark with my previous version that always reads cache 4045 * shows that it makes things worse for diff-tree comparing 4046 * two linux-2.6 kernel trees in an already checked out work 4047 * tree. This is because most diff-tree comparisons deal with 4048 * only a small number of files, while reading the cache is 4049 * expensive for a large project, and its cost outweighs the 4050 * savings we get by not inflating the object to a temporary 4051 * file. Practically, this code only helps when we are used 4052 * by diff-cache --cached, which does read the cache before 4053 * calling us. 4054 */ 4055 if (!istate->cache) 4056 return 0; 4057 4058 /* We want to avoid the working directory if our caller 4059 * doesn't need the data in a normal file, this system 4060 * is rather slow with its stat/open/mmap/close syscalls, 4061 * and the object is contained in a pack file. The pack 4062 * is probably already open and will be faster to obtain 4063 * the data through than the working directory. Loose 4064 * objects however would tend to be slower as they need 4065 * to be individually opened and inflated. 4066 */ 4067 if (!FAST_WORKING_DIRECTORY && !want_file && 4068 has_object_pack(istate->repo, oid)) 4069 return 0; 4070 4071 /* 4072 * Similarly, if we'd have to convert the file contents anyway, that 4073 * makes the optimization not worthwhile. 4074 */ 4075 if (!want_file && would_convert_to_git(istate, name)) 4076 return 0; 4077 4078 /* 4079 * If this path does not match our sparse-checkout definition, 4080 * then the file will not be in the working directory. 4081 */ 4082 if (!path_in_sparse_checkout(name, istate)) 4083 return 0; 4084 4085 len = strlen(name); 4086 pos = index_name_pos(istate, name, len); 4087 if (pos < 0) 4088 return 0; 4089 ce = istate->cache[pos]; 4090 4091 /* 4092 * This is not the sha1 we are looking for, or 4093 * unreusable because it is not a regular file. 4094 */ 4095 if (!oideq(oid, &ce->oid) || !S_ISREG(ce->ce_mode)) 4096 return 0; 4097 4098 /* 4099 * If ce is marked as "assume unchanged", there is no 4100 * guarantee that work tree matches what we are looking for. 4101 */ 4102 if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) 4103 return 0; 4104 4105 /* 4106 * If ce matches the file in the work tree, we can reuse it. 4107 */ 4108 if (ce_uptodate(ce) || 4109 (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0))) 4110 return 1; 4111 4112 return 0; 4113} 4114 4115static int diff_populate_gitlink(struct diff_filespec *s, int size_only) 4116{ 4117 struct strbuf buf = STRBUF_INIT; 4118 const char *dirty = ""; 4119 4120 /* Are we looking at the work tree? */ 4121 if (s->dirty_submodule) 4122 dirty = "-dirty"; 4123 4124 strbuf_addf(&buf, "Subproject commit %s%s\n", 4125 oid_to_hex(&s->oid), dirty); 4126 s->size = buf.len; 4127 if (size_only) { 4128 s->data = NULL; 4129 strbuf_release(&buf); 4130 } else { 4131 s->data = strbuf_detach(&buf, NULL); 4132 s->should_free = 1; 4133 } 4134 return 0; 4135} 4136 4137/* 4138 * While doing rename detection and pickaxe operation, we may need to 4139 * grab the data for the blob (or file) for our own in-core comparison. 4140 * diff_filespec has data and size fields for this purpose. 4141 */ 4142int diff_populate_filespec(struct repository *r, 4143 struct diff_filespec *s, 4144 const struct diff_populate_filespec_options *options) 4145{ 4146 int size_only = options ? options->check_size_only : 0; 4147 int check_binary = options ? options->check_binary : 0; 4148 int err = 0; 4149 int conv_flags = global_conv_flags_eol; 4150 /* 4151 * demote FAIL to WARN to allow inspecting the situation 4152 * instead of refusing. 4153 */ 4154 if (conv_flags & CONV_EOL_RNDTRP_DIE) 4155 conv_flags = CONV_EOL_RNDTRP_WARN; 4156 4157 if (!DIFF_FILE_VALID(s)) 4158 die("internal error: asking to populate invalid file."); 4159 if (S_ISDIR(s->mode)) 4160 return -1; 4161 4162 if (s->data) 4163 return 0; 4164 4165 if (size_only && 0 < s->size) 4166 return 0; 4167 4168 if (S_ISGITLINK(s->mode)) 4169 return diff_populate_gitlink(s, size_only); 4170 4171 if (!s->oid_valid || 4172 reuse_worktree_file(r->index, s->path, &s->oid, 0)) { 4173 struct strbuf buf = STRBUF_INIT; 4174 struct stat st; 4175 int fd; 4176 4177 if (lstat(s->path, &st) < 0) { 4178 err_empty: 4179 err = -1; 4180 empty: 4181 s->data = (char *)""; 4182 s->size = 0; 4183 return err; 4184 } 4185 s->size = xsize_t(st.st_size); 4186 if (!s->size) 4187 goto empty; 4188 if (S_ISLNK(st.st_mode)) { 4189 struct strbuf sb = STRBUF_INIT; 4190 4191 if (strbuf_readlink(&sb, s->path, s->size)) 4192 goto err_empty; 4193 s->size = sb.len; 4194 s->data = strbuf_detach(&sb, NULL); 4195 s->should_free = 1; 4196 return 0; 4197 } 4198 4199 /* 4200 * Even if the caller would be happy with getting 4201 * only the size, we cannot return early at this 4202 * point if the path requires us to run the content 4203 * conversion. 4204 */ 4205 if (size_only && !would_convert_to_git(r->index, s->path)) 4206 return 0; 4207 4208 /* 4209 * Note: this check uses xsize_t(st.st_size) that may 4210 * not be the true size of the blob after it goes 4211 * through convert_to_git(). This may not strictly be 4212 * correct, but the whole point of big_file_threshold 4213 * and is_binary check being that we want to avoid 4214 * opening the file and inspecting the contents, this 4215 * is probably fine. 4216 */ 4217 if (check_binary && 4218 s->size > repo_settings_get_big_file_threshold(the_repository) && 4219 s->is_binary == -1) { 4220 s->is_binary = 1; 4221 return 0; 4222 } 4223 fd = open(s->path, O_RDONLY); 4224 if (fd < 0) 4225 goto err_empty; 4226 s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0); 4227 close(fd); 4228 s->should_munmap = 1; 4229 4230 /* 4231 * Convert from working tree format to canonical git format 4232 */ 4233 if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) { 4234 size_t size = 0; 4235 munmap(s->data, s->size); 4236 s->should_munmap = 0; 4237 s->data = strbuf_detach(&buf, &size); 4238 s->size = size; 4239 s->should_free = 1; 4240 } 4241 } 4242 else { 4243 struct object_info info = { 4244 .sizep = &s->size 4245 }; 4246 4247 if (!(size_only || check_binary)) 4248 /* 4249 * Set contentp, since there is no chance that merely 4250 * the size is sufficient. 4251 */ 4252 info.contentp = &s->data; 4253 4254 if (options && options->missing_object_cb) { 4255 if (!odb_read_object_info_extended(r->objects, &s->oid, &info, 4256 OBJECT_INFO_LOOKUP_REPLACE | 4257 OBJECT_INFO_SKIP_FETCH_OBJECT)) 4258 goto object_read; 4259 options->missing_object_cb(options->missing_object_data); 4260 } 4261 if (odb_read_object_info_extended(r->objects, &s->oid, &info, 4262 OBJECT_INFO_LOOKUP_REPLACE)) 4263 die("unable to read %s", oid_to_hex(&s->oid)); 4264 4265object_read: 4266 if (size_only || check_binary) { 4267 if (size_only) 4268 return 0; 4269 if (s->size > repo_settings_get_big_file_threshold(the_repository) && 4270 s->is_binary == -1) { 4271 s->is_binary = 1; 4272 return 0; 4273 } 4274 } 4275 if (!info.contentp) { 4276 info.contentp = &s->data; 4277 if (odb_read_object_info_extended(r->objects, &s->oid, &info, 4278 OBJECT_INFO_LOOKUP_REPLACE)) 4279 die("unable to read %s", oid_to_hex(&s->oid)); 4280 } 4281 s->should_free = 1; 4282 } 4283 return 0; 4284} 4285 4286void diff_free_filespec_blob(struct diff_filespec *s) 4287{ 4288 if (s->should_free) 4289 free(s->data); 4290 else if (s->should_munmap) 4291 munmap(s->data, s->size); 4292 4293 if (s->should_free || s->should_munmap) { 4294 s->should_free = s->should_munmap = 0; 4295 s->data = NULL; 4296 } 4297} 4298 4299void diff_free_filespec_data(struct diff_filespec *s) 4300{ 4301 if (!s) 4302 return; 4303 4304 diff_free_filespec_blob(s); 4305 FREE_AND_NULL(s->cnt_data); 4306} 4307 4308static void prep_temp_blob(struct index_state *istate, 4309 const char *path, struct diff_tempfile *temp, 4310 void *blob, 4311 unsigned long size, 4312 const struct object_id *oid, 4313 int mode) 4314{ 4315 struct strbuf buf = STRBUF_INIT; 4316 char *path_dup = xstrdup(path); 4317 const char *base = basename(path_dup); 4318 struct checkout_metadata meta; 4319 4320 init_checkout_metadata(&meta, NULL, NULL, oid); 4321 4322 temp->tempfile = mks_tempfile_dt("git-blob-XXXXXX", base); 4323 if (!temp->tempfile) 4324 die_errno("unable to create temp-file"); 4325 if (convert_to_working_tree(istate, path, 4326 (const char *)blob, (size_t)size, &buf, &meta)) { 4327 blob = buf.buf; 4328 size = buf.len; 4329 } 4330 if (write_in_full(temp->tempfile->fd, blob, size) < 0 || 4331 close_tempfile_gently(temp->tempfile)) 4332 die_errno("unable to write temp-file"); 4333 temp->name = get_tempfile_path(temp->tempfile); 4334 oid_to_hex_r(temp->hex, oid); 4335 xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode); 4336 strbuf_release(&buf); 4337 free(path_dup); 4338} 4339 4340static struct diff_tempfile *prepare_temp_file(struct repository *r, 4341 struct diff_filespec *one) 4342{ 4343 struct diff_tempfile *temp = claim_diff_tempfile(); 4344 4345 if (!DIFF_FILE_VALID(one)) { 4346 not_a_valid_file: 4347 /* A '-' entry produces this for file-2, and 4348 * a '+' entry produces this for file-1. 4349 */ 4350 temp->name = "/dev/null"; 4351 xsnprintf(temp->hex, sizeof(temp->hex), "."); 4352 xsnprintf(temp->mode, sizeof(temp->mode), "."); 4353 return temp; 4354 } 4355 4356 if (!S_ISGITLINK(one->mode) && 4357 (!one->oid_valid || 4358 reuse_worktree_file(r->index, one->path, &one->oid, 1))) { 4359 struct stat st; 4360 if (lstat(one->path, &st) < 0) { 4361 if (errno == ENOENT) 4362 goto not_a_valid_file; 4363 die_errno("stat(%s)", one->path); 4364 } 4365 if (S_ISLNK(st.st_mode)) { 4366 struct strbuf sb = STRBUF_INIT; 4367 if (strbuf_readlink(&sb, one->path, st.st_size) < 0) 4368 die_errno("readlink(%s)", one->path); 4369 prep_temp_blob(r->index, one->path, temp, sb.buf, sb.len, 4370 (one->oid_valid ? 4371 &one->oid : null_oid(the_hash_algo)), 4372 (one->oid_valid ? 4373 one->mode : S_IFLNK)); 4374 strbuf_release(&sb); 4375 } 4376 else { 4377 /* we can borrow from the file in the work tree */ 4378 temp->name = one->path; 4379 if (!one->oid_valid) 4380 oid_to_hex_r(temp->hex, null_oid(the_hash_algo)); 4381 else 4382 oid_to_hex_r(temp->hex, &one->oid); 4383 /* Even though we may sometimes borrow the 4384 * contents from the work tree, we always want 4385 * one->mode. mode is trustworthy even when 4386 * !(one->oid_valid), as long as 4387 * DIFF_FILE_VALID(one). 4388 */ 4389 xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode); 4390 } 4391 return temp; 4392 } 4393 else { 4394 if (diff_populate_filespec(r, one, NULL)) 4395 die("cannot read data blob for %s", one->path); 4396 prep_temp_blob(r->index, one->path, temp, 4397 one->data, one->size, 4398 &one->oid, one->mode); 4399 } 4400 return temp; 4401} 4402 4403static void add_external_diff_name(struct repository *r, 4404 struct strvec *argv, 4405 struct diff_filespec *df) 4406{ 4407 struct diff_tempfile *temp = prepare_temp_file(r, df); 4408 strvec_push(argv, temp->name); 4409 strvec_push(argv, temp->hex); 4410 strvec_push(argv, temp->mode); 4411} 4412 4413/* An external diff command takes: 4414 * 4415 * diff-cmd name infile1 infile1-sha1 infile1-mode \ 4416 * infile2 infile2-sha1 infile2-mode [ rename-to ] 4417 * 4418 */ 4419static void run_external_diff(const struct external_diff *pgm, 4420 const char *name, 4421 const char *other, 4422 struct diff_filespec *one, 4423 struct diff_filespec *two, 4424 const char *xfrm_msg, 4425 struct diff_options *o) 4426{ 4427 struct child_process cmd = CHILD_PROCESS_INIT; 4428 struct diff_queue_struct *q = &diff_queued_diff; 4429 int quiet = !(o->output_format & DIFF_FORMAT_PATCH); 4430 int rc; 4431 4432 /* 4433 * Trivial equality is handled by diff_unmodified_pair() before 4434 * we get here. If we don't need to show the diff and the 4435 * external diff program lacks the ability to tell us whether 4436 * it's empty then we consider it non-empty without even asking. 4437 */ 4438 if (!pgm->trust_exit_code && quiet) { 4439 o->found_changes = 1; 4440 return; 4441 } 4442 4443 strvec_push(&cmd.args, pgm->cmd); 4444 strvec_push(&cmd.args, name); 4445 4446 if (one && two) { 4447 add_external_diff_name(o->repo, &cmd.args, one); 4448 add_external_diff_name(o->repo, &cmd.args, two); 4449 if (other) { 4450 strvec_push(&cmd.args, other); 4451 if (xfrm_msg) 4452 strvec_push(&cmd.args, xfrm_msg); 4453 } 4454 } 4455 4456 strvec_pushf(&cmd.env, "GIT_DIFF_PATH_COUNTER=%d", 4457 ++o->diff_path_counter); 4458 strvec_pushf(&cmd.env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); 4459 4460 diff_free_filespec_data(one); 4461 diff_free_filespec_data(two); 4462 cmd.use_shell = 1; 4463 cmd.no_stdout = quiet; 4464 rc = run_command(&cmd); 4465 if (!pgm->trust_exit_code && rc == 0) 4466 o->found_changes = 1; 4467 else if (pgm->trust_exit_code && rc == 0) 4468 ; /* nothing */ 4469 else if (pgm->trust_exit_code && rc == 1) 4470 o->found_changes = 1; 4471 else 4472 die(_("external diff died, stopping at %s"), name); 4473 4474 remove_tempfile(); 4475} 4476 4477static int similarity_index(struct diff_filepair *p) 4478{ 4479 return p->score * 100 / MAX_SCORE; 4480} 4481 4482static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev) 4483{ 4484 if (startup_info->have_repository) 4485 return repo_find_unique_abbrev(the_repository, oid, abbrev); 4486 else { 4487 char *hex = oid_to_hex(oid); 4488 if (abbrev < 0) 4489 abbrev = FALLBACK_DEFAULT_ABBREV; 4490 if (abbrev > the_hash_algo->hexsz) 4491 BUG("oid abbreviation out of range: %d", abbrev); 4492 if (abbrev) 4493 hex[abbrev] = '\0'; 4494 return hex; 4495 } 4496} 4497 4498static void fill_metainfo(struct strbuf *msg, 4499 const char *name, 4500 const char *other, 4501 struct diff_filespec *one, 4502 struct diff_filespec *two, 4503 struct diff_options *o, 4504 struct diff_filepair *p, 4505 int *must_show_header, 4506 int use_color) 4507{ 4508 const char *set = diff_get_color(use_color, DIFF_METAINFO); 4509 const char *reset = diff_get_color(use_color, DIFF_RESET); 4510 const char *line_prefix = diff_line_prefix(o); 4511 struct string_list *more_headers = NULL; 4512 4513 *must_show_header = 1; 4514 strbuf_init(msg, PATH_MAX * 2 + 300); 4515 switch (p->status) { 4516 case DIFF_STATUS_COPIED: 4517 strbuf_addf(msg, "%s%ssimilarity index %d%%", 4518 line_prefix, set, similarity_index(p)); 4519 strbuf_addf(msg, "%s\n%s%scopy from ", 4520 reset, line_prefix, set); 4521 quote_c_style(name, msg, NULL, 0); 4522 strbuf_addf(msg, "%s\n%s%scopy to ", reset, line_prefix, set); 4523 quote_c_style(other, msg, NULL, 0); 4524 strbuf_addf(msg, "%s\n", reset); 4525 break; 4526 case DIFF_STATUS_RENAMED: 4527 strbuf_addf(msg, "%s%ssimilarity index %d%%", 4528 line_prefix, set, similarity_index(p)); 4529 strbuf_addf(msg, "%s\n%s%srename from ", 4530 reset, line_prefix, set); 4531 quote_c_style(name, msg, NULL, 0); 4532 strbuf_addf(msg, "%s\n%s%srename to ", 4533 reset, line_prefix, set); 4534 quote_c_style(other, msg, NULL, 0); 4535 strbuf_addf(msg, "%s\n", reset); 4536 break; 4537 case DIFF_STATUS_MODIFIED: 4538 if (p->score) { 4539 strbuf_addf(msg, "%s%sdissimilarity index %d%%%s\n", 4540 line_prefix, 4541 set, similarity_index(p), reset); 4542 break; 4543 } 4544 /* fallthru */ 4545 default: 4546 *must_show_header = 0; 4547 } 4548 if ((more_headers = additional_headers(o, name))) { 4549 add_formatted_headers(msg, more_headers, 4550 line_prefix, set, reset); 4551 *must_show_header = 1; 4552 } 4553 if (one && two && !oideq(&one->oid, &two->oid)) { 4554 const unsigned hexsz = the_hash_algo->hexsz; 4555 int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV; 4556 4557 if (o->flags.full_index) 4558 abbrev = hexsz; 4559 4560 if (o->flags.binary) { 4561 mmfile_t mf; 4562 if ((!fill_mmfile(o->repo, &mf, one) && 4563 diff_filespec_is_binary(o->repo, one)) || 4564 (!fill_mmfile(o->repo, &mf, two) && 4565 diff_filespec_is_binary(o->repo, two))) 4566 abbrev = hexsz; 4567 } 4568 strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set, 4569 diff_abbrev_oid(&one->oid, abbrev), 4570 diff_abbrev_oid(&two->oid, abbrev)); 4571 if (one->mode == two->mode) 4572 strbuf_addf(msg, " %06o", one->mode); 4573 strbuf_addf(msg, "%s\n", reset); 4574 } 4575} 4576 4577static void run_diff_cmd(const struct external_diff *pgm, 4578 const char *name, 4579 const char *other, 4580 const char *attr_path, 4581 struct diff_filespec *one, 4582 struct diff_filespec *two, 4583 struct strbuf *msg, 4584 struct diff_options *o, 4585 struct diff_filepair *p) 4586{ 4587 const char *xfrm_msg = NULL; 4588 int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score; 4589 int must_show_header = 0; 4590 struct userdiff_driver *drv = NULL; 4591 4592 if (o->flags.allow_external || !o->ignore_driver_algorithm) 4593 drv = userdiff_find_by_path(o->repo->index, attr_path); 4594 4595 if (o->flags.allow_external && drv && drv->external.cmd) 4596 pgm = &drv->external; 4597 4598 if (msg) { 4599 /* 4600 * don't use colors when the header is intended for an 4601 * external diff driver 4602 */ 4603 fill_metainfo(msg, name, other, one, two, o, p, 4604 &must_show_header, 4605 want_color(o->use_color) && !pgm); 4606 xfrm_msg = msg->len ? msg->buf : NULL; 4607 } 4608 4609 if (pgm) { 4610 run_external_diff(pgm, name, other, one, two, xfrm_msg, o); 4611 return; 4612 } 4613 if (one && two) { 4614 if (!o->ignore_driver_algorithm && drv && drv->algorithm) 4615 set_diff_algorithm(o, drv->algorithm); 4616 4617 builtin_diff(name, other ? other : name, 4618 one, two, xfrm_msg, must_show_header, 4619 o, complete_rewrite); 4620 if (p->status == DIFF_STATUS_COPIED || 4621 p->status == DIFF_STATUS_RENAMED) 4622 o->found_changes = 1; 4623 } else { 4624 fprintf(o->file, "* Unmerged path %s\n", name); 4625 o->found_changes = 1; 4626 } 4627} 4628 4629static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate) 4630{ 4631 if (DIFF_FILE_VALID(one)) { 4632 if (!one->oid_valid) { 4633 struct stat st; 4634 if (one->is_stdin) { 4635 oidclr(&one->oid, the_repository->hash_algo); 4636 return; 4637 } 4638 if (lstat(one->path, &st) < 0) 4639 die_errno("stat '%s'", one->path); 4640 if (index_path(istate, &one->oid, one->path, &st, 0)) 4641 die("cannot hash %s", one->path); 4642 } 4643 } 4644 else 4645 oidclr(&one->oid, the_repository->hash_algo); 4646} 4647 4648static void strip_prefix(int prefix_length, const char **namep, const char **otherp) 4649{ 4650 /* Strip the prefix but do not molest /dev/null and absolute paths */ 4651 if (*namep && !is_absolute_path(*namep)) { 4652 *namep += prefix_length; 4653 if (**namep == '/') 4654 ++*namep; 4655 } 4656 if (*otherp && !is_absolute_path(*otherp)) { 4657 *otherp += prefix_length; 4658 if (**otherp == '/') 4659 ++*otherp; 4660 } 4661} 4662 4663static void run_diff(struct diff_filepair *p, struct diff_options *o) 4664{ 4665 const struct external_diff *pgm = external_diff(); 4666 struct strbuf msg; 4667 struct diff_filespec *one = p->one; 4668 struct diff_filespec *two = p->two; 4669 const char *name; 4670 const char *other; 4671 const char *attr_path; 4672 4673 name = one->path; 4674 other = (strcmp(name, two->path) ? two->path : NULL); 4675 attr_path = name; 4676 if (o->prefix_length) 4677 strip_prefix(o->prefix_length, &name, &other); 4678 4679 if (!o->flags.allow_external) 4680 pgm = NULL; 4681 4682 if (DIFF_PAIR_UNMERGED(p)) { 4683 run_diff_cmd(pgm, name, NULL, attr_path, 4684 NULL, NULL, NULL, o, p); 4685 return; 4686 } 4687 4688 diff_fill_oid_info(one, o->repo->index); 4689 diff_fill_oid_info(two, o->repo->index); 4690 4691 if (!pgm && 4692 DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && 4693 (S_IFMT & one->mode) != (S_IFMT & two->mode)) { 4694 /* 4695 * a filepair that changes between file and symlink 4696 * needs to be split into deletion and creation. 4697 */ 4698 struct diff_filespec *null = alloc_filespec(two->path); 4699 run_diff_cmd(NULL, name, other, attr_path, 4700 one, null, &msg, 4701 o, p); 4702 free(null); 4703 strbuf_release(&msg); 4704 4705 null = alloc_filespec(one->path); 4706 run_diff_cmd(NULL, name, other, attr_path, 4707 null, two, &msg, o, p); 4708 free(null); 4709 } 4710 else 4711 run_diff_cmd(pgm, name, other, attr_path, 4712 one, two, &msg, o, p); 4713 4714 strbuf_release(&msg); 4715} 4716 4717static void run_diffstat(struct diff_filepair *p, struct diff_options *o, 4718 struct diffstat_t *diffstat) 4719{ 4720 const char *name; 4721 const char *other; 4722 4723 if (!o->ignore_driver_algorithm) { 4724 struct userdiff_driver *drv = userdiff_find_by_path(o->repo->index, 4725 p->one->path); 4726 4727 if (drv && drv->algorithm) 4728 set_diff_algorithm(o, drv->algorithm); 4729 } 4730 4731 if (DIFF_PAIR_UNMERGED(p)) { 4732 /* unmerged */ 4733 builtin_diffstat(p->one->path, NULL, NULL, NULL, 4734 diffstat, o, p); 4735 return; 4736 } 4737 4738 name = p->one->path; 4739 other = (strcmp(name, p->two->path) ? p->two->path : NULL); 4740 4741 if (o->prefix_length) 4742 strip_prefix(o->prefix_length, &name, &other); 4743 4744 diff_fill_oid_info(p->one, o->repo->index); 4745 diff_fill_oid_info(p->two, o->repo->index); 4746 4747 builtin_diffstat(name, other, p->one, p->two, 4748 diffstat, o, p); 4749} 4750 4751static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) 4752{ 4753 const char *name; 4754 const char *other; 4755 const char *attr_path; 4756 4757 if (DIFF_PAIR_UNMERGED(p)) { 4758 /* unmerged */ 4759 return; 4760 } 4761 4762 name = p->one->path; 4763 other = (strcmp(name, p->two->path) ? p->two->path : NULL); 4764 attr_path = other ? other : name; 4765 4766 if (o->prefix_length) 4767 strip_prefix(o->prefix_length, &name, &other); 4768 4769 diff_fill_oid_info(p->one, o->repo->index); 4770 diff_fill_oid_info(p->two, o->repo->index); 4771 4772 builtin_checkdiff(name, other, attr_path, p->one, p->two, o); 4773} 4774 4775void repo_diff_setup(struct repository *r, struct diff_options *options) 4776{ 4777 memcpy(options, &default_diff_options, sizeof(*options)); 4778 4779 options->file = stdout; 4780 options->repo = r; 4781 4782 options->output_indicators[OUTPUT_INDICATOR_NEW] = '+'; 4783 options->output_indicators[OUTPUT_INDICATOR_OLD] = '-'; 4784 options->output_indicators[OUTPUT_INDICATOR_CONTEXT] = ' '; 4785 options->abbrev = DEFAULT_ABBREV; 4786 options->line_termination = '\n'; 4787 options->break_opt = -1; 4788 options->rename_limit = -1; 4789 options->dirstat_permille = diff_dirstat_permille_default; 4790 options->context = diff_context_default; 4791 options->interhunkcontext = diff_interhunk_context_default; 4792 options->ws_error_highlight = ws_error_highlight_default; 4793 options->flags.rename_empty = 1; 4794 options->flags.relative_name = diff_relative; 4795 options->objfind = NULL; 4796 4797 /* pathchange left =NULL by default */ 4798 options->change = diff_change; 4799 options->add_remove = diff_addremove; 4800 options->use_color = diff_use_color_default; 4801 options->detect_rename = diff_detect_rename_default; 4802 options->xdl_opts |= diff_algorithm; 4803 if (diff_indent_heuristic) 4804 DIFF_XDL_SET(options, INDENT_HEURISTIC); 4805 4806 options->orderfile = xstrdup_or_null(diff_order_file_cfg); 4807 4808 if (!options->flags.ignore_submodule_set) 4809 options->flags.ignore_untracked_in_submodules = 1; 4810 4811 if (diff_no_prefix) { 4812 diff_set_noprefix(options); 4813 } else if (!diff_mnemonic_prefix) { 4814 diff_set_default_prefix(options); 4815 } 4816 4817 options->color_moved = diff_color_moved_default; 4818 options->color_moved_ws_handling = diff_color_moved_ws_default; 4819} 4820 4821static const char diff_status_letters[] = { 4822 DIFF_STATUS_ADDED, 4823 DIFF_STATUS_COPIED, 4824 DIFF_STATUS_DELETED, 4825 DIFF_STATUS_MODIFIED, 4826 DIFF_STATUS_RENAMED, 4827 DIFF_STATUS_TYPE_CHANGED, 4828 DIFF_STATUS_UNKNOWN, 4829 DIFF_STATUS_UNMERGED, 4830 DIFF_STATUS_FILTER_AON, 4831 DIFF_STATUS_FILTER_BROKEN, 4832 '\0', 4833}; 4834 4835static unsigned int filter_bit['Z' + 1]; 4836 4837static void prepare_filter_bits(void) 4838{ 4839 int i; 4840 4841 if (!filter_bit[DIFF_STATUS_ADDED]) { 4842 for (i = 0; diff_status_letters[i]; i++) 4843 filter_bit[(int) diff_status_letters[i]] = (1 << i); 4844 } 4845} 4846 4847static unsigned filter_bit_tst(char status, const struct diff_options *opt) 4848{ 4849 return opt->filter & filter_bit[(int) status]; 4850} 4851 4852unsigned diff_filter_bit(char status) 4853{ 4854 prepare_filter_bits(); 4855 return filter_bit[(int) status]; 4856} 4857 4858int diff_check_follow_pathspec(struct pathspec *ps, int die_on_error) 4859{ 4860 unsigned forbidden_magic; 4861 4862 if (ps->nr != 1) { 4863 if (die_on_error) 4864 die(_("--follow requires exactly one pathspec")); 4865 return 0; 4866 } 4867 4868 forbidden_magic = ps->items[0].magic & ~(PATHSPEC_FROMTOP | 4869 PATHSPEC_LITERAL); 4870 if (forbidden_magic) { 4871 if (die_on_error) { 4872 struct strbuf sb = STRBUF_INIT; 4873 pathspec_magic_names(forbidden_magic, &sb); 4874 die(_("pathspec magic not supported by --follow: %s"), 4875 sb.buf); 4876 } 4877 return 0; 4878 } 4879 4880 return 1; 4881} 4882 4883void diff_setup_done(struct diff_options *options) 4884{ 4885 unsigned check_mask = DIFF_FORMAT_NAME | 4886 DIFF_FORMAT_NAME_STATUS | 4887 DIFF_FORMAT_CHECKDIFF | 4888 DIFF_FORMAT_NO_OUTPUT; 4889 /* 4890 * This must be signed because we're comparing against a potentially 4891 * negative value. 4892 */ 4893 const int hexsz = the_hash_algo->hexsz; 4894 4895 if (options->set_default) 4896 options->set_default(options); 4897 4898 if (HAS_MULTI_BITS(options->output_format & check_mask)) 4899 die(_("options '%s', '%s', '%s', and '%s' cannot be used together"), 4900 "--name-only", "--name-status", "--check", "-s"); 4901 4902 if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)) 4903 die(_("options '%s', '%s', and '%s' cannot be used together"), 4904 "-G", "-S", "--find-object"); 4905 4906 if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_G_REGEX_MASK)) 4907 die(_("options '%s' and '%s' cannot be used together, use '%s' with '%s'"), 4908 "-G", "--pickaxe-regex", "--pickaxe-regex", "-S"); 4909 4910 if (HAS_MULTI_BITS(options->pickaxe_opts & DIFF_PICKAXE_KINDS_ALL_OBJFIND_MASK)) 4911 die(_("options '%s' and '%s' cannot be used together, use '%s' with '%s' and '%s'"), 4912 "--pickaxe-all", "--find-object", "--pickaxe-all", "-G", "-S"); 4913 4914 /* 4915 * Most of the time we can say "there are changes" 4916 * only by checking if there are changed paths, but 4917 * --ignore-whitespace* options force us to look 4918 * inside contents. 4919 */ 4920 4921 if ((options->xdl_opts & XDF_WHITESPACE_FLAGS) || 4922 options->ignore_regex_nr) 4923 options->flags.diff_from_contents = 1; 4924 else 4925 options->flags.diff_from_contents = 0; 4926 4927 if (options->flags.find_copies_harder) 4928 options->detect_rename = DIFF_DETECT_COPY; 4929 4930 if (!options->flags.relative_name) 4931 options->prefix = NULL; 4932 if (options->prefix) 4933 options->prefix_length = strlen(options->prefix); 4934 else 4935 options->prefix_length = 0; 4936 4937 /* 4938 * --name-only, --name-status, --checkdiff, and -s 4939 * turn other output format off. 4940 */ 4941 if (options->output_format & (DIFF_FORMAT_NAME | 4942 DIFF_FORMAT_NAME_STATUS | 4943 DIFF_FORMAT_CHECKDIFF | 4944 DIFF_FORMAT_NO_OUTPUT)) 4945 options->output_format &= ~(DIFF_FORMAT_RAW | 4946 DIFF_FORMAT_NUMSTAT | 4947 DIFF_FORMAT_DIFFSTAT | 4948 DIFF_FORMAT_SHORTSTAT | 4949 DIFF_FORMAT_DIRSTAT | 4950 DIFF_FORMAT_SUMMARY | 4951 DIFF_FORMAT_PATCH); 4952 4953 /* 4954 * These cases always need recursive; we do not drop caller-supplied 4955 * recursive bits for other formats here. 4956 */ 4957 if (options->output_format & (DIFF_FORMAT_PATCH | 4958 DIFF_FORMAT_NUMSTAT | 4959 DIFF_FORMAT_DIFFSTAT | 4960 DIFF_FORMAT_SHORTSTAT | 4961 DIFF_FORMAT_DIRSTAT | 4962 DIFF_FORMAT_SUMMARY | 4963 DIFF_FORMAT_CHECKDIFF)) 4964 options->flags.recursive = 1; 4965 /* 4966 * Also pickaxe would not work very well if you do not say recursive 4967 */ 4968 if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) 4969 options->flags.recursive = 1; 4970 /* 4971 * When patches are generated, submodules diffed against the work tree 4972 * must be checked for dirtiness too so it can be shown in the output 4973 */ 4974 if (options->output_format & DIFF_FORMAT_PATCH) 4975 options->flags.dirty_submodules = 1; 4976 4977 if (options->detect_rename && options->rename_limit < 0) 4978 options->rename_limit = diff_rename_limit_default; 4979 if (hexsz < options->abbrev) 4980 options->abbrev = hexsz; /* full */ 4981 4982 /* 4983 * It does not make sense to show the first hit we happened 4984 * to have found. It does not make sense not to return with 4985 * exit code in such a case either. 4986 */ 4987 if (options->flags.quick) { 4988 options->output_format = DIFF_FORMAT_NO_OUTPUT; 4989 options->flags.exit_with_status = 1; 4990 } 4991 4992 /* 4993 * External diffs could declare non-identical contents equal 4994 * (think diff --ignore-space-change). 4995 */ 4996 if (options->flags.allow_external && options->flags.exit_with_status) 4997 options->flags.diff_from_contents = 1; 4998 4999 options->diff_path_counter = 0; 5000 5001 if (options->flags.follow_renames) 5002 diff_check_follow_pathspec(&options->pathspec, 1); 5003 5004 if (!options->use_color || 5005 (options->flags.allow_external && external_diff())) 5006 options->color_moved = 0; 5007 5008 if (options->filter_not) { 5009 if (!options->filter) 5010 options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON]; 5011 options->filter &= ~options->filter_not; 5012 } 5013} 5014 5015int parse_long_opt(const char *opt, const char **argv, 5016 const char **optarg) 5017{ 5018 const char *arg = argv[0]; 5019 if (!skip_prefix(arg, "--", &arg)) 5020 return 0; 5021 if (!skip_prefix(arg, opt, &arg)) 5022 return 0; 5023 if (*arg == '=') { /* stuck form: --option=value */ 5024 *optarg = arg + 1; 5025 return 1; 5026 } 5027 if (*arg != '\0') 5028 return 0; 5029 /* separate form: --option value */ 5030 if (!argv[1]) 5031 die("Option '--%s' requires a value", opt); 5032 *optarg = argv[1]; 5033 return 2; 5034} 5035 5036static int diff_opt_stat(const struct option *opt, const char *value, int unset) 5037{ 5038 struct diff_options *options = opt->value; 5039 int width = options->stat_width; 5040 int name_width = options->stat_name_width; 5041 int graph_width = options->stat_graph_width; 5042 int count = options->stat_count; 5043 char *end; 5044 5045 BUG_ON_OPT_NEG(unset); 5046 5047 if (!strcmp(opt->long_name, "stat")) { 5048 if (value) { 5049 width = strtoul(value, &end, 10); 5050 if (*end == ',') 5051 name_width = strtoul(end+1, &end, 10); 5052 if (*end == ',') 5053 count = strtoul(end+1, &end, 10); 5054 if (*end) 5055 return error(_("invalid --stat value: %s"), value); 5056 } 5057 } else if (!strcmp(opt->long_name, "stat-width")) { 5058 width = strtoul(value, &end, 10); 5059 if (*end) 5060 return error(_("%s expects a numerical value"), 5061 opt->long_name); 5062 } else if (!strcmp(opt->long_name, "stat-name-width")) { 5063 name_width = strtoul(value, &end, 10); 5064 if (*end) 5065 return error(_("%s expects a numerical value"), 5066 opt->long_name); 5067 } else if (!strcmp(opt->long_name, "stat-graph-width")) { 5068 graph_width = strtoul(value, &end, 10); 5069 if (*end) 5070 return error(_("%s expects a numerical value"), 5071 opt->long_name); 5072 } else if (!strcmp(opt->long_name, "stat-count")) { 5073 count = strtoul(value, &end, 10); 5074 if (*end) 5075 return error(_("%s expects a numerical value"), 5076 opt->long_name); 5077 } else 5078 BUG("%s should not get here", opt->long_name); 5079 5080 options->output_format &= ~DIFF_FORMAT_NO_OUTPUT; 5081 options->output_format |= DIFF_FORMAT_DIFFSTAT; 5082 options->stat_name_width = name_width; 5083 options->stat_graph_width = graph_width; 5084 options->stat_width = width; 5085 options->stat_count = count; 5086 return 0; 5087} 5088 5089static int parse_dirstat_opt(struct diff_options *options, const char *params) 5090{ 5091 struct strbuf errmsg = STRBUF_INIT; 5092 if (parse_dirstat_params(options, params, &errmsg)) 5093 die(_("Failed to parse --dirstat/-X option parameter:\n%s"), 5094 errmsg.buf); 5095 strbuf_release(&errmsg); 5096 /* 5097 * The caller knows a dirstat-related option is given from the command 5098 * line; allow it to say "return this_function();" 5099 */ 5100 options->output_format &= ~DIFF_FORMAT_NO_OUTPUT; 5101 options->output_format |= DIFF_FORMAT_DIRSTAT; 5102 return 1; 5103} 5104 5105static int diff_opt_diff_filter(const struct option *option, 5106 const char *optarg, int unset) 5107{ 5108 struct diff_options *opt = option->value; 5109 int i, optch; 5110 5111 BUG_ON_OPT_NEG(unset); 5112 prepare_filter_bits(); 5113 5114 for (i = 0; (optch = optarg[i]) != '\0'; i++) { 5115 unsigned int bit; 5116 int negate; 5117 5118 if ('a' <= optch && optch <= 'z') { 5119 negate = 1; 5120 optch = toupper(optch); 5121 } else { 5122 negate = 0; 5123 } 5124 5125 bit = (0 <= optch && optch <= 'Z') ? filter_bit[optch] : 0; 5126 if (!bit) 5127 return error(_("unknown change class '%c' in --diff-filter=%s"), 5128 optarg[i], optarg); 5129 if (negate) 5130 opt->filter_not |= bit; 5131 else 5132 opt->filter |= bit; 5133 } 5134 return 0; 5135} 5136 5137static void enable_patch_output(int *fmt) 5138{ 5139 *fmt &= ~DIFF_FORMAT_NO_OUTPUT; 5140 *fmt |= DIFF_FORMAT_PATCH; 5141} 5142 5143static int diff_opt_ws_error_highlight(const struct option *option, 5144 const char *arg, int unset) 5145{ 5146 struct diff_options *opt = option->value; 5147 int val = parse_ws_error_highlight(arg); 5148 5149 BUG_ON_OPT_NEG(unset); 5150 if (val < 0) 5151 return error(_("unknown value after ws-error-highlight=%.*s"), 5152 -1 - val, arg); 5153 opt->ws_error_highlight = val; 5154 return 0; 5155} 5156 5157static int diff_opt_find_object(const struct option *option, 5158 const char *arg, int unset) 5159{ 5160 struct diff_options *opt = option->value; 5161 struct object_id oid; 5162 5163 BUG_ON_OPT_NEG(unset); 5164 if (repo_get_oid(the_repository, arg, &oid)) 5165 return error(_("unable to resolve '%s'"), arg); 5166 5167 if (!opt->objfind) 5168 CALLOC_ARRAY(opt->objfind, 1); 5169 5170 opt->pickaxe_opts |= DIFF_PICKAXE_KIND_OBJFIND; 5171 opt->flags.recursive = 1; 5172 opt->flags.tree_in_recursive = 1; 5173 oidset_insert(opt->objfind, &oid); 5174 return 0; 5175} 5176 5177static int diff_opt_anchored(const struct option *opt, 5178 const char *arg, int unset) 5179{ 5180 struct diff_options *options = opt->value; 5181 5182 BUG_ON_OPT_NEG(unset); 5183 options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF); 5184 ALLOC_GROW(options->anchors, options->anchors_nr + 1, 5185 options->anchors_alloc); 5186 options->anchors[options->anchors_nr++] = xstrdup(arg); 5187 return 0; 5188} 5189 5190static int diff_opt_binary(const struct option *opt, 5191 const char *arg, int unset) 5192{ 5193 struct diff_options *options = opt->value; 5194 5195 BUG_ON_OPT_NEG(unset); 5196 BUG_ON_OPT_ARG(arg); 5197 enable_patch_output(&options->output_format); 5198 options->flags.binary = 1; 5199 return 0; 5200} 5201 5202static int diff_opt_break_rewrites(const struct option *opt, 5203 const char *arg, int unset) 5204{ 5205 int *break_opt = opt->value; 5206 int opt1, opt2; 5207 5208 BUG_ON_OPT_NEG(unset); 5209 if (!arg) 5210 arg = ""; 5211 opt1 = parse_rename_score(&arg); 5212 if (*arg == 0) 5213 opt2 = 0; 5214 else if (*arg != '/') 5215 return error(_("%s expects <n>/<m> form"), opt->long_name); 5216 else { 5217 arg++; 5218 opt2 = parse_rename_score(&arg); 5219 } 5220 if (*arg != 0) 5221 return error(_("%s expects <n>/<m> form"), opt->long_name); 5222 *break_opt = opt1 | (opt2 << 16); 5223 return 0; 5224} 5225 5226static int diff_opt_char(const struct option *opt, 5227 const char *arg, int unset) 5228{ 5229 char *value = opt->value; 5230 5231 BUG_ON_OPT_NEG(unset); 5232 if (arg[1]) 5233 return error(_("%s expects a character, got '%s'"), 5234 opt->long_name, arg); 5235 *value = arg[0]; 5236 return 0; 5237} 5238 5239static int diff_opt_color_moved(const struct option *opt, 5240 const char *arg, int unset) 5241{ 5242 struct diff_options *options = opt->value; 5243 5244 if (unset) { 5245 options->color_moved = COLOR_MOVED_NO; 5246 } else if (!arg) { 5247 if (diff_color_moved_default) 5248 options->color_moved = diff_color_moved_default; 5249 if (options->color_moved == COLOR_MOVED_NO) 5250 options->color_moved = COLOR_MOVED_DEFAULT; 5251 } else { 5252 int cm = parse_color_moved(arg); 5253 if (cm < 0) 5254 return error(_("bad --color-moved argument: %s"), arg); 5255 options->color_moved = cm; 5256 } 5257 return 0; 5258} 5259 5260static int diff_opt_color_moved_ws(const struct option *opt, 5261 const char *arg, int unset) 5262{ 5263 struct diff_options *options = opt->value; 5264 unsigned cm; 5265 5266 if (unset) { 5267 options->color_moved_ws_handling = 0; 5268 return 0; 5269 } 5270 5271 cm = parse_color_moved_ws(arg); 5272 if (cm & COLOR_MOVED_WS_ERROR) 5273 return error(_("invalid mode '%s' in --color-moved-ws"), arg); 5274 options->color_moved_ws_handling = cm; 5275 return 0; 5276} 5277 5278static int diff_opt_color_words(const struct option *opt, 5279 const char *arg, int unset) 5280{ 5281 struct diff_options *options = opt->value; 5282 5283 BUG_ON_OPT_NEG(unset); 5284 options->use_color = 1; 5285 options->word_diff = DIFF_WORDS_COLOR; 5286 options->word_regex = arg; 5287 return 0; 5288} 5289 5290static int diff_opt_compact_summary(const struct option *opt, 5291 const char *arg, int unset) 5292{ 5293 struct diff_options *options = opt->value; 5294 5295 BUG_ON_OPT_ARG(arg); 5296 if (unset) { 5297 options->flags.stat_with_summary = 0; 5298 } else { 5299 options->flags.stat_with_summary = 1; 5300 options->output_format &= ~DIFF_FORMAT_NO_OUTPUT; 5301 options->output_format |= DIFF_FORMAT_DIFFSTAT; 5302 } 5303 return 0; 5304} 5305 5306static int diff_opt_diff_algorithm(const struct option *opt, 5307 const char *arg, int unset) 5308{ 5309 struct diff_options *options = opt->value; 5310 5311 BUG_ON_OPT_NEG(unset); 5312 5313 if (set_diff_algorithm(options, arg)) 5314 return error(_("option diff-algorithm accepts \"myers\", " 5315 "\"minimal\", \"patience\" and \"histogram\"")); 5316 5317 options->ignore_driver_algorithm = 1; 5318 5319 return 0; 5320} 5321 5322static int diff_opt_diff_algorithm_no_arg(const struct option *opt, 5323 const char *arg, int unset) 5324{ 5325 struct diff_options *options = opt->value; 5326 5327 BUG_ON_OPT_NEG(unset); 5328 BUG_ON_OPT_ARG(arg); 5329 5330 if (set_diff_algorithm(options, opt->long_name)) 5331 BUG("available diff algorithms include \"myers\", " 5332 "\"minimal\", \"patience\" and \"histogram\""); 5333 5334 options->ignore_driver_algorithm = 1; 5335 5336 return 0; 5337} 5338 5339static int diff_opt_dirstat(const struct option *opt, 5340 const char *arg, int unset) 5341{ 5342 struct diff_options *options = opt->value; 5343 5344 BUG_ON_OPT_NEG(unset); 5345 if (!strcmp(opt->long_name, "cumulative")) { 5346 if (arg) 5347 BUG("how come --cumulative take a value?"); 5348 arg = "cumulative"; 5349 } else if (!strcmp(opt->long_name, "dirstat-by-file")) 5350 parse_dirstat_opt(options, "files"); 5351 parse_dirstat_opt(options, arg ? arg : ""); 5352 return 0; 5353} 5354 5355static int diff_opt_find_copies(const struct option *opt, 5356 const char *arg, int unset) 5357{ 5358 struct diff_options *options = opt->value; 5359 5360 BUG_ON_OPT_NEG(unset); 5361 if (!arg) 5362 arg = ""; 5363 options->rename_score = parse_rename_score(&arg); 5364 if (*arg != 0) 5365 return error(_("invalid argument to %s"), opt->long_name); 5366 5367 if (options->detect_rename == DIFF_DETECT_COPY) 5368 options->flags.find_copies_harder = 1; 5369 else 5370 options->detect_rename = DIFF_DETECT_COPY; 5371 5372 return 0; 5373} 5374 5375static int diff_opt_find_renames(const struct option *opt, 5376 const char *arg, int unset) 5377{ 5378 struct diff_options *options = opt->value; 5379 5380 BUG_ON_OPT_NEG(unset); 5381 if (!arg) 5382 arg = ""; 5383 options->rename_score = parse_rename_score(&arg); 5384 if (*arg != 0) 5385 return error(_("invalid argument to %s"), opt->long_name); 5386 5387 options->detect_rename = DIFF_DETECT_RENAME; 5388 return 0; 5389} 5390 5391static int diff_opt_follow(const struct option *opt, 5392 const char *arg, int unset) 5393{ 5394 struct diff_options *options = opt->value; 5395 5396 BUG_ON_OPT_ARG(arg); 5397 if (unset) { 5398 options->flags.follow_renames = 0; 5399 options->flags.default_follow_renames = 0; 5400 } else { 5401 options->flags.follow_renames = 1; 5402 } 5403 return 0; 5404} 5405 5406static int diff_opt_ignore_submodules(const struct option *opt, 5407 const char *arg, int unset) 5408{ 5409 struct diff_options *options = opt->value; 5410 5411 BUG_ON_OPT_NEG(unset); 5412 if (!arg) 5413 arg = "all"; 5414 options->flags.override_submodule_config = 1; 5415 handle_ignore_submodules_arg(options, arg); 5416 return 0; 5417} 5418 5419static int diff_opt_line_prefix(const struct option *opt, 5420 const char *optarg, int unset) 5421{ 5422 struct diff_options *options = opt->value; 5423 5424 BUG_ON_OPT_NEG(unset); 5425 options->line_prefix = optarg; 5426 graph_setup_line_prefix(options); 5427 return 0; 5428} 5429 5430static int diff_opt_no_prefix(const struct option *opt, 5431 const char *optarg, int unset) 5432{ 5433 struct diff_options *options = opt->value; 5434 5435 BUG_ON_OPT_NEG(unset); 5436 BUG_ON_OPT_ARG(optarg); 5437 diff_set_noprefix(options); 5438 return 0; 5439} 5440 5441static int diff_opt_default_prefix(const struct option *opt, 5442 const char *optarg, int unset) 5443{ 5444 struct diff_options *options = opt->value; 5445 5446 BUG_ON_OPT_NEG(unset); 5447 BUG_ON_OPT_ARG(optarg); 5448 FREE_AND_NULL(diff_src_prefix); 5449 FREE_AND_NULL(diff_dst_prefix); 5450 diff_set_default_prefix(options); 5451 return 0; 5452} 5453 5454static enum parse_opt_result diff_opt_output(struct parse_opt_ctx_t *ctx, 5455 const struct option *opt, 5456 const char *arg, int unset) 5457{ 5458 struct diff_options *options = opt->value; 5459 char *path; 5460 5461 BUG_ON_OPT_NEG(unset); 5462 path = prefix_filename(ctx->prefix, arg); 5463 options->file = xfopen(path, "w"); 5464 options->close_file = 1; 5465 if (options->use_color != GIT_COLOR_ALWAYS) 5466 options->use_color = GIT_COLOR_NEVER; 5467 free(path); 5468 return 0; 5469} 5470 5471static int diff_opt_patience(const struct option *opt, 5472 const char *arg, int unset) 5473{ 5474 struct diff_options *options = opt->value; 5475 int i; 5476 5477 BUG_ON_OPT_NEG(unset); 5478 BUG_ON_OPT_ARG(arg); 5479 /* 5480 * Both --patience and --anchored use PATIENCE_DIFF 5481 * internally, so remove any anchors previously 5482 * specified. 5483 */ 5484 for (i = 0; i < options->anchors_nr; i++) 5485 free(options->anchors[i]); 5486 options->anchors_nr = 0; 5487 options->ignore_driver_algorithm = 1; 5488 5489 return set_diff_algorithm(options, "patience"); 5490} 5491 5492static int diff_opt_ignore_regex(const struct option *opt, 5493 const char *arg, int unset) 5494{ 5495 struct diff_options *options = opt->value; 5496 regex_t *regex; 5497 5498 BUG_ON_OPT_NEG(unset); 5499 5500 regex = xmalloc(sizeof(*regex)); 5501 if (regcomp(regex, arg, REG_EXTENDED | REG_NEWLINE)) { 5502 free(regex); 5503 return error(_("invalid regex given to -I: '%s'"), arg); 5504 } 5505 5506 ALLOC_GROW(options->ignore_regex, options->ignore_regex_nr + 1, 5507 options->ignore_regex_alloc); 5508 options->ignore_regex[options->ignore_regex_nr++] = regex; 5509 return 0; 5510} 5511 5512static int diff_opt_pickaxe_regex(const struct option *opt, 5513 const char *arg, int unset) 5514{ 5515 struct diff_options *options = opt->value; 5516 5517 BUG_ON_OPT_NEG(unset); 5518 options->pickaxe = arg; 5519 options->pickaxe_opts |= DIFF_PICKAXE_KIND_G; 5520 if (arg && !*arg) 5521 return error(_("-G requires a non-empty argument")); 5522 return 0; 5523} 5524 5525static int diff_opt_pickaxe_string(const struct option *opt, 5526 const char *arg, int unset) 5527{ 5528 struct diff_options *options = opt->value; 5529 5530 BUG_ON_OPT_NEG(unset); 5531 options->pickaxe = arg; 5532 options->pickaxe_opts |= DIFF_PICKAXE_KIND_S; 5533 if (arg && !*arg) 5534 return error(_("-S requires a non-empty argument")); 5535 return 0; 5536} 5537 5538static int diff_opt_relative(const struct option *opt, 5539 const char *arg, int unset) 5540{ 5541 struct diff_options *options = opt->value; 5542 5543 options->flags.relative_name = !unset; 5544 if (arg) 5545 options->prefix = arg; 5546 return 0; 5547} 5548 5549static int diff_opt_submodule(const struct option *opt, 5550 const char *arg, int unset) 5551{ 5552 struct diff_options *options = opt->value; 5553 5554 BUG_ON_OPT_NEG(unset); 5555 if (!arg) 5556 arg = "log"; 5557 if (parse_submodule_params(options, arg)) 5558 return error(_("failed to parse --submodule option parameter: '%s'"), 5559 arg); 5560 return 0; 5561} 5562 5563static int diff_opt_textconv(const struct option *opt, 5564 const char *arg, int unset) 5565{ 5566 struct diff_options *options = opt->value; 5567 5568 BUG_ON_OPT_ARG(arg); 5569 if (unset) { 5570 options->flags.allow_textconv = 0; 5571 } else { 5572 options->flags.allow_textconv = 1; 5573 options->flags.textconv_set_via_cmdline = 1; 5574 } 5575 return 0; 5576} 5577 5578static int diff_opt_unified(const struct option *opt, 5579 const char *arg, int unset) 5580{ 5581 struct diff_options *options = opt->value; 5582 char *s; 5583 5584 BUG_ON_OPT_NEG(unset); 5585 5586 if (arg) { 5587 options->context = strtol(arg, &s, 10); 5588 if (*s) 5589 return error(_("%s expects a numerical value"), "--unified"); 5590 } 5591 enable_patch_output(&options->output_format); 5592 5593 return 0; 5594} 5595 5596static int diff_opt_word_diff(const struct option *opt, 5597 const char *arg, int unset) 5598{ 5599 struct diff_options *options = opt->value; 5600 5601 BUG_ON_OPT_NEG(unset); 5602 if (arg) { 5603 if (!strcmp(arg, "plain")) 5604 options->word_diff = DIFF_WORDS_PLAIN; 5605 else if (!strcmp(arg, "color")) { 5606 options->use_color = 1; 5607 options->word_diff = DIFF_WORDS_COLOR; 5608 } 5609 else if (!strcmp(arg, "porcelain")) 5610 options->word_diff = DIFF_WORDS_PORCELAIN; 5611 else if (!strcmp(arg, "none")) 5612 options->word_diff = DIFF_WORDS_NONE; 5613 else 5614 return error(_("bad --word-diff argument: %s"), arg); 5615 } else { 5616 if (options->word_diff == DIFF_WORDS_NONE) 5617 options->word_diff = DIFF_WORDS_PLAIN; 5618 } 5619 return 0; 5620} 5621 5622static int diff_opt_word_diff_regex(const struct option *opt, 5623 const char *arg, int unset) 5624{ 5625 struct diff_options *options = opt->value; 5626 5627 BUG_ON_OPT_NEG(unset); 5628 if (options->word_diff == DIFF_WORDS_NONE) 5629 options->word_diff = DIFF_WORDS_PLAIN; 5630 options->word_regex = arg; 5631 return 0; 5632} 5633 5634static int diff_opt_rotate_to(const struct option *opt, const char *arg, int unset) 5635{ 5636 struct diff_options *options = opt->value; 5637 5638 BUG_ON_OPT_NEG(unset); 5639 if (!strcmp(opt->long_name, "skip-to")) 5640 options->skip_instead_of_rotate = 1; 5641 else 5642 options->skip_instead_of_rotate = 0; 5643 options->rotate_to = arg; 5644 return 0; 5645} 5646 5647/* 5648 * Consider adding new flags to __git_diff_common_options 5649 * in contrib/completion/git-completion.bash 5650 */ 5651struct option *add_diff_options(const struct option *opts, 5652 struct diff_options *options) 5653{ 5654 struct option parseopts[] = { 5655 OPT_GROUP(N_("Diff output format options")), 5656 OPT_BITOP('p', "patch", &options->output_format, 5657 N_("generate patch"), 5658 DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT), 5659 OPT_SET_INT('s', "no-patch", &options->output_format, 5660 N_("suppress diff output"), DIFF_FORMAT_NO_OUTPUT), 5661 OPT_BITOP('u', NULL, &options->output_format, 5662 N_("generate patch"), 5663 DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT), 5664 OPT_CALLBACK_F('U', "unified", options, N_("<n>"), 5665 N_("generate diffs with <n> lines context"), 5666 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_unified), 5667 OPT_BOOL('W', "function-context", &options->flags.funccontext, 5668 N_("generate diffs with <n> lines context")), 5669 OPT_BITOP(0, "raw", &options->output_format, 5670 N_("generate the diff in raw format"), 5671 DIFF_FORMAT_RAW, DIFF_FORMAT_NO_OUTPUT), 5672 OPT_BITOP(0, "patch-with-raw", &options->output_format, 5673 N_("synonym for '-p --raw'"), 5674 DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW, 5675 DIFF_FORMAT_NO_OUTPUT), 5676 OPT_BITOP(0, "patch-with-stat", &options->output_format, 5677 N_("synonym for '-p --stat'"), 5678 DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT, 5679 DIFF_FORMAT_NO_OUTPUT), 5680 OPT_BITOP(0, "numstat", &options->output_format, 5681 N_("machine friendly --stat"), 5682 DIFF_FORMAT_NUMSTAT, DIFF_FORMAT_NO_OUTPUT), 5683 OPT_BITOP(0, "shortstat", &options->output_format, 5684 N_("output only the last line of --stat"), 5685 DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT), 5686 OPT_CALLBACK_F('X', "dirstat", options, N_("<param1>,<param2>..."), 5687 N_("output the distribution of relative amount of changes for each sub-directory"), 5688 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5689 diff_opt_dirstat), 5690 OPT_CALLBACK_F(0, "cumulative", options, NULL, 5691 N_("synonym for --dirstat=cumulative"), 5692 PARSE_OPT_NONEG | PARSE_OPT_NOARG, 5693 diff_opt_dirstat), 5694 OPT_CALLBACK_F(0, "dirstat-by-file", options, N_("<param1>,<param2>..."), 5695 N_("synonym for --dirstat=files,<param1>,<param2>..."), 5696 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5697 diff_opt_dirstat), 5698 OPT_BIT_F(0, "check", &options->output_format, 5699 N_("warn if changes introduce conflict markers or whitespace errors"), 5700 DIFF_FORMAT_CHECKDIFF, PARSE_OPT_NONEG), 5701 OPT_BITOP(0, "summary", &options->output_format, 5702 N_("condensed summary such as creations, renames and mode changes"), 5703 DIFF_FORMAT_SUMMARY, DIFF_FORMAT_NO_OUTPUT), 5704 OPT_BIT_F(0, "name-only", &options->output_format, 5705 N_("show only names of changed files"), 5706 DIFF_FORMAT_NAME, PARSE_OPT_NONEG), 5707 OPT_BIT_F(0, "name-status", &options->output_format, 5708 N_("show only names and status of changed files"), 5709 DIFF_FORMAT_NAME_STATUS, PARSE_OPT_NONEG), 5710 OPT_CALLBACK_F(0, "stat", options, N_("<width>[,<name-width>[,<count>]]"), 5711 N_("generate diffstat"), 5712 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_stat), 5713 OPT_CALLBACK_F(0, "stat-width", options, N_("<width>"), 5714 N_("generate diffstat with a given width"), 5715 PARSE_OPT_NONEG, diff_opt_stat), 5716 OPT_CALLBACK_F(0, "stat-name-width", options, N_("<width>"), 5717 N_("generate diffstat with a given name width"), 5718 PARSE_OPT_NONEG, diff_opt_stat), 5719 OPT_CALLBACK_F(0, "stat-graph-width", options, N_("<width>"), 5720 N_("generate diffstat with a given graph width"), 5721 PARSE_OPT_NONEG, diff_opt_stat), 5722 OPT_CALLBACK_F(0, "stat-count", options, N_("<count>"), 5723 N_("generate diffstat with limited lines"), 5724 PARSE_OPT_NONEG, diff_opt_stat), 5725 OPT_CALLBACK_F(0, "compact-summary", options, NULL, 5726 N_("generate compact summary in diffstat"), 5727 PARSE_OPT_NOARG, diff_opt_compact_summary), 5728 OPT_CALLBACK_F(0, "binary", options, NULL, 5729 N_("output a binary diff that can be applied"), 5730 PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_binary), 5731 OPT_BOOL(0, "full-index", &options->flags.full_index, 5732 N_("show full pre- and post-image object names on the \"index\" lines")), 5733 OPT_COLOR_FLAG(0, "color", &options->use_color, 5734 N_("show colored diff")), 5735 OPT_CALLBACK_F(0, "ws-error-highlight", options, N_("<kind>"), 5736 N_("highlight whitespace errors in the 'context', 'old' or 'new' lines in the diff"), 5737 PARSE_OPT_NONEG, diff_opt_ws_error_highlight), 5738 OPT_SET_INT('z', NULL, &options->line_termination, 5739 N_("do not munge pathnames and use NULs as output field terminators in --raw or --numstat"), 5740 0), 5741 OPT__ABBREV(&options->abbrev), 5742 OPT_STRING_F(0, "src-prefix", &options->a_prefix, N_("<prefix>"), 5743 N_("show the given source prefix instead of \"a/\""), 5744 PARSE_OPT_NONEG), 5745 OPT_STRING_F(0, "dst-prefix", &options->b_prefix, N_("<prefix>"), 5746 N_("show the given destination prefix instead of \"b/\""), 5747 PARSE_OPT_NONEG), 5748 OPT_CALLBACK_F(0, "line-prefix", options, N_("<prefix>"), 5749 N_("prepend an additional prefix to every line of output"), 5750 PARSE_OPT_NONEG, diff_opt_line_prefix), 5751 OPT_CALLBACK_F(0, "no-prefix", options, NULL, 5752 N_("do not show any source or destination prefix"), 5753 PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_no_prefix), 5754 OPT_CALLBACK_F(0, "default-prefix", options, NULL, 5755 N_("use default prefixes a/ and b/"), 5756 PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_default_prefix), 5757 OPT_INTEGER_F(0, "inter-hunk-context", &options->interhunkcontext, 5758 N_("show context between diff hunks up to the specified number of lines"), 5759 PARSE_OPT_NONEG), 5760 OPT_CALLBACK_F(0, "output-indicator-new", 5761 &options->output_indicators[OUTPUT_INDICATOR_NEW], 5762 N_("<char>"), 5763 N_("specify the character to indicate a new line instead of '+'"), 5764 PARSE_OPT_NONEG, diff_opt_char), 5765 OPT_CALLBACK_F(0, "output-indicator-old", 5766 &options->output_indicators[OUTPUT_INDICATOR_OLD], 5767 N_("<char>"), 5768 N_("specify the character to indicate an old line instead of '-'"), 5769 PARSE_OPT_NONEG, diff_opt_char), 5770 OPT_CALLBACK_F(0, "output-indicator-context", 5771 &options->output_indicators[OUTPUT_INDICATOR_CONTEXT], 5772 N_("<char>"), 5773 N_("specify the character to indicate a context instead of ' '"), 5774 PARSE_OPT_NONEG, diff_opt_char), 5775 5776 OPT_GROUP(N_("Diff rename options")), 5777 OPT_CALLBACK_F('B', "break-rewrites", &options->break_opt, N_("<n>[/<m>]"), 5778 N_("break complete rewrite changes into pairs of delete and create"), 5779 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5780 diff_opt_break_rewrites), 5781 OPT_CALLBACK_F('M', "find-renames", options, N_("<n>"), 5782 N_("detect renames"), 5783 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5784 diff_opt_find_renames), 5785 OPT_SET_INT_F('D', "irreversible-delete", &options->irreversible_delete, 5786 N_("omit the preimage for deletes"), 5787 1, PARSE_OPT_NONEG), 5788 OPT_CALLBACK_F('C', "find-copies", options, N_("<n>"), 5789 N_("detect copies"), 5790 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5791 diff_opt_find_copies), 5792 OPT_BOOL(0, "find-copies-harder", &options->flags.find_copies_harder, 5793 N_("use unmodified files as source to find copies")), 5794 OPT_SET_INT_F(0, "no-renames", &options->detect_rename, 5795 N_("disable rename detection"), 5796 0, PARSE_OPT_NONEG), 5797 OPT_BOOL(0, "rename-empty", &options->flags.rename_empty, 5798 N_("use empty blobs as rename source")), 5799 OPT_CALLBACK_F(0, "follow", options, NULL, 5800 N_("continue listing the history of a file beyond renames"), 5801 PARSE_OPT_NOARG, diff_opt_follow), 5802 OPT_INTEGER('l', NULL, &options->rename_limit, 5803 N_("prevent rename/copy detection if the number of rename/copy targets exceeds given limit")), 5804 5805 OPT_GROUP(N_("Diff algorithm options")), 5806 OPT_CALLBACK_F(0, "minimal", options, NULL, 5807 N_("produce the smallest possible diff"), 5808 PARSE_OPT_NONEG | PARSE_OPT_NOARG, 5809 diff_opt_diff_algorithm_no_arg), 5810 OPT_BIT_F('w', "ignore-all-space", &options->xdl_opts, 5811 N_("ignore whitespace when comparing lines"), 5812 XDF_IGNORE_WHITESPACE, PARSE_OPT_NONEG), 5813 OPT_BIT_F('b', "ignore-space-change", &options->xdl_opts, 5814 N_("ignore changes in amount of whitespace"), 5815 XDF_IGNORE_WHITESPACE_CHANGE, PARSE_OPT_NONEG), 5816 OPT_BIT_F(0, "ignore-space-at-eol", &options->xdl_opts, 5817 N_("ignore changes in whitespace at EOL"), 5818 XDF_IGNORE_WHITESPACE_AT_EOL, PARSE_OPT_NONEG), 5819 OPT_BIT_F(0, "ignore-cr-at-eol", &options->xdl_opts, 5820 N_("ignore carrier-return at the end of line"), 5821 XDF_IGNORE_CR_AT_EOL, PARSE_OPT_NONEG), 5822 OPT_BIT_F(0, "ignore-blank-lines", &options->xdl_opts, 5823 N_("ignore changes whose lines are all blank"), 5824 XDF_IGNORE_BLANK_LINES, PARSE_OPT_NONEG), 5825 OPT_CALLBACK_F('I', "ignore-matching-lines", options, N_("<regex>"), 5826 N_("ignore changes whose all lines match <regex>"), 5827 0, diff_opt_ignore_regex), 5828 OPT_BIT(0, "indent-heuristic", &options->xdl_opts, 5829 N_("heuristic to shift diff hunk boundaries for easy reading"), 5830 XDF_INDENT_HEURISTIC), 5831 OPT_CALLBACK_F(0, "patience", options, NULL, 5832 N_("generate diff using the \"patience diff\" algorithm"), 5833 PARSE_OPT_NONEG | PARSE_OPT_NOARG, 5834 diff_opt_patience), 5835 OPT_CALLBACK_F(0, "histogram", options, NULL, 5836 N_("generate diff using the \"histogram diff\" algorithm"), 5837 PARSE_OPT_NONEG | PARSE_OPT_NOARG, 5838 diff_opt_diff_algorithm_no_arg), 5839 OPT_CALLBACK_F(0, "diff-algorithm", options, N_("<algorithm>"), 5840 N_("choose a diff algorithm"), 5841 PARSE_OPT_NONEG, diff_opt_diff_algorithm), 5842 OPT_CALLBACK_F(0, "anchored", options, N_("<text>"), 5843 N_("generate diff using the \"anchored diff\" algorithm"), 5844 PARSE_OPT_NONEG, diff_opt_anchored), 5845 OPT_CALLBACK_F(0, "word-diff", options, N_("<mode>"), 5846 N_("show word diff, using <mode> to delimit changed words"), 5847 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_word_diff), 5848 OPT_CALLBACK_F(0, "word-diff-regex", options, N_("<regex>"), 5849 N_("use <regex> to decide what a word is"), 5850 PARSE_OPT_NONEG, diff_opt_word_diff_regex), 5851 OPT_CALLBACK_F(0, "color-words", options, N_("<regex>"), 5852 N_("equivalent to --word-diff=color --word-diff-regex=<regex>"), 5853 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_color_words), 5854 OPT_CALLBACK_F(0, "color-moved", options, N_("<mode>"), 5855 N_("moved lines of code are colored differently"), 5856 PARSE_OPT_OPTARG, diff_opt_color_moved), 5857 OPT_CALLBACK_F(0, "color-moved-ws", options, N_("<mode>"), 5858 N_("how white spaces are ignored in --color-moved"), 5859 0, diff_opt_color_moved_ws), 5860 5861 OPT_GROUP(N_("Other diff options")), 5862 OPT_CALLBACK_F(0, "relative", options, N_("<prefix>"), 5863 N_("when run from subdir, exclude changes outside and show relative paths"), 5864 PARSE_OPT_OPTARG, 5865 diff_opt_relative), 5866 OPT_BOOL('a', "text", &options->flags.text, 5867 N_("treat all files as text")), 5868 OPT_BOOL('R', NULL, &options->flags.reverse_diff, 5869 N_("swap two inputs, reverse the diff")), 5870 OPT_BOOL(0, "exit-code", &options->flags.exit_with_status, 5871 N_("exit with 1 if there were differences, 0 otherwise")), 5872 OPT_BOOL(0, "quiet", &options->flags.quick, 5873 N_("disable all output of the program")), 5874 OPT_BOOL(0, "ext-diff", &options->flags.allow_external, 5875 N_("allow an external diff helper to be executed")), 5876 OPT_CALLBACK_F(0, "textconv", options, NULL, 5877 N_("run external text conversion filters when comparing binary files"), 5878 PARSE_OPT_NOARG, diff_opt_textconv), 5879 OPT_CALLBACK_F(0, "ignore-submodules", options, N_("<when>"), 5880 N_("ignore changes to submodules in the diff generation"), 5881 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5882 diff_opt_ignore_submodules), 5883 OPT_CALLBACK_F(0, "submodule", options, N_("<format>"), 5884 N_("specify how differences in submodules are shown"), 5885 PARSE_OPT_NONEG | PARSE_OPT_OPTARG, 5886 diff_opt_submodule), 5887 OPT_SET_INT_F(0, "ita-invisible-in-index", &options->ita_invisible_in_index, 5888 N_("hide 'git add -N' entries from the index"), 5889 1, PARSE_OPT_NONEG), 5890 OPT_SET_INT_F(0, "ita-visible-in-index", &options->ita_invisible_in_index, 5891 N_("treat 'git add -N' entries as real in the index"), 5892 0, PARSE_OPT_NONEG), 5893 OPT_CALLBACK_F('S', NULL, options, N_("<string>"), 5894 N_("look for differences that change the number of occurrences of the specified string"), 5895 0, diff_opt_pickaxe_string), 5896 OPT_CALLBACK_F('G', NULL, options, N_("<regex>"), 5897 N_("look for differences that change the number of occurrences of the specified regex"), 5898 0, diff_opt_pickaxe_regex), 5899 OPT_BIT_F(0, "pickaxe-all", &options->pickaxe_opts, 5900 N_("show all changes in the changeset with -S or -G"), 5901 DIFF_PICKAXE_ALL, PARSE_OPT_NONEG), 5902 OPT_BIT_F(0, "pickaxe-regex", &options->pickaxe_opts, 5903 N_("treat <string> in -S as extended POSIX regular expression"), 5904 DIFF_PICKAXE_REGEX, PARSE_OPT_NONEG), 5905 OPT_FILENAME('O', NULL, &options->orderfile, 5906 N_("control the order in which files appear in the output")), 5907 OPT_CALLBACK_F(0, "rotate-to", options, N_("<path>"), 5908 N_("show the change in the specified path first"), 5909 PARSE_OPT_NONEG, diff_opt_rotate_to), 5910 OPT_CALLBACK_F(0, "skip-to", options, N_("<path>"), 5911 N_("skip the output to the specified path"), 5912 PARSE_OPT_NONEG, diff_opt_rotate_to), 5913 OPT_CALLBACK_F(0, "find-object", options, N_("<object-id>"), 5914 N_("look for differences that change the number of occurrences of the specified object"), 5915 PARSE_OPT_NONEG, diff_opt_find_object), 5916 OPT_CALLBACK_F(0, "diff-filter", options, N_("[(A|C|D|M|R|T|U|X|B)...[*]]"), 5917 N_("select files by diff type"), 5918 PARSE_OPT_NONEG, diff_opt_diff_filter), 5919 { 5920 .type = OPTION_CALLBACK, 5921 .long_name = "output", 5922 .value = options, 5923 .argh = N_("<file>"), 5924 .help = N_("output to a specific file"), 5925 .flags = PARSE_OPT_NONEG, 5926 .ll_callback = diff_opt_output, 5927 }, 5928 OPT_END() 5929 }; 5930 5931 return parse_options_concat(opts, parseopts); 5932} 5933 5934int diff_opt_parse(struct diff_options *options, 5935 const char **av, int ac, const char *prefix) 5936{ 5937 struct option no_options[] = { OPT_END() }; 5938 struct option *parseopts = add_diff_options(no_options, options); 5939 5940 if (!prefix) 5941 prefix = ""; 5942 5943 ac = parse_options(ac, av, prefix, parseopts, NULL, 5944 PARSE_OPT_KEEP_DASHDASH | 5945 PARSE_OPT_KEEP_UNKNOWN_OPT | 5946 PARSE_OPT_NO_INTERNAL_HELP | 5947 PARSE_OPT_ONE_SHOT | 5948 PARSE_OPT_STOP_AT_NON_OPTION); 5949 free(parseopts); 5950 5951 return ac; 5952} 5953 5954int parse_rename_score(const char **cp_p) 5955{ 5956 unsigned long num, scale; 5957 int ch, dot; 5958 const char *cp = *cp_p; 5959 5960 num = 0; 5961 scale = 1; 5962 dot = 0; 5963 for (;;) { 5964 ch = *cp; 5965 if ( !dot && ch == '.' ) { 5966 scale = 1; 5967 dot = 1; 5968 } else if ( ch == '%' ) { 5969 scale = dot ? scale*100 : 100; 5970 cp++; /* % is always at the end */ 5971 break; 5972 } else if ( ch >= '0' && ch <= '9' ) { 5973 if ( scale < 100000 ) { 5974 scale *= 10; 5975 num = (num*10) + (ch-'0'); 5976 } 5977 } else { 5978 break; 5979 } 5980 cp++; 5981 } 5982 *cp_p = cp; 5983 5984 /* user says num divided by scale and we say internally that 5985 * is MAX_SCORE * num / scale. 5986 */ 5987 return (int)((num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale)); 5988} 5989 5990struct diff_queue_struct diff_queued_diff; 5991 5992void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp) 5993{ 5994 ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc); 5995 queue->queue[queue->nr++] = dp; 5996} 5997 5998struct diff_filepair *diff_queue(struct diff_queue_struct *queue, 5999 struct diff_filespec *one, 6000 struct diff_filespec *two) 6001{ 6002 struct diff_filepair *dp = xcalloc(1, sizeof(*dp)); 6003 dp->one = one; 6004 dp->two = two; 6005 if (queue) 6006 diff_q(queue, dp); 6007 return dp; 6008} 6009 6010void diff_free_filepair(struct diff_filepair *p) 6011{ 6012 free_filespec(p->one); 6013 free_filespec(p->two); 6014 free(p); 6015} 6016 6017void diff_queue_init(struct diff_queue_struct *q) 6018{ 6019 struct diff_queue_struct blank = DIFF_QUEUE_INIT; 6020 memcpy(q, &blank, sizeof(*q)); 6021} 6022 6023void diff_queue_clear(struct diff_queue_struct *q) 6024{ 6025 for (int i = 0; i < q->nr; i++) 6026 diff_free_filepair(q->queue[i]); 6027 free(q->queue); 6028 diff_queue_init(q); 6029} 6030 6031const char *diff_aligned_abbrev(const struct object_id *oid, int len) 6032{ 6033 int abblen; 6034 const char *abbrev; 6035 6036 /* Do we want all 40 hex characters? */ 6037 if (len == the_hash_algo->hexsz) 6038 return oid_to_hex(oid); 6039 6040 /* An abbreviated value is fine, possibly followed by an ellipsis. */ 6041 abbrev = diff_abbrev_oid(oid, len); 6042 6043 if (!print_sha1_ellipsis()) 6044 return abbrev; 6045 6046 abblen = strlen(abbrev); 6047 6048 /* 6049 * In well-behaved cases, where the abbreviated result is the 6050 * same as the requested length, append three dots after the 6051 * abbreviation (hence the whole logic is limited to the case 6052 * where abblen < 37); when the actual abbreviated result is a 6053 * bit longer than the requested length, we reduce the number 6054 * of dots so that they match the well-behaved ones. However, 6055 * if the actual abbreviation is longer than the requested 6056 * length by more than three, we give up on aligning, and add 6057 * three dots anyway, to indicate that the output is not the 6058 * full object name. Yes, this may be suboptimal, but this 6059 * appears only in "diff --raw --abbrev" output and it is not 6060 * worth the effort to change it now. Note that this would 6061 * likely to work fine when the automatic sizing of default 6062 * abbreviation length is used--we would be fed -1 in "len" in 6063 * that case, and will end up always appending three-dots, but 6064 * the automatic sizing is supposed to give abblen that ensures 6065 * uniqueness across all objects (statistically speaking). 6066 */ 6067 if (abblen < the_hash_algo->hexsz - 3) { 6068 static char hex[GIT_MAX_HEXSZ + 1]; 6069 if (len < abblen && abblen <= len + 2) 6070 xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, ".."); 6071 else 6072 xsnprintf(hex, sizeof(hex), "%s...", abbrev); 6073 return hex; 6074 } 6075 6076 return oid_to_hex(oid); 6077} 6078 6079static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) 6080{ 6081 int line_termination = opt->line_termination; 6082 int inter_name_termination = line_termination ? '\t' : '\0'; 6083 6084 fprintf(opt->file, "%s", diff_line_prefix(opt)); 6085 if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) { 6086 fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode, 6087 diff_aligned_abbrev(&p->one->oid, opt->abbrev)); 6088 fprintf(opt->file, "%s ", 6089 diff_aligned_abbrev(&p->two->oid, opt->abbrev)); 6090 } 6091 if (p->score) { 6092 fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p), 6093 inter_name_termination); 6094 } else { 6095 fprintf(opt->file, "%c%c", p->status, inter_name_termination); 6096 } 6097 6098 if (p->status == DIFF_STATUS_COPIED || 6099 p->status == DIFF_STATUS_RENAMED) { 6100 const char *name_a, *name_b; 6101 name_a = p->one->path; 6102 name_b = p->two->path; 6103 strip_prefix(opt->prefix_length, &name_a, &name_b); 6104 write_name_quoted(name_a, opt->file, inter_name_termination); 6105 write_name_quoted(name_b, opt->file, line_termination); 6106 } else { 6107 const char *name_a, *name_b; 6108 name_a = p->one->mode ? p->one->path : p->two->path; 6109 name_b = NULL; 6110 strip_prefix(opt->prefix_length, &name_a, &name_b); 6111 write_name_quoted(name_a, opt->file, line_termination); 6112 } 6113} 6114 6115int diff_unmodified_pair(struct diff_filepair *p) 6116{ 6117 /* This function is written stricter than necessary to support 6118 * the currently implemented transformers, but the idea is to 6119 * let transformers to produce diff_filepairs any way they want, 6120 * and filter and clean them up here before producing the output. 6121 */ 6122 struct diff_filespec *one = p->one, *two = p->two; 6123 6124 if (DIFF_PAIR_UNMERGED(p)) 6125 return 0; /* unmerged is interesting */ 6126 6127 /* deletion, addition, mode or type change 6128 * and rename are all interesting. 6129 */ 6130 if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) || 6131 DIFF_PAIR_MODE_CHANGED(p) || 6132 strcmp(one->path, two->path)) 6133 return 0; 6134 6135 /* both are valid and point at the same path. that is, we are 6136 * dealing with a change. 6137 */ 6138 if (one->oid_valid && two->oid_valid && 6139 oideq(&one->oid, &two->oid) && 6140 !one->dirty_submodule && !two->dirty_submodule) 6141 return 1; /* no change */ 6142 if (!one->oid_valid && !two->oid_valid) 6143 return 1; /* both look at the same file on the filesystem. */ 6144 return 0; 6145} 6146 6147static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) 6148{ 6149 int include_conflict_headers = 6150 (additional_headers(o, p->one->path) && 6151 !o->pickaxe_opts && 6152 (!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o))); 6153 6154 /* 6155 * Check if we can return early without showing a diff. Note that 6156 * diff_filepair only stores {oid, path, mode, is_valid} 6157 * information for each path, and thus diff_unmodified_pair() only 6158 * considers those bits of info. However, we do not want pairs 6159 * created by create_filepairs_for_header_only_notifications() 6160 * (which always look like unmodified pairs) to be ignored, so 6161 * return early if both p is unmodified AND we don't want to 6162 * include_conflict_headers. 6163 */ 6164 if (diff_unmodified_pair(p) && !include_conflict_headers) 6165 return; 6166 6167 /* Actually, we can also return early to avoid showing tree diffs */ 6168 if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || 6169 (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) 6170 return; 6171 6172 run_diff(p, o); 6173} 6174 6175/* return 1 if any change is found; otherwise, return 0 */ 6176static int diff_flush_patch_quietly(struct diff_filepair *p, struct diff_options *o) 6177{ 6178 int saved_dry_run = o->dry_run; 6179 int saved_found_changes = o->found_changes; 6180 int ret; 6181 6182 o->dry_run = 1; 6183 o->found_changes = 0; 6184 diff_flush_patch(p, o); 6185 ret = o->found_changes; 6186 o->dry_run = saved_dry_run; 6187 o->found_changes |= saved_found_changes; 6188 return ret; 6189} 6190 6191static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o, 6192 struct diffstat_t *diffstat) 6193{ 6194 if (diff_unmodified_pair(p)) 6195 return; 6196 6197 if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || 6198 (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) 6199 return; /* no useful stat for tree diffs */ 6200 6201 run_diffstat(p, o, diffstat); 6202} 6203 6204static void diff_flush_checkdiff(struct diff_filepair *p, 6205 struct diff_options *o) 6206{ 6207 if (diff_unmodified_pair(p)) 6208 return; 6209 6210 if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || 6211 (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) 6212 return; /* nothing to check in tree diffs */ 6213 6214 run_checkdiff(p, o); 6215} 6216 6217int diff_queue_is_empty(struct diff_options *o) 6218{ 6219 struct diff_queue_struct *q = &diff_queued_diff; 6220 int i; 6221 int include_conflict_headers = 6222 (o->additional_path_headers && 6223 strmap_get_size(o->additional_path_headers) && 6224 !o->pickaxe_opts && 6225 (!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o))); 6226 6227 if (include_conflict_headers) 6228 return 0; 6229 6230 for (i = 0; i < q->nr; i++) 6231 if (!diff_unmodified_pair(q->queue[i])) 6232 return 0; 6233 return 1; 6234} 6235 6236#if DIFF_DEBUG 6237void diff_debug_filespec(struct diff_filespec *s, int x, const char *one) 6238{ 6239 fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n", 6240 x, one ? one : "", 6241 s->path, 6242 DIFF_FILE_VALID(s) ? "valid" : "invalid", 6243 s->mode, 6244 s->oid_valid ? oid_to_hex(&s->oid) : ""); 6245 fprintf(stderr, "queue[%d] %s size %lu\n", 6246 x, one ? one : "", 6247 s->size); 6248} 6249 6250void diff_debug_filepair(const struct diff_filepair *p, int i) 6251{ 6252 diff_debug_filespec(p->one, i, "one"); 6253 diff_debug_filespec(p->two, i, "two"); 6254 fprintf(stderr, "score %d, status %c rename_used %d broken %d\n", 6255 p->score, p->status ? p->status : '?', 6256 p->one->rename_used, p->broken_pair); 6257} 6258 6259void diff_debug_queue(const char *msg, struct diff_queue_struct *q) 6260{ 6261 int i; 6262 if (msg) 6263 fprintf(stderr, "%s\n", msg); 6264 fprintf(stderr, "q->nr = %d\n", q->nr); 6265 for (i = 0; i < q->nr; i++) { 6266 struct diff_filepair *p = q->queue[i]; 6267 diff_debug_filepair(p, i); 6268 } 6269} 6270#endif 6271 6272static void diff_resolve_rename_copy(void) 6273{ 6274 int i; 6275 struct diff_filepair *p; 6276 struct diff_queue_struct *q = &diff_queued_diff; 6277 6278 diff_debug_queue("resolve-rename-copy", q); 6279 6280 for (i = 0; i < q->nr; i++) { 6281 p = q->queue[i]; 6282 p->status = 0; /* undecided */ 6283 if (DIFF_PAIR_UNMERGED(p)) 6284 p->status = DIFF_STATUS_UNMERGED; 6285 else if (!DIFF_FILE_VALID(p->one)) 6286 p->status = DIFF_STATUS_ADDED; 6287 else if (!DIFF_FILE_VALID(p->two)) 6288 p->status = DIFF_STATUS_DELETED; 6289 else if (DIFF_PAIR_TYPE_CHANGED(p)) 6290 p->status = DIFF_STATUS_TYPE_CHANGED; 6291 6292 /* from this point on, we are dealing with a pair 6293 * whose both sides are valid and of the same type, i.e. 6294 * either in-place edit or rename/copy edit. 6295 */ 6296 else if (DIFF_PAIR_RENAME(p)) { 6297 /* 6298 * A rename might have re-connected a broken 6299 * pair up, causing the pathnames to be the 6300 * same again. If so, that's not a rename at 6301 * all, just a modification.. 6302 * 6303 * Otherwise, see if this source was used for 6304 * multiple renames, in which case we decrement 6305 * the count, and call it a copy. 6306 */ 6307 if (!strcmp(p->one->path, p->two->path)) 6308 p->status = DIFF_STATUS_MODIFIED; 6309 else if (--p->one->rename_used > 0) 6310 p->status = DIFF_STATUS_COPIED; 6311 else 6312 p->status = DIFF_STATUS_RENAMED; 6313 } 6314 else if (!oideq(&p->one->oid, &p->two->oid) || 6315 p->one->mode != p->two->mode || 6316 p->one->dirty_submodule || 6317 p->two->dirty_submodule || 6318 is_null_oid(&p->one->oid)) 6319 p->status = DIFF_STATUS_MODIFIED; 6320 else { 6321 /* This is a "no-change" entry and should not 6322 * happen anymore, but prepare for broken callers. 6323 */ 6324 error("feeding unmodified %s to diffcore", 6325 p->one->path); 6326 p->status = DIFF_STATUS_UNKNOWN; 6327 } 6328 } 6329 diff_debug_queue("resolve-rename-copy done", q); 6330} 6331 6332static int check_pair_status(struct diff_filepair *p) 6333{ 6334 switch (p->status) { 6335 case DIFF_STATUS_UNKNOWN: 6336 return 0; 6337 case 0: 6338 die("internal error in diff-resolve-rename-copy"); 6339 default: 6340 return 1; 6341 } 6342} 6343 6344static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) 6345{ 6346 int fmt = opt->output_format; 6347 6348 if (fmt & DIFF_FORMAT_CHECKDIFF) 6349 diff_flush_checkdiff(p, opt); 6350 else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) 6351 diff_flush_raw(p, opt); 6352 else if (fmt & DIFF_FORMAT_NAME) { 6353 const char *name_a, *name_b; 6354 name_a = p->two->path; 6355 name_b = NULL; 6356 strip_prefix(opt->prefix_length, &name_a, &name_b); 6357 fprintf(opt->file, "%s", diff_line_prefix(opt)); 6358 write_name_quoted(name_a, opt->file, opt->line_termination); 6359 } 6360 6361 opt->found_changes = 1; 6362} 6363 6364static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs) 6365{ 6366 struct strbuf sb = STRBUF_INIT; 6367 if (fs->mode) 6368 strbuf_addf(&sb, " %s mode %06o ", newdelete, fs->mode); 6369 else 6370 strbuf_addf(&sb, " %s ", newdelete); 6371 6372 quote_c_style(fs->path, &sb, NULL, 0); 6373 strbuf_addch(&sb, '\n'); 6374 emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY, 6375 sb.buf, sb.len, 0); 6376 strbuf_release(&sb); 6377} 6378 6379static void show_mode_change(struct diff_options *opt, struct diff_filepair *p, 6380 int show_name) 6381{ 6382 if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) { 6383 struct strbuf sb = STRBUF_INIT; 6384 strbuf_addf(&sb, " mode change %06o => %06o", 6385 p->one->mode, p->two->mode); 6386 if (show_name) { 6387 strbuf_addch(&sb, ' '); 6388 quote_c_style(p->two->path, &sb, NULL, 0); 6389 } 6390 strbuf_addch(&sb, '\n'); 6391 emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY, 6392 sb.buf, sb.len, 0); 6393 strbuf_release(&sb); 6394 } 6395} 6396 6397static void show_rename_copy(struct diff_options *opt, const char *renamecopy, 6398 struct diff_filepair *p) 6399{ 6400 struct strbuf sb = STRBUF_INIT; 6401 struct strbuf names = STRBUF_INIT; 6402 6403 pprint_rename(&names, p->one->path, p->two->path); 6404 strbuf_addf(&sb, " %s %s (%d%%)\n", 6405 renamecopy, names.buf, similarity_index(p)); 6406 strbuf_release(&names); 6407 emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY, 6408 sb.buf, sb.len, 0); 6409 show_mode_change(opt, p, 0); 6410 strbuf_release(&sb); 6411} 6412 6413static void diff_summary(struct diff_options *opt, struct diff_filepair *p) 6414{ 6415 switch(p->status) { 6416 case DIFF_STATUS_DELETED: 6417 show_file_mode_name(opt, "delete", p->one); 6418 break; 6419 case DIFF_STATUS_ADDED: 6420 show_file_mode_name(opt, "create", p->two); 6421 break; 6422 case DIFF_STATUS_COPIED: 6423 show_rename_copy(opt, "copy", p); 6424 break; 6425 case DIFF_STATUS_RENAMED: 6426 show_rename_copy(opt, "rename", p); 6427 break; 6428 default: 6429 if (p->score) { 6430 struct strbuf sb = STRBUF_INIT; 6431 strbuf_addstr(&sb, " rewrite "); 6432 quote_c_style(p->two->path, &sb, NULL, 0); 6433 strbuf_addf(&sb, " (%d%%)\n", similarity_index(p)); 6434 emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY, 6435 sb.buf, sb.len, 0); 6436 strbuf_release(&sb); 6437 } 6438 show_mode_change(opt, p, !p->score); 6439 break; 6440 } 6441} 6442 6443struct patch_id_t { 6444 struct git_hash_ctx *ctx; 6445 int patchlen; 6446}; 6447 6448static int remove_space(char *line, int len) 6449{ 6450 int i; 6451 char *dst = line; 6452 unsigned char c; 6453 6454 for (i = 0; i < len; i++) 6455 if (!isspace((c = line[i]))) 6456 *dst++ = c; 6457 6458 return dst - line; 6459} 6460 6461void flush_one_hunk(struct object_id *result, struct git_hash_ctx *ctx) 6462{ 6463 unsigned char hash[GIT_MAX_RAWSZ]; 6464 unsigned short carry = 0; 6465 int i; 6466 6467 git_hash_final(hash, ctx); 6468 the_hash_algo->init_fn(ctx); 6469 /* 20-byte sum, with carry */ 6470 for (i = 0; i < the_hash_algo->rawsz; ++i) { 6471 carry += result->hash[i] + hash[i]; 6472 result->hash[i] = carry; 6473 carry >>= 8; 6474 } 6475} 6476 6477static int patch_id_consume(void *priv, char *line, unsigned long len) 6478{ 6479 struct patch_id_t *data = priv; 6480 int new_len; 6481 6482 if (len > 12 && starts_with(line, "\\ ")) 6483 return 0; 6484 new_len = remove_space(line, len); 6485 6486 git_hash_update(data->ctx, line, new_len); 6487 data->patchlen += new_len; 6488 return 0; 6489} 6490 6491static void patch_id_add_string(struct git_hash_ctx *ctx, const char *str) 6492{ 6493 git_hash_update(ctx, str, strlen(str)); 6494} 6495 6496static void patch_id_add_mode(struct git_hash_ctx *ctx, unsigned mode) 6497{ 6498 /* large enough for 2^32 in octal */ 6499 char buf[12]; 6500 int len = xsnprintf(buf, sizeof(buf), "%06o", mode); 6501 git_hash_update(ctx, buf, len); 6502} 6503 6504/* returns 0 upon success, and writes result into oid */ 6505static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only) 6506{ 6507 struct diff_queue_struct *q = &diff_queued_diff; 6508 int i; 6509 struct git_hash_ctx ctx; 6510 struct patch_id_t data; 6511 6512 the_hash_algo->init_fn(&ctx); 6513 memset(&data, 0, sizeof(struct patch_id_t)); 6514 data.ctx = &ctx; 6515 oidclr(oid, the_repository->hash_algo); 6516 6517 for (i = 0; i < q->nr; i++) { 6518 xpparam_t xpp; 6519 xdemitconf_t xecfg; 6520 mmfile_t mf1, mf2; 6521 struct diff_filepair *p = q->queue[i]; 6522 int len1, len2; 6523 6524 memset(&xpp, 0, sizeof(xpp)); 6525 memset(&xecfg, 0, sizeof(xecfg)); 6526 if (p->status == 0) 6527 return error("internal diff status error"); 6528 if (p->status == DIFF_STATUS_UNKNOWN) 6529 continue; 6530 if (diff_unmodified_pair(p)) 6531 continue; 6532 if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || 6533 (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) 6534 continue; 6535 if (DIFF_PAIR_UNMERGED(p)) 6536 continue; 6537 6538 diff_fill_oid_info(p->one, options->repo->index); 6539 diff_fill_oid_info(p->two, options->repo->index); 6540 6541 len1 = remove_space(p->one->path, strlen(p->one->path)); 6542 len2 = remove_space(p->two->path, strlen(p->two->path)); 6543 patch_id_add_string(&ctx, "diff--git"); 6544 patch_id_add_string(&ctx, "a/"); 6545 git_hash_update(&ctx, p->one->path, len1); 6546 patch_id_add_string(&ctx, "b/"); 6547 git_hash_update(&ctx, p->two->path, len2); 6548 6549 if (p->one->mode == 0) { 6550 patch_id_add_string(&ctx, "newfilemode"); 6551 patch_id_add_mode(&ctx, p->two->mode); 6552 } else if (p->two->mode == 0) { 6553 patch_id_add_string(&ctx, "deletedfilemode"); 6554 patch_id_add_mode(&ctx, p->one->mode); 6555 } else if (p->one->mode != p->two->mode) { 6556 patch_id_add_string(&ctx, "oldmode"); 6557 patch_id_add_mode(&ctx, p->one->mode); 6558 patch_id_add_string(&ctx, "newmode"); 6559 patch_id_add_mode(&ctx, p->two->mode); 6560 } 6561 6562 if (diff_header_only) { 6563 /* don't do anything since we're only populating header info */ 6564 } else if (diff_filespec_is_binary(options->repo, p->one) || 6565 diff_filespec_is_binary(options->repo, p->two)) { 6566 git_hash_update(&ctx, oid_to_hex(&p->one->oid), 6567 the_hash_algo->hexsz); 6568 git_hash_update(&ctx, oid_to_hex(&p->two->oid), 6569 the_hash_algo->hexsz); 6570 } else { 6571 if (p->one->mode == 0) { 6572 patch_id_add_string(&ctx, "---/dev/null"); 6573 patch_id_add_string(&ctx, "+++b/"); 6574 git_hash_update(&ctx, p->two->path, len2); 6575 } else if (p->two->mode == 0) { 6576 patch_id_add_string(&ctx, "---a/"); 6577 git_hash_update(&ctx, p->one->path, len1); 6578 patch_id_add_string(&ctx, "+++/dev/null"); 6579 } else { 6580 patch_id_add_string(&ctx, "---a/"); 6581 git_hash_update(&ctx, p->one->path, len1); 6582 patch_id_add_string(&ctx, "+++b/"); 6583 git_hash_update(&ctx, p->two->path, len2); 6584 } 6585 6586 if (fill_mmfile(options->repo, &mf1, p->one) < 0 || 6587 fill_mmfile(options->repo, &mf2, p->two) < 0) 6588 return error("unable to read files to diff"); 6589 xpp.flags = 0; 6590 xecfg.ctxlen = 3; 6591 xecfg.flags = XDL_EMIT_NO_HUNK_HDR; 6592 if (xdi_diff_outf(&mf1, &mf2, NULL, 6593 patch_id_consume, &data, &xpp, &xecfg)) 6594 return error("unable to generate patch-id diff for %s", 6595 p->one->path); 6596 } 6597 flush_one_hunk(oid, &ctx); 6598 } 6599 6600 return 0; 6601} 6602 6603int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only) 6604{ 6605 struct diff_queue_struct *q = &diff_queued_diff; 6606 int result = diff_get_patch_id(options, oid, diff_header_only); 6607 6608 diff_queue_clear(q); 6609 6610 return result; 6611} 6612 6613static int is_summary_empty(const struct diff_queue_struct *q) 6614{ 6615 int i; 6616 6617 for (i = 0; i < q->nr; i++) { 6618 const struct diff_filepair *p = q->queue[i]; 6619 6620 switch (p->status) { 6621 case DIFF_STATUS_DELETED: 6622 case DIFF_STATUS_ADDED: 6623 case DIFF_STATUS_COPIED: 6624 case DIFF_STATUS_RENAMED: 6625 return 0; 6626 default: 6627 if (p->score) 6628 return 0; 6629 if (p->one->mode && p->two->mode && 6630 p->one->mode != p->two->mode) 6631 return 0; 6632 break; 6633 } 6634 } 6635 return 1; 6636} 6637 6638static const char rename_limit_warning[] = 6639N_("exhaustive rename detection was skipped due to too many files."); 6640 6641static const char degrade_cc_to_c_warning[] = 6642N_("only found copies from modified paths due to too many files."); 6643 6644static const char rename_limit_advice[] = 6645N_("you may want to set your %s variable to at least " 6646 "%d and retry the command."); 6647 6648void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc) 6649{ 6650 fflush(stdout); 6651 if (degraded_cc) 6652 warning(_(degrade_cc_to_c_warning)); 6653 else if (needed) 6654 warning(_(rename_limit_warning)); 6655 else 6656 return; 6657 if (0 < needed) 6658 warning(_(rename_limit_advice), varname, needed); 6659} 6660 6661static void create_filepairs_for_header_only_notifications(struct diff_options *o) 6662{ 6663 struct strset present; 6664 struct diff_queue_struct *q = &diff_queued_diff; 6665 struct hashmap_iter iter; 6666 struct strmap_entry *e; 6667 int i; 6668 6669 strset_init_with_options(&present, /*pool*/ NULL, /*strdup*/ 0); 6670 6671 /* 6672 * Find out which paths exist in diff_queued_diff, preferring 6673 * one->path for any pair that has multiple paths. 6674 */ 6675 for (i = 0; i < q->nr; i++) { 6676 struct diff_filepair *p = q->queue[i]; 6677 char *path = p->one->path ? p->one->path : p->two->path; 6678 6679 if (strmap_contains(o->additional_path_headers, path)) 6680 strset_add(&present, path); 6681 } 6682 6683 /* 6684 * Loop over paths in additional_path_headers; for each NOT already 6685 * in diff_queued_diff, create a synthetic filepair and insert that 6686 * into diff_queued_diff. 6687 */ 6688 strmap_for_each_entry(o->additional_path_headers, &iter, e) { 6689 if (!strset_contains(&present, e->key)) { 6690 struct diff_filespec *one, *two; 6691 struct diff_filepair *p; 6692 6693 one = alloc_filespec(e->key); 6694 two = alloc_filespec(e->key); 6695 fill_filespec(one, null_oid(the_hash_algo), 0, 0); 6696 fill_filespec(two, null_oid(the_hash_algo), 0, 0); 6697 p = diff_queue(q, one, two); 6698 p->status = DIFF_STATUS_MODIFIED; 6699 } 6700 } 6701 6702 /* Re-sort the filepairs */ 6703 diffcore_fix_diff_index(); 6704 6705 /* Cleanup */ 6706 strset_clear(&present); 6707} 6708 6709static void diff_flush_patch_all_file_pairs(struct diff_options *o) 6710{ 6711 int i; 6712 static struct emitted_diff_symbols esm = EMITTED_DIFF_SYMBOLS_INIT; 6713 struct diff_queue_struct *q = &diff_queued_diff; 6714 6715 if (WSEH_NEW & WS_RULE_MASK) 6716 BUG("WS rules bit mask overlaps with diff symbol flags"); 6717 6718 if (o->color_moved) 6719 o->emitted_symbols = &esm; 6720 6721 if (o->additional_path_headers) 6722 create_filepairs_for_header_only_notifications(o); 6723 6724 for (i = 0; i < q->nr; i++) { 6725 struct diff_filepair *p = q->queue[i]; 6726 if (check_pair_status(p)) 6727 diff_flush_patch(p, o); 6728 } 6729 6730 if (o->emitted_symbols) { 6731 if (o->color_moved) { 6732 struct mem_pool entry_pool; 6733 struct moved_entry_list *entry_list; 6734 6735 mem_pool_init(&entry_pool, 1024 * 1024); 6736 entry_list = add_lines_to_move_detection(o, 6737 &entry_pool); 6738 mark_color_as_moved(o, entry_list); 6739 if (o->color_moved == COLOR_MOVED_ZEBRA_DIM) 6740 dim_moved_lines(o); 6741 6742 mem_pool_discard(&entry_pool, 0); 6743 free(entry_list); 6744 } 6745 6746 for (i = 0; i < esm.nr; i++) 6747 emit_diff_symbol_from_struct(o, &esm.buf[i]); 6748 6749 for (i = 0; i < esm.nr; i++) 6750 free((void *)esm.buf[i].line); 6751 esm.nr = 0; 6752 6753 o->emitted_symbols = NULL; 6754 } 6755} 6756 6757static void diff_free_file(struct diff_options *options) 6758{ 6759 if (options->close_file && options->file) { 6760 fclose(options->file); 6761 options->file = NULL; 6762 } 6763} 6764 6765static void diff_free_ignore_regex(struct diff_options *options) 6766{ 6767 int i; 6768 6769 for (i = 0; i < options->ignore_regex_nr; i++) { 6770 regfree(options->ignore_regex[i]); 6771 free(options->ignore_regex[i]); 6772 } 6773 6774 FREE_AND_NULL(options->ignore_regex); 6775 options->ignore_regex_nr = 0; 6776} 6777 6778void diff_free(struct diff_options *options) 6779{ 6780 if (options->no_free) 6781 return; 6782 6783 if (options->objfind) { 6784 oidset_clear(options->objfind); 6785 FREE_AND_NULL(options->objfind); 6786 } 6787 6788 FREE_AND_NULL(options->orderfile); 6789 for (size_t i = 0; i < options->anchors_nr; i++) 6790 free(options->anchors[i]); 6791 FREE_AND_NULL(options->anchors); 6792 options->anchors_nr = options->anchors_alloc = 0; 6793 6794 diff_free_file(options); 6795 diff_free_ignore_regex(options); 6796 clear_pathspec(&options->pathspec); 6797} 6798 6799void diff_flush(struct diff_options *options) 6800{ 6801 struct diff_queue_struct *q = &diff_queued_diff; 6802 int i, output_format = options->output_format; 6803 int separator = 0; 6804 int dirstat_by_line = 0; 6805 6806 /* 6807 * Order: raw, stat, summary, patch 6808 * or: name/name-status/checkdiff (other bits clear) 6809 */ 6810 if (!q->nr && !options->additional_path_headers) 6811 goto free_queue; 6812 6813 if (output_format & (DIFF_FORMAT_RAW | 6814 DIFF_FORMAT_NAME | 6815 DIFF_FORMAT_NAME_STATUS | 6816 DIFF_FORMAT_CHECKDIFF)) { 6817 /* 6818 * make sure diff_Flush_patch_quietly() to be silent. 6819 */ 6820 FILE *dev_null = NULL; 6821 int saved_color_moved = options->color_moved; 6822 6823 if (options->flags.diff_from_contents) { 6824 dev_null = xfopen("/dev/null", "w"); 6825 options->color_moved = 0; 6826 } 6827 for (i = 0; i < q->nr; i++) { 6828 struct diff_filepair *p = q->queue[i]; 6829 6830 if (!check_pair_status(p)) 6831 continue; 6832 6833 if (options->flags.diff_from_contents) { 6834 FILE *saved_file = options->file; 6835 int found_changes; 6836 6837 options->file = dev_null; 6838 found_changes = diff_flush_patch_quietly(p, options); 6839 options->file = saved_file; 6840 if (!found_changes) 6841 continue; 6842 } 6843 flush_one_pair(p, options); 6844 } 6845 if (options->flags.diff_from_contents) { 6846 fclose(dev_null); 6847 options->color_moved = saved_color_moved; 6848 } 6849 separator++; 6850 } 6851 6852 if (output_format & DIFF_FORMAT_DIRSTAT && options->flags.dirstat_by_line) 6853 dirstat_by_line = 1; 6854 6855 if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) || 6856 dirstat_by_line) { 6857 struct diffstat_t diffstat; 6858 6859 compute_diffstat(options, &diffstat, q); 6860 if (output_format & DIFF_FORMAT_NUMSTAT) 6861 show_numstat(&diffstat, options); 6862 if (output_format & DIFF_FORMAT_DIFFSTAT) 6863 show_stats(&diffstat, options); 6864 if (output_format & DIFF_FORMAT_SHORTSTAT) 6865 show_shortstats(&diffstat, options); 6866 if (output_format & DIFF_FORMAT_DIRSTAT && dirstat_by_line) 6867 show_dirstat_by_line(&diffstat, options); 6868 free_diffstat_info(&diffstat); 6869 separator++; 6870 } 6871 if ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line) 6872 show_dirstat(options); 6873 6874 if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) { 6875 for (i = 0; i < q->nr; i++) { 6876 diff_summary(options, q->queue[i]); 6877 } 6878 separator++; 6879 } 6880 6881 if (output_format & DIFF_FORMAT_PATCH) { 6882 if (separator) { 6883 emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0); 6884 if (options->stat_sep) 6885 /* attach patch instead of inline */ 6886 emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP, 6887 NULL, 0, 0); 6888 } 6889 6890 diff_flush_patch_all_file_pairs(options); 6891 } 6892 6893 if (output_format & DIFF_FORMAT_CALLBACK) 6894 options->format_callback(q, options, options->format_callback_data); 6895 6896 if (output_format & DIFF_FORMAT_NO_OUTPUT && 6897 options->flags.exit_with_status && 6898 options->flags.diff_from_contents) { 6899 /* 6900 * run diff_flush_patch for the exit status. setting 6901 * options->file to /dev/null should be safe, because we 6902 * aren't supposed to produce any output anyway. 6903 */ 6904 diff_free_file(options); 6905 options->file = xfopen("/dev/null", "w"); 6906 options->close_file = 1; 6907 options->color_moved = 0; 6908 for (i = 0; i < q->nr; i++) { 6909 struct diff_filepair *p = q->queue[i]; 6910 if (check_pair_status(p)) 6911 diff_flush_patch_quietly(p, options); 6912 if (options->found_changes) 6913 break; 6914 } 6915 } 6916 6917free_queue: 6918 diff_queue_clear(q); 6919 diff_free(options); 6920 6921 /* 6922 * Report the content-level differences with HAS_CHANGES; 6923 * diff_addremove/diff_change does not set the bit when 6924 * DIFF_FROM_CONTENTS is in effect (e.g. with -w). 6925 */ 6926 if (options->flags.diff_from_contents) { 6927 if (options->found_changes) 6928 options->flags.has_changes = 1; 6929 else 6930 options->flags.has_changes = 0; 6931 } 6932} 6933 6934static int match_filter(const struct diff_options *options, const struct diff_filepair *p) 6935{ 6936 return (((p->status == DIFF_STATUS_MODIFIED) && 6937 ((p->score && 6938 filter_bit_tst(DIFF_STATUS_FILTER_BROKEN, options)) || 6939 (!p->score && 6940 filter_bit_tst(DIFF_STATUS_MODIFIED, options)))) || 6941 ((p->status != DIFF_STATUS_MODIFIED) && 6942 filter_bit_tst(p->status, options))); 6943} 6944 6945static void diffcore_apply_filter(struct diff_options *options) 6946{ 6947 int i; 6948 struct diff_queue_struct *q = &diff_queued_diff; 6949 struct diff_queue_struct outq = DIFF_QUEUE_INIT; 6950 6951 if (!options->filter) 6952 return; 6953 6954 if (filter_bit_tst(DIFF_STATUS_FILTER_AON, options)) { 6955 int found; 6956 for (i = found = 0; !found && i < q->nr; i++) { 6957 if (match_filter(options, q->queue[i])) 6958 found++; 6959 } 6960 if (found) 6961 return; 6962 6963 /* otherwise we will clear the whole queue 6964 * by copying the empty outq at the end of this 6965 * function, but first clear the current entries 6966 * in the queue. 6967 */ 6968 for (i = 0; i < q->nr; i++) 6969 diff_free_filepair(q->queue[i]); 6970 } 6971 else { 6972 /* Only the matching ones */ 6973 for (i = 0; i < q->nr; i++) { 6974 struct diff_filepair *p = q->queue[i]; 6975 if (match_filter(options, p)) 6976 diff_q(&outq, p); 6977 else 6978 diff_free_filepair(p); 6979 } 6980 } 6981 free(q->queue); 6982 *q = outq; 6983} 6984 6985/* Check whether two filespecs with the same mode and size are identical */ 6986static int diff_filespec_is_identical(struct repository *r, 6987 struct diff_filespec *one, 6988 struct diff_filespec *two) 6989{ 6990 if (S_ISGITLINK(one->mode)) 6991 return 0; 6992 if (diff_populate_filespec(r, one, NULL)) 6993 return 0; 6994 if (diff_populate_filespec(r, two, NULL)) 6995 return 0; 6996 return !memcmp(one->data, two->data, one->size); 6997} 6998 6999static int diff_filespec_check_stat_unmatch(struct repository *r, 7000 struct diff_filepair *p) 7001{ 7002 struct diff_populate_filespec_options dpf_options = { 7003 .check_size_only = 1, 7004 .missing_object_cb = diff_queued_diff_prefetch, 7005 .missing_object_data = r, 7006 }; 7007 7008 if (p->done_skip_stat_unmatch) 7009 return p->skip_stat_unmatch_result; 7010 7011 p->done_skip_stat_unmatch = 1; 7012 p->skip_stat_unmatch_result = 0; 7013 /* 7014 * 1. Entries that come from stat info dirtiness 7015 * always have both sides (iow, not create/delete), 7016 * one side of the object name is unknown, with 7017 * the same mode and size. Keep the ones that 7018 * do not match these criteria. They have real 7019 * differences. 7020 * 7021 * 2. At this point, the file is known to be modified, 7022 * with the same mode and size, and the object 7023 * name of one side is unknown. Need to inspect 7024 * the identical contents. 7025 */ 7026 if (!DIFF_FILE_VALID(p->one) || /* (1) */ 7027 !DIFF_FILE_VALID(p->two) || 7028 (p->one->oid_valid && p->two->oid_valid) || 7029 (p->one->mode != p->two->mode) || 7030 diff_populate_filespec(r, p->one, &dpf_options) || 7031 diff_populate_filespec(r, p->two, &dpf_options) || 7032 (p->one->size != p->two->size) || 7033 !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */ 7034 p->skip_stat_unmatch_result = 1; 7035 return p->skip_stat_unmatch_result; 7036} 7037 7038static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) 7039{ 7040 int i; 7041 struct diff_queue_struct *q = &diff_queued_diff; 7042 struct diff_queue_struct outq = DIFF_QUEUE_INIT; 7043 7044 for (i = 0; i < q->nr; i++) { 7045 struct diff_filepair *p = q->queue[i]; 7046 7047 if (diff_filespec_check_stat_unmatch(diffopt->repo, p)) 7048 diff_q(&outq, p); 7049 else { 7050 /* 7051 * The caller can subtract 1 from skip_stat_unmatch 7052 * to determine how many paths were dirty only 7053 * due to stat info mismatch. 7054 */ 7055 if (!diffopt->flags.no_index) 7056 diffopt->skip_stat_unmatch++; 7057 diff_free_filepair(p); 7058 } 7059 } 7060 free(q->queue); 7061 *q = outq; 7062} 7063 7064static int diffnamecmp(const void *a_, const void *b_) 7065{ 7066 const struct diff_filepair *a = *((const struct diff_filepair **)a_); 7067 const struct diff_filepair *b = *((const struct diff_filepair **)b_); 7068 const char *name_a, *name_b; 7069 7070 name_a = a->one ? a->one->path : a->two->path; 7071 name_b = b->one ? b->one->path : b->two->path; 7072 return strcmp(name_a, name_b); 7073} 7074 7075void diffcore_fix_diff_index(void) 7076{ 7077 struct diff_queue_struct *q = &diff_queued_diff; 7078 QSORT(q->queue, q->nr, diffnamecmp); 7079} 7080 7081void diff_add_if_missing(struct repository *r, 7082 struct oid_array *to_fetch, 7083 const struct diff_filespec *filespec) 7084{ 7085 if (filespec && filespec->oid_valid && 7086 !S_ISGITLINK(filespec->mode) && 7087 odb_read_object_info_extended(r->objects, &filespec->oid, NULL, 7088 OBJECT_INFO_FOR_PREFETCH)) 7089 oid_array_append(to_fetch, &filespec->oid); 7090} 7091 7092void diff_queued_diff_prefetch(void *repository) 7093{ 7094 struct repository *repo = repository; 7095 int i; 7096 struct diff_queue_struct *q = &diff_queued_diff; 7097 struct oid_array to_fetch = OID_ARRAY_INIT; 7098 7099 for (i = 0; i < q->nr; i++) { 7100 struct diff_filepair *p = q->queue[i]; 7101 diff_add_if_missing(repo, &to_fetch, p->one); 7102 diff_add_if_missing(repo, &to_fetch, p->two); 7103 } 7104 7105 /* 7106 * NEEDSWORK: Consider deduplicating the OIDs sent. 7107 */ 7108 promisor_remote_get_direct(repo, to_fetch.oid, to_fetch.nr); 7109 7110 oid_array_clear(&to_fetch); 7111} 7112 7113void init_diffstat_widths(struct diff_options *options) 7114{ 7115 options->stat_width = -1; /* use full terminal width */ 7116 options->stat_name_width = -1; /* respect diff.statNameWidth config */ 7117 options->stat_graph_width = -1; /* respect diff.statGraphWidth config */ 7118} 7119 7120void diffcore_std(struct diff_options *options) 7121{ 7122 int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT | 7123 DIFF_FORMAT_NUMSTAT | 7124 DIFF_FORMAT_PATCH | 7125 DIFF_FORMAT_SHORTSTAT | 7126 DIFF_FORMAT_DIRSTAT; 7127 7128 /* 7129 * Check if the user requested a blob-data-requiring diff output and/or 7130 * break-rewrite detection (which requires blob data). If yes, prefetch 7131 * the diff pairs. 7132 * 7133 * If no prefetching occurs, diffcore_rename() will prefetch if it 7134 * decides that it needs inexact rename detection. 7135 */ 7136 if (options->repo == the_repository && repo_has_promisor_remote(the_repository) && 7137 (options->output_format & output_formats_to_prefetch || 7138 options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)) 7139 diff_queued_diff_prefetch(options->repo); 7140 7141 /* NOTE please keep the following in sync with diff_tree_combined() */ 7142 if (options->skip_stat_unmatch) 7143 diffcore_skip_stat_unmatch(options); 7144 if (!options->found_follow) { 7145 /* See try_to_follow_renames() in tree-diff.c */ 7146 if (options->break_opt != -1) 7147 diffcore_break(options->repo, 7148 options->break_opt); 7149 if (options->detect_rename) 7150 diffcore_rename(options); 7151 if (options->break_opt != -1) 7152 diffcore_merge_broken(); 7153 } 7154 if (options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) 7155 diffcore_pickaxe(options); 7156 if (options->orderfile) 7157 diffcore_order(options->orderfile); 7158 if (options->rotate_to) 7159 diffcore_rotate(options); 7160 if (!options->found_follow && !options->skip_resolving_statuses) 7161 /* See try_to_follow_renames() in tree-diff.c */ 7162 diff_resolve_rename_copy(); 7163 diffcore_apply_filter(options); 7164 7165 if (diff_queued_diff.nr && !options->flags.diff_from_contents) 7166 options->flags.has_changes = 1; 7167 else 7168 options->flags.has_changes = 0; 7169 7170 options->found_follow = 0; 7171} 7172 7173int diff_result_code(struct rev_info *revs) 7174{ 7175 struct diff_options *opt = &revs->diffopt; 7176 int result = 0; 7177 7178 if (revs->remerge_diff) { 7179 tmp_objdir_destroy(revs->remerge_objdir); 7180 revs->remerge_objdir = NULL; 7181 } 7182 7183 diff_warn_rename_limit("diff.renameLimit", 7184 opt->needed_rename_limit, 7185 opt->degraded_cc_to_c); 7186 7187 if (opt->flags.exit_with_status && 7188 opt->flags.has_changes) 7189 result |= 01; 7190 if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) && 7191 opt->flags.check_failed) 7192 result |= 02; 7193 return result; 7194} 7195 7196int diff_can_quit_early(struct diff_options *opt) 7197{ 7198 return (opt->flags.quick && 7199 !opt->filter && 7200 opt->flags.has_changes); 7201} 7202 7203/* 7204 * Shall changes to this submodule be ignored? 7205 * 7206 * Submodule changes can be configured to be ignored separately for each path, 7207 * but that configuration can be overridden from the command line. 7208 */ 7209static int is_submodule_ignored(const char *path, struct diff_options *options) 7210{ 7211 int ignored = 0; 7212 struct diff_flags orig_flags = options->flags; 7213 if (!options->flags.override_submodule_config) 7214 set_diffopt_flags_from_submodule_config(options, path); 7215 if (options->flags.ignore_submodules) 7216 ignored = 1; 7217 options->flags = orig_flags; 7218 return ignored; 7219} 7220 7221void compute_diffstat(struct diff_options *options, 7222 struct diffstat_t *diffstat, 7223 struct diff_queue_struct *q) 7224{ 7225 int i; 7226 7227 memset(diffstat, 0, sizeof(struct diffstat_t)); 7228 for (i = 0; i < q->nr; i++) { 7229 struct diff_filepair *p = q->queue[i]; 7230 if (check_pair_status(p)) 7231 diff_flush_stat(p, options, diffstat); 7232 } 7233 options->found_changes = !!diffstat->nr; 7234} 7235 7236struct diff_filepair *diff_queue_addremove(struct diff_queue_struct *queue, 7237 struct diff_options *options, 7238 int addremove, unsigned mode, 7239 const struct object_id *oid, 7240 int oid_valid, 7241 const char *concatpath, 7242 unsigned dirty_submodule) 7243{ 7244 struct diff_filespec *one, *two; 7245 struct diff_filepair *pair; 7246 7247 if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options)) 7248 return NULL; 7249 7250 /* This may look odd, but it is a preparation for 7251 * feeding "there are unchanged files which should 7252 * not produce diffs, but when you are doing copy 7253 * detection you would need them, so here they are" 7254 * entries to the diff-core. They will be prefixed 7255 * with something like '=' or '*' (I haven't decided 7256 * which but should not make any difference). 7257 * Feeding the same new and old to diff_change() 7258 * also has the same effect. 7259 * Before the final output happens, they are pruned after 7260 * merged into rename/copy pairs as appropriate. 7261 */ 7262 if (options->flags.reverse_diff) 7263 addremove = (addremove == '+' ? '-' : 7264 addremove == '-' ? '+' : addremove); 7265 7266 if (options->prefix && 7267 strncmp(concatpath, options->prefix, options->prefix_length)) 7268 return NULL; 7269 7270 one = alloc_filespec(concatpath); 7271 two = alloc_filespec(concatpath); 7272 7273 if (addremove != '+') 7274 fill_filespec(one, oid, oid_valid, mode); 7275 if (addremove != '-') { 7276 fill_filespec(two, oid, oid_valid, mode); 7277 two->dirty_submodule = dirty_submodule; 7278 } 7279 7280 pair = diff_queue(queue, one, two); 7281 if (!options->flags.diff_from_contents) 7282 options->flags.has_changes = 1; 7283 7284 return pair; 7285} 7286 7287struct diff_filepair *diff_queue_change(struct diff_queue_struct *queue, 7288 struct diff_options *options, 7289 unsigned old_mode, unsigned new_mode, 7290 const struct object_id *old_oid, 7291 const struct object_id *new_oid, 7292 int old_oid_valid, int new_oid_valid, 7293 const char *concatpath, 7294 unsigned old_dirty_submodule, 7295 unsigned new_dirty_submodule) 7296{ 7297 struct diff_filespec *one, *two; 7298 struct diff_filepair *p; 7299 7300 if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) && 7301 is_submodule_ignored(concatpath, options)) 7302 return NULL; 7303 7304 if (options->flags.reverse_diff) { 7305 SWAP(old_mode, new_mode); 7306 SWAP(old_oid, new_oid); 7307 SWAP(old_oid_valid, new_oid_valid); 7308 SWAP(old_dirty_submodule, new_dirty_submodule); 7309 } 7310 7311 if (options->prefix && 7312 strncmp(concatpath, options->prefix, options->prefix_length)) 7313 return NULL; 7314 7315 one = alloc_filespec(concatpath); 7316 two = alloc_filespec(concatpath); 7317 fill_filespec(one, old_oid, old_oid_valid, old_mode); 7318 fill_filespec(two, new_oid, new_oid_valid, new_mode); 7319 one->dirty_submodule = old_dirty_submodule; 7320 two->dirty_submodule = new_dirty_submodule; 7321 p = diff_queue(queue, one, two); 7322 7323 if (options->flags.diff_from_contents) 7324 return p; 7325 7326 if (options->flags.quick && options->skip_stat_unmatch && 7327 !diff_filespec_check_stat_unmatch(options->repo, p)) { 7328 diff_free_filespec_data(p->one); 7329 diff_free_filespec_data(p->two); 7330 return p; 7331 } 7332 7333 options->flags.has_changes = 1; 7334 7335 return p; 7336} 7337 7338void diff_addremove(struct diff_options *options, int addremove, unsigned mode, 7339 const struct object_id *oid, int oid_valid, 7340 const char *concatpath, unsigned dirty_submodule) 7341{ 7342 diff_queue_addremove(&diff_queued_diff, options, addremove, mode, oid, 7343 oid_valid, concatpath, dirty_submodule); 7344} 7345 7346void diff_change(struct diff_options *options, 7347 unsigned old_mode, unsigned new_mode, 7348 const struct object_id *old_oid, 7349 const struct object_id *new_oid, 7350 int old_oid_valid, int new_oid_valid, 7351 const char *concatpath, 7352 unsigned old_dirty_submodule, unsigned new_dirty_submodule) 7353{ 7354 diff_queue_change(&diff_queued_diff, options, old_mode, new_mode, 7355 old_oid, new_oid, old_oid_valid, new_oid_valid, 7356 concatpath, old_dirty_submodule, new_dirty_submodule); 7357} 7358 7359struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path) 7360{ 7361 struct diff_filepair *pair; 7362 struct diff_filespec *one, *two; 7363 7364 if (options->prefix && 7365 strncmp(path, options->prefix, options->prefix_length)) 7366 return NULL; 7367 7368 one = alloc_filespec(path); 7369 two = alloc_filespec(path); 7370 pair = diff_queue(&diff_queued_diff, one, two); 7371 pair->is_unmerged = 1; 7372 return pair; 7373} 7374 7375static char *run_textconv(struct repository *r, 7376 const char *pgm, 7377 struct diff_filespec *spec, 7378 size_t *outsize) 7379{ 7380 struct diff_tempfile *temp; 7381 struct child_process child = CHILD_PROCESS_INIT; 7382 struct strbuf buf = STRBUF_INIT; 7383 int err = 0; 7384 7385 temp = prepare_temp_file(r, spec); 7386 strvec_push(&child.args, pgm); 7387 strvec_push(&child.args, temp->name); 7388 7389 child.use_shell = 1; 7390 child.out = -1; 7391 if (start_command(&child)) { 7392 remove_tempfile(); 7393 return NULL; 7394 } 7395 7396 if (strbuf_read(&buf, child.out, 0) < 0) 7397 err = error("error reading from textconv command '%s'", pgm); 7398 close(child.out); 7399 7400 if (finish_command(&child) || err) { 7401 strbuf_release(&buf); 7402 remove_tempfile(); 7403 return NULL; 7404 } 7405 remove_tempfile(); 7406 7407 return strbuf_detach(&buf, outsize); 7408} 7409 7410size_t fill_textconv(struct repository *r, 7411 struct userdiff_driver *driver, 7412 struct diff_filespec *df, 7413 char **outbuf) 7414{ 7415 size_t size; 7416 7417 if (!driver) { 7418 if (!DIFF_FILE_VALID(df)) { 7419 *outbuf = (char *) ""; 7420 return 0; 7421 } 7422 if (diff_populate_filespec(r, df, NULL)) 7423 die("unable to read files to diff"); 7424 *outbuf = df->data; 7425 return df->size; 7426 } 7427 7428 if (!driver->textconv) 7429 BUG("fill_textconv called with non-textconv driver"); 7430 7431 if (driver->textconv_cache && df->oid_valid) { 7432 *outbuf = notes_cache_get(driver->textconv_cache, 7433 &df->oid, 7434 &size); 7435 if (*outbuf) 7436 return size; 7437 } 7438 7439 *outbuf = run_textconv(r, driver->textconv, df, &size); 7440 if (!*outbuf) 7441 die("unable to read files to diff"); 7442 7443 if (driver->textconv_cache && df->oid_valid) { 7444 /* ignore errors, as we might be in a readonly repository */ 7445 notes_cache_put(driver->textconv_cache, &df->oid, *outbuf, 7446 size); 7447 /* 7448 * we could save up changes and flush them all at the end, 7449 * but we would need an extra call after all diffing is done. 7450 * Since generating a cache entry is the slow path anyway, 7451 * this extra overhead probably isn't a big deal. 7452 */ 7453 notes_cache_write(driver->textconv_cache); 7454 } 7455 7456 return size; 7457} 7458 7459int textconv_object(struct repository *r, 7460 const char *path, 7461 unsigned mode, 7462 const struct object_id *oid, 7463 int oid_valid, 7464 char **buf, 7465 unsigned long *buf_size) 7466{ 7467 struct diff_filespec *df; 7468 struct userdiff_driver *textconv; 7469 7470 df = alloc_filespec(path); 7471 fill_filespec(df, oid, oid_valid, mode); 7472 textconv = get_textconv(r, df); 7473 if (!textconv) { 7474 free_filespec(df); 7475 return 0; 7476 } 7477 7478 *buf_size = fill_textconv(r, textconv, df, buf); 7479 free_filespec(df); 7480 return 1; 7481} 7482 7483void setup_diff_pager(struct diff_options *opt) 7484{ 7485 /* 7486 * If the user asked for our exit code, then either they want --quiet 7487 * or --exit-code. We should definitely not bother with a pager in the 7488 * former case, as we will generate no output. Since we still properly 7489 * report our exit code even when a pager is run, we _could_ run a 7490 * pager with --exit-code. But since we have not done so historically, 7491 * and because it is easy to find people oneline advising "git diff 7492 * --exit-code" in hooks and other scripts, we do not do so. 7493 */ 7494 if (!opt->flags.exit_with_status && 7495 check_pager_config(the_repository, "diff") != 0) 7496 setup_pager(the_repository); 7497}