Fossil SCM

Enhancements to SVN import

drh 2016-06-10 14:43 UTC trunk merge
Commit fa9ead30dc76c2cc2624ee71931632f3fdd4dd2d
2 files changed +144 -39 +144 -39
+144 -39
--- src/import.c
+++ src/import.c
@@ -34,10 +34,20 @@
3434
char isExe; /* True if executable */
3535
char isLink; /* True if symlink */
3636
};
3737
#endif
3838
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;
3949
4050
/*
4151
** State information about an on-going fast-import parse.
4252
*/
4353
static struct {
@@ -198,11 +208,12 @@
198208
static void finish_tag(void){
199209
Blob record, cksum;
200210
if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){
201211
blob_zero(&record);
202212
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);
204215
blob_appendf(&record, "U %F\n", gg.zUser);
205216
md5sum_blob(&record, &cksum);
206217
blob_appendf(&record, "Z %b\n", &cksum);
207218
fast_insert_content(&record, 0, 0, 1);
208219
blob_reset(&cksum);
@@ -275,18 +286,21 @@
275286
276287
/* Add the required "T" cards to the manifest. Make sure they are added
277288
** in sorted order and without any duplicates. Otherwise, fossil will not
278289
** recognize the document as a valid manifest. */
279290
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);
282295
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);
284298
}
285299
}
286300
if( gg.zFrom==0 ){
287
- aTCard[nTCard++] = mprintf("T *sym-trunk *\n");
301
+ aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName);
288302
}
289303
qsort(aTCard, nTCard, sizeof(char *), string_cmp);
290304
for(i=0; i<nTCard; i++){
291305
if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){
292306
blob_appendf(&record, "%s", aTCard[i]);
@@ -313,11 +327,12 @@
313327
** This behavior seems like a bug in git-fast-export, but it is easier
314328
** to work around the problem than to fix git-fast-export.
315329
*/
316330
if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){
317331
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);
319334
blob_appendf(&record, "U %F\n", gg.zUser);
320335
md5sum_blob(&record, &cksum);
321336
blob_appendf(&record, "Z %b\n", &cksum);
322337
db_multi_exec(
323338
"INSERT OR REPLACE INTO xtag(tname, tcontent)"
@@ -749,11 +764,14 @@
749764
const char *zBranches; /* Name of branches folder in repo root */
750765
int lenBranches; /* String length of zBranches */
751766
const char *zTags; /* Name of tags folder in repo root */
752767
int lenTags; /* String length of zTags */
753768
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 */
755773
} gsvn;
756774
typedef struct {
757775
char *zKey;
758776
char *zVal;
759777
} KeyVal;
@@ -968,15 +986,17 @@
968986
" WHERE trev<%d AND tbranch=%d",
969987
gsvn.rev, branchId);
970988
}
971989
if( parentRid>0 ){
972990
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
+ }
978998
}
979999
}
9801000
if( mergeRid<MAX_INT_32 ){
9811001
if( gsvn.zComment ){
9821002
blob_appendf(&manifest, "C %F\n", gsvn.zComment);
@@ -1017,47 +1037,57 @@
10171037
char *zParentBranch =
10181038
db_text(0, "SELECT tname FROM xbranches WHERE tid=%d",
10191039
parentBranch
10201040
);
10211041
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);
10261049
}
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);
10281052
fossil_free(zParentBranch);
10291053
}else{
10301054
char *zMergeUuid = rid_to_uuid(mergeRid);
10311055
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);
10341059
}
10351060
fossil_free(zMergeUuid);
10361061
}
10371062
fossil_free(zParentUuid);
10381063
}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);
10431071
}
10441072
}
10451073
}else if( branchType==SVN_TAG ){
10461074
char *zParentUuid = rid_to_uuid(parentRid);
10471075
blob_reset(&manifest);
10481076
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);
10501079
fossil_free(zParentUuid);
10511080
}
10521081
}else{
10531082
char *zParentUuid = rid_to_uuid(parentRid);
10541083
blob_appendf(&manifest, "D %s\n", gsvn.zDate);
10551084
if( branchType!=SVN_TAG ){
10561085
blob_appendf(&manifest, "T +closed %s\n", zParentUuid);
10571086
}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);
10591089
}
10601090
fossil_free(zParentUuid);
10611091
}
10621092
if( gsvn.zUser ){
10631093
blob_appendf(&manifest, "U %F\n", gsvn.zUser);
@@ -1152,14 +1182,27 @@
11521182
}
11531183
}
11541184
11551185
/*
11561186
** 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
+*/
11581189
static int svn_parse_path(char *zPath, char **zFile, int *type){
11591190
char *zBranch = 0;
11601191
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
+ }
11611204
*type = SVN_UNKNOWN;
11621205
*zFile = 0;
11631206
if( gsvn.lenTrunk==0 ){
11641207
zBranch = "trunk";
11651208
*zFile = zPath;
@@ -1433,11 +1476,11 @@
14331476
if( strncmp(zAction, "change", 6)==0 ){
14341477
int rid = 0;
14351478
if( zKind==0 ){
14361479
fossil_fatal("Missing Node-kind");
14371480
}
1438
- if( strncmp(zKind, "dir", 3)!=0 ){
1481
+ if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){
14391482
if( deltaFlag ){
14401483
Blob deltaSrc;
14411484
Blob target;
14421485
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=("
14431486
" SELECT tuuid FROM xfiles"
@@ -1497,31 +1540,48 @@
14971540
** --git Import from the git-fast-export file format (default)
14981541
** Options:
14991542
** --import-marks FILE Restore marks table from FILE
15001543
** --export-marks FILE Save marks table to FILE
15011544
**
1502
-** --svn Import from the svnadmin-dump file format. The default
1545
+** --svn Import from the svnadmin-dump file format. The default
15031546
** behaviour (unless overridden by --flat) is to treat 3
15041547
** 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.
15071551
** Options:
15081552
** --trunk FOLDER Name of trunk folder
15091553
** --branches FOLDER Name of branches folder
15101554
** --tags FOLDER Name of tags folder
15111555
** --base PATH Path to project root in repository
15121556
** --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
15131561
**
15141562
** 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
15201571
**
15211572
** 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.
15231583
**
15241584
** See also: export
15251585
*/
15261586
void import_cmd(void){
15271587
char *zPassword;
@@ -1541,21 +1601,66 @@
15411601
int flatFlag = 0;
15421602
15431603
/* Options for --git only */
15441604
const char *markfile_in = 0;
15451605
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
+ }
15461640
15471641
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
15501644
*/
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
+ }
15511655
zBase = find_option("base", 0, 1);
15521656
flatFlag = find_option("flat", 0, 0)!=0;
15531657
gsvn.zTrunk = find_option("trunk", 0, 1);
15541658
gsvn.zBranches = find_option("branches", 0, 1);
15551659
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));
15571662
}else if( gitFlag ){
15581663
markfile_in = find_option("import-marks", 0, 1);
15591664
markfile_out = find_option("export-marks", 0, 1);
15601665
}
15611666
verify_all_options();
15621667
--- 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 @@
3434
char isExe; /* True if executable */
3535
char isLink; /* True if symlink */
3636
};
3737
#endif
3838
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;
3949
4050
/*
4151
** State information about an on-going fast-import parse.
4252
*/
4353
static struct {
@@ -198,11 +208,12 @@
198208
static void finish_tag(void){
199209
Blob record, cksum;
200210
if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){
201211
blob_zero(&record);
202212
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);
204215
blob_appendf(&record, "U %F\n", gg.zUser);
205216
md5sum_blob(&record, &cksum);
206217
blob_appendf(&record, "Z %b\n", &cksum);
207218
fast_insert_content(&record, 0, 0, 1);
208219
blob_reset(&cksum);
@@ -275,18 +286,21 @@
275286
276287
/* Add the required "T" cards to the manifest. Make sure they are added
277288
** in sorted order and without any duplicates. Otherwise, fossil will not
278289
** recognize the document as a valid manifest. */
279290
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);
282295
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);
284298
}
285299
}
286300
if( gg.zFrom==0 ){
287
- aTCard[nTCard++] = mprintf("T *sym-trunk *\n");
301
+ aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName);
288302
}
289303
qsort(aTCard, nTCard, sizeof(char *), string_cmp);
290304
for(i=0; i<nTCard; i++){
291305
if( i==0 || fossil_strcmp(aTCard[i-1], aTCard[i]) ){
292306
blob_appendf(&record, "%s", aTCard[i]);
@@ -313,11 +327,12 @@
313327
** This behavior seems like a bug in git-fast-export, but it is easier
314328
** to work around the problem than to fix git-fast-export.
315329
*/
316330
if( gg.tagCommit && gg.zDate && gg.zUser && gg.zFrom ){
317331
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);
319334
blob_appendf(&record, "U %F\n", gg.zUser);
320335
md5sum_blob(&record, &cksum);
321336
blob_appendf(&record, "Z %b\n", &cksum);
322337
db_multi_exec(
323338
"INSERT OR REPLACE INTO xtag(tname, tcontent)"
@@ -749,11 +764,14 @@
749764
const char *zBranches; /* Name of branches folder in repo root */
750765
int lenBranches; /* String length of zBranches */
751766
const char *zTags; /* Name of tags folder in repo root */
752767
int lenTags; /* String length of zTags */
753768
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 */
755773
} gsvn;
756774
typedef struct {
757775
char *zKey;
758776
char *zVal;
759777
} KeyVal;
@@ -968,15 +986,17 @@
968986
" WHERE trev<%d AND tbranch=%d",
969987
gsvn.rev, branchId);
970988
}
971989
if( parentRid>0 ){
972990
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
+ }
978998
}
979999
}
9801000
if( mergeRid<MAX_INT_32 ){
9811001
if( gsvn.zComment ){
9821002
blob_appendf(&manifest, "C %F\n", gsvn.zComment);
@@ -1017,47 +1037,57 @@
10171037
char *zParentBranch =
10181038
db_text(0, "SELECT tname FROM xbranches WHERE tid=%d",
10191039
parentBranch
10201040
);
10211041
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);
10261049
}
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);
10281052
fossil_free(zParentBranch);
10291053
}else{
10301054
char *zMergeUuid = rid_to_uuid(mergeRid);
10311055
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);
10341059
}
10351060
fossil_free(zMergeUuid);
10361061
}
10371062
fossil_free(zParentUuid);
10381063
}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);
10431071
}
10441072
}
10451073
}else if( branchType==SVN_TAG ){
10461074
char *zParentUuid = rid_to_uuid(parentRid);
10471075
blob_reset(&manifest);
10481076
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);
10501079
fossil_free(zParentUuid);
10511080
}
10521081
}else{
10531082
char *zParentUuid = rid_to_uuid(parentRid);
10541083
blob_appendf(&manifest, "D %s\n", gsvn.zDate);
10551084
if( branchType!=SVN_TAG ){
10561085
blob_appendf(&manifest, "T +closed %s\n", zParentUuid);
10571086
}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);
10591089
}
10601090
fossil_free(zParentUuid);
10611091
}
10621092
if( gsvn.zUser ){
10631093
blob_appendf(&manifest, "U %F\n", gsvn.zUser);
@@ -1152,14 +1182,27 @@
11521182
}
11531183
}
11541184
11551185
/*
11561186
** 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
+*/
11581189
static int svn_parse_path(char *zPath, char **zFile, int *type){
11591190
char *zBranch = 0;
11601191
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
+ }
11611204
*type = SVN_UNKNOWN;
11621205
*zFile = 0;
11631206
if( gsvn.lenTrunk==0 ){
11641207
zBranch = "trunk";
11651208
*zFile = zPath;
@@ -1433,11 +1476,11 @@
14331476
if( strncmp(zAction, "change", 6)==0 ){
14341477
int rid = 0;
14351478
if( zKind==0 ){
14361479
fossil_fatal("Missing Node-kind");
14371480
}
1438
- if( strncmp(zKind, "dir", 3)!=0 ){
1481
+ if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){
14391482
if( deltaFlag ){
14401483
Blob deltaSrc;
14411484
Blob target;
14421485
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=("
14431486
" SELECT tuuid FROM xfiles"
@@ -1497,31 +1540,48 @@
14971540
** --git Import from the git-fast-export file format (default)
14981541
** Options:
14991542
** --import-marks FILE Restore marks table from FILE
15001543
** --export-marks FILE Save marks table to FILE
15011544
**
1502
-** --svn Import from the svnadmin-dump file format. The default
1545
+** --svn Import from the svnadmin-dump file format. The default
15031546
** behaviour (unless overridden by --flat) is to treat 3
15041547
** 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.
15071551
** Options:
15081552
** --trunk FOLDER Name of trunk folder
15091553
** --branches FOLDER Name of branches folder
15101554
** --tags FOLDER Name of tags folder
15111555
** --base PATH Path to project root in repository
15121556
** --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
15131561
**
15141562
** 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
15201571
**
15211572
** 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.
15231583
**
15241584
** See also: export
15251585
*/
15261586
void import_cmd(void){
15271587
char *zPassword;
@@ -1541,21 +1601,66 @@
15411601
int flatFlag = 0;
15421602
15431603
/* Options for --git only */
15441604
const char *markfile_in = 0;
15451605
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
+ }
15461640
15471641
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
15501644
*/
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
+ }
15511655
zBase = find_option("base", 0, 1);
15521656
flatFlag = find_option("flat", 0, 0)!=0;
15531657
gsvn.zTrunk = find_option("trunk", 0, 1);
15541658
gsvn.zBranches = find_option("branches", 0, 1);
15551659
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));
15571662
}else if( gitFlag ){
15581663
markfile_in = find_option("import-marks", 0, 1);
15591664
markfile_out = find_option("export-marks", 0, 1);
15601665
}
15611666
verify_all_options();
15621667
--- 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

Keyboard Shortcuts

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