Fossil SCM
Enhance the "fossil diff" command so that it accepts directories as arguments and shows diffs on all files contained within those directories.
Commit
c46f98055cf49687423e15a134ed1445ff10fbd6
Parent
cbde2cf7e4a40f7…
1 file changed
+165
-168
+165
-168
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -38,12 +38,11 @@ | ||
| 38 | 38 | /* |
| 39 | 39 | ** Use the "exec-rel-paths" setting and the --exec-abs-paths and |
| 40 | 40 | ** --exec-rel-paths command line options to determine whether |
| 41 | 41 | ** certain external commands are executed using relative paths. |
| 42 | 42 | */ |
| 43 | -static int determine_exec_relative_option(int force) | |
| 44 | -{ | |
| 43 | +static int determine_exec_relative_option(int force){ | |
| 45 | 44 | static int relativePaths = -1; |
| 46 | 45 | if( force || relativePaths==-1 ){ |
| 47 | 46 | int relPathOption = find_option("exec-rel-paths", 0, 0)!=0; |
| 48 | 47 | int absPathOption = find_option("exec-abs-paths", 0, 0)!=0; |
| 49 | 48 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| @@ -54,10 +53,60 @@ | ||
| 54 | 53 | if( relPathOption ){ relativePaths = 1; } |
| 55 | 54 | if( absPathOption ){ relativePaths = 0; } |
| 56 | 55 | } |
| 57 | 56 | return relativePaths; |
| 58 | 57 | } |
| 58 | + | |
| 59 | +#if INTERFACE | |
| 60 | +/* | |
| 61 | +** An array of FileDirList objects describe the files and directories listed | |
| 62 | +** on the command line of a "diff" command. Only those objects listed are | |
| 63 | +** actually diffed. | |
| 64 | +*/ | |
| 65 | +struct FileDirList { | |
| 66 | + int nUsed; /* Number of times each entry is used */ | |
| 67 | + int nName; /* Length of the entry */ | |
| 68 | + char *zName; /* Text of the entry */ | |
| 69 | +}; | |
| 70 | +#endif | |
| 71 | + | |
| 72 | +/* | |
| 73 | +** Return true if zFile is a file named on the azInclude[] list or is | |
| 74 | +** a file in a directory named on the azInclude[] list. | |
| 75 | +** | |
| 76 | +** if azInclude is NULL, then always include zFile. | |
| 77 | +*/ | |
| 78 | +static int file_dir_match(FileDirList *p, const char *zFile){ | |
| 79 | + int i = 0; | |
| 80 | + if( p==0 || strcmp(p->zName,".")==0 ) return 1; | |
| 81 | + if( filenames_are_case_sensitive() ){ | |
| 82 | + while( p->zName ){ | |
| 83 | + if( strcmp(zFile, p->zName)==0 | |
| 84 | + || (strncmp(zFile, p->zName, p->nName)==0 | |
| 85 | + && zFile[p->nName]=='/') | |
| 86 | + ){ | |
| 87 | + break; | |
| 88 | + } | |
| 89 | + p++; | |
| 90 | + } | |
| 91 | + }else{ | |
| 92 | + while( p->zName ){ | |
| 93 | + if( fossil_stricmp(zFile, p->zName)==0 | |
| 94 | + || (fossil_strnicmp(zFile, p->zName, p->nName)==0 | |
| 95 | + && zFile[p->nName]=='/') | |
| 96 | + ){ | |
| 97 | + break; | |
| 98 | + } | |
| 99 | + p++; | |
| 100 | + } | |
| 101 | + } | |
| 102 | + if( p->zName ){ | |
| 103 | + p->nUsed++; | |
| 104 | + return 1; | |
| 105 | + } | |
| 106 | + return 0; | |
| 107 | +} | |
| 59 | 108 | |
| 60 | 109 | /* |
| 61 | 110 | ** Print the "Index:" message that patches wants to see at the top of a diff. |
| 62 | 111 | */ |
| 63 | 112 | void diff_print_index(const char *zFile, u64 diffFlags){ |
| @@ -297,46 +346,10 @@ | ||
| 297 | 346 | file_delete(zTemp2); |
| 298 | 347 | blob_reset(&cmd); |
| 299 | 348 | } |
| 300 | 349 | } |
| 301 | 350 | |
| 302 | -/* | |
| 303 | -** Do a diff against a single file named in zFile from version zFrom | |
| 304 | -** against the same file on disk. | |
| 305 | -** | |
| 306 | -** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the | |
| 307 | -** command zDiffCmd to do the diffing. | |
| 308 | -** | |
| 309 | -** When using an external diff program, zBinGlob contains the GLOB patterns | |
| 310 | -** for file names to treat as binary. If fIncludeBinary is zero, these files | |
| 311 | -** will be skipped in addition to files that may contain binary content. | |
| 312 | -*/ | |
| 313 | -static void diff_one_against_disk( | |
| 314 | - const char *zFrom, /* Version tag for the "before" file */ | |
| 315 | - const char *zDiffCmd, /* Use this "diff" command */ | |
| 316 | - const char *zBinGlob, /* Treat file names matching this as binary */ | |
| 317 | - int fIncludeBinary, /* Include binary files for external diff */ | |
| 318 | - u64 diffFlags, /* Diff control flags */ | |
| 319 | - const char *zFile /* Name of the file to be diffed */ | |
| 320 | -){ | |
| 321 | - Blob fname; | |
| 322 | - Blob content; | |
| 323 | - int isLink; | |
| 324 | - int isBin; | |
| 325 | - file_tree_name(zFile, &fname, 0, 1); | |
| 326 | - historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, | |
| 327 | - fIncludeBinary ? 0 : &isBin, 0); | |
| 328 | - if( !isLink != !file_wd_islink(zFrom) ){ | |
| 329 | - fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); | |
| 330 | - }else{ | |
| 331 | - diff_file(&content, isBin, zFile, zFile, | |
| 332 | - zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); | |
| 333 | - } | |
| 334 | - blob_reset(&content); | |
| 335 | - blob_reset(&fname); | |
| 336 | -} | |
| 337 | - | |
| 338 | 351 | /* |
| 339 | 352 | ** Run a diff between the version zFrom and files on disk. zFrom might |
| 340 | 353 | ** be NULL which means to simply show the difference between the edited |
| 341 | 354 | ** files on disk and the check-out on which they are based. |
| 342 | 355 | ** |
| @@ -345,16 +358,17 @@ | ||
| 345 | 358 | ** |
| 346 | 359 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 347 | 360 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 348 | 361 | ** will be skipped in addition to files that may contain binary content. |
| 349 | 362 | */ |
| 350 | -static void diff_all_against_disk( | |
| 363 | +static void diff_against_disk( | |
| 351 | 364 | const char *zFrom, /* Version to difference from */ |
| 352 | 365 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 353 | 366 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 354 | 367 | int fIncludeBinary, /* Treat file names matching this as binary */ |
| 355 | - u64 diffFlags /* Flags controlling diff output */ | |
| 368 | + u64 diffFlags, /* Flags controlling diff output */ | |
| 369 | + FileDirList *pFileDir /* Which files to diff */ | |
| 356 | 370 | ){ |
| 357 | 371 | int vid; |
| 358 | 372 | Blob sql; |
| 359 | 373 | Stmt q; |
| 360 | 374 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -410,10 +424,11 @@ | ||
| 410 | 424 | int isLink = db_column_int(&q, 5); |
| 411 | 425 | const char *zFullName; |
| 412 | 426 | int showDiff = 1; |
| 413 | 427 | Blob fname; |
| 414 | 428 | |
| 429 | + if( !file_dir_match(pFileDir, zPathname) ) continue; | |
| 415 | 430 | if( determine_exec_relative_option(0) ){ |
| 416 | 431 | blob_zero(&fname); |
| 417 | 432 | file_relative_name(zPathname, &fname, 1); |
| 418 | 433 | }else{ |
| 419 | 434 | blob_set(&fname, g.zLocalRoot); |
| @@ -463,43 +478,10 @@ | ||
| 463 | 478 | } |
| 464 | 479 | db_finalize(&q); |
| 465 | 480 | db_end_transaction(1); /* ROLLBACK */ |
| 466 | 481 | } |
| 467 | 482 | |
| 468 | -/* | |
| 469 | -** Do a diff of a single file named in zFile against the | |
| 470 | -** version of this file held in the undo buffer. | |
| 471 | -** | |
| 472 | -** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the | |
| 473 | -** command zDiffCmd to do the diffing. | |
| 474 | -** | |
| 475 | -** When using an external diff program, zBinGlob contains the GLOB patterns | |
| 476 | -** for file names to treat as binary. If fIncludeBinary is zero, these files | |
| 477 | -** will be skipped in addition to files that may contain binary content. | |
| 478 | -*/ | |
| 479 | -static void diff_one_against_undo( | |
| 480 | - const char *zDiffCmd, /* Use this "diff" command */ | |
| 481 | - const char *zBinGlob, /* Treat file names matching this as binary */ | |
| 482 | - int fIncludeBinary, /* Include binary files for external diff */ | |
| 483 | - u64 diffFlags, /* Diff control flags */ | |
| 484 | - const char *zFile /* Name of the file to be diffed */ | |
| 485 | -){ | |
| 486 | - Blob fname; | |
| 487 | - Blob content; | |
| 488 | - | |
| 489 | - blob_init(&content, 0, 0); | |
| 490 | - file_tree_name(zFile, &fname, 0, 1); | |
| 491 | - db_blob(&content, "SELECT content FROM undo WHERE pathname=%Q", | |
| 492 | - blob_str(&fname)); | |
| 493 | - if( blob_size(&content) ){ | |
| 494 | - diff_file(&content, 0, zFile, zFile, | |
| 495 | - zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); | |
| 496 | - } | |
| 497 | - blob_reset(&content); | |
| 498 | - blob_reset(&fname); | |
| 499 | -} | |
| 500 | - | |
| 501 | 483 | /* |
| 502 | 484 | ** Run a diff between the undo buffer and files on disk. |
| 503 | 485 | ** |
| 504 | 486 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 505 | 487 | ** command zDiffCmd to do the diffing. |
| @@ -506,76 +488,35 @@ | ||
| 506 | 488 | ** |
| 507 | 489 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 508 | 490 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 509 | 491 | ** will be skipped in addition to files that may contain binary content. |
| 510 | 492 | */ |
| 511 | -static void diff_all_against_undo( | |
| 493 | +static void diff_against_undo( | |
| 512 | 494 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 513 | 495 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 514 | 496 | int fIncludeBinary, /* Treat file names matching this as binary */ |
| 515 | - u64 diffFlags /* Flags controlling diff output */ | |
| 497 | + u64 diffFlags, /* Flags controlling diff output */ | |
| 498 | + FileDirList *pFileDir /* List of files and directories to diff */ | |
| 516 | 499 | ){ |
| 517 | 500 | Stmt q; |
| 518 | 501 | Blob content; |
| 519 | 502 | db_prepare(&q, "SELECT pathname, content FROM undo"); |
| 520 | 503 | blob_init(&content, 0, 0); |
| 521 | 504 | while( db_step(&q)==SQLITE_ROW ){ |
| 505 | + char *zFullName; | |
| 522 | 506 | const char *zFile = (const char*)db_column_text(&q, 0); |
| 523 | - char *zFullName = mprintf("%s%s", g.zLocalRoot, zFile); | |
| 507 | + if( !file_dir_match(pFileDir, zFile) ) continue; | |
| 508 | + zFullName = mprintf("%s%s", g.zLocalRoot, zFile); | |
| 524 | 509 | db_column_blob(&q, 1, &content); |
| 525 | 510 | diff_file(&content, 0, zFullName, zFile, |
| 526 | 511 | zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 527 | 512 | fossil_free(zFullName); |
| 528 | 513 | blob_reset(&content); |
| 529 | 514 | } |
| 530 | 515 | db_finalize(&q); |
| 531 | 516 | } |
| 532 | 517 | |
| 533 | -/* | |
| 534 | -** Output the differences between two versions of a single file. | |
| 535 | -** zFrom and zTo are the check-ins containing the two file versions. | |
| 536 | -** | |
| 537 | -** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the | |
| 538 | -** command zDiffCmd to do the diffing. | |
| 539 | -** | |
| 540 | -** When using an external diff program, zBinGlob contains the GLOB patterns | |
| 541 | -** for file names to treat as binary. If fIncludeBinary is zero, these files | |
| 542 | -** will be skipped in addition to files that may contain binary content. | |
| 543 | -*/ | |
| 544 | -static void diff_one_two_versions( | |
| 545 | - const char *zFrom, /* Version tag for the "before" file */ | |
| 546 | - const char *zTo, /* Version tag for the "after" file */ | |
| 547 | - const char *zDiffCmd, /* Use this "diff" command */ | |
| 548 | - const char *zBinGlob, /* GLOB pattern for files that are binary */ | |
| 549 | - int fIncludeBinary, /* True to show binary files */ | |
| 550 | - u64 diffFlags, /* Diff flags */ | |
| 551 | - const char *zFile /* Name of the file to be diffed */ | |
| 552 | -){ | |
| 553 | - char *zName; | |
| 554 | - Blob fname; | |
| 555 | - Blob v1, v2; | |
| 556 | - int isLink1, isLink2; | |
| 557 | - int isBin1, isBin2; | |
| 558 | - if( diffFlags & DIFF_BRIEF ) return; | |
| 559 | - file_tree_name(zFile, &fname, 0, 1); | |
| 560 | - zName = blob_str(&fname); | |
| 561 | - historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, | |
| 562 | - fIncludeBinary ? 0 : &isBin1, 0); | |
| 563 | - historical_version_of_file(zTo, zName, &v2, &isLink2, 0, | |
| 564 | - fIncludeBinary ? 0 : &isBin2, 0); | |
| 565 | - if( isLink1 != isLink2 ){ | |
| 566 | - diff_print_filenames(zName, zName, diffFlags); | |
| 567 | - fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); | |
| 568 | - }else{ | |
| 569 | - diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd, | |
| 570 | - zBinGlob, fIncludeBinary, diffFlags); | |
| 571 | - } | |
| 572 | - blob_reset(&v1); | |
| 573 | - blob_reset(&v2); | |
| 574 | - blob_reset(&fname); | |
| 575 | -} | |
| 576 | - | |
| 577 | 518 | /* |
| 578 | 519 | ** Show the difference between two files identified by ManifestFile |
| 579 | 520 | ** entries. |
| 580 | 521 | ** |
| 581 | 522 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| @@ -634,17 +575,18 @@ | ||
| 634 | 575 | ** |
| 635 | 576 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 636 | 577 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 637 | 578 | ** will be skipped in addition to files that may contain binary content. |
| 638 | 579 | */ |
| 639 | -static void diff_all_two_versions( | |
| 580 | +static void diff_two_versions( | |
| 640 | 581 | const char *zFrom, |
| 641 | 582 | const char *zTo, |
| 642 | 583 | const char *zDiffCmd, |
| 643 | 584 | const char *zBinGlob, |
| 644 | 585 | int fIncludeBinary, |
| 645 | - u64 diffFlags | |
| 586 | + u64 diffFlags, | |
| 587 | + FileDirList *pFileDir | |
| 646 | 588 | ){ |
| 647 | 589 | Manifest *pFrom, *pTo; |
| 648 | 590 | ManifestFile *pFromFile, *pToFile; |
| 649 | 591 | int asNewFlag = (diffFlags & DIFF_VERBOSE)!=0 ? 1 : 0; |
| 650 | 592 | |
| @@ -663,33 +605,40 @@ | ||
| 663 | 605 | cmp = -1; |
| 664 | 606 | }else{ |
| 665 | 607 | cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); |
| 666 | 608 | } |
| 667 | 609 | if( cmp<0 ){ |
| 668 | - fossil_print("DELETED %s\n", pFromFile->zName); | |
| 669 | - if( asNewFlag ){ | |
| 670 | - diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob, | |
| 671 | - fIncludeBinary, diffFlags); | |
| 610 | + if( file_dir_match(pFileDir, pFromFile->zName) ){ | |
| 611 | + fossil_print("DELETED %s\n", pFromFile->zName); | |
| 612 | + if( asNewFlag ){ | |
| 613 | + diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob, | |
| 614 | + fIncludeBinary, diffFlags); | |
| 615 | + } | |
| 672 | 616 | } |
| 673 | 617 | pFromFile = manifest_file_next(pFrom,0); |
| 674 | 618 | }else if( cmp>0 ){ |
| 675 | - fossil_print("ADDED %s\n", pToFile->zName); | |
| 676 | - if( asNewFlag ){ | |
| 677 | - diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob, | |
| 678 | - fIncludeBinary, diffFlags); | |
| 619 | + if( file_dir_match(pFileDir, pToFile->zName) ){ | |
| 620 | + fossil_print("ADDED %s\n", pToFile->zName); | |
| 621 | + if( asNewFlag ){ | |
| 622 | + diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob, | |
| 623 | + fIncludeBinary, diffFlags); | |
| 624 | + } | |
| 679 | 625 | } |
| 680 | 626 | pToFile = manifest_file_next(pTo,0); |
| 681 | 627 | }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ |
| 682 | 628 | /* No changes */ |
| 629 | + (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ | |
| 683 | 630 | pFromFile = manifest_file_next(pFrom,0); |
| 684 | 631 | pToFile = manifest_file_next(pTo,0); |
| 685 | 632 | }else{ |
| 686 | - if( diffFlags & DIFF_BRIEF ){ | |
| 687 | - fossil_print("CHANGED %s\n", pFromFile->zName); | |
| 688 | - }else{ | |
| 689 | - diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob, | |
| 690 | - fIncludeBinary, diffFlags); | |
| 633 | + if( file_dir_match(pFileDir, pToFile->zName) ){ | |
| 634 | + if( diffFlags & DIFF_BRIEF ){ | |
| 635 | + fossil_print("CHANGED %s\n", pFromFile->zName); | |
| 636 | + }else{ | |
| 637 | + diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob, | |
| 638 | + fIncludeBinary, diffFlags); | |
| 639 | + } | |
| 691 | 640 | } |
| 692 | 641 | pFromFile = manifest_file_next(pFrom,0); |
| 693 | 642 | pToFile = manifest_file_next(pTo,0); |
| 694 | 643 | } |
| 695 | 644 | } |
| @@ -805,10 +754,53 @@ | ||
| 805 | 754 | const char *diff_get_binary_glob(void){ |
| 806 | 755 | const char *zBinGlob = find_option("binary", 0, 1); |
| 807 | 756 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 808 | 757 | return zBinGlob; |
| 809 | 758 | } |
| 759 | + | |
| 760 | +/* | |
| 761 | +** The input is a list of file and/or directory names either in the current | |
| 762 | +** checkout, or in the undo buffer if useUndo is true, or in check-outs | |
| 763 | +** zFrom and/or zTo if they are not null. Return a new list that consists | |
| 764 | +** of only filenames. Any directories on the input list are expected into | |
| 765 | +** multiple files in the output list. The output list uses space obtained | |
| 766 | +** from malloc() and is NULL terminated. | |
| 767 | +*/ | |
| 768 | +static char **diff_expand_dirs( | |
| 769 | + int argc, /* Number of entries in argv[] */ | |
| 770 | + const char **argv, /* List of files and/or directories to be diffed */ | |
| 771 | + const char *zFrom, /* Source check-in name, or NULL */ | |
| 772 | + const char *zTo, /* Target check-in name, or NULL */ | |
| 773 | + int useUndo /* True if diffing against the undo buffer */ | |
| 774 | +){ | |
| 775 | + Stmt q; | |
| 776 | + int i; | |
| 777 | + db_multi_exec( | |
| 778 | + "CREATE TEMP TABLE dfiles(" | |
| 779 | + " name TEXT PRIMARY KEY," | |
| 780 | + " path TEXT" | |
| 781 | + ") WITHOUT ROWID;" | |
| 782 | + "CREATE TEMP TABLE allowed(" | |
| 783 | + " fn TEXT PRIMARY KEY" | |
| 784 | + ") WITHOUT ROWID;" | |
| 785 | + ); | |
| 786 | + if( useUndo ){ | |
| 787 | + db_multi_exec( | |
| 788 | + "INSERT OR IGNORE INTO allowed(fn)" | |
| 789 | + " SELECT pathname FROM undo INTERSECT SELECT pathname FROM vfile;" | |
| 790 | + ); | |
| 791 | + }else if( zTo ){ | |
| 792 | + } | |
| 793 | + db_prepare(&q, "INSERT OR IGNORE INTO dfiles(names) VALUES(:txt)"); | |
| 794 | + for(i=0; i<argc; i++){ | |
| 795 | + db_bind_text(&q, ":txt", argv[i]); | |
| 796 | + db_step(&q); | |
| 797 | + db_reset(&q); | |
| 798 | + } | |
| 799 | + db_finalize(&q); | |
| 800 | + | |
| 801 | +} | |
| 810 | 802 | |
| 811 | 803 | /* |
| 812 | 804 | ** COMMAND: diff |
| 813 | 805 | ** COMMAND: gdiff |
| 814 | 806 | ** |
| @@ -874,10 +866,11 @@ | ||
| 874 | 866 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 875 | 867 | const char *zBinGlob = 0; /* Treat file names matching this as binary */ |
| 876 | 868 | int fIncludeBinary = 0; /* Include binary files for external diff */ |
| 877 | 869 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 878 | 870 | u64 diffFlags = 0; /* Flags to control the DIFF */ |
| 871 | + FileDirList *pFileDir = 0; /* Restrict the diff to these files */ | |
| 879 | 872 | |
| 880 | 873 | if( find_option("tk",0,0)!=0 ){ |
| 881 | 874 | diff_tk("diff", 2); |
| 882 | 875 | return; |
| 883 | 876 | } |
| @@ -915,47 +908,51 @@ | ||
| 915 | 908 | } |
| 916 | 909 | zBinGlob = diff_get_binary_glob(); |
| 917 | 910 | fIncludeBinary = diff_include_binary_files(); |
| 918 | 911 | determine_exec_relative_option(1); |
| 919 | 912 | verify_all_options(); |
| 913 | + if( g.argc>=3 ){ | |
| 914 | + int i; | |
| 915 | + Blob fname; | |
| 916 | + pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) ); | |
| 917 | + memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1)); | |
| 918 | + for(i=2; i<g.argc; i++){ | |
| 919 | + file_tree_name(g.argv[i], &fname, 0, 1); | |
| 920 | + pFileDir[i-2].zName = fossil_strdup(blob_str(&fname)); | |
| 921 | + if( strcmp(pFileDir[i-2].zName,".")==0 ){ | |
| 922 | + pFileDir[0].zName[0] = '.'; | |
| 923 | + pFileDir[0].zName[1] = 0; | |
| 924 | + break; | |
| 925 | + } | |
| 926 | + pFileDir[i-2].nName = blob_size(&fname); | |
| 927 | + pFileDir[i-2].nUsed = 0; | |
| 928 | + blob_reset(&fname); | |
| 929 | + } | |
| 930 | + } | |
| 920 | 931 | if( againstUndo ){ |
| 921 | 932 | if( db_lget_int("undo_available",0)==0 ){ |
| 922 | 933 | fossil_print("No undo or redo is available\n"); |
| 923 | 934 | return; |
| 924 | 935 | } |
| 925 | - if( g.argc>=3 ){ | |
| 926 | - int i; | |
| 927 | - for(i=2; i<g.argc; i++){ | |
| 928 | - diff_one_against_undo(zDiffCmd, zBinGlob, fIncludeBinary, | |
| 929 | - diffFlags, g.argv[i]); | |
| 930 | - } | |
| 931 | - }else{ | |
| 932 | - diff_all_against_undo(zDiffCmd, zBinGlob, fIncludeBinary, | |
| 933 | - diffFlags); | |
| 934 | - } | |
| 935 | - }else if( zTo==0 ){ | |
| 936 | - if( g.argc>=3 ){ | |
| 937 | - int i; | |
| 938 | - for(i=2; i<g.argc; i++){ | |
| 939 | - diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, | |
| 940 | - diffFlags, g.argv[i]); | |
| 941 | - } | |
| 942 | - }else{ | |
| 943 | - diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, | |
| 944 | - diffFlags); | |
| 945 | - } | |
| 946 | - }else{ | |
| 947 | - if( g.argc>=3 ){ | |
| 948 | - int i; | |
| 949 | - for(i=2; i<g.argc; i++){ | |
| 950 | - diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, | |
| 951 | - diffFlags, g.argv[i]); | |
| 952 | - } | |
| 953 | - }else{ | |
| 954 | - diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, | |
| 955 | - diffFlags); | |
| 956 | - } | |
| 936 | + diff_against_undo(zDiffCmd, zBinGlob, fIncludeBinary, | |
| 937 | + diffFlags, pFileDir); | |
| 938 | + }else if( zTo==0 ){ | |
| 939 | + diff_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, | |
| 940 | + diffFlags, pFileDir); | |
| 941 | + }else{ | |
| 942 | + diff_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, | |
| 943 | + diffFlags, pFileDir); | |
| 944 | + } | |
| 945 | + if( pFileDir ){ | |
| 946 | + int i; | |
| 947 | + for(i=0; pFileDir[i].zName; i++){ | |
| 948 | + if( pFileDir[i].nUsed==0 && strcmp(pFileDir[0].zName,".")!=0 ){ | |
| 949 | + fossil_fatal("not found: '%s'", g.argv[i+2]); | |
| 950 | + } | |
| 951 | + fossil_free(pFileDir[i].zName); | |
| 952 | + } | |
| 953 | + fossil_free(pFileDir); | |
| 957 | 954 | } |
| 958 | 955 | } |
| 959 | 956 | |
| 960 | 957 | /* |
| 961 | 958 | ** WEBPAGE: vpatch |
| @@ -969,7 +966,7 @@ | ||
| 969 | 966 | login_check_credentials(); |
| 970 | 967 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 971 | 968 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 972 | 969 | |
| 973 | 970 | cgi_set_content_type("text/plain"); |
| 974 | - diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE); | |
| 971 | + diff_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE, 0); | |
| 975 | 972 | } |
| 976 | 973 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -38,12 +38,11 @@ | |
| 38 | /* |
| 39 | ** Use the "exec-rel-paths" setting and the --exec-abs-paths and |
| 40 | ** --exec-rel-paths command line options to determine whether |
| 41 | ** certain external commands are executed using relative paths. |
| 42 | */ |
| 43 | static int determine_exec_relative_option(int force) |
| 44 | { |
| 45 | static int relativePaths = -1; |
| 46 | if( force || relativePaths==-1 ){ |
| 47 | int relPathOption = find_option("exec-rel-paths", 0, 0)!=0; |
| 48 | int absPathOption = find_option("exec-abs-paths", 0, 0)!=0; |
| 49 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| @@ -54,10 +53,60 @@ | |
| 54 | if( relPathOption ){ relativePaths = 1; } |
| 55 | if( absPathOption ){ relativePaths = 0; } |
| 56 | } |
| 57 | return relativePaths; |
| 58 | } |
| 59 | |
| 60 | /* |
| 61 | ** Print the "Index:" message that patches wants to see at the top of a diff. |
| 62 | */ |
| 63 | void diff_print_index(const char *zFile, u64 diffFlags){ |
| @@ -297,46 +346,10 @@ | |
| 297 | file_delete(zTemp2); |
| 298 | blob_reset(&cmd); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** Do a diff against a single file named in zFile from version zFrom |
| 304 | ** against the same file on disk. |
| 305 | ** |
| 306 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 307 | ** command zDiffCmd to do the diffing. |
| 308 | ** |
| 309 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 310 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 311 | ** will be skipped in addition to files that may contain binary content. |
| 312 | */ |
| 313 | static void diff_one_against_disk( |
| 314 | const char *zFrom, /* Version tag for the "before" file */ |
| 315 | const char *zDiffCmd, /* Use this "diff" command */ |
| 316 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 317 | int fIncludeBinary, /* Include binary files for external diff */ |
| 318 | u64 diffFlags, /* Diff control flags */ |
| 319 | const char *zFile /* Name of the file to be diffed */ |
| 320 | ){ |
| 321 | Blob fname; |
| 322 | Blob content; |
| 323 | int isLink; |
| 324 | int isBin; |
| 325 | file_tree_name(zFile, &fname, 0, 1); |
| 326 | historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, |
| 327 | fIncludeBinary ? 0 : &isBin, 0); |
| 328 | if( !isLink != !file_wd_islink(zFrom) ){ |
| 329 | fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); |
| 330 | }else{ |
| 331 | diff_file(&content, isBin, zFile, zFile, |
| 332 | zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 333 | } |
| 334 | blob_reset(&content); |
| 335 | blob_reset(&fname); |
| 336 | } |
| 337 | |
| 338 | /* |
| 339 | ** Run a diff between the version zFrom and files on disk. zFrom might |
| 340 | ** be NULL which means to simply show the difference between the edited |
| 341 | ** files on disk and the check-out on which they are based. |
| 342 | ** |
| @@ -345,16 +358,17 @@ | |
| 345 | ** |
| 346 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 347 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 348 | ** will be skipped in addition to files that may contain binary content. |
| 349 | */ |
| 350 | static void diff_all_against_disk( |
| 351 | const char *zFrom, /* Version to difference from */ |
| 352 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 353 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 354 | int fIncludeBinary, /* Treat file names matching this as binary */ |
| 355 | u64 diffFlags /* Flags controlling diff output */ |
| 356 | ){ |
| 357 | int vid; |
| 358 | Blob sql; |
| 359 | Stmt q; |
| 360 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -410,10 +424,11 @@ | |
| 410 | int isLink = db_column_int(&q, 5); |
| 411 | const char *zFullName; |
| 412 | int showDiff = 1; |
| 413 | Blob fname; |
| 414 | |
| 415 | if( determine_exec_relative_option(0) ){ |
| 416 | blob_zero(&fname); |
| 417 | file_relative_name(zPathname, &fname, 1); |
| 418 | }else{ |
| 419 | blob_set(&fname, g.zLocalRoot); |
| @@ -463,43 +478,10 @@ | |
| 463 | } |
| 464 | db_finalize(&q); |
| 465 | db_end_transaction(1); /* ROLLBACK */ |
| 466 | } |
| 467 | |
| 468 | /* |
| 469 | ** Do a diff of a single file named in zFile against the |
| 470 | ** version of this file held in the undo buffer. |
| 471 | ** |
| 472 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 473 | ** command zDiffCmd to do the diffing. |
| 474 | ** |
| 475 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 476 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 477 | ** will be skipped in addition to files that may contain binary content. |
| 478 | */ |
| 479 | static void diff_one_against_undo( |
| 480 | const char *zDiffCmd, /* Use this "diff" command */ |
| 481 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 482 | int fIncludeBinary, /* Include binary files for external diff */ |
| 483 | u64 diffFlags, /* Diff control flags */ |
| 484 | const char *zFile /* Name of the file to be diffed */ |
| 485 | ){ |
| 486 | Blob fname; |
| 487 | Blob content; |
| 488 | |
| 489 | blob_init(&content, 0, 0); |
| 490 | file_tree_name(zFile, &fname, 0, 1); |
| 491 | db_blob(&content, "SELECT content FROM undo WHERE pathname=%Q", |
| 492 | blob_str(&fname)); |
| 493 | if( blob_size(&content) ){ |
| 494 | diff_file(&content, 0, zFile, zFile, |
| 495 | zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 496 | } |
| 497 | blob_reset(&content); |
| 498 | blob_reset(&fname); |
| 499 | } |
| 500 | |
| 501 | /* |
| 502 | ** Run a diff between the undo buffer and files on disk. |
| 503 | ** |
| 504 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 505 | ** command zDiffCmd to do the diffing. |
| @@ -506,76 +488,35 @@ | |
| 506 | ** |
| 507 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 508 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 509 | ** will be skipped in addition to files that may contain binary content. |
| 510 | */ |
| 511 | static void diff_all_against_undo( |
| 512 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 513 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 514 | int fIncludeBinary, /* Treat file names matching this as binary */ |
| 515 | u64 diffFlags /* Flags controlling diff output */ |
| 516 | ){ |
| 517 | Stmt q; |
| 518 | Blob content; |
| 519 | db_prepare(&q, "SELECT pathname, content FROM undo"); |
| 520 | blob_init(&content, 0, 0); |
| 521 | while( db_step(&q)==SQLITE_ROW ){ |
| 522 | const char *zFile = (const char*)db_column_text(&q, 0); |
| 523 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zFile); |
| 524 | db_column_blob(&q, 1, &content); |
| 525 | diff_file(&content, 0, zFullName, zFile, |
| 526 | zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 527 | fossil_free(zFullName); |
| 528 | blob_reset(&content); |
| 529 | } |
| 530 | db_finalize(&q); |
| 531 | } |
| 532 | |
| 533 | /* |
| 534 | ** Output the differences between two versions of a single file. |
| 535 | ** zFrom and zTo are the check-ins containing the two file versions. |
| 536 | ** |
| 537 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 538 | ** command zDiffCmd to do the diffing. |
| 539 | ** |
| 540 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 541 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 542 | ** will be skipped in addition to files that may contain binary content. |
| 543 | */ |
| 544 | static void diff_one_two_versions( |
| 545 | const char *zFrom, /* Version tag for the "before" file */ |
| 546 | const char *zTo, /* Version tag for the "after" file */ |
| 547 | const char *zDiffCmd, /* Use this "diff" command */ |
| 548 | const char *zBinGlob, /* GLOB pattern for files that are binary */ |
| 549 | int fIncludeBinary, /* True to show binary files */ |
| 550 | u64 diffFlags, /* Diff flags */ |
| 551 | const char *zFile /* Name of the file to be diffed */ |
| 552 | ){ |
| 553 | char *zName; |
| 554 | Blob fname; |
| 555 | Blob v1, v2; |
| 556 | int isLink1, isLink2; |
| 557 | int isBin1, isBin2; |
| 558 | if( diffFlags & DIFF_BRIEF ) return; |
| 559 | file_tree_name(zFile, &fname, 0, 1); |
| 560 | zName = blob_str(&fname); |
| 561 | historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, |
| 562 | fIncludeBinary ? 0 : &isBin1, 0); |
| 563 | historical_version_of_file(zTo, zName, &v2, &isLink2, 0, |
| 564 | fIncludeBinary ? 0 : &isBin2, 0); |
| 565 | if( isLink1 != isLink2 ){ |
| 566 | diff_print_filenames(zName, zName, diffFlags); |
| 567 | fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); |
| 568 | }else{ |
| 569 | diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd, |
| 570 | zBinGlob, fIncludeBinary, diffFlags); |
| 571 | } |
| 572 | blob_reset(&v1); |
| 573 | blob_reset(&v2); |
| 574 | blob_reset(&fname); |
| 575 | } |
| 576 | |
| 577 | /* |
| 578 | ** Show the difference between two files identified by ManifestFile |
| 579 | ** entries. |
| 580 | ** |
| 581 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| @@ -634,17 +575,18 @@ | |
| 634 | ** |
| 635 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 636 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 637 | ** will be skipped in addition to files that may contain binary content. |
| 638 | */ |
| 639 | static void diff_all_two_versions( |
| 640 | const char *zFrom, |
| 641 | const char *zTo, |
| 642 | const char *zDiffCmd, |
| 643 | const char *zBinGlob, |
| 644 | int fIncludeBinary, |
| 645 | u64 diffFlags |
| 646 | ){ |
| 647 | Manifest *pFrom, *pTo; |
| 648 | ManifestFile *pFromFile, *pToFile; |
| 649 | int asNewFlag = (diffFlags & DIFF_VERBOSE)!=0 ? 1 : 0; |
| 650 | |
| @@ -663,33 +605,40 @@ | |
| 663 | cmp = -1; |
| 664 | }else{ |
| 665 | cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); |
| 666 | } |
| 667 | if( cmp<0 ){ |
| 668 | fossil_print("DELETED %s\n", pFromFile->zName); |
| 669 | if( asNewFlag ){ |
| 670 | diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob, |
| 671 | fIncludeBinary, diffFlags); |
| 672 | } |
| 673 | pFromFile = manifest_file_next(pFrom,0); |
| 674 | }else if( cmp>0 ){ |
| 675 | fossil_print("ADDED %s\n", pToFile->zName); |
| 676 | if( asNewFlag ){ |
| 677 | diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob, |
| 678 | fIncludeBinary, diffFlags); |
| 679 | } |
| 680 | pToFile = manifest_file_next(pTo,0); |
| 681 | }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ |
| 682 | /* No changes */ |
| 683 | pFromFile = manifest_file_next(pFrom,0); |
| 684 | pToFile = manifest_file_next(pTo,0); |
| 685 | }else{ |
| 686 | if( diffFlags & DIFF_BRIEF ){ |
| 687 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 688 | }else{ |
| 689 | diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob, |
| 690 | fIncludeBinary, diffFlags); |
| 691 | } |
| 692 | pFromFile = manifest_file_next(pFrom,0); |
| 693 | pToFile = manifest_file_next(pTo,0); |
| 694 | } |
| 695 | } |
| @@ -805,10 +754,53 @@ | |
| 805 | const char *diff_get_binary_glob(void){ |
| 806 | const char *zBinGlob = find_option("binary", 0, 1); |
| 807 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 808 | return zBinGlob; |
| 809 | } |
| 810 | |
| 811 | /* |
| 812 | ** COMMAND: diff |
| 813 | ** COMMAND: gdiff |
| 814 | ** |
| @@ -874,10 +866,11 @@ | |
| 874 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 875 | const char *zBinGlob = 0; /* Treat file names matching this as binary */ |
| 876 | int fIncludeBinary = 0; /* Include binary files for external diff */ |
| 877 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 878 | u64 diffFlags = 0; /* Flags to control the DIFF */ |
| 879 | |
| 880 | if( find_option("tk",0,0)!=0 ){ |
| 881 | diff_tk("diff", 2); |
| 882 | return; |
| 883 | } |
| @@ -915,47 +908,51 @@ | |
| 915 | } |
| 916 | zBinGlob = diff_get_binary_glob(); |
| 917 | fIncludeBinary = diff_include_binary_files(); |
| 918 | determine_exec_relative_option(1); |
| 919 | verify_all_options(); |
| 920 | if( againstUndo ){ |
| 921 | if( db_lget_int("undo_available",0)==0 ){ |
| 922 | fossil_print("No undo or redo is available\n"); |
| 923 | return; |
| 924 | } |
| 925 | if( g.argc>=3 ){ |
| 926 | int i; |
| 927 | for(i=2; i<g.argc; i++){ |
| 928 | diff_one_against_undo(zDiffCmd, zBinGlob, fIncludeBinary, |
| 929 | diffFlags, g.argv[i]); |
| 930 | } |
| 931 | }else{ |
| 932 | diff_all_against_undo(zDiffCmd, zBinGlob, fIncludeBinary, |
| 933 | diffFlags); |
| 934 | } |
| 935 | }else if( zTo==0 ){ |
| 936 | if( g.argc>=3 ){ |
| 937 | int i; |
| 938 | for(i=2; i<g.argc; i++){ |
| 939 | diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, |
| 940 | diffFlags, g.argv[i]); |
| 941 | } |
| 942 | }else{ |
| 943 | diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, |
| 944 | diffFlags); |
| 945 | } |
| 946 | }else{ |
| 947 | if( g.argc>=3 ){ |
| 948 | int i; |
| 949 | for(i=2; i<g.argc; i++){ |
| 950 | diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, |
| 951 | diffFlags, g.argv[i]); |
| 952 | } |
| 953 | }else{ |
| 954 | diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, |
| 955 | diffFlags); |
| 956 | } |
| 957 | } |
| 958 | } |
| 959 | |
| 960 | /* |
| 961 | ** WEBPAGE: vpatch |
| @@ -969,7 +966,7 @@ | |
| 969 | login_check_credentials(); |
| 970 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 971 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 972 | |
| 973 | cgi_set_content_type("text/plain"); |
| 974 | diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE); |
| 975 | } |
| 976 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -38,12 +38,11 @@ | |
| 38 | /* |
| 39 | ** Use the "exec-rel-paths" setting and the --exec-abs-paths and |
| 40 | ** --exec-rel-paths command line options to determine whether |
| 41 | ** certain external commands are executed using relative paths. |
| 42 | */ |
| 43 | static int determine_exec_relative_option(int force){ |
| 44 | static int relativePaths = -1; |
| 45 | if( force || relativePaths==-1 ){ |
| 46 | int relPathOption = find_option("exec-rel-paths", 0, 0)!=0; |
| 47 | int absPathOption = find_option("exec-abs-paths", 0, 0)!=0; |
| 48 | #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) |
| @@ -54,10 +53,60 @@ | |
| 53 | if( relPathOption ){ relativePaths = 1; } |
| 54 | if( absPathOption ){ relativePaths = 0; } |
| 55 | } |
| 56 | return relativePaths; |
| 57 | } |
| 58 | |
| 59 | #if INTERFACE |
| 60 | /* |
| 61 | ** An array of FileDirList objects describe the files and directories listed |
| 62 | ** on the command line of a "diff" command. Only those objects listed are |
| 63 | ** actually diffed. |
| 64 | */ |
| 65 | struct FileDirList { |
| 66 | int nUsed; /* Number of times each entry is used */ |
| 67 | int nName; /* Length of the entry */ |
| 68 | char *zName; /* Text of the entry */ |
| 69 | }; |
| 70 | #endif |
| 71 | |
| 72 | /* |
| 73 | ** Return true if zFile is a file named on the azInclude[] list or is |
| 74 | ** a file in a directory named on the azInclude[] list. |
| 75 | ** |
| 76 | ** if azInclude is NULL, then always include zFile. |
| 77 | */ |
| 78 | static int file_dir_match(FileDirList *p, const char *zFile){ |
| 79 | int i = 0; |
| 80 | if( p==0 || strcmp(p->zName,".")==0 ) return 1; |
| 81 | if( filenames_are_case_sensitive() ){ |
| 82 | while( p->zName ){ |
| 83 | if( strcmp(zFile, p->zName)==0 |
| 84 | || (strncmp(zFile, p->zName, p->nName)==0 |
| 85 | && zFile[p->nName]=='/') |
| 86 | ){ |
| 87 | break; |
| 88 | } |
| 89 | p++; |
| 90 | } |
| 91 | }else{ |
| 92 | while( p->zName ){ |
| 93 | if( fossil_stricmp(zFile, p->zName)==0 |
| 94 | || (fossil_strnicmp(zFile, p->zName, p->nName)==0 |
| 95 | && zFile[p->nName]=='/') |
| 96 | ){ |
| 97 | break; |
| 98 | } |
| 99 | p++; |
| 100 | } |
| 101 | } |
| 102 | if( p->zName ){ |
| 103 | p->nUsed++; |
| 104 | return 1; |
| 105 | } |
| 106 | return 0; |
| 107 | } |
| 108 | |
| 109 | /* |
| 110 | ** Print the "Index:" message that patches wants to see at the top of a diff. |
| 111 | */ |
| 112 | void diff_print_index(const char *zFile, u64 diffFlags){ |
| @@ -297,46 +346,10 @@ | |
| 346 | file_delete(zTemp2); |
| 347 | blob_reset(&cmd); |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | ** Run a diff between the version zFrom and files on disk. zFrom might |
| 353 | ** be NULL which means to simply show the difference between the edited |
| 354 | ** files on disk and the check-out on which they are based. |
| 355 | ** |
| @@ -345,16 +358,17 @@ | |
| 358 | ** |
| 359 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 360 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 361 | ** will be skipped in addition to files that may contain binary content. |
| 362 | */ |
| 363 | static void diff_against_disk( |
| 364 | const char *zFrom, /* Version to difference from */ |
| 365 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 366 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 367 | int fIncludeBinary, /* Treat file names matching this as binary */ |
| 368 | u64 diffFlags, /* Flags controlling diff output */ |
| 369 | FileDirList *pFileDir /* Which files to diff */ |
| 370 | ){ |
| 371 | int vid; |
| 372 | Blob sql; |
| 373 | Stmt q; |
| 374 | int asNewFile; /* Treat non-existant files as empty files */ |
| @@ -410,10 +424,11 @@ | |
| 424 | int isLink = db_column_int(&q, 5); |
| 425 | const char *zFullName; |
| 426 | int showDiff = 1; |
| 427 | Blob fname; |
| 428 | |
| 429 | if( !file_dir_match(pFileDir, zPathname) ) continue; |
| 430 | if( determine_exec_relative_option(0) ){ |
| 431 | blob_zero(&fname); |
| 432 | file_relative_name(zPathname, &fname, 1); |
| 433 | }else{ |
| 434 | blob_set(&fname, g.zLocalRoot); |
| @@ -463,43 +478,10 @@ | |
| 478 | } |
| 479 | db_finalize(&q); |
| 480 | db_end_transaction(1); /* ROLLBACK */ |
| 481 | } |
| 482 | |
| 483 | /* |
| 484 | ** Run a diff between the undo buffer and files on disk. |
| 485 | ** |
| 486 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| 487 | ** command zDiffCmd to do the diffing. |
| @@ -506,76 +488,35 @@ | |
| 488 | ** |
| 489 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 490 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 491 | ** will be skipped in addition to files that may contain binary content. |
| 492 | */ |
| 493 | static void diff_against_undo( |
| 494 | const char *zDiffCmd, /* Use this diff command. NULL for built-in */ |
| 495 | const char *zBinGlob, /* Treat file names matching this as binary */ |
| 496 | int fIncludeBinary, /* Treat file names matching this as binary */ |
| 497 | u64 diffFlags, /* Flags controlling diff output */ |
| 498 | FileDirList *pFileDir /* List of files and directories to diff */ |
| 499 | ){ |
| 500 | Stmt q; |
| 501 | Blob content; |
| 502 | db_prepare(&q, "SELECT pathname, content FROM undo"); |
| 503 | blob_init(&content, 0, 0); |
| 504 | while( db_step(&q)==SQLITE_ROW ){ |
| 505 | char *zFullName; |
| 506 | const char *zFile = (const char*)db_column_text(&q, 0); |
| 507 | if( !file_dir_match(pFileDir, zFile) ) continue; |
| 508 | zFullName = mprintf("%s%s", g.zLocalRoot, zFile); |
| 509 | db_column_blob(&q, 1, &content); |
| 510 | diff_file(&content, 0, zFullName, zFile, |
| 511 | zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); |
| 512 | fossil_free(zFullName); |
| 513 | blob_reset(&content); |
| 514 | } |
| 515 | db_finalize(&q); |
| 516 | } |
| 517 | |
| 518 | /* |
| 519 | ** Show the difference between two files identified by ManifestFile |
| 520 | ** entries. |
| 521 | ** |
| 522 | ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the |
| @@ -634,17 +575,18 @@ | |
| 575 | ** |
| 576 | ** When using an external diff program, zBinGlob contains the GLOB patterns |
| 577 | ** for file names to treat as binary. If fIncludeBinary is zero, these files |
| 578 | ** will be skipped in addition to files that may contain binary content. |
| 579 | */ |
| 580 | static void diff_two_versions( |
| 581 | const char *zFrom, |
| 582 | const char *zTo, |
| 583 | const char *zDiffCmd, |
| 584 | const char *zBinGlob, |
| 585 | int fIncludeBinary, |
| 586 | u64 diffFlags, |
| 587 | FileDirList *pFileDir |
| 588 | ){ |
| 589 | Manifest *pFrom, *pTo; |
| 590 | ManifestFile *pFromFile, *pToFile; |
| 591 | int asNewFlag = (diffFlags & DIFF_VERBOSE)!=0 ? 1 : 0; |
| 592 | |
| @@ -663,33 +605,40 @@ | |
| 605 | cmp = -1; |
| 606 | }else{ |
| 607 | cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); |
| 608 | } |
| 609 | if( cmp<0 ){ |
| 610 | if( file_dir_match(pFileDir, pFromFile->zName) ){ |
| 611 | fossil_print("DELETED %s\n", pFromFile->zName); |
| 612 | if( asNewFlag ){ |
| 613 | diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob, |
| 614 | fIncludeBinary, diffFlags); |
| 615 | } |
| 616 | } |
| 617 | pFromFile = manifest_file_next(pFrom,0); |
| 618 | }else if( cmp>0 ){ |
| 619 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 620 | fossil_print("ADDED %s\n", pToFile->zName); |
| 621 | if( asNewFlag ){ |
| 622 | diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob, |
| 623 | fIncludeBinary, diffFlags); |
| 624 | } |
| 625 | } |
| 626 | pToFile = manifest_file_next(pTo,0); |
| 627 | }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ |
| 628 | /* No changes */ |
| 629 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 630 | pFromFile = manifest_file_next(pFrom,0); |
| 631 | pToFile = manifest_file_next(pTo,0); |
| 632 | }else{ |
| 633 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 634 | if( diffFlags & DIFF_BRIEF ){ |
| 635 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 636 | }else{ |
| 637 | diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob, |
| 638 | fIncludeBinary, diffFlags); |
| 639 | } |
| 640 | } |
| 641 | pFromFile = manifest_file_next(pFrom,0); |
| 642 | pToFile = manifest_file_next(pTo,0); |
| 643 | } |
| 644 | } |
| @@ -805,10 +754,53 @@ | |
| 754 | const char *diff_get_binary_glob(void){ |
| 755 | const char *zBinGlob = find_option("binary", 0, 1); |
| 756 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 757 | return zBinGlob; |
| 758 | } |
| 759 | |
| 760 | /* |
| 761 | ** The input is a list of file and/or directory names either in the current |
| 762 | ** checkout, or in the undo buffer if useUndo is true, or in check-outs |
| 763 | ** zFrom and/or zTo if they are not null. Return a new list that consists |
| 764 | ** of only filenames. Any directories on the input list are expected into |
| 765 | ** multiple files in the output list. The output list uses space obtained |
| 766 | ** from malloc() and is NULL terminated. |
| 767 | */ |
| 768 | static char **diff_expand_dirs( |
| 769 | int argc, /* Number of entries in argv[] */ |
| 770 | const char **argv, /* List of files and/or directories to be diffed */ |
| 771 | const char *zFrom, /* Source check-in name, or NULL */ |
| 772 | const char *zTo, /* Target check-in name, or NULL */ |
| 773 | int useUndo /* True if diffing against the undo buffer */ |
| 774 | ){ |
| 775 | Stmt q; |
| 776 | int i; |
| 777 | db_multi_exec( |
| 778 | "CREATE TEMP TABLE dfiles(" |
| 779 | " name TEXT PRIMARY KEY," |
| 780 | " path TEXT" |
| 781 | ") WITHOUT ROWID;" |
| 782 | "CREATE TEMP TABLE allowed(" |
| 783 | " fn TEXT PRIMARY KEY" |
| 784 | ") WITHOUT ROWID;" |
| 785 | ); |
| 786 | if( useUndo ){ |
| 787 | db_multi_exec( |
| 788 | "INSERT OR IGNORE INTO allowed(fn)" |
| 789 | " SELECT pathname FROM undo INTERSECT SELECT pathname FROM vfile;" |
| 790 | ); |
| 791 | }else if( zTo ){ |
| 792 | } |
| 793 | db_prepare(&q, "INSERT OR IGNORE INTO dfiles(names) VALUES(:txt)"); |
| 794 | for(i=0; i<argc; i++){ |
| 795 | db_bind_text(&q, ":txt", argv[i]); |
| 796 | db_step(&q); |
| 797 | db_reset(&q); |
| 798 | } |
| 799 | db_finalize(&q); |
| 800 | |
| 801 | } |
| 802 | |
| 803 | /* |
| 804 | ** COMMAND: diff |
| 805 | ** COMMAND: gdiff |
| 806 | ** |
| @@ -874,10 +866,11 @@ | |
| 866 | const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ |
| 867 | const char *zBinGlob = 0; /* Treat file names matching this as binary */ |
| 868 | int fIncludeBinary = 0; /* Include binary files for external diff */ |
| 869 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 870 | u64 diffFlags = 0; /* Flags to control the DIFF */ |
| 871 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 872 | |
| 873 | if( find_option("tk",0,0)!=0 ){ |
| 874 | diff_tk("diff", 2); |
| 875 | return; |
| 876 | } |
| @@ -915,47 +908,51 @@ | |
| 908 | } |
| 909 | zBinGlob = diff_get_binary_glob(); |
| 910 | fIncludeBinary = diff_include_binary_files(); |
| 911 | determine_exec_relative_option(1); |
| 912 | verify_all_options(); |
| 913 | if( g.argc>=3 ){ |
| 914 | int i; |
| 915 | Blob fname; |
| 916 | pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) ); |
| 917 | memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1)); |
| 918 | for(i=2; i<g.argc; i++){ |
| 919 | file_tree_name(g.argv[i], &fname, 0, 1); |
| 920 | pFileDir[i-2].zName = fossil_strdup(blob_str(&fname)); |
| 921 | if( strcmp(pFileDir[i-2].zName,".")==0 ){ |
| 922 | pFileDir[0].zName[0] = '.'; |
| 923 | pFileDir[0].zName[1] = 0; |
| 924 | break; |
| 925 | } |
| 926 | pFileDir[i-2].nName = blob_size(&fname); |
| 927 | pFileDir[i-2].nUsed = 0; |
| 928 | blob_reset(&fname); |
| 929 | } |
| 930 | } |
| 931 | if( againstUndo ){ |
| 932 | if( db_lget_int("undo_available",0)==0 ){ |
| 933 | fossil_print("No undo or redo is available\n"); |
| 934 | return; |
| 935 | } |
| 936 | diff_against_undo(zDiffCmd, zBinGlob, fIncludeBinary, |
| 937 | diffFlags, pFileDir); |
| 938 | }else if( zTo==0 ){ |
| 939 | diff_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, |
| 940 | diffFlags, pFileDir); |
| 941 | }else{ |
| 942 | diff_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, |
| 943 | diffFlags, pFileDir); |
| 944 | } |
| 945 | if( pFileDir ){ |
| 946 | int i; |
| 947 | for(i=0; pFileDir[i].zName; i++){ |
| 948 | if( pFileDir[i].nUsed==0 && strcmp(pFileDir[0].zName,".")!=0 ){ |
| 949 | fossil_fatal("not found: '%s'", g.argv[i+2]); |
| 950 | } |
| 951 | fossil_free(pFileDir[i].zName); |
| 952 | } |
| 953 | fossil_free(pFileDir); |
| 954 | } |
| 955 | } |
| 956 | |
| 957 | /* |
| 958 | ** WEBPAGE: vpatch |
| @@ -969,7 +966,7 @@ | |
| 966 | login_check_credentials(); |
| 967 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 968 | if( zFrom==0 || zTo==0 ) fossil_redirect_home(); |
| 969 | |
| 970 | cgi_set_content_type("text/plain"); |
| 971 | diff_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE, 0); |
| 972 | } |
| 973 |