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.

graham 2020-06-02 16:20 ui-local-diff
Commit a955c80b43c358fb819487b330a395a6c690f9931b6c10a0d4828b88e941d828
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
-*/
171
/*
182
** Copyright (c) 2007 D. Richard Hipp
193
**
204
** This program is free software; you can redistribute it and/or
215
** modify it under the terms of the Simplified BSD License (also
@@ -36,10 +20,54 @@
3620
** the current tree, or a particular artifact or check-in.
3721
*/
3822
#include "config.h"
3923
#include "info.h"
4024
#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
+/*---------------------------------------------------------------------------*/
4169
4270
/*
4371
** Return a string (in memory obtained from malloc) holding a
4472
** comma-separated list of tags that apply to check-in with
4573
** record-id rid. If the "propagatingOnly" flag is true, then only
@@ -339,24 +367,25 @@
339367
0, 0, 0, rid, rid2, 0);
340368
db_finalize(&q);
341369
}
342370
343371
/*
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.
348375
*/
349
-static void content_from_file(
376
+static int content_from_file(
350377
const char *zName, /* Filename (relative to checkout) of file to be read */
351378
Blob *pBlob /* Pointer to blob to receive contents */
352379
){
353380
const char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
354381
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;
357384
}
385
+ blob_read_from_file(pBlob, zFullPath, ExtFILE);
386
+ return 1;
358387
}
359388
360389
/*
361390
** Append the difference between artifacts to the output
362391
** If zLocal is not NULL, instead compare against the local
@@ -514,15 +543,14 @@
514543
ReCompiled *pRe, /* Only show diffs that match this regex, if not NULL */
515544
int pass /* 0x01 - Display single-line entries only */
516545
/* 0x02 - Display entries with "diff blocks" only */
517546
/* 0x03 - Display both */
518547
){
519
-#ifndef GLH_NO_DIVIDER
548
+#if LOCAL_DIFF_ADD_DIVIDER
520549
/* This remembers whether a side-by-side "diff-block" was shown the last
521550
** 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.
524552
*/
525553
static int diffShownLastTime = 0;
526554
#endif
527555
char *zFullName = mprintf("%s%s", g.zLocalRoot, zName);
528556
int isFilePresent = !file_access(zFullName, F_OK);
@@ -541,31 +569,25 @@
541569
/* We don't use 'diffFlags' in these tests so that whether "Hide diffs" is
542570
** in effect or not, the order won't change.
543571
*/
544572
if( showDiff && (pass == 1) ){ return; } /* Don't do diff on pass 1 of 2 */
545573
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
547575
/* If a SBS diff-block was shown by the previous entry, add a divider */
548576
if( diffShownLastTime && (diffFlags & DIFF_SIDEBYSIDE) ){
549577
@ <hr/>
550578
}
551579
/* Record whether we will be showing a diff-block this time. We DO factor in
552580
** 'diffFlags' here so that in "Hide diffs" mode, we don't get extra lines.
553581
*/
554582
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
561584
@ <p>
562585
if( !g.perm.Hyperlink ){
563586
if( isDeleted ){
564587
if( isFilePresent ){
565588
@ Deleted %h(zName) (still present as a local file).
566
- //TODO:Remove? showDiff = 1;
567589
}else{
568590
@ Deleted %h(zName).
569591
}
570592
}else if( isNew ){
571593
if( isFilePresent ){
@@ -572,15 +594,10 @@
572594
@ Added %h(zName) but not committed.
573595
}else{
574596
@ Missing %h(zName) (was added to checkout).
575597
}
576598
}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
- */
582599
case 3:
583600
@ Added %h(zName) due to a merge.
584601
break;
585602
case 5:
586603
@ Added %h(zName) due to an integrate-merge.
@@ -601,11 +618,10 @@
601618
case 4:
602619
@ Integrate-merge
603620
break;
604621
}
605622
@ of %h(zName).
606
- //TODO:Remove? showDiff = 1;
607623
}
608624
if( showDiff && diffFlags ){
609625
append_diff(zOld, NULL, zName, diffFlags, pRe);
610626
}
611627
}else{ /* With hyperlinks */
@@ -613,11 +629,10 @@
613629
if( isFilePresent ){ /* DELETEd but still on disk */
614630
@ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
615631
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> (still present
616632
@ as %z(href("%R/file/%T?ci=ckout&annot=removed from checkout",zName))
617633
@ [local file]</a>).
618
- //TODO:Remove? showDiff = 1;
619634
}else{ /* DELETEd and deleted from disk */
620635
@ Deleted %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
621636
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
622637
}
623638
}else if( isNew ){
@@ -626,11 +641,10 @@
626641
@ but not committed.
627642
}else{ /* ADDed but not present on disk */
628643
@ Missing %h(zName) (was added to checkout).
629644
}
630645
}else switch( isChnged ){
631
- /*TODO Not fully tested... see see no-hyperlink version above */
632646
case 3: /* Added by a merge */
633647
@ Added
634648
@ %z(href("%R/file/%T?ci=ckout&annot=added by merge",zName))%h(zName)
635649
@ </a> to %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> due to merge.
636650
break;
@@ -659,11 +673,10 @@
659673
}
660674
@ of %z(href("%R/finfo?name=%T&m=%!S",zName,zOld))%h(zName)</a>
661675
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> to
662676
@ %z(href("%R/file/%T?ci=ckout&annot=edited locally",zName))
663677
@ [local file]</a>
664
- //TODO:Remove? showDiff = 1;
665678
}
666679
if( showDiff ){
667680
if( diffFlags ){
668681
append_diff(zOld, NULL, zName, diffFlags, pRe);
669682
}else if( isChnged ){
@@ -886,22 +899,22 @@
886899
pIgnore = glob_create(zIgnoreFlag); /* Object versions of above */
887900
pKeep = glob_create(zKeepFlag);
888901
pClean = glob_create(zCleanFlag);
889902
nRoot = (int)strlen(g.zLocalRoot); /* Length of root component */
890903
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;
893906
int extrasFlags = atoi(zExtra); /* Which entries to show */
894907
int nExtras;
895908
int nMatch;
896909
int nShown;
897910
int nPlain;
898911
int nIgnore;
899912
int nClean;
900913
int nKeep;
901914
902
- /*TODO?
915
+ /*TODO:LD?
903916
** It feels sensible to limit the number of "extra" entries shown by default
904917
** for cases where "ignore-glob" or "clean-glob" haven't been fully setup.
905918
** A minor irritation is that this can lead to "... plus 1 more file", on a
906919
** line that COULD have been used to display the omitted file. If we knew in
907920
** advance how many entries were going to match, we could temporarily "bump"
@@ -911,56 +924,56 @@
911924
** possibly bump the limit, then re-scan the table repeating the tests
912925
** against each glob-list to decide which to show.
913926
** b) SFILE could have an extra FLAGS field: during the pre-scan, this could
914927
** be updated to indicate which groups each file belong to. This would
915928
** 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
917930
** the updates (selecting by "pathname" each time) could be a bit much.
918931
** c) vfile_scan() -- where SFILE is populated -- COULD have an option to
919932
** do the testing at the time entries are added. This would be the "best"
920933
** way, but feels too much disruption to other code for what is only a
921934
** minor benefit.
922935
** For now, I'll stick with the minor annoyance of "plus 1 more file" :-)
923936
**
924937
** 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.
926939
**
927
- **TODO?
940
+ **TODO:LD?
928941
** Does it make sense (and/or is it practiable) to offer an "ADD" button
929942
** against files that are unmanaged?
930943
**
931
- **TODO?
944
+ **TODO:LD?
932945
** Does it make sense (and/or ...) to offer ediing of the various blob-lists
933946
** from the Extras report? Showing the existing configuration screen would
934947
** probably not be a problem (permissions permitting), but what happens if
935948
** those settings have been overriden by .fossil-settings/ignore-glob? As we
936949
** have access to the local checkout, is it feasible to edit it in the browser
937950
** (perhaps piggy-backing /fileedit)?
938951
*/
939952
940953
locate_unmanaged_files(0, NULL, 0, NULL); /* Get all unmanaged */
941
- /*TODO
954
+ /*TODO:LD
942955
** The first two of these exclusions come from clean_cmd() in checkin.c.
943956
** Not sure exactly what they are intended to do (seem to have no effect on
944957
** my test repos). Last exclusion is an alternative to the WHERE clause above
945958
** so that COUNT(*) returns the correct value. TODO Even though, as noted
946959
** above, getting the count ahead of time is of little use (it was used to
947960
** bump the display limit if only one entry would be omitted), I'll probably
948961
** 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.
950963
*/
951
- /*TODO deletions from clean_cmd() */
964
+ /*TODO:LD deletions from clean_cmd() */
952965
if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){
953966
db_multi_exec("DELETE FROM sfile WHERE pathname=%B", &repo);
954967
}
955968
db_multi_exec("DELETE FROM sfile WHERE pathname IN"
956969
" (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. */
958971
db_multi_exec("DELETE FROM sfile WHERE pathname IN (%s)",
959972
fossil_all_reserved_names(0));
960973
961
- /*TODO
974
+ /*TODO:LD
962975
** If we had a count of matching entries before scanning, this is where
963976
** we'd bump the maximum to show so as to avoid "plus 1 file".
964977
** ...
965978
** If there's only one more than the maximum, let it through...
966979
** a line used to say "plus 1 more" may as well display that item!
@@ -969,12 +982,11 @@
969982
*/
970983
971984
/* Handle the special case where zExtra was empty (and got converted to zero).
972985
** If so, show "plain" files (those not matching any glob-list) but with an
973986
** 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. */
976988
if( extrasFlags==0 ){
977989
extrasFlags = EX_PLAIN;
978990
}else{
979991
maxExtrasToShow = 0x7fffffff; /* Basically, all of them... */
980992
}
@@ -997,26 +1009,26 @@
9971009
blob_zero(&desc);
9981010
if( extrasFlags & EX_PLAIN ){ blob_appendf(&desc, " + unmanaged" ); }
9991011
if( extrasFlags & EX_IGNORE ){ blob_appendf(&desc, " + ignored" ); }
10001012
if( extrasFlags & EX_CLEAN ){ blob_appendf(&desc, " + to be cleaned"); }
10011013
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... */
10031015
/* Add the string built above, skipping the leading " + " */
10041016
@ (%h(blob_str(&desc)+3))
10051017
}
10061018
blob_reset(&desc);
10071019
}
10081020
@ </b></p>
10091021
10101022
db_prepare(&q,
10111023
"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? */
10131025
g.zLocalRoot
10141026
);
10151027
/*
10161028
** Put the file-list in one paragraph with line-breaks between.
1017
- **TODO
1029
+ **TODO:LD
10181030
** Might a table (with columns for name, ignore/clean/keep) work?
10191031
*/
10201032
@ <p>
10211033
nExtras = nMatch = nShown = nPlain = nKeep = nClean = nIgnore = 0;
10221034
while( db_step(&q)==SQLITE_ROW ){
@@ -1183,33 +1195,33 @@
11831195
** If the "ef=" isn't present (as when first navigating to "/local") then a
11841196
** default setting is used. Set to "0" to initially hide the report.
11851197
*/
11861198
zExtra = P("ef");
11871199
if( zExtra==NULL ) {
1188
- zExtra = ""; /*TODO Take the default form a config. option? */
1200
+ zExtra = LOCAL_DIFF_EXTRAS_MODE;
11891201
}
11901202
showExtras = strcmp(zExtra,"0")!=0;
11911203
11921204
/* Local mode is selected by either "/local" or with a "name" of "ckout".
11931205
** First, check we have access to the checkout (and report to the user if we
11941206
** don't), then refresh the "vfile" table (recording which files in the
11951207
** checkout have changed etc.). We then change the "name" parameter to "tip"
11961208
** so that the "header" section displays info about the check-in that the
11971209
** checkout came from.
1198
- **TODO
1210
+ **TODO:LD
11991211
** It would probably make sense to limit "/local" (and other links that come
12001212
** from it) to only be permitted when Fossil is running locally in "ui" mode.
12011213
** It's probably not critical when all you can do is view files in the
12021214
** checkout (they can already see the checked-in versions), but if a COMMIT
12031215
** 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!
12051217
*/
12061218
bLocalMode = (g.zPath[0]=='l') || (fossil_strcmp(zName,"ckout")==0);
12071219
if( bLocalMode ){
12081220
vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
12091221
if( vid==0 ){
1210
- /*TODO Is this the right response? */
1222
+ /*TODO:LD Is this the right response? */
12111223
style_header("No Local Checkout");
12121224
@ No access to local checkout.
12131225
style_footer();
12141226
return;
12151227
}
@@ -1523,11 +1535,11 @@
15231535
if( showExtras ){
15241536
@ %z(chref("button","%R/local?diff=%d%s&ef=0",diffType,zW))Hide Extras</a>
15251537
}else{
15261538
@ %z(chref("button","%R/local?diff=%d%s&ef=",diffType,zW))Show Extras</a>
15271539
}
1528
- /*TODO
1540
+ /*TODO:LD
15291541
** There would be a fair chunk of stuff to get right (not least appropriate
15301542
** restrictions), but it MIGHT be nice to have a COMMIT button here...
15311543
*/
15321544
}
15331545
@</div>
@@ -1538,11 +1550,11 @@
15381550
if( bLocalMode ){
15391551
if( showExtras ){
15401552
append_extras_report(zExtra, diffType, zW);
15411553
}
15421554
/* Following SQL taken from diff_against_disk() in diffcmd.c */
1543
- /*TODO
1555
+ /*TODO:LD
15441556
** That code wrapped the query/processing in a transaction (but, from
15451557
** memory, other similar uses did not). Is it neeeded?
15461558
*/
15471559
db_begin_transaction();
15481560
db_prepare(&q3,
@@ -1561,17 +1573,15 @@
15611573
** side-effect of altering the order entries are shown in (but within each
15621574
** group the original order is maintained).
15631575
**
15641576
** If disabled, (pass gets set to 3), only one pass is made on which all
15651577
** entries are shown in their "normal" order.
1566
- **TODO
1578
+ **TODO:LD
15671579
** Add this to the original (non-local) loop?
15681580
*/
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;
15731583
do{
15741584
while( db_step(&q3)==SQLITE_ROW ){
15751585
const char *zPathname = db_column_text(&q3,0);
15761586
int isDeleted = db_column_int(&q3, 1);
15771587
int isChnged = db_column_int(&q3,2);
@@ -1580,17 +1590,21 @@
15801590
int isLink = db_column_int(&q3, 5);
15811591
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcid);
15821592
append_local_file_change_line( zPathname, zUuid,
15831593
isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass );
15841594
free(zUuid);
1595
+ anyDifferences = 1;
15851596
}
15861597
db_reset(&q3);
15871598
}while( ++pass < 3 ); /* Either "1, 2, stop" or "3, stop". */
1599
+ if( !anyDifferences ){
1600
+ @ <p>No changes in the local checkout.</p>
1601
+ }
15881602
db_finalize(&q3);
15891603
db_end_transaction(1); /* ROLLBACK */
15901604
}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? */
15921606
db_prepare(&q3,
15931607
"SELECT name,"
15941608
" mperm,"
15951609
" (SELECT uuid FROM blob WHERE rid=mlink.pid),"
15961610
" (SELECT uuid FROM blob WHERE rid=mlink.fid),"
@@ -1631,11 +1645,11 @@
16311645
login_check_credentials();
16321646
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
16331647
16341648
vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
16351649
if( vid==0 ){
1636
- /*TODO Is this the right response? */
1650
+ /*TODO:LD Is this the right response? */
16371651
style_header("No Local Checkout");
16381652
@ No access to local checkout.
16391653
style_footer();
16401654
return;
16411655
}
@@ -1642,11 +1656,11 @@
16421656
vfile_check_signature(vid, CKSIG_ENOTFILE);
16431657
16441658
cgi_set_content_type("text/plain");
16451659
16461660
db_begin_transaction();
1647
- /*TODO
1661
+ /*TODO:LD
16481662
** This query is the same as in ci_page() for local-mode (as well as in
16491663
** diff_against_disk() in diffcmd.c, where it was originally taken from).
16501664
** Should they be "coalesced" in some way?
16511665
*/
16521666
db_prepare(&q3,
@@ -1975,17 +1989,17 @@
19751989
"%R/vdiff?%s&diff=1%s%T%s",
19761990
zQuery,
19771991
zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
19781992
}
19791993
if( zBranch==0 ){
1980
- //TODO Is there an extra "&" here?
1994
+ //TODO:LD Is there an extra "&" here?
19811995
style_submenu_element("Invert",
19821996
"%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,
19831997
zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
19841998
}
19851999
if( zGlob ){
1986
- //TODO Is there an extra "&" here?
2000
+ //TODO:LD Is there an extra "&" here?
19872001
style_submenu_element("Clear glob",
19882002
"%R/vdiff?%s&%s", zQuery, zW);
19892003
}else{
19902004
style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
19912005
}
@@ -2739,11 +2753,11 @@
27392753
style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
27402754
}
27412755
}
27422756
}
27432757
style_header("Hex Artifact Content");
2744
- /* TODO
2758
+ /*TODO:LD
27452759
** Could the call to style_header() be moved so these two exclusion
27462760
** blocks could be merged? I don't think any of them make sense for
27472761
** a local file.
27482762
*/
27492763
if( !bLocalMode ){
@@ -2770,11 +2784,11 @@
27702784
}else{
27712785
content_get(rid, &content);
27722786
}
27732787
@ <blockquote><pre>
27742788
hexdump(&content);
2775
- /* TODO: Should content (and downloadName?) be reset/freed? */
2789
+ /* TODO:LD: Should content (and downloadName?) be reset/freed? */
27762790
@ </pre></blockquote>
27772791
style_footer();
27782792
}
27792793
27802794
/*
@@ -3022,10 +3036,11 @@
30223036
** For /file, a name= without a ci= while prefer to use the default
30233037
** "tip" value for ci=. */
30243038
rid = name_to_rid(zName);
30253039
}
30263040
if( fossil_strcmp(zCI,"ckout")==0 ){
3041
+ /* "ci=ckout" is an extension for viewing files in the local checkout. */
30273042
bLocalMode = 1;
30283043
rid = -1; /* Dummy value to make it look found */
30293044
}else if( rid==0 ){
30303045
rid = artifact_from_ci_and_filename(0);
30313046
}
@@ -3062,11 +3077,11 @@
30623077
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
30633078
30643079
asText = P("txt")!=0;
30653080
if( isFile ){
30663081
if( bLocalMode ){
3067
- /*TODO
3082
+ /*TODO:LD
30683083
** Is this the best way of handling annotations to the description?
30693084
** If "annot=message" is part of the URL, the message is appended
30703085
** to the description of the file. Only used for "local" files to
30713086
** distinguish such files from part of the repository.
30723087
*/
@@ -3212,19 +3227,13 @@
32123227
if( descOnly ){
32133228
style_submenu_element("Content", "%R/artifact/%s", zUuid);
32143229
}else{
32153230
@ <hr />
32163231
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
+ }
32263235
}else{
32273236
content_get(rid, &content);
32283237
}
32293238
if( renderAsWiki ){
32303239
wiki_render_by_mimetype(&content, zMime);
32313240
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button