Fossil SCM

Add the ml= and so= query parameters to /timeline. Enhance the graph layout algorithm so that any use of tl=, rl=, ml= or similar causes the named branches to appear on the left side of the graph, in the order specified (unless overridden by so=.)

drh 2024-12-24 12:53 trunk merge
Commit 055cef4b41897b26aa165dd7a7b5a56c57c9ac908f7b79ebb5e28299c97ca105
3 files changed +20 -7 +7 -5 +42 -24
+20 -7
--- src/graph.c
+++ src/graph.c
@@ -979,10 +979,11 @@
979979
** rare so this should not be a serious limitation to the algorithm.
980980
*/
981981
aMap = p->aiRailMap;
982982
for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
983983
if( nTimewarp==0 ){
984
+ int kk;
984985
/* Priority bits:
985986
**
986987
** 0x04 The preferred branch
987988
**
988989
** 0x02 A merge rail - a rail that contains merge lines into
@@ -990,16 +991,20 @@
990991
** is defined. This improves the display of r=BRANCH
991992
** options to /timeline.
992993
**
993994
** 0x01 A rail that merges with the preferred branch
994995
*/
995
- u8 aPriority[GR_MAX_RAIL];
996
- memset(aPriority, 0, p->mxRail+1);
996
+ u16 aPriority[GR_MAX_RAIL];
997
+ int mxMatch = 0;
998
+ memset(aPriority, 0, (p->mxRail+1)*sizeof(aPriority[0]));
997999
if( pLeftBranch ){
9981000
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
999
- if( match_text(pLeftBranch, pRow->zBranch) ){
1000
- aPriority[pRow->iRail] |= 4;
1001
+ int iMatch = match_text(pLeftBranch, pRow->zBranch);
1002
+ if( iMatch>0 ){
1003
+ if( iMatch>10 ) iMatch = 10;
1004
+ aPriority[pRow->iRail] |= 1<<(iMatch+1);
1005
+ if( mxMatch<iMatch ) mxMatch = iMatch;
10011006
for(i=0; i<=p->mxRail; i++){
10021007
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
10031008
}
10041009
if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
10051010
}
@@ -1010,10 +1015,11 @@
10101015
}
10111016
}
10121017
}else{
10131018
j = 1;
10141019
aPriority[0] = 4;
1020
+ mxMatch = 1;
10151021
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
10161022
if( pRow->iRail==0 ){
10171023
for(i=0; i<=p->mxRail; i++){
10181024
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
10191025
}
@@ -1023,17 +1029,24 @@
10231029
}
10241030
10251031
#if 0
10261032
fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
10271033
fprintf(stderr,"Priority:");
1028
- for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]);
1034
+ for(i=0; i<=p->mxRail; i++){
1035
+ fprintf(stderr," %x.%x",
1036
+ aPriority[i]/4, aPriority[i]&3);
1037
+ }
10291038
fprintf(stderr,"\n");
10301039
#endif
10311040
10321041
j = 0;
1033
- for(i=0; i<=p->mxRail; i++){
1034
- if( aPriority[i]>=4 ) aMap[i] = j++;
1042
+ for(kk=4; kk<=1<<(mxMatch+1); kk*=2){
1043
+ for(i=0; i<=p->mxRail; i++){
1044
+ if( aPriority[i]>=kk && aPriority[i]<kk*2 ){
1045
+ aMap[i] = j++;
1046
+ }
1047
+ }
10351048
}
10361049
for(i=p->mxRail; i>=0; i--){
10371050
if( aPriority[i]==3 ) aMap[i] = j++;
10381051
}
10391052
for(i=0; i<=p->mxRail; i++){
10401053
--- src/graph.c
+++ src/graph.c
@@ -979,10 +979,11 @@
979 ** rare so this should not be a serious limitation to the algorithm.
980 */
981 aMap = p->aiRailMap;
982 for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
983 if( nTimewarp==0 ){
 
984 /* Priority bits:
985 **
986 ** 0x04 The preferred branch
987 **
988 ** 0x02 A merge rail - a rail that contains merge lines into
@@ -990,16 +991,20 @@
990 ** is defined. This improves the display of r=BRANCH
991 ** options to /timeline.
992 **
993 ** 0x01 A rail that merges with the preferred branch
994 */
995 u8 aPriority[GR_MAX_RAIL];
996 memset(aPriority, 0, p->mxRail+1);
 
997 if( pLeftBranch ){
998 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
999 if( match_text(pLeftBranch, pRow->zBranch) ){
1000 aPriority[pRow->iRail] |= 4;
 
 
 
1001 for(i=0; i<=p->mxRail; i++){
1002 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1003 }
1004 if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
1005 }
@@ -1010,10 +1015,11 @@
1010 }
1011 }
1012 }else{
1013 j = 1;
1014 aPriority[0] = 4;
 
1015 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
1016 if( pRow->iRail==0 ){
1017 for(i=0; i<=p->mxRail; i++){
1018 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1019 }
@@ -1023,17 +1029,24 @@
1023 }
1024
1025 #if 0
1026 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
1027 fprintf(stderr,"Priority:");
1028 for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]);
 
 
 
