Overview
SHA1 Hash: | 02a584f7f5e58808cc8260b2914345d8a72ed430 |
---|---|
Date: | 2009-08-26 18:25:48 |
User: | drh |
Comment: | Add the --private option to the "fossil commit" command. This option creates a private branch which is never pushed. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/checkin.c from [e04164b91d] to [8b03f30dd7].
@@ -260,10 +260,17 @@ "\n" "# Enter comments on this check-in. Lines beginning with # are ignored.\n" "# The check-in comment follows wiki formatting rules.\n" "#\n" ); + if( g.markPrivate ){ + blob_append(&text, + "# PRIVATE BRANCH: This check-in will be private and will not sync to\n" + "# repositories.\n" + "#\n", -1 + ); + } status_report(&text, "# "); zEditor = db_get("editor", 0); if( zEditor==0 ){ zEditor = getenv("VISUAL"); } @@ -385,17 +392,21 @@ ** entries in the new branch when shown in the web timeline interface. ** ** A check-in is not permitted to fork unless the --force or -f ** option appears. A check-in is not allowed against a closed check-in. ** +** The --private option creates a private check-in that is never synced. +** Children of private check-ins are automatically private. +** ** Options: ** ** --comment|-m COMMENT-TEXT ** --branch NEW-BRANCH-NAME ** --bgcolor COLOR ** --nosign ** --force|-f +** --private ** */ void commit_cmd(void){ int rc; int vid, nrid, nvid; @@ -424,21 +435,34 @@ noSign = find_option("nosign",0,0)!=0; zComment = find_option("comment","m",1); forceFlag = find_option("force", "f", 0)!=0; zBranch = find_option("branch","b",1); zBgColor = find_option("bgcolor",0,1); + if( find_option("private",0,0) ){ + g.markPrivate = 1; + if( zBranch==0 ) zBranch = "private"; + if( zBgColor==0 ) zBgColor = "#fec084"; /* Orange */ + } zDateOvrd = find_option("date-override",0,1); zUserOvrd = find_option("user-override",0,1); db_must_be_within_tree(); noSign = db_get_boolean("omitsign", 0)|noSign; if( db_get_boolean("clearsign", 1)==0 ){ noSign = 1; } verify_all_options(); - /* - ** Autosync if requested. - */ - autosync(AUTOSYNC_PULL); + /* Get the ID of the parent manifest artifact */ + vid = db_lget_int("checkout", 0); + if( content_is_private(vid) ){ + g.markPrivate = 1; + } + + /* + ** Autosync if autosync is enabled and this is not a private check-in. + */ + if( !g.markPrivate ){ + autosync(AUTOSYNC_PULL); + } /* There are two ways this command may be executed. If there are ** no arguments following the word "commit", then all modified files ** in the checked out directory are committed. If one or more arguments ** follows "commit", then only those files are committed. @@ -482,17 +506,15 @@ if( strlen(blob_str(&unmodified)) ){ fossil_panic("file %s has not changed", blob_str(&unmodified)); } } - vid = db_lget_int("checkout", 0); - /* ** Do not allow a commit that will cause a fork unless the --force flag - ** is used. - */ - if( zBranch==0 && forceFlag==0 && !is_a_leaf(vid) ){ + ** is used or unless this is a private check-in. + */ + if( zBranch==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){ fossil_fatal("would fork. \"update\" first or use -f or --force."); } /* ** Do not allow a commit against a closed leaf @@ -558,11 +580,11 @@ blob_appendf(&manifest, "C %F\n", blob_str(&comment)); zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now"); zDate[10] = 'T'; blob_appendf(&manifest, "D %s\n", zDate); db_prepare(&q, - "SELECT pathname, uuid, origname" + "SELECT pathname, uuid, origname, blob.rid" " FROM vfile JOIN blob ON vfile.mrid=blob.rid" " WHERE NOT deleted AND vfile.vid=%d" " ORDER BY 1", vid); blob_zero(&filename); blob_appendf(&filename, "%s/", g.zLocalRoot); @@ -569,10 +591,11 @@ nBasename = blob_size(&filename); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zUuid = db_column_text(&q, 1); const char *zOrig = db_column_text(&q, 2); + int frid = db_column_int(&q, 3); const char *zPerm; blob_append(&filename, zName, -1); if( file_isexe(blob_str(&filename)) ){ zPerm = " x"; }else{ @@ -583,10 +606,11 @@ blob_appendf(&manifest, "F %F %s%s\n", zName, zUuid, zPerm); }else{ if( zPerm[0]==0 ){ zPerm = " w"; } blob_appendf(&manifest, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig); } + if( !g.markPrivate ) content_make_public(frid); } blob_reset(&filename); db_finalize(&q); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); blob_appendf(&manifest, "P %s", zUuid); @@ -593,10 +617,11 @@ db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); db_bind_int(&q2, ":id", 0); while( db_step(&q2)==SQLITE_ROW ){ int mid = db_column_int(&q2, 0); + if( !g.markPrivate && content_is_private(mid) ) continue; zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); if( zUuid ){ blob_appendf(&manifest, " %s", zUuid); free(zUuid); } @@ -629,11 +654,11 @@ } blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); md5sum_blob(&manifest, &mcksum); blob_appendf(&manifest, "Z %b\n", &mcksum); zManifestFile = mprintf("%smanifest", g.zLocalRoot); - if( !noSign && clearsign(&manifest, &manifest) ){ + if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ Blob ans; blob_zero(&ans); prompt_user("unable to sign manifest. continue [y/N]? ", &ans); if( blob_str(&ans)[0]!='y' ){ db_end_transaction(1); @@ -702,11 +727,13 @@ undo_reset(); /* Commit */ db_end_transaction(0); - autosync(AUTOSYNC_PUSH); + if( !g.markPrivate ){ + autosync(AUTOSYNC_PUSH); + } if( count_nonbranch_children(vid)>1 ){ printf("**** warning: a fork has occurred *****\n"); } }
Modified src/clone.c from [f0402d199e] to [70ab216ac2].
@@ -58,10 +58,16 @@ " VALUES('server-code', lower(hex(randomblob(20))));" "REPLACE INTO config(name,value)" " VALUES('last-sync-url', '%q');", g.urlCanonical ); + db_multi_exec( + "DELETE FROM blob WHERE rid IN private;" + "DELETE FROM delta wHERE rid IN private;" + "DELETE FROM private;" + ); + shun_artifacts(); g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); if( g.zLogin==0 ){ db_create_default_users(1); } printf("Repository cloned into %s\n", g.argv[3]);
Modified src/content.c from [6508de1118] to [ebd984372a].
@@ -450,10 +450,14 @@ db_exec(&s1); rid = db_last_insert_rowid(); if( !pBlob ){ db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); } + if( g.markPrivate ){ + db_multi_exec("INSERT INTO private VALUES(%d)", rid); + markAsUnclustered = 0; + } } blob_reset(&cmpr); /* If the srcId is specified, then the data we just added is ** really a delta. Record this fact in the delta table. @@ -510,15 +514,19 @@ db_static_prepare(&s2, "INSERT INTO phantom VALUES(:rid)" ); db_bind_int(&s2, ":rid", rid); db_exec(&s2); - db_static_prepare(&s3, - "INSERT INTO unclustered VALUES(:rid)" - ); - db_bind_int(&s3, ":rid", rid); - db_exec(&s3); + if( g.markPrivate ){ + db_multi_exec("INSERT INTO private VALUES(%d)", rid); + }else{ + db_static_prepare(&s3, + "INSERT INTO unclustered VALUES(:rid)" + ); + db_bind_int(&s3, ":rid", rid); + db_exec(&s3); + } bag_insert(&contentCache.missing, rid); db_end_transaction(0); return rid; } @@ -572,45 +580,84 @@ rid = atoi(g.argv[2]); content_undelta(rid); } /* +** Return true if the given RID is marked as PRIVATE. +*/ +int content_is_private(int rid){ + static Stmt s1; + int rc; + db_static_prepare(&s1, + "SELECT 1 FROM private WHERE rid=:rid" + ); + db_bind_int(&s1, ":rid", rid); + rc = db_step(&s1); + db_reset(&s1); + return rc==SQLITE_ROW; +} + +/* +** Make sure an artifact is public. +*/ +void content_make_public(int rid){ + static Stmt s1; + db_static_prepare(&s1, + "DELETE FROM private WHERE rid=:rid" + ); + db_bind_int(&s1, ":rid", rid); + db_exec(&s1); +} + +/* ** Change the storage of rid so that it is a delta of srcid. ** ** If rid is already a delta from some other place then no ** conversion occurs and this is a no-op unless force==1. +** +** Never generate a delta that carries a private artifact into a public +** artifact. Otherwise, when we go to send the public artifact on a +** sync operation, the other end of the sync will never be able to receive +** the source of the delta. It is OK to delta private->private and +** public->private and public->public. Just no private->public delta. ** ** If srcid is a delta that depends on rid, then srcid is ** converted to undeltaed text. ** ** If either rid or srcid contain less than 50 bytes, or if the ** resulting delta does not achieve a compression of at least 25% on ** its own the rid is left untouched. -** -** NOTE: IMHO the creation of the delta should be defered until after -** the blob sizes have been checked. Doing it before the check as is -** done now the code will generate a delta just to immediately throw -** it away, wasting space and time. */ void content_deltify(int rid, int srcid, int force){ int s; Blob data, src, delta; Stmt s1, s2; if( srcid==rid ) return; if( !force && findSrcid(rid)>0 ) return; + if( content_is_private(srcid) && !content_is_private(rid) ){ + return; + } s = srcid; while( (s = findSrcid(s))>0 ){ if( s==rid ){ content_undelta(srcid); break; } } content_get(srcid, &src); + if( blob_size(&src)<50 ){ + blob_reset(&src); + return; + } content_get(rid, &data); + if( blob_size(&data)<50 ){ + blob_reset(&src); + blob_reset(&data); + return; + } blob_delta_create(&src, &data, &delta); - if( blob_size(&src)>=50 && blob_size(&data)>=50 && - blob_size(&delta) < blob_size(&data)*0.75 ){ + if( blob_size(&delta) < blob_size(&data)*0.75 ){ blob_compress(&delta, &delta); db_prepare(&s1, "UPDATE blob SET content=:data WHERE rid=%d", rid); db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, srcid); db_bind_blob(&s1, ":data", &delta); db_begin_transaction();
Modified src/info.c from [4d0388b1d8] to [10f41cb27f].
@@ -1394,10 +1394,11 @@ Blob cksum; blob_appendf(&ctrl, "U %F\n", g.zLogin); md5sum_blob(&ctrl, &cksum); blob_appendf(&ctrl, "Z %b\n", &cksum); db_begin_transaction(); + g.markPrivate = content_is_private(rid); nrid = content_put(&ctrl, 0, 0); manifest_crosslink(nrid, &ctrl); db_end_transaction(0); } cgi_redirectf("ci?name=%d", rid);
Modified src/main.c from [0dbd180aa0] to [38aa437f8c].
@@ -81,12 +81,12 @@ Th_Interp *interp; /* The TH1 interpreter */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ - int *aCommitFile; /* Array of files to be committed */ + int markPrivate; /* All new artifacts are private if true */ int urlIsFile; /* True if a "file:" url */ int urlIsHttps; /* True if a "https:" url */ char *urlName; /* Hostname for http: or filename for file: */ char *urlHostname; /* The HOST: parameter on http headers */
Modified src/shun.c from [42fc2023fe] to [95475202fe].
@@ -192,10 +192,12 @@ db_finalize(&q); db_multi_exec( "DELETE FROM delta WHERE rid IN toshun;" "DELETE FROM blob WHERE rid IN toshun;" "DROP TABLE toshun;" + "DELETE FROM private " + " WHERE NOT EXISTS (SELECT 1 FROM blob WHERE rid=private.rid);" ); } /* ** WEBPAGE: rcvfromlist
Modified src/tag.c from [48169c968e] to [11a50e87e6].
@@ -242,10 +242,11 @@ } rid = name_to_rid(g.argv[3]); if( rid==0 ){ fossil_fatal("no such object: %s", g.argv[3]); } + g.markPrivate = content_is_private(rid); zValue = g.argc==5 ? g.argv[4] : 0; db_begin_transaction(); tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); db_end_transaction(0); } @@ -276,10 +277,11 @@ if( name_to_uuid(&uuid, 9) ){ fossil_fatal("%s", g.zErrMsg); return; } rid = name_to_rid(blob_str(&uuid)); + g.markPrivate = content_is_private(rid); blob_zero(&ctrl); #if 0 if( validate16(zTagname, strlen(zTagname)) ){ fossil_fatal(
Modified src/xfer.c from [bb547deab3] to [e2586d1f32].
@@ -97,10 +97,13 @@ ** If DELTASRC exists, then the CONTENT is a delta against the ** content of DELTASRC. ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. +** +** Any artifact successfully received by this routine is considered to +** be public and is therefore removed from the "private" table. */ static void xfer_accept_file(Xfer *pXfer){ int n; int rid; int srcid = 0; @@ -128,10 +131,11 @@ srcid = rid_from_uuid(&pXfer->aToken[2], 1); if( content_get(srcid, &src)==0 ){ rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid); pXfer->nDanglingFile++; db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); + content_make_public(rid); return; } pXfer->nDeltaRcvd++; blob_delta_apply(&src, &content, &content); blob_reset(&src); @@ -145,11 +149,11 @@ rid = content_put(&content, blob_str(&hash), 0); blob_reset(&hash); if( rid==0 ){ blob_appendf(&pXfer->err, "%s", g.zErrMsg); }else{ - /* db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); */ + content_make_public(rid); manifest_crosslink(rid, &content); } remote_has(rid); } @@ -156,10 +160,12 @@ /* ** Try to send a file as a delta against its parent. ** If successful, return the number of bytes in the delta. ** If we cannot generate an appropriate delta, then send ** nothing and return zero. +** +** Never send a delta against a private artifact. */ static int send_delta_parent( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ Blob *pContent, /* The content of the file to send */ @@ -184,11 +190,11 @@ int srcId = 0; for(i=0; srcId==0 && i<count(azQuery); i++){ srcId = db_int(0, azQuery[i], rid); } - if( srcId>0 && content_get(srcId, &src) ){ + if( srcId>0 && !content_is_private(srcId) && content_get(srcId, &src) ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId); blob_delta_create(&src, pContent, &delta); size = blob_size(&delta); if( size>=blob_size(pContent)-50 ){ size = 0; @@ -209,10 +215,12 @@ /* ** Try to send a file as a native delta. ** If successful, return the number of bytes in the delta. ** If we cannot generate an appropriate delta, then send ** nothing and return zero. +** +** Never send a delta against a private artifact. */ static int send_delta_native( Xfer *pXfer, /* The transfer context */ int rid, /* record id of the file to send */ Blob *pUuid /* The UUID of the file to send */ @@ -220,11 +228,11 @@ Blob src, delta; int size = 0; int srcId; srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid); - if( srcId>0 ){ + if( srcId>0 && !content_is_private(srcId) ){ blob_zero(&src); db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId); if( uuid_is_shunned(blob_str(&src)) ){ blob_reset(&src); return 0; @@ -250,15 +258,20 @@ ** The pUuid can be NULL in which case the correct UUID is computed ** from the rid. ** ** Try to send the file as a native delta if nativeDelta is true, or ** as a parent delta if nativeDelta is false. +** +** It should never be the case that rid is a private artifact. But +** as a precaution, this routine does check on rid and if it is private +** this routine becomes a no-op. */ static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){ Blob content, uuid; int size = 0; + if( content_is_private(rid) ) return; if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){ return; } blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid); @@ -308,16 +321,20 @@ blob_reset(&uuid); } /* ** Send a gimme message for every phantom. +** +** It should not be possible to have a private phantom. But just to be +** sure, take care not to send any "gimme" messagse on private artifacts. */ static void request_phantoms(Xfer *pXfer, int maxReq){ Stmt q; db_prepare(&q, "SELECT uuid FROM phantom JOIN blob USING(rid)" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" ); while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){ const char *zUuid = db_column_text(&q, 0); blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); pXfer->nGimmeSent++; @@ -401,11 +418,11 @@ ** unsent table, all the right files will still get transferred. ** It just might require an extra round trip or two. */ static void send_unsent(Xfer *pXfer){ Stmt q; - db_prepare(&q, "SELECT rid FROM unsent"); + db_prepare(&q, "SELECT rid FROM unsent EXCEPT SELECT rid FROM private"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); send_file(pXfer, rid, 0, 0); } db_finalize(&q); @@ -420,10 +437,17 @@ */ static void create_cluster(void){ Blob cluster, cksum; Stmt q; int nUncl; + + /* We should not ever get any private artifacts in the unclustered table. + ** But if we do (because of a bug) now is a good time to delete them. */ + db_multi_exec( + "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" + ); + nUncl = db_int(0, "SELECT count(*) FROM unclustered" " WHERE NOT EXISTS(SELECT 1 FROM phantom" " WHERE rid=unclustered.rid)"); if( nUncl<100 ){ return; @@ -455,10 +479,11 @@ Stmt q; int cnt = 0; db_prepare(&q, "SELECT uuid FROM unclustered JOIN blob USING(rid)" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); cnt++; } @@ -472,10 +497,11 @@ static void send_all(Xfer *pXfer){ Stmt q; db_prepare(&q, "SELECT uuid FROM blob " " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); } db_finalize(&q); @@ -1036,11 +1062,13 @@ if( xfer.nToken==2 && blob_eq(&xfer.aToken[0], "igot") && blob_is_uuid(&xfer.aToken[1]) ){ int rid = rid_from_uuid(&xfer.aToken[1], 0); - if( rid==0 && (pullFlag || cloneFlag) ){ + if( rid>0 ){ + content_make_public(rid); + }else if( pullFlag || cloneFlag ){ rid = content_new(blob_str(&xfer.aToken[1])); if( rid ) newPhantom = 1; } remote_has(rid); }else