Fossil SCM

Enhance the MLINK table schema to record file changes from all parents. Requires a "fossil rebuild". Other schema cleanups at the same time.

drh 2015-01-25 00:20 UTC trunk
Commit a241444d8f5225c1d977c5ac7540e7064a8613e0
--- src/browse.c
+++ src/browse.c
@@ -907,10 +907,11 @@
907907
@ SELECT pid FROM ckin, plink WHERE cid=x AND isprim)
908908
@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
909909
@ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name
910910
@ FROM ckin, mlink, event, filename
911911
@ WHERE mlink.mid=ckin.x
912
+@ AND NOT mlink.isaux
912913
@ AND mlink.fnid IN (SELECT fnid FROM foci, filename
913914
@ WHERE foci.checkinID=:ckin
914915
@ AND filename.name=foci.filename
915916
@ AND filename.name GLOB :glob)
916917
@ AND filename.fnid=mlink.fnid
917918
--- src/browse.c
+++ src/browse.c
@@ -907,10 +907,11 @@
907 @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim)
908 @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
909 @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name
910 @ FROM ckin, mlink, event, filename
911 @ WHERE mlink.mid=ckin.x
 
912 @ AND mlink.fnid IN (SELECT fnid FROM foci, filename
913 @ WHERE foci.checkinID=:ckin
914 @ AND filename.name=foci.filename
915 @ AND filename.name GLOB :glob)
916 @ AND filename.fnid=mlink.fnid
917
--- src/browse.c
+++ src/browse.c
@@ -907,10 +907,11 @@
907 @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim)
908 @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
909 @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name
910 @ FROM ckin, mlink, event, filename
911 @ WHERE mlink.mid=ckin.x
912 @ AND NOT mlink.isaux
913 @ AND mlink.fnid IN (SELECT fnid FROM foci, filename
914 @ WHERE foci.checkinID=:ckin
915 @ AND filename.name=foci.filename
916 @ AND filename.name GLOB :glob)
917 @ AND filename.fnid=mlink.fnid
918
+51 -53
--- src/manifest.c
+++ src/manifest.c
@@ -1187,16 +1187,18 @@
11871187
/*
11881188
** Add a single entry to the mlink table. Also add the filename to
11891189
** the filename table if it is not there already.
11901190
*/
11911191
static void add_one_mlink(
1192
+ int pmid, /* The parent manifest */
1193
+ const char *zFromUuid, /* UUID for content in parent */
11921194
int mid, /* The record ID of the manifest */
1193
- const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
1194
- const char *zToUuid, /* UUID for the mlink.fid. "" to delete */
1195
+ const char *zToUuid, /* UUID for content in child */
11951196
const char *zFilename, /* Filename */
11961197
const char *zPrior, /* Previous filename. NULL if unchanged */
11971198
int isPublic, /* True if mid is not a private manifest */
1199
+ int isPrimary, /* pmid is the primary parent of mid */
11981200
int mperm /* 1: exec, 2: symlink */
11991201
){
12001202
int fnid, pfnid, pid, fid;
12011203
static Stmt s1;
12021204
@@ -1216,19 +1218,21 @@
12161218
}else{
12171219
fid = uuid_to_rid(zToUuid, 1);
12181220
if( isPublic ) content_make_public(fid);
12191221
}
12201222
db_static_prepare(&s1,
1221
- "INSERT INTO mlink(mid,pid,fid,fnid,pfnid,mperm)"
1222
- "VALUES(:m,:p,:f,:n,:pfn,:mp)"
1223
+ "INSERT INTO mlink(mid,fid,pmid,pid,fnid,pfnid,mperm,isaux)"
1224
+ "VALUES(:m,:f,:pm,:p,:n,:pfn,:mp,:isaux)"
12231225
);
12241226
db_bind_int(&s1, ":m", mid);
1225
- db_bind_int(&s1, ":p", pid);
12261227
db_bind_int(&s1, ":f", fid);
1228
+ db_bind_int(&s1, ":pm", pmid);
1229
+ db_bind_int(&s1, ":p", pid);
12271230
db_bind_int(&s1, ":n", fnid);
12281231
db_bind_int(&s1, ":pfn", pfnid);
12291232
db_bind_int(&s1, ":mp", mperm);
1233
+ db_bind_int(&s1, ":isaux", isPrimary==0);
12301234
db_exec(&s1);
12311235
if( pid && fid ){
12321236
content_deltify(pid, fid, 0);
12331237
}
12341238
}
@@ -1342,24 +1346,29 @@
13421346
**
13431347
** Deleted files have mlink.fid=0.
13441348
** Added files have mlink.pid=0.
13451349
** Edited files have both mlink.pid!=0 and mlink.fid!=0
13461350
*/
1347
-static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
1351
+static void add_mlink(
1352
+ int pmid, Manifest *pParent, /* Parent check-in */
1353
+ int mid, Manifest *pChild, /* The child check-in */
1354
+ int isPrim /* TRUE if pmid is the primary parent of mid */
1355
+){
13481356
Blob otherContent;
13491357
int otherRid;
13501358
int i, rc;
13511359
ManifestFile *pChildFile, *pParentFile;
13521360
Manifest **ppOther;
13531361
static Stmt eq;
13541362
int isPublic; /* True if pChild is non-private */
13551363
1356
- /* If mlink table entires are already set for cid, then abort early
1357
- ** doing no work.
1364
+ /* If mlink table entires are already exist for the pmid-to-mid transition,
1365
+ ** then abort early doing no work.
13581366
*/
1359
- db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid");
1360
- db_bind_int(&eq, ":mid", cid);
1367
+ db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid AND pmid=:pmid");
1368
+ db_bind_int(&eq, ":mid", mid);
1369
+ db_bind_int(&eq, ":pmid", pmid);
13611370
rc = db_step(&eq);
13621371
db_reset(&eq);
13631372
if( rc==SQLITE_ROW ) return;
13641373
13651374
/* Compute the value of the missing pParent or pChild parameter.
@@ -1366,14 +1375,14 @@
13661375
** Fetch the baseline checkins for both.
13671376
*/
13681377
assert( pParent==0 || pChild==0 );
13691378
if( pParent==0 ){
13701379
ppOther = &pParent;
1371
- otherRid = pid;
1380
+ otherRid = pmid;
13721381
}else{
13731382
ppOther = &pChild;
1374
- otherRid = cid;
1383
+ otherRid = mid;
13751384
}
13761385
if( (*ppOther = manifest_cache_find(otherRid))==0 ){
13771386
content_get(otherRid, &otherContent);
13781387
if( blob_size(&otherContent)==0 ) return;
13791388
*ppOther = manifest_parse(&otherContent, otherRid, 0);
@@ -1381,20 +1390,20 @@
13811390
}
13821391
if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
13831392
manifest_destroy(*ppOther);
13841393
return;
13851394
}
1386
- isPublic = !content_is_private(cid);
1395
+ isPublic = !content_is_private(mid);
13871396
13881397
/* Try to make the parent manifest a delta from the child, if that
13891398
** is an appropriate thing to do. For a new baseline, make the
13901399
** previous baseline a delta from the current baseline.
13911400
*/
13921401
if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
1393
- content_deltify(pid, cid, 0);
1402
+ content_deltify(pmid, mid, 0);
13941403
}else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){
1395
- content_deltify(pParent->pBaseline->rid, cid, 0);
1404
+ content_deltify(pParent->pBaseline->rid, mid, 0);
13961405
}
13971406
13981407
/* Remember all children less than a few seconds younger than their parent,
13991408
** as we might want to fudge the times for those children.
14001409
*/
@@ -1414,31 +1423,32 @@
14141423
int mperm = manifest_file_mperm(pChildFile);
14151424
if( pChildFile->zPrior ){
14161425
pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0);
14171426
if( pParentFile ){
14181427
/* File with name change */
1419
- add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1420
- pChildFile->zName, pChildFile->zPrior, isPublic, mperm);
1428
+ add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1429
+ pChildFile->zName, pChildFile->zPrior,
1430
+ isPublic, isPrim, mperm);
14211431
}else{
14221432
/* File name changed, but the old name is not found in the parent!
14231433
** Treat this like a new file. */
1424
- add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1425
- isPublic, mperm);
1434
+ add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1435
+ isPublic, isPrim, mperm);
14261436
}
14271437
}else{
14281438
pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0);
14291439
if( pParentFile==0 ){
14301440
if( pChildFile->zUuid ){
14311441
/* A new file */
1432
- add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1433
- isPublic, mperm);
1442
+ add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1443
+ isPublic, isPrim, mperm);
14341444
}
14351445
}else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0
14361446
|| manifest_file_mperm(pParentFile)!=mperm ){
14371447
/* Changes in file content or permissions */
1438
- add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1439
- pChildFile->zName, 0, isPublic, mperm);
1448
+ add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1449
+ pChildFile->zName, 0, isPublic, isPrim, mperm);
14401450
}
14411451
}
14421452
}
14431453
if( pParent->zBaseline && pChild->zBaseline ){
14441454
/* Both parent and child are delta manifests. Look for files that
@@ -1450,22 +1460,22 @@
14501460
pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0);
14511461
if( pChildFile==0 ){
14521462
/* The child file reverts to baseline. Show this as a change */
14531463
pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
14541464
if( pChildFile ){
1455
- add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1456
- pChildFile->zName, 0, isPublic,
1465
+ add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1466
+ pChildFile->zName, 0, isPublic, isPrim,
14571467
manifest_file_mperm(pChildFile));
14581468
}
14591469
}
14601470
}else{
14611471
pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
14621472
if( pChildFile ){
14631473
/* File resurrected in the child after having been deleted in
14641474
** the parent. Show this as an added file. */
1465
- add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1466
- isPublic, manifest_file_mperm(pChildFile));
1475
+ add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1476
+ isPublic, isPrim, manifest_file_mperm(pChildFile));
14671477
}
14681478
}
14691479
}
14701480
}else if( pChild->zBaseline==0 ){
14711481
/* pChild is a baseline. Look for files that are present in pParent
@@ -1472,12 +1482,12 @@
14721482
** but are missing from pChild and mark them as having been deleted. */
14731483
manifest_file_rewind(pParent);
14741484
while( (pParentFile = manifest_file_next(pParent,0))!=0 ){
14751485
pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
14761486
if( pChildFile==0 && pParentFile->zUuid!=0 ){
1477
- add_one_mlink(cid, pParentFile->zUuid, 0, pParentFile->zName, 0,
1478
- isPublic, 0);
1487
+ add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0,
1488
+ isPublic, isPrim, 0);
14791489
}
14801490
}
14811491
}
14821492
manifest_cache_insert(*ppOther);
14831493
}
@@ -1781,45 +1791,33 @@
17811791
sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d",
17821792
uuid_to_rid(p->zBaseline,1));
17831793
}else{
17841794
sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL");
17851795
}
1786
- (void)db_schema_is_outofdate(); /* Make sure g.zAuxSchema is initialized */
17871796
for(i=0; i<p->nParent; i++){
17881797
int pid = uuid_to_rid(p->azParent[i], 1);
1789
- if( strcmp(g.zAuxSchema,"2014-11-24 20:35")>=0 ){
1790
- /* Support for PLINK.BASEID added on 2014-11-24 */
1791
- db_multi_exec(
1792
- "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
1793
- "VALUES(%d, %d, %d, %.17g, %s)",
1794
- pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
1795
- }else{
1796
- /* Continue to work with older schema to avoid an unnecessary
1797
- ** rebuild */
1798
- db_multi_exec(
1799
- "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1800
- "VALUES(%d, %d, %d, %.17g)",
1801
- pid, rid, i==0, p->rDate);
1802
- }
1803
- if( i==0 ){
1804
- add_mlink(pid, 0, rid, p);
1805
- parentid = pid;
1806
- }
1807
- }
1808
- db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
1798
+ db_multi_exec(
1799
+ "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
1800
+ "VALUES(%d, %d, %d, %.17g, %s)",
1801
+ pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
1802
+ add_mlink(pid, 0, rid, p, i);
1803
+ if( i==0 ) parentid = pid;
1804
+ }
1805
+ db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid);
18091806
while( db_step(&q)==SQLITE_ROW ){
18101807
int cid = db_column_int(&q, 0);
1811
- add_mlink(rid, p, cid, 0);
1808
+ int isprim = db_column_int(&q, 1);
1809
+ add_mlink(rid, p, cid, 0, isprim);
18121810
}
18131811
db_finalize(&q);
18141812
if( p->nParent==0 ){
18151813
/* For root files (files without parents) add mlink entries
18161814
** showing all content as new. */
18171815
int isPublic = !content_is_private(rid);
18181816
for(i=0; i<p->nFile; i++){
1819
- add_one_mlink(rid, 0, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1820
- isPublic, manifest_file_mperm(&p->aFile[i]));
1817
+ add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1818
+ isPublic, 1, manifest_file_mperm(&p->aFile[i]));
18211819
}
18221820
}
18231821
db_multi_exec(
18241822
"REPLACE INTO event(type,mtime,objid,user,comment,"
18251823
"bgcolor,euser,ecomment,omtime)"
18261824
--- src/manifest.c
+++ src/manifest.c
@@ -1187,16 +1187,18 @@
1187 /*
1188 ** Add a single entry to the mlink table. Also add the filename to
1189 ** the filename table if it is not there already.
1190 */
1191 static void add_one_mlink(
 
 
1192 int mid, /* The record ID of the manifest */
1193 const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
1194 const char *zToUuid, /* UUID for the mlink.fid. "" to delete */
1195 const char *zFilename, /* Filename */
1196 const char *zPrior, /* Previous filename. NULL if unchanged */
1197 int isPublic, /* True if mid is not a private manifest */
 
1198 int mperm /* 1: exec, 2: symlink */
1199 ){
1200 int fnid, pfnid, pid, fid;
1201 static Stmt s1;
1202
@@ -1216,19 +1218,21 @@
1216 }else{
1217 fid = uuid_to_rid(zToUuid, 1);
1218 if( isPublic ) content_make_public(fid);
1219 }
1220 db_static_prepare(&s1,
1221 "INSERT INTO mlink(mid,pid,fid,fnid,pfnid,mperm)"
1222 "VALUES(:m,:p,:f,:n,:pfn,:mp)"
1223 );
1224 db_bind_int(&s1, ":m", mid);
1225 db_bind_int(&s1, ":p", pid);
1226 db_bind_int(&s1, ":f", fid);
 
 
1227 db_bind_int(&s1, ":n", fnid);
1228 db_bind_int(&s1, ":pfn", pfnid);
1229 db_bind_int(&s1, ":mp", mperm);
 
1230 db_exec(&s1);
1231 if( pid && fid ){
1232 content_deltify(pid, fid, 0);
1233 }
1234 }
@@ -1342,24 +1346,29 @@
1342 **
1343 ** Deleted files have mlink.fid=0.
1344 ** Added files have mlink.pid=0.
1345 ** Edited files have both mlink.pid!=0 and mlink.fid!=0
1346 */
1347 static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){
 
 
 
 
1348 Blob otherContent;
1349 int otherRid;
1350 int i, rc;
1351 ManifestFile *pChildFile, *pParentFile;
1352 Manifest **ppOther;
1353 static Stmt eq;
1354 int isPublic; /* True if pChild is non-private */
1355
1356 /* If mlink table entires are already set for cid, then abort early
1357 ** doing no work.
1358 */
1359 db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid");
1360 db_bind_int(&eq, ":mid", cid);
 
1361 rc = db_step(&eq);
1362 db_reset(&eq);
1363 if( rc==SQLITE_ROW ) return;
1364
1365 /* Compute the value of the missing pParent or pChild parameter.
@@ -1366,14 +1375,14 @@
1366 ** Fetch the baseline checkins for both.
1367 */
1368 assert( pParent==0 || pChild==0 );
1369 if( pParent==0 ){
1370 ppOther = &pParent;
1371 otherRid = pid;
1372 }else{
1373 ppOther = &pChild;
1374 otherRid = cid;
1375 }
1376 if( (*ppOther = manifest_cache_find(otherRid))==0 ){
1377 content_get(otherRid, &otherContent);
1378 if( blob_size(&otherContent)==0 ) return;
1379 *ppOther = manifest_parse(&otherContent, otherRid, 0);
@@ -1381,20 +1390,20 @@
1381 }
1382 if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
1383 manifest_destroy(*ppOther);
1384 return;
1385 }
1386 isPublic = !content_is_private(cid);
1387
1388 /* Try to make the parent manifest a delta from the child, if that
1389 ** is an appropriate thing to do. For a new baseline, make the
1390 ** previous baseline a delta from the current baseline.
1391 */
1392 if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
1393 content_deltify(pid, cid, 0);
1394 }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){
1395 content_deltify(pParent->pBaseline->rid, cid, 0);
1396 }
1397
1398 /* Remember all children less than a few seconds younger than their parent,
1399 ** as we might want to fudge the times for those children.
1400 */
@@ -1414,31 +1423,32 @@
1414 int mperm = manifest_file_mperm(pChildFile);
1415 if( pChildFile->zPrior ){
1416 pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0);
1417 if( pParentFile ){
1418 /* File with name change */
1419 add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1420 pChildFile->zName, pChildFile->zPrior, isPublic, mperm);
 