1029 fprintf(stderr,"\n");
1030 #endif
1031
1032 j = 0;
1033 for(i=0; i<=p->mxRail; i++){
1034 if( aPriority[i]>=4 ) aMap[i] = j++;
 
 
 
 
1035 }
1036 for(i=p->mxRail; i>=0; i--){
1037 if( aPriority[i]==3 ) aMap[i] = j++;
1038 }
1039 for(i=0; i<=p->mxRail; i++){
1040
--- src/graph.c
+++ src/graph.c
@@ -979,10 +979,11 @@
979 ** rare so this should not be a serious limitation to the algorithm.
980 */
981 aMap = p->aiRailMap;
982 for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
983 if( nTimewarp==0 ){
984 int kk;
985 /* Priority bits:
986 **
987 ** 0x04 The preferred branch
988 **
989 ** 0x02 A merge rail - a rail that contains merge lines into
@@ -990,16 +991,20 @@
991 ** is defined. This improves the display of r=BRANCH
992 ** options to /timeline.
993 **
994 ** 0x01 A rail that merges with the preferred branch
995 */
996 u16 aPriority[GR_MAX_RAIL];
997 int mxMatch = 0;
998 memset(aPriority, 0, (p->mxRail+1)*sizeof(aPriority[0]));
999 if( pLeftBranch ){
1000 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
1001 int iMatch = match_text(pLeftBranch, pRow->zBranch);
1002 if( iMatch>0 ){
1003 if( iMatch>10 ) iMatch = 10;
1004 aPriority[pRow->iRail] |= 1<<(iMatch+1);
1005 if( mxMatch<iMatch ) mxMatch = iMatch;
1006 for(i=0; i<=p->mxRail; i++){
1007 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1008 }
1009 if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
1010 }
@@ -1010,10 +1015,11 @@
1015 }
1016 }
1017 }else{
1018 j = 1;
1019 aPriority[0] = 4;
1020 mxMatch = 1;
1021 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
1022 if( pRow->iRail==0 ){
1023 for(i=0; i<=p->mxRail; i++){
1024 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1025 }
@@ -1023,17 +1029,24 @@
1029 }
1030
1031 #if 0
1032 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
1033 fprintf(stderr,"Priority:");
1034 for(i=0; i<=p->mxRail; i++){
1035 fprintf(stderr," %x.%x",
1036 aPriority[i]/4, aPriority[i]&3);
1037 }
1038 fprintf(stderr,"\n");
1039 #endif
1040
1041 j = 0;
1042 for(kk=4; kk<=1<<(mxMatch+1); kk*=2){
1043 for(i=0; i<=p->mxRail; i++){
1044 if( aPriority[i]>=kk && aPriority[i]<kk*2 ){
1045 aMap[i] = j++;
1046 }
1047 }
1048 }
1049 for(i=p->mxRail; i>=0; i--){
1050 if( aPriority[i]==3 ) aMap[i] = j++;
1051 }
1052 for(i=0; i<=p->mxRail; i++){
1053
+7 -5
--- src/match.c
+++ src/match.c
@@ -147,10 +147,13 @@
147147
}
148148
149149
/*
150150
** Return non-zero (true) if the input string matches the pattern
151151
** described by the matcher.
152
+**
153
+** The return value is really the 1-based index of the particular
154
+** pattern that matched.
152155
*/
153156
int match_text(Matcher *p, const char *zText){
154157
int i;
155158
if( p==0 ){
156159
return zText==0;
@@ -157,33 +160,32 @@
157160
}
158161
switch( p->style ){
159162
case MS_BRLIST:
160163
case MS_EXACT: {
161164
for(i=0; i<p->nPattern; i++){
162
- if( strcmp(p->azPattern[i], zText)==0 ) return 1;
165
+ if( strcmp(p->azPattern[i], zText)==0 ) return i+1;
163166
}
164167
break;
165168
}
166169
case MS_GLOB: {
167170
for(i=0; i<p->nPattern; i++){
168
- if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return 1;
171
+ if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return i+1;
169172
}
170173
break;
171174
}
172175
case MS_LIKE: {
173176
for(i=0; i<p->nPattern; i++){
174
- if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return 1;
177
+ if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return i+1;
175178
}
176179
break;
177180
}
178181
case MS_REGEXP: {
179182
int nText = (int)strlen(zText);
180183
for(i=0; i<p->nPattern; i++){
181
- if( re_match(p->aRe[i], (const u8*)zText, nText) ) return 1;
184
+ if( re_match(p->aRe[i], (const u8*)zText, nText) ) return i+1;
182185
}
183186
break;
184
-
185187
}
186188
}
187189
return 0;
188190
}
189191
190192
--- src/match.c
+++ src/match.c
@@ -147,10 +147,13 @@
147 }
148
149 /*
150 ** Return non-zero (true) if the input string matches the pattern
151 ** described by the matcher.
 
 
 
152 */
153 int match_text(Matcher *p, const char *zText){
154 int i;
155 if( p==0 ){
156 return zText==0;
@@ -157,33 +160,32 @@
157 }
158 switch( p->style ){
159 case MS_BRLIST:
160 case MS_EXACT: {
161 for(i=0; i<p->nPattern; i++){
162 if( strcmp(p->azPattern[i], zText)==0 ) return 1;
163 }
164 break;
165 }
166 case MS_GLOB: {
167 for(i=0; i<p->nPattern; i++){
168 if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return 1;
169 }
170 break;
171 }
172 case MS_LIKE: {
173 for(i=0; i<p->nPattern; i++){
174 if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return 1;
175 }
176 break;
177 }
178 case MS_REGEXP: {
179 int nText = (int)strlen(zText);
180 for(i=0; i<p->nPattern; i++){
181 if( re_match(p->aRe[i], (const u8*)zText, nText) ) return 1;
182 }
183 break;
184
185 }
186 }
187 return 0;
188 }
189
190
--- src/match.c
+++ src/match.c
@@ -147,10 +147,13 @@
147 }
148
149 /*
150 ** Return non-zero (true) if the input string matches the pattern
151 ** described by the matcher.
152 **
153 ** The return value is really the 1-based index of the particular
154 ** pattern that matched.
155 */
156 int match_text(Matcher *p, const char *zText){
157 int i;
158 if( p==0 ){
159 return zText==0;
@@ -157,33 +160,32 @@
160 }
161 switch( p->style ){
162 case MS_BRLIST:
163 case MS_EXACT: {
164 for(i=0; i<p->nPattern; i++){
165 if( strcmp(p->azPattern[i], zText)==0 ) return i+1;
166 }
167 break;
168 }
169 case MS_GLOB: {
170 for(i=0; i<p->nPattern; i++){
171 if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return i+1;
172 }
173 break;
174 }
175 case MS_LIKE: {
176 for(i=0; i<p->nPattern; i++){
177 if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return i+1;
178 }
179 break;
180 }
181 case MS_REGEXP: {
182 int nText = (int)strlen(zText);
183 for(i=0; i<p->nPattern; i++){
184 if( re_match(p->aRe[i], (const u8*)zText, nText) ) return i+1;
185 }
186 break;
 
187 }
188 }
189 return 0;
190 }
191
192
+42 -24
--- src/timeline.c
+++ src/timeline.c
@@ -1515,15 +1515,15 @@
15151515
/*
15161516
** WEBPAGE: timeline
15171517
**
15181518
** Query parameters:
15191519
**
1520
-** a=TIMEORTAG Show events after TIMEORTAG
1521
-** b=TIMEORTAG Show events before TIMEORTAG
1520
+** a=TIMEORTAG Show events after TIMEORTAG.
1521
+** b=TIMEORTAG Show events before TIMEORTAG.
15221522
** c=TIMEORTAG Show events that happen "circa" TIMEORTAG
15231523
** cf=FILEHASH Show events around the time of the first use of
1524
-** the file with FILEHASH
1524
+** the file with FILEHASH.
15251525
** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available
15261526
** event if TIMEORTAG is not part of the timeline. If
15271527
** the t= or r= is used, the m event is added to the timeline
15281528
** if it isn't there already.
15291529
** x=LIST Show check-ins in the comma- or space-separated LIST
@@ -1549,30 +1549,33 @@
15491549
** from=CX ... shortest path from CX back to CHECKIN
15501550
** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
15511551
** d=CX ... from CX up to the time of CHECKIN
15521552
** from=CX ... shortest path from CX up to CHECKIN
15531553
** t=TAG Show only check-ins with the given TAG
1554
-** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel
1555
-** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'
1556
-** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'
1554
+** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
1555
+** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
1556
+** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List"
1557
+** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List"
1558
+** so=TAGLIST "Sort Order". Show TAGLIST branches ordered left to right.
15571559
** rel Show related check-ins as well as those matching t=TAG
1558
-** mionly Limit rel to show ancestors but not descendants
1560
+** mionly Show related parents but not related children.
15591561
** nowiki Do not show wiki associated with branch or tag
1560
-** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP
1562
+** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
1563
+** "like", or "regexp".
15611564
** u=USER Only show items associated with USER
15621565
** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
15631566
** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1564
-** x: "Classic".
1567
+* x: "Classic".
15651568
** advm Use the "Advanced" or "Busy" menu design.
15661569
** ng No Graph.
15671570
** ncp Omit cherrypick merges
15681571
** nd Do not highlight the focus check-in
15691572
** nsm Omit the submenu
15701573
** nc Omit all graph colors other than highlights
15711574
** v Show details of files changed
15721575
** vfx Show complete text of forum messages
1573
-** f=CHECKIN Show family (immediate parents and children) of CHECKIN
1576
+** f=CHECKIN Family (immediate parents and children) of CHECKIN
15741577
** from=CHECKIN Path through common ancestor from...
15751578
** to=CHECKIN ... to this
15761579
** to2=CHECKIN ... backup name if to= doesn't resolve
15771580
** shortest ... show only the shortest path
15781581
** rel ... also show related checkins
@@ -1580,14 +1583,14 @@
15801583
** ft=LATER ... path from CHECKIN forward to LATER
15811584
** me=CHECKIN Most direct path from...
15821585
** you=CHECKIN ... to this
15831586
** rel ... also show related checkins
15841587
** uf=FILE_HASH Show only check-ins that contain the given file version
1585
-** All qualifying check-ins are shown unless there is
1586
-** also an n= or n1= query parameter.
1588
+** All qualifying check-ins are shown unless there is
1589
+** also an n= or n1= query parameter.
15871590
** chng=GLOBLIST Show only check-ins that involve changes to a file whose
1588
-** name matches one of the comma-separate GLOBLIST
1591
+** name matches one of the comma-separate GLOBLIST
15891592
** brbg Background color determined by branch name
15901593
** ubg Background color determined by user
15911594
** deltabg Background color red for delta manifests or green
15921595
** for baseline manifests
15931596
** namechng Show only check-ins that have filename changes
@@ -1689,10 +1692,13 @@
16891692
16901693
login_check_credentials();
16911694
url_initialize(&url, "timeline");
16921695
cgi_query_parameters_to_url(&url);
16931696
blob_init(&allSql, 0, 0);
1697
+
1698
+ /* The "mionly" query parameter is like "rel", but shows merge-ins only */
1699
+ if( P("mionly")!=0 ) related = 2;
16941700
16951701
(void)P_NoBot("ss")
16961702
/* "ss" is processed via the udc but at least one spider likes to
16971703
** try to SQL inject via this argument, so let's catch that. */;
16981704
@@ -1809,26 +1815,31 @@
18091815
** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
18101816
if( zBrName==0 && zTagName==0 ){
18111817
const char *z;
18121818
if( (z = P("tl"))!=0 ){
18131819
zTagName = z;
1814
- zMatchStyle = "brlist";
1815
- }
1820
+ if( zMatchStyle==0 ) zMatchStyle = "brlist";
1821
+ }else
18161822
if( (z = P("rl"))!=0 ){
18171823
zBrName = z;
1818
- related = 1;
1819
- zMatchStyle = "brlist";
1824
+ if( related==0 ) related = 1;
1825
+ if( zMatchStyle==0 ) zMatchStyle = "brlist";
1826
+ }else
1827
+ if( (z = P("ml"))!=0 ){
1828
+ zBrName = z;
1829
+ if( related==0 ) related = 2;
1830
+ if( zMatchStyle==0 ) zMatchStyle = "brlist";
18201831
}
18211832
}
18221833
18231834
/* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
18241835
if( zBrName ){
18251836
cgi_delete_query_parameter("r");
18261837
cgi_set_query_parameter("t", zBrName); (void)P("t");
18271838
cgi_set_query_parameter("rel", "1");
18281839
zTagName = zBrName;
1829
- related = 1;
1840
+ if( related==0 ) related = 1;
18301841
zType = "ci";
18311842
}
18321843
18331844
/* Ignore empty tag query strings. */
18341845
if( zTagName && !*zTagName ){
@@ -2088,17 +2099,17 @@
20882099
}
20892100
}
20902101
path_reset();
20912102
db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
20922103
blob_reset(&ins);
2093
- if( related || P("mionly") ){
2104
+ if( related ){
20942105
db_multi_exec(
20952106
"CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
20962107
"INSERT OR IGNORE INTO related(x)"
20972108
" SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
20982109
);
2099
- if( P("mionly")==0 ){
2110
+ if( related==1 ){
21002111
db_multi_exec(
21012112
"INSERT OR IGNORE INTO related(x)"
21022113
" SELECT cid FROM plink WHERE pid IN pathnode;"
21032114
);
21042115
}
@@ -2105,11 +2116,11 @@
21052116
if( showCherrypicks ){
21062117
db_multi_exec(
21072118
"INSERT OR IGNORE INTO related(x)"
21082119
" SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
21092120
);
2110
- if( P("mionly")==0 ){
2121
+ if( related==1 ){
21112122
db_multi_exec(
21122123
"INSERT OR IGNORE INTO related(x)"
21132124
" SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
21142125
);
21152126
}
@@ -2535,11 +2546,11 @@
25352546
int ridMark = name_to_rid(zMark);
25362547
db_multi_exec(
25372548
"INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
25382549
}
25392550
add_extra_rids("selected_nodes",P("x"));
2540
- if( !related && !PB("mionly") ){
2551
+ if( related==0 ){
25412552
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
25422553
}else{
25432554
db_multi_exec(
25442555
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
25452556
"INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
@@ -2554,11 +2565,11 @@
25542565
db_multi_exec(
25552566
"INSERT OR IGNORE INTO related_nodes\n"
25562567
" SELECT pid FROM selected_nodes CROSS JOIN plink\n"
25572568
" WHERE selected_nodes.rid=plink.cid;"
25582569
);
2559
- if( P("mionly")==0 ){
2570
+ if( related==1 ){
25602571
db_multi_exec(
25612572
"INSERT OR IGNORE INTO related_nodes\n"
25622573
" SELECT cid FROM selected_nodes CROSS JOIN plink\n"
25632574
" WHERE selected_nodes.rid=plink.pid;"
25642575
);
@@ -2915,11 +2926,18 @@
29152926
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
29162927
@ &nbsp;&uarr;</a>
29172928
}
29182929
cgi_check_for_malice();
29192930
{
2920
- Matcher *pLeftBranch = match_create(matchStyle, zBrName);
2931
+ Matcher *pLeftBranch;
2932
+ if( P("so")!=0 ){
2933
+ pLeftBranch = match_create(MS_BRLIST, P("so"));
2934
+ }else if( zBrName ){
2935
+ pLeftBranch = match_create(matchStyle, zBrName);
2936
+ }else{
2937
+ pLeftBranch = match_create(matchStyle, zTagName);
2938
+ }
29212939
www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
29222940
selectedRid, secondaryRid, 0);
29232941
match_free(pLeftBranch);
29242942
}
29252943
db_finalize(&q);
29262944
--- src/timeline.c
+++ src/timeline.c
@@ -1515,15 +1515,15 @@
1515 /*
1516 ** WEBPAGE: timeline
1517 **
1518 ** Query parameters:
1519 **
1520 ** a=TIMEORTAG Show events after TIMEORTAG
1521 ** b=TIMEORTAG Show events before TIMEORTAG
1522 ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG
1523 ** cf=FILEHASH Show events around the time of the first use of
1524 ** the file with FILEHASH
1525 ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available
1526 ** event if TIMEORTAG is not part of the timeline. If
1527 ** the t= or r= is used, the m event is added to the timeline
1528 ** if it isn't there already.
1529 ** x=LIST Show check-ins in the comma- or space-separated LIST
@@ -1549,30 +1549,33 @@
1549 ** from=CX ... shortest path from CX back to CHECKIN
1550 ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
1551 ** d=CX ... from CX up to the time of CHECKIN
1552 ** from=CX ... shortest path from CX up to CHECKIN
1553 ** t=TAG Show only check-ins with the given TAG
1554 ** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel
1555 ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'
1556 ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'
 
 
1557 ** rel Show related check-ins as well as those matching t=TAG
1558 ** mionly Limit rel to show ancestors but not descendants
1559 ** nowiki Do not show wiki associated with branch or tag
1560 ** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP
 
1561 ** u=USER Only show items associated with USER
1562 ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
1563 ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1564 ** x: "Classic".
1565 ** advm Use the "Advanced" or "Busy" menu design.
1566 ** ng No Graph.
1567 ** ncp Omit cherrypick merges
1568 ** nd Do not highlight the focus check-in
1569 ** nsm Omit the submenu
1570 ** nc Omit all graph colors other than highlights
1571 ** v Show details of files changed
1572 ** vfx Show complete text of forum messages
1573 ** f=CHECKIN Show family (immediate parents and children) of CHECKIN
1574 ** from=CHECKIN Path through common ancestor from...
1575 ** to=CHECKIN ... to this
1576 ** to2=CHECKIN ... backup name if to= doesn't resolve
1577 ** shortest ... show only the shortest path
1578 ** rel ... also show related checkins
@@ -1580,14 +1583,14 @@
1580 ** ft=LATER ... path from CHECKIN forward to LATER
1581 ** me=CHECKIN Most direct path from...
1582 ** you=CHECKIN ... to this
1583 ** rel ... also show related checkins
1584 ** uf=FILE_HASH Show only check-ins that contain the given file version
1585 ** All qualifying check-ins are shown unless there is
1586 ** also an n= or n1= query parameter.
1587 ** chng=GLOBLIST Show only check-ins that involve changes to a file whose
1588 ** name matches one of the comma-separate GLOBLIST
1589 ** brbg Background color determined by branch name
1590 ** ubg Background color determined by user
1591 ** deltabg Background color red for delta manifests or green
1592 ** for baseline manifests
1593 ** namechng Show only check-ins that have filename changes
@@ -1689,10 +1692,13 @@
1689
1690 login_check_credentials();
1691 url_initialize(&url, "timeline");
1692 cgi_query_parameters_to_url(&url);
1693 blob_init(&allSql, 0, 0);
 
 
 
1694
1695 (void)P_NoBot("ss")
1696 /* "ss" is processed via the udc but at least one spider likes to
1697 ** try to SQL inject via this argument, so let's catch that. */;
1698
@@ -1809,26 +1815,31 @@
1809 ** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
1810 if( zBrName==0 && zTagName==0 ){
1811 const char *z;
1812 if( (z = P("tl"))!=0 ){
1813 zTagName = z;
1814 zMatchStyle = "brlist";
1815 }
1816 if( (z = P("rl"))!=0 ){
1817 zBrName = z;
1818 related = 1;
1819 zMatchStyle = "brlist";
 
 
 
 
 
1820 }
1821 }
1822
1823 /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
1824 if( zBrName ){
1825 cgi_delete_query_parameter("r");
1826 cgi_set_query_parameter("t", zBrName); (void)P("t");
1827 cgi_set_query_parameter("rel", "1");
1828 zTagName = zBrName;
1829 related = 1;
1830 zType = "ci";
1831 }
1832
1833 /* Ignore empty tag query strings. */
1834 if( zTagName && !*zTagName ){
@@ -2088,17 +2099,17 @@
2088 }
2089 }
2090 path_reset();
2091 db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
2092 blob_reset(&ins);
2093 if( related || P("mionly") ){
2094 db_multi_exec(
2095 "CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
2096 "INSERT OR IGNORE INTO related(x)"
2097 " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
2098 );
2099 if( P("mionly")==0 ){
2100 db_multi_exec(
2101 "INSERT OR IGNORE INTO related(x)"
2102 " SELECT cid FROM plink WHERE pid IN pathnode;"
2103 );
2104 }
@@ -2105,11 +2116,11 @@
2105 if( showCherrypicks ){
2106 db_multi_exec(
2107 "INSERT OR IGNORE INTO related(x)"
2108 " SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
2109 );
2110 if( P("mionly")==0 ){
2111 db_multi_exec(
2112 "INSERT OR IGNORE INTO related(x)"
2113 " SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
2114 );
2115 }
@@ -2535,11 +2546,11 @@
2535 int ridMark = name_to_rid(zMark);
2536 db_multi_exec(
2537 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2538 }
2539 add_extra_rids("selected_nodes",P("x"));
2540 if( !related && !PB("mionly") ){
2541 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2542 }else{
2543 db_multi_exec(
2544 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
2545 "INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
@@ -2554,11 +2565,11 @@
2554 db_multi_exec(
2555 "INSERT OR IGNORE INTO related_nodes\n"
2556 " SELECT pid FROM selected_nodes CROSS JOIN plink\n"
2557 " WHERE selected_nodes.rid=plink.cid;"
2558 );
2559 if( P("mionly")==0 ){
2560 db_multi_exec(
2561 "INSERT OR IGNORE INTO related_nodes\n"
2562 " SELECT cid FROM selected_nodes CROSS JOIN plink\n"
2563 " WHERE selected_nodes.rid=plink.pid;"
2564 );
@@ -2915,11 +2926,18 @@
2915 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
2916 @ &nbsp;&uarr;</a>
2917 }
2918 cgi_check_for_malice();
2919 {
2920 Matcher *pLeftBranch = match_create(matchStyle, zBrName);
 
 
 
 
 
 
 
2921 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
2922 selectedRid, secondaryRid, 0);
2923 match_free(pLeftBranch);
2924 }
2925 db_finalize(&q);
2926
--- src/timeline.c
+++ src/timeline.c
@@ -1515,15 +1515,15 @@
1515 /*
1516 ** WEBPAGE: timeline
1517 **
1518 ** Query parameters:
1519 **
1520 ** a=TIMEORTAG Show events after TIMEORTAG.
1521 ** b=TIMEORTAG Show events before TIMEORTAG.
1522 ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG
1523 ** cf=FILEHASH Show events around the time of the first use of
1524 ** the file with FILEHASH.
1525 ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available
1526 ** event if TIMEORTAG is not part of the timeline. If
1527 ** the t= or r= is used, the m event is added to the timeline
1528 ** if it isn't there already.
1529 ** x=LIST Show check-ins in the comma- or space-separated LIST
@@ -1549,30 +1549,33 @@
1549 ** from=CX ... shortest path from CX back to CHECKIN
1550 ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
1551 ** d=CX ... from CX up to the time of CHECKIN
1552 ** from=CX ... shortest path from CX up to CHECKIN
1553 ** t=TAG Show only check-ins with the given TAG
1554 ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
1555 ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
1556 ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List"
1557 ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List"
1558 ** so=TAGLIST "Sort Order". Show TAGLIST branches ordered left to right.
1559 ** rel Show related check-ins as well as those matching t=TAG
1560 ** mionly Show related parents but not related children.
1561 ** nowiki Do not show wiki associated with branch or tag
1562 ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
1563 ** "like", or "regexp".
1564 ** u=USER Only show items associated with USER
1565 ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
1566 ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1567 * x: "Classic".
1568 ** advm Use the "Advanced" or "Busy" menu design.
1569 ** ng No Graph.
1570 ** ncp Omit cherrypick merges
1571 ** nd Do not highlight the focus check-in
1572 ** nsm Omit the submenu
1573 ** nc Omit all graph colors other than highlights
1574 ** v Show details of files changed
1575 ** vfx Show complete text of forum messages
1576 ** f=CHECKIN Family (immediate parents and children) of CHECKIN
1577 ** from=CHECKIN Path through common ancestor from...
1578 ** to=CHECKIN ... to this
1579 ** to2=CHECKIN ... backup name if to= doesn't resolve
1580 ** shortest ... show only the shortest path
1581 ** rel ... also show related checkins
@@ -1580,14 +1583,14 @@
1583 ** ft=LATER ... path from CHECKIN forward to LATER
1584 ** me=CHECKIN Most direct path from...
1585 ** you=CHECKIN ... to this
1586 ** rel ... also show related checkins
1587 ** uf=FILE_HASH Show only check-ins that contain the given file version
1588 ** All qualifying check-ins are shown unless there is
1589 ** also an n= or n1= query parameter.
1590 ** chng=GLOBLIST Show only check-ins that involve changes to a file whose
1591 ** name matches one of the comma-separate GLOBLIST
1592 ** brbg Background color determined by branch name
1593 ** ubg Background color determined by user
1594 ** deltabg Background color red for delta manifests or green
1595 ** for baseline manifests
1596 ** namechng Show only check-ins that have filename changes
@@ -1689,10 +1692,13 @@
1692
1693 login_check_credentials();
1694 url_initialize(&url, "timeline");
1695 cgi_query_parameters_to_url(&url);
1696 blob_init(&allSql, 0, 0);
1697
1698 /* The "mionly" query parameter is like "rel", but shows merge-ins only */
1699 if( P("mionly")!=0 ) related = 2;
1700
1701 (void)P_NoBot("ss")
1702 /* "ss" is processed via the udc but at least one spider likes to
1703 ** try to SQL inject via this argument, so let's catch that. */;
1704
@@ -1809,26 +1815,31 @@
1815 ** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
1816 if( zBrName==0 && zTagName==0 ){
1817 const char *z;
1818 if( (z = P("tl"))!=0 ){
1819 zTagName = z;
1820 if( zMatchStyle==0 ) zMatchStyle = "brlist";
1821 }else
1822 if( (z = P("rl"))!=0 ){
1823 zBrName = z;
1824 if( related==0 ) related = 1;
1825 if( zMatchStyle==0 ) zMatchStyle = "brlist";
1826 }else
1827 if( (z = P("ml"))!=0 ){
1828 zBrName = z;
1829 if( related==0 ) related = 2;
1830 if( zMatchStyle==0 ) zMatchStyle = "brlist";
1831 }
1832 }
1833
1834 /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
1835 if( zBrName ){
1836 cgi_delete_query_parameter("r");
1837 cgi_set_query_parameter("t", zBrName); (void)P("t");
1838 cgi_set_query_parameter("rel", "1");
1839 zTagName = zBrName;
1840 if( related==0 ) related = 1;
1841 zType = "ci";
1842 }
1843
1844 /* Ignore empty tag query strings. */
1845 if( zTagName && !*zTagName ){
@@ -2088,17 +2099,17 @@
2099 }
2100 }
2101 path_reset();
2102 db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
2103 blob_reset(&ins);
2104 if( related ){
2105 db_multi_exec(
2106 "CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
2107 "INSERT OR IGNORE INTO related(x)"
2108 " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
2109 );
2110 if( related==1 ){
2111 db_multi_exec(
2112 "INSERT OR IGNORE INTO related(x)"
2113 " SELECT cid FROM plink WHERE pid IN pathnode;"
2114 );
2115 }
@@ -2105,11 +2116,11 @@
2116 if( showCherrypicks ){
2117 db_multi_exec(
2118 "INSERT OR IGNORE INTO related(x)"
2119 " SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
2120 );
2121 if( related==1 ){
2122 db_multi_exec(
2123 "INSERT OR IGNORE INTO related(x)"
2124 " SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
2125 );
2126 }
@@ -2535,11 +2546,11 @@
2546 int ridMark = name_to_rid(zMark);
2547 db_multi_exec(
2548 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2549 }
2550 add_extra_rids("selected_nodes",P("x"));
2551 if( related==0 ){
2552 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2553 }else{
2554 db_multi_exec(
2555 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
2556 "INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
@@ -2554,11 +2565,11 @@
2565 db_multi_exec(
2566 "INSERT OR IGNORE INTO related_nodes\n"
2567 " SELECT pid FROM selected_nodes CROSS JOIN plink\n"
2568 " WHERE selected_nodes.rid=plink.cid;"
2569 );
2570 if( related==1 ){
2571 db_multi_exec(
2572 "INSERT OR IGNORE INTO related_nodes\n"
2573 " SELECT cid FROM selected_nodes CROSS JOIN plink\n"
2574 " WHERE selected_nodes.rid=plink.pid;"
2575 );
@@ -2915,11 +2926,18 @@
2926 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
2927 @ &nbsp;&uarr;</a>
2928 }
2929 cgi_check_for_malice();
2930 {
2931 Matcher *pLeftBranch;
2932 if( P("so")!=0 ){
2933 pLeftBranch = match_create(MS_BRLIST, P("so"));
2934 }else if( zBrName ){
2935 pLeftBranch = match_create(matchStyle, zBrName);
2936 }else{
2937 pLeftBranch = match_create(matchStyle, zTagName);
2938 }
2939 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
2940 selectedRid, secondaryRid, 0);
2941 match_free(pLeftBranch);
2942 }
2943 db_finalize(&q);
2944

Keyboard Shortcuts

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