Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -1641,13 +1641,12 @@ " WHERE (vmerge.id=-1 OR vmerge.id=-2)" " ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ const char *zCherrypickUuid = db_column_text(&q, 0); int mid = db_column_int(&q, 1); - if( mid != vid ){ - blob_appendf(pOut, "Q %s\n", zCherrypickUuid); - } + if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ) continue; + blob_appendf(pOut, "Q %s\n", zCherrypickUuid); } db_finalize(&q); if( p->pCksum ) blob_appendf(pOut, "R %b\n", p->pCksum); zColor = p->zColor; @@ -1673,19 +1672,19 @@ while( db_step(&q)==SQLITE_ROW ){ const char *zIntegrateUuid = db_column_text(&q, 0); int rid = db_column_int(&q, 1); if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ -#if 0 /* Make sure the check-in manifest of the resulting merge child does not ** include a +close tag referring to the leaf check-in on a private ** branch, so as not to generate a missing artifact reference on - ** repository clones without that private branch. The merge command - ** should have dropped the --integrate option, at this point. */ - assert( !content_is_private(rid) ); -#endif - blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); + ** repository clones without that private branch. Instead, the leaf of + ** the private branch will be closed later by a separate private control + ** artifact. */ + if( !content_is_private(rid) ){ + blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); + } } } db_finalize(&q); if( p->azTag ){ @@ -2076,10 +2075,11 @@ Blob muuid; /* Manifest uuid */ Blob cksum1, cksum2; /* Before and after commit checksums */ Blob cksum1b; /* Checksum recorded in the manifest */ int szD; /* Size of the delta manifest */ int szB; /* Size of the baseline manifest */ + int nClosedPrivateLeaves = 0; /* Number of closed private leafs */ int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; /* Abort the commit due to text format conversions */ Blob ans; /* Answer to continuation prompts */ char cReply; /* First character of ans */ int bRecheck = 0; /* Repeat fork and closed-branch checks*/ @@ -2561,12 +2561,52 @@ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); db_prepare(&q, "SELECT mhash,merge FROM vmerge WHERE id=-4"); while( db_step(&q)==SQLITE_ROW ){ const char *zIntegrateUuid = db_column_text(&q, 0); - if( is_a_leaf(db_column_int(&q, 1)) ){ - fossil_print("Closed: %s\n", zIntegrateUuid); + int rid = db_column_int(&q, 1); + if( is_a_leaf(rid) ){ + if( !content_is_private(rid) ){ + fossil_print("Closed: %s\n", zIntegrateUuid); + }else{ + /* Close leaves of private branches by separate private control + ** artifacts. */ + Blob bCtrl; /* Control artifact to close the private leaf */ + char *zCtrlDate; /* Timestamp of check-in plus N seconds */ + Blob bCtrlXsum; /* Checksum of the control artifact */ + int iCtrlRid; /* RID of the control artifact */ + if( db_exists( + "SELECT 1 FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0", + TAG_CLOSED, rid) ){ + continue; /* Already closed. */ + } + nClosedPrivateLeaves++; + blob_zero(&bCtrl); + zCtrlDate = db_text(0, + "SELECT" + " strftime('%%Y-%%m-%%dT%%H:%%M:%%f',mtime,'+%d seconds')" + " FROM event WHERE objid=%d", + nClosedPrivateLeaves, nvid); + blob_appendf(&bCtrl, "D %z\n", date_in_standard_format(zCtrlDate)); + if( zCtrlDate ) fossil_free(zCtrlDate); + blob_appendf(&bCtrl, "T +closed %s\n", zIntegrateUuid); + blob_appendf(&bCtrl, "U %F\n", + sCiInfo.zUserOvrd ? sCiInfo.zUserOvrd : login_name()); + md5sum_blob(&bCtrl, &bCtrlXsum); + blob_appendf(&bCtrl, "Z %b\n", &bCtrlXsum); + blob_reset(&bCtrlXsum); + iCtrlRid = content_put_ex(&bCtrl, 0, 0, 0, /*isPrivate=*/1); + if( iCtrlRid==0 ){ + fossil_fatal("trouble committing control artifact: %s", g.zErrMsg); + } + fossil_print("Closed: %s (private)\n", zIntegrateUuid); + if( dryRunFlag ) fossil_print("%s", blob_str(&bCtrl)); + if( manifest_crosslink(iCtrlRid, &bCtrl, + dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ + fossil_fatal("%s", g.zErrMsg); + } + } }else{ fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid); } } db_finalize(&q); Index: src/merge.c ================================================================== --- src/merge.c +++ src/merge.c @@ -404,17 +404,10 @@ " Use --force to override.\n"); return; } if( integrateFlag && !is_a_leaf(mid)){ fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); - integrateFlag = 0; - } - if( integrateFlag && content_is_private(mid) ){ - fossil_warning( - "ignoring --integrate: %s is on a private branch" - "\n Use \"fossil amend --close\" (after commit) to close the leaf.", - g.argv[2]); integrateFlag = 0; } if( verboseFlag ){ print_checkin_description(mid, 12, integrateFlag ? "integrate:" : "merge-from:"); Index: www/private.wiki ================================================================== --- www/private.wiki +++ www/private.wiki @@ -40,18 +40,18 @@ A private branch created with Fossil version 1.30 or newer can also be converted into a public branch using the fossil publish command. However, there is no way to convert a private branch created with older versions of Fossil into a public branch. -The --integrate option of fossil merge (to close -the merged branch when committing) is ignored for a private branch -- or the -check-in manifest of the resulting merge child would include a -+close tag referring to the leaf check-in on the private branch, -and generate a missing artifact reference on repository clones without that -private branch. It's still possible to close the leaf of the private branch -(after committing the merge child) with the fossil amend --close -command. +If the --integrate option of fossil merge (to close +the merged branch when committing) is used for a private branch, a separate +control artifact to apply the +close tag is created and added to +the repository during the commit process. Managing the +close +tag directly from the check-in manifest of the resulting merge child would +generate a reference to the leaf check-in on the private branch, and generate +a missing artifact reference (a "phantom") on repository clones without that +private branch.

Syncing Private Branches

A private branch normally stays on the one repository where it was originally created. But sometimes you want to share private branches