Fossil SCM

Add the ability to compute common ancestors to the path object. Still using the pivot object for merges, however.

drh 2011-03-10 17:39 path-refactor
Commit cfec72248e1616673dd81a49d32011195e445282
+1 -2
--- src/bisect.c
+++ src/bisect.c
@@ -32,11 +32,11 @@
3232
} bisect;
3333
3434
/*
3535
** Find the shortest path between bad and good.
3636
*/
37
-static PathNode *bisect_path(void){
37
+void bisect_path(void){
3838
PathNode *p;
3939
bisect.bad = db_lget_int("bisect-bad", 0);
4040
if( bisect.bad==0 ){
4141
bisect.bad = db_int(0, "SELECT cid FROM plink ORDER BY mtime DESC LIMIT 1");
4242
db_lset_int("bisect-bad", bisect.bad);
@@ -51,11 +51,10 @@
5151
char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad);
5252
char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good);
5353
fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
5454
zGood, zBad);
5555
}
56
- return p;
5756
}
5857
5958
/*
6059
** COMMAND: bisect
6160
**
6261
--- src/bisect.c
+++ src/bisect.c
@@ -32,11 +32,11 @@
32 } bisect;
33
34 /*
35 ** Find the shortest path between bad and good.
36 */
37 static PathNode *bisect_path(void){
38 PathNode *p;
39 bisect.bad = db_lget_int("bisect-bad", 0);
40 if( bisect.bad==0 ){
41 bisect.bad = db_int(0, "SELECT cid FROM plink ORDER BY mtime DESC LIMIT 1");
42 db_lset_int("bisect-bad", bisect.bad);
@@ -51,11 +51,10 @@
51 char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad);
52 char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good);
53 fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
54 zGood, zBad);
55 }
56 return p;
57 }
58
59 /*
60 ** COMMAND: bisect
61 **
62
--- src/bisect.c
+++ src/bisect.c
@@ -32,11 +32,11 @@
32 } bisect;
33
34 /*
35 ** Find the shortest path between bad and good.
36 */
37 void bisect_path(void){
38 PathNode *p;
39 bisect.bad = db_lget_int("bisect-bad", 0);
40 if( bisect.bad==0 ){
41 bisect.bad = db_int(0, "SELECT cid FROM plink ORDER BY mtime DESC LIMIT 1");
42 db_lset_int("bisect-bad", bisect.bad);
@@ -51,11 +51,10 @@
51 char *zBad = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.bad);
52 char *zGood = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", bisect.good);
53 fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
54 zGood, zBad);
55 }
 
