Fossil SCM

Improvements to the way Fossil handles merging of private branches into public branches. The "closed" tag on the private branch is omitted so that it does not leak into the public branch causing a phantom. This is a start, but additional improvements are needed.

drh 2020-04-15 12:40 trunk merge
Commit b4beadb5078a03421a62f5a205cea1a52febe84b83c5189c9d271cd54a459136
--- src/branch.c
+++ src/branch.c
@@ -144,11 +144,10 @@
144144
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
145145
}
146146
blob_appendf(&branch, "T *branch * %F\n", zBranch);
147147
blob_appendf(&branch, "T *sym-%F *\n", zBranch);
148148
if( isPrivate ){
149
- blob_appendf(&branch, "T +private *\n");
150149
noSign = 1;
151150
}
152151
153152
/* Cancel all other symbolic tags */
154153
db_prepare(&q,
155154
--- src/branch.c
+++ src/branch.c
@@ -144,11 +144,10 @@
144 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
145 }
146 blob_appendf(&branch, "T *branch * %F\n", zBranch);
147 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
148 if( isPrivate ){
149 blob_appendf(&branch, "T +private *\n");
150 noSign = 1;
151 }
152
153 /* Cancel all other symbolic tags */
154 db_prepare(&q,
155
--- src/branch.c
+++ src/branch.c
@@ -144,11 +144,10 @@
144 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
145 }
146 blob_appendf(&branch, "T *branch * %F\n", zBranch);
147 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
148 if( isPrivate ){
 
149 noSign = 1;
150 }
151
152 /* Cancel all other symbolic tags */
153 db_prepare(&q,
154
+24 -10
--- src/checkin.c
+++ src/checkin.c
@@ -1673,10 +1673,18 @@
16731673
while( db_step(&q)==SQLITE_ROW ){
16741674
const char *zIntegrateUuid = db_column_text(&q, 0);
16751675
int rid = db_column_int(&q, 1);
16761676
if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
16771677
" WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
1678
+#if 0
1679
+ /* Make sure the check-in manifest of the resulting merge child does not
1680
+ ** include a +close tag referring to the leaf check-in on a private
1681
+ ** branch, so as not to generate a missing artifact reference on
1682
+ ** repository clones without that private branch. The merge command
1683
+ ** should have dropped the --integrate option, at this point. */
1684
+ assert( !content_is_private(rid) );
1685
+#endif
16781686
blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
16791687
}
16801688
}
16811689
db_finalize(&q);
16821690
@@ -2040,10 +2048,12 @@
20402048
const char *zComment; /* Check-in comment */
20412049
Stmt q; /* Various queries */
20422050
char *zUuid; /* UUID of the new check-in */
20432051
int useHash = 0; /* True to verify file status using hashing */
20442052
int noSign = 0; /* True to omit signing the manifest using GPG */
2053
+ int privateFlag = 0; /* True if the --private option is present */
2054
+ int privateParent = 0; /* True if the parent check-in is private */
20452055
int isAMerge = 0; /* True if checking in a merge */
20462056
int noWarningFlag = 0; /* True if skipping all warnings */
20472057
int noPrompt = 0; /* True if skipping all prompts */
20482058
int forceFlag = 0; /* Undocumented: Disables all checks */
20492059
int forceDelta = 0; /* Force a delta-manifest */
@@ -2077,10 +2087,11 @@
20772087
memset(&sCiInfo, 0, sizeof(sCiInfo));
20782088
url_proxy_options();
20792089
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
20802090
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
20812091
noSign = find_option("nosign",0,0)!=0;
2092
+ privateFlag = find_option("private",0,0)!=0;
20822093
forceDelta = find_option("delta",0,0)!=0;
20832094
forceBaseline = find_option("baseline",0,0)!=0;
20842095
if( forceDelta && forceBaseline ){
20852096
fossil_fatal("cannot use --delta and --baseline together");
20862097
}
@@ -2109,17 +2120,10 @@
21092120
sizeof(char*)*(nTag+2));
21102121
sCiInfo.azTag[nTag++] = zTag;
21112122
sCiInfo.azTag[nTag] = 0;
21122123
}
21132124
zComFile = find_option("message-file", "M", 1);
2114
- if( find_option("private",0,0) ){
2115
- g.markPrivate = 1;
2116
- if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2117
- if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
2118
- sCiInfo.zBrClr = "#fec084"; /* Orange */
2119
- }
2120
- }
21212125
sCiInfo.zDateOvrd = find_option("date-override",0,1);
21222126
sCiInfo.zUserOvrd = find_option("user-override",0,1);
21232127
db_must_be_within_tree();
21242128
noSign = db_get_boolean("omitsign", 0)|noSign;
21252129
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2129,15 +2133,25 @@
21292133
21302134
/* Get the ID of the parent manifest artifact */
21312135
vid = db_lget_int("checkout", 0);
21322136
if( vid==0 ){
21332137
useCksum = 1;
2134
- if( sCiInfo.zBranch==0 ) {
2138
+ if( privateFlag==0 && sCiInfo.zBranch==0 ) {
21352139
sCiInfo.zBranch=db_get("main-branch", 0);
21362140
}
2137
- }else if( content_is_private(vid) ){
2138
- g.markPrivate = 1;
2141
+ }else{
2142
+ privateParent = content_is_private(vid);
2143
+ }
2144
+
2145
+ /* Track the "private" status */
2146
+ g.markPrivate = privateFlag || privateParent;
2147
+ if( privateFlag && !privateParent ){
2148
+ /* Apply default branch name ("private") and color ("orange") if not
2149
+ ** specified otherwise on the command-line, and if the parent is not
2150
+ ** already private. */
2151
+ if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2152
+ if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084";
21392153
}
21402154
21412155
/* Do not allow the creation of a new branch using an existing open
21422156
** branch name unless the --force flag is used */
21432157
if( sCiInfo.zBranch!=0
21442158
--- src/checkin.c
+++ src/checkin.c
@@ -1673,10 +1673,18 @@
1673 while( db_step(&q)==SQLITE_ROW ){
1674 const char *zIntegrateUuid = db_column_text(&q, 0);
1675 int rid = db_column_int(&q, 1);
1676 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1677 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
 
 
 
 
 
 
 
 
1678 blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
1679 }
1680 }
1681 db_finalize(&q);
1682
@@ -2040,10 +2048,12 @@
2040 const char *zComment; /* Check-in comment */
2041 Stmt q; /* Various queries */
2042 char *zUuid; /* UUID of the new check-in */
2043 int useHash = 0; /* True to verify file status using hashing */
2044 int noSign = 0; /* True to omit signing the manifest using GPG */
 
 
2045 int isAMerge = 0; /* True if checking in a merge */
2046 int noWarningFlag = 0; /* True if skipping all warnings */
2047 int noPrompt = 0; /* True if skipping all prompts */
2048 int forceFlag = 0; /* Undocumented: Disables all checks */
2049 int forceDelta = 0; /* Force a delta-manifest */
@@ -2077,10 +2087,11 @@
2077 memset(&sCiInfo, 0, sizeof(sCiInfo));
2078 url_proxy_options();
2079 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2080 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2081 noSign = find_option("nosign",0,0)!=0;
 
2082 forceDelta = find_option("delta",0,0)!=0;
2083 forceBaseline = find_option("baseline",0,0)!=0;
2084 if( forceDelta && forceBaseline ){
2085 fossil_fatal("cannot use --delta and --baseline together");
2086 }
@@ -2109,17 +2120,10 @@
2109 sizeof(char*)*(nTag+2));
2110 sCiInfo.azTag[nTag++] = zTag;
2111 sCiInfo.azTag[nTag] = 0;
2112 }
2113 zComFile = find_option("message-file", "M", 1);
2114 if( find_option("private",0,0) ){
2115 g.markPrivate = 1;
2116 if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2117 if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
2118 sCiInfo.zBrClr = "#fec084"; /* Orange */
2119 }
2120 }
2121 sCiInfo.zDateOvrd = find_option("date-override",0,1);
2122 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2123 db_must_be_within_tree();
2124 noSign = db_get_boolean("omitsign", 0)|noSign;
2125 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2129,15 +2133,25 @@
2129
2130 /* Get the ID of the parent manifest artifact */
2131 vid = db_lget_int("checkout", 0);
2132 if( vid==0 ){
2133 useCksum = 1;
2134 if( sCiInfo.zBranch==0 ) {
2135 sCiInfo.zBranch=db_get("main-branch", 0);
2136 }
2137 }else if( content_is_private(vid) ){
2138 g.markPrivate = 1;
 
 
 
 
 
 
 
 
 
 
2139 }
2140
2141 /* Do not allow the creation of a new branch using an existing open
2142 ** branch name unless the --force flag is used */
2143 if( sCiInfo.zBranch!=0
2144
--- src/checkin.c
+++ src/checkin.c
@@ -1673,10 +1673,18 @@
1673 while( db_step(&q)==SQLITE_ROW ){
1674 const char *zIntegrateUuid = db_column_text(&q, 0);
1675 int rid = db_column_int(&q, 1);
1676 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1677 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
1678 #if 0
1679 /* Make sure the check-in manifest of the resulting merge child does not
1680 ** include a +close tag referring to the leaf check-in on a private
1681 ** branch, so as not to generate a missing artifact reference on
1682 ** repository clones without that private branch. The merge command
1683 ** should have dropped the --integrate option, at this point. */
1684 assert( !content_is_private(rid) );
1685 #endif
1686 blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
1687 }
1688 }
1689 db_finalize(&q);
1690
@@ -2040,10 +2048,12 @@
2048 const char *zComment; /* Check-in comment */
2049 Stmt q; /* Various queries */
2050 char *zUuid; /* UUID of the new check-in */
2051 int useHash = 0; /* True to verify file status using hashing */
2052 int noSign = 0; /* True to omit signing the manifest using GPG */
2053 int privateFlag = 0; /* True if the --private option is present */
2054 int privateParent = 0; /* True if the parent check-in is private */
2055 int isAMerge = 0; /* True if checking in a merge */
2056 int noWarningFlag = 0; /* True if skipping all warnings */
2057 int noPrompt = 0; /* True if skipping all prompts */
2058 int forceFlag = 0; /* Undocumented: Disables all checks */
2059 int forceDelta = 0; /* Force a delta-manifest */
@@ -2077,10 +2087,11 @@
2087 memset(&sCiInfo, 0, sizeof(sCiInfo));
2088 url_proxy_options();
2089 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2090 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2091 noSign = find_option("nosign",0,0)!=0;
2092 privateFlag = find_option("private",0,0)!=0;
2093 forceDelta = find_option("delta",0,0)!=0;
2094 forceBaseline = find_option("baseline",0,0)!=0;
2095 if( forceDelta && forceBaseline ){
2096 fossil_fatal("cannot use --delta and --baseline together");
2097 }
@@ -2109,17 +2120,10 @@
2120 sizeof(char*)*(nTag+2));
2121 sCiInfo.azTag[nTag++] = zTag;
2122 sCiInfo.azTag[nTag] = 0;
2123 }
2124 zComFile = find_option("message-file", "M", 1);
 
 
 
 
 
 
 