1421 }else{
1422 /* File name changed, but the old name is not found in the parent!
1423 ** Treat this like a new file. */
1424 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1425 isPublic, mperm);
1426 }
1427 }else{
1428 pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0);
1429 if( pParentFile==0 ){
1430 if( pChildFile->zUuid ){
1431 /* A new file */
1432 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1433 isPublic, mperm);
1434 }
1435 }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0
1436 || manifest_file_mperm(pParentFile)!=mperm ){
1437 /* Changes in file content or permissions */
1438 add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1439 pChildFile->zName, 0, isPublic, mperm);
1440 }
1441 }
1442 }
1443 if( pParent->zBaseline && pChild->zBaseline ){
1444 /* Both parent and child are delta manifests. Look for files that
@@ -1450,22 +1460,22 @@
1450 pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0);
1451 if( pChildFile==0 ){
1452 /* The child file reverts to baseline. Show this as a change */
1453 pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1454 if( pChildFile ){
1455 add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1456 pChildFile->zName, 0, isPublic,
1457 manifest_file_mperm(pChildFile));
1458 }
1459 }
1460 }else{
1461 pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1462 if( pChildFile ){
1463 /* File resurrected in the child after having been deleted in
1464 ** the parent. Show this as an added file. */
1465 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1466 isPublic, manifest_file_mperm(pChildFile));
1467 }
1468 }
1469 }
1470 }else if( pChild->zBaseline==0 ){
1471 /* pChild is a baseline. Look for files that are present in pParent
@@ -1472,12 +1482,12 @@
1472 ** but are missing from pChild and mark them as having been deleted. */
1473 manifest_file_rewind(pParent);
1474 while( (pParentFile = manifest_file_next(pParent,0))!=0 ){
1475 pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1476 if( pChildFile==0 && pParentFile->zUuid!=0 ){
1477 add_one_mlink(cid, pParentFile->zUuid, 0, pParentFile->zName, 0,
1478 isPublic, 0);
1479 }
1480 }
1481 }
1482 manifest_cache_insert(*ppOther);
1483 }
@@ -1781,45 +1791,33 @@
1781 sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d",
1782 uuid_to_rid(p->zBaseline,1));
1783 }else{
1784 sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL");
1785 }
1786 (void)db_schema_is_outofdate(); /* Make sure g.zAuxSchema is initialized */
1787 for(i=0; i<p->nParent; i++){
1788 int pid = uuid_to_rid(p->azParent[i], 1);
1789 if( strcmp(g.zAuxSchema,"2014-11-24 20:35")>=0 ){
1790 /* Support for PLINK.BASEID added on 2014-11-24 */
1791 db_multi_exec(
1792 "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
1793 "VALUES(%d, %d, %d, %.17g, %s)",
1794 pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
1795 }else{
1796 /* Continue to work with older schema to avoid an unnecessary
1797 ** rebuild */
1798 db_multi_exec(
1799 "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)"
1800 "VALUES(%d, %d, %d, %.17g)",
1801 pid, rid, i==0, p->rDate);
1802 }
1803 if( i==0 ){
1804 add_mlink(pid, 0, rid, p);
1805 parentid = pid;
1806 }
1807 }
1808 db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid);
1809 while( db_step(&q)==SQLITE_ROW ){
1810 int cid = db_column_int(&q, 0);
1811 add_mlink(rid, p, cid, 0);
 
1812 }
1813 db_finalize(&q);
1814 if( p->nParent==0 ){
1815 /* For root files (files without parents) add mlink entries
1816 ** showing all content as new. */
1817 int isPublic = !content_is_private(rid);
1818 for(i=0; i<p->nFile; i++){
1819 add_one_mlink(rid, 0, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1820 isPublic, manifest_file_mperm(&p->aFile[i]));
1821 }
1822 }
1823 db_multi_exec(
1824 "REPLACE INTO event(type,mtime,objid,user,comment,"
1825 "bgcolor,euser,ecomment,omtime)"
1826
--- src/manifest.c
+++ src/manifest.c
@@ -1187,16 +1187,18 @@
1187 /*
1188 ** Add a single entry to the mlink table. Also add the filename to
1189 ** the filename table if it is not there already.
1190 */
1191 static void add_one_mlink(
1192 int pmid, /* The parent manifest */
1193 const char *zFromUuid, /* UUID for content in parent */
1194 int mid, /* The record ID of the manifest */
1195 const char *zToUuid, /* UUID for content in child */
 
1196 const char *zFilename, /* Filename */
1197 const char *zPrior, /* Previous filename. NULL if unchanged */
1198 int isPublic, /* True if mid is not a private manifest */
1199 int isPrimary, /* pmid is the primary parent of mid */
1200 int mperm /* 1: exec, 2: symlink */
1201 ){
1202 int fnid, pfnid, pid, fid;
1203 static Stmt s1;
1204
@@ -1216,19 +1218,21 @@
1218 }else{
1219 fid = uuid_to_rid(zToUuid, 1);
1220 if( isPublic ) content_make_public(fid);
1221 }
1222 db_static_prepare(&s1,
1223 "INSERT INTO mlink(mid,fid,pmid,pid,fnid,pfnid,mperm,isaux)"
1224 "VALUES(:m,:f,:pm,:p,:n,:pfn,:mp,:isaux)"
1225 );
1226 db_bind_int(&s1, ":m", mid);
 
1227 db_bind_int(&s1, ":f", fid);
1228 db_bind_int(&s1, ":pm", pmid);
1229 db_bind_int(&s1, ":p", pid);
1230 db_bind_int(&s1, ":n", fnid);
1231 db_bind_int(&s1, ":pfn", pfnid);
1232 db_bind_int(&s1, ":mp", mperm);
1233 db_bind_int(&s1, ":isaux", isPrimary==0);
1234 db_exec(&s1);
1235 if( pid && fid ){
1236 content_deltify(pid, fid, 0);
1237 }
1238 }
@@ -1342,24 +1346,29 @@
1346 **
1347 ** Deleted files have mlink.fid=0.
1348 ** Added files have mlink.pid=0.
1349 ** Edited files have both mlink.pid!=0 and mlink.fid!=0
1350 */
1351 static void add_mlink(
1352 int pmid, Manifest *pParent, /* Parent check-in */
1353 int mid, Manifest *pChild, /* The child check-in */
1354 int isPrim /* TRUE if pmid is the primary parent of mid */
1355 ){
1356 Blob otherContent;
1357 int otherRid;
1358 int i, rc;
1359 ManifestFile *pChildFile, *pParentFile;
1360 Manifest **ppOther;
1361 static Stmt eq;
1362 int isPublic; /* True if pChild is non-private */
1363
1364 /* If mlink table entires are already exist for the pmid-to-mid transition,
1365 ** then abort early doing no work.
1366 */
1367 db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid AND pmid=:pmid");
1368 db_bind_int(&eq, ":mid", mid);
1369 db_bind_int(&eq, ":pmid", pmid);
1370 rc = db_step(&eq);
1371 db_reset(&eq);
1372 if( rc==SQLITE_ROW ) return;
1373
1374 /* Compute the value of the missing pParent or pChild parameter.
@@ -1366,14 +1375,14 @@
1375 ** Fetch the baseline checkins for both.
1376 */
1377 assert( pParent==0 || pChild==0 );
1378 if( pParent==0 ){
1379 ppOther = &pParent;
1380 otherRid = pmid;
1381 }else{
1382 ppOther = &pChild;
1383 otherRid = mid;
1384 }
1385 if( (*ppOther = manifest_cache_find(otherRid))==0 ){
1386 content_get(otherRid, &otherContent);
1387 if( blob_size(&otherContent)==0 ) return;
1388 *ppOther = manifest_parse(&otherContent, otherRid, 0);
@@ -1381,20 +1390,20 @@
1390 }
1391 if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){
1392 manifest_destroy(*ppOther);
1393 return;
1394 }
1395 isPublic = !content_is_private(mid);
1396
1397 /* Try to make the parent manifest a delta from the child, if that
1398 ** is an appropriate thing to do. For a new baseline, make the
1399 ** previous baseline a delta from the current baseline.
1400 */
1401 if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){
1402 content_deltify(pmid, mid, 0);
1403 }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){
1404 content_deltify(pParent->pBaseline->rid, mid, 0);
1405 }
1406
1407 /* Remember all children less than a few seconds younger than their parent,
1408 ** as we might want to fudge the times for those children.
1409 */
@@ -1414,31 +1423,32 @@
1423 int mperm = manifest_file_mperm(pChildFile);
1424 if( pChildFile->zPrior ){
1425 pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0);
1426 if( pParentFile ){
1427 /* File with name change */
1428 add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1429 pChildFile->zName, pChildFile->zPrior,
1430 isPublic, isPrim, mperm);
1431 }else{
1432 /* File name changed, but the old name is not found in the parent!
1433 ** Treat this like a new file. */
1434 add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1435 isPublic, isPrim, mperm);
1436 }
1437 }else{
1438 pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0);
1439 if( pParentFile==0 ){
1440 if( pChildFile->zUuid ){
1441 /* A new file */
1442 add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1443 isPublic, isPrim, mperm);
1444 }
1445 }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0
1446 || manifest_file_mperm(pParentFile)!=mperm ){
1447 /* Changes in file content or permissions */
1448 add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1449 pChildFile->zName, 0, isPublic, isPrim, mperm);
1450 }
1451 }
1452 }
1453 if( pParent->zBaseline && pChild->zBaseline ){
1454 /* Both parent and child are delta manifests. Look for files that
@@ -1450,22 +1460,22 @@
1460 pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0);
1461 if( pChildFile==0 ){
1462 /* The child file reverts to baseline. Show this as a change */
1463 pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1464 if( pChildFile ){
1465 add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid,
1466 pChildFile->zName, 0, isPublic, isPrim,
1467 manifest_file_mperm(pChildFile));
1468 }
1469 }
1470 }else{
1471 pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1472 if( pChildFile ){
1473 /* File resurrected in the child after having been deleted in
1474 ** the parent. Show this as an added file. */
1475 add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0,
1476 isPublic, isPrim, manifest_file_mperm(pChildFile));
1477 }
1478 }
1479 }
1480 }else if( pChild->zBaseline==0 ){
1481 /* pChild is a baseline. Look for files that are present in pParent
@@ -1472,12 +1482,12 @@
1482 ** but are missing from pChild and mark them as having been deleted. */
1483 manifest_file_rewind(pParent);
1484 while( (pParentFile = manifest_file_next(pParent,0))!=0 ){
1485 pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0);
1486 if( pChildFile==0 && pParentFile->zUuid!=0 ){
1487 add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0,
1488 isPublic, isPrim, 0);
1489 }
1490 }
1491 }
1492 manifest_cache_insert(*ppOther);
1493 }
@@ -1781,45 +1791,33 @@
1791 sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d",
1792 uuid_to_rid(p->zBaseline,1));
1793 }else{
1794 sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL");
1795 }
 