56 }
57
58 /*
59 ** COMMAND: bisect
60 **
61
--- src/descendants.c
+++ src/descendants.c
@@ -162,12 +162,12 @@
162162
Bag seen;
163163
PQueue queue;
164164
bag_init(&seen);
165165
pqueue_init(&queue);
166166
bag_insert(&seen, rid);
167
- pqueue_insert(&queue, rid, 0.0);
168
- while( (N--)>0 && (rid = pqueue_extract(&queue))!=0 ){
167
+ pqueue_insert(&queue, rid, 0.0, 0);
168
+ while( (N--)>0 && (rid = pqueue_extract(&queue, 0))!=0 ){
169169
Stmt q;
170170
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
171171
db_prepare(&q,
172172
"SELECT a.pid, b.mtime FROM plink a LEFT JOIN plink b ON b.cid=a.pid"
173173
" WHERE a.cid=%d", rid
@@ -174,11 +174,11 @@
174174
);
175175
while( db_step(&q)==SQLITE_ROW ){
176176
int pid = db_column_int(&q, 0);
177177
double mtime = db_column_double(&q, 1);
178178
if( bag_insert(&seen, pid) ){
179
- pqueue_insert(&queue, pid, -mtime);
179
+ pqueue_insert(&queue, pid, -mtime, 0);
180180
}
181181
}
182182
db_finalize(&q);
183183
}
184184
bag_clear(&seen);
@@ -193,20 +193,20 @@
193193
Bag seen;
194194
PQueue queue;
195195
bag_init(&seen);
196196
pqueue_init(&queue);
197197
bag_insert(&seen, rid);
198
- pqueue_insert(&queue, rid, 0.0);
199
- while( (N--)>0 && (rid = pqueue_extract(&queue))!=0 ){
198
+ pqueue_insert(&queue, rid, 0.0, 0);
199
+ while( (N--)>0 && (rid = pqueue_extract(&queue, 0))!=0 ){
200200
Stmt q;
201201
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
202202
db_prepare(&q,"SELECT cid, mtime FROM plink WHERE pid=%d", rid);
203203
while( db_step(&q)==SQLITE_ROW ){
204204
int pid = db_column_int(&q, 0);
205205
double mtime = db_column_double(&q, 1);
206206
if( bag_insert(&seen, pid) ){
207
- pqueue_insert(&queue, pid, mtime);
207
+ pqueue_insert(&queue, pid, mtime, 0);
208208
}
209209
}
210210
db_finalize(&q);
211211
}
212212
bag_clear(&seen);
213213
--- src/descendants.c
+++ src/descendants.c
@@ -162,12 +162,12 @@
162 Bag seen;
163 PQueue queue;
164 bag_init(&seen);
165 pqueue_init(&queue);
166 bag_insert(&seen, rid);
167 pqueue_insert(&queue, rid, 0.0);
168 while( (N--)>0 && (rid = pqueue_extract(&queue))!=0 ){
169 Stmt q;
170 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
171 db_prepare(&q,
172 "SELECT a.pid, b.mtime FROM plink a LEFT JOIN plink b ON b.cid=a.pid"
173 " WHERE a.cid=%d", rid
@@ -174,11 +174,11 @@
174 );
175 while( db_step(&q)==SQLITE_ROW ){
176 int pid = db_column_int(&q, 0);
177 double mtime = db_column_double(&q, 1);
178 if( bag_insert(&seen, pid) ){
179 pqueue_insert(&queue, pid, -mtime);
180 }
181 }
182 db_finalize(&q);
183 }
184 bag_clear(&seen);
@@ -193,20 +193,20 @@
193 Bag seen;
194 PQueue queue;
195 bag_init(&seen);
196 pqueue_init(&queue);
197 bag_insert(&seen, rid);
198 pqueue_insert(&queue, rid, 0.0);
199 while( (N--)>0 && (rid = pqueue_extract(&queue))!=0 ){
200 Stmt q;
201 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
202 db_prepare(&q,"SELECT cid, mtime FROM plink WHERE pid=%d", rid);
203 while( db_step(&q)==SQLITE_ROW ){
204 int pid = db_column_int(&q, 0);
205 double mtime = db_column_double(&q, 1);
206 if( bag_insert(&seen, pid) ){
207 pqueue_insert(&queue, pid, mtime);
208 }
209 }
210 db_finalize(&q);
211 }
212 bag_clear(&seen);
213
--- src/descendants.c
+++ src/descendants.c
@@ -162,12 +162,12 @@
162 Bag seen;
163 PQueue queue;
164 bag_init(&seen);
165 pqueue_init(&queue);
166 bag_insert(&seen, rid);
167 pqueue_insert(&queue, rid, 0.0, 0);
168 while( (N--)>0 && (rid = pqueue_extract(&queue, 0))!=0 ){
169 Stmt q;
170 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
171 db_prepare(&q,
172 "SELECT a.pid, b.mtime FROM plink a LEFT JOIN plink b ON b.cid=a.pid"
173 " WHERE a.cid=%d", rid
@@ -174,11 +174,11 @@
174 );
175 while( db_step(&q)==SQLITE_ROW ){
176 int pid = db_column_int(&q, 0);
177 double mtime = db_column_double(&q, 1);
178 if( bag_insert(&seen, pid) ){
179 pqueue_insert(&queue, pid, -mtime, 0);
180 }
181 }
182 db_finalize(&q);
183 }
184 bag_clear(&seen);
@@ -193,20 +193,20 @@
193 Bag seen;
194 PQueue queue;
195 bag_init(&seen);
196 pqueue_init(&queue);
197 bag_insert(&seen, rid);
198 pqueue_insert(&queue, rid, 0.0, 0);
199 while( (N--)>0 && (rid = pqueue_extract(&queue, 0))!=0 ){
200 Stmt q;
201 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid);
202 db_prepare(&q,"SELECT cid, mtime FROM plink WHERE pid=%d", rid);
203 while( db_step(&q)==SQLITE_ROW ){
204 int pid = db_column_int(&q, 0);
205 double mtime = db_column_double(&q, 1);
206 if( bag_insert(&seen, pid) ){
207 pqueue_insert(&queue, pid, mtime, 0);
208 }
209 }
210 db_finalize(&q);
211 }
212 bag_clear(&seen);
213
+2 -2
--- src/name.c
+++ src/name.c
@@ -147,11 +147,11 @@
147147
int vid;
148148
char *zUuid =
149149
db_text(0,
150150
"SELECT blob.uuid"
151151
" FROM tag, tagxref, event, blob"
152
- " WHERE tag.tagname='sym-'||%Q "
152
+ " WHERE tag.tagname='sym-%q' "
153153
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
154154
" AND event.objid=tagxref.rid "
155155
" AND blob.rid=event.objid "
156156
" ORDER BY event.mtime DESC ",
157157
zTag
@@ -171,11 +171,11 @@
171171
useUtc = 1;
172172
}
173173
zUuid = db_text(0,
174174
"SELECT blob.uuid"
175175
" FROM tag, tagxref, event, blob"
176
- " WHERE tag.tagname='sym-'||%Q "
176
+ " WHERE tag.tagname='sym-%q' "
177177
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
178178
" AND event.objid=tagxref.rid "
179179
" AND blob.rid=event.objid "
180180
" AND event.mtime<=julianday(%Q %s)"
181181
" ORDER BY event.mtime DESC ",
182182
--- src/name.c
+++ src/name.c
@@ -147,11 +147,11 @@
147 int vid;
148 char *zUuid =
149 db_text(0,
150 "SELECT blob.uuid"
151 " FROM tag, tagxref, event, blob"
152 " WHERE tag.tagname='sym-'||%Q "
153 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
154 " AND event.objid=tagxref.rid "
155 " AND blob.rid=event.objid "
156 " ORDER BY event.mtime DESC ",
157 zTag
@@ -171,11 +171,11 @@
171 useUtc = 1;
172 }
173 zUuid = db_text(0,
174 "SELECT blob.uuid"
175 " FROM tag, tagxref, event, blob"
176 " WHERE tag.tagname='sym-'||%Q "
177 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
178 " AND event.objid=tagxref.rid "
179 " AND blob.rid=event.objid "
180 " AND event.mtime<=julianday(%Q %s)"
181 " ORDER BY event.mtime DESC ",
182
--- src/name.c
+++ src/name.c
@@ -147,11 +147,11 @@
147 int vid;
148 char *zUuid =
149 db_text(0,
150 "SELECT blob.uuid"
151 " FROM tag, tagxref, event, blob"
152 " WHERE tag.tagname='sym-%q' "
153 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
154 " AND event.objid=tagxref.rid "
155 " AND blob.rid=event.objid "
156 " ORDER BY event.mtime DESC ",
157 zTag
@@ -171,11 +171,11 @@
171 useUtc = 1;
172 }
173 zUuid = db_text(0,
174 "SELECT blob.uuid"
175 " FROM tag, tagxref, event, blob"
176 " WHERE tag.tagname='sym-%q' "
177 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
178 " AND event.objid=tagxref.rid "
179 " AND blob.rid=event.objid "
180 " AND event.mtime<=julianday(%Q %s)"
181 " ORDER BY event.mtime DESC ",
182
+125 -17
--- src/path.c
+++ src/path.c
@@ -24,11 +24,12 @@
2424
#if INTERFACE
2525
/* Nodes for the paths through the DAG.
2626
*/
2727
struct PathNode {
2828
int rid; /* ID for this node */
29
- int fromIsParent; /* True if pFrom is the parent of rid */
29
+ u8 fromIsParent; /* True if pFrom is the parent of rid */
30
+ u8 isPrim; /* True if primary side of common ancestor */
3031
PathNode *pFrom; /* Node we came from */
3132
union {
3233
PathNode *pPeer; /* List of nodes of the same generation */
3334
PathNode *pTo; /* Next on path from beginning to end */
3435
} u;
@@ -43,10 +44,11 @@
4344
PathNode *pCurrent; /* Current generation of nodes */
4445
PathNode *pAll; /* All nodes */
4546
Bag seen; /* Nodes seen before */
4647
int nStep; /* Number of steps from first to last */
4748
PathNode *pStart; /* Earliest node */
49
+ PathNode *pPivot; /* Common ancestor of pStart and pEnd */
4850
PathNode *pEnd; /* Most recent */
4951
} path;
5052
5153
/*
5254
** Return the first (last) element of the computed path.
@@ -64,18 +66,18 @@
6466
*/
6567
static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
6668
PathNode *p;
6769
6870
p = fossil_malloc( sizeof(*p) );
71
+ memset(p, 0, sizeof(*p));
6972
p->rid = rid;
7073
p->fromIsParent = isParent;
7174
p->pFrom = pFrom;
7275
p->u.pPeer = path.pCurrent;
7376
path.pCurrent = p;
7477
p->pAll = path.pAll;
7578
path.pAll = p;
76
- path.pEnd = p;
7779
bag_insert(&path.seen, rid);
7880
return p;
7981
}
8082
8183
/*
@@ -92,16 +94,34 @@
9294
path.pCurrent = 0;
9395
path.pAll = 0;
9496
path.pEnd = 0;
9597
path.nStep = 0;
9698
}
99
+
100
+/*
101
+** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
102
+*/
103
+void path_reverse_path(void){
104
+ PathNode *p;
105
+ for(p=path.pEnd; p && p->pFrom; p = p->pFrom){
106
+ p->pFrom->u.pTo = p;
107
+ }
108
+ path.pEnd->u.pTo = 0;
109
+ assert( p==path.pStart );
110
+}
97111
98112
/*
99113
** Compute the shortest path from iFrom to iTo
100114
**
101115
** If directOnly is true, then use only the "primary" links from parent to
102116
** child. In other words, ignore merges.
117
+**
118
+** Return a pointer to the beginning of the path (the iFrom node).
119
+** Elements of the path can be traversed by following the PathNode.u.pTo
120
+** pointer chain.
121
+**
122
+** Return NULL if no path is found.
103123
*/
104124
PathNode *path_shortest(int iFrom, int iTo, int directOnly){
105125
Stmt s;
106126
PathNode *pPrev;
107127
PathNode *p;
@@ -133,34 +153,24 @@
133153
int isParent = db_column_int(&s, 1);
134154
if( bag_find(&path.seen, cid) ) continue;
135155
p = path_new_node(cid, pPrev, isParent);
136156
if( cid==iTo ){
137157
db_finalize(&s);
138
- return p;
158
+ path.pEnd = p;
159
+ path_reverse_path();
160
+ return path.pStart;
139161
}
140162
}
141163
db_reset(&s);
142164
pPrev = pPrev->u.pPeer;
143165
}
144166
}
167
+ db_finalize(&s);
145168
path_reset();
146169
return 0;
147170
}
148171
149
-/*
150
-** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
151
-*/
152
-PathNode *path_reverse_path(void){
153
- PathNode *p;
154
- for(p=path.pEnd; p && p->pFrom; p = p->pFrom){
155
- p->pFrom->u.pTo = p;
156
- }
157
- path.pEnd->u.pTo = 0;
158
- assert( p==path.pStart );
159
- return p;
160
-}
161
-
162172
/*
163173
** Find the mid-point of the path. If the path contains fewer than
164174
** 2 steps, return 0.
165175
*/
166176
PathNode *path_midpoint(void){
@@ -193,11 +203,10 @@
193203
iTo = name_to_rid(g.argv[3]);
194204
p = path_shortest(iFrom, iTo, directOnly);
195205
if( p==0 ){
196206
fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
197207
}
198
- path_reverse_path();
199208
for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
200209
char *z;
201210
z = db_text(0,
202211
"SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
203212
" FROM blob, event"
@@ -210,10 +219,109 @@
210219
}else{
211220
printf("\n");
212221
}
213222
}
214223
}
224
+
225
+/*
226
+** Find the closest common ancestor of two nodes. "Closest" means the
227
+** fewest number of arcs.
228
+*/
229
+int path_common_ancestor(int iMe, int iYou){
230
+ Stmt s;
231
+ PathNode *pPrev;
232
+ PathNode *p;
233
+ Bag me, you;
234
+
235
+ if( iMe==iYou ) return iMe;
236
+ if( iMe==0 || iYou==0 ) return 0;
237
+ path_reset();
238
+ path.pStart = path_new_node(iMe, 0, 0);
239
+ path.pStart->isPrim = 1;
240
+ path.pEnd = path_new_node(iYou, 0, 0);
241
+ db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
242
+ bag_init(&me);
243
+ bag_insert(&me, iMe);
244
+ bag_init(&you);
245
+ bag_insert(&you, iYou);
246
+ while( path.pCurrent ){
247
+ pPrev = path.pCurrent;
248
+ path.pCurrent = 0;
249
+ while( pPrev ){
250
+ db_bind_int(&s, ":cid", pPrev->rid);
251
+ while( db_step(&s)==SQLITE_ROW ){
252
+ int pid = db_column_int(&s, 0);
253
+ if( bag_find(pPrev->isPrim ? &you : &me, pid) ){
254
+ /* pid is the common ancestor */
255
+ PathNode *pNext;
256
+ for(p=path.pAll; p && p->rid!=pid; p=p->pAll){}
257
+ assert( p!=0 );
258
+ pNext = p;
259
+ while( pNext ){
260
+ pNext = p->pFrom;
261
+ p->pFrom = pPrev;
262
+ pPrev = p;
263
+ p = pNext;
264
+ }
265
+ if( pPrev==path.pStart ) path.pStart = path.pEnd;
266
+ path.pEnd = pPrev;
267
+ path_reverse_path();
268
+ db_finalize(&s);
269
+ return pid;
270
+ }else if( bag_find(&path.seen, pid) ){
271
+ /* pid is just an alternative path on one of the legs */
272
+ continue;
273
+ }
274
+ p = path_new_node(pid, pPrev, 0);
275
+ p->isPrim = pPrev->isPrim;
276
+ bag_insert(pPrev->isPrim ? &me : &you, pid);
277
+ }
278
+ db_reset(&s);
279
+ pPrev = pPrev->u.pPeer;
280
+ }
281
+ }
282
+ db_finalize(&s);
283
+ path_reset();
284
+ return 0;
285
+}
286
+
287
+/*
288
+** COMMAND: test-ancestor-path
289
+**
290
+** Usage: %fossil test-ancestor-path VERSION1 VERSION2
291
+**
292
+** Report the path from VERSION1 to VERSION2 through their most recent
293
+** common ancestor.
294
+*/
295
+void ancestor_path_test_cmd(void){
296
+ int iFrom;
297
+ int iTo;
298
+ int iPivot;
299
+ PathNode *p;
300
+ int n;
301
+
302
+ db_find_and_open_repository(0,0);
303
+ if( g.argc!=4 ) usage("VERSION1 VERSION2");
304
+ iFrom = name_to_rid(g.argv[2]);
305
+ iTo = name_to_rid(g.argv[3]);
306
+ iPivot = path_common_ancestor(iFrom, iTo);
307
+ for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
308
+ char *z;
309
+ z = db_text(0,
310
+ "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
311
+ " FROM blob, event"
312
+ " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
313
+ p->rid, p->rid);
314
+ printf("%4d: %s", n, z);
315
+ fossil_free(z);
316
+ if( p->rid==iFrom ) printf(" VERSION1");
317
+ if( p->rid==iTo ) printf(" VERSION2");
318
+ if( p->rid==iPivot ) printf(" PIVOT");
319
+ printf("\n");
320
+ }
321
+}
322
+
215323
216324
/*
217325
** A record of a file rename operation.
218326
*/
219327
typedef struct NameChange NameChange;
220328
--- src/path.c
+++ src/path.c
@@ -24,11 +24,12 @@
24 #if INTERFACE
25 /* Nodes for the paths through the DAG.
26 */
27 struct PathNode {
28 int rid; /* ID for this node */
29 int fromIsParent; /* True if pFrom is the parent of rid */
 
30 PathNode *pFrom; /* Node we came from */
31 union {
32 PathNode *pPeer; /* List of nodes of the same generation */
33 PathNode *pTo; /* Next on path from beginning to end */
34 } u;
@@ -43,10 +44,11 @@
43 PathNode *pCurrent; /* Current generation of nodes */
44 PathNode *pAll; /* All nodes */
45 Bag seen; /* Nodes seen before */
46 int nStep; /* Number of steps from first to last */
47 PathNode *pStart; /* Earliest node */
 
48 PathNode *pEnd; /* Most recent */
49 } path;
50
51 /*
52 ** Return the first (last) element of the computed path.
@@ -64,18 +66,18 @@
64 */
65 static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
66 PathNode *p;
67
68 p = fossil_malloc( sizeof(*p) );
 