2125 sCiInfo.zDateOvrd = find_option("date-override",0,1);
2126 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2127 db_must_be_within_tree();
2128 noSign = db_get_boolean("omitsign", 0)|noSign;
2129 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2129,15 +2133,25 @@
2133
2134 /* Get the ID of the parent manifest artifact */
2135 vid = db_lget_int("checkout", 0);
2136 if( vid==0 ){
2137 useCksum = 1;
2138 if( privateFlag==0 && sCiInfo.zBranch==0 ) {
2139 sCiInfo.zBranch=db_get("main-branch", 0);
2140 }
2141 }else{
2142 privateParent = content_is_private(vid);
2143 }
2144
2145 /* Track the "private" status */
2146 g.markPrivate = privateFlag || privateParent;
2147 if( privateFlag && !privateParent ){
2148 /* Apply default branch name ("private") and color ("orange") if not
2149 ** specified otherwise on the command-line, and if the parent is not
2150 ** already private. */
2151 if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2152 if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084";
2153 }
2154
2155 /* Do not allow the creation of a new branch using an existing open
2156 ** branch name unless the --force flag is used */
2157 if( sCiInfo.zBranch!=0
2158
--- src/json_branch.c
+++ src/json_branch.c
@@ -266,13 +266,10 @@
266266
if( zColor!=0 ){
267267
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
268268
}
269269
blob_appendf(&branch, "T *branch * %F\n", zBranch);
270270
blob_appendf(&branch, "T *sym-%F *\n", zBranch);
271
- if( zOpt->isPrivate ){
272
- blob_appendf(&branch, "T +private *\n");
273
- }
274271
275272
/* Cancel all other symbolic tags */
276273
db_prepare(&q,
277274
"SELECT tagname FROM tagxref, tag"
278275
" WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
@@ -287,11 +284,11 @@
287284
288285
blob_appendf(&branch, "U %F\n", g.zLogin);
289286
md5sum_blob(&branch, &mcksum);
290287
blob_appendf(&branch, "Z %b\n", &mcksum);
291288
292
- brid = content_put(&branch);
289
+ brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate);
293290
if( brid==0 ){
294291
fossil_panic("Problem committing manifest: %s", g.zErrMsg);
295292
}
296293
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
297294
if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
298295
--- src/json_branch.c
+++ src/json_branch.c
@@ -266,13 +266,10 @@
266 if( zColor!=0 ){
267 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
268 }
269 blob_appendf(&branch, "T *branch * %F\n", zBranch);
270 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
271 if( zOpt->isPrivate ){
272 blob_appendf(&branch, "T +private *\n");
273 }
274
275 /* Cancel all other symbolic tags */
276 db_prepare(&q,
277 "SELECT tagname FROM tagxref, tag"
278 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
@@ -287,11 +284,11 @@
287
288 blob_appendf(&branch, "U %F\n", g.zLogin);
289 md5sum_blob(&branch, &mcksum);
290 blob_appendf(&branch, "Z %b\n", &mcksum);
291
292 brid = content_put(&branch);
293 if( brid==0 ){
294 fossil_panic("Problem committing manifest: %s", g.zErrMsg);
295 }
296 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
297 if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
298
--- src/json_branch.c
+++ src/json_branch.c
@@ -266,13 +266,10 @@
266 if( zColor!=0 ){
267 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
268 }
269 blob_appendf(&branch, "T *branch * %F\n", zBranch);
270 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
 
 
 
271
272 /* Cancel all other symbolic tags */
273 db_prepare(&q,
274 "SELECT tagname FROM tagxref, tag"
275 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
@@ -287,11 +284,11 @@
284
285 blob_appendf(&branch, "U %F\n", g.zLogin);
286 md5sum_blob(&branch, &mcksum);
287 blob_appendf(&branch, "Z %b\n", &mcksum);
288
289 brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate);
290 if( brid==0 ){
291 fossil_panic("Problem committing manifest: %s", g.zErrMsg);
292 }
293 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
294 if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
295
--- src/merge.c
+++ src/merge.c
@@ -405,10 +405,17 @@
405405
return;
406406
}
407407
if( integrateFlag && !is_a_leaf(mid)){
408408
fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]);
409409
integrateFlag = 0;
410
+ }
411
+ if( integrateFlag && content_is_private(mid) ){
412
+ fossil_warning(
413
+ "ignoring --integrate: %s is on a private branch"
414
+ "\n Use \"fossil amend --close\" (after commit) to close the leaf.",
415
+ g.argv[2]);
416
+ integrateFlag = 0;
410417
}
411418
if( verboseFlag ){
412419
print_checkin_description(mid, 12,
413420
integrateFlag ? "integrate:" : "merge-from:");
414421
print_checkin_description(pid, 12, "baseline:");
415422
--- src/merge.c
+++ src/merge.c
@@ -405,10 +405,17 @@
405 return;
406 }
407 if( integrateFlag && !is_a_leaf(mid)){
408 fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]);
409 integrateFlag = 0;
 
 
 
 
 
 
 
