Fossil SCM
Fix a problem that can occasionally occur with repeated syncs to/from a git repository, where a fossil-generated mark clashes with a mark previously generated by git, causing the sync to fail.
Commit
69668f8c57a081b40ab195295326e42bd845a5fb
Parent
85a0ada6919b78f…
2 files changed
+75
-34
+4
-4
+75
-34
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -132,23 +132,28 @@ | ||
| 132 | 132 | |
| 133 | 133 | /* |
| 134 | 134 | ** create_mark() |
| 135 | 135 | ** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table, |
| 136 | 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. | |
| 137 | 141 | ** This function returns -1 in the case where 'rid' does not exist, otherwise |
| 138 | 142 | ** it returns 0. |
| 139 | 143 | ** mark->name is dynamically allocated and is owned by the caller upon return. |
| 140 | 144 | */ |
| 141 | -int create_mark(int rid, struct mark_t *mark){ | |
| 145 | +int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){ | |
| 142 | 146 | char sid[13]; |
| 143 | 147 | char *zUuid = rid_to_uuid(rid); |
| 144 | 148 | if(!zUuid){ |
| 145 | 149 | fossil_trace("Undefined rid=%d\n", rid); |
| 146 | 150 | return -1; |
| 147 | 151 | } |
| 148 | 152 | 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; | |
| 150 | 155 | mark->name = fossil_strdup(sid); |
| 151 | 156 | sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid); |
| 152 | 157 | free(zUuid); |
| 153 | 158 | insert_commit_xref(mark->rid, mark->name, mark->uuid); |
| 154 | 159 | return 0; |
| @@ -156,19 +161,22 @@ | ||
| 156 | 161 | |
| 157 | 162 | /* |
| 158 | 163 | ** mark_name_from_rid() |
| 159 | 164 | ** Find the mark associated with the given rid. Mark names always start |
| 160 | 165 | ** 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. | |
| 164 | 172 | */ |
| 165 | -char * mark_name_from_rid(int rid){ | |
| 173 | +char * mark_name_from_rid(int rid, unsigned int *unused_mark){ | |
| 166 | 174 | char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid); |
| 167 | 175 | if(zMark==NULL){ |
| 168 | 176 | struct mark_t mark; |
| 169 | - if(create_mark(rid, &mark)==0){ | |
| 177 | + if(create_mark(rid, &mark, unused_mark)==0){ | |
| 170 | 178 | zMark = mark.name; |
| 171 | 179 | }else{ |
| 172 | 180 | return NULL; |
| 173 | 181 | } |
| 174 | 182 | } |
| @@ -185,16 +193,18 @@ | ||
| 185 | 193 | ** database. Otherwise, 0 is returned. |
| 186 | 194 | ** mark->name is dynamically allocated, and owned by the caller. |
| 187 | 195 | */ |
| 188 | 196 | int parse_mark(char *line, struct mark_t *mark){ |
| 189 | 197 | char *cur_tok; |
| 198 | + char type_; | |
| 190 | 199 | cur_tok = strtok(line, " \t"); |
| 191 | 200 | if(!cur_tok||strlen(cur_tok)<2){ |
| 192 | 201 | return -1; |
| 193 | 202 | } |
| 194 | 203 | mark->rid = atoi(&cur_tok[1]); |
| 195 | - if(cur_tok[0]!='c'){ | |
| 204 | + type_ = cur_tok[0]; | |
| 205 | + if(type_!='c'&&type_!='b'){ | |
| 196 | 206 | /* This is probably a blob mark */ |
| 197 | 207 | mark->name = NULL; |
| 198 | 208 | return 0; |
| 199 | 209 | } |
| 200 | 210 | |
| @@ -202,11 +212,18 @@ | ||
| 202 | 212 | if(!cur_tok){ |
| 203 | 213 | /* This mark was generated by an older version of Fossil and doesn't |
| 204 | 214 | ** include the mark name and uuid. create_mark() will name the new mark |
| 205 | 215 | ** exactly as it was when exported to git, so that we should have a |
| 206 | 216 | ** 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); | |
| 208 | 225 | }else{ |
| 209 | 226 | mark->name = fossil_strdup(cur_tok); |
| 210 | 227 | } |
| 211 | 228 | |
| 212 | 229 | cur_tok = strtok(NULL, "\n"); |
| @@ -233,18 +250,21 @@ | ||
| 233 | 250 | /* |
| 234 | 251 | ** import_marks() |
| 235 | 252 | ** Import the marks specified in file 'f' into the 'xmark' table. |
| 236 | 253 | ** If 'blobs' is non-null, insert all blob marks into it. |
| 237 | 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. | |
| 238 | 258 | ** Each line in the file must be at most 100 characters in length. This |
| 239 | 259 | ** seems like a reasonable maximum for a 40-character uuid, and 1-13 |
| 240 | 260 | ** character rid. |
| 241 | 261 | ** The function returns -1 if any of the lines in file 'f' are malformed, |
| 242 | 262 | ** or the rid/uuid information doesn't match what is in the repository |
| 243 | 263 | ** database. Otherwise, 0 is returned. |
| 244 | 264 | */ |
| 245 | -int import_marks(FILE* f, Bag *blobs, Bag *vers){ | |
| 265 | +int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){ | |
| 246 | 266 | char line[101]; |
| 247 | 267 | while(fgets(line, sizeof(line), f)){ |
| 248 | 268 | struct mark_t mark; |
| 249 | 269 | if(strlen(line)==100&&line[99]!='\n'){ |
| 250 | 270 | /* line too long */ |
| @@ -251,22 +271,45 @@ | ||
| 251 | 271 | return -1; |
| 252 | 272 | } |
| 253 | 273 | if( parse_mark(line, &mark)<0 ){ |
| 254 | 274 | return -1; |
| 255 | 275 | }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 | 276 | if(blobs!=NULL){ |
| 259 | 277 | bag_insert(blobs, mark.rid); |
| 260 | 278 | } |
| 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 | + } | |
| 263 | 289 | } |
| 264 | 290 | free(mark.name); |
| 265 | 291 | } |
| 266 | 292 | return 0; |
| 267 | 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 | +} | |
| 268 | 311 | |
| 269 | 312 | /* |
| 270 | 313 | ** If 'blobs' is non-null, it must point to a Bag of blob rids to be |
| 271 | 314 | ** written to disk. Blob rids are written as 'b<rid>'. |
| 272 | 315 | ** If 'vers' is non-null, it must point to a Bag of commit rids to be |
| @@ -275,32 +318,24 @@ | ||
| 275 | 318 | ** This function does not fail, but may produce errors if a uuid cannot |
| 276 | 319 | ** be found for an rid in 'vers'. |
| 277 | 320 | */ |
| 278 | 321 | void export_marks(FILE* f, Bag *blobs, Bag *vers){ |
| 279 | 322 | int rid; |
| 280 | - if( blobs!=NULL ){ | |
| 323 | + | |
| 324 | + if( blobs!=NULL ) { | |
| 281 | 325 | rid = bag_first(blobs); |
| 282 | - if(rid!=0){ | |
| 326 | + if( rid!=0 ){ | |
| 283 | 327 | 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 ); | |
| 286 | 330 | } |
| 287 | 331 | } |
| 288 | 332 | if( vers!=NULL ){ |
| 289 | 333 | rid = bag_first(vers); |
| 290 | 334 | if( rid!=0 ){ |
| 291 | 335 | 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'); | |
| 302 | 337 | }while( (rid = bag_next(vers, rid))!=0 ); |
| 303 | 338 | } |
| 304 | 339 | } |
| 305 | 340 | } |
| 306 | 341 | |
| @@ -336,10 +371,11 @@ | ||
| 336 | 371 | */ |
| 337 | 372 | void export_cmd(void){ |
| 338 | 373 | Stmt q, q2, q3; |
| 339 | 374 | int i; |
| 340 | 375 | Bag blobs, vers; |
| 376 | + unsigned int unused_mark = 1; | |
| 341 | 377 | const char *markfile_in; |
| 342 | 378 | const char *markfile_out; |
| 343 | 379 | |
| 344 | 380 | bag_init(&blobs); |
| 345 | 381 | bag_init(&vers); |
| @@ -362,11 +398,11 @@ | ||
| 362 | 398 | |
| 363 | 399 | f = fossil_fopen(markfile_in, "r"); |
| 364 | 400 | if( f==0 ){ |
| 365 | 401 | fossil_fatal("cannot open %s for reading", markfile_in); |
| 366 | 402 | } |
| 367 | - if(import_marks(f, &blobs, &vers)<0){ | |
| 403 | + if(import_marks(f, &blobs, &vers, &unused_mark)<0){ | |
| 368 | 404 | fossil_fatal("error importing marks from file: %s\n", markfile_in); |
| 369 | 405 | } |
| 370 | 406 | db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); |
| 371 | 407 | db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); |
| 372 | 408 | rid = bag_first(&blobs); |
| @@ -416,15 +452,18 @@ | ||
| 416 | 452 | while( db_step(&q)==SQLITE_ROW ){ |
| 417 | 453 | int rid = db_column_int(&q, 0); |
| 418 | 454 | Blob content; |
| 419 | 455 | |
| 420 | 456 | while( !bag_find(&blobs, rid) ){ |
| 457 | + char *zMark; | |
| 421 | 458 | content_get(rid, &content); |
| 422 | 459 | db_bind_int(&q2, ":rid", rid); |
| 423 | 460 | db_step(&q2); |
| 424 | 461 | 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); | |
| 426 | 465 | bag_insert(&blobs, rid); |
| 427 | 466 | fwrite(blob_buffer(&content), 1, blob_size(&content), stdout); |
| 428 | 467 | printf("\n"); |
| 429 | 468 | blob_reset(&content); |
| 430 | 469 | |
| @@ -470,11 +509,11 @@ | ||
| 470 | 509 | if( zBranch==0 ) zBranch = "trunk"; |
| 471 | 510 | zBr = mprintf("%s", zBranch); |
| 472 | 511 | for(i=0; zBr[i]; i++){ |
| 473 | 512 | if( !fossil_isalnum(zBr[i]) ) zBr[i] = '_'; |
| 474 | 513 | } |
| 475 | - zMark = mark_name_from_rid(ckinId); | |
| 514 | + zMark = mark_name_from_rid(ckinId, &unused_mark); | |
| 476 | 515 | printf("commit refs/heads/%s\nmark %s\n", zBr, zMark); |
| 477 | 516 | free(zMark); |
| 478 | 517 | free(zBr); |
| 479 | 518 | printf("committer"); |
| 480 | 519 | print_person(zUser); |
| @@ -487,21 +526,21 @@ | ||
| 487 | 526 | " AND pid IN (SELECT objid FROM event)", |
| 488 | 527 | ckinId |
| 489 | 528 | ); |
| 490 | 529 | if( db_step(&q3) == SQLITE_ROW ){ |
| 491 | 530 | int pid = db_column_int(&q3, 0); |
| 492 | - zMark = mark_name_from_rid(pid); | |
| 531 | + zMark = mark_name_from_rid(pid, &unused_mark); | |
| 493 | 532 | printf("from %s\n", zMark); |
| 494 | 533 | free(zMark); |
| 495 | 534 | db_prepare(&q4, |
| 496 | 535 | "SELECT pid FROM plink" |
| 497 | 536 | " WHERE cid=%d AND NOT isprim" |
| 498 | 537 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" |
| 499 | 538 | " ORDER BY pid", |
| 500 | 539 | ckinId); |
| 501 | 540 | 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); | |
| 503 | 542 | printf("merge %s\n", zMark); |
| 504 | 543 | free(zMark); |
| 505 | 544 | } |
| 506 | 545 | db_finalize(&q4); |
| 507 | 546 | }else{ |
| @@ -519,17 +558,19 @@ | ||
| 519 | 558 | int zNew = db_column_int(&q4,1); |
| 520 | 559 | int mPerm = db_column_int(&q4,2); |
| 521 | 560 | if( zNew==0) |
| 522 | 561 | printf("D %s\n", zName); |
| 523 | 562 | else if( bag_find(&blobs, zNew) ) { |
| 563 | + zMark = mark_name_from_rid(zNew, &unused_mark); | |
| 524 | 564 | const char *zPerm; |
| 525 | 565 | switch( mPerm ){ |
| 526 | 566 | case PERM_LNK: zPerm = "120000"; break; |
| 527 | 567 | case PERM_EXE: zPerm = "100755"; break; |
| 528 | 568 | default: zPerm = "100644"; break; |
| 529 | 569 | } |
| 530 | - printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName); | |
| 570 | + printf("M %s %s %s\n", zPerm, zMark, zName); | |
| 571 | + free(zMark); | |
| 531 | 572 | } |
| 532 | 573 | } |
| 533 | 574 | db_finalize(&q4); |
| 534 | 575 | db_finalize(&q3); |
| 535 | 576 | printf("\n"); |
| 536 | 577 |
| --- 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,16 +193,18 @@ | |
| 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 | |
| @@ -202,11 +212,18 @@ | |
| 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"); |
| @@ -233,18 +250,21 @@ | |
| 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,22 +271,45 @@ | |
| 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,11 +398,11 @@ | |
| 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\n", 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); |
| @@ -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{ |
| @@ -519,17 +558,19 @@ | |
| 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"); |
| 536 |
| --- 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,16 +193,18 @@ | |
| 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 | |
| @@ -202,11 +212,18 @@ | |
| 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"); |
| @@ -233,18 +250,21 @@ | |
| 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 */ |
| @@ -251,22 +271,45 @@ | |
| 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,11 +398,11 @@ | |
| 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\n", 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); |
| @@ -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{ |
| @@ -519,17 +558,19 @@ | |
| 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"); |
| 577 |
+4
-4
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -1770,11 +1770,11 @@ | ||
| 1770 | 1770 | if( markfile_in ){ |
| 1771 | 1771 | FILE *f = fossil_fopen(markfile_in, "r"); |
| 1772 | 1772 | if( !f ){ |
| 1773 | 1773 | fossil_fatal("cannot open %s for reading\n", markfile_in); |
| 1774 | 1774 | } |
| 1775 | - if(import_marks(f, &blobs, NULL)<0){ | |
| 1775 | + if(import_marks(f, &blobs, NULL, NULL)<0){ | |
| 1776 | 1776 | fossil_fatal("error importing marks from file: %s\n", markfile_in); |
| 1777 | 1777 | } |
| 1778 | 1778 | fclose(f); |
| 1779 | 1779 | } |
| 1780 | 1780 | |
| @@ -1795,13 +1795,13 @@ | ||
| 1795 | 1795 | db_prepare(&q_marks, "SELECT DISTINCT trid FROM xmark"); |
| 1796 | 1796 | while( db_step(&q_marks)==SQLITE_ROW ){ |
| 1797 | 1797 | rid = db_column_int(&q_marks, 0); |
| 1798 | 1798 | if( db_int(0, "SELECT count(objid) FROM event" |
| 1799 | 1799 | " 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 | + */ | |
| 1803 | 1803 | }else{ |
| 1804 | 1804 | bag_insert(&vers, rid); |
| 1805 | 1805 | } |
| 1806 | 1806 | } |
| 1807 | 1807 | db_finalize(&q_marks); |
| 1808 | 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\n", markfile_in); |
| 1774 | } |
| 1775 | if(import_marks(f, &blobs, NULL)<0){ |
| 1776 | fossil_fatal("error importing marks from file: %s\n", 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\n", markfile_in); |
| 1774 | } |
| 1775 | if(import_marks(f, &blobs, NULL, NULL)<0){ |
| 1776 | fossil_fatal("error importing marks from file: %s\n", 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 |