69 p->rid = rid;
70 p->fromIsParent = isParent;
71 p->pFrom = pFrom;
72 p->u.pPeer = path.pCurrent;
73 path.pCurrent = p;
74 p->pAll = path.pAll;
75 path.pAll = p;
76 path.pEnd = p;
77 bag_insert(&path.seen, rid);
78 return p;
79 }
80
81 /*
@@ -92,16 +94,34 @@
92 path.pCurrent = 0;
93 path.pAll = 0;
94 path.pEnd = 0;
95 path.nStep = 0;
96 }
 
 
 
 
 
 
 
 
 
 
 
 
97
98 /*
99 ** Compute the shortest path from iFrom to iTo
100 **
101 ** If directOnly is true, then use only the "primary" links from parent to
102 ** child. In other words, ignore merges.
 
 
 
 
 
 
103 */
104 PathNode *path_shortest(int iFrom, int iTo, int directOnly){
105 Stmt s;
106 PathNode *pPrev;
107 PathNode *p;
@@ -133,34 +153,24 @@
133 int isParent = db_column_int(&s, 1);
134 if( bag_find(&path.seen, cid) ) continue;
135 p = path_new_node(cid, pPrev, isParent);
136 if( cid==iTo ){
137 db_finalize(&s);
138 return p;
 
 
139 }
140 }
141 db_reset(&s);
142 pPrev = pPrev->u.pPeer;
143 }
144 }
 