1796 for(i=0; i<p->nParent; i++){
1797 int pid = uuid_to_rid(p->azParent[i], 1);
1798 db_multi_exec(
1799 "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)"
1800 "VALUES(%d, %d, %d, %.17g, %s)",
1801 pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/);
1802 add_mlink(pid, 0, rid, p, i);
1803 if( i==0 ) parentid = pid;
1804 }
1805 db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid);
 
 
 
 
 
 
 
 
 
 
 
 
1806 while( db_step(&q)==SQLITE_ROW ){
1807 int cid = db_column_int(&q, 0);
1808 int isprim = db_column_int(&q, 1);
1809 add_mlink(rid, p, cid, 0, isprim);
1810 }
1811 db_finalize(&q);
1812 if( p->nParent==0 ){
1813 /* For root files (files without parents) add mlink entries
1814 ** showing all content as new. */
1815 int isPublic = !content_is_private(rid);
1816 for(i=0; i<p->nFile; i++){
1817 add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0,
1818 isPublic, 1, manifest_file_mperm(&p->aFile[i]));
1819 }
1820 }
1821 db_multi_exec(
1822 "REPLACE INTO event(type,mtime,objid,user,comment,"
1823 "bgcolor,euser,ecomment,omtime)"
1824
+30 -8
--- src/schema.c
+++ src/schema.c
@@ -44,12 +44,12 @@
4444
** a date) which can change frequently. When the content schema changes,
4545
** we have to execute special procedures to update the schema. When
4646
** the aux schema changes, all we need to do is rebuild the database.
4747
*/
4848
#define CONTENT_SCHEMA "2"
49
-#define AUX_SCHEMA_MIN "2011-04-25 19:50"
50
-#define AUX_SCHEMA_MAX "2014-11-24 20:35"
49
+#define AUX_SCHEMA_MIN "2015-01-24"
50
+#define AUX_SCHEMA_MAX "2015-01-24"
5151
5252
#endif /* INTERFACE */
5353
5454
5555
/*
@@ -225,23 +225,45 @@
225225
@ CREATE TABLE filename(
226226
@ fnid INTEGER PRIMARY KEY, -- Filename ID
227227
@ name TEXT UNIQUE -- Name of file page
228228
@ );
229229
@
230
+@
231
+@ -- A "file class" is like a filename except that it remains constant
232
+@ -- across renames.
233
+@ --
234
+@ CREATE TABLE fileclass(
235
+@ fclass INTEGER PRIMARY KEY, -- Unique id for this fileclass
236
+@ ckid INTEGER REFERENCES plink(cid), -- Checkin where file originates
237
+@ fnid INTEGER REFERENCES filename -- Name of the file in ckid
238
+@ );
239
+@
230240
@ -- Linkages between checkins, files created by each checkin, and
231241
@ -- the names of those files.
242
+@ --
243
+@ -- Each entry represents a file that changed content from pid to fid
244
+@ -- due to the check-in that goes from pmid to mid. fnid is the name
245
+@ -- of the file in the mid check-in. If the file was renamed as part
246
+@ -- of the mid check-in, then pfnid is the previous filename.
247
+@
248
+@ -- There can be multiple entries for (mid,fid) if the mid checkin was
249
+@ -- a merge. Entries with isaux==0 are from the primary parent. Merge
250
+@ -- parents have isaux set to true.
232251
@ --
233252
@ -- pid==0 if the file is added by checkin mid.
234253
@ -- fid==0 if the file is removed by checkin mid.
235254
@ --
236255
@ CREATE TABLE mlink(
237
-@ mid INTEGER REFERENCES blob, -- Manifest ID where change occurs
238
-@ pid INTEGER REFERENCES blob, -- File ID in parent manifest
239
-@ fid INTEGER REFERENCES blob, -- Changed file ID in this manifest
256
+@ mid INTEGER REFERENCES plink(cid), -- Checkin that contains fid
257
+@ fid INTEGER REFERENCES blob, -- New file content. 0 if deleted
258
+@ pmid INTEGER REFERENCES plink(cid), -- Checkin that contains pid
259
+@ pid INTEGER REFERENCES blob, -- Prev file content. 0 if new
240260
@ fnid INTEGER REFERENCES filename, -- Name of the file
241261
@ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged
242
-@ mperm INTEGER -- File permissions. 1==exec
262
+@ mperm INTEGER, -- File permissions. 1==exec
263
+@ fclass INTEGER REFERENCE fileclass, -- fid is an instance of this class
264
+@ isaux BOOLEAN DEFAULT 0 -- TRUE if pmid is the primary
243265
@ );
244266
@ CREATE INDEX mlink_i1 ON mlink(mid);
245267
@ CREATE INDEX mlink_i2 ON mlink(fnid);
246268
@ CREATE INDEX mlink_i3 ON mlink(fid);
247269
@ CREATE INDEX mlink_i4 ON mlink(pid);
@@ -251,11 +273,11 @@
251273
@ CREATE TABLE plink(
252274
@ pid INTEGER REFERENCES blob, -- Parent manifest
253275
@ cid INTEGER REFERENCES blob, -- Child manifest
254276
@ isprim BOOLEAN, -- pid is the primary parent of cid
255277
@ mtime DATETIME, -- the date/time stamp on cid. Julian day.
256
-@ baseid INTEGER REFERENCES blob, -- Baseline if child is a delta manifest
278
+@ baseid INTEGER REFERENCES blob, -- Baseline if cid is a delta manifest.
257279
@ UNIQUE(pid, cid)
258280
@ );
259281
@ CREATE INDEX plink_i2 ON plink(cid,pid);
260282
@
261283
@ -- A "leaf" checkin is a checkin that has no children in the same
@@ -488,11 +510,11 @@
488510
@ -- current version of the file is already in the repository.
489511
@ --
490512
@ CREATE TABLE vfile(
491513
@ id INTEGER PRIMARY KEY, -- ID of the checked out file
492514
@ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
493
-@ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add 4:i-chng 5:i-add
515
+@ chnged INT DEFAULT 0, -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add
494516
@ deleted BOOLEAN DEFAULT 0, -- True if deleted
495517
@ isexe BOOLEAN, -- True if file should be executable
496518
@ islink BOOLEAN, -- True if file should be symlink
497519
@ rid INTEGER, -- Originally from this repository record
498520
@ mrid INTEGER, -- Based on this record due to a merge
499521
--- src/schema.c
+++ src/schema.c
@@ -44,12 +44,12 @@
44 ** a date) which can change frequently. When the content schema changes,
45 ** we have to execute special procedures to update the schema. When
46 ** the aux schema changes, all we need to do is rebuild the database.
47 */
48 #define CONTENT_SCHEMA "2"
49 #define AUX_SCHEMA_MIN "2011-04-25 19:50"
50 #define AUX_SCHEMA_MAX "2014-11-24 20:35"
51
52 #endif /* INTERFACE */
53
54
55 /*
@@ -225,23 +225,45 @@
225 @ CREATE TABLE filename(
226 @ fnid INTEGER PRIMARY KEY, -- Filename ID
227 @ name TEXT UNIQUE -- Name of file page
228 @ );
229 @
 
 
 
 
 
 
 
 
 
 
230 @ -- Linkages between checkins, files created by each checkin, and
231 @ -- the names of those files.
 
 
 
 
 
 
 
 
 
232 @ --
233 @ -- pid==0 if the file is added by checkin mid.
234 @ -- fid==0 if the file is removed by checkin mid.
235 @ --
236 @ CREATE TABLE mlink(
237 @ mid INTEGER REFERENCES blob, -- Manifest ID where change occurs
238 @ pid INTEGER REFERENCES blob, -- File ID in parent manifest
239 @ fid INTEGER REFERENCES blob, -- Changed file ID in this manifest
 
240 @ fnid INTEGER REFERENCES filename, -- Name of the file
241 @ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged
242 @ mperm INTEGER -- File permissions. 1==exec
 
 
243 @ );
244 @ CREATE INDEX mlink_i1 ON mlink(mid);
245 @ CREATE INDEX mlink_i2 ON mlink(fnid);
246 @ CREATE INDEX mlink_i3 ON mlink(fid);
247 @ CREATE INDEX mlink_i4 ON mlink(pid);
@@ -251,11 +273,11 @@
251 @ CREATE TABLE plink(
252 @ pid INTEGER REFERENCES blob, -- Parent manifest
253 @ cid INTEGER REFERENCES blob, -- Child manifest
254 @ isprim BOOLEAN, -- pid is the primary parent of cid
255 @ mtime DATETIME, -- the date/time stamp on cid. Julian day.
256 @ baseid INTEGER REFERENCES blob, -- Baseline if child is a delta manifest
257 @ UNIQUE(pid, cid)
258 @ );
259 @ CREATE INDEX plink_i2 ON plink(cid,pid);
260 @
261 @ -- A "leaf" checkin is a checkin that has no children in the same
@@ -488,11 +510,11 @@
488 @ -- current version of the file is already in the repository.
489 @ --
490 @ CREATE TABLE vfile(
491 @ id INTEGER PRIMARY KEY, -- ID of the checked out file
492 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
493 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add 4:i-chng 5:i-add
494 @ deleted BOOLEAN DEFAULT 0, -- True if deleted
495 @ isexe BOOLEAN, -- True if file should be executable
496 @ islink BOOLEAN, -- True if file should be symlink
497 @ rid INTEGER, -- Originally from this repository record
498 @ mrid INTEGER, -- Based on this record due to a merge
499
--- src/schema.c
+++ src/schema.c
@@ -44,12 +44,12 @@
44 ** a date) which can change frequently. When the content schema changes,
45 ** we have to execute special procedures to update the schema. When
46 ** the aux schema changes, all we need to do is rebuild the database.
47 */
48 #define CONTENT_SCHEMA "2"
49 #define AUX_SCHEMA_MIN "2015-01-24"
50 #define AUX_SCHEMA_MAX "2015-01-24"
51
52 #endif /* INTERFACE */
53
54
55 /*
@@ -225,23 +225,45 @@
225 @ CREATE TABLE filename(
226 @ fnid INTEGER PRIMARY KEY, -- Filename ID
227 @ name TEXT UNIQUE -- Name of file page
228 @ );
229 @
230 @
231 @ -- A "file class" is like a filename except that it remains constant
232 @ -- across renames.
233 @ --
234 @ CREATE TABLE fileclass(
235 @ fclass INTEGER PRIMARY KEY, -- Unique id for this fileclass
236 @ ckid INTEGER REFERENCES plink(cid), -- Checkin where file originates
237 @ fnid INTEGER REFERENCES filename -- Name of the file in ckid
238 @ );
239 @
240 @ -- Linkages between checkins, files created by each checkin, and
241 @ -- the names of those files.
242 @ --
243 @ -- Each entry represents a file that changed content from pid to fid
244 @ -- due to the check-in that goes from pmid to mid. fnid is the name
245 @ -- of the file in the mid check-in. If the file was renamed as part
246 @ -- of the mid check-in, then pfnid is the previous filename.
247 @
248 @ -- There can be multiple entries for (mid,fid) if the mid checkin was
249 @ -- a merge. Entries with isaux==0 are from the primary parent. Merge
250 @ -- parents have isaux set to true.
251 @ --
252 @ -- pid==0 if the file is added by checkin mid.
253 @ -- fid==0 if the file is removed by checkin mid.
254 @ --
255 @ CREATE TABLE mlink(
256 @ mid INTEGER REFERENCES plink(cid), -- Checkin that contains fid
257 @ fid INTEGER REFERENCES blob, -- New file content. 0 if deleted
258 @ pmid INTEGER REFERENCES plink(cid), -- Checkin that contains pid
259 @ pid INTEGER REFERENCES blob, -- Prev file content. 0 if new
260 @ fnid INTEGER REFERENCES filename, -- Name of the file
261 @ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged
262 @ mperm INTEGER, -- File permissions. 1==exec
263 @ fclass INTEGER REFERENCE fileclass, -- fid is an instance of this class
264 @ isaux BOOLEAN DEFAULT 0 -- TRUE if pmid is the primary
265 @ );
266 @ CREATE INDEX mlink_i1 ON mlink(mid);
267 @ CREATE INDEX mlink_i2 ON mlink(fnid);
268 @ CREATE INDEX mlink_i3 ON mlink(fid);
269 @ CREATE INDEX mlink_i4 ON mlink(pid);
@@ -251,11 +273,11 @@
273 @ CREATE TABLE plink(
274 @ pid INTEGER REFERENCES blob, -- Parent manifest
275 @ cid INTEGER REFERENCES blob, -- Child manifest
276 @ isprim BOOLEAN, -- pid is the primary parent of cid
277 @ mtime DATETIME, -- the date/time stamp on cid. Julian day.
278 @ baseid INTEGER REFERENCES blob, -- Baseline if cid is a delta manifest.
279 @ UNIQUE(pid, cid)
280 @ );
281 @ CREATE INDEX plink_i2 ON plink(cid,pid);
282 @
283 @ -- A "leaf" checkin is a checkin that has no children in the same
@@ -488,11 +510,11 @@
510 @ -- current version of the file is already in the repository.
511 @ --
512 @ CREATE TABLE vfile(
513 @ id INTEGER PRIMARY KEY, -- ID of the checked out file
514 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
515 @ chnged INT DEFAULT 0, -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add
516 @ deleted BOOLEAN DEFAULT 0, -- True if deleted
517 @ isexe BOOLEAN, -- True if file should be executable
518 @ islink BOOLEAN, -- True if file should be symlink
519 @ rid INTEGER, -- Originally from this repository record
520 @ mrid INTEGER, -- Based on this record due to a merge
521

Keyboard Shortcuts

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