Fossil SCM
Added ability for "/local" to display files present in the checkout but not managed by Fossil (cf. "fossil extras").
Commit
07829f371b1c0c9ee287c018b2755fa78f65c0d2c5946caeaea2a933cd3b9257
Parent
5cbb78b486cd899…
2 files changed
+1
-1
+373
-26
+1
-1
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -63,11 +63,11 @@ | ||
| 63 | 63 | ** the command-line to that table. If directories are named, then add |
| 64 | 64 | ** all unmanaged files contained underneath those directories. If there |
| 65 | 65 | ** are no files or directories named on the command-line, then add all |
| 66 | 66 | ** unmanaged files anywhere in the checkout. |
| 67 | 67 | */ |
| 68 | -static void locate_unmanaged_files( | |
| 68 | +void locate_unmanaged_files( | |
| 69 | 69 | int argc, /* Number of command-line arguments to examine */ |
| 70 | 70 | char **argv, /* values of command-line arguments */ |
| 71 | 71 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 72 | 72 | Glob *pIgnore /* Do not add files that match this GLOB */ |
| 73 | 73 | ){ |
| 74 | 74 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -63,11 +63,11 @@ | |
| 63 | ** the command-line to that table. If directories are named, then add |
| 64 | ** all unmanaged files contained underneath those directories. If there |
| 65 | ** are no files or directories named on the command-line, then add all |
| 66 | ** unmanaged files anywhere in the checkout. |
| 67 | */ |
| 68 | static void locate_unmanaged_files( |
| 69 | int argc, /* Number of command-line arguments to examine */ |
| 70 | char **argv, /* values of command-line arguments */ |
| 71 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 72 | Glob *pIgnore /* Do not add files that match this GLOB */ |
| 73 | ){ |
| 74 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -63,11 +63,11 @@ | |
| 63 | ** the command-line to that table. If directories are named, then add |
| 64 | ** all unmanaged files contained underneath those directories. If there |
| 65 | ** are no files or directories named on the command-line, then add all |
| 66 | ** unmanaged files anywhere in the checkout. |
| 67 | */ |
| 68 | void locate_unmanaged_files( |
| 69 | int argc, /* Number of command-line arguments to examine */ |
| 70 | char **argv, /* values of command-line arguments */ |
| 71 | unsigned scanFlags, /* Zero or more SCAN_xxx flags */ |
| 72 | Glob *pIgnore /* Do not add files that match this GLOB */ |
| 73 | ){ |
| 74 |
+373
-26
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -1,15 +1,20 @@ | ||
| 1 | -/*TODO | |
| 2 | -** o Have seen some "MERGE" entries and updated messages: still not 100% happy. | |
| 1 | +/*TODO Graham's Notes | |
| 3 | 2 | ** o Should /file behave differently for non-existent local files? |
| 4 | -** o Look at adding an "extras" option (non-added, non-ignored files). | |
| 5 | 3 | ** o Find a place to add links to /local. |
| 6 | 4 | ** o Remove //TODO TESTING HACK TODO |
| 7 | 5 | ** ?? In hexdump_page(), should content (and downloadName?) be reset/freed? |
| 8 | -** ?? In the test fossil (\x\$Test\Fossil) there are (at time of writing) two | |
| 9 | -** commits under the same artifact... is this normal? | |
| 10 | -** ?? A settings entry to control one- or two-pass mode? | |
| 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 | +**------------------------------------------------------------------------------ | |
| 11 | 16 | */ |
| 12 | 17 | /* |
| 13 | 18 | ** Copyright (c) 2007 D. Richard Hipp |
| 14 | 19 | ** |
| 15 | 20 | ** This program is free software; you can redistribute it and/or |
| @@ -568,11 +573,11 @@ | ||
| 568 | 573 | }else{ |
| 569 | 574 | @ Missing %h(zName) (was added to checkout). |
| 570 | 575 | } |
| 571 | 576 | }else switch( isChnged ){ |
| 572 | 577 | /*TODO |
| 573 | - ** These "special cases" have not been properly tested (by creating | |
| 578 | + ** These "special cases" have not all been properly tested (by creating | |
| 574 | 579 | ** entries in a in a repository to trigger them), but they do display |
| 575 | 580 | ** as expected when "forced" to appear. |
| 576 | 581 | */ |
| 577 | 582 | case 3: |
| 578 | 583 | @ Added %h(zName) due to a merge. |
| @@ -825,10 +830,305 @@ | ||
| 825 | 830 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 826 | 831 | 0, 0, 0, rid, 0, 0); |
| 827 | 832 | db_finalize(&q); |
| 828 | 833 | style_footer(); |
| 829 | 834 | } |
| 835 | + | |
| 836 | +/* | |
| 837 | +** Options for the "extras" report. The bit-mask versions are used for "&ef=.." | |
| 838 | +** to select which category(ies) to show. | |
| 839 | +*/ | |
| 840 | +enum { | |
| 841 | + EXB_PLAIN, EXB_IGNORE, EXB_CLEAN, EXB_KEEP, | |
| 842 | + | |
| 843 | + EX_PLAIN = 1 << EXB_PLAIN, /* Matches none of the others */ | |
| 844 | + EX_IGNORE = 1 << EXB_IGNORE, /* Matches "ignore-glob" */ | |
| 845 | + EX_CLEAN = 1 << EXB_CLEAN, /* Matches "clean-glob" */ | |
| 846 | + EX_KEEP = 1 << EXB_KEEP, /* Matches "keep-glob" */ | |
| 847 | + EX_ALL = EX_PLAIN /* All entries */ | |
| 848 | + | EX_IGNORE | |
| 849 | + | EX_CLEAN | |
| 850 | + | EX_KEEP | |
| 851 | +}; | |
| 852 | + | |
| 853 | +/* | |
| 854 | +** Called while generating "/local": appends a report of any "extra" files that | |
| 855 | +** might be present in the current checkout. | |
| 856 | +** | |
| 857 | +** The format is controlled by "zExtra" (the value from the "ex=" URL option). | |
| 858 | +** This is converted to an int ("extrasFlags") and treated as a collection of | |
| 859 | +** the EX_xxx flags defined above. Thus "1" will list all "plain" files: | |
| 860 | +** unmanaged files that match none of the ignore/clean/keep blob-lists, and "2" | |
| 861 | +** would list all files matching the ignore-blob setting. "3" would list both | |
| 862 | +** those sets of files. | |
| 863 | +** | |
| 864 | +** An empty "zExtra" is a special case: it converts to zero which would normally | |
| 865 | +** select none of the EX_xxx flags above. Instead, it is converted to EX_PLAIN | |
| 866 | +** (=1) but with a (smallish) upper-limit on the number of files that will be | |
| 867 | +** listed. This is the "default" mode as it offers a combination of usefulness | |
| 868 | +** and non-intrusiveness: | |
| 869 | +** o If the glob-lists are configured well, the only files that will show are | |
| 870 | +** likely to be new source files that have not been "fossil add"ed. | |
| 871 | +** o If the glob-lists HAVEN'T been configured, only a relatively small number | |
| 872 | +** of temporary files (e.g. ".o" or ".obj") will be shown. | |
| 873 | +** | |
| 874 | +*/ | |
| 875 | +static void append_extras_report( | |
| 876 | + const char *zExtra, /* Value of "ef=" from URL */ | |
| 877 | + const int diffType, /* Used to preserve current */ | |
| 878 | + const char *zW /* settings in URLs */ | |
| 879 | +){ | |
| 880 | + const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; | |
| 881 | + Glob *pIgnore, *pKeep, *pClean; | |
| 882 | + int nRoot; | |
| 883 | + zIgnoreFlag = db_get("ignore-glob", 0); /* Patterns to ignore */ | |
| 884 | + zCleanFlag = db_get("clean-glob", 0); /* ...that "clean" clobbers */ | |
| 885 | + zKeepFlag = db_get("keep-glob", 0); /* ...that "clean" won't touch */ | |
| 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" | |
| 908 | + ** the limit by one show all entries would be shown. However, to know the | |
| 909 | + ** number of matches in advance we'd have to: | |
| 910 | + ** a) Pre-scan SFILE, testing and counting matches against each glob-list, | |
| 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! | |
| 967 | + if( nExtras == maxExtrasToShow+1 ){ maxExtrasToShow++; } | |
| 968 | + ** ... | |
| 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 | + } | |
| 981 | + | |
| 982 | + /* Describe the files listed. Currently, the only "built-in" options are to | |
| 983 | + ** list "plain" files (those unmanaged files not matching any glob-list), | |
| 984 | + ** or to list those files matching ONE of the glob-lists. However, manual | |
| 985 | + ** editing would allow selecting combinations of matching files. | |
| 986 | + ** | |
| 987 | + ** If only EX_PLAIN is present, then other types are explicitly excluded | |
| 988 | + ** by definition: PLAIN means "not matching any glob-list". For all other | |
| 989 | + ** cases, we cannot say things like "not ignored" because a file can match | |
| 990 | + ** more than one list. | |
| 991 | + */ | |
| 992 | + @ <p><b>Extra Files | |
| 993 | + if( extrasFlags == EX_PLAIN ){ | |
| 994 | + @ (unmanaged, not ignored, not for cleaning, not kept) | |
| 995 | + }else{ | |
| 996 | + Blob desc; | |
| 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 ){ | |
| 1023 | + const char *zName = db_column_text(&q, 0); | |
| 1024 | + int entryFlags = 0 | |
| 1025 | + | ( glob_match(pIgnore, zName+nRoot) ? EX_IGNORE : 0 ) | |
| 1026 | + | ( glob_match(pClean, zName+nRoot) ? EX_CLEAN : 0 ) | |
| 1027 | + | ( glob_match(pKeep, zName+nRoot) ? EX_KEEP : 0 ) ; | |
| 1028 | + if( entryFlags == 0 ){ | |
| 1029 | + entryFlags = EX_PLAIN; | |
| 1030 | + } | |
| 1031 | + if( entryFlags & EX_PLAIN ){ nPlain++; } | |
| 1032 | + if( entryFlags & EX_IGNORE ){ nIgnore++; } | |
| 1033 | + if( entryFlags & EX_CLEAN ){ nClean++; } | |
| 1034 | + if( entryFlags & EX_KEEP ){ nKeep++; } | |
| 1035 | + | |
| 1036 | + nExtras++; | |
| 1037 | + if( entryFlags & extrasFlags ){ | |
| 1038 | + nMatch++ ; | |
| 1039 | + if( nShown < maxExtrasToShow ){ | |
| 1040 | + nShown++; | |
| 1041 | + if( g.perm.Hyperlink ){ | |
| 1042 | + @ %z(href("%R/file/%T?ci=ckout&annot=not managed",zName+nRoot)) | |
| 1043 | + @ %h(zName+nRoot)</a> | |
| 1044 | + }else{ | |
| 1045 | + @ %h(zName+nRoot) | |
| 1046 | + } | |
| 1047 | + if( entryFlags & EX_IGNORE ){ | |
| 1048 | + @ (marked ignore) | |
| 1049 | + } | |
| 1050 | + if( entryFlags & EX_CLEAN ){ | |
| 1051 | + @ (marked clean) | |
| 1052 | + } | |
| 1053 | + if( entryFlags & EX_KEEP ){ | |
| 1054 | + @ (marked keep) | |
| 1055 | + } | |
| 1056 | + @ <br/> | |
| 1057 | + } | |
| 1058 | + } | |
| 1059 | + } | |
| 1060 | + | |
| 1061 | + if( nShown < nMatch ){ | |
| 1062 | + const int nHidden = nMatch - nShown; | |
| 1063 | + @ ... plus %d(nHidden) other matching file%h(nHidden==1?"":"s") | |
| 1064 | + @ (%z(href("/local?diff=%d%s&ef=%d",diffType,zW,extrasFlags)) | |
| 1065 | + @ show all)</a>. | |
| 1066 | + } | |
| 1067 | + @ </p> | |
| 1068 | + | |
| 1069 | + @ <p> | |
| 1070 | + if( nExtras==0 ){ | |
| 1071 | + @ No unmanaged files in checkout\ | |
| 1072 | + }else if( (nPlain==0) && (P("ef")==NULL) ){ | |
| 1073 | + @ No extra files in checkout | |
| 1074 | + @ (%z(href("%R/local?diff=%d%s&ef=1",diffType,zW))show exclusions</a>)\ | |
| 1075 | + }else{ | |
| 1076 | + /* Report types and counts of extra files, with links to see all of the | |
| 1077 | + ** selected type. Note the extra space before "including": the output of | |
| 1078 | + ** append_count() ends with "\" to get the formatting correct. | |
| 1079 | + */ | |
| 1080 | + append_count( EX_ALL, nExtras, "extra file", 1, 0, diffType,zW ); | |
| 1081 | + @ including | |
| 1082 | + append_count( EX_PLAIN, nPlain, "unmanaged", 0, 1, diffType,zW ); | |
| 1083 | + append_count( EX_IGNORE, nIgnore, "marked ignore", 0, 1, diffType,zW ); | |
| 1084 | + append_count( EX_CLEAN, nClean, "to be cleaned", 0, 1, diffType,zW ); | |
| 1085 | + append_count( EX_KEEP, nKeep, "to be kept", 0, 1, diffType,zW ); | |
| 1086 | + } | |
| 1087 | + @ .</p><hr/> | |
| 1088 | + blob_reset(&repo); | |
| 1089 | + db_finalize(&q); | |
| 1090 | +} | |
| 1091 | + | |
| 1092 | +/* | |
| 1093 | +** Append "26 extra files" type message as a link (assuming count is non-zero), | |
| 1094 | +** with pluralisation if requested and needed. If "commaBefore" is true, the | |
| 1095 | +** link is preceded by ", " if there have been earler entries with commaBefore | |
| 1096 | +** set. If false, it resets the count. This allows correct construction of | |
| 1097 | +** variants like: | |
| 1098 | +** 27 extra files including 27 ignored. | |
| 1099 | +** 30 extra files including 3 unmanaged, 27 ignored. | |
| 1100 | +** The dt (diffType) and zW parameters pass on formatting selections from the | |
| 1101 | +** rest of the /local page. | |
| 1102 | +*/ | |
| 1103 | +void append_count( | |
| 1104 | + int ef, /* Flags for link */ | |
| 1105 | + int count, /* Number of files */ | |
| 1106 | + const char *linkText, /* Link text after count */ | |
| 1107 | + int pluralise, /* Add optional "s"? */ | |
| 1108 | + int commaBefore, /* Precede with ", " if earlier non-zero counts */ | |
| 1109 | + int dt, /* DiffType */ | |
| 1110 | + const char *zW /* Whitespace setting ("" or "&w") */ | |
| 1111 | +){ | |
| 1112 | + static int earlierCounts = 0; | |
| 1113 | + if( count == 0 ){ | |
| 1114 | + return; | |
| 1115 | + } | |
| 1116 | + if( !commaBefore ){ | |
| 1117 | + earlierCounts = 0 ; | |
| 1118 | + }else if( earlierCounts ){ | |
| 1119 | + @ , | |
| 1120 | + } | |
| 1121 | + @ %z(href("%R/local?diff=%d%s&ef=%d",dt,zW,ef))%d(count) %h(linkText)\ | |
| 1122 | + if( pluralise ){ | |
| 1123 | + @ %h(count==1?"":"s")\ | |
| 1124 | + } | |
| 1125 | + @ </a>\ | |
| 1126 | + if( commaBefore ){ | |
| 1127 | + earlierCounts += count; | |
| 1128 | + } | |
| 1129 | +} | |
| 830 | 1130 | |
| 831 | 1131 | /* |
| 832 | 1132 | ** WEBPAGE: vinfo |
| 833 | 1133 | ** WEBPAGE: ci |
| 834 | 1134 | ** WEBPAGE: local |
| @@ -842,11 +1142,12 @@ | ||
| 842 | 1142 | ** The ARTIFACTID can be a unique prefix for the HASH of the check-in, |
| 843 | 1143 | ** or a tag or branch name that identifies the check-in. |
| 844 | 1144 | ** |
| 845 | 1145 | ** Use of /local (or the use of "ckout" for ARTIFACTID) will show the |
| 846 | 1146 | ** same header details as /ci/tip, but then displays any (uncommitted) |
| 847 | -** edits made to files in the checkout directory. | |
| 1147 | +** edits made to files in the checkout directory. It can also display | |
| 1148 | +** any "extra" files (roughly equivalent to "fossil extras"). | |
| 848 | 1149 | */ |
| 849 | 1150 | void ci_page(void){ |
| 850 | 1151 | Stmt q1, q2, q3; |
| 851 | 1152 | int rid; |
| 852 | 1153 | int isLeaf; |
| @@ -861,20 +1162,48 @@ | ||
| 861 | 1162 | const char *zPage = "vinfo"; /* Page that shows diffs */ |
| 862 | 1163 | const char *zPageHide = "ci"; /* Page that hides diffs */ |
| 863 | 1164 | const char *zBrName; /* Branch name */ |
| 864 | 1165 | int bLocalMode; /* TRUE for /local; FALSE otherwise */ |
| 865 | 1166 | int vid; /* Virtual file system? */ |
| 1167 | + int showExtras; /* Whether to show the extras report */ | |
| 1168 | + const char *zExtra; /* How to show the report */ | |
| 866 | 1169 | |
| 867 | 1170 | login_check_credentials(); |
| 868 | 1171 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 869 | 1172 | zName = P("name"); |
| 1173 | + /* | |
| 1174 | + ** "zExtra" controls if, and how, the "extras report" is displayed. It is a | |
| 1175 | + ** string, because when passed around in the URL (as "ef=") a value of "" has | |
| 1176 | + ** a different meaning to "0". A value of "" means "show a limited number of | |
| 1177 | + ** unmanaged files" (those that aren't ignored, marked to be cleaned, or | |
| 1178 | + ** marked kept)... this is what you'd most want to see if you've created a new | |
| 1179 | + ** source file and forgotten to "fossil add" it. A value of "0" will hide the | |
| 1180 | + ** extras report. Other (numeric) values control what the report shows (e.g. | |
| 1181 | + ** "1" would list ALL unmanaged files without limiting their number). | |
| 1182 | + ** | |
| 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 | + | |
| 870 | 1192 | /* Local mode is selected by either "/local" or with a "name" of "ckout". |
| 871 | 1193 | ** First, check we have access to the checkout (and report to the user if we |
| 872 | 1194 | ** don't), then refresh the "vfile" table (recording which files in the |
| 873 | 1195 | ** checkout have changed etc.). We then change the "name" parameter to "tip" |
| 874 | 1196 | ** so that the "header" section displays info about the check-in that the |
| 875 | 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. | |
| 876 | 1205 | */ |
| 877 | 1206 | bLocalMode = (g.zPath[0]=='l') || (fossil_strcmp(zName,"ckout")==0); |
| 878 | 1207 | if( bLocalMode ){ |
| 879 | 1208 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 880 | 1209 | if( vid==0 ){ |
| @@ -911,11 +1240,11 @@ | ||
| 911 | 1240 | " WHERE blob.rid=%d" |
| 912 | 1241 | " AND event.objid=%d", |
| 913 | 1242 | rid, rid |
| 914 | 1243 | ); |
| 915 | 1244 | zBrName = branch_of_rid(rid); |
| 916 | - | |
| 1245 | + | |
| 917 | 1246 | cookie_link_parameter("diff","diff","2"); |
| 918 | 1247 | diffType = atoi(PD("diff","2")); |
| 919 | 1248 | if( db_step(&q1)==SQLITE_ROW ){ |
| 920 | 1249 | const char *zUuid = db_column_text(&q1, 0); |
| 921 | 1250 | int nUuid = db_column_bytes(&q1, 0); |
| @@ -1146,35 +1475,38 @@ | ||
| 1146 | 1475 | @ <div class="section">Changes</div> |
| 1147 | 1476 | } |
| 1148 | 1477 | @ <div class="sectionmenu"> |
| 1149 | 1478 | diffFlags = construct_diff_flags(diffType); |
| 1150 | 1479 | zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 1480 | + | |
| 1151 | 1481 | /* In local mode, having displayed the header info for "tip", switch zName |
| 1152 | 1482 | ** to be "ckout" so the style-altering links (unified or side-by-side etc.) |
| 1153 | 1483 | ** will correctly re-select local-mode. |
| 1154 | 1484 | */ |
| 1155 | 1485 | if( bLocalMode ){ |
| 1156 | 1486 | zName = "ckout"; |
| 1157 | 1487 | } |
| 1158 | 1488 | if( diffType!=0 ){ |
| 1159 | - @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\ | |
| 1489 | + @ %z(chref("button","%R/%s/%T?diff=0&ef=%s",zPageHide,zName,zExtra))\ | |
| 1160 | 1490 | @ Hide Diffs</a> |
| 1161 | 1491 | } |
| 1162 | 1492 | if( diffType!=1 ){ |
| 1163 | - @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\ | |
| 1493 | + @ %z(chref("button","%R/%s/%T?diff=1%s&ef=%s",zPage,zName,zW,zExtra))\ | |
| 1164 | 1494 | @ Unified Diffs</a> |
| 1165 | 1495 | } |
| 1166 | 1496 | if( diffType!=2 ){ |
| 1167 | - @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\ | |
| 1497 | + @ %z(chref("button","%R/%s/%T?diff=2%s&ef=%s",zPage,zName,zW,zExtra))\ | |
| 1168 | 1498 | @ Side-by-Side Diffs</a> |
| 1169 | 1499 | } |
| 1170 | 1500 | if( diffType!=0 ){ |
| 1171 | 1501 | if( *zW ){ |
| 1172 | - @ %z(chref("button","%R/%s/%T?diff=%d",zPage,zName,diffType)) | |
| 1502 | + @ %z(chref("button","%R/%s/%T?diff=%d&ef=%s",zPage,zName,diffType,zExtra)) | |
| 1173 | 1503 | @ Show Whitespace Changes</a> |
| 1174 | 1504 | }else{ |
| 1175 | - @ %z(chref("button","%R/%s/%T?diff=%d&w",zPage,zName,diffType)) | |
| 1505 | + char *button = chref("button","%R/%s/%T?diff=%d&w&ef=%s", | |
| 1506 | + zPage,zName,diffType,zExtra); | |
| 1507 | + @ %z(button) | |
| 1176 | 1508 | @ Ignore Whitespace</a> |
| 1177 | 1509 | } |
| 1178 | 1510 | } |
| 1179 | 1511 | if( bLocalMode ){ |
| 1180 | 1512 | @ %z(chref("button","%R/localpatch")) Patch</a> |
| @@ -1184,36 +1516,46 @@ | ||
| 1184 | 1516 | @ Patch</a> |
| 1185 | 1517 | } |
| 1186 | 1518 | } |
| 1187 | 1519 | if( g.perm.Admin ){ |
| 1188 | 1520 | @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a> |
| 1521 | + } | |
| 1522 | + if( bLocalMode ){ | |
| 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 | + */ | |
| 1189 | 1532 | } |
| 1190 | 1533 | @</div> |
| 1191 | 1534 | if( pRe ){ |
| 1192 | 1535 | @ <p><b>Only differences that match regular expression "%h(zRe)" |
| 1193 | 1536 | @ are shown.</b></p> |
| 1194 | 1537 | } |
| 1195 | 1538 | if( bLocalMode ){ |
| 1196 | -//--------------------------------------------------- TODO TESTING HACK TODO | |
| 1197 | - int bTwoPass = P("op")==NULL; | |
| 1198 | -//--------------------------------------------------- TODO TESTING HACK TODO | |
| 1539 | + if( showExtras ){ | |
| 1540 | + append_extras_report(zExtra, diffType, zW); | |
| 1541 | + } | |
| 1199 | 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 | + */ | |
| 1200 | 1547 | db_begin_transaction(); |
| 1201 | 1548 | db_prepare(&q3, |
| 1202 | 1549 | "SELECT pathname, deleted, chnged , rid==0, rid, islink" |
| 1203 | 1550 | " FROM vfile" |
| 1204 | 1551 | " WHERE vid=%d" |
| 1205 | 1552 | " AND (deleted OR chnged OR rid==0)" |
| 1206 | 1553 | " ORDER BY pathname /*scan*/", |
| 1207 | 1554 | vid |
| 1208 | 1555 | ); |
| 1209 | - /* TODO Have the option of showing "extras" (non-ignored files in the | |
| 1210 | - ** checkout directory that have not been ADDed). If done, they should | |
| 1211 | - ** be ahead of any potential "diff-blocks" so they don't get lost | |
| 1212 | - ** (which is the inspiration for...) | |
| 1213 | - ** TODO Possibly (at some stage) have an option to commit? | |
| 1214 | - */ | |
| 1556 | + | |
| 1215 | 1557 | /* To prevent single-line diff-entries (those without "diff-blocks") from |
| 1216 | 1558 | ** getting "lost", there's an optional "two-pass" mode for processing |
| 1217 | 1559 | ** differences. If enabled, the first pass will only show one-line entries |
| 1218 | 1560 | ** and the second pass will only show those with diff-blocks. This has the |
| 1219 | 1561 | ** side-effect of altering the order entries are shown in (but within each |
| @@ -1222,10 +1564,13 @@ | ||
| 1222 | 1564 | ** If disabled, (pass gets set to 3), only one pass is made on which all |
| 1223 | 1565 | ** entries are shown in their "normal" order. |
| 1224 | 1566 | **TODO |
| 1225 | 1567 | ** Add this to the original (non-local) loop? |
| 1226 | 1568 | */ |
| 1569 | +//--------------------------------------------------- TODO TESTING HACK TODO | |
| 1570 | + int bTwoPass = P("op")==NULL; //TODO Taken from a config option? | |
| 1571 | +//--------------------------------------------------- TODO TESTING HACK TODO | |
| 1227 | 1572 | int pass = bTwoPass?1:3; |
| 1228 | 1573 | do{ |
| 1229 | 1574 | while( db_step(&q3)==SQLITE_ROW ){ |
| 1230 | 1575 | const char *zPathname = db_column_text(&q3,0); |
| 1231 | 1576 | int isDeleted = db_column_int(&q3, 1); |
| @@ -1232,17 +1577,17 @@ | ||
| 1232 | 1577 | int isChnged = db_column_int(&q3,2); |
| 1233 | 1578 | int isNew = db_column_int(&q3,3); |
| 1234 | 1579 | int srcid = db_column_int(&q3, 4); |
| 1235 | 1580 | int isLink = db_column_int(&q3, 5); |
| 1236 | 1581 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcid); |
| 1237 | - append_local_file_change_line(zPathname, zUuid, | |
| 1238 | - isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass); | |
| 1582 | + append_local_file_change_line( zPathname, zUuid, | |
| 1583 | + isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass ); | |
| 1239 | 1584 | free(zUuid); |
| 1240 | 1585 | } |
| 1241 | 1586 | db_reset(&q3); |
| 1242 | 1587 | }while( ++pass < 3 ); /* Either "1, 2, stop" or "3, stop". */ |
| 1243 | - db_finalize(&q3); /*TODO: Is this needed if we're reseting? */ | |
| 1588 | + db_finalize(&q3); | |
| 1244 | 1589 | db_end_transaction(1); /* ROLLBACK */ |
| 1245 | 1590 | }else{ /* Normal, non-local-mode: show diffs against parent */ |
| 1246 | 1591 | /*TODO: Implement the optional two-pass code? */ |
| 1247 | 1592 | db_prepare(&q3, |
| 1248 | 1593 | "SELECT name," |
| @@ -1630,15 +1975,17 @@ | ||
| 1630 | 1975 | "%R/vdiff?%s&diff=1%s%T%s", |
| 1631 | 1976 | zQuery, |
| 1632 | 1977 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1633 | 1978 | } |
| 1634 | 1979 | if( zBranch==0 ){ |
| 1980 | + //TODO Is there an extra "&" here? | |
| 1635 | 1981 | style_submenu_element("Invert", |
| 1636 | 1982 | "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, |
| 1637 | 1983 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1638 | 1984 | } |
| 1639 | 1985 | if( zGlob ){ |
| 1986 | + //TODO Is there an extra "&" here? | |
| 1640 | 1987 | style_submenu_element("Clear glob", |
| 1641 | 1988 | "%R/vdiff?%s&%s", zQuery, zW); |
| 1642 | 1989 | }else{ |
| 1643 | 1990 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); |
| 1644 | 1991 | } |
| 1645 | 1992 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1,15 +1,20 @@ | |
| 1 | /*TODO |
| 2 | ** o Have seen some "MERGE" entries and updated messages: still not 100% happy. |
| 3 | ** o Should /file behave differently for non-existent local files? |
| 4 | ** o Look at adding an "extras" option (non-added, non-ignored files). |
| 5 | ** o Find a place to add links to /local. |
| 6 | ** o Remove //TODO TESTING HACK TODO |
| 7 | ** ?? In hexdump_page(), should content (and downloadName?) be reset/freed? |
| 8 | ** ?? In the test fossil (\x\$Test\Fossil) there are (at time of writing) two |
| 9 | ** commits under the same artifact... is this normal? |
| 10 | ** ?? A settings entry to control one- or two-pass mode? |
| 11 | */ |
| 12 | /* |
| 13 | ** Copyright (c) 2007 D. Richard Hipp |
| 14 | ** |
| 15 | ** This program is free software; you can redistribute it and/or |
| @@ -568,11 +573,11 @@ | |
| 568 | }else{ |
| 569 | @ Missing %h(zName) (was added to checkout). |
| 570 | } |
| 571 | }else switch( isChnged ){ |
| 572 | /*TODO |
| 573 | ** These "special cases" have not been properly tested (by creating |
| 574 | ** entries in a in a repository to trigger them), but they do display |
| 575 | ** as expected when "forced" to appear. |
| 576 | */ |
| 577 | case 3: |
| 578 | @ Added %h(zName) due to a merge. |
| @@ -825,10 +830,305 @@ | |
| 825 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 826 | 0, 0, 0, rid, 0, 0); |
| 827 | db_finalize(&q); |
| 828 | style_footer(); |
| 829 | } |
| 830 | |
| 831 | /* |
| 832 | ** WEBPAGE: vinfo |
| 833 | ** WEBPAGE: ci |
| 834 | ** WEBPAGE: local |
| @@ -842,11 +1142,12 @@ | |
| 842 | ** The ARTIFACTID can be a unique prefix for the HASH of the check-in, |
| 843 | ** or a tag or branch name that identifies the check-in. |
| 844 | ** |
| 845 | ** Use of /local (or the use of "ckout" for ARTIFACTID) will show the |
| 846 | ** same header details as /ci/tip, but then displays any (uncommitted) |
| 847 | ** edits made to files in the checkout directory. |
| 848 | */ |
| 849 | void ci_page(void){ |
| 850 | Stmt q1, q2, q3; |
| 851 | int rid; |
| 852 | int isLeaf; |
| @@ -861,20 +1162,48 @@ | |
| 861 | const char *zPage = "vinfo"; /* Page that shows diffs */ |
| 862 | const char *zPageHide = "ci"; /* Page that hides diffs */ |
| 863 | const char *zBrName; /* Branch name */ |
| 864 | int bLocalMode; /* TRUE for /local; FALSE otherwise */ |
| 865 | int vid; /* Virtual file system? */ |
| 866 | |
| 867 | login_check_credentials(); |
| 868 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 869 | zName = P("name"); |
| 870 | /* Local mode is selected by either "/local" or with a "name" of "ckout". |
| 871 | ** First, check we have access to the checkout (and report to the user if we |
| 872 | ** don't), then refresh the "vfile" table (recording which files in the |
| 873 | ** checkout have changed etc.). We then change the "name" parameter to "tip" |
| 874 | ** so that the "header" section displays info about the check-in that the |
| 875 | ** checkout came from. |
| 876 | */ |
| 877 | bLocalMode = (g.zPath[0]=='l') || (fossil_strcmp(zName,"ckout")==0); |
| 878 | if( bLocalMode ){ |
| 879 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 880 | if( vid==0 ){ |
| @@ -911,11 +1240,11 @@ | |
| 911 | " WHERE blob.rid=%d" |
| 912 | " AND event.objid=%d", |
| 913 | rid, rid |
| 914 | ); |
| 915 | zBrName = branch_of_rid(rid); |
| 916 | |
| 917 | cookie_link_parameter("diff","diff","2"); |
| 918 | diffType = atoi(PD("diff","2")); |
| 919 | if( db_step(&q1)==SQLITE_ROW ){ |
| 920 | const char *zUuid = db_column_text(&q1, 0); |
| 921 | int nUuid = db_column_bytes(&q1, 0); |
| @@ -1146,35 +1475,38 @@ | |
| 1146 | @ <div class="section">Changes</div> |
| 1147 | } |
| 1148 | @ <div class="sectionmenu"> |
| 1149 | diffFlags = construct_diff_flags(diffType); |
| 1150 | zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 1151 | /* In local mode, having displayed the header info for "tip", switch zName |
| 1152 | ** to be "ckout" so the style-altering links (unified or side-by-side etc.) |
| 1153 | ** will correctly re-select local-mode. |
| 1154 | */ |
| 1155 | if( bLocalMode ){ |
| 1156 | zName = "ckout"; |
| 1157 | } |
| 1158 | if( diffType!=0 ){ |
| 1159 | @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\ |
| 1160 | @ Hide Diffs</a> |
| 1161 | } |
| 1162 | if( diffType!=1 ){ |
| 1163 | @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\ |
| 1164 | @ Unified Diffs</a> |
| 1165 | } |
| 1166 | if( diffType!=2 ){ |
| 1167 | @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\ |
| 1168 | @ Side-by-Side Diffs</a> |
| 1169 | } |
| 1170 | if( diffType!=0 ){ |
| 1171 | if( *zW ){ |
| 1172 | @ %z(chref("button","%R/%s/%T?diff=%d",zPage,zName,diffType)) |
| 1173 | @ Show Whitespace Changes</a> |
| 1174 | }else{ |
| 1175 | @ %z(chref("button","%R/%s/%T?diff=%d&w",zPage,zName,diffType)) |
| 1176 | @ Ignore Whitespace</a> |
| 1177 | } |
| 1178 | } |
| 1179 | if( bLocalMode ){ |
| 1180 | @ %z(chref("button","%R/localpatch")) Patch</a> |
| @@ -1184,36 +1516,46 @@ | |
| 1184 | @ Patch</a> |
| 1185 | } |
| 1186 | } |
| 1187 | if( g.perm.Admin ){ |
| 1188 | @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a> |
| 1189 | } |
| 1190 | @</div> |
| 1191 | if( pRe ){ |
| 1192 | @ <p><b>Only differences that match regular expression "%h(zRe)" |
| 1193 | @ are shown.</b></p> |
| 1194 | } |
| 1195 | if( bLocalMode ){ |
| 1196 | //--------------------------------------------------- TODO TESTING HACK TODO |
| 1197 | int bTwoPass = P("op")==NULL; |
| 1198 | //--------------------------------------------------- TODO TESTING HACK TODO |
| 1199 | /* Following SQL taken from diff_against_disk() in diffcmd.c */ |
| 1200 | db_begin_transaction(); |
| 1201 | db_prepare(&q3, |
| 1202 | "SELECT pathname, deleted, chnged , rid==0, rid, islink" |
| 1203 | " FROM vfile" |
| 1204 | " WHERE vid=%d" |
| 1205 | " AND (deleted OR chnged OR rid==0)" |
| 1206 | " ORDER BY pathname /*scan*/", |
| 1207 | vid |
| 1208 | ); |
| 1209 | /* TODO Have the option of showing "extras" (non-ignored files in the |
| 1210 | ** checkout directory that have not been ADDed). If done, they should |
| 1211 | ** be ahead of any potential "diff-blocks" so they don't get lost |
| 1212 | ** (which is the inspiration for...) |
| 1213 | ** TODO Possibly (at some stage) have an option to commit? |
| 1214 | */ |
| 1215 | /* To prevent single-line diff-entries (those without "diff-blocks") from |
| 1216 | ** getting "lost", there's an optional "two-pass" mode for processing |
| 1217 | ** differences. If enabled, the first pass will only show one-line entries |
| 1218 | ** and the second pass will only show those with diff-blocks. This has the |
| 1219 | ** side-effect of altering the order entries are shown in (but within each |
| @@ -1222,10 +1564,13 @@ | |
| 1222 | ** If disabled, (pass gets set to 3), only one pass is made on which all |
| 1223 | ** entries are shown in their "normal" order. |
| 1224 | **TODO |
| 1225 | ** Add this to the original (non-local) loop? |
| 1226 | */ |
| 1227 | int pass = bTwoPass?1:3; |
| 1228 | do{ |
| 1229 | while( db_step(&q3)==SQLITE_ROW ){ |
| 1230 | const char *zPathname = db_column_text(&q3,0); |
| 1231 | int isDeleted = db_column_int(&q3, 1); |
| @@ -1232,17 +1577,17 @@ | |
| 1232 | int isChnged = db_column_int(&q3,2); |
| 1233 | int isNew = db_column_int(&q3,3); |
| 1234 | int srcid = db_column_int(&q3, 4); |
| 1235 | int isLink = db_column_int(&q3, 5); |
| 1236 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcid); |
| 1237 | append_local_file_change_line(zPathname, zUuid, |
| 1238 | isDeleted, isChnged, isNew, isLink, diffFlags,pRe,pass); |
| 1239 | free(zUuid); |
| 1240 | } |
| 1241 | db_reset(&q3); |
| 1242 | }while( ++pass < 3 ); /* Either "1, 2, stop" or "3, stop". */ |
| 1243 | db_finalize(&q3); /*TODO: Is this needed if we're reseting? */ |
| 1244 | db_end_transaction(1); /* ROLLBACK */ |
| 1245 | }else{ /* Normal, non-local-mode: show diffs against parent */ |
| 1246 | /*TODO: Implement the optional two-pass code? */ |
| 1247 | db_prepare(&q3, |
| 1248 | "SELECT name," |
| @@ -1630,15 +1975,17 @@ | |
| 1630 | "%R/vdiff?%s&diff=1%s%T%s", |
| 1631 | zQuery, |
| 1632 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1633 | } |
| 1634 | if( zBranch==0 ){ |
| 1635 | style_submenu_element("Invert", |
| 1636 | "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom, |
| 1637 | zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); |
| 1638 | } |
| 1639 | if( zGlob ){ |
| 1640 | style_submenu_element("Clear glob", |
| 1641 | "%R/vdiff?%s&%s", zQuery, zW); |
| 1642 | }else{ |
| 1643 | style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); |
| 1644 | } |
| 1645 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -1,15 +1,20 @@ | |
| 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 |
| @@ -568,11 +573,11 @@ | |
| 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. |
| @@ -825,10 +830,305 @@ | |
| 830 | www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL, |
| 831 | 0, 0, 0, rid, 0, 0); |
| 832 | db_finalize(&q); |
| 833 | style_footer(); |
| 834 | } |
| 835 | |
| 836 | /* |
| 837 | ** Options for the "extras" report. The bit-mask versions are used for "&ef=.." |
| 838 | ** to select which category(ies) to show. |
| 839 | */ |
| 840 | enum { |
| 841 | EXB_PLAIN, EXB_IGNORE, EXB_CLEAN, EXB_KEEP, |
| 842 | |
| 843 | EX_PLAIN = 1 << EXB_PLAIN, /* Matches none of the others */ |
| 844 | EX_IGNORE = 1 << EXB_IGNORE, /* Matches "ignore-glob" */ |
| 845 | EX_CLEAN = 1 << EXB_CLEAN, /* Matches "clean-glob" */ |
| 846 | EX_KEEP = 1 << EXB_KEEP, /* Matches "keep-glob" */ |
| 847 | EX_ALL = EX_PLAIN /* All entries */ |
| 848 | | EX_IGNORE |
| 849 | | EX_CLEAN |
| 850 | | EX_KEEP |
| 851 | }; |
| 852 | |
| 853 | /* |
| 854 | ** Called while generating "/local": appends a report of any "extra" files that |
| 855 | ** might be present in the current checkout. |
| 856 | ** |
| 857 | ** The format is controlled by "zExtra" (the value from the "ex=" URL option). |
| 858 | ** This is converted to an int ("extrasFlags") and treated as a collection of |
| 859 | ** the EX_xxx flags defined above. Thus "1" will list all "plain" files: |
| 860 | ** unmanaged files that match none of the ignore/clean/keep blob-lists, and "2" |
| 861 | ** would list all files matching the ignore-blob setting. "3" would list both |
| 862 | ** those sets of files. |
| 863 | ** |
| 864 | ** An empty "zExtra" is a special case: it converts to zero which would normally |
| 865 | ** select none of the EX_xxx flags above. Instead, it is converted to EX_PLAIN |
| 866 | ** (=1) but with a (smallish) upper-limit on the number of files that will be |
| 867 | ** listed. This is the "default" mode as it offers a combination of usefulness |
| 868 | ** and non-intrusiveness: |
| 869 | ** o If the glob-lists are configured well, the only files that will show are |
| 870 | ** likely to be new source files that have not been "fossil add"ed. |
| 871 | ** o If the glob-lists HAVEN'T been configured, only a relatively small number |
| 872 | ** of temporary files (e.g. ".o" or ".obj") will be shown. |
| 873 | ** |
| 874 | */ |
| 875 | static void append_extras_report( |
| 876 | const char *zExtra, /* Value of "ef=" from URL */ |
| 877 | const int diffType, /* Used to preserve current */ |
| 878 | const char *zW /* settings in URLs */ |
| 879 | ){ |
| 880 | const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; |
| 881 | Glob *pIgnore, *pKeep, *pClean; |
| 882 | int nRoot; |
| 883 | zIgnoreFlag = db_get("ignore-glob", 0); /* Patterns to ignore */ |
| 884 | zCleanFlag = db_get("clean-glob", 0); /* ...that "clean" clobbers */ |
| 885 | zKeepFlag = db_get("keep-glob", 0); /* ...that "clean" won't touch */ |
| 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" |
| 908 | ** the limit by one show all entries would be shown. However, to know the |
| 909 | ** number of matches in advance we'd have to: |
| 910 | ** a) Pre-scan SFILE, testing and counting matches against each glob-list, |
| 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! |
| 967 | if( nExtras == maxExtrasToShow+1 ){ maxExtrasToShow++; } |
| 968 | ** ... |
| 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 | } |
| 981 | |
| 982 | /* Describe the files listed. Currently, the only "built-in" options are to |
| 983 | ** list "plain" files (those unmanaged files not matching any glob-list), |
| 984 | ** or to list those files matching ONE of the glob-lists. However, manual |
| 985 | ** editing would allow selecting combinations of matching files. |
| 986 | ** |
| 987 | ** If only EX_PLAIN is present, then other types are explicitly excluded |
| 988 | ** by definition: PLAIN means "not matching any glob-list". For all other |
| 989 | ** cases, we cannot say things like "not ignored" because a file can match |
| 990 | ** more than one list. |
| 991 | */ |
| 992 | @ <p><b>Extra Files |
| 993 | if( extrasFlags == EX_PLAIN ){ |
| 994 | @ (unmanaged, not ignored, not for cleaning, not kept) |
| 995 | }else{ |
| 996 | Blob desc; |
| 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 ){ |
| 1023 | const char *zName = db_column_text(&q, 0); |
| 1024 | int entryFlags = 0 |
| 1025 | | ( glob_match(pIgnore, zName+nRoot) ? EX_IGNORE : 0 ) |
| 1026 | | ( glob_match(pClean, zName+nRoot) ? EX_CLEAN : 0 ) |
| 1027 | | ( glob_match(pKeep, zName+nRoot) ? EX_KEEP : 0 ) ; |
| 1028 | if( entryFlags == 0 ){ |
| 1029 | entryFlags = EX_PLAIN; |
| 1030 | } |
| 1031 | if( entryFlags & EX_PLAIN ){ nPlain++; } |
| 1032 | if( entryFlags & EX_IGNORE ){ nIgnore++; } |
| 1033 | if( entryFlags & EX_CLEAN ){ nClean++; } |
| 1034 | if( entryFlags & EX_KEEP ){ nKeep++; } |
| 1035 | |
| 1036 | nExtras++; |
| 1037 | if( entryFlags & extrasFlags ){ |
| 1038 | nMatch++ ; |
| 1039 | if( nShown < maxExtrasToShow ){ |
| 1040 | nShown++; |
| 1041 | if( g.perm.Hyperlink ){ |
| 1042 | @ %z(href("%R/file/%T?ci=ckout&annot=not managed",zName+nRoot)) |
| 1043 | @ %h(zName+nRoot)</a> |
| 1044 | }else{ |
| 1045 | @ %h(zName+nRoot) |
| 1046 | } |
| 1047 | if( entryFlags & EX_IGNORE ){ |
| 1048 | @ (marked ignore) |
| 1049 | } |
| 1050 | if( entryFlags & EX_CLEAN ){ |
| 1051 | @ (marked clean) |
| 1052 | } |
| 1053 | if( entryFlags & EX_KEEP ){ |
| 1054 | @ (marked keep) |
| 1055 | } |
| 1056 | @ <br/> |
| 1057 | } |
| 1058 | } |
| 1059 | } |
| 1060 | |
| 1061 | if( nShown < nMatch ){ |
| 1062 | const int nHidden = nMatch - nShown; |
| 1063 | @ ... plus %d(nHidden) other matching file%h(nHidden==1?"":"s") |
| 1064 | @ (%z(href("/local?diff=%d%s&ef=%d",diffType,zW,extrasFlags)) |
| 1065 | @ show all)</a>. |
| 1066 | } |
| 1067 | @ </p> |
| 1068 | |
| 1069 | @ <p> |
| 1070 | if( nExtras==0 ){ |
| 1071 | @ No unmanaged files in checkout\ |
| 1072 | }else if( (nPlain==0) && (P("ef")==NULL) ){ |
| 1073 | @ No extra files in checkout |
| 1074 | @ (%z(href("%R/local?diff=%d%s&ef=1",diffType,zW))show exclusions</a>)\ |
| 1075 | }else{ |
| 1076 | /* Report types and counts of extra files, with links to see all of the |
| 1077 | ** selected type. Note the extra space before "including": the output of |
| 1078 | ** append_count() ends with "\" to get the formatting correct. |
| 1079 | */ |
| 1080 | append_count( EX_ALL, nExtras, "extra file", 1, 0, diffType,zW ); |
| 1081 | @ including |
| 1082 | append_count( EX_PLAIN, nPlain, "unmanaged", 0, 1, diffType,zW ); |
| 1083 | append_count( EX_IGNORE, nIgnore, "marked ignore", 0, 1, diffType,zW ); |
| 1084 | append_count( EX_CLEAN, nClean, "to be cleaned", 0, 1, diffType,zW ); |
| 1085 | append_count( EX_KEEP, nKeep, "to be kept", 0, 1, diffType,zW ); |
| 1086 | } |
| 1087 | @ .</p><hr/> |
| 1088 | blob_reset(&repo); |
| 1089 | db_finalize(&q); |
| 1090 | } |
| 1091 | |
| 1092 | /* |
| 1093 | ** Append "26 extra files" type message as a link (assuming count is non-zero), |
| 1094 | ** with pluralisation if requested and needed. If "commaBefore" is true, the |
| 1095 | ** link is preceded by ", " if there have been earler entries with commaBefore |
| 1096 | ** set. If false, it resets the count. This allows correct construction of |
| 1097 | ** variants like: |
| 1098 | ** 27 extra files including 27 ignored. |
| 1099 | ** 30 extra files including 3 unmanaged, 27 ignored. |
| 1100 | ** The dt (diffType) and zW parameters pass on formatting selections from the |
| 1101 | ** rest of the /local page. |
| 1102 | */ |
| 1103 | void append_count( |
| 1104 | int ef, /* Flags for link */ |
| 1105 | int count, /* Number of files */ |
| 1106 | const char *linkText, /* Link text after count */ |
| 1107 | int pluralise, /* Add optional "s"? */ |
| 1108 | int commaBefore, /* Precede with ", " if earlier non-zero counts */ |
| 1109 | int dt, /* DiffType */ |
| 1110 | const char *zW /* Whitespace setting ("" or "&w") */ |
| 1111 | ){ |
| 1112 | static int earlierCounts = 0; |
| 1113 | if( count == 0 ){ |
| 1114 | return; |
| 1115 | } |
| 1116 | if( !commaBefore ){ |
| 1117 | earlierCounts = 0 ; |
| 1118 | }else if( earlierCounts ){ |
| 1119 | @ , |
| 1120 | } |
| 1121 | @ %z(href("%R/local?diff=%d%s&ef=%d",dt,zW,ef))%d(count) %h(linkText)\ |
| 1122 | if( pluralise ){ |
| 1123 | @ %h(count==1?"":"s")\ |
| 1124 | } |
| 1125 | @ </a>\ |
| 1126 | if( commaBefore ){ |
| 1127 | earlierCounts += count; |
| 1128 | } |
| 1129 | } |
| 1130 | |
| 1131 | /* |
| 1132 | ** WEBPAGE: vinfo |
| 1133 | ** WEBPAGE: ci |
| 1134 | ** WEBPAGE: local |
| @@ -842,11 +1142,12 @@ | |
| 1142 | ** The ARTIFACTID can be a unique prefix for the HASH of the check-in, |
| 1143 | ** or a tag or branch name that identifies the check-in. |
| 1144 | ** |
| 1145 | ** Use of /local (or the use of "ckout" for ARTIFACTID) will show the |
| 1146 | ** same header details as /ci/tip, but then displays any (uncommitted) |
| 1147 | ** edits made to files in the checkout directory. It can also display |
| 1148 | ** any "extra" files (roughly equivalent to "fossil extras"). |
| 1149 | */ |
| 1150 | void ci_page(void){ |
| 1151 | Stmt q1, q2, q3; |
| 1152 | int rid; |
| 1153 | int isLeaf; |
| @@ -861,20 +1162,48 @@ | |
| 1162 | const char *zPage = "vinfo"; /* Page that shows diffs */ |
| 1163 | const char *zPageHide = "ci"; /* Page that hides diffs */ |
| 1164 | const char *zBrName; /* Branch name */ |
| 1165 | int bLocalMode; /* TRUE for /local; FALSE otherwise */ |
| 1166 | int vid; /* Virtual file system? */ |
| 1167 | int showExtras; /* Whether to show the extras report */ |
| 1168 | const char *zExtra; /* How to show the report */ |
| 1169 | |
| 1170 | login_check_credentials(); |
| 1171 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1172 | zName = P("name"); |
| 1173 | /* |
| 1174 | ** "zExtra" controls if, and how, the "extras report" is displayed. It is a |
| 1175 | ** string, because when passed around in the URL (as "ef=") a value of "" has |
| 1176 | ** a different meaning to "0". A value of "" means "show a limited number of |
| 1177 | ** unmanaged files" (those that aren't ignored, marked to be cleaned, or |
| 1178 | ** marked kept)... this is what you'd most want to see if you've created a new |
| 1179 | ** source file and forgotten to "fossil add" it. A value of "0" will hide the |
| 1180 | ** extras report. Other (numeric) values control what the report shows (e.g. |
| 1181 | ** "1" would list ALL unmanaged files without limiting their number). |
| 1182 | ** |
| 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 ){ |
| @@ -911,11 +1240,11 @@ | |
| 1240 | " WHERE blob.rid=%d" |
| 1241 | " AND event.objid=%d", |
| 1242 | rid, rid |
| 1243 | ); |
| 1244 | zBrName = branch_of_rid(rid); |
| 1245 | |
| 1246 | cookie_link_parameter("diff","diff","2"); |
| 1247 | diffType = atoi(PD("diff","2")); |
| 1248 | if( db_step(&q1)==SQLITE_ROW ){ |
| 1249 | const char *zUuid = db_column_text(&q1, 0); |
| 1250 | int nUuid = db_column_bytes(&q1, 0); |
| @@ -1146,35 +1475,38 @@ | |
| 1475 | @ <div class="section">Changes</div> |
| 1476 | } |
| 1477 | @ <div class="sectionmenu"> |
| 1478 | diffFlags = construct_diff_flags(diffType); |
| 1479 | zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; |
| 1480 | |
| 1481 | /* In local mode, having displayed the header info for "tip", switch zName |
| 1482 | ** to be "ckout" so the style-altering links (unified or side-by-side etc.) |
| 1483 | ** will correctly re-select local-mode. |
| 1484 | */ |
| 1485 | if( bLocalMode ){ |
| 1486 | zName = "ckout"; |
| 1487 | } |
| 1488 | if( diffType!=0 ){ |
| 1489 | @ %z(chref("button","%R/%s/%T?diff=0&ef=%s",zPageHide,zName,zExtra))\ |
| 1490 | @ Hide Diffs</a> |
| 1491 | } |
| 1492 | if( diffType!=1 ){ |
| 1493 | @ %z(chref("button","%R/%s/%T?diff=1%s&ef=%s",zPage,zName,zW,zExtra))\ |
| 1494 | @ Unified Diffs</a> |
| 1495 | } |
| 1496 | if( diffType!=2 ){ |
| 1497 | @ %z(chref("button","%R/%s/%T?diff=2%s&ef=%s",zPage,zName,zW,zExtra))\ |
| 1498 | @ Side-by-Side Diffs</a> |
| 1499 | } |
| 1500 | if( diffType!=0 ){ |
| 1501 | if( *zW ){ |
| 1502 | @ %z(chref("button","%R/%s/%T?diff=%d&ef=%s",zPage,zName,diffType,zExtra)) |
| 1503 | @ Show Whitespace Changes</a> |
| 1504 | }else{ |
| 1505 | char *button = chref("button","%R/%s/%T?diff=%d&w&ef=%s", |
| 1506 | zPage,zName,diffType,zExtra); |
| 1507 | @ %z(button) |
| 1508 | @ Ignore Whitespace</a> |
| 1509 | } |
| 1510 | } |
| 1511 | if( bLocalMode ){ |
| 1512 | @ %z(chref("button","%R/localpatch")) Patch</a> |
| @@ -1184,36 +1516,46 @@ | |
| 1516 | @ Patch</a> |
| 1517 | } |
| 1518 | } |
| 1519 | if( g.perm.Admin ){ |
| 1520 | @ %z(chref("button","%R/mlink?ci=%!S",zUuid))MLink Table</a> |
| 1521 | } |
| 1522 | if( bLocalMode ){ |
| 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> |
| 1534 | if( pRe ){ |
| 1535 | @ <p><b>Only differences that match regular expression "%h(zRe)" |
| 1536 | @ are shown.</b></p> |
| 1537 | } |
| 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, |
| 1549 | "SELECT pathname, deleted, chnged , rid==0, rid, islink" |
| 1550 | " FROM vfile" |
| 1551 | " WHERE vid=%d" |
| 1552 | " AND (deleted OR chnged OR rid==0)" |
| 1553 | " ORDER BY pathname /*scan*/", |
| 1554 | vid |
| 1555 | ); |
| 1556 | |
| 1557 | /* To prevent single-line diff-entries (those without "diff-blocks") from |
| 1558 | ** getting "lost", there's an optional "two-pass" mode for processing |
| 1559 | ** differences. If enabled, the first pass will only show one-line entries |
| 1560 | ** and the second pass will only show those with diff-blocks. This has the |
| 1561 | ** side-effect of altering the order entries are shown in (but within each |
| @@ -1222,10 +1564,13 @@ | |
| 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); |
| @@ -1232,17 +1577,17 @@ | |
| 1577 | int isChnged = db_column_int(&q3,2); |
| 1578 | int isNew = db_column_int(&q3,3); |
| 1579 | int srcid = db_column_int(&q3, 4); |
| 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," |
| @@ -1630,15 +1975,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 | } |
| 1992 |