145 path_reset();
146 return 0;
147 }
148
149 /*
150 ** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
151 */
152 PathNode *path_reverse_path(void){
153 PathNode *p;
154 for(p=path.pEnd; p && p->pFrom; p = p->pFrom){
155 p->pFrom->u.pTo = p;
156 }
157 path.pEnd->u.pTo = 0;
158 assert( p==path.pStart );
159 return p;
160 }
161
162 /*
163 ** Find the mid-point of the path. If the path contains fewer than
164 ** 2 steps, return 0.
165 */
166 PathNode *path_midpoint(void){
@@ -193,11 +203,10 @@
193 iTo = name_to_rid(g.argv[3]);
194 p = path_shortest(iFrom, iTo, directOnly);
195 if( p==0 ){
196 fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
197 }
198 path_reverse_path();
199 for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
200 char *z;
201 z = db_text(0,
202 "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
203 " FROM blob, event"
@@ -210,10 +219,109 @@
210 }else{
211 printf("\n");
212 }
213 }
214 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
216 /*
217 ** A record of a file rename operation.
218 */
219 typedef struct NameChange NameChange;
220
--- src/path.c
+++ src/path.c
@@ -24,11 +24,12 @@
24 #if INTERFACE
25 /* Nodes for the paths through the DAG.
26 */
27 struct PathNode {
28 int rid; /* ID for this node */
29 u8 fromIsParent; /* True if pFrom is the parent of rid */
30 u8 isPrim; /* True if primary side of common ancestor */
31 PathNode *pFrom; /* Node we came from */
32 union {
33 PathNode *pPeer; /* List of nodes of the same generation */
34 PathNode *pTo; /* Next on path from beginning to end */
35 } u;
@@ -43,10 +44,11 @@
44 PathNode *pCurrent; /* Current generation of nodes */
45 PathNode *pAll; /* All nodes */
46 Bag seen; /* Nodes seen before */
47 int nStep; /* Number of steps from first to last */
48 PathNode *pStart; /* Earliest node */
49 PathNode *pPivot; /* Common ancestor of pStart and pEnd */
50 PathNode *pEnd; /* Most recent */
51 } path;
52
53 /*
54 ** Return the first (last) element of the computed path.
@@ -64,18 +66,18 @@
66 */
67 static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
68 PathNode *p;
69
70 p = fossil_malloc( sizeof(*p) );
71 memset(p, 0, sizeof(*p));
72 p->rid = rid;
73 p->fromIsParent = isParent;
74 p->pFrom = pFrom;
75 p->u.pPeer = path.pCurrent;
76 path.pCurrent = p;
77 p->pAll = path.pAll;
78 path.pAll = p;
 
