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