410 }
411 if( verboseFlag ){
412 print_checkin_description(mid, 12,
413 integrateFlag ? "integrate:" : "merge-from:");
414 print_checkin_description(pid, 12, "baseline:");
415
--- src/merge.c
+++ src/merge.c
@@ -405,10 +405,17 @@
405 return;
406 }
407 if( integrateFlag && !is_a_leaf(mid)){
408 fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]);
409 integrateFlag = 0;
410 }
411 if( integrateFlag && content_is_private(mid) ){
412 fossil_warning(
413 "ignoring --integrate: %s is on a private branch"
414 "\n Use \"fossil amend --close\" (after commit) to close the leaf.",
415 g.argv[2]);
416 integrateFlag = 0;
417 }
418 if( verboseFlag ){
419 print_checkin_description(mid, 12,
420 integrateFlag ? "integrate:" : "merge-from:");
421 print_checkin_description(pid, 12, "baseline:");
422
+84 -14
--- src/rebuild.c
+++ src/rebuild.c
@@ -1140,30 +1140,79 @@
11401140
free(zFNameFormat);
11411141
zFNameFormat = 0;
11421142
cchFNamePrefix = 0;
11431143
}
11441144
#endif
1145
+
1146
+/*
1147
+** Helper functions used by the `deconstruct' and `reconstruct' commands to
1148
+** save and restore the contents of the PRIVATE table.
1149
+*/
1150
+void private_export(char *zFileName)
1151
+{
1152
+ Stmt q;
1153
+ Blob fctx = empty_blob;
1154
+ blob_append(&fctx, "# The UUIDs of private artifacts\n", -1);
1155
+ db_prepare(&q,
1156
+ "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );");
1157
+ while( db_step(&q)==SQLITE_ROW ){
1158
+ const char *zUuid = db_column_text(&q, 0);
1159
+ blob_append(&fctx, zUuid, -1);
1160
+ blob_append(&fctx, "\n", -1);
1161
+ }
1162
+ db_finalize(&q);
1163
+ blob_write_to_file(&fctx, zFileName);
1164
+ blob_reset(&fctx);
1165
+}
1166
+void private_import(char *zFileName)
1167
+{
1168
+ Blob fctx;
1169
+ if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){
1170
+ Blob line, value;
1171
+ while( blob_line(&fctx, &line)>0 ){
1172
+ char *zUuid;
1173
+ int nUuid;
1174
+ if( blob_token(&line, &value)==0 ) continue; /* Empty line */
1175
+ if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */
1176
+ blob_trim(&value);
1177
+ zUuid = blob_buffer(&value);
1178
+ nUuid = blob_size(&value);
1179
+ zUuid[nUuid] = 0;
1180
+ if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){
1181
+ canonical16(zUuid, nUuid);
1182
+ db_multi_exec(
1183
+ "INSERT OR IGNORE INTO private"
1184
+ " SELECT rid FROM blob WHERE uuid = %Q;",
1185
+ zUuid);
1186
+ }
1187
+ }
1188
+ blob_reset(&fctx);
1189
+ }
1190
+}
11451191
11461192
/*
11471193
** COMMAND: reconstruct*
11481194
**
11491195
** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
11501196
**
1151
-** This command studies the artifacts (files) in DIRECTORY and
1152
-** reconstructs the fossil record from them. It places the new
1153
-** fossil repository in FILENAME. Subdirectories are read, files
1154
-** with leading '.' in the filename are ignored.
1197
+** This command studies the artifacts (files) in DIRECTORY and reconstructs the
1198
+** Fossil record from them. It places the new Fossil repository in FILENAME.
1199
+** Subdirectories are read, files with leading '.' in the filename are ignored.
11551200
**
11561201
** Options:
1157
-** -K|--keep-rid1 Read the filename of the artifact with
1158
-** RID=1 from the file .rid in DIRECTORY.
1202
+** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the
1203
+** file .rid in DIRECTORY.
1204
+** -P|--keep-private Mark the artifacts listed in the file .private in
1205
+** DIRECTORY as private in the new Fossil repository.
11591206
**
11601207
** See also: deconstruct, rebuild
11611208
*/
11621209
void reconstruct_cmd(void) {
11631210
char *zPassword;
1211
+ int fKeepPrivate;
11641212
fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1213
+ fKeepPrivate = find_option("keep-private","P",0)!=0;
11651214
if( g.argc!=4 ){
11661215
usage("FILENAME DIRECTORY");
11671216
}
11681217
if( file_isdir(g.argv[3], ExtFILE)!=1 ){
11691218
fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
@@ -1182,11 +1231,19 @@
11821231
fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
11831232
recon_read_dir(g.argv[3]);
11841233
fossil_print("\nBuilding the Fossil repository...\n");
11851234
11861235
rebuild_db(0, 1, 1);
1236
+
1237
+ /* Backwards compatibility: Mark check-ins with "+private" tags as private. */
11871238
reconstruct_private_table();
1239
+ /* Newer method: Import the list of private artifacts to the PRIVATE table. */
1240
+ if( fKeepPrivate ){
1241
+ char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]);
1242
+ private_import(zFnDotPrivate);
1243
+ free(zFnDotPrivate);
1244
+ }
11881245
11891246
/* Skip the verify_before_commit() step on a reconstruct. Most artifacts
11901247
** will have been changed and verification therefore takes a really, really
11911248
** long time.
11921249
*/
@@ -1202,32 +1259,35 @@
12021259
/*
12031260
** COMMAND: deconstruct*
12041261
**
12051262
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
12061263
**
1207
-**
1208
-** This command exports all artifacts of a given repository and
1209
-** writes all artifacts to the file system. The DESTINATION directory
1210
-** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
1211
-** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters.
1212
-** If -L|--prefixlength is given, the length (default 2) of the directory
1213
-** prefix can be set to 0,1,..,9 characters.
1264
+** This command exports all artifacts of a given repository and writes all
1265
+** artifacts to the file system. The DESTINATION directory will be populated
1266
+** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
1267
+** 40+ character artifact ID, AA the first 2 characters.
1268
+** If -L|--prefixlength is given, the length (default 2) of the directory prefix
1269
+** can be set to 0,1,..,9 characters.
12141270
**
12151271
** Options:
12161272
** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
12171273
** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
12181274
** the file .rid1 in the DESTINATION directory.
12191275
** -L|--prefixlength N Set the length of the names of the DESTINATION
12201276
** subdirectories to N.
12211277
** --private Include private artifacts.
1278
+** -P|--keep-private Save the list of private artifacts to the file
1279
+** .private in the DESTINATION directory (implies
1280
+** the --private option).
12221281
**
1223
-** See also: rebuild, reconstruct
1282
+** See also: reconstruct, rebuild
12241283
*/
12251284
void deconstruct_cmd(void){
12261285
const char *zPrefixOpt;
12271286
Stmt s;
12281287
int privateFlag;
1288
+ int fKeepPrivate;
12291289
12301290
fKeepRid1 = find_option("keep-rid1","K",0)!=0;
12311291
/* get and check prefix length argument and build format string */
12321292
zPrefixOpt=find_option("prefixlength","L",1);
12331293
if( !zPrefixOpt ){
@@ -1240,10 +1300,12 @@
12401300
}
12411301
}
12421302
/* open repository and open query for all artifacts */
12431303
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
12441304
privateFlag = find_option("private",0,0)!=0;
1305
+ fKeepPrivate = find_option("keep-private","P",0)!=0;
1306
+ if( fKeepPrivate ) privateFlag = 1;
12451307
verify_all_options();
12461308
/* check number of arguments */
12471309
if( g.argc!=3 ){
12481310
usage ("?OPTIONS? DESTINATION");
12491311
}
@@ -1307,13 +1369,21 @@
13071369
rebuild_step(rid, size, &content);
13081370
}
13091371
}
13101372
}
13111373
db_finalize(&s);
1374
+
1375
+ /* Export the list of private artifacts. */
1376
+ if( fKeepPrivate ){
1377
+ char *zFnDotPrivate = mprintf("%s/.private", zDestDir);
1378
+ private_export(zFnDotPrivate);
1379
+ free(zFnDotPrivate);
1380
+ }
1381
+
13121382
if(!g.fQuiet && ttyOutput ){
13131383
fossil_print("\n");
13141384
}
13151385
13161386
/* free filename format string */
13171387
free(zFNameFormat);
13181388
zFNameFormat = 0;
13191389
}
13201390
--- src/rebuild.c
+++ src/rebuild.c
@@ -1140,30 +1140,79 @@
1140 free(zFNameFormat);
1141 zFNameFormat = 0;
1142 cchFNamePrefix = 0;
1143 }
1144 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1145
1146 /*
1147 ** COMMAND: reconstruct*
1148 **
1149 ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
1150 **
1151 ** This command studies the artifacts (files) in DIRECTORY and
1152 ** reconstructs the fossil record from them. It places the new
1153 ** fossil repository in FILENAME. Subdirectories are read, files
1154 ** with leading '.' in the filename are ignored.
1155 **
1156 ** Options:
1157 ** -K|--keep-rid1 Read the filename of the artifact with
1158 ** RID=1 from the file .rid in DIRECTORY.
 
 
1159 **
1160 ** See also: deconstruct, rebuild
1161 */
1162 void reconstruct_cmd(void) {
1163 char *zPassword;
 
1164 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
 
1165 if( g.argc!=4 ){
1166 usage("FILENAME DIRECTORY");
1167 }
1168 if( file_isdir(g.argv[3], ExtFILE)!=1 ){
1169 fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
@@ -1182,11 +1231,19 @@
1182 fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
1183 recon_read_dir(g.argv[3]);
1184 fossil_print("\nBuilding the Fossil repository...\n");
1185
1186 rebuild_db(0, 1, 1);
 
 
1187 reconstruct_private_table();
 
 
 
 
 
 
1188
1189 /* Skip the verify_before_commit() step on a reconstruct. Most artifacts
1190 ** will have been changed and verification therefore takes a really, really
1191 ** long time.
1192 */
@@ -1202,32 +1259,35 @@
1202 /*
1203 ** COMMAND: deconstruct*
1204 **
1205 ** Usage %fossil deconstruct ?OPTIONS? DESTINATION
1206 **
1207 **
1208 ** This command exports all artifacts of a given repository and
1209 ** writes all artifacts to the file system. The DESTINATION directory
1210 ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
1211 ** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters.
1212 ** If -L|--prefixlength is given, the length (default 2) of the directory
1213 ** prefix can be set to 0,1,..,9 characters.
1214 **
1215 ** Options:
1216 ** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
1217 ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
1218 ** the file .rid1 in the DESTINATION directory.
1219 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1220 ** subdirectories to N.
1221 ** --private Include private artifacts.
 
 
 
1222 **
1223 ** See also: rebuild, reconstruct
1224 */
1225 void deconstruct_cmd(void){
1226 const char *zPrefixOpt;
1227 Stmt s;
1228 int privateFlag;
 
1229
1230 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1231 /* get and check prefix length argument and build format string */
1232 zPrefixOpt=find_option("prefixlength","L",1);
1233 if( !zPrefixOpt ){
@@ -1240,10 +1300,12 @@
1240 }
1241 }
1242 /* open repository and open query for all artifacts */
1243 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
1244 privateFlag = find_option("private",0,0)!=0;
 
 
1245 verify_all_options();
1246 /* check number of arguments */
1247 if( g.argc!=3 ){
1248 usage ("?OPTIONS? DESTINATION");
1249 }
@@ -1307,13 +1369,21 @@
1307 rebuild_step(rid, size, &content);
1308 }
1309 }
1310 }
1311 db_finalize(&s);
 
 
 
 
 
 
 
 
1312 if(!g.fQuiet && ttyOutput ){
1313 fossil_print("\n");
1314 }
1315
1316 /* free filename format string */
1317 free(zFNameFormat);
1318 zFNameFormat = 0;
1319 }
1320
--- src/rebuild.c
+++ src/rebuild.c
@@ -1140,30 +1140,79 @@
1140 free(zFNameFormat);
1141 zFNameFormat = 0;
1142 cchFNamePrefix = 0;
1143 }
1144 #endif
1145
1146 /*
1147 ** Helper functions used by the `deconstruct' and `reconstruct' commands to
1148 ** save and restore the contents of the PRIVATE table.
1149 */
1150 void private_export(char *zFileName)
1151 {
1152 Stmt q;
1153 Blob fctx = empty_blob;
1154 blob_append(&fctx, "# The UUIDs of private artifacts\n", -1);
1155 db_prepare(&q,
1156 "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );");
1157 while( db_step(&q)==SQLITE_ROW ){
1158 const char *zUuid = db_column_text(&q, 0);
1159 blob_append(&fctx, zUuid, -1);
1160 blob_append(&fctx, "\n", -1);
1161 }
1162 db_finalize(&q);
1163 blob_write_to_file(&fctx, zFileName);
1164 blob_reset(&fctx);
1165 }
1166 void private_import(char *zFileName)
1167 {
1168 Blob fctx;
1169 if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){
1170 Blob line, value;
1171 while( blob_line(&fctx, &line)>0 ){
1172 char *zUuid;
1173 int nUuid;
1174 if( blob_token(&line, &value)==0 ) continue; /* Empty line */
1175 if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */
1176 blob_trim(&value);
1177 zUuid = blob_buffer(&value);
1178 nUuid = blob_size(&value);
1179 zUuid[nUuid] = 0;
1180 if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){
1181 canonical16(zUuid, nUuid);
1182 db_multi_exec(
1183 "INSERT OR IGNORE INTO private"
1184 " SELECT rid FROM blob WHERE uuid = %Q;",
1185 zUuid);
1186 }
1187 }
1188 blob_reset(&fctx);
1189 }
1190 }
1191
1192 /*
1193 ** COMMAND: reconstruct*
1194 **
1195 ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
1196 **
1197 ** This command studies the artifacts (files) in DIRECTORY and reconstructs the
1198 ** Fossil record from them. It places the new Fossil repository in FILENAME.
1199 ** Subdirectories are read, files with leading '.' in the filename are ignored.
 
1200 **
1201 ** Options:
1202 ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the
1203 ** file .rid in DIRECTORY.
1204 ** -P|--keep-private Mark the artifacts listed in the file .private in
1205 ** DIRECTORY as private in the new Fossil repository.
1206 **
1207 ** See also: deconstruct, rebuild
1208 */
1209 void reconstruct_cmd(void) {
1210 char *zPassword;
1211 int fKeepPrivate;
1212 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1213 fKeepPrivate = find_option("keep-private","P",0)!=0;
1214 if( g.argc!=4 ){
1215 usage("FILENAME DIRECTORY");
1216 }
1217 if( file_isdir(g.argv[3], ExtFILE)!=1 ){
1218 fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
@@ -1182,11 +1231,19 @@
1231 fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
1232 recon_read_dir(g.argv[3]);
1233 fossil_print("\nBuilding the Fossil repository...\n");
1234
1235 rebuild_db(0, 1, 1);
1236
1237 /* Backwards compatibility: Mark check-ins with "+private" tags as private. */
1238 reconstruct_private_table();
1239 /* Newer method: Import the list of private artifacts to the PRIVATE table. */
1240 if( fKeepPrivate ){
1241 char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]);
1242 private_import(zFnDotPrivate);
1243 free(zFnDotPrivate);
1244 }
1245
1246 /* Skip the verify_before_commit() step on a reconstruct. Most artifacts
1247 ** will have been changed and verification therefore takes a really, really
1248 ** long time.
1249 */
@@ -1202,32 +1259,35 @@
1259 /*
1260 ** COMMAND: deconstruct*
1261 **
1262 ** Usage %fossil deconstruct ?OPTIONS? DESTINATION
1263 **
1264 ** This command exports all artifacts of a given repository and writes all
1265 ** artifacts to the file system. The DESTINATION directory will be populated
1266 ** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
1267 ** 40+ character artifact ID, AA the first 2 characters.
1268 ** If -L|--prefixlength is given, the length (default 2) of the directory prefix
1269 ** can be set to 0,1,..,9 characters.
 
1270 **
1271 ** Options:
1272 ** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
1273 ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
1274 ** the file .rid1 in the DESTINATION directory.
1275 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1276 ** subdirectories to N.
1277 ** --private Include private artifacts.
1278 ** -P|--keep-private Save the list of private artifacts to the file
1279 ** .private in the DESTINATION directory (implies
1280 ** the --private option).
1281 **
1282 ** See also: reconstruct, rebuild
1283 */
1284 void deconstruct_cmd(void){
1285 const char *zPrefixOpt;
1286 Stmt s;
1287 int privateFlag;
1288 int fKeepPrivate;
1289
1290 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1291 /* get and check prefix length argument and build format string */
1292 zPrefixOpt=find_option("prefixlength","L",1);
1293 if( !zPrefixOpt ){
@@ -1240,10 +1300,12 @@
1300 }
1301 }
1302 /* open repository and open query for all artifacts */
1303 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
1304 privateFlag = find_option("private",0,0)!=0;
1305 fKeepPrivate = find_option("keep-private","P",0)!=0;
1306 if( fKeepPrivate ) privateFlag = 1;
1307 verify_all_options();
1308 /* check number of arguments */
1309 if( g.argc!=3 ){
1310 usage ("?OPTIONS? DESTINATION");
1311 }
@@ -1307,13 +1369,21 @@
1369 rebuild_step(rid, size, &content);
1370 }
1371 }
1372 }
1373 db_finalize(&s);
1374
1375 /* Export the list of private artifacts. */
1376 if( fKeepPrivate ){
1377 char *zFnDotPrivate = mprintf("%s/.private", zDestDir);
1378 private_export(zFnDotPrivate);
1379 free(zFnDotPrivate);
1380 }
1381
1382 if(!g.fQuiet && ttyOutput ){
1383 fossil_print("\n");
1384 }
1385
1386 /* free filename format string */
1387 free(zFNameFormat);
1388 zFNameFormat = 0;
1389 }
1390
+15 -2
--- www/private.wiki
+++ www/private.wiki
@@ -31,14 +31,27 @@
3131
fossil update trunk
3232
fossil merge private
3333
fossil commit
3434
</pre></blockquote>
3535
36
-The private branch remains private. (There is no way to convert a private
37
-branch into a public branch.) But all of the changes associated with
36
+The private branch remains private, but all of the changes associated with
3837
the private branch are now folded into the public branch and are hence
3938
visible to other users of the project.
39
+
40
+A private branch created with Fossil version 1.30 or newer can also be
41
+converted into a public branch using the <code>fossil publish</code>
42
+command. However, there is no way to convert a private branch created with
43
+older versions of Fossil into a public branch.
44
+
45
+The <code>--integrate</code> option of <code>fossil merge</code> (to close
46
+the merged branch when committing) is ignored for a private branch -- or the
47
+check-in manifest of the resulting merge child would include a
48
+<code>+close</code> tag referring to the leaf check-in on the private branch,
49
+and generate a missing artifact reference on repository clones without that
50
+private branch. It's still possible to close the leaf of the private branch
51
+(after committing the merge child) with the <code>fossil amend --close</code>
52
+command.
4053
4154
<h2>Syncing Private Branches</h2>
4255
4356
A private branch normally stays on the one repository where it was
4457
originally created. But sometimes you want to share private branches
4558
--- www/private.wiki
+++ www/private.wiki
@@ -31,14 +31,27 @@
31 fossil update trunk
32 fossil merge private
33 fossil commit
34 </pre></blockquote>
35
36 The private branch remains private. (There is no way to convert a private
37 branch into a public branch.) But all of the changes associated with
38 the private branch are now folded into the public branch and are hence
39 visible to other users of the project.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
41 <h2>Syncing Private Branches</h2>
42
43 A private branch normally stays on the one repository where it was
44 originally created. But sometimes you want to share private branches
45
--- www/private.wiki
+++ www/private.wiki
@@ -31,14 +31,27 @@
31 fossil update trunk
32 fossil merge private
33 fossil commit
34 </pre></blockquote>
35
36 The private branch remains private, but all of the changes associated with
 
37 the private branch are now folded into the public branch and are hence
38 visible to other users of the project.
39
40 A private branch created with Fossil version 1.30 or newer can also be
41 converted into a public branch using the <code>fossil publish</code>
42 command. However, there is no way to convert a private branch created with
43 older versions of Fossil into a public branch.
44
45 The <code>--integrate</code> option of <code>fossil merge</code> (to close
46 the merged branch when committing) is ignored for a private branch -- or the
47 check-in manifest of the resulting merge child would include a
48 <code>+close</code> tag referring to the leaf check-in on the private branch,
49 and generate a missing artifact reference on repository clones without that
50 private branch. It's still possible to close the leaf of the private branch
51 (after committing the merge child) with the <code>fossil amend --close</code>
52 command.
53
54 <h2>Syncing Private Branches</h2>
55
56 A private branch normally stays on the one repository where it was
57 originally created. But sometimes you want to share private branches
58

Keyboard Shortcuts

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