79 bag_insert(&path.seen, rid);
80 return p;
81 }
82
83 /*
@@ -92,16 +94,34 @@
94 path.pCurrent = 0;
95 path.pAll = 0;
96 path.pEnd = 0;
97 path.nStep = 0;
98 }
99
100 /*
101 ** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
102 */
103 void path_reverse_path(void){
104 PathNode *p;
105 for(p=path.pEnd; p && p->pFrom; p = p->pFrom){
106 p->pFrom->u.pTo = p;
107 }
108 path.pEnd->u.pTo = 0;
109 assert( p==path.pStart );
110 }
111
112 /*
113 ** Compute the shortest path from iFrom to iTo
114 **
115 ** If directOnly is true, then use only the "primary" links from parent to
116 ** child. In other words, ignore merges.
117 **
118 ** Return a pointer to the beginning of the path (the iFrom node).
119 ** Elements of the path can be traversed by following the PathNode.u.pTo
120 ** pointer chain.
121 **
122 ** Return NULL if no path is found.
123 */
124 PathNode *path_shortest(int iFrom, int iTo, int directOnly){
125 Stmt s;
126 PathNode *pPrev;
127 PathNode *p;
@@ -133,34 +153,24 @@
153 int isParent = db_column_int(&s, 1);
154 if( bag_find(&path.seen, cid) ) continue;
155 p = path_new_node(cid, pPrev, isParent);
156 if( cid==iTo ){
157 db_finalize(&s);
158 path.pEnd = p;
159 path_reverse_path();
160 return path.pStart;
161 }
162 }
163 db_reset(&s);
164 pPrev = pPrev->u.pPeer;
165 }
166 }
167 db_finalize(&s);
168 path_reset();
169 return 0;
170 }
171
 
 
 
 
 
 
 
 
 
 
 
 
 
