Index: src/import.c ================================================================== --- src/import.c +++ src/import.c @@ -34,10 +34,20 @@ char isExe; /* True if executable */ char isLink; /* True if symlink */ }; #endif +/* +** State information common to all import types. +*/ +static struct { + const char *zTrunkName; /* Name of trunk branch */ + const char *zBranchPre; /* Prepended to non-trunk branch names */ + const char *zBranchSuf; /* Appended to non-trunk branch names */ + const char *zTagPre; /* Prepended to non-trunk tag names */ + const char *zTagSuf; /* Appended to non-trunk tag names */ +} gimport; /* ** State information about an on-going fast-import parse. */ static struct { @@ -198,11 +208,12 @@ static void finish_tag(void){ Blob record, cksum; if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ blob_zero(&record); blob_appendf(&record, "D %s\n", gg.zDate); - blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); + blob_appendf(&record, "T +%F%F%F %s\n", gimport.zTagPre, gg.zTag, + gimport.zTagSuf, gg.zFrom); blob_appendf(&record, "U %F\n", gg.zUser); md5sum_blob(&record, &cksum); blob_appendf(&record, "Z %b\n", &cksum); fast_insert_content(&record, 0, 0, 1); blob_reset(&cksum); @@ -275,18 +286,21 @@ /* Add the required "T" cards to the manifest. Make sure they are added ** in sorted order and without any duplicates. Otherwise, fossil will not ** recognize the document as a valid manifest. */ if( !gg.tagCommit && fossil_strcmp(zFromBranch, gg.zBranch)!=0 ){ - aTCard[nTCard++] = mprintf("T *branch * %F\n", gg.zBranch); - aTCard[nTCard++] = mprintf("T *sym-%F *\n", gg.zBranch); + aTCard[nTCard++] = mprintf("T *branch * %F%F%F\n", gimport.zBranchPre, + gg.zBranch, gimport.zBranchSuf); + aTCard[nTCard++] = mprintf("T *sym-%F%F%F *\n", gimport.zBranchPre, + gg.zBranch, gimport.zBranchSuf); if( zFromBranch ){ - aTCard[nTCard++] = mprintf("T -sym-%F *\n", zFromBranch); + aTCard[nTCard++] = mprintf("T -sym-%F%F%F *\n", gimport.zBranchPre, + zFromBranch, gimport.zBranchSuf); } } if( gg.zFrom==0 ){ - aTCard[nTCard++] = mprintf("T *sym-trunk *\n"); + aTCard[nTCard++] = mprintf("T *sym-%F *\n", gimport.zTrunkName); } qsort(aTCard, nTCard, sizeof(char *), string_cmp); for(i=0; i0 ){ pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); - pParentFile = manifest_file_next(pParentManifest, 0); - parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", - parentRid); - if( parentBranch!=branchId && branchType!=SVN_TAG ){ - sameAsParent = 0; + if( pParentManifest ){ + pParentFile = manifest_file_next(pParentManifest, 0); + parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", + parentRid); + if( parentBranch!=branchId && branchType!=SVN_TAG ){ + sameAsParent = 0; + } } } if( mergeRid nIgn && zPath[nIgn] == '/')) ){ + return 0; + } + } + } *type = SVN_UNKNOWN; *zFile = 0; if( gsvn.lenTrunk==0 ){ zBranch = "trunk"; *zFile = zPath; @@ -1434,11 +1477,11 @@ if( strncmp(zAction, "change", 6)==0 ){ int rid = 0; if( zKind==0 ){ fossil_fatal("Missing Node-kind"); } - if( strncmp(zKind, "dir", 3)!=0 ){ + if( rec.contentFlag && strncmp(zKind, "dir", 3)!=0 ){ if( deltaFlag ){ Blob deltaSrc; Blob target; rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" " SELECT tuuid FROM xfiles" @@ -1498,31 +1541,48 @@ ** --git Import from the git-fast-export file format (default) ** Options: ** --import-marks FILE Restore marks table from FILE ** --export-marks FILE Save marks table to FILE ** -** --svn Import from the svnadmin-dump file format. The default +** --svn Import from the svnadmin-dump file format. The default ** behaviour (unless overridden by --flat) is to treat 3 ** folders in the SVN root as special, following the -** common layout of SVN repositories. These are (by -** default) trunk/, branches/ and tags/ +** common layout of SVN repositories. These are (by +** default) trunk/, branches/ and tags/. The SVN --deltas +** format is supported but not required. ** Options: ** --trunk FOLDER Name of trunk folder ** --branches FOLDER Name of branches folder ** --tags FOLDER Name of tags folder ** --base PATH Path to project root in repository ** --flat The whole dump is a single branch +** --rev-tags Tag each revision, implied by -i +** --no-rev-tags Disables tagging effect of -i +** --rename-rev PAT Rev tag names, default "svn-rev-%" +** --ignore-tree DIR Ignores subtree rooted at DIR ** ** Common Options: -** -i|--incremental allow importing into an existing repository -** -f|--force overwrite repository if already exists -** -q|--quiet omit progress output -** --no-rebuild skip the "rebuilding metadata" step -** --no-vacuum skip the final VACUUM of the database file +** -i|--incremental allow importing into an existing repository +** -f|--force overwrite repository if already exists +** -q|--quiet omit progress output +** --no-rebuild skip the "rebuilding metadata" step +** --no-vacuum skip the final VACUUM of the database file +** --rename-trunk NAME use NAME as name of imported trunk branch +** --rename-branch PAT rename all branch names using PAT pattern +** --rename-tag PAT rename all tag names using PAT pattern ** ** The --incremental option allows an existing repository to be extended -** with new content. +** with new content. The --rename-* options may be useful to avoid name +** conflicts when using the --incremental option. +** +** The argument to --rename-* contains one "%" character to be replaced +** with the original name. For example, "--rename-tag svn-%-tag" renames +** the tag called "release" to "svn-release-tag". +** +** --ignore-tree is useful for importing Subversion repositories which +** move branches to subdirectories of "branches/deleted" instead of +** deleting them. It can be supplied multiple times if necessary. ** ** See also: export */ void import_cmd(void){ char *zPassword; @@ -1542,21 +1602,66 @@ int flatFlag=0; /* Options for --git only */ const char *markfile_in; const char *markfile_out; + + /* Interpret --rename-* options. Use a table to avoid code duplication. */ + const struct { + const char *zOpt, **varPre, *zDefaultPre, **varSuf, *zDefaultSuf; + int format; /* 1=git, 2=svn, 3=any */ + } renOpts[] = { + {"rename-branch", &gimport.zBranchPre, "", &gimport.zBranchSuf, "", 3}, + {"rename-tag" , &gimport.zTagPre , "", &gimport.zTagSuf , "", 3}, + {"rename-rev" , &gsvn.zRevPre, "svn-rev-", &gsvn.zRevSuf , "", 2}, + }, *renOpt = renOpts; + int i; + for( i = 0; i < sizeof(renOpts) / sizeof(*renOpts); ++i, ++renOpt ){ + if( 1 << svnFlag & renOpt->format ){ + const char *zArgument = find_option(renOpt->zOpt, 0, 1); + if( zArgument ){ + const char *sep = strchr(zArgument, '%'); + if( !sep ){ + fossil_fatal("missing '%%' in argument to --%s", renOpt->zOpt); + }else if( strchr(sep + 1, '%') ){ + fossil_fatal("multiple '%%' in argument to --%s", renOpt->zOpt); + } + *renOpt->varPre = fossil_malloc(sep - zArgument + 1); + memcpy((char *)*renOpt->varPre, zArgument, sep - zArgument); + ((char *)*renOpt->varPre)[sep - zArgument] = 0; + *renOpt->varSuf = sep + 1; + }else{ + *renOpt->varPre = renOpt->zDefaultPre; + *renOpt->varSuf = renOpt->zDefaultSuf; + } + } + } + if( !(gimport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ + gimport.zTrunkName = "trunk"; + } if( svnFlag ){ - /* Get --svn related options here, so verify_all_options() fail when svn - * only option are specify with --git + /* Get --svn related options here, so verify_all_options() fails when + * svn-only options are specified with --git */ + const char *zIgnTree; + unsigned nIgnTree = 0; + while( (zIgnTree = find_option("ignore-tree", 0, 1)) ){ + if ( *zIgnTree ){ + gsvn.azIgnTree = fossil_realloc(gsvn.azIgnTree, + sizeof(*gsvn.azIgnTree) * (nIgnTree + 2)); + gsvn.azIgnTree[nIgnTree++] = zIgnTree; + gsvn.azIgnTree[nIgnTree] = 0; + } + } zBase = find_option("base", 0, 1); flatFlag = find_option("flat", 0, 0)!=0; gsvn.zTrunk = find_option("trunk", 0, 1); gsvn.zBranches = find_option("branches", 0, 1); gsvn.zTags = find_option("tags", 0, 1); - gsvn.incrFlag = incrFlag; + gsvn.revFlag = find_option("rev-tags", 0, 0) + || (incrFlag && !find_option("no-rev-tags", 0, 0)); }else if( gitFlag ){ markfile_in = find_option("import-marks", 0, 1); markfile_out = find_option("export-marks", 0, 1); } verify_all_options();