Fossil SCM
Enhancements to SVN import
Commit
fa9ead30dc76c2cc2624ee71931632f3fdd4dd2d
Parent
96164bb2b137b48…
2 files changed
+144
-39
+144
-39
+144
-39
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -34,10 +34,20 @@ | ||
| 34 | 34 | char isExe; /* True if executable */ |
| 35 | 35 | char isLink; /* True if symlink */ |
| 36 | 36 | }; |
| 37 | 37 | #endif |
| 38 | 38 | |
| 39 | +/* | |
| 40 | +** State information common to all import types. | |
| 41 | +*/ | |
| 42 | +static struct { | |
| 43 | + const char *zTrunkName; /* Name of trunk branch */ | |
| 44 | + const char *zBranchPre; /* Prepended to non-trunk branch names */ | |
| 45 | + const char *zBranchSuf; /* Appended to non-trunk branch names */ | |
| 46 | + const char *zTagPre; /* Prepended to non-trunk tag names */ | |
| 47 | + const char *zTagSuf; /* Appended to non-trunk tag names */ | |
| 48 | +} gimport; | |
| 39 | 49 | |
| 40 | 50 | /* |
| 41 | 51 | ** State information about an on-going fast-import parse. |
| 42 | 52 | */ |
| 43 | 53 | static struct { |
| @@ -198,11 +208,12 @@ | ||
| 198 | 208 | static void finish_tag(void){ |
| 199 | 209 | Blob record, cksum; |
| 200 | 210 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 201 | 211 | blob_zero(&record); |
| 202 | 212 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 203 | - blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); | |
| 213 | + blob_appendf(&record, "T +%F%F%F %s\n", gimport.zTagPre, gg.zTag, | |
| 214 | + gimport.zTagSuf, gg.zFrom); | |
| 204 | 215 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 205 | 216 | md5sum_blob(&record, &cksum); |
| 206 | 217 | blob_appendf(&record, "Z %b\n", &cksum); |
| 207 | 218 | fast_insert_content(&record, 0, 0, 1); |
| 208 | 219 | blob_reset(&cksum); |
| @@ -275,18 +286,21 @@ | ||
| 275 | 286 | |
| 276 | 287 | /* Add the required "T" cards to the manifest. Make sure they are added |
| 277 | 288 | ** in sorted order and without any duplicates. Otherwise, fossil will not |
| 278 | 289 | ** recognize the document as a valid manifest. */ |
| 279 | 290 | if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 280 | - aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch); | |
| 281 | - aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch); | |
| 291 | + aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre, | |
| 292 | + gg.zBranch, gimport.zBranchSuf); | |
| 293 | + aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre, | |
| 294 | + gg.zBranch, gimport.zBranchSuf); | |
| 282 | 295 | if( zFromBranch ){ |
| 283 | - aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch); | |
| 296 | + aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre, | |
| 297 | + zFromBranch, gimport.zBranchSuf); | |
| 284 | 298 | } |
| 285 | 299 | } |
| 286 | 300 | if( gg.zFrom==0 ){ |
| 287 | - aTCard[nTCard++] = mprintf("T *sym-trunk *\n"); | |
| 301 | + aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName); | |
| 288 | 302 | } |
| 289 | 303 | qsort(aTCard, nTCard, sizeof(char *), string_cmp); |
| 290 | 304 | for(i=0; i<nTCard; i++){ |
| 291 | 305 | if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ |
| 292 | 306 | blob_appendf(&record, "%s", aTCard[i]); |
| @@ -313,11 +327,12 @@ | ||
| 313 | 327 | ** This behavior seems like a bug in git-fast-export, but it is easier |
| 314 | 328 | ** to work around the problem than to fix git-fast-export. |
| 315 | 329 | */ |
| 316 | 330 | if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 317 | 331 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 318 | - blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin); | |
| 332 | + blob_appendf(&record, "T +sym-%F%F%F %s\n", gimport.zBranchPre, gg.zBranch, | |
| 333 | + gimport.zBranchSuf, gg.zPrevCheckin); | |
| 319 | 334 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 320 | 335 | md5sum_blob(&record, &cksum); |
| 321 | 336 | blob_appendf(&record, "Z %b\n", &cksum); |
| 322 | 337 | db_multi_exec( |
| 323 | 338 | "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| @@ -749,11 +764,14 @@ | ||
| 749 | 764 | const char *zBranches; /* Name of branches folder in repo root */ |
| 750 | 765 | int lenBranches; /* String length of zBranches */ |
| 751 | 766 | const char *zTags; /* Name of tags folder in repo root */ |
| 752 | 767 | int lenTags; /* String length of zTags */ |
| 753 | 768 | Bag newBranches; /* Branches that were created in this revision */ |
| 754 | - int incrFlag; /* Add svn-rev-nn tags on every checkin */ | |
| 769 | + int revFlag; /* Add svn-rev-nn tags on every checkin */ | |
| 770 | + const char *zRevPre; /* Prepended to revision tag names */ | |
| 771 | + const char *zRevSuf; /* Appended to revision tag names */ | |
| 772 | + const char **azIgnTree; /* NULL-terminated list of dirs to ignore */ | |
| 755 | 773 | } gsvn; |
| 756 | 774 | typedef struct { |
| 757 | 775 | char *zKey; |
| 758 | 776 | char *zVal; |
| 759 | 777 | } KeyVal; |
| @@ -968,15 +986,17 @@ | ||
| 968 | 986 | " WHERE trev<%d AND tbranch=%d", |
| 969 | 987 | gsvn.rev, branchId); |
| 970 | 988 | } |
| 971 | 989 | if( parentRid>0 ){ |
| 972 | 990 | pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 973 | - pParentFile = manifest_file_next(pParentManifest, 0); | |
| 974 | - parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", | |
| 975 | - parentRid); | |
| 976 | - if( parentBranch!=branchId && branchType!=SVN_TAG ){ | |
| 977 | - sameAsParent = 0; | |
| 991 | + if( pParentManifest ){ | |
| 992 | + pParentFile = manifest_file_next(pParentManifest, 0); | |
| 993 | + parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", | |
| 994 | + parentRid); | |
| 995 | + if( parentBranch!=branchId && branchType!=SVN_TAG ){ | |
| 996 | + sameAsParent = 0; | |
| 997 | + } | |
| 978 | 998 | } |
| 979 | 999 | } |
| 980 | 1000 | if( mergeRid<MAX_INT_32 ){ |
| 981 | 1001 | if( gsvn.zComment ){ |
| 982 | 1002 | blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| @@ -1017,47 +1037,57 @@ | ||
| 1017 | 1037 | char *zParentBranch = |
| 1018 | 1038 | db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1019 | 1039 | parentBranch |
| 1020 | 1040 | ); |
| 1021 | 1041 | blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1022 | - blob_appendf(&manifest, "T *branch * %F\n", zBranch); | |
| 1023 | - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); | |
| 1024 | - if( gsvn.incrFlag ){ | |
| 1025 | - blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); | |
| 1042 | + blob_appendf(&manifest, "T *branch * %F%F%F\n", gimport.zBranchPre, | |
| 1043 | + zBranch, gimport.zBranchSuf); | |
| 1044 | + blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, | |
| 1045 | + zBranch, gimport.zBranchSuf); | |
| 1046 | + if( gsvn.revFlag ){ | |
| 1047 | + blob_appendf(&manifest, "T +sym-%Fr%d%F *\n", gimport.zTagPre, | |
| 1048 | + gsvn.rev, gimport.zTagSuf); | |
| 1026 | 1049 | } |
| 1027 | - blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch); | |
| 1050 | + blob_appendf(&manifest, "T -sym-%F%F%F *\n", gimport.zBranchPre, | |
| 1051 | + zParentBranch, gimport.zBranchSuf); | |
| 1028 | 1052 | fossil_free(zParentBranch); |
| 1029 | 1053 | }else{ |
| 1030 | 1054 | char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1031 | 1055 | blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1032 | - if( gsvn.incrFlag ){ | |
| 1033 | - blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); | |
| 1056 | + if( gsvn.revFlag ){ | |
| 1057 | + blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, | |
| 1058 | + gsvn.rev, gsvn.zRevSuf); | |
| 1034 | 1059 | } |
| 1035 | 1060 | fossil_free(zMergeUuid); |
| 1036 | 1061 | } |
| 1037 | 1062 | fossil_free(zParentUuid); |
| 1038 | 1063 | }else{ |
| 1039 | - blob_appendf(&manifest, "T *branch * %F\n", zBranch); | |
| 1040 | - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); | |
| 1041 | - if( gsvn.incrFlag ){ | |
| 1042 | - blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); | |
| 1064 | + blob_appendf(&manifest, "T *branch * %F%F%F\n", | |
| 1065 | + gimport.zBranchPre, zBranch, gimport.zBranchSuf); | |
| 1066 | + blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, | |
| 1067 | + zBranch, gimport.zBranchSuf); | |
| 1068 | + if( gsvn.revFlag ){ | |
| 1069 | + blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev, | |
| 1070 | + gsvn.zRevSuf); | |
| 1043 | 1071 | } |
| 1044 | 1072 | } |
| 1045 | 1073 | }else if( branchType==SVN_TAG ){ |
| 1046 | 1074 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1047 | 1075 | blob_reset(&manifest); |
| 1048 | 1076 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1049 | - blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid); | |
| 1077 | + blob_appendf(&manifest, "T +sym-%F%F%F %s\n", gimport.zTagPre, zBranch, | |
| 1078 | + gimport.zTagSuf, zParentUuid); | |
| 1050 | 1079 | fossil_free(zParentUuid); |
| 1051 | 1080 | } |
| 1052 | 1081 | }else{ |
| 1053 | 1082 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1054 | 1083 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1055 | 1084 | if( branchType!=SVN_TAG ){ |
| 1056 | 1085 | blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1057 | 1086 | }else{ |
| 1058 | - blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid); | |
| 1087 | + blob_appendf(&manifest, "T -sym-%F%F%F %s\n", gimport.zBranchPre, | |
| 1088 | + zBranch, gimport.zBranchSuf, zParentUuid); | |
| 1059 | 1089 | } |
| 1060 | 1090 | fossil_free(zParentUuid); |
| 1061 | 1091 | } |
| 1062 | 1092 | if( gsvn.zUser ){ |
| 1063 | 1093 | blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| @@ -1152,14 +1182,27 @@ | ||
| 1152 | 1182 | } |
| 1153 | 1183 | } |
| 1154 | 1184 | |
| 1155 | 1185 | /* |
| 1156 | 1186 | ** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1157 | - */ | |
| 1187 | +** Return 0 if not a branch, tag, or trunk, or if ignored by --ignore-tree. | |
| 1188 | +*/ | |
| 1158 | 1189 | static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1159 | 1190 | char *zBranch = 0; |
| 1160 | 1191 | int branchId = 0; |
| 1192 | + if( gsvn.azIgnTree ){ | |
| 1193 | + const char **pzIgnTree; | |
| 1194 | + unsigned nPath = strlen(zPath); | |
| 1195 | + for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){ | |
| 1196 | + const char *zIgn = *pzIgnTree; | |
| 1197 | + int nIgn = strlen(zIgn); | |
| 1198 | + if( strncmp(zPath, zIgn, nIgn) == 0 | |
| 1199 | + && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){ | |
| 1200 | + return 0; | |
| 1201 | + } | |
| 1202 | + } | |
| 1203 | + } | |
| 1161 | 1204 | *type = SVN_UNKNOWN; |
| 1162 | 1205 | *zFile = 0; |
| 1163 | 1206 | if( gsvn.lenTrunk==0 ){ |
| 1164 | 1207 | zBranch = "trunk"; |
| 1165 | 1208 | *zFile = zPath; |
| @@ -1433,11 +1476,11 @@ | ||
| 1433 | 1476 | if( strncmp(zAction, "change", 6)==0 ){ |
| 1434 | 1477 | int rid = 0; |
| 1435 | 1478 | if( zKind==0 ){ |
| 1436 | 1479 | fossil_fatal("Missing Node-kind"); |
| 1437 | 1480 | } |
| 1438 | - if( strncmp(zKind, "dir", 3)!=0 ){ | |
| 1481 | + if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){ | |
| 1439 | 1482 | if( deltaFlag ){ |
| 1440 | 1483 | Blob deltaSrc; |
| 1441 | 1484 | Blob target; |
| 1442 | 1485 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1443 | 1486 | " SELECT tuuid FROM xfiles" |
| @@ -1497,31 +1540,48 @@ | ||
| 1497 | 1540 | ** --git Import from the git-fast-export file format (default) |
| 1498 | 1541 | ** Options: |
| 1499 | 1542 | ** --import-marks FILE Restore marks table from FILE |
| 1500 | 1543 | ** --export-marks FILE Save marks table to FILE |
| 1501 | 1544 | ** |
| 1502 | -** --svn Import from the svnadmin-dump file format. The default | |
| 1545 | +** --svn Import from the svnadmin-dump file format. The default | |
| 1503 | 1546 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1504 | 1547 | ** folders in the SVN root as special, following the |
| 1505 | -** common layout of SVN repositories. These are (by | |
| 1506 | -** default) trunk/, branches/ and tags/ | |
| 1548 | +** common layout of SVN repositories. These are (by | |
| 1549 | +** default) trunk/, branches/ and tags/. The SVN --deltas | |
| 1550 | +** format is supported but not required. | |
| 1507 | 1551 | ** Options: |
| 1508 | 1552 | ** --trunk FOLDER Name of trunk folder |
| 1509 | 1553 | ** --branches FOLDER Name of branches folder |
| 1510 | 1554 | ** --tags FOLDER Name of tags folder |
| 1511 | 1555 | ** --base PATH Path to project root in repository |
| 1512 | 1556 | ** --flat The whole dump is a single branch |
| 1557 | +** --rev-tags Tag each revision, implied by -i | |
| 1558 | +** --no-rev-tags Disables tagging effect of -i | |
| 1559 | +** --rename-rev PAT Rev tag names, default "svn-rev-%" | |
| 1560 | +** --ignore-tree DIR Ignores subtree rooted at DIR | |
| 1513 | 1561 | ** |
| 1514 | 1562 | ** Common Options: |
| 1515 | -** -i|--incremental allow importing into an existing repository | |
| 1516 | -** -f|--force overwrite repository if already exists | |
| 1517 | -** -q|--quiet omit progress output | |
| 1518 | -** --no-rebuild skip the "rebuilding metadata" step | |
| 1519 | -** --no-vacuum skip the final VACUUM of the database file | |
| 1563 | +** -i|--incremental allow importing into an existing repository | |
| 1564 | +** -f|--force overwrite repository if already exists | |
| 1565 | +** -q|--quiet omit progress output | |
| 1566 | +** --no-rebuild skip the "rebuilding metadata" step | |
| 1567 | +** --no-vacuum skip the final VACUUM of the database file | |
| 1568 | +** --rename-trunk NAME use NAME as name of imported trunk branch | |
| 1569 | +** --rename-branch PAT rename all branch names using PAT pattern | |
| 1570 | +** --rename-tag PAT rename all tag names using PAT pattern | |
| 1520 | 1571 | ** |
| 1521 | 1572 | ** The --incremental option allows an existing repository to be extended |
| 1522 | -** with new content. | |
| 1573 | +** with new content. The --rename-* options may be useful to avoid name | |
| 1574 | +** conflicts when using the --incremental option. | |
| 1575 | +** | |
| 1576 | +** The argument to --rename-* contains one "%" character to be replaced | |
| 1577 | +** with the original name. For example, "--rename-tag svn-%-tag" renames | |
| 1578 | +** the tag called "release" to "svn-release-tag". | |
| 1579 | +** | |
| 1580 | +** --ignore-tree is useful for importing Subversion repositories which | |
| 1581 | +** move branches to subdirectories of "branches/deleted" instead of | |
| 1582 | +** deleting them. It can be supplied multiple times if necessary. | |
| 1523 | 1583 | ** |
| 1524 | 1584 | ** See also: export |
| 1525 | 1585 | */ |
| 1526 | 1586 | void import_cmd(void){ |
| 1527 | 1587 | char *zPassword; |
| @@ -1541,21 +1601,66 @@ | ||
| 1541 | 1601 | int flatFlag = 0; |
| 1542 | 1602 | |
| 1543 | 1603 | /* Options for --git only */ |
| 1544 | 1604 | const char *markfile_in = 0; |
| 1545 | 1605 | const char *markfile_out = 0; |
| 1606 | + | |
| 1607 | + /* Interpret --rename-* options. Use a table to avoid code duplication. */ | |
| 1608 | + const struct { | |
| 1609 | + const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf; | |
| 1610 | + int format; /* 1=git, 2=svn, 3=any */ | |
| 1611 | + } renOpts[] = { | |
| 1612 | + {"rename-branch", &gimport.zBranchPre, "", &gimport.zBranchSuf, "", 3}, | |
| 1613 | + {"rename-tag" , &gimport.zTagPre , "", &gimport.zTagSuf , "", 3}, | |
| 1614 | + {"rename-rev" , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf , "", 2}, | |
| 1615 | + }, *renOpt = renOpts; | |
| 1616 | + int i; | |
| 1617 | + for( i = 0; i < sizeof(renOpts) / sizeof(*renOpts); ++i, ++renOpt ){ | |
| 1618 | + if( 1 << svnFlag & renOpt->format ){ | |
| 1619 | + const char *zArgument = find_option(renOpt->zOpt, 0, 1); | |
| 1620 | + if( zArgument ){ | |
| 1621 | + const char *sep = strchr(zArgument, '%'); | |
| 1622 | + if( !sep ){ | |
| 1623 | + fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt); | |
| 1624 | + }else if( strchr(sep + 1, '%') ){ | |
| 1625 | + fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt); | |
| 1626 | + } | |
| 1627 | + *renOpt->varPre = fossil_malloc(sep - zArgument + 1); | |
| 1628 | + memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument); | |
| 1629 | + ((char *)*renOpt->varPre)[sep - zArgument] = 0; | |
| 1630 | + *renOpt->varSuf = sep + 1; | |
| 1631 | + }else{ | |
| 1632 | + *renOpt->varPre = renOpt->zDefaultPre; | |
| 1633 | + *renOpt->varSuf = renOpt->zDefaultSuf; | |
| 1634 | + } | |
| 1635 | + } | |
| 1636 | + } | |
| 1637 | + if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ | |
| 1638 | + gimport.zTrunkName = "trunk"; | |
| 1639 | + } | |
| 1546 | 1640 | |
| 1547 | 1641 | if( svnFlag ){ |
| 1548 | - /* Get --svn related options here, so verify_all_options() fail when svn | |
| 1549 | - * only option are specify with --git | |
| 1642 | + /* Get --svn related options here, so verify_all_options() fails when | |
| 1643 | + * svn-only options are specified with --git | |
| 1550 | 1644 | */ |
| 1645 | + const char *zIgnTree; | |
| 1646 | + unsigned nIgnTree = 0; | |
| 1647 | + while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){ | |
| 1648 | + if ( *zIgnTree ){ | |
| 1649 | + gsvn.azIgnTree = fossil_realloc(gsvn.azIgnTree, | |
| 1650 | + sizeof(*gsvn.azIgnTree) * (nIgnTree + 2)); | |
| 1651 | + gsvn.azIgnTree[nIgnTree++] = zIgnTree; | |
| 1652 | + gsvn.azIgnTree[nIgnTree] = 0; | |
| 1653 | + } | |
| 1654 | + } | |
| 1551 | 1655 | zBase = find_option("base", 0, 1); |
| 1552 | 1656 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1553 | 1657 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1554 | 1658 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1555 | 1659 | gsvn.zTags = find_option("tags", 0, 1); |
| 1556 | - gsvn.incrFlag = incrFlag; | |
| 1660 | + gsvn.revFlag = find_option("rev-tags", 0, 0) | |
| 1661 | + || (incrFlag && !find_option("no-rev-tags", 0, 0)); | |
| 1557 | 1662 | }else if( gitFlag ){ |
| 1558 | 1663 | markfile_in = find_option("import-marks", 0, 1); |
| 1559 | 1664 | markfile_out = find_option("export-marks", 0, 1); |
| 1560 | 1665 | } |
| 1561 | 1666 | verify_all_options(); |
| 1562 | 1667 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -34,10 +34,20 @@ | |
| 34 | char isExe; /* True if executable */ |
| 35 | char isLink; /* True if symlink */ |
| 36 | }; |
| 37 | #endif |
| 38 | |
| 39 | |
| 40 | /* |
| 41 | ** State information about an on-going fast-import parse. |
| 42 | */ |
| 43 | static struct { |
| @@ -198,11 +208,12 @@ | |
| 198 | static void finish_tag(void){ |
| 199 | Blob record, cksum; |
| 200 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 201 | blob_zero(&record); |
| 202 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 203 | blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); |
| 204 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 205 | md5sum_blob(&record, &cksum); |
| 206 | blob_appendf(&record, "Z %b\n", &cksum); |
| 207 | fast_insert_content(&record, 0, 0, 1); |
| 208 | blob_reset(&cksum); |
| @@ -275,18 +286,21 @@ | |
| 275 | |
| 276 | /* Add the required "T" cards to the manifest. Make sure they are added |
| 277 | ** in sorted order and without any duplicates. Otherwise, fossil will not |
| 278 | ** recognize the document as a valid manifest. */ |
| 279 | if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 280 | aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch); |
| 281 | aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch); |
| 282 | if( zFromBranch ){ |
| 283 | aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch); |
| 284 | } |
| 285 | } |
| 286 | if( gg.zFrom==0 ){ |
| 287 | aTCard[nTCard++] = mprintf("T *sym-trunk *\n"); |
| 288 | } |
| 289 | qsort(aTCard, nTCard, sizeof(char *), string_cmp); |
| 290 | for(i=0; i<nTCard; i++){ |
| 291 | if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ |
| 292 | blob_appendf(&record, "%s", aTCard[i]); |
| @@ -313,11 +327,12 @@ | |
| 313 | ** This behavior seems like a bug in git-fast-export, but it is easier |
| 314 | ** to work around the problem than to fix git-fast-export. |
| 315 | */ |
| 316 | if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 317 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 318 | blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin); |
| 319 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 320 | md5sum_blob(&record, &cksum); |
| 321 | blob_appendf(&record, "Z %b\n", &cksum); |
| 322 | db_multi_exec( |
| 323 | "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| @@ -749,11 +764,14 @@ | |
| 749 | const char *zBranches; /* Name of branches folder in repo root */ |
| 750 | int lenBranches; /* String length of zBranches */ |
| 751 | const char *zTags; /* Name of tags folder in repo root */ |
| 752 | int lenTags; /* String length of zTags */ |
| 753 | Bag newBranches; /* Branches that were created in this revision */ |
| 754 | int incrFlag; /* Add svn-rev-nn tags on every checkin */ |
| 755 | } gsvn; |
| 756 | typedef struct { |
| 757 | char *zKey; |
| 758 | char *zVal; |
| 759 | } KeyVal; |
| @@ -968,15 +986,17 @@ | |
| 968 | " WHERE trev<%d AND tbranch=%d", |
| 969 | gsvn.rev, branchId); |
| 970 | } |
| 971 | if( parentRid>0 ){ |
| 972 | pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 973 | pParentFile = manifest_file_next(pParentManifest, 0); |
| 974 | parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", |
| 975 | parentRid); |
| 976 | if( parentBranch!=branchId && branchType!=SVN_TAG ){ |
| 977 | sameAsParent = 0; |
| 978 | } |
| 979 | } |
| 980 | if( mergeRid<MAX_INT_32 ){ |
| 981 | if( gsvn.zComment ){ |
| 982 | blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| @@ -1017,47 +1037,57 @@ | |
| 1017 | char *zParentBranch = |
| 1018 | db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1019 | parentBranch |
| 1020 | ); |
| 1021 | blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1022 | blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 1023 | blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 1024 | if( gsvn.incrFlag ){ |
| 1025 | blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1026 | } |
| 1027 | blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch); |
| 1028 | fossil_free(zParentBranch); |
| 1029 | }else{ |
| 1030 | char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1031 | blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1032 | if( gsvn.incrFlag ){ |
| 1033 | blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1034 | } |
| 1035 | fossil_free(zMergeUuid); |
| 1036 | } |
| 1037 | fossil_free(zParentUuid); |
| 1038 | }else{ |
| 1039 | blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 1040 | blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 1041 | if( gsvn.incrFlag ){ |
| 1042 | blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1043 | } |
| 1044 | } |
| 1045 | }else if( branchType==SVN_TAG ){ |
| 1046 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1047 | blob_reset(&manifest); |
| 1048 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1049 | blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid); |
| 1050 | fossil_free(zParentUuid); |
| 1051 | } |
| 1052 | }else{ |
| 1053 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1054 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1055 | if( branchType!=SVN_TAG ){ |
| 1056 | blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1057 | }else{ |
| 1058 | blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid); |
| 1059 | } |
| 1060 | fossil_free(zParentUuid); |
| 1061 | } |
| 1062 | if( gsvn.zUser ){ |
| 1063 | blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| @@ -1152,14 +1182,27 @@ | |
| 1152 | } |
| 1153 | } |
| 1154 | |
| 1155 | /* |
| 1156 | ** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1157 | */ |
| 1158 | static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1159 | char *zBranch = 0; |
| 1160 | int branchId = 0; |
| 1161 | *type = SVN_UNKNOWN; |
| 1162 | *zFile = 0; |
| 1163 | if( gsvn.lenTrunk==0 ){ |
| 1164 | zBranch = "trunk"; |
| 1165 | *zFile = zPath; |
| @@ -1433,11 +1476,11 @@ | |
| 1433 | if( strncmp(zAction, "change", 6)==0 ){ |
| 1434 | int rid = 0; |
| 1435 | if( zKind==0 ){ |
| 1436 | fossil_fatal("Missing Node-kind"); |
| 1437 | } |
| 1438 | if( strncmp(zKind, "dir", 3)!=0 ){ |
| 1439 | if( deltaFlag ){ |
| 1440 | Blob deltaSrc; |
| 1441 | Blob target; |
| 1442 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1443 | " SELECT tuuid FROM xfiles" |
| @@ -1497,31 +1540,48 @@ | |
| 1497 | ** --git Import from the git-fast-export file format (default) |
| 1498 | ** Options: |
| 1499 | ** --import-marks FILE Restore marks table from FILE |
| 1500 | ** --export-marks FILE Save marks table to FILE |
| 1501 | ** |
| 1502 | ** --svn Import from the svnadmin-dump file format. The default |
| 1503 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1504 | ** folders in the SVN root as special, following the |
| 1505 | ** common layout of SVN repositories. These are (by |
| 1506 | ** default) trunk/, branches/ and tags/ |
| 1507 | ** Options: |
| 1508 | ** --trunk FOLDER Name of trunk folder |
| 1509 | ** --branches FOLDER Name of branches folder |
| 1510 | ** --tags FOLDER Name of tags folder |
| 1511 | ** --base PATH Path to project root in repository |
| 1512 | ** --flat The whole dump is a single branch |
| 1513 | ** |
| 1514 | ** Common Options: |
| 1515 | ** -i|--incremental allow importing into an existing repository |
| 1516 | ** -f|--force overwrite repository if already exists |
| 1517 | ** -q|--quiet omit progress output |
| 1518 | ** --no-rebuild skip the "rebuilding metadata" step |
| 1519 | ** --no-vacuum skip the final VACUUM of the database file |
| 1520 | ** |
| 1521 | ** The --incremental option allows an existing repository to be extended |
| 1522 | ** with new content. |
| 1523 | ** |
| 1524 | ** See also: export |
| 1525 | */ |
| 1526 | void import_cmd(void){ |
| 1527 | char *zPassword; |
| @@ -1541,21 +1601,66 @@ | |
| 1541 | int flatFlag = 0; |
| 1542 | |
| 1543 | /* Options for --git only */ |
| 1544 | const char *markfile_in = 0; |
| 1545 | const char *markfile_out = 0; |
| 1546 | |
| 1547 | if( svnFlag ){ |
| 1548 | /* Get --svn related options here, so verify_all_options() fail when svn |
| 1549 | * only option are specify with --git |
| 1550 | */ |
| 1551 | zBase = find_option("base", 0, 1); |
| 1552 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1553 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1554 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1555 | gsvn.zTags = find_option("tags", 0, 1); |
| 1556 | gsvn.incrFlag = incrFlag; |
| 1557 | }else if( gitFlag ){ |
| 1558 | markfile_in = find_option("import-marks", 0, 1); |
| 1559 | markfile_out = find_option("export-marks", 0, 1); |
| 1560 | } |
| 1561 | verify_all_options(); |
| 1562 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -34,10 +34,20 @@ | |
| 34 | char isExe; /* True if executable */ |
| 35 | char isLink; /* True if symlink */ |
| 36 | }; |
| 37 | #endif |
| 38 | |
| 39 | /* |
| 40 | ** State information common to all import types. |
| 41 | */ |
| 42 | static struct { |
| 43 | const char *zTrunkName; /* Name of trunk branch */ |
| 44 | const char *zBranchPre; /* Prepended to non-trunk branch names */ |
| 45 | const char *zBranchSuf; /* Appended to non-trunk branch names */ |
| 46 | const char *zTagPre; /* Prepended to non-trunk tag names */ |
| 47 | const char *zTagSuf; /* Appended to non-trunk tag names */ |
| 48 | } gimport; |
| 49 | |
| 50 | /* |
| 51 | ** State information about an on-going fast-import parse. |
| 52 | */ |
| 53 | static struct { |
| @@ -198,11 +208,12 @@ | |
| 208 | static void finish_tag(void){ |
| 209 | Blob record, cksum; |
| 210 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 211 | blob_zero(&record); |
| 212 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 213 | blob_appendf(&record, "T +%F%F%F %s\n", gimport.zTagPre, gg.zTag, |
| 214 | gimport.zTagSuf, gg.zFrom); |
| 215 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 216 | md5sum_blob(&record, &cksum); |
| 217 | blob_appendf(&record, "Z %b\n", &cksum); |
| 218 | fast_insert_content(&record, 0, 0, 1); |
| 219 | blob_reset(&cksum); |
| @@ -275,18 +286,21 @@ | |
| 286 | |
| 287 | /* Add the required "T" cards to the manifest. Make sure they are added |
| 288 | ** in sorted order and without any duplicates. Otherwise, fossil will not |
| 289 | ** recognize the document as a valid manifest. */ |
| 290 | if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 291 | aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre, |
| 292 | gg.zBranch, gimport.zBranchSuf); |
| 293 | aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre, |
| 294 | gg.zBranch, gimport.zBranchSuf); |
| 295 | if( zFromBranch ){ |
| 296 | aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre, |
| 297 | zFromBranch, gimport.zBranchSuf); |
| 298 | } |
| 299 | } |
| 300 | if( gg.zFrom==0 ){ |
| 301 | aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName); |
| 302 | } |
| 303 | qsort(aTCard, nTCard, sizeof(char *), string_cmp); |
| 304 | for(i=0; i<nTCard; i++){ |
| 305 | if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ |
| 306 | blob_appendf(&record, "%s", aTCard[i]); |
| @@ -313,11 +327,12 @@ | |
| 327 | ** This behavior seems like a bug in git-fast-export, but it is easier |
| 328 | ** to work around the problem than to fix git-fast-export. |
| 329 | */ |
| 330 | if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 331 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 332 | blob_appendf(&record, "T +sym-%F%F%F %s\n", gimport.zBranchPre, gg.zBranch, |
| 333 | gimport.zBranchSuf, gg.zPrevCheckin); |
| 334 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 335 | md5sum_blob(&record, &cksum); |
| 336 | blob_appendf(&record, "Z %b\n", &cksum); |
| 337 | db_multi_exec( |
| 338 | "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| @@ -749,11 +764,14 @@ | |
| 764 | const char *zBranches; /* Name of branches folder in repo root */ |
| 765 | int lenBranches; /* String length of zBranches */ |
| 766 | const char *zTags; /* Name of tags folder in repo root */ |
| 767 | int lenTags; /* String length of zTags */ |
| 768 | Bag newBranches; /* Branches that were created in this revision */ |
| 769 | int revFlag; /* Add svn-rev-nn tags on every checkin */ |
| 770 | const char *zRevPre; /* Prepended to revision tag names */ |
| 771 | const char *zRevSuf; /* Appended to revision tag names */ |
| 772 | const char **azIgnTree; /* NULL-terminated list of dirs to ignore */ |
| 773 | } gsvn; |
| 774 | typedef struct { |
| 775 | char *zKey; |
| 776 | char *zVal; |
| 777 | } KeyVal; |
| @@ -968,15 +986,17 @@ | |
| 986 | " WHERE trev<%d AND tbranch=%d", |
| 987 | gsvn.rev, branchId); |
| 988 | } |
| 989 | if( parentRid>0 ){ |
| 990 | pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 991 | if( pParentManifest ){ |
| 992 | pParentFile = manifest_file_next(pParentManifest, 0); |
| 993 | parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", |
| 994 | parentRid); |
| 995 | if( parentBranch!=branchId && branchType!=SVN_TAG ){ |
| 996 | sameAsParent = 0; |
| 997 | } |
| 998 | } |
| 999 | } |
| 1000 | if( mergeRid<MAX_INT_32 ){ |
| 1001 | if( gsvn.zComment ){ |
| 1002 | blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| @@ -1017,47 +1037,57 @@ | |
| 1037 | char *zParentBranch = |
| 1038 | db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1039 | parentBranch |
| 1040 | ); |
| 1041 | blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1042 | blob_appendf(&manifest, "T *branch * %F%F%F\n", gimport.zBranchPre, |
| 1043 | zBranch, gimport.zBranchSuf); |
| 1044 | blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, |
| 1045 | zBranch, gimport.zBranchSuf); |
| 1046 | if( gsvn.revFlag ){ |
| 1047 | blob_appendf(&manifest, "T +sym-%Fr%d%F *\n", gimport.zTagPre, |
| 1048 | gsvn.rev, gimport.zTagSuf); |
| 1049 | } |
| 1050 | blob_appendf(&manifest, "T -sym-%F%F%F *\n", gimport.zBranchPre, |
| 1051 | zParentBranch, gimport.zBranchSuf); |
| 1052 | fossil_free(zParentBranch); |
| 1053 | }else{ |
| 1054 | char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1055 | blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1056 | if( gsvn.revFlag ){ |
| 1057 | blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, |
| 1058 | gsvn.rev, gsvn.zRevSuf); |
| 1059 | } |
| 1060 | fossil_free(zMergeUuid); |
| 1061 | } |
| 1062 | fossil_free(zParentUuid); |
| 1063 | }else{ |
| 1064 | blob_appendf(&manifest, "T *branch * %F%F%F\n", |
| 1065 | gimport.zBranchPre, zBranch, gimport.zBranchSuf); |
| 1066 | blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, |
| 1067 | zBranch, gimport.zBranchSuf); |
| 1068 | if( gsvn.revFlag ){ |
| 1069 | blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev, |
| 1070 | gsvn.zRevSuf); |
| 1071 | } |
| 1072 | } |
| 1073 | }else if( branchType==SVN_TAG ){ |
| 1074 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1075 | blob_reset(&manifest); |
| 1076 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1077 | blob_appendf(&manifest, "T +sym-%F%F%F %s\n", gimport.zTagPre, zBranch, |
| 1078 | gimport.zTagSuf, zParentUuid); |
| 1079 | fossil_free(zParentUuid); |
| 1080 | } |
| 1081 | }else{ |
| 1082 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1083 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1084 | if( branchType!=SVN_TAG ){ |
| 1085 | blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1086 | }else{ |
| 1087 | blob_appendf(&manifest, "T -sym-%F%F%F %s\n", gimport.zBranchPre, |
| 1088 | zBranch, gimport.zBranchSuf, zParentUuid); |
| 1089 | } |
| 1090 | fossil_free(zParentUuid); |
| 1091 | } |
| 1092 | if( gsvn.zUser ){ |
| 1093 | blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| @@ -1152,14 +1182,27 @@ | |
| 1182 | } |
| 1183 | } |
| 1184 | |
| 1185 | /* |
| 1186 | ** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1187 | ** Return 0 if not a branch, tag, or trunk, or if ignored by --ignore-tree. |
| 1188 | */ |
| 1189 | static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1190 | char *zBranch = 0; |
| 1191 | int branchId = 0; |
| 1192 | if( gsvn.azIgnTree ){ |
| 1193 | const char **pzIgnTree; |
| 1194 | unsigned nPath = strlen(zPath); |
| 1195 | for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){ |
| 1196 | const char *zIgn = *pzIgnTree; |
| 1197 | int nIgn = strlen(zIgn); |
| 1198 | if( strncmp(zPath, zIgn, nIgn) == 0 |
| 1199 | && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){ |
| 1200 | return 0; |
| 1201 | } |
| 1202 | } |
| 1203 | } |
| 1204 | *type = SVN_UNKNOWN; |
| 1205 | *zFile = 0; |
| 1206 | if( gsvn.lenTrunk==0 ){ |
| 1207 | zBranch = "trunk"; |
| 1208 | *zFile = zPath; |
| @@ -1433,11 +1476,11 @@ | |
| 1476 | if( strncmp(zAction, "change", 6)==0 ){ |
| 1477 | int rid = 0; |
| 1478 | if( zKind==0 ){ |
| 1479 | fossil_fatal("Missing Node-kind"); |
| 1480 | } |
| 1481 | if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){ |
| 1482 | if( deltaFlag ){ |
| 1483 | Blob deltaSrc; |
| 1484 | Blob target; |
| 1485 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1486 | " SELECT tuuid FROM xfiles" |
| @@ -1497,31 +1540,48 @@ | |
| 1540 | ** --git Import from the git-fast-export file format (default) |
| 1541 | ** Options: |
| 1542 | ** --import-marks FILE Restore marks table from FILE |
| 1543 | ** --export-marks FILE Save marks table to FILE |
| 1544 | ** |
| 1545 | ** --svn Import from the svnadmin-dump file format. The default |
| 1546 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1547 | ** folders in the SVN root as special, following the |
| 1548 | ** common layout of SVN repositories. These are (by |
| 1549 | ** default) trunk/, branches/ and tags/. The SVN --deltas |
| 1550 | ** format is supported but not required. |
| 1551 | ** Options: |
| 1552 | ** --trunk FOLDER Name of trunk folder |
| 1553 | ** --branches FOLDER Name of branches folder |
| 1554 | ** --tags FOLDER Name of tags folder |
| 1555 | ** --base PATH Path to project root in repository |
| 1556 | ** --flat The whole dump is a single branch |
| 1557 | ** --rev-tags Tag each revision, implied by -i |
| 1558 | ** --no-rev-tags Disables tagging effect of -i |
| 1559 | ** --rename-rev PAT Rev tag names, default "svn-rev-%" |
| 1560 | ** --ignore-tree DIR Ignores subtree rooted at DIR |
| 1561 | ** |
| 1562 | ** Common Options: |
| 1563 | ** -i|--incremental allow importing into an existing repository |
| 1564 | ** -f|--force overwrite repository if already exists |
| 1565 | ** -q|--quiet omit progress output |
| 1566 | ** --no-rebuild skip the "rebuilding metadata" step |
| 1567 | ** --no-vacuum skip the final VACUUM of the database file |
| 1568 | ** --rename-trunk NAME use NAME as name of imported trunk branch |
| 1569 | ** --rename-branch PAT rename all branch names using PAT pattern |
| 1570 | ** --rename-tag PAT rename all tag names using PAT pattern |
| 1571 | ** |
| 1572 | ** The --incremental option allows an existing repository to be extended |
| 1573 | ** with new content. The --rename-* options may be useful to avoid name |
| 1574 | ** conflicts when using the --incremental option. |
| 1575 | ** |
| 1576 | ** The argument to --rename-* contains one "%" character to be replaced |
| 1577 | ** with the original name. For example, "--rename-tag svn-%-tag" renames |
| 1578 | ** the tag called "release" to "svn-release-tag". |
| 1579 | ** |
| 1580 | ** --ignore-tree is useful for importing Subversion repositories which |
| 1581 | ** move branches to subdirectories of "branches/deleted" instead of |
| 1582 | ** deleting them. It can be supplied multiple times if necessary. |
| 1583 | ** |
| 1584 | ** See also: export |
| 1585 | */ |
| 1586 | void import_cmd(void){ |
| 1587 | char *zPassword; |
| @@ -1541,21 +1601,66 @@ | |
| 1601 | int flatFlag = 0; |
| 1602 | |
| 1603 | /* Options for --git only */ |
| 1604 | const char *markfile_in = 0; |
| 1605 | const char *markfile_out = 0; |
| 1606 | |
| 1607 | /* Interpret --rename-* options. Use a table to avoid code duplication. */ |
| 1608 | const struct { |
| 1609 | const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf; |
| 1610 | int format; /* 1=git, 2=svn, 3=any */ |
| 1611 | } renOpts[] = { |
| 1612 | {"rename-branch", &gimport.zBranchPre, "", &gimport.zBranchSuf, "", 3}, |
| 1613 | {"rename-tag" , &gimport.zTagPre , "", &gimport.zTagSuf , "", 3}, |
| 1614 | {"rename-rev" , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf , "", 2}, |
| 1615 | }, *renOpt = renOpts; |
| 1616 | int i; |
| 1617 | for( i = 0; i < sizeof(renOpts) / sizeof(*renOpts); ++i, ++renOpt ){ |
| 1618 | if( 1 << svnFlag & renOpt->format ){ |
| 1619 | const char *zArgument = find_option(renOpt->zOpt, 0, 1); |
| 1620 | if( zArgument ){ |
| 1621 | const char *sep = strchr(zArgument, '%'); |
| 1622 | if( !sep ){ |
| 1623 | fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt); |
| 1624 | }else if( strchr(sep + 1, '%') ){ |
| 1625 | fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt); |
| 1626 | } |
| 1627 | *renOpt->varPre = fossil_malloc(sep - zArgument + 1); |
| 1628 | memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument); |
| 1629 | ((char *)*renOpt->varPre)[sep - zArgument] = 0; |
| 1630 | *renOpt->varSuf = sep + 1; |
| 1631 | }else{ |
| 1632 | *renOpt->varPre = renOpt->zDefaultPre; |
| 1633 | *renOpt->varSuf = renOpt->zDefaultSuf; |
| 1634 | } |
| 1635 | } |
| 1636 | } |
| 1637 | if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ |
| 1638 | gimport.zTrunkName = "trunk"; |
| 1639 | } |
| 1640 | |
| 1641 | if( svnFlag ){ |
| 1642 | /* Get --svn related options here, so verify_all_options() fails when |
| 1643 | * svn-only options are specified with --git |
| 1644 | */ |
| 1645 | const char *zIgnTree; |
| 1646 | unsigned nIgnTree = 0; |
| 1647 | while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){ |
| 1648 | if ( *zIgnTree ){ |
| 1649 | gsvn.azIgnTree = fossil_realloc(gsvn.azIgnTree, |
| 1650 | sizeof(*gsvn.azIgnTree) * (nIgnTree + 2)); |
| 1651 | gsvn.azIgnTree[nIgnTree++] = zIgnTree; |
| 1652 | gsvn.azIgnTree[nIgnTree] = 0; |
| 1653 | } |
| 1654 | } |
| 1655 | zBase = find_option("base", 0, 1); |
| 1656 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1657 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1658 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1659 | gsvn.zTags = find_option("tags", 0, 1); |
| 1660 | gsvn.revFlag = find_option("rev-tags", 0, 0) |
| 1661 | || (incrFlag && !find_option("no-rev-tags", 0, 0)); |
| 1662 | }else if( gitFlag ){ |
| 1663 | markfile_in = find_option("import-marks", 0, 1); |
| 1664 | markfile_out = find_option("export-marks", 0, 1); |
| 1665 | } |
| 1666 | verify_all_options(); |
| 1667 |
+144
-39
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -34,10 +34,20 @@ | ||
| 34 | 34 | char isExe; /* True if executable */ |
| 35 | 35 | char isLink; /* True if symlink */ |
| 36 | 36 | }; |
| 37 | 37 | #endif |
| 38 | 38 | |
| 39 | +/* | |
| 40 | +** State information common to all import types. | |
| 41 | +*/ | |
| 42 | +static struct { | |
| 43 | + const char *zTrunkName; /* Name of trunk branch */ | |
| 44 | + const char *zBranchPre; /* Prepended to non-trunk branch names */ | |
| 45 | + const char *zBranchSuf; /* Appended to non-trunk branch names */ | |
| 46 | + const char *zTagPre; /* Prepended to non-trunk tag names */ | |
| 47 | + const char *zTagSuf; /* Appended to non-trunk tag names */ | |
| 48 | +} gimport; | |
| 39 | 49 | |
| 40 | 50 | /* |
| 41 | 51 | ** State information about an on-going fast-import parse. |
| 42 | 52 | */ |
| 43 | 53 | static struct { |
| @@ -198,11 +208,12 @@ | ||
| 198 | 208 | static void finish_tag(void){ |
| 199 | 209 | Blob record, cksum; |
| 200 | 210 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 201 | 211 | blob_zero(&record); |
| 202 | 212 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 203 | - blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); | |
| 213 | + blob_appendf(&record, "T +%F%F%F %s\n", gimport.zTagPre, gg.zTag, | |
| 214 | + gimport.zTagSuf, gg.zFrom); | |
| 204 | 215 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 205 | 216 | md5sum_blob(&record, &cksum); |
| 206 | 217 | blob_appendf(&record, "Z %b\n", &cksum); |
| 207 | 218 | fast_insert_content(&record, 0, 0, 1); |
| 208 | 219 | blob_reset(&cksum); |
| @@ -275,18 +286,21 @@ | ||
| 275 | 286 | |
| 276 | 287 | /* Add the required "T" cards to the manifest. Make sure they are added |
| 277 | 288 | ** in sorted order and without any duplicates. Otherwise, fossil will not |
| 278 | 289 | ** recognize the document as a valid manifest. */ |
| 279 | 290 | if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 280 | - aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch); | |
| 281 | - aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch); | |
| 291 | + aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre, | |
| 292 | + gg.zBranch, gimport.zBranchSuf); | |
| 293 | + aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre, | |
| 294 | + gg.zBranch, gimport.zBranchSuf); | |
| 282 | 295 | if( zFromBranch ){ |
| 283 | - aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch); | |
| 296 | + aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre, | |
| 297 | + zFromBranch, gimport.zBranchSuf); | |
| 284 | 298 | } |
| 285 | 299 | } |
| 286 | 300 | if( gg.zFrom==0 ){ |
| 287 | - aTCard[nTCard++] = mprintf("T *sym-trunk *\n"); | |
| 301 | + aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName); | |
| 288 | 302 | } |
| 289 | 303 | qsort(aTCard, nTCard, sizeof(char *), string_cmp); |
| 290 | 304 | for(i=0; i<nTCard; i++){ |
| 291 | 305 | if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ |
| 292 | 306 | blob_appendf(&record, "%s", aTCard[i]); |
| @@ -313,11 +327,12 @@ | ||
| 313 | 327 | ** This behavior seems like a bug in git-fast-export, but it is easier |
| 314 | 328 | ** to work around the problem than to fix git-fast-export. |
| 315 | 329 | */ |
| 316 | 330 | if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 317 | 331 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 318 | - blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin); | |
| 332 | + blob_appendf(&record, "T +sym-%F%F%F %s\n", gimport.zBranchPre, gg.zBranch, | |
| 333 | + gimport.zBranchSuf, gg.zPrevCheckin); | |
| 319 | 334 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 320 | 335 | md5sum_blob(&record, &cksum); |
| 321 | 336 | blob_appendf(&record, "Z %b\n", &cksum); |
| 322 | 337 | db_multi_exec( |
| 323 | 338 | "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| @@ -749,11 +764,14 @@ | ||
| 749 | 764 | const char *zBranches; /* Name of branches folder in repo root */ |
| 750 | 765 | int lenBranches; /* String length of zBranches */ |
| 751 | 766 | const char *zTags; /* Name of tags folder in repo root */ |
| 752 | 767 | int lenTags; /* String length of zTags */ |
| 753 | 768 | Bag newBranches; /* Branches that were created in this revision */ |
| 754 | - int incrFlag; /* Add svn-rev-nn tags on every checkin */ | |
| 769 | + int revFlag; /* Add svn-rev-nn tags on every checkin */ | |
| 770 | + const char *zRevPre; /* Prepended to revision tag names */ | |
| 771 | + const char *zRevSuf; /* Appended to revision tag names */ | |
| 772 | + const char **azIgnTree; /* NULL-terminated list of dirs to ignore */ | |
| 755 | 773 | } gsvn; |
| 756 | 774 | typedef struct { |
| 757 | 775 | char *zKey; |
| 758 | 776 | char *zVal; |
| 759 | 777 | } KeyVal; |
| @@ -968,15 +986,17 @@ | ||
| 968 | 986 | " WHERE trev<%d AND tbranch=%d", |
| 969 | 987 | gsvn.rev, branchId); |
| 970 | 988 | } |
| 971 | 989 | if( parentRid>0 ){ |
| 972 | 990 | pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 973 | - pParentFile = manifest_file_next(pParentManifest, 0); | |
| 974 | - parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", | |
| 975 | - parentRid); | |
| 976 | - if( parentBranch!=branchId && branchType!=SVN_TAG ){ | |
| 977 | - sameAsParent = 0; | |
| 991 | + if( pParentManifest ){ | |
| 992 | + pParentFile = manifest_file_next(pParentManifest, 0); | |
| 993 | + parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", | |
| 994 | + parentRid); | |
| 995 | + if( parentBranch!=branchId && branchType!=SVN_TAG ){ | |
| 996 | + sameAsParent = 0; | |
| 997 | + } | |
| 978 | 998 | } |
| 979 | 999 | } |
| 980 | 1000 | if( mergeRid<MAX_INT_32 ){ |
| 981 | 1001 | if( gsvn.zComment ){ |
| 982 | 1002 | blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| @@ -1017,47 +1037,57 @@ | ||
| 1017 | 1037 | char *zParentBranch = |
| 1018 | 1038 | db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1019 | 1039 | parentBranch |
| 1020 | 1040 | ); |
| 1021 | 1041 | blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1022 | - blob_appendf(&manifest, "T *branch * %F\n", zBranch); | |
| 1023 | - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); | |
| 1024 | - if( gsvn.incrFlag ){ | |
| 1025 | - blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); | |
| 1042 | + blob_appendf(&manifest, "T *branch * %F%F%F\n", gimport.zBranchPre, | |
| 1043 | + zBranch, gimport.zBranchSuf); | |
| 1044 | + blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, | |
| 1045 | + zBranch, gimport.zBranchSuf); | |
| 1046 | + if( gsvn.revFlag ){ | |
| 1047 | + blob_appendf(&manifest, "T +sym-%Fr%d%F *\n", gimport.zTagPre, | |
| 1048 | + gsvn.rev, gimport.zTagSuf); | |
| 1026 | 1049 | } |
| 1027 | - blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch); | |
| 1050 | + blob_appendf(&manifest, "T -sym-%F%F%F *\n", gimport.zBranchPre, | |
| 1051 | + zParentBranch, gimport.zBranchSuf); | |
| 1028 | 1052 | fossil_free(zParentBranch); |
| 1029 | 1053 | }else{ |
| 1030 | 1054 | char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1031 | 1055 | blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1032 | - if( gsvn.incrFlag ){ | |
| 1033 | - blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); | |
| 1056 | + if( gsvn.revFlag ){ | |
| 1057 | + blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, | |
| 1058 | + gsvn.rev, gsvn.zRevSuf); | |
| 1034 | 1059 | } |
| 1035 | 1060 | fossil_free(zMergeUuid); |
| 1036 | 1061 | } |
| 1037 | 1062 | fossil_free(zParentUuid); |
| 1038 | 1063 | }else{ |
| 1039 | - blob_appendf(&manifest, "T *branch * %F\n", zBranch); | |
| 1040 | - blob_appendf(&manifest, "T *sym-%F *\n", zBranch); | |
| 1041 | - if( gsvn.incrFlag ){ | |
| 1042 | - blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); | |
| 1064 | + blob_appendf(&manifest, "T *branch * %F%F%F\n", | |
| 1065 | + gimport.zBranchPre, zBranch, gimport.zBranchSuf); | |
| 1066 | + blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, | |
| 1067 | + zBranch, gimport.zBranchSuf); | |
| 1068 | + if( gsvn.revFlag ){ | |
| 1069 | + blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev, | |
| 1070 | + gsvn.zRevSuf); | |
| 1043 | 1071 | } |
| 1044 | 1072 | } |
| 1045 | 1073 | }else if( branchType==SVN_TAG ){ |
| 1046 | 1074 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1047 | 1075 | blob_reset(&manifest); |
| 1048 | 1076 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1049 | - blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid); | |
| 1077 | + blob_appendf(&manifest, "T +sym-%F%F%F %s\n", gimport.zTagPre, zBranch, | |
| 1078 | + gimport.zTagSuf, zParentUuid); | |
| 1050 | 1079 | fossil_free(zParentUuid); |
| 1051 | 1080 | } |
| 1052 | 1081 | }else{ |
| 1053 | 1082 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1054 | 1083 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1055 | 1084 | if( branchType!=SVN_TAG ){ |
| 1056 | 1085 | blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1057 | 1086 | }else{ |
| 1058 | - blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid); | |
| 1087 | + blob_appendf(&manifest, "T -sym-%F%F%F %s\n", gimport.zBranchPre, | |
| 1088 | + zBranch, gimport.zBranchSuf, zParentUuid); | |
| 1059 | 1089 | } |
| 1060 | 1090 | fossil_free(zParentUuid); |
| 1061 | 1091 | } |
| 1062 | 1092 | if( gsvn.zUser ){ |
| 1063 | 1093 | blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| @@ -1152,14 +1182,27 @@ | ||
| 1152 | 1182 | } |
| 1153 | 1183 | } |
| 1154 | 1184 | |
| 1155 | 1185 | /* |
| 1156 | 1186 | ** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1157 | - */ | |
| 1187 | +** Return 0 if not a branch, tag, or trunk, or if ignored by --ignore-tree. | |
| 1188 | +*/ | |
| 1158 | 1189 | static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1159 | 1190 | char *zBranch = 0; |
| 1160 | 1191 | int branchId = 0; |
| 1192 | + if( gsvn.azIgnTree ){ | |
| 1193 | + const char **pzIgnTree; | |
| 1194 | + unsigned nPath = strlen(zPath); | |
| 1195 | + for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){ | |
| 1196 | + const char *zIgn = *pzIgnTree; | |
| 1197 | + int nIgn = strlen(zIgn); | |
| 1198 | + if( strncmp(zPath, zIgn, nIgn) == 0 | |
| 1199 | + && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){ | |
| 1200 | + return 0; | |
| 1201 | + } | |
| 1202 | + } | |
| 1203 | + } | |
| 1161 | 1204 | *type = SVN_UNKNOWN; |
| 1162 | 1205 | *zFile = 0; |
| 1163 | 1206 | if( gsvn.lenTrunk==0 ){ |
| 1164 | 1207 | zBranch = "trunk"; |
| 1165 | 1208 | *zFile = zPath; |
| @@ -1433,11 +1476,11 @@ | ||
| 1433 | 1476 | if( strncmp(zAction, "change", 6)==0 ){ |
| 1434 | 1477 | int rid = 0; |
| 1435 | 1478 | if( zKind==0 ){ |
| 1436 | 1479 | fossil_fatal("Missing Node-kind"); |
| 1437 | 1480 | } |
| 1438 | - if( strncmp(zKind, "dir", 3)!=0 ){ | |
| 1481 | + if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){ | |
| 1439 | 1482 | if( deltaFlag ){ |
| 1440 | 1483 | Blob deltaSrc; |
| 1441 | 1484 | Blob target; |
| 1442 | 1485 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1443 | 1486 | " SELECT tuuid FROM xfiles" |
| @@ -1497,31 +1540,48 @@ | ||
| 1497 | 1540 | ** --git Import from the git-fast-export file format (default) |
| 1498 | 1541 | ** Options: |
| 1499 | 1542 | ** --import-marks FILE Restore marks table from FILE |
| 1500 | 1543 | ** --export-marks FILE Save marks table to FILE |
| 1501 | 1544 | ** |
| 1502 | -** --svn Import from the svnadmin-dump file format. The default | |
| 1545 | +** --svn Import from the svnadmin-dump file format. The default | |
| 1503 | 1546 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1504 | 1547 | ** folders in the SVN root as special, following the |
| 1505 | -** common layout of SVN repositories. These are (by | |
| 1506 | -** default) trunk/, branches/ and tags/ | |
| 1548 | +** common layout of SVN repositories. These are (by | |
| 1549 | +** default) trunk/, branches/ and tags/. The SVN --deltas | |
| 1550 | +** format is supported but not required. | |
| 1507 | 1551 | ** Options: |
| 1508 | 1552 | ** --trunk FOLDER Name of trunk folder |
| 1509 | 1553 | ** --branches FOLDER Name of branches folder |
| 1510 | 1554 | ** --tags FOLDER Name of tags folder |
| 1511 | 1555 | ** --base PATH Path to project root in repository |
| 1512 | 1556 | ** --flat The whole dump is a single branch |
| 1557 | +** --rev-tags Tag each revision, implied by -i | |
| 1558 | +** --no-rev-tags Disables tagging effect of -i | |
| 1559 | +** --rename-rev PAT Rev tag names, default "svn-rev-%" | |
| 1560 | +** --ignore-tree DIR Ignores subtree rooted at DIR | |
| 1513 | 1561 | ** |
| 1514 | 1562 | ** Common Options: |
| 1515 | -** -i|--incremental allow importing into an existing repository | |
| 1516 | -** -f|--force overwrite repository if already exists | |
| 1517 | -** -q|--quiet omit progress output | |
| 1518 | -** --no-rebuild skip the "rebuilding metadata" step | |
| 1519 | -** --no-vacuum skip the final VACUUM of the database file | |
| 1563 | +** -i|--incremental allow importing into an existing repository | |
| 1564 | +** -f|--force overwrite repository if already exists | |
| 1565 | +** -q|--quiet omit progress output | |
| 1566 | +** --no-rebuild skip the "rebuilding metadata" step | |
| 1567 | +** --no-vacuum skip the final VACUUM of the database file | |
| 1568 | +** --rename-trunk NAME use NAME as name of imported trunk branch | |
| 1569 | +** --rename-branch PAT rename all branch names using PAT pattern | |
| 1570 | +** --rename-tag PAT rename all tag names using PAT pattern | |
| 1520 | 1571 | ** |
| 1521 | 1572 | ** The --incremental option allows an existing repository to be extended |
| 1522 | -** with new content. | |
| 1573 | +** with new content. The --rename-* options may be useful to avoid name | |
| 1574 | +** conflicts when using the --incremental option. | |
| 1575 | +** | |
| 1576 | +** The argument to --rename-* contains one "%" character to be replaced | |
| 1577 | +** with the original name. For example, "--rename-tag svn-%-tag" renames | |
| 1578 | +** the tag called "release" to "svn-release-tag". | |
| 1579 | +** | |
| 1580 | +** --ignore-tree is useful for importing Subversion repositories which | |
| 1581 | +** move branches to subdirectories of "branches/deleted" instead of | |
| 1582 | +** deleting them. It can be supplied multiple times if necessary. | |
| 1523 | 1583 | ** |
| 1524 | 1584 | ** See also: export |
| 1525 | 1585 | */ |
| 1526 | 1586 | void import_cmd(void){ |
| 1527 | 1587 | char *zPassword; |
| @@ -1541,21 +1601,66 @@ | ||
| 1541 | 1601 | int flatFlag = 0; |
| 1542 | 1602 | |
| 1543 | 1603 | /* Options for --git only */ |
| 1544 | 1604 | const char *markfile_in = 0; |
| 1545 | 1605 | const char *markfile_out = 0; |
| 1606 | + | |
| 1607 | + /* Interpret --rename-* options. Use a table to avoid code duplication. */ | |
| 1608 | + const struct { | |
| 1609 | + const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf; | |
| 1610 | + int format; /* 1=git, 2=svn, 3=any */ | |
| 1611 | + } renOpts[] = { | |
| 1612 | + {"rename-branch", &gimport.zBranchPre, "", &gimport.zBranchSuf, "", 3}, | |
| 1613 | + {"rename-tag" , &gimport.zTagPre , "", &gimport.zTagSuf , "", 3}, | |
| 1614 | + {"rename-rev" , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf , "", 2}, | |
| 1615 | + }, *renOpt = renOpts; | |
| 1616 | + int i; | |
| 1617 | + for( i = 0; i < sizeof(renOpts) / sizeof(*renOpts); ++i, ++renOpt ){ | |
| 1618 | + if( 1 << svnFlag & renOpt->format ){ | |
| 1619 | + const char *zArgument = find_option(renOpt->zOpt, 0, 1); | |
| 1620 | + if( zArgument ){ | |
| 1621 | + const char *sep = strchr(zArgument, '%'); | |
| 1622 | + if( !sep ){ | |
| 1623 | + fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt); | |
| 1624 | + }else if( strchr(sep + 1, '%') ){ | |
| 1625 | + fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt); | |
| 1626 | + } | |
| 1627 | + *renOpt->varPre = fossil_malloc(sep - zArgument + 1); | |
| 1628 | + memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument); | |
| 1629 | + ((char *)*renOpt->varPre)[sep - zArgument] = 0; | |
| 1630 | + *renOpt->varSuf = sep + 1; | |
| 1631 | + }else{ | |
| 1632 | + *renOpt->varPre = renOpt->zDefaultPre; | |
| 1633 | + *renOpt->varSuf = renOpt->zDefaultSuf; | |
| 1634 | + } | |
| 1635 | + } | |
| 1636 | + } | |
| 1637 | + if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ | |
| 1638 | + gimport.zTrunkName = "trunk"; | |
| 1639 | + } | |
| 1546 | 1640 | |
| 1547 | 1641 | if( svnFlag ){ |
| 1548 | - /* Get --svn related options here, so verify_all_options() fail when svn | |
| 1549 | - * only option are specify with --git | |
| 1642 | + /* Get --svn related options here, so verify_all_options() fails when | |
| 1643 | + * svn-only options are specified with --git | |
| 1550 | 1644 | */ |
| 1645 | + const char *zIgnTree; | |
| 1646 | + unsigned nIgnTree = 0; | |
| 1647 | + while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){ | |
| 1648 | + if ( *zIgnTree ){ | |
| 1649 | + gsvn.azIgnTree = fossil_realloc(gsvn.azIgnTree, | |
| 1650 | + sizeof(*gsvn.azIgnTree) * (nIgnTree + 2)); | |
| 1651 | + gsvn.azIgnTree[nIgnTree++] = zIgnTree; | |
| 1652 | + gsvn.azIgnTree[nIgnTree] = 0; | |
| 1653 | + } | |
| 1654 | + } | |
| 1551 | 1655 | zBase = find_option("base", 0, 1); |
| 1552 | 1656 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1553 | 1657 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1554 | 1658 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1555 | 1659 | gsvn.zTags = find_option("tags", 0, 1); |
| 1556 | - gsvn.incrFlag = incrFlag; | |
| 1660 | + gsvn.revFlag = find_option("rev-tags", 0, 0) | |
| 1661 | + || (incrFlag && !find_option("no-rev-tags", 0, 0)); | |
| 1557 | 1662 | }else if( gitFlag ){ |
| 1558 | 1663 | markfile_in = find_option("import-marks", 0, 1); |
| 1559 | 1664 | markfile_out = find_option("export-marks", 0, 1); |
| 1560 | 1665 | } |
| 1561 | 1666 | verify_all_options(); |
| 1562 | 1667 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -34,10 +34,20 @@ | |
| 34 | char isExe; /* True if executable */ |
| 35 | char isLink; /* True if symlink */ |
| 36 | }; |
| 37 | #endif |
| 38 | |
| 39 | |
| 40 | /* |
| 41 | ** State information about an on-going fast-import parse. |
| 42 | */ |
| 43 | static struct { |
| @@ -198,11 +208,12 @@ | |
| 198 | static void finish_tag(void){ |
| 199 | Blob record, cksum; |
| 200 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 201 | blob_zero(&record); |
| 202 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 203 | blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); |
| 204 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 205 | md5sum_blob(&record, &cksum); |
| 206 | blob_appendf(&record, "Z %b\n", &cksum); |
| 207 | fast_insert_content(&record, 0, 0, 1); |
| 208 | blob_reset(&cksum); |
| @@ -275,18 +286,21 @@ | |
| 275 | |
| 276 | /* Add the required "T" cards to the manifest. Make sure they are added |
| 277 | ** in sorted order and without any duplicates. Otherwise, fossil will not |
| 278 | ** recognize the document as a valid manifest. */ |
| 279 | if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 280 | aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch); |
| 281 | aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch); |
| 282 | if( zFromBranch ){ |
| 283 | aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch); |
| 284 | } |
| 285 | } |
| 286 | if( gg.zFrom==0 ){ |
| 287 | aTCard[nTCard++] = mprintf("T *sym-trunk *\n"); |
| 288 | } |
| 289 | qsort(aTCard, nTCard, sizeof(char *), string_cmp); |
| 290 | for(i=0; i<nTCard; i++){ |
| 291 | if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ |
| 292 | blob_appendf(&record, "%s", aTCard[i]); |
| @@ -313,11 +327,12 @@ | |
| 313 | ** This behavior seems like a bug in git-fast-export, but it is easier |
| 314 | ** to work around the problem than to fix git-fast-export. |
| 315 | */ |
| 316 | if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 317 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 318 | blob_appendf(&record, "T +sym-%F %s\n", gg.zBranch, gg.zPrevCheckin); |
| 319 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 320 | md5sum_blob(&record, &cksum); |
| 321 | blob_appendf(&record, "Z %b\n", &cksum); |
| 322 | db_multi_exec( |
| 323 | "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| @@ -749,11 +764,14 @@ | |
| 749 | const char *zBranches; /* Name of branches folder in repo root */ |
| 750 | int lenBranches; /* String length of zBranches */ |
| 751 | const char *zTags; /* Name of tags folder in repo root */ |
| 752 | int lenTags; /* String length of zTags */ |
| 753 | Bag newBranches; /* Branches that were created in this revision */ |
| 754 | int incrFlag; /* Add svn-rev-nn tags on every checkin */ |
| 755 | } gsvn; |
| 756 | typedef struct { |
| 757 | char *zKey; |
| 758 | char *zVal; |
| 759 | } KeyVal; |
| @@ -968,15 +986,17 @@ | |
| 968 | " WHERE trev<%d AND tbranch=%d", |
| 969 | gsvn.rev, branchId); |
| 970 | } |
| 971 | if( parentRid>0 ){ |
| 972 | pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 973 | pParentFile = manifest_file_next(pParentManifest, 0); |
| 974 | parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", |
| 975 | parentRid); |
| 976 | if( parentBranch!=branchId && branchType!=SVN_TAG ){ |
| 977 | sameAsParent = 0; |
| 978 | } |
| 979 | } |
| 980 | if( mergeRid<MAX_INT_32 ){ |
| 981 | if( gsvn.zComment ){ |
| 982 | blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| @@ -1017,47 +1037,57 @@ | |
| 1017 | char *zParentBranch = |
| 1018 | db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1019 | parentBranch |
| 1020 | ); |
| 1021 | blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1022 | blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 1023 | blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 1024 | if( gsvn.incrFlag ){ |
| 1025 | blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1026 | } |
| 1027 | blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch); |
| 1028 | fossil_free(zParentBranch); |
| 1029 | }else{ |
| 1030 | char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1031 | blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1032 | if( gsvn.incrFlag ){ |
| 1033 | blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1034 | } |
| 1035 | fossil_free(zMergeUuid); |
| 1036 | } |
| 1037 | fossil_free(zParentUuid); |
| 1038 | }else{ |
| 1039 | blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 1040 | blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 1041 | if( gsvn.incrFlag ){ |
| 1042 | blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1043 | } |
| 1044 | } |
| 1045 | }else if( branchType==SVN_TAG ){ |
| 1046 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1047 | blob_reset(&manifest); |
| 1048 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1049 | blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid); |
| 1050 | fossil_free(zParentUuid); |
| 1051 | } |
| 1052 | }else{ |
| 1053 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1054 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1055 | if( branchType!=SVN_TAG ){ |
| 1056 | blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1057 | }else{ |
| 1058 | blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid); |
| 1059 | } |
| 1060 | fossil_free(zParentUuid); |
| 1061 | } |
| 1062 | if( gsvn.zUser ){ |
| 1063 | blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| @@ -1152,14 +1182,27 @@ | |
| 1152 | } |
| 1153 | } |
| 1154 | |
| 1155 | /* |
| 1156 | ** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1157 | */ |
| 1158 | static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1159 | char *zBranch = 0; |
| 1160 | int branchId = 0; |
| 1161 | *type = SVN_UNKNOWN; |
| 1162 | *zFile = 0; |
| 1163 | if( gsvn.lenTrunk==0 ){ |
| 1164 | zBranch = "trunk"; |
| 1165 | *zFile = zPath; |
| @@ -1433,11 +1476,11 @@ | |
| 1433 | if( strncmp(zAction, "change", 6)==0 ){ |
| 1434 | int rid = 0; |
| 1435 | if( zKind==0 ){ |
| 1436 | fossil_fatal("Missing Node-kind"); |
| 1437 | } |
| 1438 | if( strncmp(zKind, "dir", 3)!=0 ){ |
| 1439 | if( deltaFlag ){ |
| 1440 | Blob deltaSrc; |
| 1441 | Blob target; |
| 1442 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1443 | " SELECT tuuid FROM xfiles" |
| @@ -1497,31 +1540,48 @@ | |
| 1497 | ** --git Import from the git-fast-export file format (default) |
| 1498 | ** Options: |
| 1499 | ** --import-marks FILE Restore marks table from FILE |
| 1500 | ** --export-marks FILE Save marks table to FILE |
| 1501 | ** |
| 1502 | ** --svn Import from the svnadmin-dump file format. The default |
| 1503 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1504 | ** folders in the SVN root as special, following the |
| 1505 | ** common layout of SVN repositories. These are (by |
| 1506 | ** default) trunk/, branches/ and tags/ |
| 1507 | ** Options: |
| 1508 | ** --trunk FOLDER Name of trunk folder |
| 1509 | ** --branches FOLDER Name of branches folder |
| 1510 | ** --tags FOLDER Name of tags folder |
| 1511 | ** --base PATH Path to project root in repository |
| 1512 | ** --flat The whole dump is a single branch |
| 1513 | ** |
| 1514 | ** Common Options: |
| 1515 | ** -i|--incremental allow importing into an existing repository |
| 1516 | ** -f|--force overwrite repository if already exists |
| 1517 | ** -q|--quiet omit progress output |
| 1518 | ** --no-rebuild skip the "rebuilding metadata" step |
| 1519 | ** --no-vacuum skip the final VACUUM of the database file |
| 1520 | ** |
| 1521 | ** The --incremental option allows an existing repository to be extended |
| 1522 | ** with new content. |
| 1523 | ** |
| 1524 | ** See also: export |
| 1525 | */ |
| 1526 | void import_cmd(void){ |
| 1527 | char *zPassword; |
| @@ -1541,21 +1601,66 @@ | |
| 1541 | int flatFlag = 0; |
| 1542 | |
| 1543 | /* Options for --git only */ |
| 1544 | const char *markfile_in = 0; |
| 1545 | const char *markfile_out = 0; |
| 1546 | |
| 1547 | if( svnFlag ){ |
| 1548 | /* Get --svn related options here, so verify_all_options() fail when svn |
| 1549 | * only option are specify with --git |
| 1550 | */ |
| 1551 | zBase = find_option("base", 0, 1); |
| 1552 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1553 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1554 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1555 | gsvn.zTags = find_option("tags", 0, 1); |
| 1556 | gsvn.incrFlag = incrFlag; |
| 1557 | }else if( gitFlag ){ |
| 1558 | markfile_in = find_option("import-marks", 0, 1); |
| 1559 | markfile_out = find_option("export-marks", 0, 1); |
| 1560 | } |
| 1561 | verify_all_options(); |
| 1562 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -34,10 +34,20 @@ | |
| 34 | char isExe; /* True if executable */ |
| 35 | char isLink; /* True if symlink */ |
| 36 | }; |
| 37 | #endif |
| 38 | |
| 39 | /* |
| 40 | ** State information common to all import types. |
| 41 | */ |
| 42 | static struct { |
| 43 | const char *zTrunkName; /* Name of trunk branch */ |
| 44 | const char *zBranchPre; /* Prepended to non-trunk branch names */ |
| 45 | const char *zBranchSuf; /* Appended to non-trunk branch names */ |
| 46 | const char *zTagPre; /* Prepended to non-trunk tag names */ |
| 47 | const char *zTagSuf; /* Appended to non-trunk tag names */ |
| 48 | } gimport; |
| 49 | |
| 50 | /* |
| 51 | ** State information about an on-going fast-import parse. |
| 52 | */ |
| 53 | static struct { |
| @@ -198,11 +208,12 @@ | |
| 208 | static void finish_tag(void){ |
| 209 | Blob record, cksum; |
| 210 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 211 | blob_zero(&record); |
| 212 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 213 | blob_appendf(&record, "T +%F%F%F %s\n", gimport.zTagPre, gg.zTag, |
| 214 | gimport.zTagSuf, gg.zFrom); |
| 215 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 216 | md5sum_blob(&record, &cksum); |
| 217 | blob_appendf(&record, "Z %b\n", &cksum); |
| 218 | fast_insert_content(&record, 0, 0, 1); |
| 219 | blob_reset(&cksum); |
| @@ -275,18 +286,21 @@ | |
| 286 | |
| 287 | /* Add the required "T" cards to the manifest. Make sure they are added |
| 288 | ** in sorted order and without any duplicates. Otherwise, fossil will not |
| 289 | ** recognize the document as a valid manifest. */ |
| 290 | if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ |
| 291 | aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre, |
| 292 | gg.zBranch, gimport.zBranchSuf); |
| 293 | aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre, |
| 294 | gg.zBranch, gimport.zBranchSuf); |
| 295 | if( zFromBranch ){ |
| 296 | aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre, |
| 297 | zFromBranch, gimport.zBranchSuf); |
| 298 | } |
| 299 | } |
| 300 | if( gg.zFrom==0 ){ |
| 301 | aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName); |
| 302 | } |
| 303 | qsort(aTCard, nTCard, sizeof(char *), string_cmp); |
| 304 | for(i=0; i<nTCard; i++){ |
| 305 | if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){ |
| 306 | blob_appendf(&record, "%s", aTCard[i]); |
| @@ -313,11 +327,12 @@ | |
| 327 | ** This behavior seems like a bug in git-fast-export, but it is easier |
| 328 | ** to work around the problem than to fix git-fast-export. |
| 329 | */ |
| 330 | if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){ |
| 331 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 332 | blob_appendf(&record, "T +sym-%F%F%F %s\n", gimport.zBranchPre, gg.zBranch, |
| 333 | gimport.zBranchSuf, gg.zPrevCheckin); |
| 334 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 335 | md5sum_blob(&record, &cksum); |
| 336 | blob_appendf(&record, "Z %b\n", &cksum); |
| 337 | db_multi_exec( |
| 338 | "INSERT OR REPLACE INTO xtag(tname, tcontent)" |
| @@ -749,11 +764,14 @@ | |
| 764 | const char *zBranches; /* Name of branches folder in repo root */ |
| 765 | int lenBranches; /* String length of zBranches */ |
| 766 | const char *zTags; /* Name of tags folder in repo root */ |
| 767 | int lenTags; /* String length of zTags */ |
| 768 | Bag newBranches; /* Branches that were created in this revision */ |
| 769 | int revFlag; /* Add svn-rev-nn tags on every checkin */ |
| 770 | const char *zRevPre; /* Prepended to revision tag names */ |
| 771 | const char *zRevSuf; /* Appended to revision tag names */ |
| 772 | const char **azIgnTree; /* NULL-terminated list of dirs to ignore */ |
| 773 | } gsvn; |
| 774 | typedef struct { |
| 775 | char *zKey; |
| 776 | char *zVal; |
| 777 | } KeyVal; |
| @@ -968,15 +986,17 @@ | |
| 986 | " WHERE trev<%d AND tbranch=%d", |
| 987 | gsvn.rev, branchId); |
| 988 | } |
| 989 | if( parentRid>0 ){ |
| 990 | pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 991 | if( pParentManifest ){ |
| 992 | pParentFile = manifest_file_next(pParentManifest, 0); |
| 993 | parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", |
| 994 | parentRid); |
| 995 | if( parentBranch!=branchId && branchType!=SVN_TAG ){ |
| 996 | sameAsParent = 0; |
| 997 | } |
| 998 | } |
| 999 | } |
| 1000 | if( mergeRid<MAX_INT_32 ){ |
| 1001 | if( gsvn.zComment ){ |
| 1002 | blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| @@ -1017,47 +1037,57 @@ | |
| 1037 | char *zParentBranch = |
| 1038 | db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1039 | parentBranch |
| 1040 | ); |
| 1041 | blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1042 | blob_appendf(&manifest, "T *branch * %F%F%F\n", gimport.zBranchPre, |
| 1043 | zBranch, gimport.zBranchSuf); |
| 1044 | blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, |
| 1045 | zBranch, gimport.zBranchSuf); |
| 1046 | if( gsvn.revFlag ){ |
| 1047 | blob_appendf(&manifest, "T +sym-%Fr%d%F *\n", gimport.zTagPre, |
| 1048 | gsvn.rev, gimport.zTagSuf); |
| 1049 | } |
| 1050 | blob_appendf(&manifest, "T -sym-%F%F%F *\n", gimport.zBranchPre, |
| 1051 | zParentBranch, gimport.zBranchSuf); |
| 1052 | fossil_free(zParentBranch); |
| 1053 | }else{ |
| 1054 | char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1055 | blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1056 | if( gsvn.revFlag ){ |
| 1057 | blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, |
| 1058 | gsvn.rev, gsvn.zRevSuf); |
| 1059 | } |
| 1060 | fossil_free(zMergeUuid); |
| 1061 | } |
| 1062 | fossil_free(zParentUuid); |
| 1063 | }else{ |
| 1064 | blob_appendf(&manifest, "T *branch * %F%F%F\n", |
| 1065 | gimport.zBranchPre, zBranch, gimport.zBranchSuf); |
| 1066 | blob_appendf(&manifest, "T *sym-%F%F%F *\n", gimport.zBranchPre, |
| 1067 | zBranch, gimport.zBranchSuf); |
| 1068 | if( gsvn.revFlag ){ |
| 1069 | blob_appendf(&manifest, "T +sym-%F%d%F *\n", gsvn.zRevPre, gsvn.rev, |
| 1070 | gsvn.zRevSuf); |
| 1071 | } |
| 1072 | } |
| 1073 | }else if( branchType==SVN_TAG ){ |
| 1074 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1075 | blob_reset(&manifest); |
| 1076 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1077 | blob_appendf(&manifest, "T +sym-%F%F%F %s\n", gimport.zTagPre, zBranch, |
| 1078 | gimport.zTagSuf, zParentUuid); |
| 1079 | fossil_free(zParentUuid); |
| 1080 | } |
| 1081 | }else{ |
| 1082 | char *zParentUuid = rid_to_uuid(parentRid); |
| 1083 | blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1084 | if( branchType!=SVN_TAG ){ |
| 1085 | blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1086 | }else{ |
| 1087 | blob_appendf(&manifest, "T -sym-%F%F%F %s\n", gimport.zBranchPre, |
| 1088 | zBranch, gimport.zBranchSuf, zParentUuid); |
| 1089 | } |
| 1090 | fossil_free(zParentUuid); |
| 1091 | } |
| 1092 | if( gsvn.zUser ){ |
| 1093 | blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| @@ -1152,14 +1182,27 @@ | |
| 1182 | } |
| 1183 | } |
| 1184 | |
| 1185 | /* |
| 1186 | ** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1187 | ** Return 0 if not a branch, tag, or trunk, or if ignored by --ignore-tree. |
| 1188 | */ |
| 1189 | static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1190 | char *zBranch = 0; |
| 1191 | int branchId = 0; |
| 1192 | if( gsvn.azIgnTree ){ |
| 1193 | const char **pzIgnTree; |
| 1194 | unsigned nPath = strlen(zPath); |
| 1195 | for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){ |
| 1196 | const char *zIgn = *pzIgnTree; |
| 1197 | int nIgn = strlen(zIgn); |
| 1198 | if( strncmp(zPath, zIgn, nIgn) == 0 |
| 1199 | && ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){ |
| 1200 | return 0; |
| 1201 | } |
| 1202 | } |
| 1203 | } |
| 1204 | *type = SVN_UNKNOWN; |
| 1205 | *zFile = 0; |
| 1206 | if( gsvn.lenTrunk==0 ){ |
| 1207 | zBranch = "trunk"; |
| 1208 | *zFile = zPath; |
| @@ -1433,11 +1476,11 @@ | |
| 1476 | if( strncmp(zAction, "change", 6)==0 ){ |
| 1477 | int rid = 0; |
| 1478 | if( zKind==0 ){ |
| 1479 | fossil_fatal("Missing Node-kind"); |
| 1480 | } |
| 1481 | if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){ |
| 1482 | if( deltaFlag ){ |
| 1483 | Blob deltaSrc; |
| 1484 | Blob target; |
| 1485 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1486 | " SELECT tuuid FROM xfiles" |
| @@ -1497,31 +1540,48 @@ | |
| 1540 | ** --git Import from the git-fast-export file format (default) |
| 1541 | ** Options: |
| 1542 | ** --import-marks FILE Restore marks table from FILE |
| 1543 | ** --export-marks FILE Save marks table to FILE |
| 1544 | ** |
| 1545 | ** --svn Import from the svnadmin-dump file format. The default |
| 1546 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1547 | ** folders in the SVN root as special, following the |
| 1548 | ** common layout of SVN repositories. These are (by |
| 1549 | ** default) trunk/, branches/ and tags/. The SVN --deltas |
| 1550 | ** format is supported but not required. |
| 1551 | ** Options: |
| 1552 | ** --trunk FOLDER Name of trunk folder |
| 1553 | ** --branches FOLDER Name of branches folder |
| 1554 | ** --tags FOLDER Name of tags folder |
| 1555 | ** --base PATH Path to project root in repository |
| 1556 | ** --flat The whole dump is a single branch |
| 1557 | ** --rev-tags Tag each revision, implied by -i |
| 1558 | ** --no-rev-tags Disables tagging effect of -i |
| 1559 | ** --rename-rev PAT Rev tag names, default "svn-rev-%" |
| 1560 | ** --ignore-tree DIR Ignores subtree rooted at DIR |
| 1561 | ** |
| 1562 | ** Common Options: |
| 1563 | ** -i|--incremental allow importing into an existing repository |
| 1564 | ** -f|--force overwrite repository if already exists |
| 1565 | ** -q|--quiet omit progress output |
| 1566 | ** --no-rebuild skip the "rebuilding metadata" step |
| 1567 | ** --no-vacuum skip the final VACUUM of the database file |
| 1568 | ** --rename-trunk NAME use NAME as name of imported trunk branch |
| 1569 | ** --rename-branch PAT rename all branch names using PAT pattern |
| 1570 | ** --rename-tag PAT rename all tag names using PAT pattern |
| 1571 | ** |
| 1572 | ** The --incremental option allows an existing repository to be extended |
| 1573 | ** with new content. The --rename-* options may be useful to avoid name |
| 1574 | ** conflicts when using the --incremental option. |
| 1575 | ** |
| 1576 | ** The argument to --rename-* contains one "%" character to be replaced |
| 1577 | ** with the original name. For example, "--rename-tag svn-%-tag" renames |
| 1578 | ** the tag called "release" to "svn-release-tag". |
| 1579 | ** |
| 1580 | ** --ignore-tree is useful for importing Subversion repositories which |
| 1581 | ** move branches to subdirectories of "branches/deleted" instead of |
| 1582 | ** deleting them. It can be supplied multiple times if necessary. |
| 1583 | ** |
| 1584 | ** See also: export |
| 1585 | */ |
| 1586 | void import_cmd(void){ |
| 1587 | char *zPassword; |
| @@ -1541,21 +1601,66 @@ | |
| 1601 | int flatFlag = 0; |
| 1602 | |
| 1603 | /* Options for --git only */ |
| 1604 | const char *markfile_in = 0; |
| 1605 | const char *markfile_out = 0; |
| 1606 | |
| 1607 | /* Interpret --rename-* options. Use a table to avoid code duplication. */ |
| 1608 | const struct { |
| 1609 | const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf; |
| 1610 | int format; /* 1=git, 2=svn, 3=any */ |
| 1611 | } renOpts[] = { |
| 1612 | {"rename-branch", &gimport.zBranchPre, "", &gimport.zBranchSuf, "", 3}, |
| 1613 | {"rename-tag" , &gimport.zTagPre , "", &gimport.zTagSuf , "", 3}, |
| 1614 | {"rename-rev" , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf , "", 2}, |
| 1615 | }, *renOpt = renOpts; |
| 1616 | int i; |
| 1617 | for( i = 0; i < sizeof(renOpts) / sizeof(*renOpts); ++i, ++renOpt ){ |
| 1618 | if( 1 << svnFlag & renOpt->format ){ |
| 1619 | const char *zArgument = find_option(renOpt->zOpt, 0, 1); |
| 1620 | if( zArgument ){ |
| 1621 | const char *sep = strchr(zArgument, '%'); |
| 1622 | if( !sep ){ |
| 1623 | fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt); |
| 1624 | }else if( strchr(sep + 1, '%') ){ |
| 1625 | fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt); |
| 1626 | } |
| 1627 | *renOpt->varPre = fossil_malloc(sep - zArgument + 1); |
| 1628 | memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument); |
| 1629 | ((char *)*renOpt->varPre)[sep - zArgument] = 0; |
| 1630 | *renOpt->varSuf = sep + 1; |
| 1631 | }else{ |
| 1632 | *renOpt->varPre = renOpt->zDefaultPre; |
| 1633 | *renOpt->varSuf = renOpt->zDefaultSuf; |
| 1634 | } |
| 1635 | } |
| 1636 | } |
| 1637 | if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ |
| 1638 | gimport.zTrunkName = "trunk"; |
| 1639 | } |
| 1640 | |
| 1641 | if( svnFlag ){ |
| 1642 | /* Get --svn related options here, so verify_all_options() fails when |
| 1643 | * svn-only options are specified with --git |
| 1644 | */ |
| 1645 | const char *zIgnTree; |
| 1646 | unsigned nIgnTree = 0; |
| 1647 | while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){ |
| 1648 | if ( *zIgnTree ){ |
| 1649 | gsvn.azIgnTree = fossil_realloc(gsvn.azIgnTree, |
| 1650 | sizeof(*gsvn.azIgnTree) * (nIgnTree + 2)); |
| 1651 | gsvn.azIgnTree[nIgnTree++] = zIgnTree; |
| 1652 | gsvn.azIgnTree[nIgnTree] = 0; |
| 1653 | } |
| 1654 | } |
| 1655 | zBase = find_option("base", 0, 1); |
| 1656 | flatFlag = find_option("flat", 0, 0)!=0; |
| 1657 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1658 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1659 | gsvn.zTags = find_option("tags", 0, 1); |
| 1660 | gsvn.revFlag = find_option("rev-tags", 0, 0) |
| 1661 | || (incrFlag && !find_option("no-rev-tags", 0, 0)); |
| 1662 | }else if( gitFlag ){ |
| 1663 | markfile_in = find_option("import-marks", 0, 1); |
| 1664 | markfile_out = find_option("export-marks", 0, 1); |
| 1665 | } |
| 1666 | verify_all_options(); |
| 1667 |