172 /*
173 ** Find the mid-point of the path. If the path contains fewer than
174 ** 2 steps, return 0.
175 */
176 PathNode *path_midpoint(void){
@@ -193,11 +203,10 @@
203 iTo = name_to_rid(g.argv[3]);
204 p = path_shortest(iFrom, iTo, directOnly);
205 if( p==0 ){
206 fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
207 }
 
208 for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
209 char *z;
210 z = db_text(0,
211 "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
212 " FROM blob, event"
@@ -210,10 +219,109 @@
219 }else{
220 printf("\n");
221 }
222 }
223 }
224
225 /*
226 ** Find the closest common ancestor of two nodes. "Closest" means the
227 ** fewest number of arcs.
228 */
229 int path_common_ancestor(int iMe, int iYou){
230 Stmt s;
231 PathNode *pPrev;
232 PathNode *p;
233 Bag me, you;
234
235 if( iMe==iYou ) return iMe;
236 if( iMe==0 || iYou==0 ) return 0;
237 path_reset();
238 path.pStart = path_new_node(iMe, 0, 0);
239 path.pStart->isPrim = 1;
240 path.pEnd = path_new_node(iYou, 0, 0);
241 db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
242 bag_init(&me);
243 bag_insert(&me, iMe);
244 bag_init(&you);
245 bag_insert(&you, iYou);
246 while( path.pCurrent ){
247 pPrev = path.pCurrent;
248 path.pCurrent = 0;
249 while( pPrev ){
250 db_bind_int(&s, ":cid", pPrev->rid);
251 while( db_step(&s)==SQLITE_ROW ){
252 int pid = db_column_int(&s, 0);
253 if( bag_find(pPrev->isPrim ? &you : &me, pid) ){
254 /* pid is the common ancestor */
255 PathNode *pNext;
256 for(p=path.pAll; p && p->rid!=pid; p=p->pAll){}
257 assert( p!=0 );
258 pNext = p;
259 while( pNext ){
260 pNext = p->pFrom;
261 p->pFrom = pPrev;
262 pPrev = p;
263 p = pNext;
264 }
265 if( pPrev==path.pStart ) path.pStart = path.pEnd;
266 path.pEnd = pPrev;
267 path_reverse_path();
268 db_finalize(&s);
269 return pid;
270 }else if( bag_find(&path.seen, pid) ){
271 /* pid is just an alternative path on one of the legs */
272 continue;
273 }
274 p = path_new_node(pid, pPrev, 0);
275 p->isPrim = pPrev->isPrim;
276 bag_insert(pPrev->isPrim ? &me : &you, pid);
277 }
278 db_reset(&s);
279 pPrev = pPrev->u.pPeer;
280 }
281 }
282 db_finalize(&s);
283 path_reset();
284 return 0;
285 }
286
287 /*
288 ** COMMAND: test-ancestor-path
289 **
290 ** Usage: %fossil test-ancestor-path VERSION1 VERSION2
291 **
292 ** Report the path from VERSION1 to VERSION2 through their most recent
293 ** common ancestor.
294 */
295 void ancestor_path_test_cmd(void){
296 int iFrom;
297 int iTo;
298 int iPivot;
299 PathNode *p;
300 int n;
301
302 db_find_and_open_repository(0,0);
303 if( g.argc!=4 ) usage("VERSION1 VERSION2");
304 iFrom = name_to_rid(g.argv[2]);
305 iTo = name_to_rid(g.argv[3]);
306 iPivot = path_common_ancestor(iFrom, iTo);
307 for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
308 char *z;
309 z = db_text(0,
310 "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)"
311 " FROM blob, event"
312 " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'",
313 p->rid, p->rid);
314 printf("%4d: %s", n, z);
315 fossil_free(z);
316 if( p->rid==iFrom ) printf(" VERSION1");
317 if( p->rid==iTo ) printf(" VERSION2");
318 if( p->rid==iPivot ) printf(" PIVOT");
319 printf("\n");
320 }
321 }
322
323
324 /*
325 ** A record of a file rename operation.
326 */
327 typedef struct NameChange NameChange;
328
+6 -2
--- src/pqueue.c
+++ src/pqueue.c
@@ -38,10 +38,11 @@
3838
struct PQueue {
3939
int cnt; /* Number of entries in the queue */
4040
int sz; /* Number of slots in a[] */
4141
struct QueueElement {
4242
int id; /* ID of the element */
43
+ void *p; /* Content pointer */
4344
double value; /* Value of element. Kept in ascending order */
4445
} *a;
4546
};
4647
#endif
4748
@@ -69,11 +70,11 @@
6970
}
7071
7172
/*
7273
** Insert element e into the queue.
7374
*/
74
-void pqueue_insert(PQueue *p, int e, double v){
75
+void pqueue_insert(PQueue *p, int e, double v, void *pData){
7576
int i, j;
7677
if( p->cnt+1>p->sz ){
7778
pqueue_resize(p, p->cnt+5);
7879
}
7980
for(i=0; i<p->cnt; i++){
@@ -83,26 +84,29 @@
8384
}
8485
break;
8586
}
8687
}
8788
p->a[i].id = e;
89
+ p->a[i].p = pData;
8890
p->a[i].value = v;
8991
p->cnt++;
9092
}
9193
9294
/*
9395
** Extract the first element from the queue (the element with
9496
** the smallest value) and return its ID. Return 0 if the queue
9597
** is empty.
9698
*/
97
-int pqueue_extract(PQueue *p){
99
+int pqueue_extract(PQueue *p, void **pp){
98100
int e, i;
99101
if( p->cnt==0 ){
102
+ if( pp ) *pp = 0;
100103
return 0;
101104
}
102105
e = p->a[0].id;
106
+ if( pp ) *pp = p->a[0].p;
103107
for(i=0; i<p->cnt-1; i++){
104108
p->a[i] = p->a[i+1];
105109
}
106110
p->cnt--;
107111
return e;
108112
}
109113
--- src/pqueue.c
+++ src/pqueue.c
@@ -38,10 +38,11 @@
38 struct PQueue {
39 int cnt; /* Number of entries in the queue */
40 int sz; /* Number of slots in a[] */
41 struct QueueElement {
42 int id; /* ID of the element */
 
43 double value; /* Value of element. Kept in ascending order */
44 } *a;
45 };
46 #endif
47
@@ -69,11 +70,11 @@
69 }
70
71 /*
72 ** Insert element e into the queue.
73 */
74 void pqueue_insert(PQueue *p, int e, double v){
75 int i, j;
76 if( p->cnt+1>p->sz ){
77 pqueue_resize(p, p->cnt+5);
78 }
79 for(i=0; i<p->cnt; i++){
@@ -83,26 +84,29 @@
83 }
84 break;
85 }
86 }
87 p->a[i].id = e;
 
