| | @@ -58,10 +58,11 @@ |
| 58 | 58 | char **azMerge; /* Merge values */ |
| 59 | 59 | int nFile; /* Number of aFile values */ |
| 60 | 60 | int nFileAlloc; /* Number of slots in aFile[] */ |
| 61 | 61 | ImportFile *aFile; /* Information about files in a commit */ |
| 62 | 62 | int fromLoaded; /* True zFrom content loaded into aFile[] */ |
| 63 | + int tagCommit; /* True if the commit adds a tag */ |
| 63 | 64 | } gg; |
| 64 | 65 | |
| 65 | 66 | /* |
| 66 | 67 | ** Duplicate a string. |
| 67 | 68 | */ |
| | @@ -148,16 +149,16 @@ |
| 148 | 149 | blob_reset(&cmpr); |
| 149 | 150 | rid = db_last_insert_rowid(); |
| 150 | 151 | } |
| 151 | 152 | if( zMark ){ |
| 152 | 153 | db_multi_exec( |
| 153 | | - "INSERT OR IGNORE INTO xtag(tname, trid, tuuid)" |
| 154 | + "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" |
| 154 | 155 | "VALUES(%Q,%d,%B)", |
| 155 | 156 | zMark, rid, &hash |
| 156 | 157 | ); |
| 157 | 158 | db_multi_exec( |
| 158 | | - "INSERT OR IGNORE INTO xtag(tname, trid, tuuid)" |
| 159 | + "INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" |
| 159 | 160 | "VALUES(%B,%d,%B)", |
| 160 | 161 | &hash, rid, &hash |
| 161 | 162 | ); |
| 162 | 163 | } |
| 163 | 164 | if( saveUuid ){ |
| | @@ -244,29 +245,54 @@ |
| 244 | 245 | zFromBranch = db_text(0, "SELECT brnm FROM xbranch WHERE tname=%Q", |
| 245 | 246 | gg.zFromMark); |
| 246 | 247 | }else{ |
| 247 | 248 | zFromBranch = 0; |
| 248 | 249 | } |
| 249 | | - if( fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 250 | + if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 250 | 251 | blob_appendf(&record, "T *branch * %F\n", gg.zBranch); |
| 251 | 252 | blob_appendf(&record, "T *sym-%F *\n", gg.zBranch); |
| 252 | 253 | if( zFromBranch ){ |
| 253 | 254 | blob_appendf(&record, "T -sym-%F *\n", zFromBranch); |
| 254 | 255 | } |
| 255 | 256 | } |
| 256 | 257 | free(zFromBranch); |
| 257 | 258 | if( gg.zFrom==0 ){ |
| 258 | | - blob_appendf(&record, "T +sym-trunk *\n"); |
| 259 | + blob_appendf(&record, "T *sym-trunk *\n"); |
| 259 | 260 | } |
| 260 | 261 | db_multi_exec("INSERT INTO xbranch(tname, brnm) VALUES(%Q,%Q)", |
| 261 | 262 | gg.zMark, gg.zBranch); |
| 262 | 263 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 263 | 264 | md5sum_blob(&record, &cksum); |
| 264 | 265 | blob_appendf(&record, "Z %b\n", &cksum); |
| 265 | 266 | fast_insert_content(&record, gg.zMark, 1); |
| 266 | 267 | blob_reset(&record); |
| 267 | 268 | blob_reset(&cksum); |
| 269 | + |
| 270 | + /* The "git fast-export" command might output multiple "commit" lines |
| 271 | + ** that reference a tag using "refs/tags/TAGNAME". The tag should only |
| 272 | + ** be applied to the last commit that is output. The problem is we do not |
| 273 | + ** know at this time if the current commit is the last one to hold this |
| 274 | + ** tag or not. So make an entry in the XTAG table to record this tag |
| 275 | + ** but overwrite that entry if a later instance of the same tag appears. |
| 276 | + ** |
| 277 | + ** This behavior seems like a bug in git-fast-export, but it is easier |
| 278 | + ** to work around the problem than to fix git-fast-export. |
| 279 | + */ |
| 280 | + if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 281 | + blob_appendf(&record, "D %s\n", gg.zDate); |
| 282 | + blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin); |
| 283 | + blob_appendf(&record, "U %F\n", gg.zUser); |
| 284 | + md5sum_blob(&record, &cksum); |
| 285 | + blob_appendf(&record, "Z %b\n", &cksum); |
| 286 | + db_multi_exec( |
| 287 | + "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| 288 | + " VALUES(%Q,%Q)", gg.zBranch, blob_str(&record) |
| 289 | + ); |
| 290 | + blob_reset(&record); |
| 291 | + blob_reset(&cksum); |
| 292 | + } |
| 293 | + |
| 268 | 294 | fossil_free(gg.zPrevBranch); |
| 269 | 295 | gg.zPrevBranch = gg.zBranch; |
| 270 | 296 | gg.zBranch = 0; |
| 271 | 297 | import_reset(0); |
| 272 | 298 | } |
| | @@ -326,11 +352,11 @@ |
| 326 | 352 | ** Convert a "mark" or "committish" into the UUID. |
| 327 | 353 | */ |
| 328 | 354 | static char *resolve_committish(const char *zCommittish){ |
| 329 | 355 | char *zRes; |
| 330 | 356 | |
| 331 | | - zRes = db_text(0, "SELECT tuuid FROM xtag WHERE tname=%Q", zCommittish); |
| 357 | + zRes = db_text(0, "SELECT tuuid FROM xmark WHERE tname=%Q", zCommittish); |
| 332 | 358 | return zRes; |
| 333 | 359 | } |
| 334 | 360 | |
| 335 | 361 | /* |
| 336 | 362 | ** Create a new entry in the gg.aFile[] array |
| | @@ -424,12 +450,33 @@ |
| 424 | 450 | if( memcmp(zLine, "commit ", 7)==0 ){ |
| 425 | 451 | gg.xFinish(); |
| 426 | 452 | gg.xFinish = finish_commit; |
| 427 | 453 | trim_newline(&zLine[7]); |
| 428 | 454 | z = &zLine[7]; |
| 455 | + |
| 456 | + /* The argument to the "commit" line might match either of these |
| 457 | + ** patterns: |
| 458 | + ** |
| 459 | + ** (A) refs/heads/BRANCHNAME |
| 460 | + ** (B) refs/tags/TAGNAME |
| 461 | + ** |
| 462 | + ** If pattern A is used, then the branchname used is as shown. |
| 463 | + ** Except, the "master" branch which is the default branch name in |
| 464 | + ** Git is changed to "trunk" which is the default name in Fossil. |
| 465 | + ** If the pattern is B, then the new commit should be on the same |
| 466 | + ** branch as its parent. And, we might need to add the TAGNAME |
| 467 | + ** tag to the new commit. However, if there are multiple instances |
| 468 | + ** of pattern B with the same TAGNAME, then only put the tag on the |
| 469 | + ** last commit that holds that tag. |
| 470 | + ** |
| 471 | + ** None of the above is explained in the git-fast-export |
| 472 | + ** documentation. We had to figure it out via trial and error. |
| 473 | + */ |
| 429 | 474 | for(i=strlen(z)-1; i>=0 && z[i]!='/'; i--){} |
| 475 | + gg.tagCommit = memcmp(&z[i-4], "tags", 4)==0; /* True for pattern B */ |
| 430 | 476 | if( z[i+1]!=0 ) z += i+1; |
| 477 | + if( fossil_strcmp(z, "master")==0 ) z = "trunk"; |
| 431 | 478 | gg.zBranch = fossil_strdup(z); |
| 432 | 479 | gg.fromLoaded = 0; |
| 433 | 480 | }else |
| 434 | 481 | if( memcmp(zLine, "tag ", 4)==0 ){ |
| 435 | 482 | gg.xFinish(); |
| | @@ -631,11 +678,13 @@ |
| 631 | 678 | ** in the future. |
| 632 | 679 | */ |
| 633 | 680 | void git_import_cmd(void){ |
| 634 | 681 | char *zPassword; |
| 635 | 682 | FILE *pIn; |
| 683 | + Stmt q; |
| 636 | 684 | int forceFlag = find_option("force", "f", 0)!=0; |
| 685 | + |
| 637 | 686 | find_option("git",0,0); /* Skip the --git option for now */ |
| 638 | 687 | verify_all_options(); |
| 639 | 688 | if( g.argc!=3 && g.argc!=4 ){ |
| 640 | 689 | usage("REPOSITORY-NAME"); |
| 641 | 690 | } |
| | @@ -647,17 +696,48 @@ |
| 647 | 696 | } |
| 648 | 697 | if( forceFlag ) unlink(g.argv[2]); |
| 649 | 698 | db_create_repository(g.argv[2]); |
| 650 | 699 | db_open_repository(g.argv[2]); |
| 651 | 700 | db_open_config(0); |
| 701 | + |
| 702 | + /* The following temp-tables are used to hold information needed for |
| 703 | + ** the import. |
| 704 | + ** |
| 705 | + ** The XMARK table provides a mapping from fast-import "marks" and symbols |
| 706 | + ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts). |
| 707 | + ** Given any valid fast-import symbol, the corresponding fossil rid and |
| 708 | + ** uuid can found by searching against the xmark.tname field. |
| 709 | + ** |
| 710 | + ** The XBRANCH table maps commit marks and symbols into the branch those |
| 711 | + ** commits belong to. If xbranch.tname is a fast-import symbol for a |
| 712 | + ** checkin then xbranch.brnm is the branch that checkin is part of. |
| 713 | + ** |
| 714 | + ** The XTAG table records information about tags that need to be applied |
| 715 | + ** to various branches after the import finishes. The xtag.tcontent field |
| 716 | + ** contains the text of an artifact that will add a tag to a check-in. |
| 717 | + ** The git-fast-export file format might specify the same tag multiple |
| 718 | + ** times but only the last tag should be used. And we do not know which |
| 719 | + ** occurrence of the tag is the last until the import finishes. |
| 720 | + */ |
| 652 | 721 | db_multi_exec( |
| 653 | | - "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 722 | + "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 654 | 723 | "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" |
| 724 | + "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" |
| 655 | 725 | ); |
| 726 | + |
| 727 | + |
| 656 | 728 | db_begin_transaction(); |
| 657 | 729 | db_initial_setup(0, 0, 1); |
| 658 | 730 | git_fast_import(pIn); |
| 731 | + db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 732 | + while( db_step(&q)==SQLITE_ROW ){ |
| 733 | + Blob record; |
| 734 | + db_ephemeral_blob(&q, 0, &record); |
| 735 | + fast_insert_content(&record, 0, 0); |
| 736 | + import_reset(0); |
| 737 | + } |
| 738 | + db_finalize(&q); |
| 659 | 739 | db_end_transaction(0); |
| 660 | 740 | db_begin_transaction(); |
| 661 | 741 | printf("Rebuilding repository meta-data...\n"); |
| 662 | 742 | rebuild_db(0, 1, 1); |
| 663 | 743 | verify_cancel(); |
| 664 | 744 | |