Index: src/import.c ================================================================== --- src/import.c +++ src/import.c @@ -925,12 +925,11 @@ /* ** Returns the UUID for the RID, or NULL if not found. ** The returned string is allocated via db_text() and must be ** free()d by the caller. */ -char * rid_to_uuid(int rid) -{ +char *rid_to_uuid(int rid){ return db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); } #define SVN_UNKNOWN 0 #define SVN_TRUNK 1 Index: src/manifest.c ================================================================== --- src/manifest.c +++ src/manifest.c @@ -1542,14 +1542,126 @@ if( pmid<=0 ) continue; add_mlink(pmid, 0, mid, pChild, 0); } } } + +/* +** For a check-in with RID "rid" that has nParent parent check-ins given +** by the UUIDs in azParent[], create all appropriate plink and mlink table +** entries. +** +** The primary parent is the first UUID on the azParent[] list. +** +** Return the RID of the primary parent. +*/ +static int manifest_add_checkin_linkages( + int rid, /* The RID of the check-in */ + Manifest *p, /* Manifest for this check-in */ + int nParent, /* Number of parents for this check-in */ + char **azParent /* UUIDs for each parent */ +){ + int i; + int parentid = 0; + char zBaseId[30]; /* Baseline manifest RID for deltas. "NULL" otherwise */ + Stmt q; + + if( p->zBaseline ){ + sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d", + uuid_to_rid(p->zBaseline,1)); + }else{ + sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL"); + } + for(i=0; irDate, zBaseId/*safe-for-%s*/); + if( i==0 ) parentid = pid; + } + add_mlink(parentid, 0, rid, p, 1); + if( nParent>1 ){ + /* Change MLINK.PID from 0 to -1 for files that are added by merge. */ + db_multi_exec( + "UPDATE mlink SET pid=-1" + " WHERE mid=%d" + " AND pid=0" + " AND fnid IN " + " (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid" + " HAVING count(*)<%d)", + rid, rid, nParent + ); + } + db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid); + while( db_step(&q)==SQLITE_ROW ){ + int cid = db_column_int(&q, 0); + int isprim = db_column_int(&q, 1); + add_mlink(rid, p, cid, 0, isprim); + } + db_finalize(&q); + if( nParent==0 ){ + /* For root files (files without parents) add mlink entries + ** showing all content as new. */ + int isPublic = !content_is_private(rid); + for(i=0; inFile; i++){ + add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0, + isPublic, 1, manifest_file_mperm(&p->aFile[i])); + } + } + return parentid; +} + +/* +** There exists a "parent" tag against checkin rid that has value zValue. +** If value is well-formed (meaning that is is a list of UUIDs), then use +** zValue to reparent check-in rid. +*/ +void manifest_reparent_checkin(int rid, const char *zValue){ + int nParent; + char *zCopy = 0; + char **azParent = 0; + Manifest *p = 0; + int i; + int n = (int)strlen(zValue); + nParent = (n+1)/(UUID_SIZE+1); + if( nParent*(UUID_SIZE+1) - 1 !=n ) return; + if( nParent<1 ) return; + zCopy = fossil_strdup(zValue); + azParent = fossil_malloc( sizeof(azParent[0])*nParent ); + for(i=0; izBaseline ){ - sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d", - uuid_to_rid(p->zBaseline,1)); - }else{ - sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL"); - } - for(i=0; inParent; i++){ - int pid = uuid_to_rid(p->azParent[i], 1); - db_multi_exec( - "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)" - "VALUES(%d, %d, %d, %.17g, %s)", - pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/); - if( i==0 ) parentid = pid; - } - add_mlink(parentid, 0, rid, p, 1); - if( p->nParent>1 ){ - /* Change MLINK.PID from 0 to -1 for files that are added by merge. */ - db_multi_exec( - "UPDATE mlink SET pid=-1" - " WHERE mid=%d" - " AND pid=0" - " AND fnid IN " - " (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid" - " HAVING count(*)<%d)", - rid, rid, p->nParent - ); - } - db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid); - while( db_step(&q)==SQLITE_ROW ){ - int cid = db_column_int(&q, 0); - int isprim = db_column_int(&q, 1); - add_mlink(rid, p, cid, 0, isprim); - } - db_finalize(&q); - if( p->nParent==0 ){ - /* For root files (files without parents) add mlink entries - ** showing all content as new. */ - int isPublic = !content_is_private(rid); - for(i=0; inFile; i++){ - add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0, - isPublic, 1, manifest_file_mperm(&p->aFile[i])); - } - } + parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent); search_doc_touch('c', rid, 0); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," "bgcolor,euser,ecomment,omtime)" "VALUES('ci'," Index: src/tag.c ================================================================== --- src/tag.c +++ src/tag.c @@ -148,10 +148,13 @@ return id; } /* ** Insert a tag into the database. +** +** Also translate zTag into a tagid and return the tagid. (In other words +** if zTag is "bgcolor" then return TAG_BGCOLOR.) */ int tag_insert( const char *zTag, /* Name of the tag (w/o the "+" or "-" prefix */ int tagtype, /* 0:cancel 1:singleton 2:propagated */ const char *zValue, /* Value if the tag is really a property */ @@ -228,10 +231,13 @@ " SET mtime=julianday(%Q)," " omtime=coalesce(omtime,mtime)" " WHERE objid=%d", zValue, rid); } + if( tagid==TAG_PARENT && tagtype==1 ){ + manifest_reparent_checkin(rid, zValue); + } if( tagtype==1 ) tagtype = 0; tag_propagate(rid, tagid, tagtype, rid, zValue, mtime); return tagid; } @@ -273,13 +279,26 @@ db_begin_transaction(); tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); db_end_transaction(0); } +/* +** OR this value into the tagtype argument to tag_add_artifact to +** cause the tag to be displayed on standard output rather than be +** inserted. Used for --dryrun options and debugging. +*/ +#if INTERFACE +#define TAG_ADD_DRYRUN 0x04 +#endif + /* ** Add a control record to the repository that either creates ** or cancels a tag. +** +** tagtype should normally be 0, 1, or 2. But if the TAG_ADD_DRYRUN bit +** is also set, then simply print the text of the tag on standard output +** (for testing purposes) rather than create the tag. */ void tag_add_artifact( const char *zPrefix, /* Prefix to prepend to tag name */ const char *zTagname, /* The tag to add or cancel */ const char *zObjName, /* Name of object attached to */ @@ -293,11 +312,16 @@ char *zDate; Blob uuid; Blob ctrl; Blob cksum; static const char zTagtype[] = { '-', '+', '*' }; + int dryRun = 0; + if( tagtype & TAG_ADD_DRYRUN ){ + tagtype &= ~TAG_ADD_DRYRUN; + dryRun = 1; + } assert( tagtype>=0 && tagtype<=2 ); user_select(); blob_zero(&uuid); blob_append(&uuid, zObjName, -1); if( name_to_uuid(&uuid, 9, "*") ){ @@ -327,12 +351,17 @@ blob_appendf(&ctrl, "\n"); } blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : login_name()); md5sum_blob(&ctrl, &cksum); blob_appendf(&ctrl, "Z %b\n", &cksum); - nrid = content_put(&ctrl); - manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); + if( dryRun ){ + fossil_print("%s", blob_str(&ctrl)); + blob_reset(&ctrl); + }else{ + nrid = content_put(&ctrl); + manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); + } assert( blob_is_reset(&ctrl) ); } /* ** COMMAND: tag @@ -347,23 +376,26 @@ ** be usable instead of a CHECK-IN in commands such as ** update and merge. If the --propagate flag is present, ** the tag value propagates to all descendants of CHECK-IN ** ** Options: -** --raw Raw tag name. -** --propagate Propagating tag. -** --date-override DATETIME Set date and time added. -** --user-override USER Name USER when adding the tag. +** --raw Raw tag name. +** --propagate Propagating tag. +** --date-override DATETIME Set date and time added. +** --user-override USER Name USER when adding the tag. +** --dryrun|-n Display the tag text, but to not +** actually insert it into the database. ** ** The --date-override and --user-override options support ** importing history from other SCM systems. DATETIME has ** the form 'YYYY-MMM-DD HH:MM:SS'. ** ** %fossil tag cancel ?--raw? TAGNAME CHECK-IN ** ** Remove the tag TAGNAME from CHECK-IN, and also remove -** the propagation of the tag to any descendants. +** the propagation of the tag to any descendants. Use the +** the --dryrun or -n options to see what would have happened. ** ** %fossil tag find ?OPTIONS? TAGNAME ** ** List all objects that use TAGNAME. TYPE can be "ci" for ** check-ins or "e" for events. The limit option limits the number @@ -416,33 +448,37 @@ goto tag_cmd_usage; } if( strncmp(g.argv[2],"add",n)==0 ){ char *zValue; + int dryRun = 0; const char *zDateOvrd = find_option("date-override",0,1); const char *zUserOvrd = find_option("user-override",0,1); + if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; if( g.argc!=5 && g.argc!=6 ){ - usage("add ?--raw? ?--propagate? TAGNAME CHECK-IN ?VALUE?"); + usage("add ?options? TAGNAME CHECK-IN ?VALUE?"); } zValue = g.argc==6 ? g.argv[5] : 0; db_begin_transaction(); tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue, - 1+fPropagate,zDateOvrd,zUserOvrd); + 1+fPropagate+dryRun,zDateOvrd,zUserOvrd); db_end_transaction(0); }else if( strncmp(g.argv[2],"branch",n)==0 ){ fossil_fatal("the \"fossil tag branch\" command is discontinued\n" "Use the \"fossil branch new\" command instead."); }else if( strncmp(g.argv[2],"cancel",n)==0 ){ + int dryRun = 0; + if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; if( g.argc!=5 ){ - usage("cancel ?--raw? TAGNAME CHECK-IN"); + usage("cancel ?options? TAGNAME CHECK-IN"); } db_begin_transaction(); - tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, 0, 0, 0); + tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, dryRun, 0, 0); db_end_transaction(0); }else if( strncmp(g.argv[2],"find",n)==0 ){ Stmt q; @@ -546,10 +582,65 @@ return; tag_cmd_usage: usage("add|cancel|find|list ..."); } + +/* +** COMMAND: reparent* +** +** Usage: %fossil reparent [OPTIONS] CHECK-IN PARENT .... +** +** Create a "parent" tag that causes CHECK-IN to be interpreted as a +** child of PARENT. If multiple PARENTs are listed, then the first is +** the primary parent and others are merge ancestors. +** +** This is an experts-only command. It is used to patch up a repository +** that has been damaged by a shun or that has been pieced together from +** two or more separate repositories. You should never need to reparent +** during normal operations. +** +** Reparenting is accomplished by adding a parent tag. So to undo the +** reparenting operation, simply delete the tag. +** +** --test Make database entries but do not add the tag artifact. +** So the reparent operation will be undone by the next +** "fossil rebuild" command. +** --dryrun | -n Print the tag that would have been created but do not +** actually change the database in any way. +*/ +void reparent_cmd(void){ + int bTest = find_option("test","",0)!=0; + int rid; + int i; + Blob value; + char *zUuid; + int dryRun = 0; + + if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN; + db_find_and_open_repository(0, 0); + verify_all_options(); + if( g.argc<4 ){ + usage("reparent [OPTIONS] PARENT ..."); + } + rid = name_to_typed_rid(g.argv[2], "ci"); + blob_init(&value, 0, 0); + for(i=3; i3 ) blob_append(&value, " ", 1); + zUuid = rid_to_uuid(pid); + blob_append(&value, zUuid, UUID_SIZE); + fossil_free(zUuid); + } + if( bTest && !dryRun ){ + tag_insert("parent", 1, blob_str(&value), -1, 0.0, rid); + }else{ + zUuid = rid_to_uuid(rid); + tag_add_artifact("","parent",zUuid,blob_str(&value),1|dryRun,0,0); + } +} + /* ** WEBPAGE: taglist ** ** List all non-propagating symbolic tags.