88 p->a[i].value = v;
89 p->cnt++;
90 }
91
92 /*
93 ** Extract the first element from the queue (the element with
94 ** the smallest value) and return its ID. Return 0 if the queue
95 ** is empty.
96 */
97 int pqueue_extract(PQueue *p){
98 int e, i;
99 if( p->cnt==0 ){
 
100 return 0;
101 }
102 e = p->a[0].id;
 
103 for(i=0; i<p->cnt-1; i++){
104 p->a[i] = p->a[i+1];
105 }
106 p->cnt--;
107 return e;
108 }
109
--- src/pqueue.c
+++ src/pqueue.c
@@ -38,10 +38,11 @@
38 struct PQueue {
39 int cnt; /* Number of entries in the queue */
40 int sz; /* Number of slots in a[] */
41 struct QueueElement {
42 int id; /* ID of the element */
43 void *p; /* Content pointer */
44 double value; /* Value of element. Kept in ascending order */
45 } *a;
46 };
47 #endif
48
@@ -69,11 +70,11 @@
70 }
71
72 /*
73 ** Insert element e into the queue.
74 */
75 void pqueue_insert(PQueue *p, int e, double v, void *pData){
76 int i, j;
77 if( p->cnt+1>p->sz ){
78 pqueue_resize(p, p->cnt+5);
79 }
80 for(i=0; i<p->cnt; i++){
@@ -83,26 +84,29 @@
84 }
85 break;
86 }
87 }
88 p->a[i].id = e;
89 p->a[i].p = pData;
90 p->a[i].value = v;
91 p->cnt++;
92 }
93
94 /*
95 ** Extract the first element from the queue (the element with
96 ** the smallest value) and return its ID. Return 0 if the queue
97 ** is empty.
98 */
99 int pqueue_extract(PQueue *p, void **pp){
100 int e, i;
101 if( p->cnt==0 ){
102 if( pp ) *pp = 0;
103 return 0;
104 }
105 e = p->a[0].id;
106 if( pp ) *pp = p->a[0].p;
107 for(i=0; i<p->cnt-1; i++){
108 p->a[i] = p->a[i+1];
109 }
110 p->cnt--;
111 return e;
112 }
113
+3 -3
--- src/tag.c
+++ src/tag.c
@@ -44,11 +44,11 @@
4444
Stmt ins; /* INSERT INTO tagxref */
4545
Stmt eventupdate; /* UPDATE event */
4646
4747
assert( tagType==0 || tagType==2 );
4848
pqueue_init(&queue);
49
- pqueue_insert(&queue, pid, 0.0);
49
+ pqueue_insert(&queue, pid, 0.0, 0);
5050
5151
/* Query for children of :pid to which to propagate the tag.
5252
** Three returns: (1) rid of the child. (2) timestamp of child.
5353
** (3) True to propagate or false to block.
5454
*/
@@ -79,18 +79,18 @@
7979
if( tagid==TAG_BGCOLOR ){
8080
db_prepare(&eventupdate,
8181
"UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
8282
);
8383
}
84
- while( (pid = pqueue_extract(&queue))!=0 ){
84
+ while( (pid = pqueue_extract(&queue, 0))!=0 ){
8585
db_bind_int(&s, ":pid", pid);
8686
while( db_step(&s)==SQLITE_ROW ){
8787
int doit = db_column_int(&s, 2);
8888
if( doit ){
8989
int cid = db_column_int(&s, 0);
9090
double mtime = db_column_double(&s, 1);
91
- pqueue_insert(&queue, cid, mtime);
91
+ pqueue_insert(&queue, cid, mtime, 0);
9292
db_bind_int(&ins, ":rid", cid);
9393
db_step(&ins);
9494
db_reset(&ins);
9595
if( tagid==TAG_BGCOLOR ){
9696
db_bind_int(&eventupdate, ":rid", cid);
9797
--- src/tag.c
+++ src/tag.c
@@ -44,11 +44,11 @@
44 Stmt ins; /* INSERT INTO tagxref */
45 Stmt eventupdate; /* UPDATE event */
46
47 assert( tagType==0 || tagType==2 );
48 pqueue_init(&queue);
49 pqueue_insert(&queue, pid, 0.0);
50
51 /* Query for children of :pid to which to propagate the tag.
52 ** Three returns: (1) rid of the child. (2) timestamp of child.
53 ** (3) True to propagate or false to block.
54 */
@@ -79,18 +79,18 @@
79 if( tagid==TAG_BGCOLOR ){
80 db_prepare(&eventupdate,
81 "UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
82 );
83 }
84 while( (pid = pqueue_extract(&queue))!=0 ){
85 db_bind_int(&s, ":pid", pid);
86 while( db_step(&s)==SQLITE_ROW ){
87 int doit = db_column_int(&s, 2);
88 if( doit ){
89 int cid = db_column_int(&s, 0);
90 double mtime = db_column_double(&s, 1);
91 pqueue_insert(&queue, cid, mtime);
92 db_bind_int(&ins, ":rid", cid);
93 db_step(&ins);
94 db_reset(&ins);
95 if( tagid==TAG_BGCOLOR ){
96 db_bind_int(&eventupdate, ":rid", cid);
97
--- src/tag.c
+++ src/tag.c
@@ -44,11 +44,11 @@
44 Stmt ins; /* INSERT INTO tagxref */
45 Stmt eventupdate; /* UPDATE event */
46
47 assert( tagType==0 || tagType==2 );
48 pqueue_init(&queue);
49 pqueue_insert(&queue, pid, 0.0, 0);
50
51 /* Query for children of :pid to which to propagate the tag.
52 ** Three returns: (1) rid of the child. (2) timestamp of child.
53 ** (3) True to propagate or false to block.
54 */
@@ -79,18 +79,18 @@
79 if( tagid==TAG_BGCOLOR ){
80 db_prepare(&eventupdate,
81 "UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
82 );
83 }
84 while( (pid = pqueue_extract(&queue, 0))!=0 ){
85 db_bind_int(&s, ":pid", pid);
86 while( db_step(&s)==SQLITE_ROW ){
87 int doit = db_column_int(&s, 2);
88 if( doit ){
89 int cid = db_column_int(&s, 0);
90 double mtime = db_column_double(&s, 1);
91 pqueue_insert(&queue, cid, mtime, 0);
92 db_bind_int(&ins, ":rid", cid);
93 db_step(&ins);
94 db_reset(&ins);
95 if( tagid==TAG_BGCOLOR ){
96 db_bind_int(&eventupdate, ":rid", cid);
97
+21 -11
--- src/timeline.c
+++ src/timeline.c
@@ -763,10 +763,12 @@
763763
const char *zThisUser = 0; /* Suppress links to this user */
764764
HQuery url; /* URL for various branch links */
765765
int from_rid = name_to_rid(P("from")); /* from= for path timelines */
766766
int to_rid = name_to_rid(P("to")); /* to= for path timelines */
767767
int noMerge = P("nomerge")!=0; /* Do not follow merge links */
768
+ int me_rid = name_to_rid(P("me")); /* me= for common ancestory path */
769
+ int you_rid = name_to_rid(P("you"));/* you= for common ancst path */
768770
769771
/* To view the timeline, must have permission to read project data.
770772
*/
771773
login_check_credentials();
772774
if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ login_needed(); return; }
@@ -795,38 +797,46 @@
795797
blob_zero(&desc);
796798
blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
797799
blob_append(&sql, timeline_query_for_www(), -1);
798800
url_initialize(&url, "timeline");
799801
if( !useDividers ) url_add_parameter(&url, "nd", 0);
800
- if( from_rid && to_rid && g.okRead ){
802
+ if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.okRead ){
801803
/* If from= and to= are present, display all nodes on a path connecting
802804
** the two */
803
- PathNode *p;
804
- const char *z;
805
+ PathNode *p = 0;
806
+ const char *zFrom = 0;
807
+ const char *zTo = 0;
805808
806
- path_shortest(from_rid, to_rid, noMerge);
807
- p = path_reverse_path();
809
+ if( from_rid && to_rid ){
810
+ p = path_shortest(from_rid, to_rid, noMerge);
811
+ zFrom = P("from");
812
+ zTo = P("to");
813
+ }else{
814
+ if( path_common_ancestor(me_rid, you_rid) ){
815
+ p = path_first();
816
+ }
817
+ zFrom = P("me");
818
+ zTo = P("you");
819
+ }
808820
blob_append(&sql, " AND event.objid IN (0", -1);
809821
while( p ){
810822
blob_appendf(&sql, ",%d", p->rid);
811823
p = p->u.pTo;
812824
}
813825
blob_append(&sql, ")", -1);
814826
path_reset();
815827
blob_append(&desc, "All nodes on the path from ", -1);
816
- z = P("from");
817828
if( g.okHistory ){
818
- blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>", g.zTop, z, z);
829
+ blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>", g.zTop,zFrom,zFrom);
819830
}else{
820
- blob_appendf(&desc, "[%h]", z);
831
+ blob_appendf(&desc, "[%h]", zFrom);
821832
}
822833
blob_append(&desc, " and ", -1);
823
- z = P("to");
824834
if( g.okHistory ){
825
- blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>.", g.zTop, z, z);
835
+ blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>.", g.zTop, zTo, zTo);
826836
}else{
827
- blob_appendf(&desc, "[%h].", z);
837
+ blob_appendf(&desc, "[%h].", zTo);
828838
}
829839
tmFlags |= TIMELINE_DISJOINT;
830840
db_multi_exec("%s", blob_str(&sql));
831841
}else if( (p_rid || d_rid) && g.okRead ){
832842
/* If p= or d= is present, ignore all other parameters other than n= */
833843
--- src/timeline.c
+++ src/timeline.c
@@ -763,10 +763,12 @@
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; }
@@ -795,38 +797,46 @@
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 PathNode *p;
804 const char *z;
 
805
806 path_shortest(from_rid, to_rid, noMerge);
807 p = path_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 path_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 tmFlags |= TIMELINE_DISJOINT;
830 db_multi_exec("%s", blob_str(&sql));
831 }else if( (p_rid || d_rid) && g.okRead ){
832 /* If p= or d= is present, ignore all other parameters other than n= */
833
--- src/timeline.c
+++ src/timeline.c
@@ -763,10 +763,12 @@
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 int me_rid = name_to_rid(P("me")); /* me= for common ancestory path */
769 int you_rid = name_to_rid(P("you"));/* you= for common ancst path */
770
771 /* To view the timeline, must have permission to read project data.
772 */
773 login_check_credentials();
774 if( !g.okRead && !g.okRdTkt && !g.okRdWiki ){ login_needed(); return; }
@@ -795,38 +797,46 @@
797 blob_zero(&desc);
798 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
799 blob_append(&sql, timeline_query_for_www(), -1);
800 url_initialize(&url, "timeline");
801 if( !useDividers ) url_add_parameter(&url, "nd", 0);
802 if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.okRead ){
803 /* If from= and to= are present, display all nodes on a path connecting
804 ** the two */
805 PathNode *p = 0;
806 const char *zFrom = 0;
807 const char *zTo = 0;
808
809 if( from_rid && to_rid ){
810 p = path_shortest(from_rid, to_rid, noMerge);
811 zFrom = P("from");
812 zTo = P("to");
813 }else{
814 if( path_common_ancestor(me_rid, you_rid) ){
815 p = path_first();
816 }
817 zFrom = P("me");
818 zTo = P("you");
819 }
820 blob_append(&sql, " AND event.objid IN (0", -1);
821 while( p ){
822 blob_appendf(&sql, ",%d", p->rid);
823 p = p->u.pTo;
824 }
825 blob_append(&sql, ")", -1);
826 path_reset();
827 blob_append(&desc, "All nodes on the path from ", -1);
 
828 if( g.okHistory ){
829 blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>", g.zTop,zFrom,zFrom);
830 }else{
831 blob_appendf(&desc, "[%h]", zFrom);
832 }
833 blob_append(&desc, " and ", -1);
 
834 if( g.okHistory ){
835 blob_appendf(&desc, "<a href='%s/info/%h'>[%h]</a>.", g.zTop, zTo, zTo);
836 }else{
837 blob_appendf(&desc, "[%h].", zTo);
838 }
839 tmFlags |= TIMELINE_DISJOINT;
840 db_multi_exec("%s", blob_str(&sql));
841 }else if( (p_rid || d_rid) && g.okRead ){
842 /* If p= or d= is present, ignore all other parameters other than n= */
843

Keyboard Shortcuts

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