/* ** COMMAND: rstats ** ** Usage: %fossil rstats ** ** Deliver a report of the repository statistics for the ** current checkout. */ void rstats_cmd(void){ i64 t; int n, m, fsize, vid; char zBuf[100]; db_must_be_within_tree(); vid = db_lget_int("checkout",0); if( vid==0 ){ fossil_panic("no checkout"); } fsize = file_size(g.zRepositoryName); n = db_int(0, "SELECT count(*) FROM blob"); m = db_int(0, "SELECT count(*) FROM delta"); printf(" Number of Artifacts: %d\n", n); printf(" %d full text + %d delta blobs\n", (n-m), m); if( n>0 ){ int a, b; t = db_int64(0, "SELECT total(size) FROM blob WHERE size>0"); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t); if( t/fsize < 5 ){ b = 10; fsize /= 10; }else{ b = 1; } a = t/fsize; printf(" %d bytes average, %s bytes total\n\n", ((int)(((double)t)/(double)n)), (zBuf)); } n = db_int(0, "SELECT count(distinct mid) FROM mlink"); printf(" Number Of Checkins: %d\n", n); n = db_int(0, "SELECT count(*) FROM filename"); printf(" Number Of Files: %d\n", n); n = db_int(0, "SELECT count(*) FROM tag WHERE +tagname GLOB 'wiki-*'"); printf("Number Of Wiki Pages: %d\n", n); n = db_int(0, "SELECT count(*) FROM tag WHERE +tagname GLOB 'tkt-*'"); printf(" Number Of Tickets: %d\n", n); n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event) + 0.99"); printf(" Duration Of Project: %d days\n", n); }
/* ** Implementation of the /json/status page. ** */ cson_value * json_page_status(){ Stmt q = empty_Stmt; cson_object * oPay; /*cson_object * files;*/ int vid, nErr = 0; cson_object * tmpO; char * zTmp; i64 iMtime; cson_array * aFiles; if(!db_open_local(0)){ json_set_err(FSL_JSON_E_DB_NEEDS_CHECKOUT, NULL); return NULL; } oPay = cson_new_object(); cson_object_set(oPay, "repository", json_new_string(db_repository_filename())); cson_object_set(oPay, "localRoot", json_new_string(g.zLocalRoot)); vid = db_lget_int("checkout", 0); if(!vid){ json_set_err( FSL_JSON_E_UNKNOWN, "Can this even happen?" ); return 0; } /* TODO: dupe show_common_info() state */ tmpO = cson_new_object(); cson_object_set(oPay, "checkout", cson_object_value(tmpO)); zTmp = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); cson_object_set(tmpO, "uuid", json_new_string(zTmp) ); free(zTmp); cson_object_set( tmpO, "tags", json_tags_for_checkin_rid(vid, 0) ); /* FIXME: optimize the datetime/timestamp queries into 1 query. */ zTmp = db_text(0, "SELECT datetime(mtime) || " "' UTC' FROM event WHERE objid=%d", vid); cson_object_set(tmpO, "datetime", json_new_string(zTmp)); free(zTmp); iMtime = db_int64(0, "SELECT CAST(strftime('%%s',mtime) AS INTEGER) " "FROM event WHERE objid=%d", vid); cson_object_set(tmpO, "timestamp", cson_value_new_integer((cson_int_t)iMtime)); #if 0 /* TODO: add parent artifact info */ tmpO = cson_new_object(); cson_object_set( oPay, "parent", cson_object_value(tmpO) ); cson_object_set( tmpO, "uuid", TODO ); cson_object_set( tmpO, "timestamp", TODO ); #endif /* Now get the list of non-pristine files... */ aFiles = cson_new_array(); cson_object_set( oPay, "files", cson_array_value( aFiles ) ); db_prepare(&q, "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)" " FROM vfile " " WHERE is_selected(id)" " AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1" ); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); int isDeleted = db_column_int(&q, 1); int isChnged = db_column_int(&q,2); int isNew = db_column_int(&q,3)==0; int isRenamed = db_column_int(&q,4); cson_object * oFile; char const * zStatus = "???"; char * zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); if( isDeleted ){ zStatus = "deleted"; }else if( isNew ){ zStatus = "new" /* maintenance reminder: MUST come BEFORE the isChnged checks. */; }else if( isRenamed ){ zStatus = "renamed"; }else if( !file_wd_isfile_or_link(zFullName) ){ if( file_access(zFullName, F_OK)==0 ){ zStatus = "notAFile"; ++nErr; }else{ zStatus = "missing"; ++nErr; } }else if( 2==isChnged ){ zStatus = "updatedByMerge"; }else if( 3==isChnged ){ zStatus = "addedByMerge"; }else if( 4==isChnged ){ zStatus = "updatedByIntegrate"; }else if( 5==isChnged ){ zStatus = "addedByIntegrate"; }else if( 1==isChnged ){ if( file_contains_merge_marker(zFullName) ){ zStatus = "conflict"; }else{ zStatus = "edited"; } } oFile = cson_new_object(); cson_array_append( aFiles, cson_object_value(oFile) ); /* optimization potential: move these keys into cson_strings to take advantage of refcounting. */ cson_object_set( oFile, "name", json_new_string( zPathname ) ); cson_object_set( oFile, "status", json_new_string( zStatus ) ); free(zFullName); } cson_object_set( oPay, "errorCount", json_new_int( nErr ) ); db_finalize(&q); #if 0 /* TODO: add "merged with" status. First need (A) to decide on a structure and (B) to set up some tests for the multi-merge case.*/ db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid" " WHERE id<=0"); while( db_step(&q)==SQLITE_ROW ){ const char *zLabel = "MERGED_WITH"; switch( db_column_int(&q, 1) ){ case -1: zLabel = "CHERRYPICK "; break; case -2: zLabel = "BACKOUT "; break; case -4: zLabel = "INTEGRATE "; break; } blob_append(report, zPrefix, nPrefix); blob_appendf(report, "%s %s\n", zLabel, db_column_text(&q, 0)); } db_finalize(&q); if( nErr ){ fossil_fatal("aborting due to prior errors"); } #endif return cson_object_value( oPay ); }
cson_value * json_artifact_file(cson_object * zParent, int rid){ cson_object * pay = NULL; Stmt q = empty_Stmt; cson_array * checkin_arr = NULL; char contentFormat; i64 contentSize = -1; char * parentUuid; if( ! g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' privileges."); return NULL; } pay = zParent; contentFormat = json_artifact_get_content_format_flag(); if( 0 != contentFormat ){ Blob content = empty_blob; const char *zMime; char const * zFormat = (contentFormat<1) ? "raw" : "html"; content_get(rid, &content); zMime = mimetype_from_content(&content); cson_object_set(zParent, "contentType", json_new_string(zMime ? zMime : "text/plain")); if(!zMime){/* text/plain */ if(0 < blob_size(&content)){ if( 0 < contentFormat ){/*HTML-size it*/ Blob html = empty_blob; wiki_convert(&content, &html, 0); assert( blob_size(&content) < blob_size(&html) ); blob_swap( &html, &content ); assert( blob_size(&content) > blob_size(&html) ); blob_reset( &html ); }/*else as-is*/ } cson_object_set(zParent, "content", cson_value_new_string(blob_str(&content), (unsigned int)blob_size(&content))); }/*else binary: ignore*/ contentSize = blob_size(&content); cson_object_set(zParent, "contentSize", json_new_int(contentSize) ); cson_object_set(zParent, "contentFormat", json_new_string(zFormat) ); blob_reset(&content); } contentSize = db_int64(-1, "SELECT size FROM blob WHERE rid=%d", rid); assert( -1 < contentSize ); cson_object_set(zParent, "size", json_new_int(contentSize) ); parentUuid = db_text(NULL, "SELECT DISTINCT p.uuid " "FROM blob p, blob f, mlink m " "WHERE m.pid=p.rid " "AND m.fid=f.rid " "AND f.rid=%d", rid ); if(parentUuid){ cson_object_set( zParent, "parent", json_new_string(parentUuid) ); fossil_free(parentUuid); } /* Find checkins associated with this file... */ db_prepare(&q, "SELECT filename.name AS name, " " (mlink.pid==0) AS isNew," " (mlink.fid==0) AS isDel," " cast(strftime('%%s',event.mtime) as int) AS timestamp," " coalesce(event.ecomment,event.comment) as comment," " coalesce(event.euser,event.user) as user," #if 0 " a.size AS size," /* same for all checkins. */ #endif " b.uuid as checkin, " #if 0 " mlink.mperm as mperm," #endif " coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND " " rid=mlink.mid),'trunk') as branch" " FROM mlink, filename, event, blob a, blob b" " WHERE filename.fnid=mlink.fnid" " AND event.objid=mlink.mid" " AND a.rid=mlink.fid" " AND b.rid=mlink.mid" " AND mlink.fid=%d" " ORDER BY filename.name, event.mtime", TAG_BRANCH, rid ); /* TODO: add a "state" flag for the file in each checkin, e.g. "modified", "new", "deleted". */ checkin_arr = cson_new_array(); cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); while( (SQLITE_ROW==db_step(&q) ) ){ cson_object * row = cson_value_get_object(cson_sqlite3_row_to_object(q.pStmt)); /* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */ char const isNew = cson_value_get_bool(cson_object_get(row,"isNew")); char const isDel = cson_value_get_bool(cson_object_get(row,"isDel")); cson_object_set(row, "isNew", NULL); cson_object_set(row, "isDel", NULL); cson_object_set(row, "state", json_new_string(json_artifact_status_to_string(isNew, isDel))); cson_array_append( checkin_arr, cson_object_value(row) ); } db_finalize(&q); return cson_object_value(pay); }
/* ** Given the RID for a checkin, construct a tarball containing ** all files in that checkin ** ** If RID is for an object that is not a real manifest, then the ** resulting tarball contains a single file which is the RID ** object. ** ** If the RID object does not exist in the repository, then ** pTar is zeroed. ** ** zDir is a "synthetic" subdirectory which all files get ** added to as part of the tarball. It may be 0 or an empty string, in ** which case it is ignored. The intention is to create a tarball which ** politely expands into a subdir instead of filling your current dir ** with source files. For example, pass a UUID or "ProjectName". ** */ void tarball_of_checkin(int rid, Blob *pTar, const char *zDir){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; char *zName; unsigned int mTime; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pTar); return; } blob_zero(&hash); blob_zero(&filename); if( zDir && zDir[0] ){ blob_appendf(&filename, "%s/", zDir); } nPrefix = blob_size(&filename); pManifest = manifest_get(rid, CFTYPE_MANIFEST); if( pManifest ){ mTime = (pManifest->rDate - 2440587.5)*86400.0; tar_begin(mTime); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); tar_add_file(zName, &mfile, 0, mTime); sha1sum_blob(&mfile, &hash); blob_reset(&mfile); blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); tar_add_file(zName, &hash, 0, mTime); blob_reset(&hash); } manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ int fid = uuid_to_rid(pFile->zUuid, 0); if( fid ){ content_get(fid, &file); blob_resize(&filename, nPrefix); blob_append(&filename, pFile->zName, -1); zName = blob_str(&filename); tar_add_file(zName, &file, manifest_file_mperm(pFile), mTime); blob_reset(&file); } } }else{ sha1sum_blob(&mfile, &hash); blob_append(&filename, blob_str(&hash), 16); zName = blob_str(&filename); mTime = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0;"); tar_begin(mTime); tar_add_file(zName, &mfile, 0, mTime); } manifest_destroy(pManifest); blob_reset(&mfile); blob_reset(&filename); tar_finish(pTar); }