Fossil SCM
Add the --import-marks and --export-marks options to "fossil import" when the --git option is used.
Commit
b3acfa2e4acd84bf9fac3017e2bdb4b17115eabd
Parent
41c2220934de8cb…
2 files changed
+237
-25
+50
-2
+237
-25
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -19,10 +19,28 @@ | ||
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "export.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | +#if INTERFACE | |
| 25 | +/* | |
| 26 | +** struct mark_t | |
| 27 | +** holds information for translating between git commits | |
| 28 | +** and fossil commits. | |
| 29 | +** -git_name: This is the mark name that identifies the commit to git. | |
| 30 | +** It will always begin with a ':'. | |
| 31 | +** -rid: The unique object ID that identifies this commit within the | |
| 32 | +** repository database. | |
| 33 | +** -uuid: The SHA-1 of artifact corresponding to rid. | |
| 34 | +*/ | |
| 35 | +struct mark_t{ | |
| 36 | + char *name; | |
| 37 | + int rid; | |
| 38 | + char uuid[41]; | |
| 39 | +}; | |
| 40 | +#endif | |
| 41 | + | |
| 24 | 42 | /* |
| 25 | 43 | ** Output a "committer" record for the given user. |
| 26 | 44 | */ |
| 27 | 45 | static void print_person(const char *zUser){ |
| 28 | 46 | static Stmt q; |
| @@ -96,10 +114,199 @@ | ||
| 96 | 114 | db_reset(&q); |
| 97 | 115 | } |
| 98 | 116 | |
| 99 | 117 | #define BLOBMARK(rid) ((rid) * 2) |
| 100 | 118 | #define COMMITMARK(rid) ((rid) * 2 + 1) |
| 119 | + | |
| 120 | +/* | |
| 121 | +** insert_commit_xref() | |
| 122 | +** Insert a new (mark,rid,uuid) entry into the 'xmark' table. | |
| 123 | +** zName and zUuid must be non-null and must point to NULL-terminated strings. | |
| 124 | +*/ | |
| 125 | +void insert_commit_xref(int rid, const char *zName, const char *zUuid){ | |
| 126 | + db_multi_exec( | |
| 127 | + "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" | |
| 128 | + "VALUES(%Q,%d,%Q)", | |
| 129 | + zName, rid, zUuid | |
| 130 | + ); | |
| 131 | +} | |
| 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 | + sprintf(sid, ":%d", COMMITMARK(rid)); | |
| 150 | + mark->name = fossil_strdup(sid); | |
| 151 | + strcpy(mark->uuid, zUuid); | |
| 152 | + free(zUuid); | |
| 153 | + insert_commit_xref(mark->rid, mark->name, mark->uuid); | |
| 154 | + return 0; | |
| 155 | +} | |
| 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 | + } | |
| 175 | + return zMark; | |
| 176 | +} | |
| 177 | + | |
| 178 | +/* | |
| 179 | +** parse_mark() | |
| 180 | +** Create a new (mark,rid,uuid) entry in the 'xmark' table given a line | |
| 181 | +** from a marks file. Return the cross-ref information as a struct mark_t | |
| 182 | +** in *mark. | |
| 183 | +** This function returns -1 in the case that the line is blank, malformed, or | |
| 184 | +** the rid/uuid named in 'line' does not match what is in the repository | |
| 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 | + char type; | |
| 191 | + cur_tok = strtok(line, " \t"); | |
| 192 | + if(!cur_tok||strlen(cur_tok)<2){ | |
| 193 | + return -1; | |
| 194 | + } | |
| 195 | + mark->rid = atoi(&cur_tok[1]); | |
| 196 | + if(cur_tok[0]!='c'){ | |
| 197 | + /* This is probably a blob mark */ | |
| 198 | + mark->name = NULL; | |
| 199 | + return 0; | |
| 200 | + } | |
| 201 | + | |
| 202 | + cur_tok = strtok(NULL, " \t"); | |
| 203 | + if(!cur_tok){ | |
| 204 | + /* This mark was generated by an older version of Fossil and doesn't | |
| 205 | + ** include the mark name and uuid. create_mark() will name the new mark | |
| 206 | + ** exactly as it was when exported to git, so that we should have a | |
| 207 | + ** valid mapping from git sha1<->mark name<->fossil sha1. */ | |
| 208 | + return create_mark(mark->rid, mark); | |
| 209 | + }else{ | |
| 210 | + mark->name = fossil_strdup(cur_tok); | |
| 211 | + } | |
| 212 | + | |
| 213 | + cur_tok = strtok(NULL, "\n"); | |
| 214 | + if(!cur_tok||strlen(cur_tok)!=40){ | |
| 215 | + free(mark->name); | |
| 216 | + fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok); | |
| 217 | + return -1; | |
| 218 | + }else{ | |
| 219 | + strcpy(mark->uuid, cur_tok); | |
| 220 | + } | |
| 221 | + | |
| 222 | + /* make sure that rid corresponds to UUID */ | |
| 223 | + if(fast_uuid_to_rid(mark->uuid)!=mark->rid){ | |
| 224 | + free(mark->name); | |
| 225 | + fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid); | |
| 226 | + return -1; | |
| 227 | + } | |
| 228 | + | |
| 229 | + /* insert a cross-ref into the 'xmark' table */ | |
| 230 | + insert_commit_xref(mark->rid, mark->name, mark->uuid); | |
| 231 | + return 0; | |
| 232 | +} | |
| 233 | + | |
| 234 | +/* | |
| 235 | +** import_marks() | |
| 236 | +** Import the marks specified in file 'f' into the 'xmark' table. | |
| 237 | +** If 'blobs' is non-null, insert all blob marks into it. | |
| 238 | +** If 'vers' is non-null, insert all commit marks into it. | |
| 239 | +** Each line in the file must be at most 100 characters in length. This | |
| 240 | +** seems like a reasonable maximum for a 40-character uuid, and 1-13 | |
| 241 | +** character rid. | |
| 242 | +** The function returns -1 if any of the lines in file 'f' are malformed, | |
| 243 | +** or the rid/uuid information doesn't match what is in the repository | |
| 244 | +** database. Otherwise, 0 is returned. | |
| 245 | +*/ | |
| 246 | +int import_marks(FILE* f, Bag *blobs, Bag *vers){ | |
| 247 | + char line[101]; | |
| 248 | + size_t len; | |
| 249 | + while(fgets(line, sizeof(line), f)){ | |
| 250 | + struct mark_t mark; | |
| 251 | + if(strlen(line)==100&&line[99]!='\n'){ | |
| 252 | + /* line too long */ | |
| 253 | + return -1; | |
| 254 | + } | |
| 255 | + if( parse_mark(line, &mark)<0 ){ | |
| 256 | + return -1; | |
| 257 | + }else if( line[0]=='b' ){ | |
| 258 | + /* Don't import blob marks into 'xmark' table--git doesn't use them, | |
| 259 | + ** so they need to be left free for git to reuse. */ | |
| 260 | + if(blobs!=NULL){ | |
| 261 | + bag_insert(blobs, mark.rid); | |
| 262 | + } | |
| 263 | + }else if( vers!=NULL ){ | |
| 264 | + bag_insert(vers, mark.rid); | |
| 265 | + } | |
| 266 | + free(mark.name); | |
| 267 | + } | |
| 268 | + return 0; | |
| 269 | +} | |
| 270 | + | |
| 271 | +/* | |
| 272 | +** If 'blobs' is non-null, it must point to a Bag of blob rids to be | |
| 273 | +** written to disk. Blob rids are written as 'b<rid>'. | |
| 274 | +** If 'vers' is non-null, it must point to a Bag of commit rids to be | |
| 275 | +** written to disk. Commit rids are written as 'c<rid> :<mark> <uuid>'. | |
| 276 | +** All commit (mark,rid,uuid) tuples are stored in 'xmark' table. | |
| 277 | +** This function does not fail, but may produce errors if a uuid cannot | |
| 278 | +** be found for an rid in 'vers'. | |
| 279 | +*/ | |
| 280 | +void export_marks(FILE* f, Bag *blobs, Bag *vers){ | |
| 281 | + int rid; | |
| 282 | + if( blobs!=NULL ){ | |
| 283 | + rid = bag_first(blobs); | |
| 284 | + if(rid!=0){ | |
| 285 | + do{ | |
| 286 | + fprintf(f, "b%d\n", rid); | |
| 287 | + }while((rid = bag_next(blobs, rid))!=0); | |
| 288 | + } | |
| 289 | + } | |
| 290 | + if( vers!=NULL ){ | |
| 291 | + rid = bag_first(vers); | |
| 292 | + if( rid!=0 ){ | |
| 293 | + do{ | |
| 294 | + char *zUuid = rid_to_uuid(rid); | |
| 295 | + char *zMark; | |
| 296 | + if(zUuid==NULL){ | |
| 297 | + fossil_trace("No uuid matching rid=%d when exporting marks\n", rid); | |
| 298 | + continue; | |
| 299 | + } | |
| 300 | + zMark = mark_name_from_rid(rid); | |
| 301 | + fprintf(f, "c%d %s %s\n", rid, zMark, zUuid); | |
| 302 | + free(zMark); | |
| 303 | + free(zUuid); | |
| 304 | + }while( (rid = bag_next(vers, rid))!=0 ); | |
| 305 | + } | |
| 306 | + } | |
| 307 | +} | |
| 101 | 308 | |
| 102 | 309 | /* |
| 103 | 310 | ** COMMAND: export |
| 104 | 311 | ** |
| 105 | 312 | ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
| @@ -147,35 +354,41 @@ | ||
| 147 | 354 | verify_all_options(); |
| 148 | 355 | if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); } |
| 149 | 356 | |
| 150 | 357 | db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)"); |
| 151 | 358 | db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)"); |
| 359 | + db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT)"); | |
| 152 | 360 | if( markfile_in!=0 ){ |
| 153 | 361 | Stmt qb,qc; |
| 154 | 362 | char line[100]; |
| 155 | 363 | FILE *f; |
| 364 | + int rid; | |
| 156 | 365 | |
| 157 | 366 | f = fossil_fopen(markfile_in, "r"); |
| 158 | 367 | if( f==0 ){ |
| 159 | 368 | fossil_fatal("cannot open %s for reading", markfile_in); |
| 160 | 369 | } |
| 370 | + if(import_marks(f, &blobs, &vers)<0){ | |
| 371 | + fossil_fatal("error importing marks from file: %s\n", markfile_in); | |
| 372 | + } | |
| 161 | 373 | db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); |
| 162 | 374 | db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); |
| 163 | - while( fgets(line, sizeof(line), f)!=0 ){ | |
| 164 | - if( *line == 'b' ){ | |
| 165 | - db_bind_text(&qb, ":rid", line + 1); | |
| 375 | + rid = bag_first(&blobs); | |
| 376 | + if(rid!=0){ | |
| 377 | + do{ | |
| 378 | + db_bind_int(&qb, ":rid", rid); | |
| 166 | 379 | db_step(&qb); |
| 167 | 380 | db_reset(&qb); |
| 168 | - bag_insert(&blobs, atoi(line + 1)); | |
| 169 | - }else if( *line == 'c' ){ | |
| 170 | - db_bind_text(&qc, ":rid", line + 1); | |
| 381 | + }while((rid = bag_next(&blobs, rid))!=0); | |
| 382 | + } | |
| 383 | + rid = bag_first(&vers); | |
| 384 | + if(rid!=0){ | |
| 385 | + do{ | |
| 386 | + db_bind_int(&qc, ":rid", rid); | |
| 171 | 387 | db_step(&qc); |
| 172 | 388 | db_reset(&qc); |
| 173 | - bag_insert(&vers, atoi(line + 1)); | |
| 174 | - }else{ | |
| 175 | - fossil_fatal("bad input from %s: %s", markfile_in, line); | |
| 176 | - } | |
| 389 | + }while((rid = bag_next(&vers, rid))!=0); | |
| 177 | 390 | } |
| 178 | 391 | db_finalize(&qb); |
| 179 | 392 | db_finalize(&qc); |
| 180 | 393 | fclose(f); |
| 181 | 394 | } |
| @@ -249,10 +462,11 @@ | ||
| 249 | 462 | int ckinId = db_column_int(&q, 1); |
| 250 | 463 | const char *zComment = db_column_text(&q, 2); |
| 251 | 464 | const char *zUser = db_column_text(&q, 3); |
| 252 | 465 | const char *zBranch = db_column_text(&q, 4); |
| 253 | 466 | char *zBr; |
| 467 | + char *zMark; | |
| 254 | 468 | |
| 255 | 469 | bag_insert(&vers, ckinId); |
| 256 | 470 | db_bind_int(&q2, ":rid", ckinId); |
| 257 | 471 | db_step(&q2); |
| 258 | 472 | db_reset(&q2); |
| @@ -259,11 +473,13 @@ | ||
| 259 | 473 | if( zBranch==0 ) zBranch = "trunk"; |
| 260 | 474 | zBr = mprintf("%s", zBranch); |
| 261 | 475 | for(i=0; zBr[i]; i++){ |
| 262 | 476 | if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_'; |
| 263 | 477 | } |
| 264 | - printf("commit refs/heads/%s\nmark :%d\n", zBr, COMMITMARK(ckinId)); | |
| 478 | + zMark = mark_name_from_rid(ckinId); | |
| 479 | + printf("commit refs/heads/%s\nmark %s\n", zBr, zMark); | |
| 480 | + free(zMark); | |
| 265 | 481 | free(zBr); |
| 266 | 482 | printf("committer"); |
| 267 | 483 | print_person(zUser); |
| 268 | 484 | printf(" %s +0000\n", zSecondsSince1970); |
| 269 | 485 | if( zComment==0 ) zComment = "null comment"; |
| @@ -273,19 +489,24 @@ | ||
| 273 | 489 | " WHERE cid=%d AND isprim" |
| 274 | 490 | " AND pid IN (SELECT objid FROM event)", |
| 275 | 491 | ckinId |
| 276 | 492 | ); |
| 277 | 493 | if( db_step(&q3) == SQLITE_ROW ){ |
| 278 | - printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0))); | |
| 494 | + int pid = db_column_int(&q3, 0); | |
| 495 | + zMark = mark_name_from_rid(pid); | |
| 496 | + printf("from %s\n", zMark); | |
| 497 | + free(zMark); | |
| 279 | 498 | db_prepare(&q4, |
| 280 | 499 | "SELECT pid FROM plink" |
| 281 | 500 | " WHERE cid=%d AND NOT isprim" |
| 282 | 501 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" |
| 283 | 502 | " ORDER BY pid", |
| 284 | 503 | ckinId); |
| 285 | 504 | while( db_step(&q4)==SQLITE_ROW ){ |
| 286 | - printf("merge :%d\n", COMMITMARK(db_column_int(&q4,0))); | |
| 505 | + zMark = mark_name_from_rid(db_column_int(&q4, 0)); | |
| 506 | + printf("merge %s\n", zMark); | |
| 507 | + free(zMark); | |
| 287 | 508 | } |
| 288 | 509 | db_finalize(&q4); |
| 289 | 510 | }else{ |
| 290 | 511 | printf("deleteall\n"); |
| 291 | 512 | } |
| @@ -316,11 +537,10 @@ | ||
| 316 | 537 | db_finalize(&q3); |
| 317 | 538 | printf("\n"); |
| 318 | 539 | } |
| 319 | 540 | db_finalize(&q2); |
| 320 | 541 | db_finalize(&q); |
| 321 | - bag_clear(&blobs); | |
| 322 | 542 | manifest_cache_clear(); |
| 323 | 543 | |
| 324 | 544 | |
| 325 | 545 | /* Output tags */ |
| 326 | 546 | db_prepare(&q, |
| @@ -345,28 +565,20 @@ | ||
| 345 | 565 | printf("tagger <tagger> %s +0000\n", zSecSince1970); |
| 346 | 566 | printf("data 0\n"); |
| 347 | 567 | fossil_free(zEncoded); |
| 348 | 568 | } |
| 349 | 569 | db_finalize(&q); |
| 350 | - bag_clear(&vers); | |
| 351 | 570 | |
| 352 | 571 | if( markfile_out!=0 ){ |
| 353 | 572 | FILE *f; |
| 354 | 573 | f = fossil_fopen(markfile_out, "w"); |
| 355 | 574 | if( f == 0 ){ |
| 356 | 575 | fossil_fatal("cannot open %s for writing", markfile_out); |
| 357 | 576 | } |
| 358 | - db_prepare(&q, "SELECT rid FROM oldblob"); | |
| 359 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 360 | - fprintf(f, "b%d\n", db_column_int(&q, 0)); | |
| 361 | - } | |
| 362 | - db_finalize(&q); | |
| 363 | - db_prepare(&q, "SELECT rid FROM oldcommit"); | |
| 364 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 365 | - fprintf(f, "c%d\n", db_column_int(&q, 0)); | |
| 366 | - } | |
| 367 | - db_finalize(&q); | |
| 577 | + export_marks(f, &blobs, &vers); | |
| 368 | 578 | if( ferror(f)!=0 || fclose(f)!=0 ) { |
| 369 | 579 | fossil_fatal("error while writing %s", markfile_out); |
| 370 | 580 | } |
| 371 | 581 | } |
| 582 | + bag_clear(&blobs); | |
| 583 | + bag_clear(&vers); | |
| 372 | 584 | } |
| 373 | 585 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -19,10 +19,28 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "export.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | /* |
| 25 | ** Output a "committer" record for the given user. |
| 26 | */ |
| 27 | static void print_person(const char *zUser){ |
| 28 | static Stmt q; |
| @@ -96,10 +114,199 @@ | |
| 96 | db_reset(&q); |
| 97 | } |
| 98 | |
| 99 | #define BLOBMARK(rid) ((rid) * 2) |
| 100 | #define COMMITMARK(rid) ((rid) * 2 + 1) |
| 101 | |
| 102 | /* |
| 103 | ** COMMAND: export |
| 104 | ** |
| 105 | ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
| @@ -147,35 +354,41 @@ | |
| 147 | verify_all_options(); |
| 148 | if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); } |
| 149 | |
| 150 | db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)"); |
| 151 | db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)"); |
| 152 | if( markfile_in!=0 ){ |
| 153 | Stmt qb,qc; |
| 154 | char line[100]; |
| 155 | FILE *f; |
| 156 | |
| 157 | f = fossil_fopen(markfile_in, "r"); |
| 158 | if( f==0 ){ |
| 159 | fossil_fatal("cannot open %s for reading", markfile_in); |
| 160 | } |
| 161 | db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); |
| 162 | db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); |
| 163 | while( fgets(line, sizeof(line), f)!=0 ){ |
| 164 | if( *line == 'b' ){ |
| 165 | db_bind_text(&qb, ":rid", line + 1); |
| 166 | db_step(&qb); |
| 167 | db_reset(&qb); |
| 168 | bag_insert(&blobs, atoi(line + 1)); |
| 169 | }else if( *line == 'c' ){ |
| 170 | db_bind_text(&qc, ":rid", line + 1); |
| 171 | db_step(&qc); |
| 172 | db_reset(&qc); |
| 173 | bag_insert(&vers, atoi(line + 1)); |
| 174 | }else{ |
| 175 | fossil_fatal("bad input from %s: %s", markfile_in, line); |
| 176 | } |
| 177 | } |
| 178 | db_finalize(&qb); |
| 179 | db_finalize(&qc); |
| 180 | fclose(f); |
| 181 | } |
| @@ -249,10 +462,11 @@ | |
| 249 | int ckinId = db_column_int(&q, 1); |
| 250 | const char *zComment = db_column_text(&q, 2); |
| 251 | const char *zUser = db_column_text(&q, 3); |
| 252 | const char *zBranch = db_column_text(&q, 4); |
| 253 | char *zBr; |
| 254 | |
| 255 | bag_insert(&vers, ckinId); |
| 256 | db_bind_int(&q2, ":rid", ckinId); |
| 257 | db_step(&q2); |
| 258 | db_reset(&q2); |
| @@ -259,11 +473,13 @@ | |
| 259 | if( zBranch==0 ) zBranch = "trunk"; |
| 260 | zBr = mprintf("%s", zBranch); |
| 261 | for(i=0; zBr[i]; i++){ |
| 262 | if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_'; |
| 263 | } |
| 264 | printf("commit refs/heads/%s\nmark :%d\n", zBr, COMMITMARK(ckinId)); |
| 265 | free(zBr); |
| 266 | printf("committer"); |
| 267 | print_person(zUser); |
| 268 | printf(" %s +0000\n", zSecondsSince1970); |
| 269 | if( zComment==0 ) zComment = "null comment"; |
| @@ -273,19 +489,24 @@ | |
| 273 | " WHERE cid=%d AND isprim" |
| 274 | " AND pid IN (SELECT objid FROM event)", |
| 275 | ckinId |
| 276 | ); |
| 277 | if( db_step(&q3) == SQLITE_ROW ){ |
| 278 | printf("from :%d\n", COMMITMARK(db_column_int(&q3, 0))); |
| 279 | db_prepare(&q4, |
| 280 | "SELECT pid FROM plink" |
| 281 | " WHERE cid=%d AND NOT isprim" |
| 282 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" |
| 283 | " ORDER BY pid", |
| 284 | ckinId); |
| 285 | while( db_step(&q4)==SQLITE_ROW ){ |
| 286 | printf("merge :%d\n", COMMITMARK(db_column_int(&q4,0))); |
| 287 | } |
| 288 | db_finalize(&q4); |
| 289 | }else{ |
| 290 | printf("deleteall\n"); |
| 291 | } |
| @@ -316,11 +537,10 @@ | |
| 316 | db_finalize(&q3); |
| 317 | printf("\n"); |
| 318 | } |
| 319 | db_finalize(&q2); |
| 320 | db_finalize(&q); |
| 321 | bag_clear(&blobs); |
| 322 | manifest_cache_clear(); |
| 323 | |
| 324 | |
| 325 | /* Output tags */ |
| 326 | db_prepare(&q, |
| @@ -345,28 +565,20 @@ | |
| 345 | printf("tagger <tagger> %s +0000\n", zSecSince1970); |
| 346 | printf("data 0\n"); |
| 347 | fossil_free(zEncoded); |
| 348 | } |
| 349 | db_finalize(&q); |
| 350 | bag_clear(&vers); |
| 351 | |
| 352 | if( markfile_out!=0 ){ |
| 353 | FILE *f; |
| 354 | f = fossil_fopen(markfile_out, "w"); |
| 355 | if( f == 0 ){ |
| 356 | fossil_fatal("cannot open %s for writing", markfile_out); |
| 357 | } |
| 358 | db_prepare(&q, "SELECT rid FROM oldblob"); |
| 359 | while( db_step(&q)==SQLITE_ROW ){ |
| 360 | fprintf(f, "b%d\n", db_column_int(&q, 0)); |
| 361 | } |
| 362 | db_finalize(&q); |
| 363 | db_prepare(&q, "SELECT rid FROM oldcommit"); |
| 364 | while( db_step(&q)==SQLITE_ROW ){ |
| 365 | fprintf(f, "c%d\n", db_column_int(&q, 0)); |
| 366 | } |
| 367 | db_finalize(&q); |
| 368 | if( ferror(f)!=0 || fclose(f)!=0 ) { |
| 369 | fossil_fatal("error while writing %s", markfile_out); |
| 370 | } |
| 371 | } |
| 372 | } |
| 373 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -19,10 +19,28 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "export.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | #if INTERFACE |
| 25 | /* |
| 26 | ** struct mark_t |
| 27 | ** holds information for translating between git commits |
| 28 | ** and fossil commits. |
| 29 | ** -git_name: This is the mark name that identifies the commit to git. |
| 30 | ** It will always begin with a ':'. |
| 31 | ** -rid: The unique object ID that identifies this commit within the |
| 32 | ** repository database. |
| 33 | ** -uuid: The SHA-1 of artifact corresponding to rid. |
| 34 | */ |
| 35 | struct mark_t{ |
| 36 | char *name; |
| 37 | int rid; |
| 38 | char uuid[41]; |
| 39 | }; |
| 40 | #endif |
| 41 | |
| 42 | /* |
| 43 | ** Output a "committer" record for the given user. |
| 44 | */ |
| 45 | static void print_person(const char *zUser){ |
| 46 | static Stmt q; |
| @@ -96,10 +114,199 @@ | |
| 114 | db_reset(&q); |
| 115 | } |
| 116 | |
| 117 | #define BLOBMARK(rid) ((rid) * 2) |
| 118 | #define COMMITMARK(rid) ((rid) * 2 + 1) |
| 119 | |
| 120 | /* |
| 121 | ** insert_commit_xref() |
| 122 | ** Insert a new (mark,rid,uuid) entry into the 'xmark' table. |
| 123 | ** zName and zUuid must be non-null and must point to NULL-terminated strings. |
| 124 | */ |
| 125 | void insert_commit_xref(int rid, const char *zName, const char *zUuid){ |
| 126 | db_multi_exec( |
| 127 | "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" |
| 128 | "VALUES(%Q,%d,%Q)", |
| 129 | zName, rid, zUuid |
| 130 | ); |
| 131 | } |
| 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 | sprintf(sid, ":%d", COMMITMARK(rid)); |
| 150 | mark->name = fossil_strdup(sid); |
| 151 | strcpy(mark->uuid, zUuid); |
| 152 | free(zUuid); |
| 153 | insert_commit_xref(mark->rid, mark->name, mark->uuid); |
| 154 | return 0; |
| 155 | } |
| 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 | } |
| 175 | return zMark; |
| 176 | } |
| 177 | |
| 178 | /* |
| 179 | ** parse_mark() |
| 180 | ** Create a new (mark,rid,uuid) entry in the 'xmark' table given a line |
| 181 | ** from a marks file. Return the cross-ref information as a struct mark_t |
| 182 | ** in *mark. |
| 183 | ** This function returns -1 in the case that the line is blank, malformed, or |
| 184 | ** the rid/uuid named in 'line' does not match what is in the repository |
| 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 | char type; |
| 191 | cur_tok = strtok(line, " \t"); |
| 192 | if(!cur_tok||strlen(cur_tok)<2){ |
| 193 | return -1; |
| 194 | } |
| 195 | mark->rid = atoi(&cur_tok[1]); |
| 196 | if(cur_tok[0]!='c'){ |
| 197 | /* This is probably a blob mark */ |
| 198 | mark->name = NULL; |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | cur_tok = strtok(NULL, " \t"); |
| 203 | if(!cur_tok){ |
| 204 | /* This mark was generated by an older version of Fossil and doesn't |
| 205 | ** include the mark name and uuid. create_mark() will name the new mark |
| 206 | ** exactly as it was when exported to git, so that we should have a |
| 207 | ** valid mapping from git sha1<->mark name<->fossil sha1. */ |
| 208 | return create_mark(mark->rid, mark); |
| 209 | }else{ |
| 210 | mark->name = fossil_strdup(cur_tok); |
| 211 | } |
| 212 | |
| 213 | cur_tok = strtok(NULL, "\n"); |
| 214 | if(!cur_tok||strlen(cur_tok)!=40){ |
| 215 | free(mark->name); |
| 216 | fossil_trace("Invalid SHA-1 in marks file: %s\n", cur_tok); |
| 217 | return -1; |
| 218 | }else{ |
| 219 | strcpy(mark->uuid, cur_tok); |
| 220 | } |
| 221 | |
| 222 | /* make sure that rid corresponds to UUID */ |
| 223 | if(fast_uuid_to_rid(mark->uuid)!=mark->rid){ |
| 224 | free(mark->name); |
| 225 | fossil_trace("Non-existent SHA-1 in marks file: %s\n", mark->uuid); |
| 226 | return -1; |
| 227 | } |
| 228 | |
| 229 | /* insert a cross-ref into the 'xmark' table */ |
| 230 | insert_commit_xref(mark->rid, mark->name, mark->uuid); |
| 231 | return 0; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | ** import_marks() |
| 236 | ** Import the marks specified in file 'f' into the 'xmark' table. |
| 237 | ** If 'blobs' is non-null, insert all blob marks into it. |
| 238 | ** If 'vers' is non-null, insert all commit marks into it. |
| 239 | ** Each line in the file must be at most 100 characters in length. This |
| 240 | ** seems like a reasonable maximum for a 40-character uuid, and 1-13 |
| 241 | ** character rid. |
| 242 | ** The function returns -1 if any of the lines in file 'f' are malformed, |
| 243 | ** or the rid/uuid information doesn't match what is in the repository |
| 244 | ** database. Otherwise, 0 is returned. |
| 245 | */ |
| 246 | int import_marks(FILE* f, Bag *blobs, Bag *vers){ |
| 247 | char line[101]; |
| 248 | size_t len; |
| 249 | while(fgets(line, sizeof(line), f)){ |
| 250 | struct mark_t mark; |
| 251 | if(strlen(line)==100&&line[99]!='\n'){ |
| 252 | /* line too long */ |
| 253 | return -1; |
| 254 | } |
| 255 | if( parse_mark(line, &mark)<0 ){ |
| 256 | return -1; |
| 257 | }else if( line[0]=='b' ){ |
| 258 | /* Don't import blob marks into 'xmark' table--git doesn't use them, |
| 259 | ** so they need to be left free for git to reuse. */ |
| 260 | if(blobs!=NULL){ |
| 261 | bag_insert(blobs, mark.rid); |
| 262 | } |
| 263 | }else if( vers!=NULL ){ |
| 264 | bag_insert(vers, mark.rid); |
| 265 | } |
| 266 | free(mark.name); |
| 267 | } |
| 268 | return 0; |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | ** If 'blobs' is non-null, it must point to a Bag of blob rids to be |
| 273 | ** written to disk. Blob rids are written as 'b<rid>'. |
| 274 | ** If 'vers' is non-null, it must point to a Bag of commit rids to be |
| 275 | ** written to disk. Commit rids are written as 'c<rid> :<mark> <uuid>'. |
| 276 | ** All commit (mark,rid,uuid) tuples are stored in 'xmark' table. |
| 277 | ** This function does not fail, but may produce errors if a uuid cannot |
| 278 | ** be found for an rid in 'vers'. |
| 279 | */ |
| 280 | void export_marks(FILE* f, Bag *blobs, Bag *vers){ |
| 281 | int rid; |
| 282 | if( blobs!=NULL ){ |
| 283 | rid = bag_first(blobs); |
| 284 | if(rid!=0){ |
| 285 | do{ |
| 286 | fprintf(f, "b%d\n", rid); |
| 287 | }while((rid = bag_next(blobs, rid))!=0); |
| 288 | } |
| 289 | } |
| 290 | if( vers!=NULL ){ |
| 291 | rid = bag_first(vers); |
| 292 | if( rid!=0 ){ |
| 293 | do{ |
| 294 | char *zUuid = rid_to_uuid(rid); |
| 295 | char *zMark; |
| 296 | if(zUuid==NULL){ |
| 297 | fossil_trace("No uuid matching rid=%d when exporting marks\n", rid); |
| 298 | continue; |
| 299 | } |
| 300 | zMark = mark_name_from_rid(rid); |
| 301 | fprintf(f, "c%d %s %s\n", rid, zMark, zUuid); |
| 302 | free(zMark); |
| 303 | free(zUuid); |
| 304 | }while( (rid = bag_next(vers, rid))!=0 ); |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | /* |
| 310 | ** COMMAND: export |
| 311 | ** |
| 312 | ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
| @@ -147,35 +354,41 @@ | |
| 354 | verify_all_options(); |
| 355 | if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); } |
| 356 | |
| 357 | db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)"); |
| 358 | db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)"); |
| 359 | db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT)"); |
| 360 | if( markfile_in!=0 ){ |
| 361 | Stmt qb,qc; |
| 362 | char line[100]; |
| 363 | FILE *f; |
| 364 | int rid; |
| 365 | |
| 366 | f = fossil_fopen(markfile_in, "r"); |
| 367 | if( f==0 ){ |
| 368 | fossil_fatal("cannot open %s for reading", markfile_in); |
| 369 | } |
| 370 | if(import_marks(f, &blobs, &vers)<0){ |
| 371 | fossil_fatal("error importing marks from file: %s\n", markfile_in); |
| 372 | } |
| 373 | db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); |
| 374 | db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); |
| 375 | rid = bag_first(&blobs); |
| 376 | if(rid!=0){ |
| 377 | do{ |
| 378 | db_bind_int(&qb, ":rid", rid); |
| 379 | db_step(&qb); |
| 380 | db_reset(&qb); |
| 381 | }while((rid = bag_next(&blobs, rid))!=0); |
| 382 | } |
| 383 | rid = bag_first(&vers); |
| 384 | if(rid!=0){ |
| 385 | do{ |
| 386 | db_bind_int(&qc, ":rid", rid); |
| 387 | db_step(&qc); |
| 388 | db_reset(&qc); |
| 389 | }while((rid = bag_next(&vers, rid))!=0); |
| 390 | } |
| 391 | db_finalize(&qb); |
| 392 | db_finalize(&qc); |
| 393 | fclose(f); |
| 394 | } |
| @@ -249,10 +462,11 @@ | |
| 462 | int ckinId = db_column_int(&q, 1); |
| 463 | const char *zComment = db_column_text(&q, 2); |
| 464 | const char *zUser = db_column_text(&q, 3); |
| 465 | const char *zBranch = db_column_text(&q, 4); |
| 466 | char *zBr; |
| 467 | char *zMark; |
| 468 | |
| 469 | bag_insert(&vers, ckinId); |
| 470 | db_bind_int(&q2, ":rid", ckinId); |
| 471 | db_step(&q2); |
| 472 | db_reset(&q2); |
| @@ -259,11 +473,13 @@ | |
| 473 | if( zBranch==0 ) zBranch = "trunk"; |
| 474 | zBr = mprintf("%s", zBranch); |
| 475 | for(i=0; zBr[i]; i++){ |
| 476 | if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_'; |
| 477 | } |
| 478 | zMark = mark_name_from_rid(ckinId); |
| 479 | printf("commit refs/heads/%s\nmark %s\n", zBr, zMark); |
| 480 | free(zMark); |
| 481 | free(zBr); |
| 482 | printf("committer"); |
| 483 | print_person(zUser); |
| 484 | printf(" %s +0000\n", zSecondsSince1970); |
| 485 | if( zComment==0 ) zComment = "null comment"; |
| @@ -273,19 +489,24 @@ | |
| 489 | " WHERE cid=%d AND isprim" |
| 490 | " AND pid IN (SELECT objid FROM event)", |
| 491 | ckinId |
| 492 | ); |
| 493 | if( db_step(&q3) == SQLITE_ROW ){ |
| 494 | int pid = db_column_int(&q3, 0); |
| 495 | zMark = mark_name_from_rid(pid); |
| 496 | printf("from %s\n", zMark); |
| 497 | free(zMark); |
| 498 | db_prepare(&q4, |
| 499 | "SELECT pid FROM plink" |
| 500 | " WHERE cid=%d AND NOT isprim" |
| 501 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" |
| 502 | " ORDER BY pid", |
| 503 | ckinId); |
| 504 | while( db_step(&q4)==SQLITE_ROW ){ |
| 505 | zMark = mark_name_from_rid(db_column_int(&q4, 0)); |
| 506 | printf("merge %s\n", zMark); |
| 507 | free(zMark); |
| 508 | } |
| 509 | db_finalize(&q4); |
| 510 | }else{ |
| 511 | printf("deleteall\n"); |
| 512 | } |
| @@ -316,11 +537,10 @@ | |
| 537 | db_finalize(&q3); |
| 538 | printf("\n"); |
| 539 | } |
| 540 | db_finalize(&q2); |
| 541 | db_finalize(&q); |
| 542 | manifest_cache_clear(); |
| 543 | |
| 544 | |
| 545 | /* Output tags */ |
| 546 | db_prepare(&q, |
| @@ -345,28 +565,20 @@ | |
| 565 | printf("tagger <tagger> %s +0000\n", zSecSince1970); |
| 566 | printf("data 0\n"); |
| 567 | fossil_free(zEncoded); |
| 568 | } |
| 569 | db_finalize(&q); |
| 570 | |
| 571 | if( markfile_out!=0 ){ |
| 572 | FILE *f; |
| 573 | f = fossil_fopen(markfile_out, "w"); |
| 574 | if( f == 0 ){ |
| 575 | fossil_fatal("cannot open %s for writing", markfile_out); |
| 576 | } |
| 577 | export_marks(f, &blobs, &vers); |
| 578 | if( ferror(f)!=0 || fclose(f)!=0 ) { |
| 579 | fossil_fatal("error while writing %s", markfile_out); |
| 580 | } |
| 581 | } |
| 582 | bag_clear(&blobs); |
| 583 | bag_clear(&vers); |
| 584 | } |
| 585 |
+50
-2
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -1494,10 +1494,13 @@ | ||
| 1494 | 1494 | ** data is read from standard input. |
| 1495 | 1495 | ** |
| 1496 | 1496 | ** The following formats are currently understood by this command |
| 1497 | 1497 | ** |
| 1498 | 1498 | ** --git Import from the git-fast-export file format (default) |
| 1499 | +** Options: | |
| 1500 | +** --import-marks FILE Restore marks table from FILE | |
| 1501 | +** --export-marks FILE Save marks table to FILE | |
| 1499 | 1502 | ** |
| 1500 | 1503 | ** --svn Import from the svnadmin-dump file format. The default |
| 1501 | 1504 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1502 | 1505 | ** folders in the SVN root as special, following the |
| 1503 | 1506 | ** common layout of SVN repositories. These are (by |
| @@ -1525,19 +1528,24 @@ | ||
| 1525 | 1528 | char *zPassword; |
| 1526 | 1529 | FILE *pIn; |
| 1527 | 1530 | Stmt q; |
| 1528 | 1531 | int forceFlag = find_option("force", "f", 0)!=0; |
| 1529 | 1532 | int svnFlag = find_option("svn", 0, 0)!=0; |
| 1533 | + int gitFlag = find_option("git", 0, 0)!=0; | |
| 1530 | 1534 | int omitRebuild = find_option("no-rebuild",0,0)!=0; |
| 1531 | 1535 | int omitVacuum = find_option("no-vacuum",0,0)!=0; |
| 1532 | 1536 | |
| 1533 | 1537 | /* Options common to all input formats */ |
| 1534 | 1538 | int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1535 | 1539 | |
| 1536 | 1540 | /* Options for --svn only */ |
| 1537 | 1541 | const char *zBase=""; |
| 1538 | 1542 | int flatFlag=0; |
| 1543 | + | |
| 1544 | + /* Options for --git only */ | |
| 1545 | + const char *markfile_in; | |
| 1546 | + const char *markfile_out; | |
| 1539 | 1547 | |
| 1540 | 1548 | if( svnFlag ){ |
| 1541 | 1549 | /* Get --svn related options here, so verify_all_options() fail when svn |
| 1542 | 1550 | * only option are specify with --git |
| 1543 | 1551 | */ |
| @@ -1545,12 +1553,13 @@ | ||
| 1545 | 1553 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1546 | 1554 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1547 | 1555 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1548 | 1556 | gsvn.zTags = find_option("tags", 0, 1); |
| 1549 | 1557 | gsvn.incrFlag = incrFlag; |
| 1550 | - }else{ | |
| 1551 | - find_option("git",0,0); /* Skip the --git option for now */ | |
| 1558 | + }else if( gitFlag ){ | |
| 1559 | + markfile_in = find_option("import-marks", 0, 1); | |
| 1560 | + markfile_out = find_option("export-marks", 0, 1); | |
| 1552 | 1561 | } |
| 1553 | 1562 | verify_all_options(); |
| 1554 | 1563 | |
| 1555 | 1564 | if( g.argc!=3 && g.argc!=4 ){ |
| 1556 | 1565 | usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| @@ -1624,10 +1633,13 @@ | ||
| 1624 | 1633 | gsvn.lenTags++; |
| 1625 | 1634 | } |
| 1626 | 1635 | } |
| 1627 | 1636 | svn_dump_import(pIn); |
| 1628 | 1637 | }else{ |
| 1638 | + Bag blobs, vers; | |
| 1639 | + bag_init(&blobs); | |
| 1640 | + bag_init(&vers); | |
| 1629 | 1641 | /* The following temp-tables are used to hold information needed for |
| 1630 | 1642 | ** the import. |
| 1631 | 1643 | ** |
| 1632 | 1644 | ** The XMARK table provides a mapping from fast-import "marks" and symbols |
| 1633 | 1645 | ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts). |
| @@ -1648,10 +1660,21 @@ | ||
| 1648 | 1660 | db_multi_exec( |
| 1649 | 1661 | "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 1650 | 1662 | "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" |
| 1651 | 1663 | "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" |
| 1652 | 1664 | ); |
| 1665 | + | |
| 1666 | + if(markfile_in){ | |
| 1667 | + FILE *f = fossil_fopen(markfile_in, "r"); | |
| 1668 | + if(!f){ | |
| 1669 | + fossil_fatal("cannot open %s for reading\n", markfile_in); | |
| 1670 | + } | |
| 1671 | + if(import_marks(f, &blobs, NULL)<0){ | |
| 1672 | + fossil_fatal("error importing marks from file: %s\n", markfile_in); | |
| 1673 | + } | |
| 1674 | + fclose(f); | |
| 1675 | + } | |
| 1653 | 1676 | |
| 1654 | 1677 | manifest_crosslink_begin(); |
| 1655 | 1678 | git_fast_import(pIn); |
| 1656 | 1679 | db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 1657 | 1680 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1659,10 +1682,35 @@ | ||
| 1659 | 1682 | db_ephemeral_blob(&q, 0, &record); |
| 1660 | 1683 | fast_insert_content(&record, 0, 0, 1); |
| 1661 | 1684 | import_reset(0); |
| 1662 | 1685 | } |
| 1663 | 1686 | db_finalize(&q); |
| 1687 | + if(markfile_out){ | |
| 1688 | + int rid; | |
| 1689 | + Stmt q_marks; | |
| 1690 | + FILE *f; | |
| 1691 | + db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark"); | |
| 1692 | + while( db_step(&q_marks)==SQLITE_ROW){ | |
| 1693 | + rid = db_column_int(&q_marks, 0); | |
| 1694 | + if(db_int(0, "SELECT count(objid) FROM event WHERE objid=%d AND type='ci'", rid)==0){ | |
| 1695 | + if(bag_find(&blobs, rid)==0){ | |
| 1696 | + bag_insert(&blobs, rid); | |
| 1697 | + } | |
| 1698 | + }else{ | |
| 1699 | + bag_insert(&vers, rid); | |
| 1700 | + } | |
| 1701 | + } | |
| 1702 | + db_finalize(&q_marks); | |
| 1703 | + f = fossil_fopen(markfile_out, "w"); | |
| 1704 | + if(!f){ | |
| 1705 | + fossil_fatal("cannot open %s for writing\n", markfile_out); | |
| 1706 | + } | |
| 1707 | + export_marks(f, &blobs, &vers); | |
| 1708 | + fclose(f); | |
| 1709 | + bag_clear(&blobs); | |
| 1710 | + bag_clear(&vers); | |
| 1711 | + } | |
| 1664 | 1712 | manifest_crosslink_end(MC_NONE); |
| 1665 | 1713 | } |
| 1666 | 1714 | |
| 1667 | 1715 | verify_cancel(); |
| 1668 | 1716 | db_end_transaction(0); |
| 1669 | 1717 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1494,10 +1494,13 @@ | |
| 1494 | ** data is read from standard input. |
| 1495 | ** |
| 1496 | ** The following formats are currently understood by this command |
| 1497 | ** |
| 1498 | ** --git Import from the git-fast-export file format (default) |
| 1499 | ** |
| 1500 | ** --svn Import from the svnadmin-dump file format. The default |
| 1501 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1502 | ** folders in the SVN root as special, following the |
| 1503 | ** common layout of SVN repositories. These are (by |
| @@ -1525,19 +1528,24 @@ | |
| 1525 | char *zPassword; |
| 1526 | FILE *pIn; |
| 1527 | Stmt q; |
| 1528 | int forceFlag = find_option("force", "f", 0)!=0; |
| 1529 | int svnFlag = find_option("svn", 0, 0)!=0; |
| 1530 | int omitRebuild = find_option("no-rebuild",0,0)!=0; |
| 1531 | int omitVacuum = find_option("no-vacuum",0,0)!=0; |
| 1532 | |
| 1533 | /* Options common to all input formats */ |
| 1534 | int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1535 | |
| 1536 | /* Options for --svn only */ |
| 1537 | const char *zBase=""; |
| 1538 | int flatFlag=0; |
| 1539 | |
| 1540 | if( svnFlag ){ |
| 1541 | /* Get --svn related options here, so verify_all_options() fail when svn |
| 1542 | * only option are specify with --git |
| 1543 | */ |
| @@ -1545,12 +1553,13 @@ | |
| 1545 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1546 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1547 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1548 | gsvn.zTags = find_option("tags", 0, 1); |
| 1549 | gsvn.incrFlag = incrFlag; |
| 1550 | }else{ |
| 1551 | find_option("git",0,0); /* Skip the --git option for now */ |
| 1552 | } |
| 1553 | verify_all_options(); |
| 1554 | |
| 1555 | if( g.argc!=3 && g.argc!=4 ){ |
| 1556 | usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| @@ -1624,10 +1633,13 @@ | |
| 1624 | gsvn.lenTags++; |
| 1625 | } |
| 1626 | } |
| 1627 | svn_dump_import(pIn); |
| 1628 | }else{ |
| 1629 | /* The following temp-tables are used to hold information needed for |
| 1630 | ** the import. |
| 1631 | ** |
| 1632 | ** The XMARK table provides a mapping from fast-import "marks" and symbols |
| 1633 | ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts). |
| @@ -1648,10 +1660,21 @@ | |
| 1648 | db_multi_exec( |
| 1649 | "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 1650 | "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" |
| 1651 | "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" |
| 1652 | ); |
| 1653 | |
| 1654 | manifest_crosslink_begin(); |
| 1655 | git_fast_import(pIn); |
| 1656 | db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 1657 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1659,10 +1682,35 @@ | |
| 1659 | db_ephemeral_blob(&q, 0, &record); |
| 1660 | fast_insert_content(&record, 0, 0, 1); |
| 1661 | import_reset(0); |
| 1662 | } |
| 1663 | db_finalize(&q); |
| 1664 | manifest_crosslink_end(MC_NONE); |
| 1665 | } |
| 1666 | |
| 1667 | verify_cancel(); |
| 1668 | db_end_transaction(0); |
| 1669 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -1494,10 +1494,13 @@ | |
| 1494 | ** data is read from standard input. |
| 1495 | ** |
| 1496 | ** The following formats are currently understood by this command |
| 1497 | ** |
| 1498 | ** --git Import from the git-fast-export file format (default) |
| 1499 | ** Options: |
| 1500 | ** --import-marks FILE Restore marks table from FILE |
| 1501 | ** --export-marks FILE Save marks table to FILE |
| 1502 | ** |
| 1503 | ** --svn Import from the svnadmin-dump file format. The default |
| 1504 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1505 | ** folders in the SVN root as special, following the |
| 1506 | ** common layout of SVN repositories. These are (by |
| @@ -1525,19 +1528,24 @@ | |
| 1528 | char *zPassword; |
| 1529 | FILE *pIn; |
| 1530 | Stmt q; |
| 1531 | int forceFlag = find_option("force", "f", 0)!=0; |
| 1532 | int svnFlag = find_option("svn", 0, 0)!=0; |
| 1533 | int gitFlag = find_option("git", 0, 0)!=0; |
| 1534 | int omitRebuild = find_option("no-rebuild",0,0)!=0; |
| 1535 | int omitVacuum = find_option("no-vacuum",0,0)!=0; |
| 1536 | |
| 1537 | /* Options common to all input formats */ |
| 1538 | int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1539 | |
| 1540 | /* Options for --svn only */ |
| 1541 | const char *zBase=""; |
| 1542 | int flatFlag=0; |
| 1543 | |
| 1544 | /* Options for --git only */ |
| 1545 | const char *markfile_in; |
| 1546 | const char *markfile_out; |
| 1547 | |
| 1548 | if( svnFlag ){ |
| 1549 | /* Get --svn related options here, so verify_all_options() fail when svn |
| 1550 | * only option are specify with --git |
| 1551 | */ |
| @@ -1545,12 +1553,13 @@ | |
| 1553 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1554 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1555 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1556 | gsvn.zTags = find_option("tags", 0, 1); |
| 1557 | gsvn.incrFlag = incrFlag; |
| 1558 | }else if( gitFlag ){ |
| 1559 | markfile_in = find_option("import-marks", 0, 1); |
| 1560 | markfile_out = find_option("export-marks", 0, 1); |
| 1561 | } |
| 1562 | verify_all_options(); |
| 1563 | |
| 1564 | if( g.argc!=3 && g.argc!=4 ){ |
| 1565 | usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| @@ -1624,10 +1633,13 @@ | |
| 1633 | gsvn.lenTags++; |
| 1634 | } |
| 1635 | } |
| 1636 | svn_dump_import(pIn); |
| 1637 | }else{ |
| 1638 | Bag blobs, vers; |
| 1639 | bag_init(&blobs); |
| 1640 | bag_init(&vers); |
| 1641 | /* The following temp-tables are used to hold information needed for |
| 1642 | ** the import. |
| 1643 | ** |
| 1644 | ** The XMARK table provides a mapping from fast-import "marks" and symbols |
| 1645 | ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts). |
| @@ -1648,10 +1660,21 @@ | |
| 1660 | db_multi_exec( |
| 1661 | "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 1662 | "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" |
| 1663 | "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" |
| 1664 | ); |
| 1665 | |
| 1666 | if(markfile_in){ |
| 1667 | FILE *f = fossil_fopen(markfile_in, "r"); |
| 1668 | if(!f){ |
| 1669 | fossil_fatal("cannot open %s for reading\n", markfile_in); |
| 1670 | } |
| 1671 | if(import_marks(f, &blobs, NULL)<0){ |
| 1672 | fossil_fatal("error importing marks from file: %s\n", markfile_in); |
| 1673 | } |
| 1674 | fclose(f); |
| 1675 | } |
| 1676 | |
| 1677 | manifest_crosslink_begin(); |
| 1678 | git_fast_import(pIn); |
| 1679 | db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 1680 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1659,10 +1682,35 @@ | |
| 1682 | db_ephemeral_blob(&q, 0, &record); |
| 1683 | fast_insert_content(&record, 0, 0, 1); |
| 1684 | import_reset(0); |
| 1685 | } |
| 1686 | db_finalize(&q); |
| 1687 | if(markfile_out){ |
| 1688 | int rid; |
| 1689 | Stmt q_marks; |
| 1690 | FILE *f; |
| 1691 | db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark"); |
| 1692 | while( db_step(&q_marks)==SQLITE_ROW){ |
| 1693 | rid = db_column_int(&q_marks, 0); |
| 1694 | if(db_int(0, "SELECT count(objid) FROM event WHERE objid=%d AND type='ci'", rid)==0){ |
| 1695 | if(bag_find(&blobs, rid)==0){ |
| 1696 | bag_insert(&blobs, rid); |
| 1697 | } |
| 1698 | }else{ |
| 1699 | bag_insert(&vers, rid); |
| 1700 | } |
| 1701 | } |
| 1702 | db_finalize(&q_marks); |
| 1703 | f = fossil_fopen(markfile_out, "w"); |
| 1704 | if(!f){ |
| 1705 | fossil_fatal("cannot open %s for writing\n", markfile_out); |
| 1706 | } |
| 1707 | export_marks(f, &blobs, &vers); |
| 1708 | fclose(f); |
| 1709 | bag_clear(&blobs); |
| 1710 | bag_clear(&vers); |
| 1711 | } |
| 1712 | manifest_crosslink_end(MC_NONE); |
| 1713 | } |
| 1714 | |
| 1715 | verify_cancel(); |
| 1716 | db_end_transaction(0); |
| 1717 |