Fossil SCM

Fixes for incremental Git import/export.

mistachkin 2016-10-26 21:28 trunk merge
Commit 27c8985cc1b75cf56873723ca4e2ac1a6c265ded
+90 -47
--- src/export.c
+++ src/export.c
@@ -132,23 +132,28 @@
132132
133133
/*
134134
** create_mark()
135135
** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table,
136136
** and return that information as a struct mark_t in *mark.
137
+** *unused_mark is a value representing a mark that is free for use--that is,
138
+** it does not appear in the marks file, and has not been used during this
139
+** export run. Specifically, it is the supremum of the set of used marks
140
+** plus one.
137141
** This function returns -1 in the case where 'rid' does not exist, otherwise
138142
** it returns 0.
139143
** mark->name is dynamically allocated and is owned by the caller upon return.
140144
*/
141
-int create_mark(int rid, struct mark_t *mark){
145
+int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){
142146
char sid[13];
143147
char *zUuid = rid_to_uuid(rid);
144
- if(!zUuid){
148
+ if( !zUuid ){
145149
fossil_trace("Undefined rid=%d\n", rid);
146150
return -1;
147151
}
148152
mark->rid = rid;
149
- sqlite3_snprintf(sizeof(sid), sid, ":%d", COMMITMARK(rid));
153
+ sqlite3_snprintf(sizeof(sid), sid, ":%d", *unused_mark);
154
+ *unused_mark += 1;
150155
mark->name = fossil_strdup(sid);
151156
sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
152157
free(zUuid);
153158
insert_commit_xref(mark->rid, mark->name, mark->uuid);
154159
return 0;
@@ -156,19 +161,22 @@
156161
157162
/*
158163
** mark_name_from_rid()
159164
** Find the mark associated with the given rid. Mark names always start
160165
** with ':', and are pulled from the 'xmark' temporary table.
161
-** This function returns NULL if the rid does not exist in the 'xmark' table.
162
-** Otherwise, it returns the name of the mark, which is dynamically allocated
163
-** and is owned by the caller of this function.
166
+** If the given rid doesn't have a mark associated with it yet, one is
167
+** created with a value of *unused_mark.
168
+** *unused_mark functions exactly as in create_mark().
169
+** This function returns NULL if the rid does not have an associated UUID,
170
+** (i.e. is not valid). Otherwise, it returns the name of the mark, which is
171
+** dynamically allocated and is owned by the caller of this function.
164172
*/
165
-char * mark_name_from_rid(int rid){
173
+char * mark_name_from_rid(int rid, unsigned int *unused_mark){
166174
char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid);
167
- if(zMark==NULL){
175
+ if( zMark==NULL ){
168176
struct mark_t mark;
169
- if(create_mark(rid, &mark)==0){
177
+ if( create_mark(rid, &mark, unused_mark)==0 ){
170178
zMark = mark.name;
171179
}else{
172180
return NULL;
173181
}
174182
}
@@ -185,43 +193,52 @@
185193
** database. Otherwise, 0 is returned.
186194
** mark->name is dynamically allocated, and owned by the caller.
187195
*/
188196
int parse_mark(char *line, struct mark_t *mark){
189197
char *cur_tok;
198
+ char type_;
190199
cur_tok = strtok(line, " \t");
191
- if(!cur_tok||strlen(cur_tok)<2){
200
+ if( !cur_tok || strlen(cur_tok)<2 ){
192201
return -1;
193202
}
194203
mark->rid = atoi(&cur_tok[1]);
195
- if(cur_tok[0]!='c'){
204
+ type_ = cur_tok[0];
205
+ if( type_!='c' && type_!='b' ){
196206
/* This is probably a blob mark */
197207
mark->name = NULL;
198208
return 0;
199209
}
200210
201211
cur_tok = strtok(NULL, " \t");
202
- if(!cur_tok){
212
+ if( !cur_tok ){
203213
/* This mark was generated by an older version of Fossil and doesn't
204214
** include the mark name and uuid. create_mark() will name the new mark
205215
** exactly as it was when exported to git, so that we should have a
206216
** valid mapping from git sha1<->mark name<->fossil sha1. */
207
- return create_mark(mark->rid, mark);
217
+ unsigned int mid;
218
+ if( type_=='c' ){
219
+ mid = COMMITMARK(mark->rid);
220
+ }
221
+ else{
222
+ mid = BLOBMARK(mark->rid);
223
+ }
224
+ return create_mark(mark->rid, mark, &mid);
208225
}else{
209226
mark->name = fossil_strdup(cur_tok);
210227
}
211228
212229
cur_tok = strtok(NULL, "\n");
213
- if(!cur_tok||strlen(cur_tok)!=40){
230
+ if( !cur_tok || strlen(cur_tok)!=40 ){
214231
free(mark->name);
215232
fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
216233
return -1;
217234
}else{
218235
sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
219236
}
220237
221238
/* make sure that rid corresponds to UUID */
222
- if(fast_uuid_to_rid(mark->uuid)!=mark->rid){
239
+ if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
223240
free(mark->name);
224241
fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
225242
return -1;
226243
}
227244
@@ -233,40 +250,66 @@
233250
/*
234251
** import_marks()
235252
** Import the marks specified in file 'f' into the 'xmark' table.
236253
** If 'blobs' is non-null, insert all blob marks into it.
237254
** If 'vers' is non-null, insert all commit marks into it.
255
+** If 'unused_marks' is non-null, upon return of this function, all values
256
+** x >= *unused_marks are free to use as marks, i.e. they do not clash with
257
+** any marks appearing in the marks file.
238258
** Each line in the file must be at most 100 characters in length. This
239259
** seems like a reasonable maximum for a 40-character uuid, and 1-13
240260
** character rid.
241261
** The function returns -1 if any of the lines in file 'f' are malformed,
242262
** or the rid/uuid information doesn't match what is in the repository
243263
** database. Otherwise, 0 is returned.
244264
*/
245
-int import_marks(FILE* f, Bag *blobs, Bag *vers){
265
+int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){
246266
char line[101];
247267
while(fgets(line, sizeof(line), f)){
248268
struct mark_t mark;
249
- if(strlen(line)==100&&line[99]!='\n'){
269
+ if( strlen(line)==100 && line[99]!='\n' ){
250270
/* line too long */
251271
return -1;
252272
}
253273
if( parse_mark(line, &mark)<0 ){
254274
return -1;
255275
}else if( line[0]=='b' ){
256
- /* Don't import blob marks into 'xmark' table--git doesn't use them,
257
- ** so they need to be left free for git to reuse. */
258
- if(blobs!=NULL){
276
+ if( blobs!=NULL ){
259277
bag_insert(blobs, mark.rid);
260278
}
261
- }else if( vers!=NULL ){
262
- bag_insert(vers, mark.rid);
279
+ }else{
280
+ if( vers!=NULL ){
281
+ bag_insert(vers, mark.rid);
282
+ }
283
+ }
284
+ if( unused_mark!=NULL ){
285
+ unsigned int mid = atoi(mark.name + 1);
286
+ if( mid>=*unused_mark ){
287
+ *unused_mark = mid + 1;
288
+ }
263289
}
264290
free(mark.name);
265291
}
266292
return 0;
267293
}
294
+
295
+void export_mark(FILE* f, int rid, char obj_type)
296
+{
297
+ unsigned int z = 0;
298
+ char *zUuid = rid_to_uuid(rid);
299
+ char *zMark;
300
+ if( zUuid==NULL ){
301
+ fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
302
+ return;
303
+ }
304
+ /* Since rid is already in the 'xmark' table, the value of z won't be
305
+ ** used, but pass in a valid pointer just to be safe. */
306
+ zMark = mark_name_from_rid(rid, &z);
307
+ fprintf(f, "%c%d %s %s\n", obj_type, rid, zMark, zUuid);
308
+ free(zMark);
309
+ free(zUuid);
310
+}
268311
269312
/*
270313
** If 'blobs' is non-null, it must point to a Bag of blob rids to be
271314
** written to disk. Blob rids are written as 'b<rid>'.
272315
** If 'vers' is non-null, it must point to a Bag of commit rids to be
@@ -275,32 +318,24 @@
275318
** This function does not fail, but may produce errors if a uuid cannot
276319
** be found for an rid in 'vers'.
277320
*/
278321
void export_marks(FILE* f, Bag *blobs, Bag *vers){
279322
int rid;
323
+
280324
if( blobs!=NULL ){
281325
rid = bag_first(blobs);
282
- if(rid!=0){
326
+ if( rid!=0 ){
283327
do{
284
- fprintf(f, "b%d\n", rid);
285
- }while((rid = bag_next(blobs, rid))!=0);
328
+ export_mark(f, rid, 'b');
329
+ }while( (rid = bag_next(blobs, rid))!=0 );
286330
}
287331
}
288332
if( vers!=NULL ){
289333
rid = bag_first(vers);
290334
if( rid!=0 ){
291335
do{
292
- char *zUuid = rid_to_uuid(rid);
293
- char *zMark;
294
- if(zUuid==NULL){
295
- fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
296
- continue;
297
- }
298
- zMark = mark_name_from_rid(rid);
299
- fprintf(f, "c%d %s %s\n", rid, zMark, zUuid);
300
- free(zMark);
301
- free(zUuid);
336
+ export_mark(f, rid, 'c');
302337
}while( (rid = bag_next(vers, rid))!=0 );
303338
}
304339
}
305340
}
306341
@@ -336,10 +371,11 @@
336371
*/
337372
void export_cmd(void){
338373
Stmt q, q2, q3;
339374
int i;
340375
Bag blobs, vers;
376
+ unsigned int unused_mark = 1;
341377
const char *markfile_in;
342378
const char *markfile_out;
343379
344380
bag_init(&blobs);
345381
bag_init(&vers);
@@ -362,25 +398,25 @@
362398
363399
f = fossil_fopen(markfile_in, "r");
364400
if( f==0 ){
365401
fossil_fatal("cannot open %s for reading", markfile_in);
366402
}
367
- if(import_marks(f, &blobs, &vers)<0){
403
+ if( import_marks(f, &blobs, &vers, &unused_mark)<0 ){
368404
fossil_fatal("error importing marks from file: %s", markfile_in);
369405
}
370406
db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
371407
db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
372408
rid = bag_first(&blobs);
373
- if(rid!=0){
409
+ if( rid!=0 ){
374410
do{
375411
db_bind_int(&qb, ":rid", rid);
376412
db_step(&qb);
377413
db_reset(&qb);
378414
}while((rid = bag_next(&blobs, rid))!=0);
379415
}
380416
rid = bag_first(&vers);
381
- if(rid!=0){
417
+ if( rid!=0 ){
382418
do{
383419
db_bind_int(&qc, ":rid", rid);
384420
db_step(&qc);
385421
db_reset(&qc);
386422
}while((rid = bag_next(&vers, rid))!=0);
@@ -416,15 +452,18 @@
416452
while( db_step(&q)==SQLITE_ROW ){
417453
int rid = db_column_int(&q, 0);
418454
Blob content;
419455
420456
while( !bag_find(&blobs, rid) ){
457
+ char *zMark;
421458
content_get(rid, &content);
422459
db_bind_int(&q2, ":rid", rid);
423460
db_step(&q2);
424461
db_reset(&q2);
425
- printf("blob\nmark :%d\ndata %d\n", BLOBMARK(rid), blob_size(&content));
462
+ zMark = mark_name_from_rid(rid, &unused_mark);
463
+ printf("blob\nmark %s\ndata %d\n", zMark, blob_size(&content));
464
+ free(zMark);
426465
bag_insert(&blobs, rid);
427466
fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
428467
printf("\n");
429468
blob_reset(&content);
430469
@@ -470,11 +509,11 @@
470509
if( zBranch==0 ) zBranch = "trunk";
471510
zBr = mprintf("%s", zBranch);
472511
for(i=0; zBr[i]; i++){
473512
if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
474513
}
475
- zMark = mark_name_from_rid(ckinId);
514
+ zMark = mark_name_from_rid(ckinId, &unused_mark);
476515
printf("commit refs/heads/%s\nmark %s\n", zBr, zMark);
477516
free(zMark);
478517
free(zBr);
479518
printf("committer");
480519
print_person(zUser);
@@ -487,21 +526,21 @@
487526
" AND pid IN (SELECT objid FROM event)",
488527
ckinId
489528
);
490529
if( db_step(&q3) == SQLITE_ROW ){
491530
int pid = db_column_int(&q3, 0);
492
- zMark = mark_name_from_rid(pid);
531
+ zMark = mark_name_from_rid(pid, &unused_mark);
493532
printf("from %s\n", zMark);
494533
free(zMark);
495534
db_prepare(&q4,
496535
"SELECT pid FROM plink"
497536
" WHERE cid=%d AND NOT isprim"
498537
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
499538
" ORDER BY pid",
500539
ckinId);
501540
while( db_step(&q4)==SQLITE_ROW ){
502
- zMark = mark_name_from_rid(db_column_int(&q4, 0));
541
+ zMark = mark_name_from_rid(db_column_int(&q4, 0), &unused_mark);
503542
printf("merge %s\n", zMark);
504543
free(zMark);
505544
}
506545
db_finalize(&q4);
507546
}else{
@@ -516,20 +555,22 @@
516555
);
517556
while( db_step(&q4)==SQLITE_ROW ){
518557
const char *zName = db_column_text(&q4,0);
519558
int zNew = db_column_int(&q4,1);
520559
int mPerm = db_column_int(&q4,2);
521
- if( zNew==0)
560
+ if( zNew==0 ){
522561
printf("D %s\n", zName);
523
- else if( bag_find(&blobs, zNew) ) {
562
+ }else if( bag_find(&blobs, zNew) ){
563
+ zMark = mark_name_from_rid(zNew, &unused_mark);
524564
const char *zPerm;
525565
switch( mPerm ){
526566
case PERM_LNK: zPerm = "120000"; break;
527567
case PERM_EXE: zPerm = "100755"; break;
528568
default: zPerm = "100644"; break;
529569
}
530
- printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
570
+ printf("M %s %s %s\n", zPerm, zMark, zName);
571
+ free(zMark);
531572
}
532573
}
533574
db_finalize(&q4);
534575
db_finalize(&q3);
535576
printf("\n");
@@ -547,20 +588,22 @@
547588
);
548589
while( db_step(&q)==SQLITE_ROW ){
549590
const char *zTagname = db_column_text(&q, 0);
550591
char *zEncoded = 0;
551592
int rid = db_column_int(&q, 1);
593
+ char *zMark = mark_name_from_rid(rid, &unused_mark);
552594
const char *zSecSince1970 = db_column_text(&q, 2);
553595
int i;
554596
if( rid==0 || !bag_find(&vers, rid) ) continue;
555597
zTagname += 4;
556598
zEncoded = mprintf("%s", zTagname);
557599
for(i=0; zEncoded[i]; i++){
558600
if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
559601
}
560602
printf("tag %s\n", zEncoded);
561
- printf("from :%d\n", COMMITMARK(rid));
603
+ printf("from %s\n", zMark);
604
+ free(zMark);
562605
printf("tagger <tagger> %s +0000\n", zSecSince1970);
563606
printf("data 0\n");
564607
fossil_free(zEncoded);
565608
}
566609
db_finalize(&q);
@@ -570,12 +613,12 @@
570613
f = fossil_fopen(markfile_out, "w");
571614
if( f == 0 ){
572615
fossil_fatal("cannot open %s for writing", markfile_out);
573616
}
574617
export_marks(f, &blobs, &vers);
575
- if( ferror(f)!=0 || fclose(f)!=0 ) {
618
+ if( ferror(f)!=0 || fclose(f)!=0 ){
576619
fossil_fatal("error while writing %s", markfile_out);
577620
}
578621
}
579622
bag_clear(&blobs);
580623
bag_clear(&vers);
581624
}
582625
--- src/export.c
+++ src/export.c
@@ -132,23 +132,28 @@
132
133 /*
134 ** create_mark()
135 ** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table,
136 ** and return that information as a struct mark_t in *mark.
 
 
 
 
137 ** This function returns -1 in the case where 'rid' does not exist, otherwise
138 ** it returns 0.
139 ** mark->name is dynamically allocated and is owned by the caller upon return.
140 */
141 int create_mark(int rid, struct mark_t *mark){
142 char sid[13];
143 char *zUuid = rid_to_uuid(rid);
144 if(!zUuid){
145 fossil_trace("Undefined rid=%d\n", rid);
146 return -1;
147 }
148 mark->rid = rid;
149 sqlite3_snprintf(sizeof(sid), sid, ":%d", COMMITMARK(rid));
 
150 mark->name = fossil_strdup(sid);
151 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
152 free(zUuid);
153 insert_commit_xref(mark->rid, mark->name, mark->uuid);
154 return 0;
@@ -156,19 +161,22 @@
156
157 /*
158 ** mark_name_from_rid()
159 ** Find the mark associated with the given rid. Mark names always start
160 ** with ':', and are pulled from the 'xmark' temporary table.
161 ** This function returns NULL if the rid does not exist in the 'xmark' table.
162 ** Otherwise, it returns the name of the mark, which is dynamically allocated
163 ** and is owned by the caller of this function.
 
 
 
164 */
165 char * mark_name_from_rid(int rid){
166 char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid);
167 if(zMark==NULL){
168 struct mark_t mark;
169 if(create_mark(rid, &mark)==0){
170 zMark = mark.name;
171 }else{
172 return NULL;
173 }
174 }
@@ -185,43 +193,52 @@
185 ** database. Otherwise, 0 is returned.
186 ** mark->name is dynamically allocated, and owned by the caller.
187 */
188 int parse_mark(char *line, struct mark_t *mark){
189 char *cur_tok;
 
190 cur_tok = strtok(line, " \t");
191 if(!cur_tok||strlen(cur_tok)<2){
192 return -1;
193 }
194 mark->rid = atoi(&cur_tok[1]);
195 if(cur_tok[0]!='c'){
 
196 /* This is probably a blob mark */
197 mark->name = NULL;
198 return 0;
199 }
200
201 cur_tok = strtok(NULL, " \t");
202 if(!cur_tok){
203 /* This mark was generated by an older version of Fossil and doesn't
204 ** include the mark name and uuid. create_mark() will name the new mark
205 ** exactly as it was when exported to git, so that we should have a
206 ** valid mapping from git sha1<->mark name<->fossil sha1. */
207 return create_mark(mark->rid, mark);
 
 
 
 
 
 
 
208 }else{
209 mark->name = fossil_strdup(cur_tok);
210 }
211
212 cur_tok = strtok(NULL, "\n");
213 if(!cur_tok||strlen(cur_tok)!=40){
214 free(mark->name);
215 fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
216 return -1;
217 }else{
218 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
219 }
220
221 /* make sure that rid corresponds to UUID */
222 if(fast_uuid_to_rid(mark->uuid)!=mark->rid){
223 free(mark->name);
224 fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
225 return -1;
226 }
227
@@ -233,40 +250,66 @@
233 /*
234 ** import_marks()
235 ** Import the marks specified in file 'f' into the 'xmark' table.
236 ** If 'blobs' is non-null, insert all blob marks into it.
237 ** If 'vers' is non-null, insert all commit marks into it.
 
 
 
238 ** Each line in the file must be at most 100 characters in length. This
239 ** seems like a reasonable maximum for a 40-character uuid, and 1-13
240 ** character rid.
241 ** The function returns -1 if any of the lines in file 'f' are malformed,
242 ** or the rid/uuid information doesn't match what is in the repository
243 ** database. Otherwise, 0 is returned.
244 */
245 int import_marks(FILE* f, Bag *blobs, Bag *vers){
246 char line[101];
247 while(fgets(line, sizeof(line), f)){
248 struct mark_t mark;
249 if(strlen(line)==100&&line[99]!='\n'){
250 /* line too long */
251 return -1;
252 }
253 if( parse_mark(line, &mark)<0 ){
254 return -1;
255 }else if( line[0]=='b' ){
256 /* Don't import blob marks into 'xmark' table--git doesn't use them,
257 ** so they need to be left free for git to reuse. */
258 if(blobs!=NULL){
259 bag_insert(blobs, mark.rid);
260 }
261 }else if( vers!=NULL ){
262 bag_insert(vers, mark.rid);
 
 
 
 
 
 
 
 
263 }
264 free(mark.name);
265 }
266 return 0;
267 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
269 /*
270 ** If 'blobs' is non-null, it must point to a Bag of blob rids to be
271 ** written to disk. Blob rids are written as 'b<rid>'.
272 ** If 'vers' is non-null, it must point to a Bag of commit rids to be
@@ -275,32 +318,24 @@
275 ** This function does not fail, but may produce errors if a uuid cannot
276 ** be found for an rid in 'vers'.
277 */
278 void export_marks(FILE* f, Bag *blobs, Bag *vers){
279 int rid;
 
280 if( blobs!=NULL ){
281 rid = bag_first(blobs);
282 if(rid!=0){
283 do{
284 fprintf(f, "b%d\n", rid);
285 }while((rid = bag_next(blobs, rid))!=0);
286 }
287 }
288 if( vers!=NULL ){
289 rid = bag_first(vers);
290 if( rid!=0 ){
291 do{
292 char *zUuid = rid_to_uuid(rid);
293 char *zMark;
294 if(zUuid==NULL){
295 fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
296 continue;
297 }
298 zMark = mark_name_from_rid(rid);
299 fprintf(f, "c%d %s %s\n", rid, zMark, zUuid);
300 free(zMark);
301 free(zUuid);
302 }while( (rid = bag_next(vers, rid))!=0 );
303 }
304 }
305 }
306
@@ -336,10 +371,11 @@
336 */
337 void export_cmd(void){
338 Stmt q, q2, q3;
339 int i;
340 Bag blobs, vers;
 
341 const char *markfile_in;
342 const char *markfile_out;
343
344 bag_init(&blobs);
345 bag_init(&vers);
@@ -362,25 +398,25 @@
362
363 f = fossil_fopen(markfile_in, "r");
364 if( f==0 ){
365 fossil_fatal("cannot open %s for reading", markfile_in);
366 }
367 if(import_marks(f, &blobs, &vers)<0){
368 fossil_fatal("error importing marks from file: %s", markfile_in);
369 }
370 db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
371 db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
372 rid = bag_first(&blobs);
373 if(rid!=0){
374 do{
375 db_bind_int(&qb, ":rid", rid);
376 db_step(&qb);
377 db_reset(&qb);
378 }while((rid = bag_next(&blobs, rid))!=0);
379 }
380 rid = bag_first(&vers);
381 if(rid!=0){
382 do{
383 db_bind_int(&qc, ":rid", rid);
384 db_step(&qc);
385 db_reset(&qc);
386 }while((rid = bag_next(&vers, rid))!=0);
@@ -416,15 +452,18 @@
416 while( db_step(&q)==SQLITE_ROW ){
417 int rid = db_column_int(&q, 0);
418 Blob content;
419
420 while( !bag_find(&blobs, rid) ){
 
421 content_get(rid, &content);
422 db_bind_int(&q2, ":rid", rid);
423 db_step(&q2);
424 db_reset(&q2);
425 printf("blob\nmark :%d\ndata %d\n", BLOBMARK(rid), blob_size(&content));
 
 
426 bag_insert(&blobs, rid);
427 fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
428 printf("\n");
429 blob_reset(&content);
430
@@ -470,11 +509,11 @@
470 if( zBranch==0 ) zBranch = "trunk";
471 zBr = mprintf("%s", zBranch);
472 for(i=0; zBr[i]; i++){
473 if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
474 }
475 zMark = mark_name_from_rid(ckinId);
476 printf("commit refs/heads/%s\nmark %s\n", zBr, zMark);
477 free(zMark);
478 free(zBr);
479 printf("committer");
480 print_person(zUser);
@@ -487,21 +526,21 @@
487 " AND pid IN (SELECT objid FROM event)",
488 ckinId
489 );
490 if( db_step(&q3) == SQLITE_ROW ){
491 int pid = db_column_int(&q3, 0);
492 zMark = mark_name_from_rid(pid);
493 printf("from %s\n", zMark);
494 free(zMark);
495 db_prepare(&q4,
496 "SELECT pid FROM plink"
497 " WHERE cid=%d AND NOT isprim"
498 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
499 " ORDER BY pid",
500 ckinId);
501 while( db_step(&q4)==SQLITE_ROW ){
502 zMark = mark_name_from_rid(db_column_int(&q4, 0));
503 printf("merge %s\n", zMark);
504 free(zMark);
505 }
506 db_finalize(&q4);
507 }else{
@@ -516,20 +555,22 @@
516 );
517 while( db_step(&q4)==SQLITE_ROW ){
518 const char *zName = db_column_text(&q4,0);
519 int zNew = db_column_int(&q4,1);
520 int mPerm = db_column_int(&q4,2);
521 if( zNew==0)
522 printf("D %s\n", zName);
523 else if( bag_find(&blobs, zNew) ) {
 
524 const char *zPerm;
525 switch( mPerm ){
526 case PERM_LNK: zPerm = "120000"; break;
527 case PERM_EXE: zPerm = "100755"; break;
528 default: zPerm = "100644"; break;
529 }
530 printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
 
531 }
532 }
533 db_finalize(&q4);
534 db_finalize(&q3);
535 printf("\n");
@@ -547,20 +588,22 @@
547 );
548 while( db_step(&q)==SQLITE_ROW ){
549 const char *zTagname = db_column_text(&q, 0);
550 char *zEncoded = 0;
551 int rid = db_column_int(&q, 1);
 
552 const char *zSecSince1970 = db_column_text(&q, 2);
553 int i;
554 if( rid==0 || !bag_find(&vers, rid) ) continue;
555 zTagname += 4;
556 zEncoded = mprintf("%s", zTagname);
557 for(i=0; zEncoded[i]; i++){
558 if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
559 }
560 printf("tag %s\n", zEncoded);
561 printf("from :%d\n", COMMITMARK(rid));
 
562 printf("tagger <tagger> %s +0000\n", zSecSince1970);
563 printf("data 0\n");
564 fossil_free(zEncoded);
565 }
566 db_finalize(&q);
@@ -570,12 +613,12 @@
570 f = fossil_fopen(markfile_out, "w");
571 if( f == 0 ){
572 fossil_fatal("cannot open %s for writing", markfile_out);
573 }
574 export_marks(f, &blobs, &vers);
575 if( ferror(f)!=0 || fclose(f)!=0 ) {
576 fossil_fatal("error while writing %s", markfile_out);
577 }
578 }
579 bag_clear(&blobs);
580 bag_clear(&vers);
581 }
582
--- src/export.c
+++ src/export.c
@@ -132,23 +132,28 @@
132
133 /*
134 ** create_mark()
135 ** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table,
136 ** and return that information as a struct mark_t in *mark.
137 ** *unused_mark is a value representing a mark that is free for use--that is,
138 ** it does not appear in the marks file, and has not been used during this
139 ** export run. Specifically, it is the supremum of the set of used marks
140 ** plus one.
141 ** This function returns -1 in the case where 'rid' does not exist, otherwise
142 ** it returns 0.
143 ** mark->name is dynamically allocated and is owned by the caller upon return.
144 */
145 int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){
146 char sid[13];
147 char *zUuid = rid_to_uuid(rid);
148 if( !zUuid ){
149 fossil_trace("Undefined rid=%d\n", rid);
150 return -1;
151 }
152 mark->rid = rid;
153 sqlite3_snprintf(sizeof(sid), sid, ":%d", *unused_mark);
154 *unused_mark += 1;
155 mark->name = fossil_strdup(sid);
156 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
157 free(zUuid);
158 insert_commit_xref(mark->rid, mark->name, mark->uuid);
159 return 0;
@@ -156,19 +161,22 @@
161
162 /*
163 ** mark_name_from_rid()
164 ** Find the mark associated with the given rid. Mark names always start
165 ** with ':', and are pulled from the 'xmark' temporary table.
166 ** If the given rid doesn't have a mark associated with it yet, one is
167 ** created with a value of *unused_mark.
168 ** *unused_mark functions exactly as in create_mark().
169 ** This function returns NULL if the rid does not have an associated UUID,
170 ** (i.e. is not valid). Otherwise, it returns the name of the mark, which is
171 ** dynamically allocated and is owned by the caller of this function.
172 */
173 char * mark_name_from_rid(int rid, unsigned int *unused_mark){
174 char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid);
175 if( zMark==NULL ){
176 struct mark_t mark;
177 if( create_mark(rid, &mark, unused_mark)==0 ){
178 zMark = mark.name;
179 }else{
180 return NULL;
181 }
182 }
@@ -185,43 +193,52 @@
193 ** database. Otherwise, 0 is returned.
194 ** mark->name is dynamically allocated, and owned by the caller.
195 */
196 int parse_mark(char *line, struct mark_t *mark){
197 char *cur_tok;
198 char type_;
199 cur_tok = strtok(line, " \t");
200 if( !cur_tok || strlen(cur_tok)<2 ){
201 return -1;
202 }
203 mark->rid = atoi(&cur_tok[1]);
204 type_ = cur_tok[0];
205 if( type_!='c' && type_!='b' ){
206 /* This is probably a blob mark */
207 mark->name = NULL;
208 return 0;
209 }
210
211 cur_tok = strtok(NULL, " \t");
212 if( !cur_tok ){
213 /* This mark was generated by an older version of Fossil and doesn't
214 ** include the mark name and uuid. create_mark() will name the new mark
215 ** exactly as it was when exported to git, so that we should have a
216 ** valid mapping from git sha1<->mark name<->fossil sha1. */
217 unsigned int mid;
218 if( type_=='c' ){
219 mid = COMMITMARK(mark->rid);
220 }
221 else{
222 mid = BLOBMARK(mark->rid);
223 }
224 return create_mark(mark->rid, mark, &mid);
225 }else{
226 mark->name = fossil_strdup(cur_tok);
227 }
228
229 cur_tok = strtok(NULL, "\n");
230 if( !cur_tok || strlen(cur_tok)!=40 ){
231 free(mark->name);
232 fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
233 return -1;
234 }else{
235 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
236 }
237
238 /* make sure that rid corresponds to UUID */
239 if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
240 free(mark->name);
241 fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
242 return -1;
243 }
244
@@ -233,40 +250,66 @@
250 /*
251 ** import_marks()
252 ** Import the marks specified in file 'f' into the 'xmark' table.
253 ** If 'blobs' is non-null, insert all blob marks into it.
254 ** If 'vers' is non-null, insert all commit marks into it.
255 ** If 'unused_marks' is non-null, upon return of this function, all values
256 ** x >= *unused_marks are free to use as marks, i.e. they do not clash with
257 ** any marks appearing in the marks file.
258 ** Each line in the file must be at most 100 characters in length. This
259 ** seems like a reasonable maximum for a 40-character uuid, and 1-13
260 ** character rid.
261 ** The function returns -1 if any of the lines in file 'f' are malformed,
262 ** or the rid/uuid information doesn't match what is in the repository
263 ** database. Otherwise, 0 is returned.
264 */
265 int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){
266 char line[101];
267 while(fgets(line, sizeof(line), f)){
268 struct mark_t mark;
269 if( strlen(line)==100 && line[99]!='\n' ){
270 /* line too long */
271 return -1;
272 }
273 if( parse_mark(line, &mark)<0 ){
274 return -1;
275 }else if( line[0]=='b' ){
276 if( blobs!=NULL ){
 
 
277 bag_insert(blobs, mark.rid);
278 }
279 }else{
280 if( vers!=NULL ){
281 bag_insert(vers, mark.rid);
282 }
283 }
284 if( unused_mark!=NULL ){
285 unsigned int mid = atoi(mark.name + 1);
286 if( mid>=*unused_mark ){
287 *unused_mark = mid + 1;
288 }
289 }
290 free(mark.name);
291 }
292 return 0;
293 }
294
295 void export_mark(FILE* f, int rid, char obj_type)
296 {
297 unsigned int z = 0;
298 char *zUuid = rid_to_uuid(rid);
299 char *zMark;
300 if( zUuid==NULL ){
301 fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
302 return;
303 }
304 /* Since rid is already in the 'xmark' table, the value of z won't be
305 ** used, but pass in a valid pointer just to be safe. */
306 zMark = mark_name_from_rid(rid, &z);
307 fprintf(f, "%c%d %s %s\n", obj_type, rid, zMark, zUuid);
308 free(zMark);
309 free(zUuid);
310 }
311
312 /*
313 ** If 'blobs' is non-null, it must point to a Bag of blob rids to be
314 ** written to disk. Blob rids are written as 'b<rid>'.
315 ** If 'vers' is non-null, it must point to a Bag of commit rids to be
@@ -275,32 +318,24 @@
318 ** This function does not fail, but may produce errors if a uuid cannot
319 ** be found for an rid in 'vers'.
320 */
321 void export_marks(FILE* f, Bag *blobs, Bag *vers){
322 int rid;
323
324 if( blobs!=NULL ){
325 rid = bag_first(blobs);
326 if( rid!=0 ){
327 do{
328 export_mark(f, rid, 'b');
329 }while( (rid = bag_next(blobs, rid))!=0 );
330 }
331 }
332 if( vers!=NULL ){
333 rid = bag_first(vers);
334 if( rid!=0 ){
335 do{
336 export_mark(f, rid, 'c');
 
 
 
 
 
 
 
 
 
337 }while( (rid = bag_next(vers, rid))!=0 );
338 }
339 }
340 }
341
@@ -336,10 +371,11 @@
371 */
372 void export_cmd(void){
373 Stmt q, q2, q3;
374 int i;
375 Bag blobs, vers;
376 unsigned int unused_mark = 1;
377 const char *markfile_in;
378 const char *markfile_out;
379
380 bag_init(&blobs);
381 bag_init(&vers);
@@ -362,25 +398,25 @@
398
399 f = fossil_fopen(markfile_in, "r");
400 if( f==0 ){
401 fossil_fatal("cannot open %s for reading", markfile_in);
402 }
403 if( import_marks(f, &blobs, &vers, &unused_mark)<0 ){
404 fossil_fatal("error importing marks from file: %s", markfile_in);
405 }
406 db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
407 db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
408 rid = bag_first(&blobs);
409 if( rid!=0 ){
410 do{
411 db_bind_int(&qb, ":rid", rid);
412 db_step(&qb);
413 db_reset(&qb);
414 }while((rid = bag_next(&blobs, rid))!=0);
415 }
416 rid = bag_first(&vers);
417 if( rid!=0 ){
418 do{
419 db_bind_int(&qc, ":rid", rid);
420 db_step(&qc);
421 db_reset(&qc);
422 }while((rid = bag_next(&vers, rid))!=0);
@@ -416,15 +452,18 @@
452 while( db_step(&q)==SQLITE_ROW ){
453 int rid = db_column_int(&q, 0);
454 Blob content;
455
456 while( !bag_find(&blobs, rid) ){
457 char *zMark;
458 content_get(rid, &content);
459 db_bind_int(&q2, ":rid", rid);
460 db_step(&q2);
461 db_reset(&q2);
462 zMark = mark_name_from_rid(rid, &unused_mark);
463 printf("blob\nmark %s\ndata %d\n", zMark, blob_size(&content));
464 free(zMark);
465 bag_insert(&blobs, rid);
466 fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
467 printf("\n");
468 blob_reset(&content);
469
@@ -470,11 +509,11 @@
509 if( zBranch==0 ) zBranch = "trunk";
510 zBr = mprintf("%s", zBranch);
511 for(i=0; zBr[i]; i++){
512 if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
513 }
514 zMark = mark_name_from_rid(ckinId, &unused_mark);
515 printf("commit refs/heads/%s\nmark %s\n", zBr, zMark);
516 free(zMark);
517 free(zBr);
518 printf("committer");
519 print_person(zUser);
@@ -487,21 +526,21 @@
526 " AND pid IN (SELECT objid FROM event)",
527 ckinId
528 );
529 if( db_step(&q3) == SQLITE_ROW ){
530 int pid = db_column_int(&q3, 0);
531 zMark = mark_name_from_rid(pid, &unused_mark);
532 printf("from %s\n", zMark);
533 free(zMark);
534 db_prepare(&q4,
535 "SELECT pid FROM plink"
536 " WHERE cid=%d AND NOT isprim"
537 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
538 " ORDER BY pid",
539 ckinId);
540 while( db_step(&q4)==SQLITE_ROW ){
541 zMark = mark_name_from_rid(db_column_int(&q4, 0), &unused_mark);
542 printf("merge %s\n", zMark);
543 free(zMark);
544 }
545 db_finalize(&q4);
546 }else{
@@ -516,20 +555,22 @@
555 );
556 while( db_step(&q4)==SQLITE_ROW ){
557 const char *zName = db_column_text(&q4,0);
558 int zNew = db_column_int(&q4,1);
559 int mPerm = db_column_int(&q4,2);
560 if( zNew==0 ){
561 printf("D %s\n", zName);
562 }else if( bag_find(&blobs, zNew) ){
563 zMark = mark_name_from_rid(zNew, &unused_mark);
564 const char *zPerm;
565 switch( mPerm ){
566 case PERM_LNK: zPerm = "120000"; break;
567 case PERM_EXE: zPerm = "100755"; break;
568 default: zPerm = "100644"; break;
569 }
570 printf("M %s %s %s\n", zPerm, zMark, zName);
571 free(zMark);
572 }
573 }
574 db_finalize(&q4);
575 db_finalize(&q3);
576 printf("\n");
@@ -547,20 +588,22 @@
588 );
589 while( db_step(&q)==SQLITE_ROW ){
590 const char *zTagname = db_column_text(&q, 0);
591 char *zEncoded = 0;
592 int rid = db_column_int(&q, 1);
593 char *zMark = mark_name_from_rid(rid, &unused_mark);
594 const char *zSecSince1970 = db_column_text(&q, 2);
595 int i;
596 if( rid==0 || !bag_find(&vers, rid) ) continue;
597 zTagname += 4;
598 zEncoded = mprintf("%s", zTagname);
599 for(i=0; zEncoded[i]; i++){
600 if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
601 }
602 printf("tag %s\n", zEncoded);
603 printf("from %s\n", zMark);
604 free(zMark);
605 printf("tagger <tagger> %s +0000\n", zSecSince1970);
606 printf("data 0\n");
607 fossil_free(zEncoded);
608 }
609 db_finalize(&q);
@@ -570,12 +613,12 @@
613 f = fossil_fopen(markfile_out, "w");
614 if( f == 0 ){
615 fossil_fatal("cannot open %s for writing", markfile_out);
616 }
617 export_marks(f, &blobs, &vers);
618 if( ferror(f)!=0 || fclose(f)!=0 ){
619 fossil_fatal("error while writing %s", markfile_out);
620 }
621 }
622 bag_clear(&blobs);
623 bag_clear(&vers);
624 }
625
+90 -47
--- src/export.c
+++ src/export.c
@@ -132,23 +132,28 @@
132132
133133
/*
134134
** create_mark()
135135
** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table,
136136
** and return that information as a struct mark_t in *mark.
137
+** *unused_mark is a value representing a mark that is free for use--that is,
138
+** it does not appear in the marks file, and has not been used during this
139
+** export run. Specifically, it is the supremum of the set of used marks
140
+** plus one.
137141
** This function returns -1 in the case where 'rid' does not exist, otherwise
138142
** it returns 0.
139143
** mark->name is dynamically allocated and is owned by the caller upon return.
140144
*/
141
-int create_mark(int rid, struct mark_t *mark){
145
+int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){
142146
char sid[13];
143147
char *zUuid = rid_to_uuid(rid);
144
- if(!zUuid){
148
+ if( !zUuid ){
145149
fossil_trace("Undefined rid=%d\n", rid);
146150
return -1;
147151
}
148152
mark->rid = rid;
149
- sqlite3_snprintf(sizeof(sid), sid, ":%d", COMMITMARK(rid));
153
+ sqlite3_snprintf(sizeof(sid), sid, ":%d", *unused_mark);
154
+ *unused_mark += 1;
150155
mark->name = fossil_strdup(sid);
151156
sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
152157
free(zUuid);
153158
insert_commit_xref(mark->rid, mark->name, mark->uuid);
154159
return 0;
@@ -156,19 +161,22 @@
156161
157162
/*
158163
** mark_name_from_rid()
159164
** Find the mark associated with the given rid. Mark names always start
160165
** with ':', and are pulled from the 'xmark' temporary table.
161
-** This function returns NULL if the rid does not exist in the 'xmark' table.
162
-** Otherwise, it returns the name of the mark, which is dynamically allocated
163
-** and is owned by the caller of this function.
166
+** If the given rid doesn't have a mark associated with it yet, one is
167
+** created with a value of *unused_mark.
168
+** *unused_mark functions exactly as in create_mark().
169
+** This function returns NULL if the rid does not have an associated UUID,
170
+** (i.e. is not valid). Otherwise, it returns the name of the mark, which is
171
+** dynamically allocated and is owned by the caller of this function.
164172
*/
165
-char * mark_name_from_rid(int rid){
173
+char * mark_name_from_rid(int rid, unsigned int *unused_mark){
166174
char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid);
167
- if(zMark==NULL){
175
+ if( zMark==NULL ){
168176
struct mark_t mark;
169
- if(create_mark(rid, &mark)==0){
177
+ if( create_mark(rid, &mark, unused_mark)==0 ){
170178
zMark = mark.name;
171179
}else{
172180
return NULL;
173181
}
174182
}
@@ -185,43 +193,52 @@
185193
** database. Otherwise, 0 is returned.
186194
** mark->name is dynamically allocated, and owned by the caller.
187195
*/
188196
int parse_mark(char *line, struct mark_t *mark){
189197
char *cur_tok;
198
+ char type_;
190199
cur_tok = strtok(line, " \t");
191
- if(!cur_tok||strlen(cur_tok)<2){
200
+ if( !cur_tok || strlen(cur_tok)<2 ){
192201
return -1;
193202
}
194203
mark->rid = atoi(&cur_tok[1]);
195
- if(cur_tok[0]!='c'){
204
+ type_ = cur_tok[0];
205
+ if( type_!='c' && type_!='b' ){
196206
/* This is probably a blob mark */
197207
mark->name = NULL;
198208
return 0;
199209
}
200210
201211
cur_tok = strtok(NULL, " \t");
202
- if(!cur_tok){
212
+ if( !cur_tok ){
203213
/* This mark was generated by an older version of Fossil and doesn't
204214
** include the mark name and uuid. create_mark() will name the new mark
205215
** exactly as it was when exported to git, so that we should have a
206216
** valid mapping from git sha1<->mark name<->fossil sha1. */
207
- return create_mark(mark->rid, mark);
217
+ unsigned int mid;
218
+ if( type_=='c' ){
219
+ mid = COMMITMARK(mark->rid);
220
+ }
221
+ else{
222
+ mid = BLOBMARK(mark->rid);
223
+ }
224
+ return create_mark(mark->rid, mark, &mid);
208225
}else{
209226
mark->name = fossil_strdup(cur_tok);
210227
}
211228
212229
cur_tok = strtok(NULL, "\n");
213
- if(!cur_tok||strlen(cur_tok)!=40){
230
+ if( !cur_tok || strlen(cur_tok)!=40 ){
214231
free(mark->name);
215232
fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
216233
return -1;
217234
}else{
218235
sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
219236
}
220237
221238
/* make sure that rid corresponds to UUID */
222
- if(fast_uuid_to_rid(mark->uuid)!=mark->rid){
239
+ if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
223240
free(mark->name);
224241
fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
225242
return -1;
226243
}
227244
@@ -233,40 +250,66 @@
233250
/*
234251
** import_marks()
235252
** Import the marks specified in file 'f' into the 'xmark' table.
236253
** If 'blobs' is non-null, insert all blob marks into it.
237254
** If 'vers' is non-null, insert all commit marks into it.
255
+** If 'unused_marks' is non-null, upon return of this function, all values
256
+** x >= *unused_marks are free to use as marks, i.e. they do not clash with
257
+** any marks appearing in the marks file.
238258
** Each line in the file must be at most 100 characters in length. This
239259
** seems like a reasonable maximum for a 40-character uuid, and 1-13
240260
** character rid.
241261
** The function returns -1 if any of the lines in file 'f' are malformed,
242262
** or the rid/uuid information doesn't match what is in the repository
243263
** database. Otherwise, 0 is returned.
244264
*/
245
-int import_marks(FILE* f, Bag *blobs, Bag *vers){
265
+int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){
246266
char line[101];
247267
while(fgets(line, sizeof(line), f)){
248268
struct mark_t mark;
249
- if(strlen(line)==100&&line[99]!='\n'){
269
+ if( strlen(line)==100 && line[99]!='\n' ){
250270
/* line too long */
251271
return -1;
252272
}
253273
if( parse_mark(line, &mark)<0 ){
254274
return -1;
255275
}else if( line[0]=='b' ){
256
- /* Don't import blob marks into 'xmark' table--git doesn't use them,
257
- ** so they need to be left free for git to reuse. */
258
- if(blobs!=NULL){
276
+ if( blobs!=NULL ){
259277
bag_insert(blobs, mark.rid);
260278
}
261
- }else if( vers!=NULL ){
262
- bag_insert(vers, mark.rid);
279
+ }else{
280
+ if( vers!=NULL ){
281
+ bag_insert(vers, mark.rid);
282
+ }
283
+ }
284
+ if( unused_mark!=NULL ){
285
+ unsigned int mid = atoi(mark.name + 1);
286
+ if( mid>=*unused_mark ){
287
+ *unused_mark = mid + 1;
288
+ }
263289
}
264290
free(mark.name);
265291
}
266292
return 0;
267293
}
294
+
295
+void export_mark(FILE* f, int rid, char obj_type)
296
+{
297
+ unsigned int z = 0;
298
+ char *zUuid = rid_to_uuid(rid);
299
+ char *zMark;
300
+ if( zUuid==NULL ){
301
+ fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
302
+ return;
303
+ }
304
+ /* Since rid is already in the 'xmark' table, the value of z won't be
305
+ ** used, but pass in a valid pointer just to be safe. */
306
+ zMark = mark_name_from_rid(rid, &z);
307
+ fprintf(f, "%c%d %s %s\n", obj_type, rid, zMark, zUuid);
308
+ free(zMark);
309
+ free(zUuid);
310
+}
268311
269312
/*
270313
** If 'blobs' is non-null, it must point to a Bag of blob rids to be
271314
** written to disk. Blob rids are written as 'b<rid>'.
272315
** If 'vers' is non-null, it must point to a Bag of commit rids to be
@@ -275,32 +318,24 @@
275318
** This function does not fail, but may produce errors if a uuid cannot
276319
** be found for an rid in 'vers'.
277320
*/
278321
void export_marks(FILE* f, Bag *blobs, Bag *vers){
279322
int rid;
323
+
280324
if( blobs!=NULL ){
281325
rid = bag_first(blobs);
282
- if(rid!=0){
326
+ if( rid!=0 ){
283327
do{
284
- fprintf(f, "b%d\n", rid);
285
- }while((rid = bag_next(blobs, rid))!=0);
328
+ export_mark(f, rid, 'b');
329
+ }while( (rid = bag_next(blobs, rid))!=0 );
286330
}
287331
}
288332
if( vers!=NULL ){
289333
rid = bag_first(vers);
290334
if( rid!=0 ){
291335
do{
292
- char *zUuid = rid_to_uuid(rid);
293
- char *zMark;
294
- if(zUuid==NULL){
295
- fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
296
- continue;
297
- }
298
- zMark = mark_name_from_rid(rid);
299
- fprintf(f, "c%d %s %s\n", rid, zMark, zUuid);
300
- free(zMark);
301
- free(zUuid);
336
+ export_mark(f, rid, 'c');
302337
}while( (rid = bag_next(vers, rid))!=0 );
303338
}
304339
}
305340
}
306341
@@ -336,10 +371,11 @@
336371
*/
337372
void export_cmd(void){
338373
Stmt q, q2, q3;
339374
int i;
340375
Bag blobs, vers;
376
+ unsigned int unused_mark = 1;
341377
const char *markfile_in;
342378
const char *markfile_out;
343379
344380
bag_init(&blobs);
345381
bag_init(&vers);
@@ -362,25 +398,25 @@
362398
363399
f = fossil_fopen(markfile_in, "r");
364400
if( f==0 ){
365401
fossil_fatal("cannot open %s for reading", markfile_in);
366402
}
367
- if(import_marks(f, &blobs, &vers)<0){
403
+ if( import_marks(f, &blobs, &vers, &unused_mark)<0 ){
368404
fossil_fatal("error importing marks from file: %s", markfile_in);
369405
}
370406
db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
371407
db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
372408
rid = bag_first(&blobs);
373
- if(rid!=0){
409
+ if( rid!=0 ){
374410
do{
375411
db_bind_int(&qb, ":rid", rid);
376412
db_step(&qb);
377413
db_reset(&qb);
378414
}while((rid = bag_next(&blobs, rid))!=0);
379415
}
380416
rid = bag_first(&vers);
381
- if(rid!=0){
417
+ if( rid!=0 ){
382418
do{
383419
db_bind_int(&qc, ":rid", rid);
384420
db_step(&qc);
385421
db_reset(&qc);
386422
}while((rid = bag_next(&vers, rid))!=0);
@@ -416,15 +452,18 @@
416452
while( db_step(&q)==SQLITE_ROW ){
417453
int rid = db_column_int(&q, 0);
418454
Blob content;
419455
420456
while( !bag_find(&blobs, rid) ){
457
+ char *zMark;
421458
content_get(rid, &content);
422459
db_bind_int(&q2, ":rid", rid);
423460
db_step(&q2);
424461
db_reset(&q2);
425
- printf("blob\nmark :%d\ndata %d\n", BLOBMARK(rid), blob_size(&content));
462
+ zMark = mark_name_from_rid(rid, &unused_mark);
463
+ printf("blob\nmark %s\ndata %d\n", zMark, blob_size(&content));
464
+ free(zMark);
426465
bag_insert(&blobs, rid);
427466
fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
428467
printf("\n");
429468
blob_reset(&content);
430469
@@ -470,11 +509,11 @@
470509
if( zBranch==0 ) zBranch = "trunk";
471510
zBr = mprintf("%s", zBranch);
472511
for(i=0; zBr[i]; i++){
473512
if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
474513
}
475
- zMark = mark_name_from_rid(ckinId);
514
+ zMark = mark_name_from_rid(ckinId, &unused_mark);
476515
printf("commit refs/heads/%s\nmark %s\n", zBr, zMark);
477516
free(zMark);
478517
free(zBr);
479518
printf("committer");
480519
print_person(zUser);
@@ -487,21 +526,21 @@
487526
" AND pid IN (SELECT objid FROM event)",
488527
ckinId
489528
);
490529
if( db_step(&q3) == SQLITE_ROW ){
491530
int pid = db_column_int(&q3, 0);
492
- zMark = mark_name_from_rid(pid);
531
+ zMark = mark_name_from_rid(pid, &unused_mark);
493532
printf("from %s\n", zMark);
494533
free(zMark);
495534
db_prepare(&q4,
496535
"SELECT pid FROM plink"
497536
" WHERE cid=%d AND NOT isprim"
498537
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
499538
" ORDER BY pid",
500539
ckinId);
501540
while( db_step(&q4)==SQLITE_ROW ){
502
- zMark = mark_name_from_rid(db_column_int(&q4, 0));
541
+ zMark = mark_name_from_rid(db_column_int(&q4, 0), &unused_mark);
503542
printf("merge %s\n", zMark);
504543
free(zMark);
505544
}
506545
db_finalize(&q4);
507546
}else{
@@ -516,20 +555,22 @@
516555
);
517556
while( db_step(&q4)==SQLITE_ROW ){
518557
const char *zName = db_column_text(&q4,0);
519558
int zNew = db_column_int(&q4,1);
520559
int mPerm = db_column_int(&q4,2);
521
- if( zNew==0)
560
+ if( zNew==0 ){
522561
printf("D %s\n", zName);
523
- else if( bag_find(&blobs, zNew) ) {
562
+ }else if( bag_find(&blobs, zNew) ){
563
+ zMark = mark_name_from_rid(zNew, &unused_mark);
524564
const char *zPerm;
525565
switch( mPerm ){
526566
case PERM_LNK: zPerm = "120000"; break;
527567
case PERM_EXE: zPerm = "100755"; break;
528568
default: zPerm = "100644"; break;
529569
}
530
- printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
570
+ printf("M %s %s %s\n", zPerm, zMark, zName);
571
+ free(zMark);
531572
}
532573
}
533574
db_finalize(&q4);
534575
db_finalize(&q3);
535576
printf("\n");
@@ -547,20 +588,22 @@
547588
);
548589
while( db_step(&q)==SQLITE_ROW ){
549590
const char *zTagname = db_column_text(&q, 0);
550591
char *zEncoded = 0;
551592
int rid = db_column_int(&q, 1);
593
+ char *zMark = mark_name_from_rid(rid, &unused_mark);
552594
const char *zSecSince1970 = db_column_text(&q, 2);
553595
int i;
554596
if( rid==0 || !bag_find(&vers, rid) ) continue;
555597
zTagname += 4;
556598
zEncoded = mprintf("%s", zTagname);
557599
for(i=0; zEncoded[i]; i++){
558600
if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
559601
}
560602
printf("tag %s\n", zEncoded);
561
- printf("from :%d\n", COMMITMARK(rid));
603
+ printf("from %s\n", zMark);
604
+ free(zMark);
562605
printf("tagger <tagger> %s +0000\n", zSecSince1970);
563606
printf("data 0\n");
564607
fossil_free(zEncoded);
565608
}
566609
db_finalize(&q);
@@ -570,12 +613,12 @@
570613
f = fossil_fopen(markfile_out, "w");
571614
if( f == 0 ){
572615
fossil_fatal("cannot open %s for writing", markfile_out);
573616
}
574617
export_marks(f, &blobs, &vers);
575
- if( ferror(f)!=0 || fclose(f)!=0 ) {
618
+ if( ferror(f)!=0 || fclose(f)!=0 ){
576619
fossil_fatal("error while writing %s", markfile_out);
577620
}
578621
}
579622
bag_clear(&blobs);
580623
bag_clear(&vers);
581624
}
582625
--- src/export.c
+++ src/export.c
@@ -132,23 +132,28 @@
132
133 /*
134 ** create_mark()
135 ** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table,
136 ** and return that information as a struct mark_t in *mark.
 
 
 
 
137 ** This function returns -1 in the case where 'rid' does not exist, otherwise
138 ** it returns 0.
139 ** mark->name is dynamically allocated and is owned by the caller upon return.
140 */
141 int create_mark(int rid, struct mark_t *mark){
142 char sid[13];
143 char *zUuid = rid_to_uuid(rid);
144 if(!zUuid){
145 fossil_trace("Undefined rid=%d\n", rid);
146 return -1;
147 }
148 mark->rid = rid;
149 sqlite3_snprintf(sizeof(sid), sid, ":%d", COMMITMARK(rid));
 
150 mark->name = fossil_strdup(sid);
151 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
152 free(zUuid);
153 insert_commit_xref(mark->rid, mark->name, mark->uuid);
154 return 0;
@@ -156,19 +161,22 @@
156
157 /*
158 ** mark_name_from_rid()
159 ** Find the mark associated with the given rid. Mark names always start
160 ** with ':', and are pulled from the 'xmark' temporary table.
161 ** This function returns NULL if the rid does not exist in the 'xmark' table.
162 ** Otherwise, it returns the name of the mark, which is dynamically allocated
163 ** and is owned by the caller of this function.
 
 
 
164 */
165 char * mark_name_from_rid(int rid){
166 char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid);
167 if(zMark==NULL){
168 struct mark_t mark;
169 if(create_mark(rid, &mark)==0){
170 zMark = mark.name;
171 }else{
172 return NULL;
173 }
174 }
@@ -185,43 +193,52 @@
185 ** database. Otherwise, 0 is returned.
186 ** mark->name is dynamically allocated, and owned by the caller.
187 */
188 int parse_mark(char *line, struct mark_t *mark){
189 char *cur_tok;
 
190 cur_tok = strtok(line, " \t");
191 if(!cur_tok||strlen(cur_tok)<2){
192 return -1;
193 }
194 mark->rid = atoi(&cur_tok[1]);
195 if(cur_tok[0]!='c'){
 
196 /* This is probably a blob mark */
197 mark->name = NULL;
198 return 0;
199 }
200
201 cur_tok = strtok(NULL, " \t");
202 if(!cur_tok){
203 /* This mark was generated by an older version of Fossil and doesn't
204 ** include the mark name and uuid. create_mark() will name the new mark
205 ** exactly as it was when exported to git, so that we should have a
206 ** valid mapping from git sha1<->mark name<->fossil sha1. */
207 return create_mark(mark->rid, mark);
 
 
 
 
 
 
 
208 }else{
209 mark->name = fossil_strdup(cur_tok);
210 }
211
212 cur_tok = strtok(NULL, "\n");
213 if(!cur_tok||strlen(cur_tok)!=40){
214 free(mark->name);
215 fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
216 return -1;
217 }else{
218 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
219 }
220
221 /* make sure that rid corresponds to UUID */
222 if(fast_uuid_to_rid(mark->uuid)!=mark->rid){
223 free(mark->name);
224 fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
225 return -1;
226 }
227
@@ -233,40 +250,66 @@
233 /*
234 ** import_marks()
235 ** Import the marks specified in file 'f' into the 'xmark' table.
236 ** If 'blobs' is non-null, insert all blob marks into it.
237 ** If 'vers' is non-null, insert all commit marks into it.
 
 
 
238 ** Each line in the file must be at most 100 characters in length. This
239 ** seems like a reasonable maximum for a 40-character uuid, and 1-13
240 ** character rid.
241 ** The function returns -1 if any of the lines in file 'f' are malformed,
242 ** or the rid/uuid information doesn't match what is in the repository
243 ** database. Otherwise, 0 is returned.
244 */
245 int import_marks(FILE* f, Bag *blobs, Bag *vers){
246 char line[101];
247 while(fgets(line, sizeof(line), f)){
248 struct mark_t mark;
249 if(strlen(line)==100&&line[99]!='\n'){
250 /* line too long */
251 return -1;
252 }
253 if( parse_mark(line, &mark)<0 ){
254 return -1;
255 }else if( line[0]=='b' ){
256 /* Don't import blob marks into 'xmark' table--git doesn't use them,
257 ** so they need to be left free for git to reuse. */
258 if(blobs!=NULL){
259 bag_insert(blobs, mark.rid);
260 }
261 }else if( vers!=NULL ){
262 bag_insert(vers, mark.rid);
 
 
 
 
 
 
 
 
263 }
264 free(mark.name);
265 }
266 return 0;
267 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
269 /*
270 ** If 'blobs' is non-null, it must point to a Bag of blob rids to be
271 ** written to disk. Blob rids are written as 'b<rid>'.
272 ** If 'vers' is non-null, it must point to a Bag of commit rids to be
@@ -275,32 +318,24 @@
275 ** This function does not fail, but may produce errors if a uuid cannot
276 ** be found for an rid in 'vers'.
277 */
278 void export_marks(FILE* f, Bag *blobs, Bag *vers){
279 int rid;
 
280 if( blobs!=NULL ){
281 rid = bag_first(blobs);
282 if(rid!=0){
283 do{
284 fprintf(f, "b%d\n", rid);
285 }while((rid = bag_next(blobs, rid))!=0);
286 }
287 }
288 if( vers!=NULL ){
289 rid = bag_first(vers);
290 if( rid!=0 ){
291 do{
292 char *zUuid = rid_to_uuid(rid);
293 char *zMark;
294 if(zUuid==NULL){
295 fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
296 continue;
297 }
298 zMark = mark_name_from_rid(rid);
299 fprintf(f, "c%d %s %s\n", rid, zMark, zUuid);
300 free(zMark);
301 free(zUuid);
302 }while( (rid = bag_next(vers, rid))!=0 );
303 }
304 }
305 }
306
@@ -336,10 +371,11 @@
336 */
337 void export_cmd(void){
338 Stmt q, q2, q3;
339 int i;
340 Bag blobs, vers;
 
341 const char *markfile_in;
342 const char *markfile_out;
343
344 bag_init(&blobs);
345 bag_init(&vers);
@@ -362,25 +398,25 @@
362
363 f = fossil_fopen(markfile_in, "r");
364 if( f==0 ){
365 fossil_fatal("cannot open %s for reading", markfile_in);
366 }
367 if(import_marks(f, &blobs, &vers)<0){
368 fossil_fatal("error importing marks from file: %s", markfile_in);
369 }
370 db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
371 db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
372 rid = bag_first(&blobs);
373 if(rid!=0){
374 do{
375 db_bind_int(&qb, ":rid", rid);
376 db_step(&qb);
377 db_reset(&qb);
378 }while((rid = bag_next(&blobs, rid))!=0);
379 }
380 rid = bag_first(&vers);
381 if(rid!=0){
382 do{
383 db_bind_int(&qc, ":rid", rid);
384 db_step(&qc);
385 db_reset(&qc);
386 }while((rid = bag_next(&vers, rid))!=0);
@@ -416,15 +452,18 @@
416 while( db_step(&q)==SQLITE_ROW ){
417 int rid = db_column_int(&q, 0);
418 Blob content;
419
420 while( !bag_find(&blobs, rid) ){
 
421 content_get(rid, &content);
422 db_bind_int(&q2, ":rid", rid);
423 db_step(&q2);
424 db_reset(&q2);
425 printf("blob\nmark :%d\ndata %d\n", BLOBMARK(rid), blob_size(&content));
 
 
426 bag_insert(&blobs, rid);
427 fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
428 printf("\n");
429 blob_reset(&content);
430
@@ -470,11 +509,11 @@
470 if( zBranch==0 ) zBranch = "trunk";
471 zBr = mprintf("%s", zBranch);
472 for(i=0; zBr[i]; i++){
473 if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
474 }
475 zMark = mark_name_from_rid(ckinId);
476 printf("commit refs/heads/%s\nmark %s\n", zBr, zMark);
477 free(zMark);
478 free(zBr);
479 printf("committer");
480 print_person(zUser);
@@ -487,21 +526,21 @@
487 " AND pid IN (SELECT objid FROM event)",
488 ckinId
489 );
490 if( db_step(&q3) == SQLITE_ROW ){
491 int pid = db_column_int(&q3, 0);
492 zMark = mark_name_from_rid(pid);
493 printf("from %s\n", zMark);
494 free(zMark);
495 db_prepare(&q4,
496 "SELECT pid FROM plink"
497 " WHERE cid=%d AND NOT isprim"
498 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
499 " ORDER BY pid",
500 ckinId);
501 while( db_step(&q4)==SQLITE_ROW ){
502 zMark = mark_name_from_rid(db_column_int(&q4, 0));
503 printf("merge %s\n", zMark);
504 free(zMark);
505 }
506 db_finalize(&q4);
507 }else{
@@ -516,20 +555,22 @@
516 );
517 while( db_step(&q4)==SQLITE_ROW ){
518 const char *zName = db_column_text(&q4,0);
519 int zNew = db_column_int(&q4,1);
520 int mPerm = db_column_int(&q4,2);
521 if( zNew==0)
522 printf("D %s\n", zName);
523 else if( bag_find(&blobs, zNew) ) {
 
524 const char *zPerm;
525 switch( mPerm ){
526 case PERM_LNK: zPerm = "120000"; break;
527 case PERM_EXE: zPerm = "100755"; break;
528 default: zPerm = "100644"; break;
529 }
530 printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
 
531 }
532 }
533 db_finalize(&q4);
534 db_finalize(&q3);
535 printf("\n");
@@ -547,20 +588,22 @@
547 );
548 while( db_step(&q)==SQLITE_ROW ){
549 const char *zTagname = db_column_text(&q, 0);
550 char *zEncoded = 0;
551 int rid = db_column_int(&q, 1);
 
552 const char *zSecSince1970 = db_column_text(&q, 2);
553 int i;
554 if( rid==0 || !bag_find(&vers, rid) ) continue;
555 zTagname += 4;
556 zEncoded = mprintf("%s", zTagname);
557 for(i=0; zEncoded[i]; i++){
558 if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
559 }
560 printf("tag %s\n", zEncoded);
561 printf("from :%d\n", COMMITMARK(rid));
 
562 printf("tagger <tagger> %s +0000\n", zSecSince1970);
563 printf("data 0\n");
564 fossil_free(zEncoded);
565 }
566 db_finalize(&q);
@@ -570,12 +613,12 @@
570 f = fossil_fopen(markfile_out, "w");
571 if( f == 0 ){
572 fossil_fatal("cannot open %s for writing", markfile_out);
573 }
574 export_marks(f, &blobs, &vers);
575 if( ferror(f)!=0 || fclose(f)!=0 ) {
576 fossil_fatal("error while writing %s", markfile_out);
577 }
578 }
579 bag_clear(&blobs);
580 bag_clear(&vers);
581 }
582
--- src/export.c
+++ src/export.c
@@ -132,23 +132,28 @@
132
133 /*
134 ** create_mark()
135 ** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table,
136 ** and return that information as a struct mark_t in *mark.
137 ** *unused_mark is a value representing a mark that is free for use--that is,
138 ** it does not appear in the marks file, and has not been used during this
139 ** export run. Specifically, it is the supremum of the set of used marks
140 ** plus one.
141 ** This function returns -1 in the case where 'rid' does not exist, otherwise
142 ** it returns 0.
143 ** mark->name is dynamically allocated and is owned by the caller upon return.
144 */
145 int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){
146 char sid[13];
147 char *zUuid = rid_to_uuid(rid);
148 if( !zUuid ){
149 fossil_trace("Undefined rid=%d\n", rid);
150 return -1;
151 }
152 mark->rid = rid;
153 sqlite3_snprintf(sizeof(sid), sid, ":%d", *unused_mark);
154 *unused_mark += 1;
155 mark->name = fossil_strdup(sid);
156 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid);
157 free(zUuid);
158 insert_commit_xref(mark->rid, mark->name, mark->uuid);
159 return 0;
@@ -156,19 +161,22 @@
161
162 /*
163 ** mark_name_from_rid()
164 ** Find the mark associated with the given rid. Mark names always start
165 ** with ':', and are pulled from the 'xmark' temporary table.
166 ** If the given rid doesn't have a mark associated with it yet, one is
167 ** created with a value of *unused_mark.
168 ** *unused_mark functions exactly as in create_mark().
169 ** This function returns NULL if the rid does not have an associated UUID,
170 ** (i.e. is not valid). Otherwise, it returns the name of the mark, which is
171 ** dynamically allocated and is owned by the caller of this function.
172 */
173 char * mark_name_from_rid(int rid, unsigned int *unused_mark){
174 char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid);
175 if( zMark==NULL ){
176 struct mark_t mark;
177 if( create_mark(rid, &mark, unused_mark)==0 ){
178 zMark = mark.name;
179 }else{
180 return NULL;
181 }
182 }
@@ -185,43 +193,52 @@
193 ** database. Otherwise, 0 is returned.
194 ** mark->name is dynamically allocated, and owned by the caller.
195 */
196 int parse_mark(char *line, struct mark_t *mark){
197 char *cur_tok;
198 char type_;
199 cur_tok = strtok(line, " \t");
200 if( !cur_tok || strlen(cur_tok)<2 ){
201 return -1;
202 }
203 mark->rid = atoi(&cur_tok[1]);
204 type_ = cur_tok[0];
205 if( type_!='c' && type_!='b' ){
206 /* This is probably a blob mark */
207 mark->name = NULL;
208 return 0;
209 }
210
211 cur_tok = strtok(NULL, " \t");
212 if( !cur_tok ){
213 /* This mark was generated by an older version of Fossil and doesn't
214 ** include the mark name and uuid. create_mark() will name the new mark
215 ** exactly as it was when exported to git, so that we should have a
216 ** valid mapping from git sha1<->mark name<->fossil sha1. */
217 unsigned int mid;
218 if( type_=='c' ){
219 mid = COMMITMARK(mark->rid);
220 }
221 else{
222 mid = BLOBMARK(mark->rid);
223 }
224 return create_mark(mark->rid, mark, &mid);
225 }else{
226 mark->name = fossil_strdup(cur_tok);
227 }
228
229 cur_tok = strtok(NULL, "\n");
230 if( !cur_tok || strlen(cur_tok)!=40 ){
231 free(mark->name);
232 fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok);
233 return -1;
234 }else{
235 sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok);
236 }
237
238 /* make sure that rid corresponds to UUID */
239 if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){
240 free(mark->name);
241 fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid);
242 return -1;
243 }
244
@@ -233,40 +250,66 @@
250 /*
251 ** import_marks()
252 ** Import the marks specified in file 'f' into the 'xmark' table.
253 ** If 'blobs' is non-null, insert all blob marks into it.
254 ** If 'vers' is non-null, insert all commit marks into it.
255 ** If 'unused_marks' is non-null, upon return of this function, all values
256 ** x >= *unused_marks are free to use as marks, i.e. they do not clash with
257 ** any marks appearing in the marks file.
258 ** Each line in the file must be at most 100 characters in length. This
259 ** seems like a reasonable maximum for a 40-character uuid, and 1-13
260 ** character rid.
261 ** The function returns -1 if any of the lines in file 'f' are malformed,
262 ** or the rid/uuid information doesn't match what is in the repository
263 ** database. Otherwise, 0 is returned.
264 */
265 int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){
266 char line[101];
267 while(fgets(line, sizeof(line), f)){
268 struct mark_t mark;
269 if( strlen(line)==100 && line[99]!='\n' ){
270 /* line too long */
271 return -1;
272 }
273 if( parse_mark(line, &mark)<0 ){
274 return -1;
275 }else if( line[0]=='b' ){
276 if( blobs!=NULL ){
 
 
277 bag_insert(blobs, mark.rid);
278 }
279 }else{
280 if( vers!=NULL ){
281 bag_insert(vers, mark.rid);
282 }
283 }
284 if( unused_mark!=NULL ){
285 unsigned int mid = atoi(mark.name + 1);
286 if( mid>=*unused_mark ){
287 *unused_mark = mid + 1;
288 }
289 }
290 free(mark.name);
291 }
292 return 0;
293 }
294
295 void export_mark(FILE* f, int rid, char obj_type)
296 {
297 unsigned int z = 0;
298 char *zUuid = rid_to_uuid(rid);
299 char *zMark;
300 if( zUuid==NULL ){
301 fossil_trace("No uuid matching rid=%d when exporting marks\n", rid);
302 return;
303 }
304 /* Since rid is already in the 'xmark' table, the value of z won't be
305 ** used, but pass in a valid pointer just to be safe. */
306 zMark = mark_name_from_rid(rid, &z);
307 fprintf(f, "%c%d %s %s\n", obj_type, rid, zMark, zUuid);
308 free(zMark);
309 free(zUuid);
310 }
311
312 /*
313 ** If 'blobs' is non-null, it must point to a Bag of blob rids to be
314 ** written to disk. Blob rids are written as 'b<rid>'.
315 ** If 'vers' is non-null, it must point to a Bag of commit rids to be
@@ -275,32 +318,24 @@
318 ** This function does not fail, but may produce errors if a uuid cannot
319 ** be found for an rid in 'vers'.
320 */
321 void export_marks(FILE* f, Bag *blobs, Bag *vers){
322 int rid;
323
324 if( blobs!=NULL ){
325 rid = bag_first(blobs);
326 if( rid!=0 ){
327 do{
328 export_mark(f, rid, 'b');
329 }while( (rid = bag_next(blobs, rid))!=0 );
330 }
331 }
332 if( vers!=NULL ){
333 rid = bag_first(vers);
334 if( rid!=0 ){
335 do{
336 export_mark(f, rid, 'c');
 
 
 
 
 
 
 
 
 
337 }while( (rid = bag_next(vers, rid))!=0 );
338 }
339 }
340 }
341
@@ -336,10 +371,11 @@
371 */
372 void export_cmd(void){
373 Stmt q, q2, q3;
374 int i;
375 Bag blobs, vers;
376 unsigned int unused_mark = 1;
377 const char *markfile_in;
378 const char *markfile_out;
379
380 bag_init(&blobs);
381 bag_init(&vers);
@@ -362,25 +398,25 @@
398
399 f = fossil_fopen(markfile_in, "r");
400 if( f==0 ){
401 fossil_fatal("cannot open %s for reading", markfile_in);
402 }
403 if( import_marks(f, &blobs, &vers, &unused_mark)<0 ){
404 fossil_fatal("error importing marks from file: %s", markfile_in);
405 }
406 db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)");
407 db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)");
408 rid = bag_first(&blobs);
409 if( rid!=0 ){
410 do{
411 db_bind_int(&qb, ":rid", rid);
412 db_step(&qb);
413 db_reset(&qb);
414 }while((rid = bag_next(&blobs, rid))!=0);
415 }
416 rid = bag_first(&vers);
417 if( rid!=0 ){
418 do{
419 db_bind_int(&qc, ":rid", rid);
420 db_step(&qc);
421 db_reset(&qc);
422 }while((rid = bag_next(&vers, rid))!=0);
@@ -416,15 +452,18 @@
452 while( db_step(&q)==SQLITE_ROW ){
453 int rid = db_column_int(&q, 0);
454 Blob content;
455
456 while( !bag_find(&blobs, rid) ){
457 char *zMark;
458 content_get(rid, &content);
459 db_bind_int(&q2, ":rid", rid);
460 db_step(&q2);
461 db_reset(&q2);
462 zMark = mark_name_from_rid(rid, &unused_mark);
463 printf("blob\nmark %s\ndata %d\n", zMark, blob_size(&content));
464 free(zMark);
465 bag_insert(&blobs, rid);
466 fwrite(blob_buffer(&content), 1, blob_size(&content), stdout);
467 printf("\n");
468 blob_reset(&content);
469
@@ -470,11 +509,11 @@
509 if( zBranch==0 ) zBranch = "trunk";
510 zBr = mprintf("%s", zBranch);
511 for(i=0; zBr[i]; i++){
512 if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_';
513 }
514 zMark = mark_name_from_rid(ckinId, &unused_mark);
515 printf("commit refs/heads/%s\nmark %s\n", zBr, zMark);
516 free(zMark);
517 free(zBr);
518 printf("committer");
519 print_person(zUser);
@@ -487,21 +526,21 @@
526 " AND pid IN (SELECT objid FROM event)",
527 ckinId
528 );
529 if( db_step(&q3) == SQLITE_ROW ){
530 int pid = db_column_int(&q3, 0);
531 zMark = mark_name_from_rid(pid, &unused_mark);
532 printf("from %s\n", zMark);
533 free(zMark);
534 db_prepare(&q4,
535 "SELECT pid FROM plink"
536 " WHERE cid=%d AND NOT isprim"
537 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
538 " ORDER BY pid",
539 ckinId);
540 while( db_step(&q4)==SQLITE_ROW ){
541 zMark = mark_name_from_rid(db_column_int(&q4, 0), &unused_mark);
542 printf("merge %s\n", zMark);
543 free(zMark);
544 }
545 db_finalize(&q4);
546 }else{
@@ -516,20 +555,22 @@
555 );
556 while( db_step(&q4)==SQLITE_ROW ){
557 const char *zName = db_column_text(&q4,0);
558 int zNew = db_column_int(&q4,1);
559 int mPerm = db_column_int(&q4,2);
560 if( zNew==0 ){
561 printf("D %s\n", zName);
562 }else if( bag_find(&blobs, zNew) ){
563 zMark = mark_name_from_rid(zNew, &unused_mark);
564 const char *zPerm;
565 switch( mPerm ){
566 case PERM_LNK: zPerm = "120000"; break;
567 case PERM_EXE: zPerm = "100755"; break;
568 default: zPerm = "100644"; break;
569 }
570 printf("M %s %s %s\n", zPerm, zMark, zName);
571 free(zMark);
572 }
573 }
574 db_finalize(&q4);
575 db_finalize(&q3);
576 printf("\n");
@@ -547,20 +588,22 @@
588 );
589 while( db_step(&q)==SQLITE_ROW ){
590 const char *zTagname = db_column_text(&q, 0);
591 char *zEncoded = 0;
592 int rid = db_column_int(&q, 1);
593 char *zMark = mark_name_from_rid(rid, &unused_mark);
594 const char *zSecSince1970 = db_column_text(&q, 2);
595 int i;
596 if( rid==0 || !bag_find(&vers, rid) ) continue;
597 zTagname += 4;
598 zEncoded = mprintf("%s", zTagname);
599 for(i=0; zEncoded[i]; i++){
600 if( !fossil_isalnum(zEncoded[i]) ) zEncoded[i] = '_';
601 }
602 printf("tag %s\n", zEncoded);
603 printf("from %s\n", zMark);
604 free(zMark);
605 printf("tagger <tagger> %s +0000\n", zSecSince1970);
606 printf("data 0\n");
607 fossil_free(zEncoded);
608 }
609 db_finalize(&q);
@@ -570,12 +613,12 @@
613 f = fossil_fopen(markfile_out, "w");
614 if( f == 0 ){
615 fossil_fatal("cannot open %s for writing", markfile_out);
616 }
617 export_marks(f, &blobs, &vers);
618 if( ferror(f)!=0 || fclose(f)!=0 ){
619 fossil_fatal("error while writing %s", markfile_out);
620 }
621 }
622 bag_clear(&blobs);
623 bag_clear(&vers);
624 }
625
+4 -4
--- src/import.c
+++ src/import.c
@@ -1770,11 +1770,11 @@
17701770
if( markfile_in ){
17711771
FILE *f = fossil_fopen(markfile_in, "r");
17721772
if( !f ){
17731773
fossil_fatal("cannot open %s for reading", markfile_in);
17741774
}
1775
- if(import_marks(f, &blobs, NULL)<0){
1775
+ if( import_marks(f, &blobs, NULL, NULL)<0 ){
17761776
fossil_fatal("error importing marks from file: %s", markfile_in);
17771777
}
17781778
fclose(f);
17791779
}
17801780
@@ -1795,13 +1795,13 @@
17951795
db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
17961796
while( db_step(&q_marks)==SQLITE_ROW ){
17971797
rid = db_column_int(&q_marks, 0);
17981798
if( db_int(0, "SELECT count(objid) FROM event"
17991799
" WHERE objid=%d AND type='ci'", rid)==0 ){
1800
- if( bag_find(&blobs, rid)==0 ){
1801
- bag_insert(&blobs, rid);
1802
- }
1800
+ /* Blob marks exported by git aren't saved between runs, so they need
1801
+ ** to be left free for git to re-use in the future.
1802
+ */
18031803
}else{
18041804
bag_insert(&vers, rid);
18051805
}
18061806
}
18071807
db_finalize(&q_marks);
18081808
--- src/import.c
+++ src/import.c
@@ -1770,11 +1770,11 @@
1770 if( markfile_in ){
1771 FILE *f = fossil_fopen(markfile_in, "r");
1772 if( !f ){
1773 fossil_fatal("cannot open %s for reading", markfile_in);
1774 }
1775 if(import_marks(f, &blobs, NULL)<0){
1776 fossil_fatal("error importing marks from file: %s", markfile_in);
1777 }
1778 fclose(f);
1779 }
1780
@@ -1795,13 +1795,13 @@
1795 db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
1796 while( db_step(&q_marks)==SQLITE_ROW ){
1797 rid = db_column_int(&q_marks, 0);
1798 if( db_int(0, "SELECT count(objid) FROM event"
1799 " WHERE objid=%d AND type='ci'", rid)==0 ){
1800 if( bag_find(&blobs, rid)==0 ){
1801 bag_insert(&blobs, rid);
1802 }
1803 }else{
1804 bag_insert(&vers, rid);
1805 }
1806 }
1807 db_finalize(&q_marks);
1808
--- src/import.c
+++ src/import.c
@@ -1770,11 +1770,11 @@
1770 if( markfile_in ){
1771 FILE *f = fossil_fopen(markfile_in, "r");
1772 if( !f ){
1773 fossil_fatal("cannot open %s for reading", markfile_in);
1774 }
1775 if( import_marks(f, &blobs, NULL, NULL)<0 ){
1776 fossil_fatal("error importing marks from file: %s", markfile_in);
1777 }
1778 fclose(f);
1779 }
1780
@@ -1795,13 +1795,13 @@
1795 db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
1796 while( db_step(&q_marks)==SQLITE_ROW ){
1797 rid = db_column_int(&q_marks, 0);
1798 if( db_int(0, "SELECT count(objid) FROM event"
1799 " WHERE objid=%d AND type='ci'", rid)==0 ){
1800 /* Blob marks exported by git aren't saved between runs, so they need
1801 ** to be left free for git to re-use in the future.
1802 */
1803 }else{
1804 bag_insert(&vers, rid);
1805 }
1806 }
1807 db_finalize(&q_marks);
1808
+4 -4
--- src/import.c
+++ src/import.c
@@ -1770,11 +1770,11 @@
17701770
if( markfile_in ){
17711771
FILE *f = fossil_fopen(markfile_in, "r");
17721772
if( !f ){
17731773
fossil_fatal("cannot open %s for reading", markfile_in);
17741774
}
1775
- if(import_marks(f, &blobs, NULL)<0){
1775
+ if( import_marks(f, &blobs, NULL, NULL)<0 ){
17761776
fossil_fatal("error importing marks from file: %s", markfile_in);
17771777
}
17781778
fclose(f);
17791779
}
17801780
@@ -1795,13 +1795,13 @@
17951795
db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
17961796
while( db_step(&q_marks)==SQLITE_ROW ){
17971797
rid = db_column_int(&q_marks, 0);
17981798
if( db_int(0, "SELECT count(objid) FROM event"
17991799
" WHERE objid=%d AND type='ci'", rid)==0 ){
1800
- if( bag_find(&blobs, rid)==0 ){
1801
- bag_insert(&blobs, rid);
1802
- }
1800
+ /* Blob marks exported by git aren't saved between runs, so they need
1801
+ ** to be left free for git to re-use in the future.
1802
+ */
18031803
}else{
18041804
bag_insert(&vers, rid);
18051805
}
18061806
}
18071807
db_finalize(&q_marks);
18081808
--- src/import.c
+++ src/import.c
@@ -1770,11 +1770,11 @@
1770 if( markfile_in ){
1771 FILE *f = fossil_fopen(markfile_in, "r");
1772 if( !f ){
1773 fossil_fatal("cannot open %s for reading", markfile_in);
1774 }
1775 if(import_marks(f, &blobs, NULL)<0){
1776 fossil_fatal("error importing marks from file: %s", markfile_in);
1777 }
1778 fclose(f);
1779 }
1780
@@ -1795,13 +1795,13 @@
1795 db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
1796 while( db_step(&q_marks)==SQLITE_ROW ){
1797 rid = db_column_int(&q_marks, 0);
1798 if( db_int(0, "SELECT count(objid) FROM event"
1799 " WHERE objid=%d AND type='ci'", rid)==0 ){
1800 if( bag_find(&blobs, rid)==0 ){
1801 bag_insert(&blobs, rid);
1802 }
1803 }else{
1804 bag_insert(&vers, rid);
1805 }
1806 }
1807 db_finalize(&q_marks);
1808
--- src/import.c
+++ src/import.c
@@ -1770,11 +1770,11 @@
1770 if( markfile_in ){
1771 FILE *f = fossil_fopen(markfile_in, "r");
1772 if( !f ){
1773 fossil_fatal("cannot open %s for reading", markfile_in);
1774 }
1775 if( import_marks(f, &blobs, NULL, NULL)<0 ){
1776 fossil_fatal("error importing marks from file: %s", markfile_in);
1777 }
1778 fclose(f);
1779 }
1780
@@ -1795,13 +1795,13 @@
1795 db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark");
1796 while( db_step(&q_marks)==SQLITE_ROW ){
1797 rid = db_column_int(&q_marks, 0);
1798 if( db_int(0, "SELECT count(objid) FROM event"
1799 " WHERE objid=%d AND type='ci'", rid)==0 ){
1800 /* Blob marks exported by git aren't saved between runs, so they need
1801 ** to be left free for git to re-use in the future.
1802 */
1803 }else{
1804 bag_insert(&vers, rid);
1805 }
1806 }
1807 db_finalize(&q_marks);
1808
--- www/changes.wiki
+++ www/changes.wiki
@@ -7,10 +7,11 @@
77
* Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
88
from the "ticketchng" table.
99
* Enhance the "brlist" page to make use of branch colors.
1010
* Remove the "fusefs" command from builds that do not have the underlying
1111
support enabled.
12
+ * Fixes for incremental git import/export.
1213
* TH1 enhancements:
1314
<ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
1415
<li>Add <nowiki>[unversioned list]</nowiki> command.</li>
1516
<li>Add project_description variable.</li>
1617
</ul>
1718
--- www/changes.wiki
+++ www/changes.wiki
@@ -7,10 +7,11 @@
7 * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
8 from the "ticketchng" table.
9 * Enhance the "brlist" page to make use of branch colors.
10 * Remove the "fusefs" command from builds that do not have the underlying
11 support enabled.
 
12 * TH1 enhancements:
13 <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
14 <li>Add <nowiki>[unversioned list]</nowiki> command.</li>
15 <li>Add project_description variable.</li>
16 </ul>
17
--- www/changes.wiki
+++ www/changes.wiki
@@ -7,10 +7,11 @@
7 * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
8 from the "ticketchng" table.
9 * Enhance the "brlist" page to make use of branch colors.
10 * Remove the "fusefs" command from builds that do not have the underlying
11 support enabled.
12 * Fixes for incremental git import/export.
13 * TH1 enhancements:
14 <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
15 <li>Add <nowiki>[unversioned list]</nowiki> command.</li>
16 <li>Add project_description variable.</li>
17 </ul>
18
--- www/changes.wiki
+++ www/changes.wiki
@@ -7,10 +7,11 @@
77
* Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
88
from the "ticketchng" table.
99
* Enhance the "brlist" page to make use of branch colors.
1010
* Remove the "fusefs" command from builds that do not have the underlying
1111
support enabled.
12
+ * Fixes for incremental git import/export.
1213
* TH1 enhancements:
1314
<ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
1415
<li>Add <nowiki>[unversioned list]</nowiki> command.</li>
1516
<li>Add project_description variable.</li>
1617
</ul>
1718
--- www/changes.wiki
+++ www/changes.wiki
@@ -7,10 +7,11 @@
7 * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
8 from the "ticketchng" table.
9 * Enhance the "brlist" page to make use of branch colors.
10 * Remove the "fusefs" command from builds that do not have the underlying
11 support enabled.
 
12 * TH1 enhancements:
13 <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
14 <li>Add <nowiki>[unversioned list]</nowiki> command.</li>
15 <li>Add project_description variable.</li>
16 </ul>
17
--- www/changes.wiki
+++ www/changes.wiki
@@ -7,10 +7,11 @@
7 * Fix [/help?cmd=ticket|ticket set] when using the "+" prefix with fields
8 from the "ticketchng" table.
9 * Enhance the "brlist" page to make use of branch colors.
10 * Remove the "fusefs" command from builds that do not have the underlying
11 support enabled.
12 * Fixes for incremental git import/export.
13 * TH1 enhancements:
14 <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
15 <li>Add <nowiki>[unversioned list]</nowiki> command.</li>
16 <li>Add project_description variable.</li>
17 </ul>
18
+49 -10
--- www/inout.wiki
+++ www/inout.wiki
@@ -49,15 +49,54 @@
4949
format that Fossil will generate. However,
5050
future versions of Fossil might add the ability to generate other
5151
VCS interchange formats, and so for compatibility, the use of the --git
5252
option recommended.
5353
54
-An anonymous user sends this comment:
55
-
56
-<blockquote>
57
-The main Fossil branch is called "trunk", while the main git branch is
58
-called "master". After you've exported your FOSSIL repo to git, you won't
59
-see any files and gitk will complain about a missing "HEAD". You can
60
-resolve this problem by merging "trunk" with "master"
61
-(first verify using git status that you are on the "master" branch):
62
-<tt>git merge trunk</tt>
63
-</blockquote>
54
+<h2>Bidirectional Synchronization</h2>
55
+Fossil also has the ability to synchronize with a Git repository via repeated
56
+imports and/or exports. To do this, it uses marks files to store a record of
57
+artifacts which are known by both Git and Fossil to exist at a given point in
58
+time.
59
+
60
+To illustrate, consider the example of a remote Fossil repository that a
61
+user wants to import into a local Git repository. First, the user would clone
62
+the remote repository and import it into a new Git repository:
63
+
64
+<blockquote><pre>
65
+fossil clone /path/to/remote/repo.fossil repo.fossil
66
+mkdir repo
67
+cd repo
68
+fossil open ../repo.fossil
69
+mkdir ../repo.git
70
+cd ../repo.git
71
+git init .
72
+fossil export --git --export-marks ../repo/fossil.marks \
73
+ ../repo.fossil | git fast-import \
74
+ --export-marks=../repo/git.marks
75
+</pre></blockquote>
76
+
77
+Once the import has completed, the user would need to <tt>git checkout
78
+trunk</tt>. At any point after this, new changes can be imported from the
79
+remote Fossil repository:
80
+
81
+<blockquote><pre>
82
+cd ../repo
83
+fossil pull
84
+cd ../repo.git
85
+fossil export --git --import-marks ../repo/fossil.marks \
86
+ --export-marks ../repo/fossil.marks \
87
+ ../repo.fossil | git fast-import \
88
+ --import-marks=../repo/git.marks \
89
+ --export-marks=../repo/git.marks
90
+</pre></blockquote>
91
+
92
+Changes in the Git repository can be exported to the Fossil repository and then
93
+pushed to the remote:
94
+
95
+<blockquote><pre>
96
+git fast-export --import-marks=../repo/git.marks \
97
+ --export-marks=../repo/git.marks --all | fossil import --git \
98
+ --incremental --import-marks ../repo/fossil.marks \
99
+ --export-marks ../repo/fossil.marks ../repo.fossil
100
+cd ../repo
101
+fossil push
102
+</pre></blockquote>
64103
--- www/inout.wiki
+++ www/inout.wiki
@@ -49,15 +49,54 @@
49 format that Fossil will generate. However,
50 future versions of Fossil might add the ability to generate other
51 VCS interchange formats, and so for compatibility, the use of the --git
52 option recommended.
53
54 An anonymous user sends this comment:
55
56 <blockquote>
57 The main Fossil branch is called "trunk", while the main git branch is
58 called "master". After you've exported your FOSSIL repo to git, you won't
59 see any files and gitk will complain about a missing "HEAD". You can
60 resolve this problem by merging "trunk" with "master"
61 (first verify using git status that you are on the "master" branch):
62 <tt>git merge trunk</tt>
63 </blockquote>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
--- www/inout.wiki
+++ www/inout.wiki
@@ -49,15 +49,54 @@
49 format that Fossil will generate. However,
50 future versions of Fossil might add the ability to generate other
51 VCS interchange formats, and so for compatibility, the use of the --git
52 option recommended.
53
54 <h2>Bidirectional Synchronization</h2>
55 Fossil also has the ability to synchronize with a Git repository via repeated
56 imports and/or exports. To do this, it uses marks files to store a record of
57 artifacts which are known by both Git and Fossil to exist at a given point in
58 time.
59
60 To illustrate, consider the example of a remote Fossil repository that a
61 user wants to import into a local Git repository. First, the user would clone
62 the remote repository and import it into a new Git repository:
63
64 <blockquote><pre>
65 fossil clone /path/to/remote/repo.fossil repo.fossil
66 mkdir repo
67 cd repo
68 fossil open ../repo.fossil
69 mkdir ../repo.git
70 cd ../repo.git
71 git init .
72 fossil export --git --export-marks ../repo/fossil.marks \
73 ../repo.fossil | git fast-import \
74 --export-marks=../repo/git.marks
75 </pre></blockquote>
76
77 Once the import has completed, the user would need to <tt>git checkout
78 trunk</tt>. At any point after this, new changes can be imported from the
79 remote Fossil repository:
80
81 <blockquote><pre>
82 cd ../repo
83 fossil pull
84 cd ../repo.git
85 fossil export --git --import-marks ../repo/fossil.marks \
86 --export-marks ../repo/fossil.marks \
87 ../repo.fossil | git fast-import \
88 --import-marks=../repo/git.marks \
89 --export-marks=../repo/git.marks
90 </pre></blockquote>
91
92 Changes in the Git repository can be exported to the Fossil repository and then
93 pushed to the remote:
94
95 <blockquote><pre>
96 git fast-export --import-marks=../repo/git.marks \
97 --export-marks=../repo/git.marks --all | fossil import --git \
98 --incremental --import-marks ../repo/fossil.marks \
99 --export-marks ../repo/fossil.marks ../repo.fossil
100 cd ../repo
101 fossil push
102 </pre></blockquote>
103

Keyboard Shortcuts

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