Fossil SCM

fossil-scm / src / descendants.c
Source Blame History 725 lines
6458f02… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2007 D. Richard Hipp
6458f02… drh 3 **
6458f02… drh 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
c06edd2… drh 7
6458f02… drh 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
6458f02… drh 11 **
6458f02… drh 12 ** Author contact information:
6458f02… drh 13 ** [email protected]
6458f02… drh 14 ** http://www.hwaci.com/drh/
6458f02… drh 15 **
6458f02… drh 16 *******************************************************************************
6458f02… drh 17 **
db0c512… drh 18 ** This file contains code used to find descendants of a version
6458f02… drh 19 ** or leaves of a version tree.
6458f02… drh 20 */
6458f02… drh 21 #include "config.h"
6458f02… drh 22 #include "descendants.h"
6458f02… drh 23 #include <assert.h>
6458f02… drh 24
6458f02… drh 25
6458f02… drh 26 /*
6458f02… drh 27 ** Create a temporary table named "leaves" if it does not
6458f02… drh 28 ** already exist. Load this table with the RID of all
db0c512… drh 29 ** check-ins that are leaves which are descended from
5ac4e15… drh 30 ** check-in iBase.
b6e22e6… drh 31 **
42c2a18… drh 32 ** A "leaf" is a check-in that has no children in the same branch.
5ac4e15… drh 33 ** There is a separate permanent table LEAF that contains all leaves
5ac4e15… drh 34 ** in the tree. This routine is used to compute a subset of that
c49030f… drh 35 ** table consisting of leaves that are descended from a single check-in.
b6e22e6… drh 36 **
b6e22e6… drh 37 ** The closeMode flag determines behavior associated with the "closed"
b6e22e6… drh 38 ** tag:
b6e22e6… drh 39 **
b6e22e6… drh 40 ** closeMode==0 Show all leaves regardless of the "closed" tag.
b6e22e6… drh 41 **
b6e22e6… drh 42 ** closeMode==1 Show only leaves without the "closed" tag.
b6e22e6… drh 43 **
b6e22e6… drh 44 ** closeMode==2 Show only leaves with the "closed" tag.
b6e22e6… drh 45 **
b6e22e6… drh 46 ** The default behavior is to ignore closed leaves (closeMode==0). To
b6e22e6… drh 47 ** Show all leaves, use closeMode==1. To show only closed leaves, use
b6e22e6… drh 48 ** closeMode==2.
6458f02… drh 49 */
b6e22e6… drh 50 void compute_leaves(int iBase, int closeMode){
6458f02… drh 51
042a08b… drh 52 /* Create the LEAVES table if it does not already exist. Make sure
042a08b… drh 53 ** it is empty.
042a08b… drh 54 */
6458f02… drh 55 db_multi_exec(
6458f02… drh 56 "CREATE TEMP TABLE IF NOT EXISTS leaves("
6458f02… drh 57 " rid INTEGER PRIMARY KEY"
6458f02… drh 58 ");"
6458f02… drh 59 "DELETE FROM leaves;"
6458f02… drh 60 );
042a08b… drh 61
5ac4e15… drh 62 if( iBase>0 ){
6953ca8… drh 63 Bag seen; /* Descendants seen */
6953ca8… drh 64 Bag pending; /* Unpropagated descendants */
6953ca8… drh 65 Stmt q1; /* Query to find children of a check-in */
6953ca8… drh 66 Stmt isBr; /* Query to check to see if a check-in starts a new branch */
6953ca8… drh 67 Stmt ins; /* INSERT statement for a new record */
6953ca8… drh 68
6953ca8… drh 69 /* Initialize the bags. */
6953ca8… drh 70 bag_init(&seen);
6953ca8… drh 71 bag_init(&pending);
6953ca8… drh 72 bag_insert(&pending, iBase);
6953ca8… drh 73
6953ca8… drh 74 /* This query returns all non-branch-merge children of check-in :rid.
6953ca8… drh 75 **
8b17c23… jan.nijtmans 76 ** If a child is a merge of a fork within the same branch, it is
6953ca8… drh 77 ** returned. Only merge children in different branches are excluded.
6953ca8… drh 78 */
6953ca8… drh 79 db_prepare(&q1,
6953ca8… drh 80 "SELECT cid FROM plink"
6953ca8… drh 81 " WHERE pid=:rid"
6953ca8… drh 82 " AND (isprim"
6953ca8… drh 83 " OR coalesce((SELECT value FROM tagxref"
6953ca8… drh 84 " WHERE tagid=%d AND rid=plink.pid), 'trunk')"
6953ca8… drh 85 "=coalesce((SELECT value FROM tagxref"
6953ca8… drh 86 " WHERE tagid=%d AND rid=plink.cid), 'trunk'))",
6953ca8… drh 87 TAG_BRANCH, TAG_BRANCH
6953ca8… drh 88 );
8b17c23… jan.nijtmans 89
6953ca8… drh 90 /* This query returns a single row if check-in :rid is the first
6953ca8… drh 91 ** check-in of a new branch.
6953ca8… drh 92 */
8b17c23… jan.nijtmans 93 db_prepare(&isBr,
6953ca8… drh 94 "SELECT 1 FROM tagxref"
6953ca8… drh 95 " WHERE rid=:rid AND tagid=%d AND tagtype=2"
6953ca8… drh 96 " AND srcid>0",
6953ca8… drh 97 TAG_BRANCH
6953ca8… drh 98 );
8b17c23… jan.nijtmans 99
6953ca8… drh 100 /* This statement inserts check-in :rid into the LEAVES table.
6953ca8… drh 101 */
6953ca8… drh 102 db_prepare(&ins, "INSERT OR IGNORE INTO leaves VALUES(:rid)");
8b17c23… jan.nijtmans 103
6953ca8… drh 104 while( bag_count(&pending) ){
6953ca8… drh 105 int rid = bag_first(&pending);
6953ca8… drh 106 int cnt = 0;
6953ca8… drh 107 bag_remove(&pending, rid);
6953ca8… drh 108 db_bind_int(&q1, ":rid", rid);
6953ca8… drh 109 while( db_step(&q1)==SQLITE_ROW ){
6953ca8… drh 110 int cid = db_column_int(&q1, 0);
6953ca8… drh 111 if( bag_insert(&seen, cid) ){
6953ca8… drh 112 bag_insert(&pending, cid);
6953ca8… drh 113 }
6953ca8… drh 114 db_bind_int(&isBr, ":rid", cid);
6953ca8… drh 115 if( db_step(&isBr)==SQLITE_DONE ){
6953ca8… drh 116 cnt++;
6953ca8… drh 117 }
6953ca8… drh 118 db_reset(&isBr);
6953ca8… drh 119 }
6953ca8… drh 120 db_reset(&q1);
6953ca8… drh 121 if( cnt==0 && !is_a_leaf(rid) ){
6953ca8… drh 122 cnt++;
6953ca8… drh 123 }
6953ca8… drh 124 if( cnt==0 ){
6953ca8… drh 125 db_bind_int(&ins, ":rid", rid);
6953ca8… drh 126 db_step(&ins);
6953ca8… drh 127 db_reset(&ins);
6953ca8… drh 128 }
6953ca8… drh 129 }
6953ca8… drh 130 db_finalize(&ins);
6953ca8… drh 131 db_finalize(&isBr);
6953ca8… drh 132 db_finalize(&q1);
6953ca8… drh 133 bag_clear(&pending);
6953ca8… drh 134 bag_clear(&seen);
c6c05c8… jan.nijtmans 135 }else{
c6c05c8… jan.nijtmans 136 db_multi_exec(
c6c05c8… jan.nijtmans 137 "INSERT INTO leaves"
c6c05c8… jan.nijtmans 138 " SELECT leaf.rid FROM leaf"
c6c05c8… jan.nijtmans 139 );
6953ca8… drh 140 }
b6e22e6… drh 141 if( closeMode==1 ){
b6e22e6… drh 142 db_multi_exec(
b6e22e6… drh 143 "DELETE FROM leaves WHERE rid IN"
b6e22e6… drh 144 " (SELECT leaves.rid FROM leaves, tagxref"
b6e22e6… drh 145 " WHERE tagxref.rid=leaves.rid "
b6e22e6… drh 146 " AND tagxref.tagid=%d"
b6e22e6… drh 147 " AND tagxref.tagtype>0)",
b6e22e6… drh 148 TAG_CLOSED
b6e22e6… drh 149 );
b6e22e6… drh 150 }else if( closeMode==2 ){
b6e22e6… drh 151 db_multi_exec(
b6e22e6… drh 152 "DELETE FROM leaves WHERE rid NOT IN"
b6e22e6… drh 153 " (SELECT leaves.rid FROM leaves, tagxref"
b6e22e6… drh 154 " WHERE tagxref.rid=leaves.rid "
b6e22e6… drh 155 " AND tagxref.tagid=%d"
b6e22e6… drh 156 " AND tagxref.tagtype>0)",
b6e22e6… drh 157 TAG_CLOSED
b6e22e6… drh 158 );
b6e22e6… drh 159 }
b6e22e6… drh 160 }
b6e22e6… drh 161
b6e22e6… drh 162 /*
4e23c2a… drh 163 ** If RID refers to a check-in, return the mtime of that check-in - the
e2bdc10… danield 164 ** Julian day number of when the check-in occurred.
4e23c2a… drh 165 */
4e23c2a… drh 166 double mtime_of_rid(int rid, double mtime){
4e23c2a… drh 167 static Stmt q;
4e23c2a… drh 168 db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid");
4e23c2a… drh 169 db_bind_int(&q, ":rid", rid);
4e23c2a… drh 170 if( db_step(&q)==SQLITE_ROW ){
4e23c2a… drh 171 mtime = db_column_double(&q,0);
4e23c2a… drh 172 }
4e23c2a… drh 173 db_reset(&q);
4e23c2a… drh 174 return mtime;
4e23c2a… drh 175 }
4e23c2a… drh 176
4e23c2a… drh 177
4e23c2a… drh 178
4e23c2a… drh 179 /*
def9af4… andygoth 180 ** Load the record ID rid and up to |N|-1 closest ancestors into
8b7a979… drh 181 ** the "ok" table. If N is zero, no limit. If ridBackTo is not zero
8b7a979… drh 182 ** then stop the search upon reaching the ancestor with rid==ridBackTo.
def9af4… andygoth 183 */
8b7a979… drh 184 void compute_ancestors(int rid, int N, int directOnly, int ridBackTo){
def9af4… andygoth 185 if( !N ){
def9af4… andygoth 186 N = -1;
def9af4… andygoth 187 }else if( N<0 ){
def9af4… andygoth 188 N = -N;
def9af4… andygoth 189 }
5a23a72… drh 190 if( directOnly ){
c1abd61… drh 191 /* Direct mode means to show primary parents only */
5a23a72… drh 192 db_multi_exec(
5a23a72… drh 193 "WITH RECURSIVE "
5a23a72… drh 194 " ancestor(rid, mtime) AS ("
5a23a72… drh 195 " SELECT %d, mtime FROM event WHERE objid=%d "
5a23a72… drh 196 " UNION "
5a23a72… drh 197 " SELECT plink.pid, event.mtime"
5a23a72… drh 198 " FROM ancestor, plink, event"
5a23a72… drh 199 " WHERE plink.cid=ancestor.rid"
5a23a72… drh 200 " AND event.objid=plink.pid"
5a23a72… drh 201 " AND plink.isPrim"
5a23a72… drh 202 " ORDER BY mtime DESC LIMIT %d"
5a23a72… drh 203 " )"
5a23a72… drh 204 "INSERT INTO ok"
5a23a72… drh 205 " SELECT rid FROM ancestor;",
5a23a72… drh 206 rid, rid, N
5a23a72… drh 207 );
5a23a72… drh 208 }else{
c1abd61… drh 209 /* If not in directMode, also include merge parents, including
c1abd61… drh 210 ** cherrypick merges. Except, terminate searches at the cherrypick
c1abd61… drh 211 ** merge parent itself. In other words, include:
c1abd61… drh 212 ** (1) Primary parents
c1abd61… drh 213 ** (2) Merge parents
c1abd61… drh 214 ** (3) Cherrypick merge parents.
c1abd61… drh 215 ** (4) All ancestores of 1 and 2 but not of 3.
c1abd61… drh 216 */
8b7a979… drh 217 double rLimitMtime = 0.0;
8b7a979… drh 218 if( ridBackTo ){
4e23c2a… drh 219 rLimitMtime = mtime_of_rid(ridBackTo, 0.0);
8b7a979… drh 220 }
5a23a72… drh 221 db_multi_exec(
ebd239d… drh 222 "WITH RECURSIVE\n"
ebd239d… drh 223 " parent(pid,cid,isCP) AS (\n"
ebd239d… drh 224 " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
ebd239d… drh 225 " UNION ALL\n"
ebd239d… drh 226 " SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"
ebd239d… drh 227 " ),\n"
ebd239d… drh 228 " ancestor(rid, mtime, isCP) AS (\n"
ebd239d… drh 229 " SELECT %d, mtime, 0 FROM event WHERE objid=%d\n"
ebd239d… drh 230 " UNION\n"
ebd239d… drh 231 " SELECT parent.pid, event.mtime, parent.isCP\n"
ebd239d… drh 232 " FROM ancestor, parent, event\n"
ebd239d… drh 233 " WHERE parent.cid=ancestor.rid\n"
ebd239d… drh 234 " AND event.objid=parent.pid\n"
ebd239d… drh 235 " AND NOT ancestor.isCP\n"
ebd239d… drh 236 " AND (event.mtime>=%.17g OR parent.pid=%d)\n"
ebd239d… drh 237 " ORDER BY mtime DESC LIMIT %d\n"
ebd239d… drh 238 " )\n"
ebd239d… drh 239 "INSERT OR IGNORE INTO ok SELECT rid FROM ancestor;",
048a74b… drh 240 rid, rid, rLimitMtime, ridBackTo, N
5a23a72… drh 241 );
048a74b… drh 242 if( ridBackTo && db_changes()>1 ){
048a74b… drh 243 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
048a74b… drh 244 }
5a23a72… drh 245 }
e632a09… drh 246 }
e632a09… drh 247
e632a09… drh 248 /*
e632a09… drh 249 ** Compute the youngest ancestor of record ID rid that is a member of
e632a09… drh 250 ** branch zBranch.
e632a09… drh 251 */
e632a09… drh 252 int compute_youngest_ancestor_in_branch(int rid, const char *zBranch){
e632a09… drh 253 return db_int(0,
f2ebd7e… drh 254 "WITH RECURSIVE "
f2ebd7e… drh 255 " ancestor(rid, mtime) AS ("
f2ebd7e… drh 256 " SELECT %d, mtime FROM event WHERE objid=%d "
f2ebd7e… drh 257 " UNION "
f2ebd7e… drh 258 " SELECT plink.pid, event.mtime"
f2ebd7e… drh 259 " FROM ancestor, plink, event"
f2ebd7e… drh 260 " WHERE plink.cid=ancestor.rid"
e632a09… drh 261 " AND event.objid=plink.pid"
e632a09… drh 262 " ORDER BY mtime DESC"
f2ebd7e… drh 263 " )"
e632a09… drh 264 " SELECT ancestor.rid FROM ancestor"
e632a09… drh 265 " WHERE EXISTS(SELECT 1 FROM tagxref"
ecaeb31… florian 266 " WHERE tagid=%d AND tagxref.rid=ancestor.rid"
e632a09… drh 267 " AND value=%Q AND tagtype>0)"
60debc7… drh 268 " ORDER BY mtime DESC"
e632a09… drh 269 " LIMIT 1",
e632a09… drh 270 rid, rid, TAG_BRANCH, zBranch
f2ebd7e… drh 271 );
621be70… drh 272 }
621be70… drh 273
621be70… drh 274 /*
6599d09… drh 275 ** Compute all direct ancestors (merge ancestors do not count)
621be70… drh 276 ** for the check-in rid and put them in a table named "ancestor".
621be70… drh 277 ** Label each generation with consecutive integers going backwards
621be70… drh 278 ** in time such that rid has the smallest generation number and the oldest
621be70… drh 279 ** direct ancestor as the largest generation number.
621be70… drh 280 */
6599d09… drh 281 void compute_direct_ancestors(int rid){
841772c… drh 282 db_multi_exec(
1c40de1… drh 283 "CREATE TEMP TABLE IF NOT EXISTS ancestor(rid INTEGER UNIQUE NOT NULL,"
9ba8a39… drh 284 " generation INTEGER PRIMARY KEY);"
841772c… drh 285 "DELETE FROM ancestor;"
6599d09… drh 286 "WITH RECURSIVE g(x,i) AS ("
6599d09… drh 287 " VALUES(%d,1)"
6599d09… drh 288 " UNION ALL"
6599d09… drh 289 " SELECT plink.pid, g.i+1 FROM plink, g"
6599d09… drh 290 " WHERE plink.cid=g.x AND plink.isprim)"
c3b5f1d… jan.nijtmans 291 "INSERT INTO ancestor(rid,generation) SELECT x,i FROM g;",
6599d09… drh 292 rid
6599d09… drh 293 );
9ba8a39… drh 294 }
9ba8a39… drh 295
9ba8a39… drh 296 /*
9ba8a39… drh 297 ** Compute the "mtime" of the file given whose blob.rid is "fid" that
9ba8a39… drh 298 ** is part of check-in "vid". The mtime will be the mtime on vid or
9ba8a39… drh 299 ** some ancestor of vid where fid first appears.
9ba8a39… drh 300 */
9ba8a39… drh 301 int mtime_of_manifest_file(
9ba8a39… drh 302 int vid, /* The check-in that contains fid */
9ba8a39… drh 303 int fid, /* The id of the file whose check-in time is sought */
9ba8a39… drh 304 i64 *pMTime /* Write result here */
9ba8a39… drh 305 ){
9ba8a39… drh 306 static int prevVid = -1;
9ba8a39… drh 307 static Stmt q;
9ba8a39… drh 308
9ba8a39… drh 309 if( prevVid!=vid ){
9ba8a39… drh 310 prevVid = vid;
e776494… andygoth 311 db_multi_exec("CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
e776494… andygoth 312 "DELETE FROM ok;");
8b7a979… drh 313 compute_ancestors(vid, 100000000, 1, 0);
9ba8a39… drh 314 }
9ba8a39… drh 315 db_static_prepare(&q,
9ba8a39… drh 316 "SELECT (max(event.mtime)-2440587.5)*86400 FROM mlink, event"
9ba8a39… drh 317 " WHERE mlink.mid=event.objid"
9ba8a39… drh 318 " AND +mlink.mid IN ok"
9ba8a39… drh 319 " AND mlink.fid=:fid");
9ba8a39… drh 320 db_bind_int(&q, ":fid", fid);
9ba8a39… drh 321 if( db_step(&q)!=SQLITE_ROW ){
841772c… drh 322 db_reset(&q);
9ba8a39… drh 323 return 1;
9ba8a39… drh 324 }
9ba8a39… drh 325 *pMTime = db_column_int64(&q, 0);
9ba8a39… drh 326 db_reset(&q);
9ba8a39… drh 327 return 0;
841772c… drh 328 }
841772c… drh 329
841772c… drh 330 /*
def9af4… andygoth 331 ** Load the record ID rid and up to |N|-1 closest descendants into
def9af4… andygoth 332 ** the "ok" table. If N is zero, no limit.
841772c… drh 333 */
841772c… drh 334 void compute_descendants(int rid, int N){
def9af4… andygoth 335 if( !N ){
def9af4… andygoth 336 N = -1;
def9af4… andygoth 337 }else if( N<0 ){
def9af4… andygoth 338 N = -N;
def9af4… andygoth 339 }
31fcde8… drh 340 db_multi_exec(
ebd239d… drh 341 "WITH RECURSIVE\n"
ebd239d… drh 342 " dx(rid,mtime) AS (\n"
ebd239d… drh 343 " SELECT %d, 0\n"
ebd239d… drh 344 " UNION\n"
ebd239d… drh 345 " SELECT plink.cid, plink.mtime FROM dx, plink\n"
ebd239d… drh 346 " WHERE plink.pid=dx.rid\n"
ebd239d… drh 347 " ORDER BY 2\n"
ebd239d… drh 348 " )\n"
31fcde8… drh 349 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
31fcde8… drh 350 rid, N
31fcde8… drh 351 );
841772c… drh 352 }
841772c… drh 353
841772c… drh 354 /*
841772c… drh 355 ** COMMAND: descendants*
841772c… drh 356 **
a81a47f… drh 357 ** Usage: %fossil descendants ?CHECKIN? ?OPTIONS?
841772c… drh 358 **
c49030f… drh 359 ** Find all leaf descendants of the check-in specified or if the argument
c49030f… drh 360 ** is omitted, of the check-in currently checked out.
841772c… drh 361 **
841772c… drh 362 ** Options:
decd537… stephan 363 ** -R|--repository REPO Extract info from repository REPO
11384f1… drh 364 ** -W|--width N Width of lines (default is to auto-detect).
11384f1… drh 365 ** Must be greater than 20 or else 0 for no
11384f1… drh 366 ** limit, resulting in a one line per entry.
841772c… drh 367 **
c965636… drh 368 ** See also: [[finfo]], [[info]], [[leaves]]
841772c… drh 369 */
841772c… drh 370 void descendants_cmd(void){
841772c… drh 371 Stmt q;
e3df30f… mistachkin 372 int base, width;
e3df30f… mistachkin 373 const char *zWidth;
841772c… drh 374
841772c… drh 375 db_find_and_open_repository(0,0);
e3df30f… mistachkin 376 zWidth = find_option("width","W",1);
e3df30f… mistachkin 377 if( zWidth ){
e3df30f… mistachkin 378 width = atoi(zWidth);
e3df30f… mistachkin 379 if( (width!=0) && (width<=20) ){
e3df30f… mistachkin 380 fossil_fatal("-W|--width value must be >20 or 0");
e3df30f… mistachkin 381 }
e3df30f… mistachkin 382 }else{
e3df30f… mistachkin 383 width = -1;
e3df30f… mistachkin 384 }
74ac0c9… drh 385
74ac0c9… drh 386 /* We should be done with options.. */
74ac0c9… drh 387 verify_all_options();
74ac0c9… drh 388
2a013f0… drh 389 if( g.argc==2 ){
2a013f0… drh 390 base = db_lget_int("checkout", 0);
2a013f0… drh 391 }else{
2a013f0… drh 392 base = name_to_typed_rid(g.argv[2], "ci");
b6e22e6… drh 393 }
b6e22e6… drh 394 if( base==0 ) return;
b6e22e6… drh 395 compute_leaves(base, 0);
6458f02… drh 396 db_prepare(&q,
6458f02… drh 397 "%s"
6458f02… drh 398 " AND event.objid IN (SELECT rid FROM leaves)"
6458f02… drh 399 " ORDER BY event.mtime DESC",
6458f02… drh 400 timeline_query_for_tty()
6458f02… drh 401 );
e86aeb7… drh 402 print_timeline(&q, 0, width, 0, 0);
5ac4e15… drh 403 db_finalize(&q);
5ac4e15… drh 404 }
5ac4e15… drh 405
5ac4e15… drh 406 /*
841772c… drh 407 ** COMMAND: leaves*
7bef5bf… drh 408 **
7bef5bf… drh 409 ** Usage: %fossil leaves ?OPTIONS?
5ac4e15… drh 410 **
7bef5bf… drh 411 ** Find leaves of all branches. By default show only open leaves.
8b17c23… jan.nijtmans 412 ** The -a|--all flag causes all leaves (closed and open) to be shown.
8b17c23… jan.nijtmans 413 ** The -c|--closed flag shows only closed leaves.
5ac4e15… drh 414 **
5ac4e15… drh 415 ** The --recompute flag causes the content of the "leaf" table in the
5ac4e15… drh 416 ** repository database to be recomputed.
2210be1… drh 417 **
2210be1… drh 418 ** Options:
4cb50c4… stephan 419 ** -a|--all Show ALL leaves
4cb50c4… stephan 420 ** --bybranch Order output by branch name
4cb50c4… stephan 421 ** -c|--closed Show only closed leaves
4cb50c4… stephan 422 ** -m|--multiple Show only cases with multiple leaves on a single branch
4cb50c4… stephan 423 ** --recompute Recompute the "leaf" table in the repository DB
11384f1… drh 424 ** -W|--width N Width of lines (default is to auto-detect). Must be
11384f1… drh 425 ** more than 39 or else 0 no limit, resulting in a single
11384f1… drh 426 ** line per entry.
2210be1… drh 427 **
c965636… drh 428 ** See also: [[descendants]], [[finfo]], [[info]], [[branch]]
5ac4e15… drh 429 */
5ac4e15… drh 430 void leaves_cmd(void){
5ac4e15… drh 431 Stmt q;
5ac4e15… drh 432 Blob sql;
8b17c23… jan.nijtmans 433 int showAll = find_option("all", "a", 0)!=0;
8b17c23… jan.nijtmans 434 int showClosed = find_option("closed", "c", 0)!=0;
5ac4e15… drh 435 int recomputeFlag = find_option("recompute",0,0)!=0;
f76d912… drh 436 int byBranch = find_option("bybranch",0,0)!=0;
7bef5bf… drh 437 int multipleFlag = find_option("multiple","m",0)!=0;
fa6ece7… jan.nijtmans 438 const char *zWidth = find_option("width","W",1);
b3bac11… drh 439 char *zLastBr = 0;
fa6ece7… jan.nijtmans 440 int n, width;
f76d912… drh 441 char zLineNo[10];
ca0d66b… danield 442 const char *zMainBranch = db_main_branch();
4fe2214… drh 443
7bef5bf… drh 444 if( multipleFlag ) byBranch = 1;
fa6ece7… jan.nijtmans 445 if( zWidth ){
fa6ece7… jan.nijtmans 446 width = atoi(zWidth);
fa6ece7… jan.nijtmans 447 if( (width!=0) && (width<=39) ){
fa6ece7… jan.nijtmans 448 fossil_fatal("-W|--width value must be >39 or 0");
fa6ece7… jan.nijtmans 449 }
fa6ece7… jan.nijtmans 450 }else{
0ff6a9e… mistachkin 451 width = -1;
fa6ece7… jan.nijtmans 452 }
4fe2214… drh 453 db_find_and_open_repository(0,0);
e1ee31a… jan.nijtmans 454
74ac0c9… drh 455 /* We should be done with options.. */
74ac0c9… drh 456 verify_all_options();
74ac0c9… drh 457
5ac4e15… drh 458 if( recomputeFlag ) leaf_rebuild();
5ac4e15… drh 459 blob_zero(&sql);
5ac4e15… drh 460 blob_append(&sql, timeline_query_for_tty(), -1);
7bef5bf… drh 461 if( !multipleFlag ){
fc3d9f5… jan.nijtmans 462 /* The usual case - show all leaves */
7bef5bf… drh 463 blob_append_sql(&sql, " AND blob.rid IN leaf");
7bef5bf… drh 464 }else{
7bef5bf… drh 465 /* Show only leaves where two are more occur in the same branch */
7bef5bf… drh 466 db_multi_exec(
7bef5bf… drh 467 "CREATE TEMP TABLE openLeaf(rid INTEGER PRIMARY KEY);"
7bef5bf… drh 468 "INSERT INTO openLeaf(rid)"
7bef5bf… drh 469 " SELECT rid FROM leaf"
7bef5bf… drh 470 " WHERE NOT EXISTS("
7bef5bf… drh 471 " SELECT 1 FROM tagxref"
7bef5bf… drh 472 " WHERE tagid=%d AND tagtype>0 AND rid=leaf.rid);",
7bef5bf… drh 473 TAG_CLOSED
7bef5bf… drh 474 );
7bef5bf… drh 475 db_multi_exec(
7bef5bf… drh 476 "CREATE TEMP TABLE ambiguousBranch(brname TEXT);"
7bef5bf… drh 477 "INSERT INTO ambiguousBranch(brname)"
7bef5bf… drh 478 " SELECT (SELECT value FROM tagxref WHERE tagid=%d AND rid=openLeaf.rid)"
7bef5bf… drh 479 " FROM openLeaf"
7bef5bf… drh 480 " GROUP BY 1 HAVING count(*)>1;",
7bef5bf… drh 481 TAG_BRANCH
7bef5bf… drh 482 );
7bef5bf… drh 483 db_multi_exec(
7bef5bf… drh 484 "CREATE TEMP TABLE ambiguousLeaf(rid INTEGER PRIMARY KEY);\n"
7bef5bf… drh 485 "INSERT INTO ambiguousLeaf(rid)\n"
7bef5bf… drh 486 " SELECT rid FROM openLeaf\n"
7bef5bf… drh 487 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=openLeaf.rid)"
7bef5bf… drh 488 " IN (SELECT brname FROM ambiguousBranch);",
7bef5bf… drh 489 TAG_BRANCH
7bef5bf… drh 490 );
7bef5bf… drh 491 blob_append_sql(&sql, " AND blob.rid IN ambiguousLeaf");
7bef5bf… drh 492 }
5ac4e15… drh 493 if( showClosed ){
49b0ff1… drh 494 blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
7bef5bf… drh 495 }else if( !showAll ){
49b0ff1… drh 496 blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
5ac4e15… drh 497 }
f76d912… drh 498 if( byBranch ){
f76d912… drh 499 db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase,"
f76d912… drh 500 " event.mtime DESC",
49b0ff1… drh 501 blob_sql_text(&sql));
f76d912… drh 502 }else{
49b0ff1… drh 503 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
f76d912… drh 504 }
5ac4e15… drh 505 blob_reset(&sql);
f76d912… drh 506 n = 0;
f76d912… drh 507 while( db_step(&q)==SQLITE_ROW ){
7bef5bf… drh 508 const char *zId = db_column_text(&q, 1);
7bef5bf… drh 509 const char *zDate = db_column_text(&q, 2);
7bef5bf… drh 510 const char *zCom = db_column_text(&q, 3);
7bef5bf… drh 511 const char *zBr = db_column_text(&q, 7);
d21e3c5… stephan 512 char *z = 0;
d21e3c5… stephan 513 char * zBranchPoint = 0;
7bef5bf… drh 514
7bef5bf… drh 515 if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){
7bef5bf… drh 516 fossil_print("*** %s ***\n", zBr);
7bef5bf… drh 517 fossil_free(zLastBr);
7bef5bf… drh 518 zLastBr = fossil_strdup(zBr);
7bef5bf… drh 519 if( multipleFlag ) n = 0;
7bef5bf… drh 520 }
7bef5bf… drh 521 n++;
7bef5bf… drh 522 sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
7bef5bf… drh 523 fossil_print("%6s ", zLineNo);
d21e3c5… stephan 524 if(0!=fossil_strcmp(zBr,zMainBranch)){
d21e3c5… stephan 525 int ridOfRoot;
d21e3c5… stephan 526 z = mprintf("root:%s", zId);
d21e3c5… stephan 527 ridOfRoot = symbolic_name_to_rid(z, "ci");
d21e3c5… stephan 528 if(ridOfRoot>0){
d21e3c5… stephan 529 zBranchPoint = mprintf(" (branched from: [%.*z])", hash_digits(0),
d21e3c5… stephan 530 rid_to_uuid(ridOfRoot));
d21e3c5… stephan 531 }
d21e3c5… stephan 532 fossil_free(z);
d21e3c5… stephan 533 }
d21e3c5… stephan 534 z = mprintf("%s [%S] %s%s", zDate, zId, zCom,
d21e3c5… stephan 535 zBranchPoint ? zBranchPoint : "");
2476b81… drh 536 comment_print(z, zCom, 7, width, get_comment_format());
7bef5bf… drh 537 fossil_free(z);
d21e3c5… stephan 538 fossil_free(zBranchPoint);
f76d912… drh 539 }
f76d912… drh 540 fossil_free(zLastBr);
5ac4e15… drh 541 db_finalize(&q);
5ac4e15… drh 542 }
5ac4e15… drh 543
5ac4e15… drh 544 /*
26eef7f… rberteig 545 ** WEBPAGE: leaves
7ab0328… drh 546 **
7ab0328… drh 547 ** Show leaf check-ins in a timeline. By default only open leaves
7ab0328… drh 548 ** are listed.
7ab0328… drh 549 **
7ab0328… drh 550 ** A "leaf" is a check-in with no children in the same branch. A
7ab0328… drh 551 ** "closed leaf" is a leaf that has a "closed" tag. An "open leaf"
7ab0328… drh 552 ** is a leaf without a "closed" tag.
7ab0328… drh 553 **
7ab0328… drh 554 ** Query parameters:
5ac4e15… drh 555 **
7ab0328… drh 556 ** all Show all leaves
7ab0328… drh 557 ** closed Show only closed leaves
efb903f… drh 558 ** ng No graph
efb903f… drh 559 ** nohidden Hide check-ins with "hidden" tag
efb903f… drh 560 ** onlyhidden Show only check-ins with "hidden" tag
efb903f… drh 561 ** brbg Background color by branch name
efb903f… drh 562 ** ubg Background color by user name
5ac4e15… drh 563 */
5ac4e15… drh 564 void leaves_page(void){
5ac4e15… drh 565 Blob sql;
5ac4e15… drh 566 Stmt q;
5ac4e15… drh 567 int showAll = P("all")!=0;
5ac4e15… drh 568 int showClosed = P("closed")!=0;
efb903f… drh 569 int fNg = PB("ng")!=0; /* Flag for the "ng" query parameter */
efb903f… drh 570 int fNoHidden = PB("nohidden")!=0; /* "nohidden" query parameter */
efb903f… drh 571 int fOnlyHidden = PB("onlyhidden")!=0; /* "onlyhidden" query parameter */
efb903f… drh 572 int fBrBg = PB("brbg")!=0; /* Flag for the "brbg" query parameter */
efb903f… drh 573 int fUBg = PB("ubg")!=0; /* Flag for the "ubg" query parameter */
efb903f… drh 574 HQuery url; /* URL to /leaves plus query parameters */
efb903f… drh 575 int tmFlags; /* Timeline display flags */
5ac4e15… drh 576
5ac4e15… drh 577 login_check_credentials();
653dd40… drh 578 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
efb903f… drh 579 url_initialize(&url, "leaves");
efb903f… drh 580 if( fNg ) url_add_parameter(&url, "ng", "");
efb903f… drh 581 if( fNoHidden ) url_add_parameter(&url, "nohidden", "");
efb903f… drh 582 if( fOnlyHidden ) url_add_parameter(&url, "onlyhidden", "");
efb903f… drh 583 if( fBrBg ) url_add_parameter(&url, "brbg", "");
efb903f… drh 584 if( fUBg ) url_add_parameter(&url, "ubg", "");
5ac4e15… drh 585 if( !showAll ){
efb903f… drh 586 style_submenu_element("All", "%s", url_render(&url, "all", "", 0, 0));
5ac4e15… drh 587 }
5ac4e15… drh 588 if( !showClosed ){
efb903f… drh 589 style_submenu_element("Closed", "%s", url_render(&url, "closed", "", 0, 0));
5ac4e15… drh 590 }
5ac4e15… drh 591 if( showClosed || showAll ){
efb903f… drh 592 style_submenu_element("Open", "%s", url_render(&url, 0, 0, 0, 0));
5ac4e15… drh 593 }
efb903f… drh 594 url_reset(&url);
57f1e87… drh 595 cgi_check_for_malice();
112c713… drh 596 style_set_current_feature("leaves");
5ac4e15… drh 597 style_header("Leaves");
5ac4e15… drh 598 login_anonymous_available();
efb903f… drh 599 timeline_ss_submenu();
76e77ab… drh 600 #if 0
752895d… drh 601 style_sidebox_begin("Nomenclature:", "33%");
752895d… drh 602 @ <ol>
752895d… drh 603 @ <li> A <div class="sideboxDescribed">leaf</div>
752895d… drh 604 @ is a check-in with no descendants in the same branch.</li>
3243e63… drh 605 @ <li> An <div class="sideboxDescribed">open leaf</div>
3243e63… drh 606 @ is a leaf that does not have a "closed" tag
3243e63… drh 607 @ and is thus assumed to still be in use.</li>
3243e63… drh 608 @ <li> A <div class="sideboxDescribed">closed leaf</div>
3243e63… drh 609 @ has a "closed" tag and is thus assumed to
3243e63… drh 610 @ be historical and no longer in active use.</li>
3243e63… drh 611 @ </ol>
3243e63… drh 612 style_sidebox_end();
76e77ab… drh 613 #endif
83ac468… drh 614
b6e22e6… drh 615 if( showAll ){
83ac468… drh 616 @ <h1>All leaves, both open and closed:</h1>
b6e22e6… drh 617 }else if( showClosed ){
83ac468… drh 618 @ <h1>Closed leaves:</h1>
83ac468… drh 619 }else{
83ac468… drh 620 @ <h1>Open leaves:</h1>
83ac468… drh 621 }
5ac4e15… drh 622 blob_zero(&sql);
5ac4e15… drh 623 blob_append(&sql, timeline_query_for_www(), -1);
49b0ff1… drh 624 blob_append_sql(&sql, " AND blob.rid IN leaf");
5ac4e15… drh 625 if( showClosed ){
49b0ff1… drh 626 blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
5ac4e15… drh 627 }else if( !showAll ){
49b0ff1… drh 628 blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
49b0ff1… drh 629 }
efb903f… drh 630 if( fNoHidden || fOnlyHidden ){
efb903f… drh 631 const char* zUnaryOp = fNoHidden ? "NOT" : "";
efb903f… drh 632 blob_append_sql(&sql,
efb903f… drh 633 " AND %s EXISTS(SELECT 1 FROM tagxref"
efb903f… drh 634 " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
efb903f… drh 635 zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
efb903f… drh 636 }
49b0ff1… drh 637 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
5ac4e15… drh 638 blob_reset(&sql);
efb903f… drh 639 /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
efb903f… drh 640 ** many descenders to (off-screen) parents. */
efb903f… drh 641 tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
efb903f… drh 642 if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
efb903f… drh 643 if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
efb903f… drh 644 if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
e632a09… drh 645 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
eb3cc76… drh 646 db_finalize(&q);
f5482a0… wyoung 647 @ <br>
112c713… drh 648 style_finish_page();
eb3cc76… drh 649 }
eb3cc76… drh 650
eb3cc76… drh 651 #if INTERFACE
eb3cc76… drh 652 /* Flag parameters to compute_uses_file() */
eb3cc76… drh 653 #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
eb3cc76… drh 654
eb3cc76… drh 655 #endif
112c713… drh 656
ebd239d… drh 657 /*
ebd239d… drh 658 ** Append a new VALUES term.
ebd239d… drh 659 */
ebd239d… drh 660 static void uses_file_append_term(Blob *pSql, int *pnCnt, int rid){
ebd239d… drh 661 if( *pnCnt==0 ){
ebd239d… drh 662 blob_append_sql(pSql, "(%d)", rid);
ebd239d… drh 663 *pnCnt = 4;
ebd239d… drh 664 }else if( (*pnCnt)%10==9 ){
ebd239d… drh 665 blob_append_sql(pSql, ",\n (%d)", rid);
ebd239d… drh 666 }else{
ebd239d… drh 667 blob_append_sql(pSql, ",(%d)", rid);
ebd239d… drh 668 }
ebd239d… drh 669 ++*pnCnt;
ebd239d… drh 670 }
ebd239d… drh 671
eb3cc76… drh 672
eb3cc76… drh 673 /*
eb3cc76… drh 674 ** Add to table zTab the record ID (rid) of every check-in that contains
eb3cc76… drh 675 ** the file fid.
eb3cc76… drh 676 */
eb3cc76… drh 677 void compute_uses_file(const char *zTab, int fid, int usesFlags){
eb3cc76… drh 678 Bag seen;
eb3cc76… drh 679 Bag pending;
ebd239d… drh 680 Blob ins = BLOB_INITIALIZER;
ebd239d… drh 681 int nIns = 0;
eb3cc76… drh 682 Stmt q;
eb3cc76… drh 683 int rid;
eb3cc76… drh 684
eb3cc76… drh 685 bag_init(&seen);
eb3cc76… drh 686 bag_init(&pending);
ebd239d… drh 687 blob_append_sql(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
eb3cc76… drh 688 db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
eb3cc76… drh 689 while( db_step(&q)==SQLITE_ROW ){
eb3cc76… drh 690 int mid = db_column_int(&q, 0);
eb3cc76… drh 691 bag_insert(&pending, mid);
eb3cc76… drh 692 bag_insert(&seen, mid);
ebd239d… drh 693 uses_file_append_term(&ins, &nIns, mid);
eb3cc76… drh 694 }
eb3cc76… drh 695 db_finalize(&q);
8b17c23… jan.nijtmans 696
eb3cc76… drh 697 db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
eb3cc76… drh 698 while( db_step(&q)==SQLITE_ROW ){
eb3cc76… drh 699 int mid = db_column_int(&q, 0);
eb3cc76… drh 700 bag_insert(&seen, mid);
eb3cc76… drh 701 if( usesFlags & USESFILE_DELETE ){
ebd239d… drh 702 uses_file_append_term(&ins, &nIns, mid);
eb3cc76… drh 703 }
eb3cc76… drh 704 }
eb3cc76… drh 705 db_finalize(&q);
d81c42d… drh 706 db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim");
eb3cc76… drh 707
eb3cc76… drh 708 while( (rid = bag_first(&pending))!=0 ){
eb3cc76… drh 709 bag_remove(&pending, rid);
eb3cc76… drh 710 db_bind_int(&q, ":rid", rid);
eb3cc76… drh 711 while( db_step(&q)==SQLITE_ROW ){
eb3cc76… drh 712 int mid = db_column_int(&q, 0);
eb3cc76… drh 713 if( bag_find(&seen, mid) ) continue;
eb3cc76… drh 714 bag_insert(&seen, mid);
eb3cc76… drh 715 bag_insert(&pending, mid);
ebd239d… drh 716 uses_file_append_term(&ins, &nIns, mid);
eb3cc76… drh 717 }
eb3cc76… drh 718 db_reset(&q);
eb3cc76… drh 719 }
6458f02… drh 720 db_finalize(&q);
ebd239d… drh 721 db_exec_sql(blob_str(&ins));
ebd239d… drh 722 blob_reset(&ins);
eb3cc76… drh 723 bag_clear(&seen);
eb3cc76… drh 724 bag_clear(&pending);
6458f02… drh 725 }

Keyboard Shortcuts

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