Fossil SCM

Add a path-tracing option to the timeline display.

drh 2011-03-09 17:33 trunk
Commit 63ac111d5baf713827bef1a5dc31fb9a34cf410f
2 files changed +18 -4 +39 -1
+18 -4
--- src/bisect.c
+++ src/bisect.c
@@ -21,13 +21,13 @@
2121
*/
2222
#include "config.h"
2323
#include "bisect.h"
2424
#include <assert.h>
2525
26
+#if INTERFACE
2627
/* Nodes for the shortest path algorithm.
2728
*/
28
-typedef struct BisectNode BisectNode;
2929
struct BisectNode {
3030
int rid; /* ID for this node */
3131
int fromIsParent; /* True if pFrom is the parent of rid */
3232
BisectNode *pFrom; /* Node we came from */
3333
union {
@@ -34,10 +34,11 @@
3434
BisectNode *pPeer; /* List of nodes of the same generation */
3535
BisectNode *pTo; /* Next on path from beginning to end */
3636
} u;
3737
BisectNode *pAll; /* List of all nodes */
3838
};
39
+#endif
3940
4041
/*
4142
** Local variables for this module
4243
*/
4344
static struct {
@@ -71,11 +72,11 @@
7172
}
7273
7374
/*
7475
** Reset memory used by the shortest path algorithm.
7576
*/
76
-static void bisect_reset(void){
77
+void bisect_reset(void){
7778
BisectNode *p;
7879
while( bisect.pAll ){
7980
p = bisect.pAll;
8081
bisect.pAll = p->pAll;
8182
fossil_free(p);
@@ -91,11 +92,11 @@
9192
** Compute the shortest path from iFrom to iTo
9293
**
9394
** If directOnly is true, then use only the "primary" links from parent to
9495
** child. In other words, ignore merges.
9596
*/
96
-static BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){
97
+BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){
9798
Stmt s;
9899
BisectNode *pPrev;
99100
BisectNode *p;
100101
101102
bisect_reset();
@@ -139,17 +140,18 @@
139140
}
140141
141142
/*
142143
** Construct the path from bisect.pStart to bisect.pEnd in the u.pTo fields.
143144
*/
144
-static void bisect_reverse_path(void){
145
+BisectNode *bisect_reverse_path(void){
145146
BisectNode *p;
146147
for(p=bisect.pEnd; p && p->pFrom; p = p->pFrom){
147148
p->pFrom->u.pTo = p;
148149
}
149150
bisect.pEnd->u.pTo = 0;
150151
assert( p==bisect.pStart );
152
+ return p;
151153
}
152154
153155
/*
154156
** COMMAND: test-shortest-path
155157
**
@@ -189,10 +191,22 @@
189191
}else{
190192
printf("\n");
191193
}
192194
}
193195
}
196
+
197
+/*
198
+** WEBPAGE: path
199
+**
200
+** example: /path?from=trunk&to=experimental&nomerge
201
+**
202
+** Show a timeline of all changes along a path between two versions.
203
+*/
204
+void path_page(void){
205
+ login_check_credentials();
206
+ if( !g.okRead ){ login_needed(); return; }
207
+}
194208
195209
/*
196210
** Find the shortest path between bad and good.
197211
*/
198212
static BisectNode *bisect_path(void){
199213
--- src/bisect.c
+++ src/bisect.c
@@ -21,13 +21,13 @@
21 */
22 #include "config.h"
23 #include "bisect.h"
24 #include <assert.h>
25
 
26 /* Nodes for the shortest path algorithm.
27 */
28 typedef struct BisectNode BisectNode;
29 struct BisectNode {
30 int rid; /* ID for this node */
31 int fromIsParent; /* True if pFrom is the parent of rid */
32 BisectNode *pFrom; /* Node we came from */
33 union {
@@ -34,10 +34,11 @@
34 BisectNode *pPeer; /* List of nodes of the same generation */
35 BisectNode *pTo; /* Next on path from beginning to end */
36 } u;
37 BisectNode *pAll; /* List of all nodes */
38 };
 
39
40 /*
41 ** Local variables for this module
42 */
43 static struct {
@@ -71,11 +72,11 @@
71 }
72
73 /*
74 ** Reset memory used by the shortest path algorithm.
75 */
76 static void bisect_reset(void){
77 BisectNode *p;
78 while( bisect.pAll ){
79 p = bisect.pAll;
80 bisect.pAll = p->pAll;
81 fossil_free(p);
@@ -91,11 +92,11 @@
91 ** Compute the shortest path from iFrom to iTo
92 **
93 ** If directOnly is true, then use only the "primary" links from parent to
94 ** child. In other words, ignore merges.
95 */
96 static BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){
97 Stmt s;
98 BisectNode *pPrev;
99 BisectNode *p;
100
101 bisect_reset();
@@ -139,17 +140,18 @@
139 }
140
141 /*
142 ** Construct the path from bisect.pStart to bisect.pEnd in the u.pTo fields.
143 */
144 static void bisect_reverse_path(void){
145 BisectNode *p;
146 for(p=bisect.pEnd; p && p->pFrom; p = p->pFrom){
147 p->pFrom->u.pTo = p;
148 }
149 bisect.pEnd->u.pTo = 0;
150 assert( p==bisect.pStart );
 
151 }
152
153 /*
154 ** COMMAND: test-shortest-path
155 **
@@ -189,10 +191,22 @@
189 }else{
190 printf("\n");
191 }
192 }
193 }
 
 
 
 
 
 
 
 
 
 
 
 
194
195 /*
196 ** Find the shortest path between bad and good.
197 */
198 static BisectNode *bisect_path(void){
199
--- src/bisect.c
+++ src/bisect.c
@@ -21,13 +21,13 @@
21 */
22 #include "config.h"
23 #include "bisect.h"
24 #include <assert.h>
25
26 #if INTERFACE
27 /* Nodes for the shortest path algorithm.
28 */
 
29 struct BisectNode {
30 int rid; /* ID for this node */
31 int fromIsParent; /* True if pFrom is the parent of rid */
32 BisectNode *pFrom; /* Node we came from */
33 union {
@@ -34,10 +34,11 @@
34 BisectNode *pPeer; /* List of nodes of the same generation */
35 BisectNode *pTo; /* Next on path from beginning to end */
36 } u;
37 BisectNode *pAll; /* List of all nodes */
38 };
39 #endif
40
41 /*
42 ** Local variables for this module
43 */
44 static struct {
@@ -71,11 +72,11 @@
72 }
73
74 /*
75 ** Reset memory used by the shortest path algorithm.
76 */
77 void bisect_reset(void){
78 BisectNode *p;
79 while( bisect.pAll ){
80 p = bisect.pAll;
81 bisect.pAll = p->pAll;
82 fossil_free(p);
@@ -91,11 +92,11 @@
92 ** Compute the shortest path from iFrom to iTo
93 **
94 ** If directOnly is true, then use only the "primary" links from parent to
95 ** child. In other words, ignore merges.
96 */
97 BisectNode *bisect_shortest_path(int iFrom, int iTo, int directOnly){
98 Stmt s;
99 BisectNode *pPrev;
100 BisectNode *p;
101
102 bisect_reset();
@@ -139,17 +140,18 @@
140 }
141
142 /*
143 ** Construct the path from bisect.pStart to bisect.pEnd in the u.pTo fields.
144 */
145 BisectNode *bisect_reverse_path(void){
146 BisectNode *p;
147 for(p=bisect.pEnd; p && p->pFrom; p = p->pFrom){
148 p->pFrom->u.pTo = p;
149 }
150 bisect.pEnd->u.pTo = 0;
151 assert( p==bisect.pStart );
152 return p;
153 }
154
155 /*
156 ** COMMAND: test-shortest-path
157 **
@@ -189,10 +191,22 @@
191 }else{
192 printf("\n");
193 }
194 }
195 }
196
197 /*
198 ** WEBPAGE: path
199 **
200 ** example: /path?from=trunk&to=experimental&nomerge
201 **
202 ** Show a timeline of all changes along a path between two versions.
203 */
204 void path_page(void){
205 login_check_credentials();
206 if( !g.okRead ){ login_needed(); return; }
207 }
208
209 /*
210 ** Find the shortest path between bad and good.
211 */
212 static BisectNode *bisect_path(void){
213
+39 -1
--- src/timeline.c
+++ src/timeline.c
@@ -726,10 +726,13 @@
726726
** y=TYPE 'ci', 'w', 't', 'e'
727727
** s=TEXT string search (comment and brief)
728728
** ng Suppress the graph if present
729729
** nd Suppress "divider" lines
730730
** f=RID Show family (immediate parents and children) of RID
731
+** from=RID Path from...
732
+** to=RID ... to this
733
+** nomerge ... avoid merge links on the path
731734
**
732735
** p= and d= can appear individually or together. If either p= or d=
733736
** appear, then u=, y=, a=, and b= are ignored.
734737
**
735738
** If a= and b= appear, only a= is used. If neither appear, the most
@@ -757,10 +760,13 @@
757760
int tagid; /* Tag ID */
758761
int tmFlags; /* Timeline flags */
759762
const char *zThisTag = 0; /* Suppress links to this tag */
760763
const char *zThisUser = 0; /* Suppress links to this user */
761764
HQuery url; /* URL for various branch links */
765
+ int from_rid = name_to_rid(P("from")); /* from= for path timelines */
766
+ int to_rid = name_to_rid(P("to")); /* to= for path timelines */
767
+ int noMerge = P("nomerge")!=0; /* Do not follow merge links */
762768
763769
/* To view the timeline, must have permission to read project data.
764770
*/
765771
login_check_credentials();
766772
if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ login_needed(); return; }
@@ -789,11 +795,42 @@
789795
blob_zero(&desc);
790796
blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
791797
blob_append(&sql, timeline_query_for_www(), -1);
792798
url_initialize(&url, "timeline");
793799
if( !useDividers ) url_add_parameter(&url, "nd", 0);
794
- if( (p_rid || d_rid) && g.okRead ){
800
+ if( from_rid && to_rid && g.okRead ){
801
+ /* If from= and to= are present, display all nodes on a path connecting
802
+ ** the two */
803
+ BisectNode *p;
804
+ const char *z;
805
+
806
+ bisect_shortest_path(from_rid, to_rid, noMerge);
807
+ p = bisect_reverse_path();
808
+ blob_append(&sql, " AND event.objid IN (0", -1);
809
+ while( p ){
810
+ blob_appendf(&sql, ",%d", p->rid);
811
+ p = p->u.pTo;
812
+ }
813
+ blob_append(&sql, ")", -1);
814
+ bisect_reset();
815
+ blob_append(&desc, "All nodes on the path from ", -1);
816
+ z = P("from");
817
+ if( g.okHistory ){
818
+ blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>", g.zTop, z, z);
819
+ }else{
820
+ blob_appendf(&desc, "[%h]", z);
821
+ }
822
+ blob_append(&desc, " and ", -1);
823
+ z = P("to");
824
+ if( g.okHistory ){
825
+ blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>.", g.zTop, z, z);
826
+ }else{
827
+ blob_appendf(&desc, "[%h].", z);
828
+ }
829
+ db_multi_exec("%s", blob_str(&sql));
830
+
831
+ }else if( (p_rid || d_rid) && g.okRead ){
795832
/* If p= or d= is present, ignore all other parameters other than n= */
796833
char *zUuid;
797834
int np, nd;
798835
799836
if( p_rid && d_rid ){
@@ -853,10 +890,11 @@
853890
g.zTop, zUuid, zUuid);
854891
}else{
855892
blob_appendf(&desc, "[%.10s]", zUuid);
856893
}
857894
}else{
895
+ /* Otherwise, a timeline based on a span of time */
858896
int n;
859897
const char *zEType = "timeline item";
860898
char *zDate;
861899
char *zNEntry = mprintf("%d", nEntry);
862900
url_add_parameter(&url, "n", zNEntry);
863901
--- src/timeline.c
+++ src/timeline.c
@@ -726,10 +726,13 @@
726 ** y=TYPE 'ci', 'w', 't', 'e'
727 ** s=TEXT string search (comment and brief)
728 ** ng Suppress the graph if present
729 ** nd Suppress "divider" lines
730 ** f=RID Show family (immediate parents and children) of RID
 
 
 
731 **
732 ** p= and d= can appear individually or together. If either p= or d=
733 ** appear, then u=, y=, a=, and b= are ignored.
734 **
735 ** If a= and b= appear, only a= is used. If neither appear, the most
@@ -757,10 +760,13 @@
757 int tagid; /* Tag ID */
758 int tmFlags; /* Timeline flags */
759 const char *zThisTag = 0; /* Suppress links to this tag */
760 const char *zThisUser = 0; /* Suppress links to this user */
761 HQuery url; /* URL for various branch links */
 
 
 
762
763 /* To view the timeline, must have permission to read project data.
764 */
765 login_check_credentials();
766 if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ login_needed(); return; }
@@ -789,11 +795,42 @@
789 blob_zero(&desc);
790 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
791 blob_append(&sql, timeline_query_for_www(), -1);
792 url_initialize(&url, "timeline");
793 if( !useDividers ) url_add_parameter(&url, "nd", 0);
794 if( (p_rid || d_rid) && g.okRead ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795 /* If p= or d= is present, ignore all other parameters other than n= */
796 char *zUuid;
797 int np, nd;
798
799 if( p_rid && d_rid ){
@@ -853,10 +890,11 @@
853 g.zTop, zUuid, zUuid);
854 }else{
855 blob_appendf(&desc, "[%.10s]", zUuid);
856 }
857 }else{
 
858 int n;
859 const char *zEType = "timeline item";
860 char *zDate;
861 char *zNEntry = mprintf("%d", nEntry);
862 url_add_parameter(&url, "n", zNEntry);
863
--- src/timeline.c
+++ src/timeline.c
@@ -726,10 +726,13 @@
726 ** y=TYPE 'ci', 'w', 't', 'e'
727 ** s=TEXT string search (comment and brief)
728 ** ng Suppress the graph if present
729 ** nd Suppress "divider" lines
730 ** f=RID Show family (immediate parents and children) of RID
731 ** from=RID Path from...
732 ** to=RID ... to this
733 ** nomerge ... avoid merge links on the path
734 **
735 ** p= and d= can appear individually or together. If either p= or d=
736 ** appear, then u=, y=, a=, and b= are ignored.
737 **
738 ** If a= and b= appear, only a= is used. If neither appear, the most
@@ -757,10 +760,13 @@
760 int tagid; /* Tag ID */
761 int tmFlags; /* Timeline flags */
762 const char *zThisTag = 0; /* Suppress links to this tag */
763 const char *zThisUser = 0; /* Suppress links to this user */
764 HQuery url; /* URL for various branch links */
765 int from_rid = name_to_rid(P("from")); /* from= for path timelines */
766 int to_rid = name_to_rid(P("to")); /* to= for path timelines */
767 int noMerge = P("nomerge")!=0; /* Do not follow merge links */
768
769 /* To view the timeline, must have permission to read project data.
770 */
771 login_check_credentials();
772 if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ login_needed(); return; }
@@ -789,11 +795,42 @@
795 blob_zero(&desc);
796 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
797 blob_append(&sql, timeline_query_for_www(), -1);
798 url_initialize(&url, "timeline");
799 if( !useDividers ) url_add_parameter(&url, "nd", 0);
800 if( from_rid && to_rid && g.okRead ){
801 /* If from= and to= are present, display all nodes on a path connecting
802 ** the two */
803 BisectNode *p;
804 const char *z;
805
806 bisect_shortest_path(from_rid, to_rid, noMerge);
807 p = bisect_reverse_path();
808 blob_append(&sql, " AND event.objid IN (0", -1);
809 while( p ){
810 blob_appendf(&sql, ",%d", p->rid);
811 p = p->u.pTo;
812 }
813 blob_append(&sql, ")", -1);
814 bisect_reset();
815 blob_append(&desc, "All nodes on the path from ", -1);
816 z = P("from");
817 if( g.okHistory ){
818 blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>", g.zTop, z, z);
819 }else{
820 blob_appendf(&desc, "[%h]", z);
821 }
822 blob_append(&desc, " and ", -1);
823 z = P("to");
824 if( g.okHistory ){
825 blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>.", g.zTop, z, z);
826 }else{
827 blob_appendf(&desc, "[%h].", z);
828 }
829 db_multi_exec("%s", blob_str(&sql));
830
831 }else if( (p_rid || d_rid) && g.okRead ){
832 /* If p= or d= is present, ignore all other parameters other than n= */
833 char *zUuid;
834 int np, nd;
835
836 if( p_rid && d_rid ){
@@ -853,10 +890,11 @@
890 g.zTop, zUuid, zUuid);
891 }else{
892 blob_appendf(&desc, "[%.10s]", zUuid);
893 }
894 }else{
895 /* Otherwise, a timeline based on a span of time */
896 int n;
897 const char *zEType = "timeline item";
898 char *zDate;
899 char *zNEntry = mprintf("%d", nEntry);
900 url_add_parameter(&url, "n", zNEntry);
901

Keyboard Shortcuts

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