Fossil SCM
Added a "no changes" messages if no local files are different; report trying to view a non-existent local file. Tidy-up and added config-options and "what I did" block near top of info.c.
Commit
a955c80b43c358fb819487b330a395a6c690f9931b6c10a0d4828b88e941d828
Parent
bf5a21e0364383b…
1 file changed
+97
-88
+97
-88
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1,21 +1,5 @@ | ||
| 1 | -/*TODO Graham's Notes | |
| 2 | -** o Should /file behave differently for non-existent local files? | |
| 3 | -** o Find a place to add links to /local. | |
| 4 | -** o Remove //TODO TESTING HACK TODO | |
| 5 | -** ?? In hexdump_page(), should content (and downloadName?) be reset/freed? | |
| 6 | -** ?? In clean_cmd() in checkin.c, should "Blob repo" be blob_reset()? | |
| 7 | -** ?? Do I need to worry about deleting/emptying TEMP SFILE? | |
| 8 | -** ?? Is it normal for one artifact to have several check-ins associated with | |
| 9 | -** it? In the test fossil (\x\$Test\Fossil) there are several commits under | |
| 10 | -** the same artifact... | |
| 11 | -** ?? A setting to control one- or two-pass mode? | |
| 12 | -** ?? Add two-pass to the normal loop? | |
| 13 | -** ?? A setting to control whether Extras are intially shown or not? | |
| 14 | -** ?? A setting to control max. entries to show initially? | |
| 15 | -**------------------------------------------------------------------------------ | |
| 16 | -*/ | |
| 17 | 1 | /* |
| 18 | 2 | ** Copyright (c) 2007 D. Richard Hipp |
| 19 | 3 | ** |
| 20 | 4 | ** This program is free software; you can redistribute it and/or |
| 21 | 5 | ** modify it under the terms of the Simplified BSD License (also |
| @@ -36,10 +20,54 @@ | ||
| 36 | 20 | ** the current tree, or a particular artifact or check-in. |
| 37 | 21 | */ |
| 38 | 22 | #include "config.h" |
| 39 | 23 | #include "info.h" |
| 40 | 24 | #include <assert.h> |
| 25 | + | |
| 26 | +/*----------------------------------------------------------------------------- | |
| 27 | +** LOCAL DIFF CHANGES | |
| 28 | +** | |
| 29 | +** The goal is to show "(working) changes in the current checkout" in much the | |
| 30 | +** same way as the current "check-in" pages show the changes within a check-in. | |
| 31 | +** | |
| 32 | +** The page is on "/local" (no parameters needed; an alias of "/vinfo" and | |
| 33 | +** "/ci"), but at present there are no links to it within Fossil. Other changes: | |
| 34 | +** "/localpatch" produces a patch-set for all changes in the current checkout; | |
| 35 | +** "/localdiff" is an alias for "/fdiff" to show changes for an individual file; | |
| 36 | +** both "/file" and "/hexdump" have been extended to support local files. | |
| 37 | +** | |
| 38 | +** A number of points-of-query are labeld "TODO:LD". Most relate to these | |
| 39 | +** changes, although some are about existing code. | |
| 40 | +** | |
| 41 | +** In "checkin.c", the function "locate_unmanaged_files()" has been made | |
| 42 | +** non-static so that it can be called from here. | |
| 43 | +**----------------------------------------------------------------------------- | |
| 44 | +** Options to control the "local changes" changes. At present, these are | |
| 45 | +** defines: if these changes are adopted, some may want to be made into | |
| 46 | +** configuration options. | |
| 47 | +** | |
| 48 | +** INTEGER: Controls how many unmanaged files will be shown before the "plus xxx | |
| 49 | +** other matching files." line is shown (with an option to view all of them).*/ | |
| 50 | +#define LOCAL_DIFF_MAX_EXTRAS (5) | |
| 51 | +/* | |
| 52 | +** STRING: Controls whether the "extras" report is initially shown or hidden. A | |
| 53 | +** value of "0" hides the report; a value of "" (an empty string) will show it. | |
| 54 | +*/ | |
| 55 | +#define LOCAL_DIFF_EXTRAS_MODE ("") | |
| 56 | +/* | |
| 57 | +** BOOLEAN: Controls whether one or two passes are made through the list of | |
| 58 | +** changed files. In two-pass mode, all single-line differences are displayed | |
| 59 | +** ahead of all differences involving "diff-blocks", making them less likely to | |
| 60 | +** be overlooked. If disabled, only one pass is made, listing all changes in the | |
| 61 | +** order found. Possible TODO: Do the same for "normal" diffs. */ | |
| 62 | +#define LOCAL_DIFF_USE_TWO_PASSES (1) | |
| 63 | +/* | |
| 64 | +** BOOLEAN: Controls whether dividers ("<hr/>") added after any "diff-blocks" | |
| 65 | +** (except the last one)... IMHO doing so makes it easier to see where one block | |
| 66 | +** ends and the next starts. Possible TODO: Do the same for "normal" diffs. */ | |
| 67 | +#define LOCAL_DIFF_ADD_DIVIDER (1) | |
| 68 | +/*---------------------------------------------------------------------------*/ | |
| 41 | 69 | |
| 42 | 70 | /* |
| 43 | 71 | ** Return a string (in memory obtained from malloc) holding a |
| 44 | 72 | ** comma-separated list of tags that apply to check-in with |
| 45 | 73 | ** record-id rid. If the "propagatingOnly" flag is true, then only |
| @@ -339,24 +367,25 @@ | ||
| 339 | 367 | 0, 0, 0, rid, rid2, 0); |
| 340 | 368 | db_finalize(&q); |
| 341 | 369 | } |
| 342 | 370 | |
| 343 | 371 | /* |
| 344 | -** Read the content of file zName (prepended with the checkout directory) | |
| 345 | -** and put it into the uninitialized blob. The blob is zeroed if the file | |
| 346 | -** does not exist (if the file cannot be read, blob_read_from_file() aborts | |
| 347 | -** the program). | |
| 372 | +** Read the content of file zName (prepended with the checkout directory) and | |
| 373 | +** put it into the uninitialized blob, returning 1. The blob is zeroed if the | |
| 374 | +** file does not exist or cannot be accessed, in which case it returns 0. | |
| 348 | 375 | */ |
| 349 | -static void content_from_file( | |
| 376 | +static int content_from_file( | |
| 350 | 377 | const char *zName, /* Filename (relative to checkout) of file to be read */ |
| 351 | 378 | Blob *pBlob /* Pointer to blob to receive contents */ |
| 352 | 379 | ){ |
| 353 | 380 | const char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 354 | 381 | blob_zero(pBlob); |
| 355 | - if( file_size(zFullPath, ExtFILE)>=0 ){ | |
| 356 | - blob_read_from_file(pBlob, zFullPath, ExtFILE); | |
| 382 | + if( file_size(zFullPath, ExtFILE)<0 ){ | |
| 383 | + return 0; | |
| 357 | 384 | } |
| 385 | + blob_read_from_file(pBlob, zFullPath, ExtFILE); | |
| 386 | + return 1; | |
| 358 | 387 | } |
| 359 | 388 | |
| 360 | 389 | /* |
| 361 | 390 | ** Append the difference between artifacts to the output |
| 362 | 391 | ** If zLocal is not NULL, instead compare against the local |
| @@ -514,15 +543,14 @@ | ||
| 514 | 543 | ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */ |
| 515 | 544 | int pass /* 0x01 - Display single-line entries only */ |
| 516 | 545 | /* 0x02 - Display entries with "diff blocks" only */ |
| 517 | 546 | /* 0x03 - Display both */ |
| 518 | 547 | ){ |
| 519 | -#ifndef GLH_NO_DIVIDER | |
| 548 | +#if LOCAL_DIFF_ADD_DIVIDER | |
| 520 | 549 | /* This remembers whether a side-by-side "diff-block" was shown the last |
| 521 | 550 | ** time through. If it was, we will add "<hr/>" to better separate the |
| 522 | - ** blocks and so single-line entries (when not in two-pass mode) are easier | |
| 523 | - ** to spot. | |
| 551 | + ** blocks. | |
| 524 | 552 | */ |
| 525 | 553 | static int diffShownLastTime = 0; |
| 526 | 554 | #endif |
| 527 | 555 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zName); |
| 528 | 556 | int isFilePresent = !file_access(zFullName, F_OK); |
| @@ -541,31 +569,25 @@ | ||
| 541 | 569 | /* We don't use 'diffFlags' in these tests so that whether "Hide diffs" is |
| 542 | 570 | ** in effect or not, the order won't change. |
| 543 | 571 | */ |
| 544 | 572 | if( showDiff && (pass == 1) ){ return; } /* Don't do diff on pass 1 of 2 */ |
| 545 | 573 | if( !showDiff && (pass == 2) ){ return; } /* Don't do line on pass 2 of 2 */ |
| 546 | -#ifndef GLH_NO_DIVIDER | |
| 574 | +#if LOCAL_DIFF_ADD_DIVIDER | |
| 547 | 575 | /* If a SBS diff-block was shown by the previous entry, add a divider */ |
| 548 | 576 | if( diffShownLastTime && (diffFlags & DIFF_SIDEBYSIDE) ){ |
| 549 | 577 | @ <hr/> |
| 550 | 578 | } |
| 551 | 579 | /* Record whether we will be showing a diff-block this time. We DO factor in |
| 552 | 580 | ** 'diffFlags' here so that in "Hide diffs" mode, we don't get extra lines. |
| 553 | 581 | */ |
| 554 | 582 | diffShownLastTime = showDiff && diffFlags; |
| 555 | -#endif /*GLH_NO_DIVIDER*/ | |
| 556 | -//--------------------------------------------------- TODO TESTING HACK TODO | |
| 557 | -if( strncmp(zName,"aa",2)==0 ){ | |
| 558 | - isChnged = atoi(zName+2); | |
| 559 | -} | |
| 560 | -//--------------------------------------------------- TODO TESTING HACK TODO | |
| 583 | +#endif | |
| 561 | 584 | @ <p> |
| 562 | 585 | if( !g.perm.Hyperlink ){ |
| 563 | 586 | if( isDeleted ){ |
| 564 | 587 | if( isFilePresent ){ |
| 565 | 588 | @ Deleted %h(zName) (still present as a local file). |
| 566 | - //TODO:Remove? showDiff = 1; | |
| 567 | 589 | }else{ |
| 568 | 590 | @ Deleted %h(zName). |
| 569 | 591 | } |
| 570 | 592 | }else if( isNew ){ |
| 571 | 593 | if( isFilePresent ){ |
| @@ -572,15 +594,10 @@ | ||
| 572 | 594 | @ Added %h(zName) but not committed. |
| 573 | 595 | }else{ |
| 574 | 596 | @ Missing %h(zName) (was added to checkout). |
| 575 | 597 | } |
| 576 | 598 | }else switch( isChnged ){ |
| 577 | - /*TODO | |
| 578 | - ** These "special cases" have not all been properly tested (by creating | |
| 579 | - ** entries in a in a repository to trigger them), but they do display | |
| 580 | - ** as expected when "forced" to appear. | |
| 581 | - */ | |
| 582 | 599 | case 3: |
| 583 | 600 | @ Added %h(zName) due to a merge. |
| 584 | 601 | break; |
| 585 | 602 | case 5: |
| 586 | 603 | @ Added %h(zName) due to an integrate-merge. |
| @@ -601,11 +618,10 @@ | ||
| 601 | 618 | case 4: |
| 602 | 619 | @ Integrate-merge |
| 603 | 620 | break; |
| 604 | 621 | } |
| 605 | 622 | @ of %h(zName). |
| 606 | - //TODO:Remove? showDiff = 1; | |
| 607 | 623 | } |
| 608 | 624 | if( showDiff && diffFlags ){ |
| 609 | 625 | append_diff(zOld, NULL, zName, diffFlags, pRe); |
| 610 | 626 | } |
| 611 | 627 | }else{ /* With hyperlinks */ |
| @@ -613,11 +629,10 @@ | ||
| 613 | 629 | if( isFilePresent ){ /* DELETEd but still on disk */ |
| 614 | 630 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 615 | 631 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> (still present |
| 616 | 632 | @ as %z(href("%R/file/%T?ci=ckout&annot=removed from checkout",zName)) |
| 617 | 633 | @ [local file]</a>). |
| 618 | - //TODO:Remove? showDiff = 1; | |
| 619 | 634 | }else{ /* DELETEd and deleted from disk */ |
| 620 | 635 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 621 | 636 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 622 | 637 | } |
| 623 | 638 | }else if( isNew ){ |
| @@ -626,11 +641,10 @@ | ||
| 626 | 641 | @ but not committed. |
| 627 | 642 | }else{ /* ADDed but not present on disk */ |
| 628 | 643 | @ Missing %h(zName) (was added to checkout). |
| 629 | 644 | } |
| 630 | 645 | }else switch( isChnged ){ |
| 631 | - /*TODO Not fully tested... see see no-hyperlink version above */ | |
| 632 | 646 | case 3: /* Added by a merge */ |
| 633 | 647 | @ Added |
| 634 | 648 | @ %z(href("%R/file/%T?ci=ckout&annot=added by merge",zName))%h(zName) |
| 635 | 649 | @ </a> to %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> due to merge. |
| 636 | 650 | break; |
| @@ -659,11 +673,10 @@ | ||
| 659 | 673 | } |
| 660 | 674 | @ of %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 661 | 675 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> to |
| 662 | 676 | @ %z(href("%R/file/%T?ci=ckout&annot=edited locally",zName)) |
| 663 | 677 | @ [local file]</a> |
| 664 | - //TODO:Remove? showDiff = 1; | |
| 665 | 678 | } |
| 666 | 679 | if( showDiff ){ |
| 667 | 680 | if( diffFlags ){ |
| 668 | 681 | append_diff(zOld, NULL, zName, diffFlags, pRe); |
| 669 | 682 | }else if( isChnged ){ |
| @@ -886,22 +899,22 @@ | ||
| 886 | 899 | pIgnore = glob_create(zIgnoreFlag); /* Object versions of above */ |
| 887 | 900 | pKeep = glob_create(zKeepFlag); |
| 888 | 901 | pClean = glob_create(zCleanFlag); |
| 889 | 902 | nRoot = (int)strlen(g.zLocalRoot); /* Length of root component */ |
| 890 | 903 | Stmt q; |
| 891 | - Blob repo; /* TODO May not be needed */ | |
| 892 | - int maxExtrasToShow = 5; /* TODO Take from a setting? */ | |
| 904 | + Blob repo; /* TODO:LD May not be needed */ | |
| 905 | + int maxExtrasToShow = LOCAL_DIFF_MAX_EXTRAS; | |
| 893 | 906 | int extrasFlags = atoi(zExtra); /* Which entries to show */ |
| 894 | 907 | int nExtras; |
| 895 | 908 | int nMatch; |
| 896 | 909 | int nShown; |
| 897 | 910 | int nPlain; |
| 898 | 911 | int nIgnore; |
| 899 | 912 | int nClean; |
| 900 | 913 | int nKeep; |
| 901 | 914 | |
| 902 | - /*TODO? | |
| 915 | + /*TODO:LD? | |
| 903 | 916 | ** It feels sensible to limit the number of "extra" entries shown by default |
| 904 | 917 | ** for cases where "ignore-glob" or "clean-glob" haven't been fully setup. |
| 905 | 918 | ** A minor irritation is that this can lead to "... plus 1 more file", on a |
| 906 | 919 | ** line that COULD have been used to display the omitted file. If we knew in |
| 907 | 920 | ** advance how many entries were going to match, we could temporarily "bump" |
| @@ -911,56 +924,56 @@ | ||
| 911 | 924 | ** possibly bump the limit, then re-scan the table repeating the tests |
| 912 | 925 | ** against each glob-list to decide which to show. |
| 913 | 926 | ** b) SFILE could have an extra FLAGS field: during the pre-scan, this could |
| 914 | 927 | ** be updated to indicate which groups each file belong to. This would |
| 915 | 928 | ** save re-testing every file against each glob-list (the main pass could |
| 916 | - ** select "WHERE flags & selector" to get only the matching entries, but | |
| 929 | + ** select "WHERE flags & selector" to get only the matching entries), but | |
| 917 | 930 | ** the updates (selecting by "pathname" each time) could be a bit much. |
| 918 | 931 | ** c) vfile_scan() -- where SFILE is populated -- COULD have an option to |
| 919 | 932 | ** do the testing at the time entries are added. This would be the "best" |
| 920 | 933 | ** way, but feels too much disruption to other code for what is only a |
| 921 | 934 | ** minor benefit. |
| 922 | 935 | ** For now, I'll stick with the minor annoyance of "plus 1 more file" :-) |
| 923 | 936 | ** |
| 924 | 937 | ** Being able to determine the counts up-front would also allow us to hide |
| 925 | - ** the "extras report" if there were no unmanaged files. | |
| 938 | + ** the whole "extras report" if there were no unmanaged files. | |
| 926 | 939 | ** |
| 927 | - **TODO? | |
| 940 | + **TODO:LD? | |
| 928 | 941 | ** Does it make sense (and/or is it practiable) to offer an "ADD" button |
| 929 | 942 | ** against files that are unmanaged? |
| 930 | 943 | ** |
| 931 | - **TODO? | |
| 944 | + **TODO:LD? | |
| 932 | 945 | ** Does it make sense (and/or ...) to offer ediing of the various blob-lists |
| 933 | 946 | ** from the Extras report? Showing the existing configuration screen would |
| 934 | 947 | ** probably not be a problem (permissions permitting), but what happens if |
| 935 | 948 | ** those settings have been overriden by .fossil-settings/ignore-glob? As we |
| 936 | 949 | ** have access to the local checkout, is it feasible to edit it in the browser |
| 937 | 950 | ** (perhaps piggy-backing /fileedit)? |
| 938 | 951 | */ |
| 939 | 952 | |
| 940 | 953 | locate_unmanaged_files(0, NULL, 0, NULL); /* Get all unmanaged */ |
| 941 | - /*TODO | |
| 954 | + /*TODO:LD | |
| 942 | 955 | ** The first two of these exclusions come from clean_cmd() in checkin.c. |
| 943 | 956 | ** Not sure exactly what they are intended to do (seem to have no effect on |
| 944 | 957 | ** my test repos). Last exclusion is an alternative to the WHERE clause above |
| 945 | 958 | ** so that COUNT(*) returns the correct value. TODO Even though, as noted |
| 946 | 959 | ** above, getting the count ahead of time is of little use (it was used to |
| 947 | 960 | ** bump the display limit if only one entry would be omitted), I'll probably |
| 948 | 961 | ** retain omitting the WHERE, and using DELETE FROM to exclude reserved |
| 949 | - ** names, just in case (c) above was implemented. | |
| 962 | + ** names, just in case (c) above were to be implemented. | |
| 950 | 963 | */ |
| 951 | - /*TODO deletions from clean_cmd() */ | |
| 964 | + /*TODO:LD deletions from clean_cmd() */ | |
| 952 | 965 | if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){ |
| 953 | 966 | db_multi_exec("DELETE FROM sfile WHERE pathname=%B", &repo); |
| 954 | 967 | } |
| 955 | 968 | db_multi_exec("DELETE FROM sfile WHERE pathname IN" |
| 956 | 969 | " (SELECT pathname FROM vfile)"); |
| 957 | - /*TODO Delete reserved names, rather than WHERE them out. */ | |
| 970 | + /*TODO:LD Delete reserved names, rather than WHERE them out. */ | |
| 958 | 971 | db_multi_exec("DELETE FROM sfile WHERE pathname IN (%s)", |
| 959 | 972 | fossil_all_reserved_names(0)); |
| 960 | 973 | |
| 961 | - /*TODO | |
| 974 | + /*TODO:LD | |
| 962 | 975 | ** If we had a count of matching entries before scanning, this is where |
| 963 | 976 | ** we'd bump the maximum to show so as to avoid "plus 1 file". |
| 964 | 977 | ** ... |
| 965 | 978 | ** If there's only one more than the maximum, let it through... |
| 966 | 979 | ** a line used to say "plus 1 more" may as well display that item! |
| @@ -969,12 +982,11 @@ | ||
| 969 | 982 | */ |
| 970 | 983 | |
| 971 | 984 | /* Handle the special case where zExtra was empty (and got converted to zero). |
| 972 | 985 | ** If so, show "plain" files (those not matching any glob-list) but with an |
| 973 | 986 | ** upper limit to the number shown (set above). If a value WAS given (i.e. |
| 974 | - ** after following a link), display all of the selected entries. | |
| 975 | - */ | |
| 987 | + ** after following a link), display all of the selected entries. */ | |
| 976 | 988 | if( extrasFlags==0 ){ |
| 977 | 989 | extrasFlags = EX_PLAIN; |
| 978 | 990 | }else{ |
| 979 | 991 | maxExtrasToShow = 0x7fffffff; /* Basically, all of them... */ |
| 980 | 992 | } |
| @@ -997,26 +1009,26 @@ | ||
| 997 | 1009 | blob_zero(&desc); |
| 998 | 1010 | if( extrasFlags & EX_PLAIN ){ blob_appendf(&desc, " + unmanaged" ); } |
| 999 | 1011 | if( extrasFlags & EX_IGNORE ){ blob_appendf(&desc, " + ignored" ); } |
| 1000 | 1012 | if( extrasFlags & EX_CLEAN ){ blob_appendf(&desc, " + to be cleaned"); } |
| 1001 | 1013 | if( extrasFlags & EX_KEEP ){ blob_appendf(&desc, " + to be kept" ); } |
| 1002 | - if( blob_size(&desc) > 3 ){ /* Should never fail... */ | |
| 1014 | + if( blob_size(&desc) > 3 ){ /* Should never fail... */ | |
| 1003 | 1015 | /* Add the string built above, skipping the leading " + " */ |
| 1004 | 1016 | @ (%h(blob_str(&desc)+3)) |
| 1005 | 1017 | } |
| 1006 | 1018 | blob_reset(&desc); |
| 1007 | 1019 | } |
| 1008 | 1020 | @ </b></p> |
| 1009 | 1021 | |
| 1010 | 1022 | db_prepare(&q, |
| 1011 | 1023 | "SELECT %Q || pathname FROM sfile" |
| 1012 | - " ORDER BY 1", //TODO Order by pathname? | |
| 1024 | + " ORDER BY 1", /*TODO:LD Order by pathname, as for differences? */ | |
| 1013 | 1025 | g.zLocalRoot |
| 1014 | 1026 | ); |
| 1015 | 1027 | /* |
| 1016 | 1028 | ** Put the file-list in one paragraph with line-breaks between. |
| 1017 | - **TODO | |
| 1029 | + **TODO:LD | |
| 1018 | 1030 | ** Might a table (with columns for name, ignore/clean/keep) work? |
| 1019 | 1031 | */ |
| 1020 | 1032 | @ <p> |
| 1021 | 1033 | nExtras = nMatch = nShown = nPlain = nKeep = nClean = nIgnore = 0; |
| 1022 | 1034 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1183,33 +1195,33 @@ | ||
| 1183 | 1195 | ** If the "ef=" isn't present (as when first navigating to "/local") then a |
| 1184 | 1196 | ** default setting is used. Set to "0" to initially hide the report. |
| 1185 | 1197 | */ |
| 1186 | 1198 | zExtra = P("ef"); |
| 1187 | 1199 | if( zExtra==NULL ) { |
| 1188 | - zExtra = ""; /*TODO Take the default form a config. option? */ | |
| 1200 | + zExtra = LOCAL_DIFF_EXTRAS_MODE; | |
| 1189 | 1201 | } |
| 1190 | 1202 | showExtras = strcmp(zExtra,"0")!=0; |
| 1191 | 1203 | |
| 1192 | 1204 | /* Local mode is selected by either "/local" or with a "name" of "ckout". |
| 1193 | 1205 | ** First, check we have access to the checkout (and report to the user if we |
| 1194 | 1206 | ** don't), then refresh the "vfile" table (recording which files in the |
| 1195 | 1207 | ** checkout have changed etc.). We then change the "name" parameter to "tip" |
| 1196 | 1208 | ** so that the "header" section displays info about the check-in that the |
| 1197 | 1209 | ** checkout came from. |
| 1198 | - **TODO | |
| 1210 | + **TODO:LD | |
| 1199 | 1211 | ** It would probably make sense to limit "/local" (and other links that come |
| 1200 | 1212 | ** from it) to only be permitted when Fossil is running locally in "ui" mode. |
| 1201 | 1213 | ** It's probably not critical when all you can do is view files in the |
| 1202 | 1214 | ** checkout (they can already see the checked-in versions), but if a COMMIT |
| 1203 | 1215 | ** option WERE ever to be implemented, you wouldn't essentially random people |
| 1204 | - ** on the internet firing off commits. | |
| 1216 | + ** on the internet firing off commits! | |
| 1205 | 1217 | */ |
| 1206 | 1218 | bLocalMode = (g.zPath[0]=='l') || (fossil_strcmp(zName,"ckout")==0); |
| 1207 | 1219 | if( bLocalMode ){ |
| 1208 | 1220 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 1209 | 1221 | if( vid==0 ){ |
| 1210 | - /*TODO Is this the right response? */ | |
| 1222 | + /*TODO:LD Is this the right response? */ | |
| 1211 | 1223 | style_header("No Local Checkout"); |
| 1212 | 1224 | @ No access to local checkout. |
| 1213 | 1225 | style_footer(); |
| 1214 | 1226 | return; |
| 1215 | 1227 | } |
| @@ -1523,11 +1535,11 @@ | ||
| 1523 | 1535 | if( showExtras ){ |
| 1524 | 1536 | @ %z(chref("button","%R/local?diff=%d%s&ef=0",diffType,zW))Hide Extras</a> |
| 1525 | 1537 | }else{ |
| 1526 | 1538 | @ %z(chref("button","%R/local?diff=%d%s&ef=",diffType,zW))Show Extras</a> |
| 1527 | 1539 | } |
| 1528 | - /*TODO | |
| 1540 | + /*TODO:LD | |
| 1529 | 1541 | ** There would be a fair chunk of stuff to get right (not least appropriate |
| 1530 | 1542 | ** restrictions), but it MIGHT be nice to have a COMMIT button here... |
| 1531 | 1543 | */ |
| 1532 | 1544 | } |
| 1533 | 1545 | @</div> |
| @@ -1538,11 +1550,11 @@ | ||
| 1538 | 1550 | if( bLocalMode ){ |
| 1539 | 1551 | if( showExtras ){ |
| 1540 | 1552 | append_extras_report(zExtra, diffType, zW); |
| 1541 | 1553 | } |
| 1542 | 1554 | /* Following SQL taken from diff_against_disk() in diffcmd.c */ |
| 1543 | - /*TODO | |
| 1555 | + /*TODO:LD | |
| 1544 | 1556 | ** That code wrapped the query/processing in a transaction (but, from |
| 1545 | 1557 | ** memory, other similar uses did not). Is it neeeded? |
| 1546 | 1558 | */ |
| 1547 | 1559 | db_begin_transaction(); |
| 1548 | 1560 | db_prepare(&q3, |
| @@ -1561,17 +1573,15 @@ | ||
| 1561 | 1573 | ** side-effect of altering the order entries are shown in (but within each |
| 1562 | 1574 | ** group the original order is maintained). |
| 1563 | 1575 | ** |
| 1564 | 1576 | ** If disabled, (pass gets set to 3), only one pass is made on which all |
| 1565 | 1577 | ** entries are shown in their "normal" order. |
| 1566 | - **TODO | |
| 1578 | + **TODO:LD | |
| 1567 | 1579 | ** Add this to the original (non-local) loop? |
| 1568 | 1580 | */ |
| 1569 | -//--------------------------------------------------- TODO TESTING HACK TODO | |
| 1570 | - int bTwoPass = P("op")==NULL; //TODO Taken from a config option? | |
| 1571 | -//--------------------------------------------------- TODO TESTING HACK TODO | |
| 1572 | - int pass = bTwoPass?1:3; | |
| 1581 | + int pass = LOCAL_DIFF_USE_TWO_PASSES?1:3; | |
| 1582 | + int anyDifferences = 0; | |
| 1573 | 1583 | do{ |
| 1574 | 1584 | while( db_step(&q3)==SQLITE_ROW ){ |
| 1575 | 1585 | const char *zPathname = db_column_text(&q3,0); |
| 1576 | 1586 | int isDeleted = db_column_int(&q3, 1); |
| 1577 | 1587 | int isChnged = db_column_int(&q3,2); |
| @@ -1580,17 +1590,21 @@ | ||
| 1580 | 1590 | int isLink = db_column_int(&q3, 5); |
| 1581 | 1591 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcid); |
| 1582 | 1592 | append_local_file_change_line( zPathname, zUuid, |
| 1583 | 1593 | isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass ); |
| 1584 | 1594 | free(zUuid); |
| 1595 | + anyDifferences = 1; | |
| 1585 | 1596 | } |
| 1586 | 1597 | db_reset(&q3); |
| 1587 | 1598 | }while( ++pass < 3 ); /* Either "1, 2, stop" or "3, stop". */ |
| 1599 | + if( !anyDifferences ){ | |
| 1600 | + @ <p>No changes in the local checkout.</p> | |
| 1601 | + } | |
| 1588 | 1602 | db_finalize(&q3); |
| 1589 | 1603 | db_end_transaction(1); /* ROLLBACK */ |
| 1590 | 1604 | }else{ /* Normal, non-local-mode: show diffs against parent */ |
| 1591 | - /*TODO: Implement the optional two-pass code? */ | |
| 1605 | + /*TODO:LD: Implement the optional two-pass code? */ | |
| 1592 | 1606 | db_prepare(&q3, |
| 1593 | 1607 | "SELECT name," |
| 1594 | 1608 | " mperm," |
| 1595 | 1609 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 1596 | 1610 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| @@ -1631,11 +1645,11 @@ | ||
| 1631 | 1645 | login_check_credentials(); |
| 1632 | 1646 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1633 | 1647 | |
| 1634 | 1648 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 1635 | 1649 | if( vid==0 ){ |
| 1636 | - /*TODO Is this the right response? */ | |
| 1650 | + /*TODO:LD Is this the right response? */ | |
| 1637 | 1651 | style_header("No Local Checkout"); |
| 1638 | 1652 | @ No access to local checkout. |
| 1639 | 1653 | style_footer(); |
| 1640 | 1654 | return; |
| 1641 | 1655 | } |
| @@ -1642,11 +1656,11 @@ | ||
| 1642 | 1656 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 1643 | 1657 | |
| 1644 | 1658 | cgi_set_content_type("text/plain"); |
| 1645 | 1659 | |
| 1646 | 1660 | db_begin_transaction(); |
| 1647 | - /*TODO | |
| 1661 | + /*TODO:LD | |
| 1648 | 1662 | ** This query is the same as in ci_page() for local-mode (as well as in |
| 1649 | 1663 | ** diff_against_disk() in diffcmd.c, where it was originally taken from). |
| 1650 | 1664 | ** Should they be "coalesced" in some way? |
| 1651 | 1665 | */ |
| 1652 | 1666 | db_prepare(&q3, |
| @@ -1975,17 +1989,17 @@ | ||
| 1975 | 1989 | "%R/vdiff?%s&diff=1%s%T%s", |
| 1976 | 1990 | zQuery, |
| 1977 | 1991 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1978 | 1992 | } |
| 1979 | 1993 | if( zBranch==0 ){ |
| 1980 | - //TODO Is there an extra "&" here? | |
| 1994 | + //TODO:LD Is there an extra "&" here? | |
| 1981 | 1995 | style_submenu_element("Invert", |
| 1982 | 1996 | "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, |
| 1983 | 1997 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1984 | 1998 | } |
| 1985 | 1999 | if( zGlob ){ |
| 1986 | - //TODO Is there an extra "&" here? | |
| 2000 | + //TODO:LD Is there an extra "&" here? | |
| 1987 | 2001 | style_submenu_element("Clear glob", |
| 1988 | 2002 | "%R/vdiff?%s&%s", zQuery, zW); |
| 1989 | 2003 | }else{ |
| 1990 | 2004 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); |
| 1991 | 2005 | } |
| @@ -2739,11 +2753,11 @@ | ||
| 2739 | 2753 | style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); |
| 2740 | 2754 | } |
| 2741 | 2755 | } |
| 2742 | 2756 | } |
| 2743 | 2757 | style_header("Hex Artifact Content"); |
| 2744 | - /* TODO | |
| 2758 | + /*TODO:LD | |
| 2745 | 2759 | ** Could the call to style_header() be moved so these two exclusion |
| 2746 | 2760 | ** blocks could be merged? I don't think any of them make sense for |
| 2747 | 2761 | ** a local file. |
| 2748 | 2762 | */ |
| 2749 | 2763 | if( !bLocalMode ){ |
| @@ -2770,11 +2784,11 @@ | ||
| 2770 | 2784 | }else{ |
| 2771 | 2785 | content_get(rid, &content); |
| 2772 | 2786 | } |
| 2773 | 2787 | @ <blockquote><pre> |
| 2774 | 2788 | hexdump(&content); |
| 2775 | - /* TODO: Should content (and downloadName?) be reset/freed? */ | |
| 2789 | + /* TODO:LD: Should content (and downloadName?) be reset/freed? */ | |
| 2776 | 2790 | @ </pre></blockquote> |
| 2777 | 2791 | style_footer(); |
| 2778 | 2792 | } |
| 2779 | 2793 | |
| 2780 | 2794 | /* |
| @@ -3022,10 +3036,11 @@ | ||
| 3022 | 3036 | ** For /file, a name= without a ci= while prefer to use the default |
| 3023 | 3037 | ** "tip" value for ci=. */ |
| 3024 | 3038 | rid = name_to_rid(zName); |
| 3025 | 3039 | } |
| 3026 | 3040 | if( fossil_strcmp(zCI,"ckout")==0 ){ |
| 3041 | + /* "ci=ckout" is an extension for viewing files in the local checkout. */ | |
| 3027 | 3042 | bLocalMode = 1; |
| 3028 | 3043 | rid = -1; /* Dummy value to make it look found */ |
| 3029 | 3044 | }else if( rid==0 ){ |
| 3030 | 3045 | rid = artifact_from_ci_and_filename(0); |
| 3031 | 3046 | } |
| @@ -3062,11 +3077,11 @@ | ||
| 3062 | 3077 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 3063 | 3078 | |
| 3064 | 3079 | asText = P("txt")!=0; |
| 3065 | 3080 | if( isFile ){ |
| 3066 | 3081 | if( bLocalMode ){ |
| 3067 | - /*TODO | |
| 3082 | + /*TODO:LD | |
| 3068 | 3083 | ** Is this the best way of handling annotations to the description? |
| 3069 | 3084 | ** If "annot=message" is part of the URL, the message is appended |
| 3070 | 3085 | ** to the description of the file. Only used for "local" files to |
| 3071 | 3086 | ** distinguish such files from part of the repository. |
| 3072 | 3087 | */ |
| @@ -3212,19 +3227,13 @@ | ||
| 3212 | 3227 | if( descOnly ){ |
| 3213 | 3228 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 3214 | 3229 | }else{ |
| 3215 | 3230 | @ <hr /> |
| 3216 | 3231 | if( bLocalMode ){ |
| 3217 | - /*TODO | |
| 3218 | - ** Should we handle non-existent local files differently? Currently, | |
| 3219 | - ** they are shown the same as if the file was present but empty. This | |
| 3220 | - ** should never happen through "normal" operation, but someone might | |
| 3221 | - ** craft a link to one. Perhaps have content_from_file() perform an | |
| 3222 | - ** existence-check (rather than relying on blob_read_from_file() which | |
| 3223 | - ** it calls returning an empty blob)? | |
| 3224 | - */ | |
| 3225 | - content_from_file(zName, &content); | |
| 3232 | + if( !content_from_file(zName, &content) ){ | |
| 3233 | + fossil_warning("Cannot find/access %s.", zName); | |
| 3234 | + } | |
| 3226 | 3235 | }else{ |
| 3227 | 3236 | content_get(rid, &content); |
| 3228 | 3237 | } |
| 3229 | 3238 | if( renderAsWiki ){ |
| 3230 | 3239 | wiki_render_by_mimetype(&content, zMime); |
| 3231 | 3240 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1,21 +1,5 @@ | |
| 1 | /*TODO Graham's Notes |
| 2 | ** o Should /file behave differently for non-existent local files? |
| 3 | ** o Find a place to add links to /local. |
| 4 | ** o Remove //TODO TESTING HACK TODO |
| 5 | ** ?? In hexdump_page(), should content (and downloadName?) be reset/freed? |
| 6 | ** ?? In clean_cmd() in checkin.c, should "Blob repo" be blob_reset()? |
| 7 | ** ?? Do I need to worry about deleting/emptying TEMP SFILE? |
| 8 | ** ?? Is it normal for one artifact to have several check-ins associated with |
| 9 | ** it? In the test fossil (\x\$Test\Fossil) there are several commits under |
| 10 | ** the same artifact... |
| 11 | ** ?? A setting to control one- or two-pass mode? |
| 12 | ** ?? Add two-pass to the normal loop? |
| 13 | ** ?? A setting to control whether Extras are intially shown or not? |
| 14 | ** ?? A setting to control max. entries to show initially? |
| 15 | **------------------------------------------------------------------------------ |
| 16 | */ |
| 17 | /* |
| 18 | ** Copyright (c) 2007 D. Richard Hipp |
| 19 | ** |
| 20 | ** This program is free software; you can redistribute it and/or |
| 21 | ** modify it under the terms of the Simplified BSD License (also |
| @@ -36,10 +20,54 @@ | |
| 36 | ** the current tree, or a particular artifact or check-in. |
| 37 | */ |
| 38 | #include "config.h" |
| 39 | #include "info.h" |
| 40 | #include <assert.h> |
| 41 | |
| 42 | /* |
| 43 | ** Return a string (in memory obtained from malloc) holding a |
| 44 | ** comma-separated list of tags that apply to check-in with |
| 45 | ** record-id rid. If the "propagatingOnly" flag is true, then only |
| @@ -339,24 +367,25 @@ | |
| 339 | 0, 0, 0, rid, rid2, 0); |
| 340 | db_finalize(&q); |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | ** Read the content of file zName (prepended with the checkout directory) |
| 345 | ** and put it into the uninitialized blob. The blob is zeroed if the file |
| 346 | ** does not exist (if the file cannot be read, blob_read_from_file() aborts |
| 347 | ** the program). |
| 348 | */ |
| 349 | static void content_from_file( |
| 350 | const char *zName, /* Filename (relative to checkout) of file to be read */ |
| 351 | Blob *pBlob /* Pointer to blob to receive contents */ |
| 352 | ){ |
| 353 | const char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 354 | blob_zero(pBlob); |
| 355 | if( file_size(zFullPath, ExtFILE)>=0 ){ |
| 356 | blob_read_from_file(pBlob, zFullPath, ExtFILE); |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | /* |
| 361 | ** Append the difference between artifacts to the output |
| 362 | ** If zLocal is not NULL, instead compare against the local |
| @@ -514,15 +543,14 @@ | |
| 514 | ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */ |
| 515 | int pass /* 0x01 - Display single-line entries only */ |
| 516 | /* 0x02 - Display entries with "diff blocks" only */ |
| 517 | /* 0x03 - Display both */ |
| 518 | ){ |
| 519 | #ifndef GLH_NO_DIVIDER |
| 520 | /* This remembers whether a side-by-side "diff-block" was shown the last |
| 521 | ** time through. If it was, we will add "<hr/>" to better separate the |
| 522 | ** blocks and so single-line entries (when not in two-pass mode) are easier |
| 523 | ** to spot. |
| 524 | */ |
| 525 | static int diffShownLastTime = 0; |
| 526 | #endif |
| 527 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zName); |
| 528 | int isFilePresent = !file_access(zFullName, F_OK); |
| @@ -541,31 +569,25 @@ | |
| 541 | /* We don't use 'diffFlags' in these tests so that whether "Hide diffs" is |
| 542 | ** in effect or not, the order won't change. |
| 543 | */ |
| 544 | if( showDiff && (pass == 1) ){ return; } /* Don't do diff on pass 1 of 2 */ |
| 545 | if( !showDiff && (pass == 2) ){ return; } /* Don't do line on pass 2 of 2 */ |
| 546 | #ifndef GLH_NO_DIVIDER |
| 547 | /* If a SBS diff-block was shown by the previous entry, add a divider */ |
| 548 | if( diffShownLastTime && (diffFlags & DIFF_SIDEBYSIDE) ){ |
| 549 | @ <hr/> |
| 550 | } |
| 551 | /* Record whether we will be showing a diff-block this time. We DO factor in |
| 552 | ** 'diffFlags' here so that in "Hide diffs" mode, we don't get extra lines. |
| 553 | */ |
| 554 | diffShownLastTime = showDiff && diffFlags; |
| 555 | #endif /*GLH_NO_DIVIDER*/ |
| 556 | //--------------------------------------------------- TODO TESTING HACK TODO |
| 557 | if( strncmp(zName,"aa",2)==0 ){ |
| 558 | isChnged = atoi(zName+2); |
| 559 | } |
| 560 | //--------------------------------------------------- TODO TESTING HACK TODO |
| 561 | @ <p> |
| 562 | if( !g.perm.Hyperlink ){ |
| 563 | if( isDeleted ){ |
| 564 | if( isFilePresent ){ |
| 565 | @ Deleted %h(zName) (still present as a local file). |
| 566 | //TODO:Remove? showDiff = 1; |
| 567 | }else{ |
| 568 | @ Deleted %h(zName). |
| 569 | } |
| 570 | }else if( isNew ){ |
| 571 | if( isFilePresent ){ |
| @@ -572,15 +594,10 @@ | |
| 572 | @ Added %h(zName) but not committed. |
| 573 | }else{ |
| 574 | @ Missing %h(zName) (was added to checkout). |
| 575 | } |
| 576 | }else switch( isChnged ){ |
| 577 | /*TODO |
| 578 | ** These "special cases" have not all been properly tested (by creating |
| 579 | ** entries in a in a repository to trigger them), but they do display |
| 580 | ** as expected when "forced" to appear. |
| 581 | */ |
| 582 | case 3: |
| 583 | @ Added %h(zName) due to a merge. |
| 584 | break; |
| 585 | case 5: |
| 586 | @ Added %h(zName) due to an integrate-merge. |
| @@ -601,11 +618,10 @@ | |
| 601 | case 4: |
| 602 | @ Integrate-merge |
| 603 | break; |
| 604 | } |
| 605 | @ of %h(zName). |
| 606 | //TODO:Remove? showDiff = 1; |
| 607 | } |
| 608 | if( showDiff && diffFlags ){ |
| 609 | append_diff(zOld, NULL, zName, diffFlags, pRe); |
| 610 | } |
| 611 | }else{ /* With hyperlinks */ |
| @@ -613,11 +629,10 @@ | |
| 613 | if( isFilePresent ){ /* DELETEd but still on disk */ |
| 614 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 615 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> (still present |
| 616 | @ as %z(href("%R/file/%T?ci=ckout&annot=removed from checkout",zName)) |
| 617 | @ [local file]</a>). |
| 618 | //TODO:Remove? showDiff = 1; |
| 619 | }else{ /* DELETEd and deleted from disk */ |
| 620 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 621 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 622 | } |
| 623 | }else if( isNew ){ |
| @@ -626,11 +641,10 @@ | |
| 626 | @ but not committed. |
| 627 | }else{ /* ADDed but not present on disk */ |
| 628 | @ Missing %h(zName) (was added to checkout). |
| 629 | } |
| 630 | }else switch( isChnged ){ |
| 631 | /*TODO Not fully tested... see see no-hyperlink version above */ |
| 632 | case 3: /* Added by a merge */ |
| 633 | @ Added |
| 634 | @ %z(href("%R/file/%T?ci=ckout&annot=added by merge",zName))%h(zName) |
| 635 | @ </a> to %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> due to merge. |
| 636 | break; |
| @@ -659,11 +673,10 @@ | |
| 659 | } |
| 660 | @ of %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 661 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> to |
| 662 | @ %z(href("%R/file/%T?ci=ckout&annot=edited locally",zName)) |
| 663 | @ [local file]</a> |
| 664 | //TODO:Remove? showDiff = 1; |
| 665 | } |
| 666 | if( showDiff ){ |
| 667 | if( diffFlags ){ |
| 668 | append_diff(zOld, NULL, zName, diffFlags, pRe); |
| 669 | }else if( isChnged ){ |
| @@ -886,22 +899,22 @@ | |
| 886 | pIgnore = glob_create(zIgnoreFlag); /* Object versions of above */ |
| 887 | pKeep = glob_create(zKeepFlag); |
| 888 | pClean = glob_create(zCleanFlag); |
| 889 | nRoot = (int)strlen(g.zLocalRoot); /* Length of root component */ |
| 890 | Stmt q; |
| 891 | Blob repo; /* TODO May not be needed */ |
| 892 | int maxExtrasToShow = 5; /* TODO Take from a setting? */ |
| 893 | int extrasFlags = atoi(zExtra); /* Which entries to show */ |
| 894 | int nExtras; |
| 895 | int nMatch; |
| 896 | int nShown; |
| 897 | int nPlain; |
| 898 | int nIgnore; |
| 899 | int nClean; |
| 900 | int nKeep; |
| 901 | |
| 902 | /*TODO? |
| 903 | ** It feels sensible to limit the number of "extra" entries shown by default |
| 904 | ** for cases where "ignore-glob" or "clean-glob" haven't been fully setup. |
| 905 | ** A minor irritation is that this can lead to "... plus 1 more file", on a |
| 906 | ** line that COULD have been used to display the omitted file. If we knew in |
| 907 | ** advance how many entries were going to match, we could temporarily "bump" |
| @@ -911,56 +924,56 @@ | |
| 911 | ** possibly bump the limit, then re-scan the table repeating the tests |
| 912 | ** against each glob-list to decide which to show. |
| 913 | ** b) SFILE could have an extra FLAGS field: during the pre-scan, this could |
| 914 | ** be updated to indicate which groups each file belong to. This would |
| 915 | ** save re-testing every file against each glob-list (the main pass could |
| 916 | ** select "WHERE flags & selector" to get only the matching entries, but |
| 917 | ** the updates (selecting by "pathname" each time) could be a bit much. |
| 918 | ** c) vfile_scan() -- where SFILE is populated -- COULD have an option to |
| 919 | ** do the testing at the time entries are added. This would be the "best" |
| 920 | ** way, but feels too much disruption to other code for what is only a |
| 921 | ** minor benefit. |
| 922 | ** For now, I'll stick with the minor annoyance of "plus 1 more file" :-) |
| 923 | ** |
| 924 | ** Being able to determine the counts up-front would also allow us to hide |
| 925 | ** the "extras report" if there were no unmanaged files. |
| 926 | ** |
| 927 | **TODO? |
| 928 | ** Does it make sense (and/or is it practiable) to offer an "ADD" button |
| 929 | ** against files that are unmanaged? |
| 930 | ** |
| 931 | **TODO? |
| 932 | ** Does it make sense (and/or ...) to offer ediing of the various blob-lists |
| 933 | ** from the Extras report? Showing the existing configuration screen would |
| 934 | ** probably not be a problem (permissions permitting), but what happens if |
| 935 | ** those settings have been overriden by .fossil-settings/ignore-glob? As we |
| 936 | ** have access to the local checkout, is it feasible to edit it in the browser |
| 937 | ** (perhaps piggy-backing /fileedit)? |
| 938 | */ |
| 939 | |
| 940 | locate_unmanaged_files(0, NULL, 0, NULL); /* Get all unmanaged */ |
| 941 | /*TODO |
| 942 | ** The first two of these exclusions come from clean_cmd() in checkin.c. |
| 943 | ** Not sure exactly what they are intended to do (seem to have no effect on |
| 944 | ** my test repos). Last exclusion is an alternative to the WHERE clause above |
| 945 | ** so that COUNT(*) returns the correct value. TODO Even though, as noted |
| 946 | ** above, getting the count ahead of time is of little use (it was used to |
| 947 | ** bump the display limit if only one entry would be omitted), I'll probably |
| 948 | ** retain omitting the WHERE, and using DELETE FROM to exclude reserved |
| 949 | ** names, just in case (c) above was implemented. |
| 950 | */ |
| 951 | /*TODO deletions from clean_cmd() */ |
| 952 | if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){ |
| 953 | db_multi_exec("DELETE FROM sfile WHERE pathname=%B", &repo); |
| 954 | } |
| 955 | db_multi_exec("DELETE FROM sfile WHERE pathname IN" |
| 956 | " (SELECT pathname FROM vfile)"); |
| 957 | /*TODO Delete reserved names, rather than WHERE them out. */ |
| 958 | db_multi_exec("DELETE FROM sfile WHERE pathname IN (%s)", |
| 959 | fossil_all_reserved_names(0)); |
| 960 | |
| 961 | /*TODO |
| 962 | ** If we had a count of matching entries before scanning, this is where |
| 963 | ** we'd bump the maximum to show so as to avoid "plus 1 file". |
| 964 | ** ... |
| 965 | ** If there's only one more than the maximum, let it through... |
| 966 | ** a line used to say "plus 1 more" may as well display that item! |
| @@ -969,12 +982,11 @@ | |
| 969 | */ |
| 970 | |
| 971 | /* Handle the special case where zExtra was empty (and got converted to zero). |
| 972 | ** If so, show "plain" files (those not matching any glob-list) but with an |
| 973 | ** upper limit to the number shown (set above). If a value WAS given (i.e. |
| 974 | ** after following a link), display all of the selected entries. |
| 975 | */ |
| 976 | if( extrasFlags==0 ){ |
| 977 | extrasFlags = EX_PLAIN; |
| 978 | }else{ |
| 979 | maxExtrasToShow = 0x7fffffff; /* Basically, all of them... */ |
| 980 | } |
| @@ -997,26 +1009,26 @@ | |
| 997 | blob_zero(&desc); |
| 998 | if( extrasFlags & EX_PLAIN ){ blob_appendf(&desc, " + unmanaged" ); } |
| 999 | if( extrasFlags & EX_IGNORE ){ blob_appendf(&desc, " + ignored" ); } |
| 1000 | if( extrasFlags & EX_CLEAN ){ blob_appendf(&desc, " + to be cleaned"); } |
| 1001 | if( extrasFlags & EX_KEEP ){ blob_appendf(&desc, " + to be kept" ); } |
| 1002 | if( blob_size(&desc) > 3 ){ /* Should never fail... */ |
| 1003 | /* Add the string built above, skipping the leading " + " */ |
| 1004 | @ (%h(blob_str(&desc)+3)) |
| 1005 | } |
| 1006 | blob_reset(&desc); |
| 1007 | } |
| 1008 | @ </b></p> |
| 1009 | |
| 1010 | db_prepare(&q, |
| 1011 | "SELECT %Q || pathname FROM sfile" |
| 1012 | " ORDER BY 1", //TODO Order by pathname? |
| 1013 | g.zLocalRoot |
| 1014 | ); |
| 1015 | /* |
| 1016 | ** Put the file-list in one paragraph with line-breaks between. |
| 1017 | **TODO |
| 1018 | ** Might a table (with columns for name, ignore/clean/keep) work? |
| 1019 | */ |
| 1020 | @ <p> |
| 1021 | nExtras = nMatch = nShown = nPlain = nKeep = nClean = nIgnore = 0; |
| 1022 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1183,33 +1195,33 @@ | |
| 1183 | ** If the "ef=" isn't present (as when first navigating to "/local") then a |
| 1184 | ** default setting is used. Set to "0" to initially hide the report. |
| 1185 | */ |
| 1186 | zExtra = P("ef"); |
| 1187 | if( zExtra==NULL ) { |
| 1188 | zExtra = ""; /*TODO Take the default form a config. option? */ |
| 1189 | } |
| 1190 | showExtras = strcmp(zExtra,"0")!=0; |
| 1191 | |
| 1192 | /* Local mode is selected by either "/local" or with a "name" of "ckout". |
| 1193 | ** First, check we have access to the checkout (and report to the user if we |
| 1194 | ** don't), then refresh the "vfile" table (recording which files in the |
| 1195 | ** checkout have changed etc.). We then change the "name" parameter to "tip" |
| 1196 | ** so that the "header" section displays info about the check-in that the |
| 1197 | ** checkout came from. |
| 1198 | **TODO |
| 1199 | ** It would probably make sense to limit "/local" (and other links that come |
| 1200 | ** from it) to only be permitted when Fossil is running locally in "ui" mode. |
| 1201 | ** It's probably not critical when all you can do is view files in the |
| 1202 | ** checkout (they can already see the checked-in versions), but if a COMMIT |
| 1203 | ** option WERE ever to be implemented, you wouldn't essentially random people |
| 1204 | ** on the internet firing off commits. |
| 1205 | */ |
| 1206 | bLocalMode = (g.zPath[0]=='l') || (fossil_strcmp(zName,"ckout")==0); |
| 1207 | if( bLocalMode ){ |
| 1208 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 1209 | if( vid==0 ){ |
| 1210 | /*TODO Is this the right response? */ |
| 1211 | style_header("No Local Checkout"); |
| 1212 | @ No access to local checkout. |
| 1213 | style_footer(); |
| 1214 | return; |
| 1215 | } |
| @@ -1523,11 +1535,11 @@ | |
| 1523 | if( showExtras ){ |
| 1524 | @ %z(chref("button","%R/local?diff=%d%s&ef=0",diffType,zW))Hide Extras</a> |
| 1525 | }else{ |
| 1526 | @ %z(chref("button","%R/local?diff=%d%s&ef=",diffType,zW))Show Extras</a> |
| 1527 | } |
| 1528 | /*TODO |
| 1529 | ** There would be a fair chunk of stuff to get right (not least appropriate |
| 1530 | ** restrictions), but it MIGHT be nice to have a COMMIT button here... |
| 1531 | */ |
| 1532 | } |
| 1533 | @</div> |
| @@ -1538,11 +1550,11 @@ | |
| 1538 | if( bLocalMode ){ |
| 1539 | if( showExtras ){ |
| 1540 | append_extras_report(zExtra, diffType, zW); |
| 1541 | } |
| 1542 | /* Following SQL taken from diff_against_disk() in diffcmd.c */ |
| 1543 | /*TODO |
| 1544 | ** That code wrapped the query/processing in a transaction (but, from |
| 1545 | ** memory, other similar uses did not). Is it neeeded? |
| 1546 | */ |
| 1547 | db_begin_transaction(); |
| 1548 | db_prepare(&q3, |
| @@ -1561,17 +1573,15 @@ | |
| 1561 | ** side-effect of altering the order entries are shown in (but within each |
| 1562 | ** group the original order is maintained). |
| 1563 | ** |
| 1564 | ** If disabled, (pass gets set to 3), only one pass is made on which all |
| 1565 | ** entries are shown in their "normal" order. |
| 1566 | **TODO |
| 1567 | ** Add this to the original (non-local) loop? |
| 1568 | */ |
| 1569 | //--------------------------------------------------- TODO TESTING HACK TODO |
| 1570 | int bTwoPass = P("op")==NULL; //TODO Taken from a config option? |
| 1571 | //--------------------------------------------------- TODO TESTING HACK TODO |
| 1572 | int pass = bTwoPass?1:3; |
| 1573 | do{ |
| 1574 | while( db_step(&q3)==SQLITE_ROW ){ |
| 1575 | const char *zPathname = db_column_text(&q3,0); |
| 1576 | int isDeleted = db_column_int(&q3, 1); |
| 1577 | int isChnged = db_column_int(&q3,2); |
| @@ -1580,17 +1590,21 @@ | |
| 1580 | int isLink = db_column_int(&q3, 5); |
| 1581 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcid); |
| 1582 | append_local_file_change_line( zPathname, zUuid, |
| 1583 | isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass ); |
| 1584 | free(zUuid); |
| 1585 | } |
| 1586 | db_reset(&q3); |
| 1587 | }while( ++pass < 3 ); /* Either "1, 2, stop" or "3, stop". */ |
| 1588 | db_finalize(&q3); |
| 1589 | db_end_transaction(1); /* ROLLBACK */ |
| 1590 | }else{ /* Normal, non-local-mode: show diffs against parent */ |
| 1591 | /*TODO: Implement the optional two-pass code? */ |
| 1592 | db_prepare(&q3, |
| 1593 | "SELECT name," |
| 1594 | " mperm," |
| 1595 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 1596 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| @@ -1631,11 +1645,11 @@ | |
| 1631 | login_check_credentials(); |
| 1632 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1633 | |
| 1634 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 1635 | if( vid==0 ){ |
| 1636 | /*TODO Is this the right response? */ |
| 1637 | style_header("No Local Checkout"); |
| 1638 | @ No access to local checkout. |
| 1639 | style_footer(); |
| 1640 | return; |
| 1641 | } |
| @@ -1642,11 +1656,11 @@ | |
| 1642 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 1643 | |
| 1644 | cgi_set_content_type("text/plain"); |
| 1645 | |
| 1646 | db_begin_transaction(); |
| 1647 | /*TODO |
| 1648 | ** This query is the same as in ci_page() for local-mode (as well as in |
| 1649 | ** diff_against_disk() in diffcmd.c, where it was originally taken from). |
| 1650 | ** Should they be "coalesced" in some way? |
| 1651 | */ |
| 1652 | db_prepare(&q3, |
| @@ -1975,17 +1989,17 @@ | |
| 1975 | "%R/vdiff?%s&diff=1%s%T%s", |
| 1976 | zQuery, |
| 1977 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1978 | } |
| 1979 | if( zBranch==0 ){ |
| 1980 | //TODO Is there an extra "&" here? |
| 1981 | style_submenu_element("Invert", |
| 1982 | "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, |
| 1983 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1984 | } |
| 1985 | if( zGlob ){ |
| 1986 | //TODO Is there an extra "&" here? |
| 1987 | style_submenu_element("Clear glob", |
| 1988 | "%R/vdiff?%s&%s", zQuery, zW); |
| 1989 | }else{ |
| 1990 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); |
| 1991 | } |
| @@ -2739,11 +2753,11 @@ | |
| 2739 | style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); |
| 2740 | } |
| 2741 | } |
| 2742 | } |
| 2743 | style_header("Hex Artifact Content"); |
| 2744 | /* TODO |
| 2745 | ** Could the call to style_header() be moved so these two exclusion |
| 2746 | ** blocks could be merged? I don't think any of them make sense for |
| 2747 | ** a local file. |
| 2748 | */ |
| 2749 | if( !bLocalMode ){ |
| @@ -2770,11 +2784,11 @@ | |
| 2770 | }else{ |
| 2771 | content_get(rid, &content); |
| 2772 | } |
| 2773 | @ <blockquote><pre> |
| 2774 | hexdump(&content); |
| 2775 | /* TODO: Should content (and downloadName?) be reset/freed? */ |
| 2776 | @ </pre></blockquote> |
| 2777 | style_footer(); |
| 2778 | } |
| 2779 | |
| 2780 | /* |
| @@ -3022,10 +3036,11 @@ | |
| 3022 | ** For /file, a name= without a ci= while prefer to use the default |
| 3023 | ** "tip" value for ci=. */ |
| 3024 | rid = name_to_rid(zName); |
| 3025 | } |
| 3026 | if( fossil_strcmp(zCI,"ckout")==0 ){ |
| 3027 | bLocalMode = 1; |
| 3028 | rid = -1; /* Dummy value to make it look found */ |
| 3029 | }else if( rid==0 ){ |
| 3030 | rid = artifact_from_ci_and_filename(0); |
| 3031 | } |
| @@ -3062,11 +3077,11 @@ | |
| 3062 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 3063 | |
| 3064 | asText = P("txt")!=0; |
| 3065 | if( isFile ){ |
| 3066 | if( bLocalMode ){ |
| 3067 | /*TODO |
| 3068 | ** Is this the best way of handling annotations to the description? |
| 3069 | ** If "annot=message" is part of the URL, the message is appended |
| 3070 | ** to the description of the file. Only used for "local" files to |
| 3071 | ** distinguish such files from part of the repository. |
| 3072 | */ |
| @@ -3212,19 +3227,13 @@ | |
| 3212 | if( descOnly ){ |
| 3213 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 3214 | }else{ |
| 3215 | @ <hr /> |
| 3216 | if( bLocalMode ){ |
| 3217 | /*TODO |
| 3218 | ** Should we handle non-existent local files differently? Currently, |
| 3219 | ** they are shown the same as if the file was present but empty. This |
| 3220 | ** should never happen through "normal" operation, but someone might |
| 3221 | ** craft a link to one. Perhaps have content_from_file() perform an |
| 3222 | ** existence-check (rather than relying on blob_read_from_file() which |
| 3223 | ** it calls returning an empty blob)? |
| 3224 | */ |
| 3225 | content_from_file(zName, &content); |
| 3226 | }else{ |
| 3227 | content_get(rid, &content); |
| 3228 | } |
| 3229 | if( renderAsWiki ){ |
| 3230 | wiki_render_by_mimetype(&content, zMime); |
| 3231 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1,21 +1,5 @@ | |
| 1 | /* |
| 2 | ** Copyright (c) 2007 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| @@ -36,10 +20,54 @@ | |
| 20 | ** the current tree, or a particular artifact or check-in. |
| 21 | */ |
| 22 | #include "config.h" |
| 23 | #include "info.h" |
| 24 | #include <assert.h> |
| 25 | |
| 26 | /*----------------------------------------------------------------------------- |
| 27 | ** LOCAL DIFF CHANGES |
| 28 | ** |
| 29 | ** The goal is to show "(working) changes in the current checkout" in much the |
| 30 | ** same way as the current "check-in" pages show the changes within a check-in. |
| 31 | ** |
| 32 | ** The page is on "/local" (no parameters needed; an alias of "/vinfo" and |
| 33 | ** "/ci"), but at present there are no links to it within Fossil. Other changes: |
| 34 | ** "/localpatch" produces a patch-set for all changes in the current checkout; |
| 35 | ** "/localdiff" is an alias for "/fdiff" to show changes for an individual file; |
| 36 | ** both "/file" and "/hexdump" have been extended to support local files. |
| 37 | ** |
| 38 | ** A number of points-of-query are labeld "TODO:LD". Most relate to these |
| 39 | ** changes, although some are about existing code. |
| 40 | ** |
| 41 | ** In "checkin.c", the function "locate_unmanaged_files()" has been made |
| 42 | ** non-static so that it can be called from here. |
| 43 | **----------------------------------------------------------------------------- |
| 44 | ** Options to control the "local changes" changes. At present, these are |
| 45 | ** defines: if these changes are adopted, some may want to be made into |
| 46 | ** configuration options. |
| 47 | ** |
| 48 | ** INTEGER: Controls how many unmanaged files will be shown before the "plus xxx |
| 49 | ** other matching files." line is shown (with an option to view all of them).*/ |
| 50 | #define LOCAL_DIFF_MAX_EXTRAS (5) |
| 51 | /* |
| 52 | ** STRING: Controls whether the "extras" report is initially shown or hidden. A |
| 53 | ** value of "0" hides the report; a value of "" (an empty string) will show it. |
| 54 | */ |
| 55 | #define LOCAL_DIFF_EXTRAS_MODE ("") |
| 56 | /* |
| 57 | ** BOOLEAN: Controls whether one or two passes are made through the list of |
| 58 | ** changed files. In two-pass mode, all single-line differences are displayed |
| 59 | ** ahead of all differences involving "diff-blocks", making them less likely to |
| 60 | ** be overlooked. If disabled, only one pass is made, listing all changes in the |
| 61 | ** order found. Possible TODO: Do the same for "normal" diffs. */ |
| 62 | #define LOCAL_DIFF_USE_TWO_PASSES (1) |
| 63 | /* |
| 64 | ** BOOLEAN: Controls whether dividers ("<hr/>") added after any "diff-blocks" |
| 65 | ** (except the last one)... IMHO doing so makes it easier to see where one block |
| 66 | ** ends and the next starts. Possible TODO: Do the same for "normal" diffs. */ |
| 67 | #define LOCAL_DIFF_ADD_DIVIDER (1) |
| 68 | /*---------------------------------------------------------------------------*/ |
| 69 | |
| 70 | /* |
| 71 | ** Return a string (in memory obtained from malloc) holding a |
| 72 | ** comma-separated list of tags that apply to check-in with |
| 73 | ** record-id rid. If the "propagatingOnly" flag is true, then only |
| @@ -339,24 +367,25 @@ | |
| 367 | 0, 0, 0, rid, rid2, 0); |
| 368 | db_finalize(&q); |
| 369 | } |
| 370 | |
| 371 | /* |
| 372 | ** Read the content of file zName (prepended with the checkout directory) and |
| 373 | ** put it into the uninitialized blob, returning 1. The blob is zeroed if the |
| 374 | ** file does not exist or cannot be accessed, in which case it returns 0. |
| 375 | */ |
| 376 | static int content_from_file( |
| 377 | const char *zName, /* Filename (relative to checkout) of file to be read */ |
| 378 | Blob *pBlob /* Pointer to blob to receive contents */ |
| 379 | ){ |
| 380 | const char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 381 | blob_zero(pBlob); |
| 382 | if( file_size(zFullPath, ExtFILE)<0 ){ |
| 383 | return 0; |
| 384 | } |
| 385 | blob_read_from_file(pBlob, zFullPath, ExtFILE); |
| 386 | return 1; |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 | ** Append the difference between artifacts to the output |
| 391 | ** If zLocal is not NULL, instead compare against the local |
| @@ -514,15 +543,14 @@ | |
| 543 | ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */ |
| 544 | int pass /* 0x01 - Display single-line entries only */ |
| 545 | /* 0x02 - Display entries with "diff blocks" only */ |
| 546 | /* 0x03 - Display both */ |
| 547 | ){ |
| 548 | #if LOCAL_DIFF_ADD_DIVIDER |
| 549 | /* This remembers whether a side-by-side "diff-block" was shown the last |
| 550 | ** time through. If it was, we will add "<hr/>" to better separate the |
| 551 | ** blocks. |
| 552 | */ |
| 553 | static int diffShownLastTime = 0; |
| 554 | #endif |
| 555 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zName); |
| 556 | int isFilePresent = !file_access(zFullName, F_OK); |
| @@ -541,31 +569,25 @@ | |
| 569 | /* We don't use 'diffFlags' in these tests so that whether "Hide diffs" is |
| 570 | ** in effect or not, the order won't change. |
| 571 | */ |
| 572 | if( showDiff && (pass == 1) ){ return; } /* Don't do diff on pass 1 of 2 */ |
| 573 | if( !showDiff && (pass == 2) ){ return; } /* Don't do line on pass 2 of 2 */ |
| 574 | #if LOCAL_DIFF_ADD_DIVIDER |
| 575 | /* If a SBS diff-block was shown by the previous entry, add a divider */ |
| 576 | if( diffShownLastTime && (diffFlags & DIFF_SIDEBYSIDE) ){ |
| 577 | @ <hr/> |
| 578 | } |
| 579 | /* Record whether we will be showing a diff-block this time. We DO factor in |
| 580 | ** 'diffFlags' here so that in "Hide diffs" mode, we don't get extra lines. |
| 581 | */ |
| 582 | diffShownLastTime = showDiff && diffFlags; |
| 583 | #endif |
| 584 | @ <p> |
| 585 | if( !g.perm.Hyperlink ){ |
| 586 | if( isDeleted ){ |
| 587 | if( isFilePresent ){ |
| 588 | @ Deleted %h(zName) (still present as a local file). |
| 589 | }else{ |
| 590 | @ Deleted %h(zName). |
| 591 | } |
| 592 | }else if( isNew ){ |
| 593 | if( isFilePresent ){ |
| @@ -572,15 +594,10 @@ | |
| 594 | @ Added %h(zName) but not committed. |
| 595 | }else{ |
| 596 | @ Missing %h(zName) (was added to checkout). |
| 597 | } |
| 598 | }else switch( isChnged ){ |
| 599 | case 3: |
| 600 | @ Added %h(zName) due to a merge. |
| 601 | break; |
| 602 | case 5: |
| 603 | @ Added %h(zName) due to an integrate-merge. |
| @@ -601,11 +618,10 @@ | |
| 618 | case 4: |
| 619 | @ Integrate-merge |
| 620 | break; |
| 621 | } |
| 622 | @ of %h(zName). |
| 623 | } |
| 624 | if( showDiff && diffFlags ){ |
| 625 | append_diff(zOld, NULL, zName, diffFlags, pRe); |
| 626 | } |
| 627 | }else{ /* With hyperlinks */ |
| @@ -613,11 +629,10 @@ | |
| 629 | if( isFilePresent ){ /* DELETEd but still on disk */ |
| 630 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 631 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> (still present |
| 632 | @ as %z(href("%R/file/%T?ci=ckout&annot=removed from checkout",zName)) |
| 633 | @ [local file]</a>). |
| 634 | }else{ /* DELETEd and deleted from disk */ |
| 635 | @ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 636 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>. |
| 637 | } |
| 638 | }else if( isNew ){ |
| @@ -626,11 +641,10 @@ | |
| 641 | @ but not committed. |
| 642 | }else{ /* ADDed but not present on disk */ |
| 643 | @ Missing %h(zName) (was added to checkout). |
| 644 | } |
| 645 | }else switch( isChnged ){ |
| 646 | case 3: /* Added by a merge */ |
| 647 | @ Added |
| 648 | @ %z(href("%R/file/%T?ci=ckout&annot=added by merge",zName))%h(zName) |
| 649 | @ </a> to %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> due to merge. |
| 650 | break; |
| @@ -659,11 +673,10 @@ | |
| 673 | } |
| 674 | @ of %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a> |
| 675 | @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> to |
| 676 | @ %z(href("%R/file/%T?ci=ckout&annot=edited locally",zName)) |
| 677 | @ [local file]</a> |
| 678 | } |
| 679 | if( showDiff ){ |
| 680 | if( diffFlags ){ |
| 681 | append_diff(zOld, NULL, zName, diffFlags, pRe); |
| 682 | }else if( isChnged ){ |
| @@ -886,22 +899,22 @@ | |
| 899 | pIgnore = glob_create(zIgnoreFlag); /* Object versions of above */ |
| 900 | pKeep = glob_create(zKeepFlag); |
| 901 | pClean = glob_create(zCleanFlag); |
| 902 | nRoot = (int)strlen(g.zLocalRoot); /* Length of root component */ |
| 903 | Stmt q; |
| 904 | Blob repo; /* TODO:LD May not be needed */ |
| 905 | int maxExtrasToShow = LOCAL_DIFF_MAX_EXTRAS; |
| 906 | int extrasFlags = atoi(zExtra); /* Which entries to show */ |
| 907 | int nExtras; |
| 908 | int nMatch; |
| 909 | int nShown; |
| 910 | int nPlain; |
| 911 | int nIgnore; |
| 912 | int nClean; |
| 913 | int nKeep; |
| 914 | |
| 915 | /*TODO:LD? |
| 916 | ** It feels sensible to limit the number of "extra" entries shown by default |
| 917 | ** for cases where "ignore-glob" or "clean-glob" haven't been fully setup. |
| 918 | ** A minor irritation is that this can lead to "... plus 1 more file", on a |
| 919 | ** line that COULD have been used to display the omitted file. If we knew in |
| 920 | ** advance how many entries were going to match, we could temporarily "bump" |
| @@ -911,56 +924,56 @@ | |
| 924 | ** possibly bump the limit, then re-scan the table repeating the tests |
| 925 | ** against each glob-list to decide which to show. |
| 926 | ** b) SFILE could have an extra FLAGS field: during the pre-scan, this could |
| 927 | ** be updated to indicate which groups each file belong to. This would |
| 928 | ** save re-testing every file against each glob-list (the main pass could |
| 929 | ** select "WHERE flags & selector" to get only the matching entries), but |
| 930 | ** the updates (selecting by "pathname" each time) could be a bit much. |
| 931 | ** c) vfile_scan() -- where SFILE is populated -- COULD have an option to |
| 932 | ** do the testing at the time entries are added. This would be the "best" |
| 933 | ** way, but feels too much disruption to other code for what is only a |
| 934 | ** minor benefit. |
| 935 | ** For now, I'll stick with the minor annoyance of "plus 1 more file" :-) |
| 936 | ** |
| 937 | ** Being able to determine the counts up-front would also allow us to hide |
| 938 | ** the whole "extras report" if there were no unmanaged files. |
| 939 | ** |
| 940 | **TODO:LD? |
| 941 | ** Does it make sense (and/or is it practiable) to offer an "ADD" button |
| 942 | ** against files that are unmanaged? |
| 943 | ** |
| 944 | **TODO:LD? |
| 945 | ** Does it make sense (and/or ...) to offer ediing of the various blob-lists |
| 946 | ** from the Extras report? Showing the existing configuration screen would |
| 947 | ** probably not be a problem (permissions permitting), but what happens if |
| 948 | ** those settings have been overriden by .fossil-settings/ignore-glob? As we |
| 949 | ** have access to the local checkout, is it feasible to edit it in the browser |
| 950 | ** (perhaps piggy-backing /fileedit)? |
| 951 | */ |
| 952 | |
| 953 | locate_unmanaged_files(0, NULL, 0, NULL); /* Get all unmanaged */ |
| 954 | /*TODO:LD |
| 955 | ** The first two of these exclusions come from clean_cmd() in checkin.c. |
| 956 | ** Not sure exactly what they are intended to do (seem to have no effect on |
| 957 | ** my test repos). Last exclusion is an alternative to the WHERE clause above |
| 958 | ** so that COUNT(*) returns the correct value. TODO Even though, as noted |
| 959 | ** above, getting the count ahead of time is of little use (it was used to |
| 960 | ** bump the display limit if only one entry would be omitted), I'll probably |
| 961 | ** retain omitting the WHERE, and using DELETE FROM to exclude reserved |
| 962 | ** names, just in case (c) above were to be implemented. |
| 963 | */ |
| 964 | /*TODO:LD deletions from clean_cmd() */ |
| 965 | if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){ |
| 966 | db_multi_exec("DELETE FROM sfile WHERE pathname=%B", &repo); |
| 967 | } |
| 968 | db_multi_exec("DELETE FROM sfile WHERE pathname IN" |
| 969 | " (SELECT pathname FROM vfile)"); |
| 970 | /*TODO:LD Delete reserved names, rather than WHERE them out. */ |
| 971 | db_multi_exec("DELETE FROM sfile WHERE pathname IN (%s)", |
| 972 | fossil_all_reserved_names(0)); |
| 973 | |
| 974 | /*TODO:LD |
| 975 | ** If we had a count of matching entries before scanning, this is where |
| 976 | ** we'd bump the maximum to show so as to avoid "plus 1 file". |
| 977 | ** ... |
| 978 | ** If there's only one more than the maximum, let it through... |
| 979 | ** a line used to say "plus 1 more" may as well display that item! |
| @@ -969,12 +982,11 @@ | |
| 982 | */ |
| 983 | |
| 984 | /* Handle the special case where zExtra was empty (and got converted to zero). |
| 985 | ** If so, show "plain" files (those not matching any glob-list) but with an |
| 986 | ** upper limit to the number shown (set above). If a value WAS given (i.e. |
| 987 | ** after following a link), display all of the selected entries. */ |
| 988 | if( extrasFlags==0 ){ |
| 989 | extrasFlags = EX_PLAIN; |
| 990 | }else{ |
| 991 | maxExtrasToShow = 0x7fffffff; /* Basically, all of them... */ |
| 992 | } |
| @@ -997,26 +1009,26 @@ | |
| 1009 | blob_zero(&desc); |
| 1010 | if( extrasFlags & EX_PLAIN ){ blob_appendf(&desc, " + unmanaged" ); } |
| 1011 | if( extrasFlags & EX_IGNORE ){ blob_appendf(&desc, " + ignored" ); } |
| 1012 | if( extrasFlags & EX_CLEAN ){ blob_appendf(&desc, " + to be cleaned"); } |
| 1013 | if( extrasFlags & EX_KEEP ){ blob_appendf(&desc, " + to be kept" ); } |
| 1014 | if( blob_size(&desc) > 3 ){ /* Should never fail... */ |
| 1015 | /* Add the string built above, skipping the leading " + " */ |
| 1016 | @ (%h(blob_str(&desc)+3)) |
| 1017 | } |
| 1018 | blob_reset(&desc); |
| 1019 | } |
| 1020 | @ </b></p> |
| 1021 | |
| 1022 | db_prepare(&q, |
| 1023 | "SELECT %Q || pathname FROM sfile" |
| 1024 | " ORDER BY 1", /*TODO:LD Order by pathname, as for differences? */ |
| 1025 | g.zLocalRoot |
| 1026 | ); |
| 1027 | /* |
| 1028 | ** Put the file-list in one paragraph with line-breaks between. |
| 1029 | **TODO:LD |
| 1030 | ** Might a table (with columns for name, ignore/clean/keep) work? |
| 1031 | */ |
| 1032 | @ <p> |
| 1033 | nExtras = nMatch = nShown = nPlain = nKeep = nClean = nIgnore = 0; |
| 1034 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1183,33 +1195,33 @@ | |
| 1195 | ** If the "ef=" isn't present (as when first navigating to "/local") then a |
| 1196 | ** default setting is used. Set to "0" to initially hide the report. |
| 1197 | */ |
| 1198 | zExtra = P("ef"); |
| 1199 | if( zExtra==NULL ) { |
| 1200 | zExtra = LOCAL_DIFF_EXTRAS_MODE; |
| 1201 | } |
| 1202 | showExtras = strcmp(zExtra,"0")!=0; |
| 1203 | |
| 1204 | /* Local mode is selected by either "/local" or with a "name" of "ckout". |
| 1205 | ** First, check we have access to the checkout (and report to the user if we |
| 1206 | ** don't), then refresh the "vfile" table (recording which files in the |
| 1207 | ** checkout have changed etc.). We then change the "name" parameter to "tip" |
| 1208 | ** so that the "header" section displays info about the check-in that the |
| 1209 | ** checkout came from. |
| 1210 | **TODO:LD |
| 1211 | ** It would probably make sense to limit "/local" (and other links that come |
| 1212 | ** from it) to only be permitted when Fossil is running locally in "ui" mode. |
| 1213 | ** It's probably not critical when all you can do is view files in the |
| 1214 | ** checkout (they can already see the checked-in versions), but if a COMMIT |
| 1215 | ** option WERE ever to be implemented, you wouldn't essentially random people |
| 1216 | ** on the internet firing off commits! |
| 1217 | */ |
| 1218 | bLocalMode = (g.zPath[0]=='l') || (fossil_strcmp(zName,"ckout")==0); |
| 1219 | if( bLocalMode ){ |
| 1220 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 1221 | if( vid==0 ){ |
| 1222 | /*TODO:LD Is this the right response? */ |
| 1223 | style_header("No Local Checkout"); |
| 1224 | @ No access to local checkout. |
| 1225 | style_footer(); |
| 1226 | return; |
| 1227 | } |
| @@ -1523,11 +1535,11 @@ | |
| 1535 | if( showExtras ){ |
| 1536 | @ %z(chref("button","%R/local?diff=%d%s&ef=0",diffType,zW))Hide Extras</a> |
| 1537 | }else{ |
| 1538 | @ %z(chref("button","%R/local?diff=%d%s&ef=",diffType,zW))Show Extras</a> |
| 1539 | } |
| 1540 | /*TODO:LD |
| 1541 | ** There would be a fair chunk of stuff to get right (not least appropriate |
| 1542 | ** restrictions), but it MIGHT be nice to have a COMMIT button here... |
| 1543 | */ |
| 1544 | } |
| 1545 | @</div> |
| @@ -1538,11 +1550,11 @@ | |
| 1550 | if( bLocalMode ){ |
| 1551 | if( showExtras ){ |
| 1552 | append_extras_report(zExtra, diffType, zW); |
| 1553 | } |
| 1554 | /* Following SQL taken from diff_against_disk() in diffcmd.c */ |
| 1555 | /*TODO:LD |
| 1556 | ** That code wrapped the query/processing in a transaction (but, from |
| 1557 | ** memory, other similar uses did not). Is it neeeded? |
| 1558 | */ |
| 1559 | db_begin_transaction(); |
| 1560 | db_prepare(&q3, |
| @@ -1561,17 +1573,15 @@ | |
| 1573 | ** side-effect of altering the order entries are shown in (but within each |
| 1574 | ** group the original order is maintained). |
| 1575 | ** |
| 1576 | ** If disabled, (pass gets set to 3), only one pass is made on which all |
| 1577 | ** entries are shown in their "normal" order. |
| 1578 | **TODO:LD |
| 1579 | ** Add this to the original (non-local) loop? |
| 1580 | */ |
| 1581 | int pass = LOCAL_DIFF_USE_TWO_PASSES?1:3; |
| 1582 | int anyDifferences = 0; |
| 1583 | do{ |
| 1584 | while( db_step(&q3)==SQLITE_ROW ){ |
| 1585 | const char *zPathname = db_column_text(&q3,0); |
| 1586 | int isDeleted = db_column_int(&q3, 1); |
| 1587 | int isChnged = db_column_int(&q3,2); |
| @@ -1580,17 +1590,21 @@ | |
| 1590 | int isLink = db_column_int(&q3, 5); |
| 1591 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcid); |
| 1592 | append_local_file_change_line( zPathname, zUuid, |
| 1593 | isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass ); |
| 1594 | free(zUuid); |
| 1595 | anyDifferences = 1; |
| 1596 | } |
| 1597 | db_reset(&q3); |
| 1598 | }while( ++pass < 3 ); /* Either "1, 2, stop" or "3, stop". */ |
| 1599 | if( !anyDifferences ){ |
| 1600 | @ <p>No changes in the local checkout.</p> |
| 1601 | } |
| 1602 | db_finalize(&q3); |
| 1603 | db_end_transaction(1); /* ROLLBACK */ |
| 1604 | }else{ /* Normal, non-local-mode: show diffs against parent */ |
| 1605 | /*TODO:LD: Implement the optional two-pass code? */ |
| 1606 | db_prepare(&q3, |
| 1607 | "SELECT name," |
| 1608 | " mperm," |
| 1609 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 1610 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| @@ -1631,11 +1645,11 @@ | |
| 1645 | login_check_credentials(); |
| 1646 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1647 | |
| 1648 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 1649 | if( vid==0 ){ |
| 1650 | /*TODO:LD Is this the right response? */ |
| 1651 | style_header("No Local Checkout"); |
| 1652 | @ No access to local checkout. |
| 1653 | style_footer(); |
| 1654 | return; |
| 1655 | } |
| @@ -1642,11 +1656,11 @@ | |
| 1656 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 1657 | |
| 1658 | cgi_set_content_type("text/plain"); |
| 1659 | |
| 1660 | db_begin_transaction(); |
| 1661 | /*TODO:LD |
| 1662 | ** This query is the same as in ci_page() for local-mode (as well as in |
| 1663 | ** diff_against_disk() in diffcmd.c, where it was originally taken from). |
| 1664 | ** Should they be "coalesced" in some way? |
| 1665 | */ |
| 1666 | db_prepare(&q3, |
| @@ -1975,17 +1989,17 @@ | |
| 1989 | "%R/vdiff?%s&diff=1%s%T%s", |
| 1990 | zQuery, |
| 1991 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1992 | } |
| 1993 | if( zBranch==0 ){ |
| 1994 | //TODO:LD Is there an extra "&" here? |
| 1995 | style_submenu_element("Invert", |
| 1996 | "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, |
| 1997 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1998 | } |
| 1999 | if( zGlob ){ |
| 2000 | //TODO:LD Is there an extra "&" here? |
| 2001 | style_submenu_element("Clear glob", |
| 2002 | "%R/vdiff?%s&%s", zQuery, zW); |
| 2003 | }else{ |
| 2004 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); |
| 2005 | } |
| @@ -2739,11 +2753,11 @@ | |
| 2753 | style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); |
| 2754 | } |
| 2755 | } |
| 2756 | } |
| 2757 | style_header("Hex Artifact Content"); |
| 2758 | /*TODO:LD |
| 2759 | ** Could the call to style_header() be moved so these two exclusion |
| 2760 | ** blocks could be merged? I don't think any of them make sense for |
| 2761 | ** a local file. |
| 2762 | */ |
| 2763 | if( !bLocalMode ){ |
| @@ -2770,11 +2784,11 @@ | |
| 2784 | }else{ |
| 2785 | content_get(rid, &content); |
| 2786 | } |
| 2787 | @ <blockquote><pre> |
| 2788 | hexdump(&content); |
| 2789 | /* TODO:LD: Should content (and downloadName?) be reset/freed? */ |
| 2790 | @ </pre></blockquote> |
| 2791 | style_footer(); |
| 2792 | } |
| 2793 | |
| 2794 | /* |
| @@ -3022,10 +3036,11 @@ | |
| 3036 | ** For /file, a name= without a ci= while prefer to use the default |
| 3037 | ** "tip" value for ci=. */ |
| 3038 | rid = name_to_rid(zName); |
| 3039 | } |
| 3040 | if( fossil_strcmp(zCI,"ckout")==0 ){ |
| 3041 | /* "ci=ckout" is an extension for viewing files in the local checkout. */ |
| 3042 | bLocalMode = 1; |
| 3043 | rid = -1; /* Dummy value to make it look found */ |
| 3044 | }else if( rid==0 ){ |
| 3045 | rid = artifact_from_ci_and_filename(0); |
| 3046 | } |
| @@ -3062,11 +3077,11 @@ | |
| 3077 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 3078 | |
| 3079 | asText = P("txt")!=0; |
| 3080 | if( isFile ){ |
| 3081 | if( bLocalMode ){ |
| 3082 | /*TODO:LD |
| 3083 | ** Is this the best way of handling annotations to the description? |
| 3084 | ** If "annot=message" is part of the URL, the message is appended |
| 3085 | ** to the description of the file. Only used for "local" files to |
| 3086 | ** distinguish such files from part of the repository. |
| 3087 | */ |
| @@ -3212,19 +3227,13 @@ | |
| 3227 | if( descOnly ){ |
| 3228 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 3229 | }else{ |
| 3230 | @ <hr /> |
| 3231 | if( bLocalMode ){ |
| 3232 | if( !content_from_file(zName, &content) ){ |
| 3233 | fossil_warning("Cannot find/access %s.", zName); |
| 3234 | } |
| 3235 | }else{ |
| 3236 | content_get(rid, &content); |
| 3237 | } |
| 3238 | if( renderAsWiki ){ |
| 3239 | wiki_render_by_mimetype(&content, zMime); |
| 3240 |