Fossil SCM
Working on fixing/rewriting svn-import function. This is not in a stable state yet. DO NOT TEST
Commit
9604c28207d0aea31510a2c717d94c2c5e4fcf0e
Parent
2caad83d8c9ea8c…
2 files changed
+5
-5
+273
-158
+5
-5
| --- src/foci.c | ||
| +++ src/foci.c | ||
| @@ -25,17 +25,17 @@ | ||
| 25 | 25 | #include <assert.h> |
| 26 | 26 | |
| 27 | 27 | /* |
| 28 | 28 | ** The schema for the virtual table: |
| 29 | 29 | */ |
| 30 | -static const char zFociSchema[] = | |
| 30 | +static const char zFociSchema[] = | |
| 31 | 31 | @ CREATE TABLE files_of_checkin( |
| 32 | 32 | @ checkinID INTEGER, -- RID for the checkin manifest |
| 33 | 33 | @ filename TEXT, -- Name of a file |
| 34 | 34 | @ uuid TEXT, -- SHA1 hash of the file |
| 35 | 35 | @ previousName TEXT, -- Name of the file in previous checkin |
| 36 | -@ prem TEXT -- Permissions on the file | |
| 36 | +@ perm TEXT -- Permissions on the file | |
| 37 | 37 | @ ); |
| 38 | 38 | ; |
| 39 | 39 | |
| 40 | 40 | #if INTERFACE |
| 41 | 41 | /* |
| @@ -139,11 +139,11 @@ | ||
| 139 | 139 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 140 | 140 | return pCsr->pFile==0; |
| 141 | 141 | } |
| 142 | 142 | |
| 143 | 143 | static int fociFilter( |
| 144 | - sqlite3_vtab_cursor *pCursor, | |
| 144 | + sqlite3_vtab_cursor *pCursor, | |
| 145 | 145 | int idxNum, const char *idxStr, |
| 146 | 146 | int argc, sqlite3_value **argv |
| 147 | 147 | ){ |
| 148 | 148 | FociCursor *pCur = (FociCursor *)pCursor; |
| 149 | 149 | manifest_destroy(pCur->pMan); |
| @@ -158,12 +158,12 @@ | ||
| 158 | 158 | } |
| 159 | 159 | return SQLITE_OK; |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | 162 | static int fociColumn( |
| 163 | - sqlite3_vtab_cursor *pCursor, | |
| 164 | - sqlite3_context *ctx, | |
| 163 | + sqlite3_vtab_cursor *pCursor, | |
| 164 | + sqlite3_context *ctx, | |
| 165 | 165 | int i |
| 166 | 166 | ){ |
| 167 | 167 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 168 | 168 | switch( i ){ |
| 169 | 169 | case 0: /* checkinID */ |
| 170 | 170 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -25,17 +25,17 @@ | |
| 25 | #include <assert.h> |
| 26 | |
| 27 | /* |
| 28 | ** The schema for the virtual table: |
| 29 | */ |
| 30 | static const char zFociSchema[] = |
| 31 | @ CREATE TABLE files_of_checkin( |
| 32 | @ checkinID INTEGER, -- RID for the checkin manifest |
| 33 | @ filename TEXT, -- Name of a file |
| 34 | @ uuid TEXT, -- SHA1 hash of the file |
| 35 | @ previousName TEXT, -- Name of the file in previous checkin |
| 36 | @ prem TEXT -- Permissions on the file |
| 37 | @ ); |
| 38 | ; |
| 39 | |
| 40 | #if INTERFACE |
| 41 | /* |
| @@ -139,11 +139,11 @@ | |
| 139 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 140 | return pCsr->pFile==0; |
| 141 | } |
| 142 | |
| 143 | static int fociFilter( |
| 144 | sqlite3_vtab_cursor *pCursor, |
| 145 | int idxNum, const char *idxStr, |
| 146 | int argc, sqlite3_value **argv |
| 147 | ){ |
| 148 | FociCursor *pCur = (FociCursor *)pCursor; |
| 149 | manifest_destroy(pCur->pMan); |
| @@ -158,12 +158,12 @@ | |
| 158 | } |
| 159 | return SQLITE_OK; |
| 160 | } |
| 161 | |
| 162 | static int fociColumn( |
| 163 | sqlite3_vtab_cursor *pCursor, |
| 164 | sqlite3_context *ctx, |
| 165 | int i |
| 166 | ){ |
| 167 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 168 | switch( i ){ |
| 169 | case 0: /* checkinID */ |
| 170 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -25,17 +25,17 @@ | |
| 25 | #include <assert.h> |
| 26 | |
| 27 | /* |
| 28 | ** The schema for the virtual table: |
| 29 | */ |
| 30 | static const char zFociSchema[] = |
| 31 | @ CREATE TABLE files_of_checkin( |
| 32 | @ checkinID INTEGER, -- RID for the checkin manifest |
| 33 | @ filename TEXT, -- Name of a file |
| 34 | @ uuid TEXT, -- SHA1 hash of the file |
| 35 | @ previousName TEXT, -- Name of the file in previous checkin |
| 36 | @ perm TEXT -- Permissions on the file |
| 37 | @ ); |
| 38 | ; |
| 39 | |
| 40 | #if INTERFACE |
| 41 | /* |
| @@ -139,11 +139,11 @@ | |
| 139 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 140 | return pCsr->pFile==0; |
| 141 | } |
| 142 | |
| 143 | static int fociFilter( |
| 144 | sqlite3_vtab_cursor *pCursor, |
| 145 | int idxNum, const char *idxStr, |
| 146 | int argc, sqlite3_value **argv |
| 147 | ){ |
| 148 | FociCursor *pCur = (FociCursor *)pCursor; |
| 149 | manifest_destroy(pCur->pMan); |
| @@ -158,12 +158,12 @@ | |
| 158 | } |
| 159 | return SQLITE_OK; |
| 160 | } |
| 161 | |
| 162 | static int fociColumn( |
| 163 | sqlite3_vtab_cursor *pCursor, |
| 164 | sqlite3_context *ctx, |
| 165 | int i |
| 166 | ){ |
| 167 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 168 | switch( i ){ |
| 169 | case 0: /* checkinID */ |
| 170 |
+273
-158
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -12,12 +12,12 @@ | ||
| 12 | 12 | ** Author contact information: |
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** |
| 15 | 15 | ******************************************************************************* |
| 16 | 16 | ** |
| 17 | -** This file contains code used to import the content of a Git | |
| 18 | -** repository in the git-fast-import format as a new Fossil | |
| 17 | +** This file contains code used to import the content of a Git/SVN | |
| 18 | +** repository in the git-fast-import/svn-dump formats as a new Fossil | |
| 19 | 19 | ** repository. |
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "import.h" |
| 23 | 23 | #include <assert.h> |
| @@ -718,25 +718,125 @@ | ||
| 718 | 718 | fossil_fatal("bad fast-import line: [%s]", zLine); |
| 719 | 719 | return; |
| 720 | 720 | } |
| 721 | 721 | |
| 722 | 722 | static struct{ |
| 723 | - int rev; /* SVN revision number */ | |
| 724 | - int parentRev; /* SVN revision number of parent check-in */ | |
| 725 | - char *zParentBranch; /* Name of branch of parent check-in */ | |
| 726 | 723 | char *zBranch; /* Name of a branch for a commit */ |
| 727 | 724 | char *zDate; /* Date/time stamp */ |
| 728 | 725 | char *zUser; /* User name */ |
| 729 | 726 | char *zComment; /* Comment of a commit */ |
| 730 | - int flatFlag; /* True if whole repo is a single file tree */ | |
| 727 | + char *zFrom; /* from value as a UUID */ | |
| 728 | + int nMerge; /* Number of merge values */ | |
| 729 | + int nMergeAlloc; /* Number of slots in azMerge[] */ | |
| 730 | + char **azMerge; /* Merge values */ | |
| 731 | + int nFile; /* Number of aFile values */ | |
| 732 | + int nFileAlloc; /* Number of slots in aFile[] */ | |
| 733 | + ImportFile *aFile; /* Information about files in a commit */ | |
| 734 | + int fromLoaded; /* True zFrom content loaded into aFile[] */ | |
| 735 | +} gsvn; | |
| 736 | + | |
| 737 | +/* | |
| 738 | +** Create a new entry in the gsvn.aFile[] array | |
| 739 | +*/ | |
| 740 | +static ImportFile *svn_import_add_file(){ | |
| 741 | + ImportFile *pFile; | |
| 742 | + if( gsvn.nFile>=gsvn.nFileAlloc ){ | |
| 743 | + gsvn.nFileAlloc = gsvn.nFileAlloc*2 + 100; | |
| 744 | + gsvn.aFile = fossil_realloc(gsvn.aFile, gsvn.nFileAlloc*sizeof(gsvn.aFile[0])); | |
| 745 | + } | |
| 746 | + pFile = &gsvn.aFile[gsvn.nFile++]; | |
| 747 | + memset(pFile, 0, sizeof(*pFile)); | |
| 748 | + return pFile; | |
| 749 | +} | |
| 750 | + | |
| 751 | +/* | |
| 752 | +** Load all file information out of the gsvn.zFrom check-in | |
| 753 | +*/ | |
| 754 | +static void svn_import_prior_files(void){ | |
| 755 | + Manifest *p; | |
| 756 | + int rid; | |
| 757 | + ManifestFile *pOld; | |
| 758 | + ImportFile *pNew; | |
| 759 | + if( gsvn.fromLoaded ) return; | |
| 760 | + gsvn.fromLoaded = 1; | |
| 761 | + if( gsvn.zFrom==0 && gsvn.zPrevCheckin!=0 | |
| 762 | + && fossil_strcmp(gsvn.zBranch, gsvn.zPrevBranch)==0 | |
| 763 | + ){ | |
| 764 | + gsvn.zFrom = gsvn.zPrevCheckin; | |
| 765 | + gsvn.zPrevCheckin = 0; | |
| 766 | + } | |
| 767 | + if( gsvn.zFrom==0 ) return; | |
| 768 | + rid = fast_uuid_to_rid(gsvn.zFrom); | |
| 769 | + if( rid==0 ) return; | |
| 770 | + p = manifest_get(rid, CFTYPE_MANIFEST, 0); | |
| 771 | + if( p==0 ) return; | |
| 772 | + manifest_file_rewind(p); | |
| 773 | + while( (pOld = manifest_file_next(p, 0))!=0 ){ | |
| 774 | + pNew = import_add_file(); | |
| 775 | + pNew->zName = fossil_strdup(pOld->zName); | |
| 776 | + pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0; | |
| 777 | + pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0; | |
| 778 | + pNew->zUuid = fossil_strdup(pOld->zUuid); | |
| 779 | + pNew->isFrom = 1; | |
| 780 | + } | |
| 781 | + manifest_destroy(p); | |
| 782 | +} | |
| 783 | + | |
| 784 | +/* | |
| 785 | +** Deallocate the state information. | |
| 786 | +** | |
| 787 | +** The azMerge[] and aFile[] arrays are zeroed but allocated space is | |
| 788 | +** retained unless the freeAll flag is set. | |
| 789 | +*/ | |
| 790 | +static void svn_import_reset(int freeAll){ | |
| 791 | + int i; | |
| 792 | +// gsvn.xFinish = 0; | |
| 793 | +// fossil_free(gsvn.zTag); gsvn.zTag = 0; | |
| 794 | + fossil_free(gsvn.zBranch); gsvn.zBranch = 0; | |
| 795 | +// fossil_free(gsvn.aData); gsvn.aData = 0; | |
| 796 | +// fossil_free(gsvn.zMark); gsvn.zMark = 0; | |
| 797 | + fossil_free(gsvn.zDate); gsvn.zDate = 0; | |
| 798 | + fossil_free(gsvn.zUser); gsvn.zUser = 0; | |
| 799 | + fossil_free(gsvn.zComment); gsvn.zComment = 0; | |
| 800 | + fossil_free(gsvn.zFrom); gsvn.zFrom = 0; | |
| 801 | +// fossil_free(gsvn.zFromMark); gsvn.zFromMark = 0; | |
| 802 | + for(i=0; i<gsvn.nMerge; i++){ | |
| 803 | + fossil_free(gsvn.azMerge[i]); gsvn.azMerge[i] = 0; | |
| 804 | + } | |
| 805 | + gsvn.nMerge = 0; | |
| 806 | + for(i=0; i<gsvn.nFile; i++){ | |
| 807 | + fossil_free(gsvn.aFile[i].zName); | |
| 808 | + fossil_free(gsvn.aFile[i].zUuid); | |
| 809 | + fossil_free(gsvn.aFile[i].zPrior); | |
| 810 | + } | |
| 811 | + memset(gsvn.aFile, 0, gsvn.nFile*sizeof(gsvn.aFile[0])); | |
| 812 | + gsvn.nFile = 0; | |
| 813 | + if( freeAll ){ | |
| 814 | +// fossil_free(gsvn.zPrevBranch); | |
| 815 | +// fossil_free(gsvn.zPrevCheckin); | |
| 816 | + fossil_free(gsvn.azMerge); | |
| 817 | + fossil_free(gsvn.aFile); | |
| 818 | + memset(&gsvn, 0, sizeof(gsvn)); | |
| 819 | + } | |
| 820 | +// gsvn.xFinish = finish_noop; | |
| 821 | +} | |
| 822 | + | |
| 823 | +static struct{ | |
| 824 | + int rev; /* SVN revision number */ | |
| 825 | +// int parentRev; /* SVN revision number of parent check-in */ | |
| 826 | +// char *zParentBranch; /* Name of branch of parent check-in */ | |
| 827 | +// char *zBranch; /* Name of a branch for a commit */ | |
| 828 | + char *zDate; /* Date/time stamp */ | |
| 829 | + char *zUser; /* User name */ | |
| 830 | + char *zComment; /* Comment of a commit */ | |
| 831 | +// int flatFlag; /* True if whole repo is a single file tree */ | |
| 731 | 832 | const char *zTrunk; /* Name of trunk folder in repo root */ |
| 732 | 833 | int lenTrunk; /* String length of zTrunk */ |
| 733 | 834 | const char *zBranches; /* Name of branches folder in repo root */ |
| 734 | 835 | int lenBranches; /* String length of zBranches */ |
| 735 | 836 | const char *zTags; /* Name of tags folder in repo root */ |
| 736 | 837 | int lenTags; /* String length of zTags */ |
| 737 | - Blob filter; /* Path to repo root */ | |
| 738 | 838 | } gsvn; |
| 739 | 839 | typedef struct { |
| 740 | 840 | char *zKey; |
| 741 | 841 | const char *zVal; |
| 742 | 842 | } KeyVal; |
| @@ -774,11 +874,11 @@ | ||
| 774 | 874 | fossil_free(rec->aHeaders[i].zKey); |
| 775 | 875 | } |
| 776 | 876 | fossil_free(rec->aHeaders); |
| 777 | 877 | fossil_free(rec->aProps); |
| 778 | 878 | fossil_free(rec->pRawProps); |
| 779 | - blob_reset(&rec->content); | |
| 879 | + blob_reset(&rec->content); | |
| 780 | 880 | } |
| 781 | 881 | |
| 782 | 882 | static int svn_read_headers(FILE *pIn, SvnRecord *rec){ |
| 783 | 883 | char zLine[1000]; |
| 784 | 884 | |
| @@ -903,11 +1003,11 @@ | ||
| 903 | 1003 | rec->contentFlag = 0; |
| 904 | 1004 | } |
| 905 | 1005 | return 1; |
| 906 | 1006 | } |
| 907 | 1007 | |
| 908 | -static void svn_create_manifest( | |
| 1008 | +static void svn_finish_revision( | |
| 909 | 1009 | ){ |
| 910 | 1010 | Blob manifest; |
| 911 | 1011 | static Stmt insRev; |
| 912 | 1012 | static Stmt qParent; |
| 913 | 1013 | static Stmt qParent2; |
| @@ -1109,46 +1209,63 @@ | ||
| 1109 | 1209 | } |
| 1110 | 1210 | zDiff += lenData; |
| 1111 | 1211 | } |
| 1112 | 1212 | } |
| 1113 | 1213 | |
| 1114 | -static char *svn_extract_branch(const char *zPath){ | |
| 1115 | - int nFilter = blob_size(&gsvn.filter); | |
| 1116 | - char *zBranch = 0; | |
| 1117 | - if( strncmp(zPath, blob_str(&gsvn.filter), nFilter)==0 ){ | |
| 1118 | - if( strncmp(zPath+nFilter, gsvn.zBranches, gsvn.lenBranches)==0 ){ | |
| 1119 | - int lenBranch; | |
| 1120 | - const char *zTemp = zPath+nFilter+gsvn.lenBranches; | |
| 1121 | - while( *zTemp && *zTemp!='/' ){ zTemp++; } | |
| 1122 | - lenBranch = zTemp-(zPath+nFilter+gsvn.lenBranches); | |
| 1123 | - zTemp = zPath+nFilter+gsvn.lenBranches; | |
| 1124 | - zBranch = fossil_malloc(lenBranch+1); | |
| 1125 | - memcpy(zBranch, zTemp, lenBranch); | |
| 1126 | - zBranch[lenBranch] = '\0'; | |
| 1127 | - }else | |
| 1128 | - if( strncmp(zPath+nFilter, gsvn.zTrunk, gsvn.lenTrunk-1)==0 ){ | |
| 1129 | - zBranch = mprintf("trunk"); | |
| 1130 | - } | |
| 1131 | - } | |
| 1132 | - return zBranch; | |
| 1214 | +/* | |
| 1215 | +** Extract the name of the branch or tag that the given path is on. | |
| 1216 | +** Returns: 1 - It is on the trunk | |
| 1217 | +** 2 - It is on a branch | |
| 1218 | +** 3 - It is a tag | |
| 1219 | +** 0 - It is none of the above | |
| 1220 | + */ | |
| 1221 | +static int *svn_parse_path(char *zPath, char **zBranch, char **zFile){ | |
| 1222 | + if( strncmp(zPath, gsvn.zTrunk, gsvn.lenTrunk)==0 ){ | |
| 1223 | + *zBranch = "trunk"; | |
| 1224 | + *zFile = zPath+gsvn.lenTrunk; | |
| 1225 | + return 1; | |
| 1226 | + }else | |
| 1227 | + if( strncmp(zPath, gsvn.zBranches, gsvn.lenBranches)==0 ){ | |
| 1228 | + *zFile = *zBranch = zPath+gsvn.lenBranches; | |
| 1229 | + while( **zFile && **zFile!='/' ){ (*zFile)++; } | |
| 1230 | + if( *zFile ){ | |
| 1231 | + **zFile = '\0'; | |
| 1232 | + (*zFile)++; | |
| 1233 | + }else{ | |
| 1234 | + *zFile = 0; | |
| 1235 | + } | |
| 1236 | + return 2; | |
| 1237 | + }else | |
| 1238 | + if( strncmp(zPath, gsvn.zTags, gsvn.lenTags)==0 ){ | |
| 1239 | + *zFile = *zBranch = zPath+gsvn.lenTags; | |
| 1240 | + while( **zFile && **zFile!='/' ){ (*zFile)++; } | |
| 1241 | + if( *zFile ){ | |
| 1242 | + **zFile = '\0'; | |
| 1243 | + (*zFile)++; | |
| 1244 | + }else{ | |
| 1245 | + *zFile = 0; | |
| 1246 | + } | |
| 1247 | + return 3; | |
| 1248 | + } | |
| 1249 | + *zFile = *zBranch = 0; | |
| 1250 | + return 0; | |
| 1133 | 1251 | } |
| 1134 | 1252 | |
| 1135 | 1253 | /* |
| 1136 | 1254 | ** Read the svn-dump format from pIn and insert the corresponding |
| 1137 | 1255 | ** content into the database. |
| 1138 | 1256 | */ |
| 1139 | 1257 | static void svn_dump_import(FILE *pIn){ |
| 1140 | 1258 | SvnRecord rec; |
| 1141 | 1259 | int ver; |
| 1142 | - const char *zTemp; | |
| 1260 | + char *zTemp; | |
| 1143 | 1261 | const char *zUuid; |
| 1144 | - Stmt addHist; | |
| 1145 | - Stmt insTag; | |
| 1146 | - Stmt cpyPath; | |
| 1262 | + Stmt addFile; | |
| 1147 | 1263 | Stmt delPath; |
| 1148 | - int bHasFiles = 0; | |
| 1149 | - int nFilter = blob_size(&gsvn.filter); | |
| 1264 | + Stmt addSrc; | |
| 1265 | + Stmt addBranch; | |
| 1266 | + Stmt cpyPath; | |
| 1150 | 1267 | |
| 1151 | 1268 | /* version */ |
| 1152 | 1269 | if( svn_read_rec(pIn, &rec) |
| 1153 | 1270 | && (zTemp = svn_find_header(rec, "SVN-fs-dump-format-version")) ){ |
| 1154 | 1271 | ver = atoi(zTemp); |
| @@ -1162,68 +1279,76 @@ | ||
| 1162 | 1279 | /* UUID */ |
| 1163 | 1280 | if( !svn_read_rec(pIn, &rec) || !(zUuid = svn_find_header(rec, "UUID")) ){ |
| 1164 | 1281 | fossil_fatal("Missing UUID!"); |
| 1165 | 1282 | } |
| 1166 | 1283 | svn_free_rec(&rec); |
| 1284 | + | |
| 1167 | 1285 | /* content */ |
| 1168 | - db_prepare(&addHist, | |
| 1169 | - "INSERT INTO xhist (trev, tpath, trid, tperm) " | |
| 1170 | - "VALUES(:rev, :path, :rid, :perm)" | |
| 1171 | - ); | |
| 1172 | - db_prepare(&insTag, "INSERT INTO xtags (trev, ttag) VALUES(:rev, :tag)"); | |
| 1173 | - db_prepare(&cpyPath, | |
| 1174 | - "WITH xsrc AS (SELECT * FROM (" | |
| 1175 | - " SELECT tpath, trid, tperm, max(trev) trev FROM xhist" | |
| 1176 | - " WHERE trev<=:srcrev GROUP BY tpath" | |
| 1177 | - " ) WHERE trid NOTNULL)" | |
| 1178 | - "INSERT INTO xhist (trev, tpath, trid, tperm)" | |
| 1179 | - " SELECT :rev, :path||substr(tpath, length(:srcpath)+1), trid, tperm" | |
| 1180 | - " FROM xsrc WHERE tpath>:srcpath||'/' AND tpath<:srcpath||'0'" | |
| 1286 | + db_prepare(&addFile, | |
| 1287 | + "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" | |
| 1288 | + " VALUES(:path, :branch, :uuid, :perm)" | |
| 1181 | 1289 | ); |
| 1182 | 1290 | db_prepare(&delPath, |
| 1183 | - "INSERT INTO xhist (trev, tpath, trid, tperm)" | |
| 1184 | - " SELECT :rev, tpath, NULL, NULL" | |
| 1185 | - " FROM xfiles WHERE (tpath>:path||'/' AND tpath<:path||'0') OR tpath=:path" | |
| 1291 | + "DELETE FROM xfiles" | |
| 1292 | + " WHERE (tpath=:path OR (tpath>:path||'/' AND tpath<:path||'0'))" | |
| 1293 | + " AND tbranch=:branch" | |
| 1294 | + ); | |
| 1295 | + db_prepare(&addSrc, | |
| 1296 | + "INSERT INTO xsrc (tpath, tbranch, tsrc, tsrcbranch, tsrcrev)" | |
| 1297 | + " VALUES(:path, :branch, :srcpath, :srcbranch, :srcrev)" | |
| 1298 | + ); | |
| 1299 | + db_prepare(&addBranch, | |
| 1300 | + "INSERT INTO xchanges (tbranch, ttype) VALUES(:branch, :type)" | |
| 1301 | + ); | |
| 1302 | + db_prepare(&cpyPath, | |
| 1303 | + "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" | |
| 1304 | + " SELECT :path||substr(filename, length(:srcpath)+1), :branch, uuid, perm" | |
| 1305 | + " FROM xfoci" | |
| 1306 | + " WHERE chekinID=:rid AND filename>:srcpath||'/' AND filename<:srcpath||'0'" | |
| 1186 | 1307 | ); |
| 1187 | 1308 | gsvn.rev = -1; |
| 1188 | 1309 | while( svn_read_rec(pIn, &rec) ){ |
| 1189 | 1310 | if( (zTemp = svn_find_header(rec, "Revision-number")) ){ /* revision node */ |
| 1190 | 1311 | /* finish previous revision */ |
| 1191 | 1312 | const char *zDate = NULL; |
| 1192 | - if( bHasFiles ){ | |
| 1193 | - svn_create_manifest(); | |
| 1194 | - } | |
| 1195 | - fossil_free(gsvn.zUser); | |
| 1196 | - fossil_free(gsvn.zComment); | |
| 1197 | - fossil_free(gsvn.zDate); | |
| 1198 | - fossil_free(gsvn.zBranch); | |
| 1199 | - fossil_free(gsvn.zParentBranch); | |
| 1313 | + if( gsvn.rev>=0 ){ | |
| 1314 | + svn_finish_revision(); | |
| 1315 | + fossil_free(gsvn.zUser); | |
| 1316 | + fossil_free(gsvn.zComment); | |
| 1317 | + fossil_free(gsvn.zDate); | |
| 1318 | + } | |
| 1200 | 1319 | /* start new revision */ |
| 1201 | 1320 | gsvn.rev = atoi(zTemp); |
| 1202 | 1321 | gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author")); |
| 1203 | 1322 | gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log")); |
| 1204 | 1323 | zDate = svn_find_prop(rec, "svn:date"); |
| 1205 | 1324 | if( zDate ){ |
| 1206 | - gsvn.zDate = date_in_standard_format(zDate); | |
| 1207 | - } | |
| 1208 | - gsvn.parentRev = -1; | |
| 1209 | - gsvn.zParentBranch = 0; | |
| 1210 | - gsvn.zBranch = 0; | |
| 1211 | - bHasFiles = 0; | |
| 1212 | - fossil_print("\rImporting SVN revision: %d", gsvn.rev); | |
| 1213 | - db_bind_int(&addHist, ":rev", gsvn.rev); | |
| 1214 | - db_bind_int(&cpyPath, ":rev", gsvn.rev); | |
| 1215 | - db_bind_int(&delPath, ":rev", gsvn.rev); | |
| 1325 | + zDate = date_in_standard_format(zDate); | |
| 1326 | + } | |
| 1327 | + gsvn.zDate = zDate; | |
| 1328 | + fossil_print("\rImporting SVN revision: %d", gsvn.rev); | |
| 1216 | 1329 | }else |
| 1217 | 1330 | if( (zTemp = svn_find_header(rec, "Node-path")) ){ /* file/dir node */ |
| 1218 | - const char *zPath = zTemp; | |
| 1219 | 1331 | const char *zAction = svn_find_header(rec, "Node-action"); |
| 1220 | 1332 | const char *zKind = svn_find_header(rec, "Node-kind"); |
| 1221 | 1333 | const char *zSrcPath = svn_find_header(rec, "Node-copyfrom-path"); |
| 1222 | 1334 | const char *zPerm = svn_find_prop(rec, "svn:executable") ? "x" : 0; |
| 1335 | + char *zBranch; | |
| 1336 | + char *zFile; | |
| 1337 | + char *zSrcBranch; | |
| 1338 | + char *zSrcFile; | |
| 1223 | 1339 | int deltaFlag = 0; |
| 1224 | 1340 | int srcRev = 0; |
| 1341 | + int branchType = svn_parse_path(zTemp, &zBranch, &zFile); | |
| 1342 | + if( branchType==0 ){ | |
| 1343 | + svn_free_rec(&rec); | |
| 1344 | + continue; | |
| 1345 | + } | |
| 1346 | + db_bind_text(&addBranch, ":branch", zBranch); | |
| 1347 | + db_bind_int(&addBranch, ":type", branchType); | |
| 1348 | + db_step(&addBranch); | |
| 1349 | + db_reset(&addBranch); | |
| 1225 | 1350 | if( (zTemp = svn_find_header(rec, "Text-delta")) ){ |
| 1226 | 1351 | deltaFlag = strncmp(zTemp, "true", 4)==0; |
| 1227 | 1352 | } |
| 1228 | 1353 | if( zSrcPath ){ |
| 1229 | 1354 | zTemp = svn_find_header(rec, "Node-copyfrom-rev"); |
| @@ -1230,71 +1355,55 @@ | ||
| 1230 | 1355 | if( zTemp ){ |
| 1231 | 1356 | srcRev = atoi(zTemp); |
| 1232 | 1357 | }else{ |
| 1233 | 1358 | fossil_fatal("Missing copyfrom-rev"); |
| 1234 | 1359 | } |
| 1235 | - } | |
| 1236 | - if( !gsvn.flatFlag ){ | |
| 1237 | - char *zBranch; | |
| 1238 | - if( (zBranch=svn_extract_branch(zPath))!=0 ){ | |
| 1239 | - if( gsvn.zBranch!=0 ){ | |
| 1240 | - if( strcmp(zBranch, gsvn.zBranch)!=0 | |
| 1241 | - && strncmp(zAction, "delete", 6)!=0) | |
| 1242 | - { | |
| 1243 | - fossil_fatal("Commit to multiple branches"); | |
| 1244 | - } | |
| 1245 | - fossil_free(zBranch); | |
| 1246 | - }else{ | |
| 1247 | - gsvn.zBranch = zBranch; | |
| 1248 | - } | |
| 1249 | - } | |
| 1360 | + if( svn_extract_branch(zSrcPath, &zSrcBranch, &zSrcFile)==0 ){ | |
| 1361 | + fossil_fatal("Copy from path outside the import paths"); | |
| 1362 | + } | |
| 1363 | + db_bind_text(&addSrc, ":path", zFile); | |
| 1364 | + db_bind_text(&addSrc, ":branch", zBranch); | |
| 1365 | + db_bind_text(&addSrc, ":srcpath", zSrcFile); | |
| 1366 | + db_bind_text(&addSrc, ":srcbranch", zSrcBranch); | |
| 1367 | + db_bind_int(&addSrc, ":srcrev", srcRev); | |
| 1368 | + db_step(&addSrc); | |
| 1369 | + db_reset(&addSrc); | |
| 1250 | 1370 | } |
| 1251 | 1371 | if( strncmp(zAction, "delete", 6)==0 |
| 1252 | 1372 | || strncmp(zAction, "replace", 7)==0 ) |
| 1253 | 1373 | { |
| 1254 | - db_bind_text(&delPath, ":path", zPath); | |
| 1374 | + db_bind_text(&delPath, ":path", zFile); | |
| 1375 | + db_bind_text(&delPath, ":branch", zBranch); | |
| 1255 | 1376 | db_step(&delPath); |
| 1256 | 1377 | db_reset(&delPath); |
| 1257 | - bHasFiles = 1; | |
| 1258 | 1378 | } /* no 'else' here since 'replace' does both a 'delete' and an 'add' */ |
| 1259 | 1379 | if( strncmp(zAction, "add", 3)==0 |
| 1260 | 1380 | || strncmp(zAction, "replace", 7)==0 ) |
| 1261 | 1381 | { |
| 1262 | 1382 | if( zKind==0 ){ |
| 1263 | 1383 | fossil_fatal("Missing Node-kind"); |
| 1264 | 1384 | }else if( strncmp(zKind, "dir", 3)==0 ){ |
| 1265 | 1385 | if( zSrcPath ){ |
| 1266 | - db_bind_int(&cpyPath, ":srcrev", srcRev); | |
| 1267 | - db_bind_text(&cpyPath, ":path", zPath); | |
| 1268 | - db_bind_text(&cpyPath, ":srcpath", zSrcPath); | |
| 1386 | + int srcRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" | |
| 1387 | + " WHERE trev<=%d AND tbranch=%Q", | |
| 1388 | + srcRev, zSrcBranch); | |
| 1389 | + db_bind_text(&cpyPath, ":path", zFile); | |
| 1390 | + db_bind_text(&cpyPath, ":branch", zBranch); | |
| 1391 | + db_bind_text(&cpyPath, ":srcpath", zSrcFile); | |
| 1392 | + db_bind_int(&cpyPath, ":rid", srcRid); | |
| 1269 | 1393 | db_step(&cpyPath); |
| 1270 | 1394 | db_reset(&cpyPath); |
| 1271 | - bHasFiles = 1; | |
| 1272 | - if( !gsvn.flatFlag ){ | |
| 1273 | - if( strncmp(zPath+nFilter, gsvn.zBranches, gsvn.lenBranches)==0 ){ | |
| 1274 | - zTemp = zPath+nFilter+gsvn.lenBranches+strlen(gsvn.zBranch); | |
| 1275 | - if( *zTemp==0 ){ | |
| 1276 | - gsvn.parentRev = srcRev; | |
| 1277 | - gsvn.zParentBranch = svn_extract_branch(zSrcPath); | |
| 1278 | - } | |
| 1279 | - }else if( strncmp(zPath+nFilter, gsvn.zTags, gsvn.lenTags)==0 ){ | |
| 1280 | - zTemp = zPath+nFilter+gsvn.lenTags; | |
| 1281 | - db_bind_int(&insTag, ":rev", srcRev); | |
| 1282 | - db_bind_text(&insTag, ":tag", zTemp); | |
| 1283 | - db_step(&insTag); | |
| 1284 | - db_reset(&insTag); | |
| 1285 | - } | |
| 1286 | - } | |
| 1287 | 1395 | } |
| 1288 | 1396 | }else{ |
| 1289 | 1397 | int rid = 0; |
| 1290 | 1398 | if( zSrcPath ){ |
| 1291 | - rid = db_int(0, "SELECT trid, max(trev) FROM xhist" | |
| 1292 | - " WHERE trev<=%d AND tpath=%Q", srcRev, zSrcPath); | |
| 1293 | - if( rid==0 ){ | |
| 1294 | - fossil_fatal("Reference to non-existent path/revision"); | |
| 1295 | - } | |
| 1399 | + int srcRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" | |
| 1400 | + " WHERE trev<=%d AND tbranch=%Q", | |
| 1401 | + srcRev, zSrcBranch); | |
| 1402 | + rid = db_int(0, "SELECT rid FROM xfoci" | |
| 1403 | + " WHERE chekinID=%d AND filename=%Q", | |
| 1404 | + srcRid, zSrcFile) | |
| 1296 | 1405 | } |
| 1297 | 1406 | if( deltaFlag ){ |
| 1298 | 1407 | Blob deltaSrc; |
| 1299 | 1408 | Blob target; |
| 1300 | 1409 | if( rid!=0 ){ |
| @@ -1338,11 +1447,11 @@ | ||
| 1338 | 1447 | db_step(&addHist); |
| 1339 | 1448 | db_reset(&addHist); |
| 1340 | 1449 | bHasFiles = 1; |
| 1341 | 1450 | } |
| 1342 | 1451 | }else |
| 1343 | - if( strncmp(zAction, "delete", 6)!=0 ){ /* already did this above */ | |
| 1452 | + if( strncmp(zAction, "delete", 6)!=0 ){ /* already did this one above */ | |
| 1344 | 1453 | fossil_fatal("Unknown Node-action"); |
| 1345 | 1454 | } |
| 1346 | 1455 | }else{ |
| 1347 | 1456 | fossil_fatal("Unknown record type"); |
| 1348 | 1457 | } |
| @@ -1374,18 +1483,19 @@ | ||
| 1374 | 1483 | ** The following formats are currently understood by this command |
| 1375 | 1484 | ** |
| 1376 | 1485 | ** git Import from the git-fast-export file format |
| 1377 | 1486 | ** |
| 1378 | 1487 | ** svn Import from the svnadmin-dump file format. The default |
| 1379 | -** behaviour is to treat 3 folders in the SVN root as special, | |
| 1380 | -** following the common layout of SVN repositories. These are | |
| 1381 | -** (by default) trunk/, branches/ and tags/ | |
| 1488 | +** behaviour (unless overridden by --flat) is to treat 3 folders | |
| 1489 | +** in the SVN root as special, following the common layout of | |
| 1490 | +** SVN repositories. These are (by default) trunk/, branches/ | |
| 1491 | +** and tags/ | |
| 1382 | 1492 | ** Options: |
| 1383 | 1493 | ** --trunk FOLDER Name of trunk folder |
| 1384 | 1494 | ** --branches FOLDER Name of branches folder |
| 1385 | 1495 | ** --tags FOLDER Name of tags folder |
| 1386 | -** --filter PATH Path to project root in repository | |
| 1496 | +** --base PATH Path to project root in repository | |
| 1387 | 1497 | ** --flat The whole dump is a single branch |
| 1388 | 1498 | ** |
| 1389 | 1499 | ** The --incremental option allows an existing repository to be extended |
| 1390 | 1500 | ** with new content. |
| 1391 | 1501 | ** |
| @@ -1396,18 +1506,18 @@ | ||
| 1396 | 1506 | */ |
| 1397 | 1507 | void import_cmd(void){ |
| 1398 | 1508 | char *zPassword; |
| 1399 | 1509 | FILE *pIn; |
| 1400 | 1510 | Stmt q; |
| 1401 | - const char *zFilter = find_option("filter", 0, 1); | |
| 1511 | + const char *zBase = find_option("base", 0, 1); | |
| 1402 | 1512 | int lenFilter; |
| 1403 | 1513 | int forceFlag = find_option("force", "f", 0)!=0; |
| 1404 | 1514 | int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1405 | 1515 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1406 | 1516 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1407 | 1517 | gsvn.zTags = find_option("tags", 0, 1); |
| 1408 | - gsvn.flatFlag = find_option("flat", 0, 0)!=0; | |
| 1518 | + int flatFlag = find_option("flat", 0, 0)!=0; | |
| 1409 | 1519 | |
| 1410 | 1520 | verify_all_options(); |
| 1411 | 1521 | if( g.argc!=4 && g.argc!=5 ){ |
| 1412 | 1522 | usage("FORMAT REPOSITORY-NAME"); |
| 1413 | 1523 | } |
| @@ -1464,54 +1574,59 @@ | ||
| 1464 | 1574 | db_finalize(&q); |
| 1465 | 1575 | }else |
| 1466 | 1576 | if( strncmp(g.argv[2], "svn", 3)==0 ){ |
| 1467 | 1577 | db_multi_exec( |
| 1468 | 1578 | "CREATE TEMP TABLE xrevisions(" |
| 1469 | - " trev INTEGER PRIMARY KEY, tbranch TEXT, tuuid TEXT" | |
| 1470 | - ");" | |
| 1471 | - "CREATE TEMP TABLE xhist(" | |
| 1472 | - " trev INT, tpath TEXT NOT NULL, trid TEXT, tperm TEXT," | |
| 1473 | - " UNIQUE (trev, tpath) ON CONFLICT REPLACE" | |
| 1579 | + " trev INTEGER, tbranch TEXT, trid INT, PRIMARY KEY(tbranch, trev)" | |
| 1474 | 1580 | ");" |
| 1475 | 1581 | "CREATE TEMP TABLE xfiles(" |
| 1476 | - " tpath TEXT NOT NULL, trid TEXT, tperm TEXT," | |
| 1477 | - " UNIQUE (tpath) ON CONFLICT REPLACE" | |
| 1478 | - ");" | |
| 1479 | - "CREATE TEMP TRIGGER xfilesdeltrig AFTER INSERT ON xhist FOR EACH ROW" | |
| 1480 | - " WHEN new.trid ISNULL" | |
| 1481 | - " BEGIN DELETE FROM xfiles WHERE xfiles.tpath=new.tpath; END;" | |
| 1482 | - "CREATE TEMP TRIGGER xfilesaddtrig AFTER INSERT ON xhist FOR EACH ROW" | |
| 1483 | - " WHEN new.trid NOTNULL BEGIN INSERT INTO xfiles(tpath,trid,tperm)" | |
| 1484 | - " VALUES(new.tpath, new.trid, new.tperm); END;" | |
| 1485 | - "CREATE TEMP TABLE xtags(" | |
| 1486 | - " trev INT, ttag TEXT" | |
| 1487 | - ");" | |
| 1582 | + " tpath TEXT, tbranch TEXT, tuuid TEXT, tperm TEXT," | |
| 1583 | + " UNIQUE (tbranch, tpath) ON CONFLICT REPLACE" | |
| 1584 | + ");" | |
| 1585 | + "CREATE TEMP TABLE xsrc(" | |
| 1586 | + " tpath TEXT, tbranch TEXT, tsrc TEXT, tsrcbranch TEXT, tsrcrev INT" | |
| 1587 | + ");" | |
| 1588 | + "CREATE TEMP TABLE xchanged(" | |
| 1589 | + " tbranch TEXT, ttype INT," | |
| 1590 | + " UNIQUE (tbranch) ON CONFLICT REPLACE" | |
| 1591 | + ");" | |
| 1592 | + "CREATE VIRTUAL TABLE temp.xfoci USING files_of_checkin;" | |
| 1488 | 1593 | ); |
| 1489 | - if( gsvn.zTrunk==0 ){ gsvn.zTrunk = "trunk/"; } | |
| 1490 | - if( gsvn.zBranches==0 ){ gsvn.zBranches = "branches/"; } | |
| 1491 | - if( gsvn.zTags==0 ){ gsvn.zTags = "tags/"; } | |
| 1492 | - gsvn.lenTrunk = strlen(gsvn.zTrunk); | |
| 1493 | - gsvn.lenBranches = strlen(gsvn.zBranches); | |
| 1494 | - gsvn.lenTags = strlen(gsvn.zTags); | |
| 1495 | - if( gsvn.zTrunk[gsvn.lenTrunk-1]!='/' ){ | |
| 1496 | - gsvn.zTrunk = mprintf("%s/", gsvn.zTrunk); | |
| 1497 | - gsvn.lenTrunk++; | |
| 1498 | - } | |
| 1499 | - if( gsvn.zBranches[gsvn.lenBranches-1]!='/' ){ | |
| 1500 | - gsvn.zBranches = mprintf("%s/", gsvn.zBranches); | |
| 1501 | - gsvn.lenBranches++; | |
| 1502 | - } | |
| 1503 | - if( gsvn.zTags[gsvn.lenTags-1]!='/' ){ | |
| 1504 | - gsvn.zTags = mprintf("%s/", gsvn.zTags); | |
| 1505 | - gsvn.lenTags++; | |
| 1506 | - } | |
| 1507 | - if( zFilter==0 ){ zFilter = ""; } | |
| 1508 | - lenFilter = strlen(zFilter); | |
| 1509 | - blob_zero(&gsvn.filter); | |
| 1510 | - blob_set(&gsvn.filter, zFilter); | |
| 1511 | - if( lenFilter>0 && zFilter[lenFilter-1]!='/' ){ | |
| 1512 | - blob_append(&gsvn.filter, "/", 1); | |
| 1594 | + if( zBase==0 ){ zBase = ""; } | |
| 1595 | + if( strlen(zBase)>0 ){ | |
| 1596 | + if( zBase[strlen(zBase)-1]!='/' ){ | |
| 1597 | + zBase = mprintf("%s/", zBase); | |
| 1598 | + } | |
| 1599 | + if( flatFlag ){ | |
| 1600 | + gsvn.zTrunk = zBase; | |
| 1601 | + gsvn.zBranches = 0; | |
| 1602 | + gsvn.zTags = 0; | |
| 1603 | + gsvn.lenTrunk = strlen(zBase); | |
| 1604 | + gsvn.lenBranches = 0; | |
| 1605 | + gsvn.lenTags = 0; | |
| 1606 | + }else{ | |
| 1607 | + if( gsvn.zTrunk==0 ){ gsvn.zTrunk = "trunk/"; } | |
| 1608 | + if( gsvn.zBranches==0 ){ gsvn.zBranches = "branches/"; } | |
| 1609 | + if( gsvn.zTags==0 ){ gsvn.zTags = "tags/"; } | |
| 1610 | + gsvn.zTrunk = mprintf("%s%s", zBase, gsvn.zTrunk); | |
| 1611 | + gsvn.zBranches = mprintf("%s%s", zBase, gsvn.zBranches); | |
| 1612 | + gsvn.zTags = mprintf("%s%s", zBase, gsvn.zTags); | |
| 1613 | + gsvn.lenTrunk = strlen(gsvn.zTrunk); | |
| 1614 | + gsvn.lenBranches = strlen(gsvn.zBranches); | |
| 1615 | + gsvn.lenTags = strlen(gsvn.zTags); | |
| 1616 | + if( gsvn.zTrunk[gsvn.lenTrunk-1]!='/' ){ | |
| 1617 | + gsvn.zTrunk = mprintf("%s/", gsvn.zTrunk); | |
| 1618 | + gsvn.lenTrunk++; | |
| 1619 | + } | |
| 1620 | + if( gsvn.zBranches[gsvn.lenBranches-1]!='/' ){ | |
| 1621 | + gsvn.zBranches = mprintf("%s/", gsvn.zBranches); | |
| 1622 | + gsvn.lenBranches++; | |
| 1623 | + } | |
| 1624 | + if( gsvn.zTags[gsvn.lenTags-1]!='/' ){ | |
| 1625 | + gsvn.zTags = mprintf("%s/", gsvn.zTags); | |
| 1626 | + gsvn.lenTags++; | |
| 1627 | + } | |
| 1513 | 1628 | } |
| 1514 | 1629 | svn_dump_import(pIn); |
| 1515 | 1630 | } |
| 1516 | 1631 | |
| 1517 | 1632 | verify_cancel(); |
| 1518 | 1633 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -12,12 +12,12 @@ | |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** |
| 15 | ******************************************************************************* |
| 16 | ** |
| 17 | ** This file contains code used to import the content of a Git |
| 18 | ** repository in the git-fast-import format as a new Fossil |
| 19 | ** repository. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "import.h" |
| 23 | #include <assert.h> |
| @@ -718,25 +718,125 @@ | |
| 718 | fossil_fatal("bad fast-import line: [%s]", zLine); |
| 719 | return; |
| 720 | } |
| 721 | |
| 722 | static struct{ |
| 723 | int rev; /* SVN revision number */ |
| 724 | int parentRev; /* SVN revision number of parent check-in */ |
| 725 | char *zParentBranch; /* Name of branch of parent check-in */ |
| 726 | char *zBranch; /* Name of a branch for a commit */ |
| 727 | char *zDate; /* Date/time stamp */ |
| 728 | char *zUser; /* User name */ |
| 729 | char *zComment; /* Comment of a commit */ |
| 730 | int flatFlag; /* True if whole repo is a single file tree */ |
| 731 | const char *zTrunk; /* Name of trunk folder in repo root */ |
| 732 | int lenTrunk; /* String length of zTrunk */ |
| 733 | const char *zBranches; /* Name of branches folder in repo root */ |
| 734 | int lenBranches; /* String length of zBranches */ |
| 735 | const char *zTags; /* Name of tags folder in repo root */ |
| 736 | int lenTags; /* String length of zTags */ |
| 737 | Blob filter; /* Path to repo root */ |
| 738 | } gsvn; |
| 739 | typedef struct { |
| 740 | char *zKey; |
| 741 | const char *zVal; |
| 742 | } KeyVal; |
| @@ -774,11 +874,11 @@ | |
| 774 | fossil_free(rec->aHeaders[i].zKey); |
| 775 | } |
| 776 | fossil_free(rec->aHeaders); |
| 777 | fossil_free(rec->aProps); |
| 778 | fossil_free(rec->pRawProps); |
| 779 | blob_reset(&rec->content); |
| 780 | } |
| 781 | |
| 782 | static int svn_read_headers(FILE *pIn, SvnRecord *rec){ |
| 783 | char zLine[1000]; |
| 784 | |
| @@ -903,11 +1003,11 @@ | |
| 903 | rec->contentFlag = 0; |
| 904 | } |
| 905 | return 1; |
| 906 | } |
| 907 | |
| 908 | static void svn_create_manifest( |
| 909 | ){ |
| 910 | Blob manifest; |
| 911 | static Stmt insRev; |
| 912 | static Stmt qParent; |
| 913 | static Stmt qParent2; |
| @@ -1109,46 +1209,63 @@ | |
| 1109 | } |
| 1110 | zDiff += lenData; |
| 1111 | } |
| 1112 | } |
| 1113 | |
| 1114 | static char *svn_extract_branch(const char *zPath){ |
| 1115 | int nFilter = blob_size(&gsvn.filter); |
| 1116 | char *zBranch = 0; |
| 1117 | if( strncmp(zPath, blob_str(&gsvn.filter), nFilter)==0 ){ |
| 1118 | if( strncmp(zPath+nFilter, gsvn.zBranches, gsvn.lenBranches)==0 ){ |
| 1119 | int lenBranch; |
| 1120 | const char *zTemp = zPath+nFilter+gsvn.lenBranches; |
| 1121 | while( *zTemp && *zTemp!='/' ){ zTemp++; } |
| 1122 | lenBranch = zTemp-(zPath+nFilter+gsvn.lenBranches); |
| 1123 | zTemp = zPath+nFilter+gsvn.lenBranches; |
| 1124 | zBranch = fossil_malloc(lenBranch+1); |
| 1125 | memcpy(zBranch, zTemp, lenBranch); |
| 1126 | zBranch[lenBranch] = '\0'; |
| 1127 | }else |
| 1128 | if( strncmp(zPath+nFilter, gsvn.zTrunk, gsvn.lenTrunk-1)==0 ){ |
| 1129 | zBranch = mprintf("trunk"); |
| 1130 | } |
| 1131 | } |
| 1132 | return zBranch; |
| 1133 | } |
| 1134 | |
| 1135 | /* |
| 1136 | ** Read the svn-dump format from pIn and insert the corresponding |
| 1137 | ** content into the database. |
| 1138 | */ |
| 1139 | static void svn_dump_import(FILE *pIn){ |
| 1140 | SvnRecord rec; |
| 1141 | int ver; |
| 1142 | const char *zTemp; |
| 1143 | const char *zUuid; |
| 1144 | Stmt addHist; |
| 1145 | Stmt insTag; |
| 1146 | Stmt cpyPath; |
| 1147 | Stmt delPath; |
| 1148 | int bHasFiles = 0; |
| 1149 | int nFilter = blob_size(&gsvn.filter); |
| 1150 | |
| 1151 | /* version */ |
| 1152 | if( svn_read_rec(pIn, &rec) |
| 1153 | && (zTemp = svn_find_header(rec, "SVN-fs-dump-format-version")) ){ |
| 1154 | ver = atoi(zTemp); |
| @@ -1162,68 +1279,76 @@ | |
| 1162 | /* UUID */ |
| 1163 | if( !svn_read_rec(pIn, &rec) || !(zUuid = svn_find_header(rec, "UUID")) ){ |
| 1164 | fossil_fatal("Missing UUID!"); |
| 1165 | } |
| 1166 | svn_free_rec(&rec); |
| 1167 | /* content */ |
| 1168 | db_prepare(&addHist, |
| 1169 | "INSERT INTO xhist (trev, tpath, trid, tperm) " |
| 1170 | "VALUES(:rev, :path, :rid, :perm)" |
| 1171 | ); |
| 1172 | db_prepare(&insTag, "INSERT INTO xtags (trev, ttag) VALUES(:rev, :tag)"); |
| 1173 | db_prepare(&cpyPath, |
| 1174 | "WITH xsrc AS (SELECT * FROM (" |
| 1175 | " SELECT tpath, trid, tperm, max(trev) trev FROM xhist" |
| 1176 | " WHERE trev<=:srcrev GROUP BY tpath" |
| 1177 | " ) WHERE trid NOTNULL)" |
| 1178 | "INSERT INTO xhist (trev, tpath, trid, tperm)" |
| 1179 | " SELECT :rev, :path||substr(tpath, length(:srcpath)+1), trid, tperm" |
| 1180 | " FROM xsrc WHERE tpath>:srcpath||'/' AND tpath<:srcpath||'0'" |
| 1181 | ); |
| 1182 | db_prepare(&delPath, |
| 1183 | "INSERT INTO xhist (trev, tpath, trid, tperm)" |
| 1184 | " SELECT :rev, tpath, NULL, NULL" |
| 1185 | " FROM xfiles WHERE (tpath>:path||'/' AND tpath<:path||'0') OR tpath=:path" |
| 1186 | ); |
| 1187 | gsvn.rev = -1; |
| 1188 | while( svn_read_rec(pIn, &rec) ){ |
| 1189 | if( (zTemp = svn_find_header(rec, "Revision-number")) ){ /* revision node */ |
| 1190 | /* finish previous revision */ |
| 1191 | const char *zDate = NULL; |
| 1192 | if( bHasFiles ){ |
| 1193 | svn_create_manifest(); |
| 1194 | } |
| 1195 | fossil_free(gsvn.zUser); |
| 1196 | fossil_free(gsvn.zComment); |
| 1197 | fossil_free(gsvn.zDate); |
| 1198 | fossil_free(gsvn.zBranch); |
| 1199 | fossil_free(gsvn.zParentBranch); |
| 1200 | /* start new revision */ |
| 1201 | gsvn.rev = atoi(zTemp); |
| 1202 | gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author")); |
| 1203 | gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log")); |
| 1204 | zDate = svn_find_prop(rec, "svn:date"); |
| 1205 | if( zDate ){ |
| 1206 | gsvn.zDate = date_in_standard_format(zDate); |
| 1207 | } |
| 1208 | gsvn.parentRev = -1; |
| 1209 | gsvn.zParentBranch = 0; |
| 1210 | gsvn.zBranch = 0; |
| 1211 | bHasFiles = 0; |
| 1212 | fossil_print("\rImporting SVN revision: %d", gsvn.rev); |
| 1213 | db_bind_int(&addHist, ":rev", gsvn.rev); |
| 1214 | db_bind_int(&cpyPath, ":rev", gsvn.rev); |
| 1215 | db_bind_int(&delPath, ":rev", gsvn.rev); |
| 1216 | }else |
| 1217 | if( (zTemp = svn_find_header(rec, "Node-path")) ){ /* file/dir node */ |
| 1218 | const char *zPath = zTemp; |
| 1219 | const char *zAction = svn_find_header(rec, "Node-action"); |
| 1220 | const char *zKind = svn_find_header(rec, "Node-kind"); |
| 1221 | const char *zSrcPath = svn_find_header(rec, "Node-copyfrom-path"); |
| 1222 | const char *zPerm = svn_find_prop(rec, "svn:executable") ? "x" : 0; |
| 1223 | int deltaFlag = 0; |
| 1224 | int srcRev = 0; |
| 1225 | if( (zTemp = svn_find_header(rec, "Text-delta")) ){ |
| 1226 | deltaFlag = strncmp(zTemp, "true", 4)==0; |
| 1227 | } |
| 1228 | if( zSrcPath ){ |
| 1229 | zTemp = svn_find_header(rec, "Node-copyfrom-rev"); |
| @@ -1230,71 +1355,55 @@ | |
| 1230 | if( zTemp ){ |
| 1231 | srcRev = atoi(zTemp); |
| 1232 | }else{ |
| 1233 | fossil_fatal("Missing copyfrom-rev"); |
| 1234 | } |
| 1235 | } |
| 1236 | if( !gsvn.flatFlag ){ |
| 1237 | char *zBranch; |
| 1238 | if( (zBranch=svn_extract_branch(zPath))!=0 ){ |
| 1239 | if( gsvn.zBranch!=0 ){ |
| 1240 | if( strcmp(zBranch, gsvn.zBranch)!=0 |
| 1241 | && strncmp(zAction, "delete", 6)!=0) |
| 1242 | { |
| 1243 | fossil_fatal("Commit to multiple branches"); |
| 1244 | } |
| 1245 | fossil_free(zBranch); |
| 1246 | }else{ |
| 1247 | gsvn.zBranch = zBranch; |
| 1248 | } |
| 1249 | } |
| 1250 | } |
| 1251 | if( strncmp(zAction, "delete", 6)==0 |
| 1252 | || strncmp(zAction, "replace", 7)==0 ) |
| 1253 | { |
| 1254 | db_bind_text(&delPath, ":path", zPath); |
| 1255 | db_step(&delPath); |
| 1256 | db_reset(&delPath); |
| 1257 | bHasFiles = 1; |
| 1258 | } /* no 'else' here since 'replace' does both a 'delete' and an 'add' */ |
| 1259 | if( strncmp(zAction, "add", 3)==0 |
| 1260 | || strncmp(zAction, "replace", 7)==0 ) |
| 1261 | { |
| 1262 | if( zKind==0 ){ |
| 1263 | fossil_fatal("Missing Node-kind"); |
| 1264 | }else if( strncmp(zKind, "dir", 3)==0 ){ |
| 1265 | if( zSrcPath ){ |
| 1266 | db_bind_int(&cpyPath, ":srcrev", srcRev); |
| 1267 | db_bind_text(&cpyPath, ":path", zPath); |
| 1268 | db_bind_text(&cpyPath, ":srcpath", zSrcPath); |
| 1269 | db_step(&cpyPath); |
| 1270 | db_reset(&cpyPath); |
| 1271 | bHasFiles = 1; |
| 1272 | if( !gsvn.flatFlag ){ |
| 1273 | if( strncmp(zPath+nFilter, gsvn.zBranches, gsvn.lenBranches)==0 ){ |
| 1274 | zTemp = zPath+nFilter+gsvn.lenBranches+strlen(gsvn.zBranch); |
| 1275 | if( *zTemp==0 ){ |
| 1276 | gsvn.parentRev = srcRev; |
| 1277 | gsvn.zParentBranch = svn_extract_branch(zSrcPath); |
| 1278 | } |
| 1279 | }else if( strncmp(zPath+nFilter, gsvn.zTags, gsvn.lenTags)==0 ){ |
| 1280 | zTemp = zPath+nFilter+gsvn.lenTags; |
| 1281 | db_bind_int(&insTag, ":rev", srcRev); |
| 1282 | db_bind_text(&insTag, ":tag", zTemp); |
| 1283 | db_step(&insTag); |
| 1284 | db_reset(&insTag); |
| 1285 | } |
| 1286 | } |
| 1287 | } |
| 1288 | }else{ |
| 1289 | int rid = 0; |
| 1290 | if( zSrcPath ){ |
| 1291 | rid = db_int(0, "SELECT trid, max(trev) FROM xhist" |
| 1292 | " WHERE trev<=%d AND tpath=%Q", srcRev, zSrcPath); |
| 1293 | if( rid==0 ){ |
| 1294 | fossil_fatal("Reference to non-existent path/revision"); |
| 1295 | } |
| 1296 | } |
| 1297 | if( deltaFlag ){ |
| 1298 | Blob deltaSrc; |
| 1299 | Blob target; |
| 1300 | if( rid!=0 ){ |
| @@ -1338,11 +1447,11 @@ | |
| 1338 | db_step(&addHist); |
| 1339 | db_reset(&addHist); |
| 1340 | bHasFiles = 1; |
| 1341 | } |
| 1342 | }else |
| 1343 | if( strncmp(zAction, "delete", 6)!=0 ){ /* already did this above */ |
| 1344 | fossil_fatal("Unknown Node-action"); |
| 1345 | } |
| 1346 | }else{ |
| 1347 | fossil_fatal("Unknown record type"); |
| 1348 | } |
| @@ -1374,18 +1483,19 @@ | |
| 1374 | ** The following formats are currently understood by this command |
| 1375 | ** |
| 1376 | ** git Import from the git-fast-export file format |
| 1377 | ** |
| 1378 | ** svn Import from the svnadmin-dump file format. The default |
| 1379 | ** behaviour is to treat 3 folders in the SVN root as special, |
| 1380 | ** following the common layout of SVN repositories. These are |
| 1381 | ** (by default) trunk/, branches/ and tags/ |
| 1382 | ** Options: |
| 1383 | ** --trunk FOLDER Name of trunk folder |
| 1384 | ** --branches FOLDER Name of branches folder |
| 1385 | ** --tags FOLDER Name of tags folder |
| 1386 | ** --filter PATH Path to project root in repository |
| 1387 | ** --flat The whole dump is a single branch |
| 1388 | ** |
| 1389 | ** The --incremental option allows an existing repository to be extended |
| 1390 | ** with new content. |
| 1391 | ** |
| @@ -1396,18 +1506,18 @@ | |
| 1396 | */ |
| 1397 | void import_cmd(void){ |
| 1398 | char *zPassword; |
| 1399 | FILE *pIn; |
| 1400 | Stmt q; |
| 1401 | const char *zFilter = find_option("filter", 0, 1); |
| 1402 | int lenFilter; |
| 1403 | int forceFlag = find_option("force", "f", 0)!=0; |
| 1404 | int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1405 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1406 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1407 | gsvn.zTags = find_option("tags", 0, 1); |
| 1408 | gsvn.flatFlag = find_option("flat", 0, 0)!=0; |
| 1409 | |
| 1410 | verify_all_options(); |
| 1411 | if( g.argc!=4 && g.argc!=5 ){ |
| 1412 | usage("FORMAT REPOSITORY-NAME"); |
| 1413 | } |
| @@ -1464,54 +1574,59 @@ | |
| 1464 | db_finalize(&q); |
| 1465 | }else |
| 1466 | if( strncmp(g.argv[2], "svn", 3)==0 ){ |
| 1467 | db_multi_exec( |
| 1468 | "CREATE TEMP TABLE xrevisions(" |
| 1469 | " trev INTEGER PRIMARY KEY, tbranch TEXT, tuuid TEXT" |
| 1470 | ");" |
| 1471 | "CREATE TEMP TABLE xhist(" |
| 1472 | " trev INT, tpath TEXT NOT NULL, trid TEXT, tperm TEXT," |
| 1473 | " UNIQUE (trev, tpath) ON CONFLICT REPLACE" |
| 1474 | ");" |
| 1475 | "CREATE TEMP TABLE xfiles(" |
| 1476 | " tpath TEXT NOT NULL, trid TEXT, tperm TEXT," |
| 1477 | " UNIQUE (tpath) ON CONFLICT REPLACE" |
| 1478 | ");" |
| 1479 | "CREATE TEMP TRIGGER xfilesdeltrig AFTER INSERT ON xhist FOR EACH ROW" |
| 1480 | " WHEN new.trid ISNULL" |
| 1481 | " BEGIN DELETE FROM xfiles WHERE xfiles.tpath=new.tpath; END;" |
| 1482 | "CREATE TEMP TRIGGER xfilesaddtrig AFTER INSERT ON xhist FOR EACH ROW" |
| 1483 | " WHEN new.trid NOTNULL BEGIN INSERT INTO xfiles(tpath,trid,tperm)" |
| 1484 | " VALUES(new.tpath, new.trid, new.tperm); END;" |
| 1485 | "CREATE TEMP TABLE xtags(" |
| 1486 | " trev INT, ttag TEXT" |
| 1487 | ");" |
| 1488 | ); |
| 1489 | if( gsvn.zTrunk==0 ){ gsvn.zTrunk = "trunk/"; } |
| 1490 | if( gsvn.zBranches==0 ){ gsvn.zBranches = "branches/"; } |
| 1491 | if( gsvn.zTags==0 ){ gsvn.zTags = "tags/"; } |
| 1492 | gsvn.lenTrunk = strlen(gsvn.zTrunk); |
| 1493 | gsvn.lenBranches = strlen(gsvn.zBranches); |
| 1494 | gsvn.lenTags = strlen(gsvn.zTags); |
| 1495 | if( gsvn.zTrunk[gsvn.lenTrunk-1]!='/' ){ |
| 1496 | gsvn.zTrunk = mprintf("%s/", gsvn.zTrunk); |
| 1497 | gsvn.lenTrunk++; |
| 1498 | } |
| 1499 | if( gsvn.zBranches[gsvn.lenBranches-1]!='/' ){ |
| 1500 | gsvn.zBranches = mprintf("%s/", gsvn.zBranches); |
| 1501 | gsvn.lenBranches++; |
| 1502 | } |
| 1503 | if( gsvn.zTags[gsvn.lenTags-1]!='/' ){ |
| 1504 | gsvn.zTags = mprintf("%s/", gsvn.zTags); |
| 1505 | gsvn.lenTags++; |
| 1506 | } |
| 1507 | if( zFilter==0 ){ zFilter = ""; } |
| 1508 | lenFilter = strlen(zFilter); |
| 1509 | blob_zero(&gsvn.filter); |
| 1510 | blob_set(&gsvn.filter, zFilter); |
| 1511 | if( lenFilter>0 && zFilter[lenFilter-1]!='/' ){ |
| 1512 | blob_append(&gsvn.filter, "/", 1); |
| 1513 | } |
| 1514 | svn_dump_import(pIn); |
| 1515 | } |
| 1516 | |
| 1517 | verify_cancel(); |
| 1518 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -12,12 +12,12 @@ | |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** |
| 15 | ******************************************************************************* |
| 16 | ** |
| 17 | ** This file contains code used to import the content of a Git/SVN |
| 18 | ** repository in the git-fast-import/svn-dump formats as a new Fossil |
| 19 | ** repository. |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "import.h" |
| 23 | #include <assert.h> |
| @@ -718,25 +718,125 @@ | |
| 718 | fossil_fatal("bad fast-import line: [%s]", zLine); |
| 719 | return; |
| 720 | } |
| 721 | |
| 722 | static struct{ |
| 723 | char *zBranch; /* Name of a branch for a commit */ |
| 724 | char *zDate; /* Date/time stamp */ |
| 725 | char *zUser; /* User name */ |
| 726 | char *zComment; /* Comment of a commit */ |
| 727 | char *zFrom; /* from value as a UUID */ |
| 728 | int nMerge; /* Number of merge values */ |
| 729 | int nMergeAlloc; /* Number of slots in azMerge[] */ |
| 730 | char **azMerge; /* Merge values */ |
| 731 | int nFile; /* Number of aFile values */ |
| 732 | int nFileAlloc; /* Number of slots in aFile[] */ |
| 733 | ImportFile *aFile; /* Information about files in a commit */ |
| 734 | int fromLoaded; /* True zFrom content loaded into aFile[] */ |
| 735 | } gsvn; |
| 736 | |
| 737 | /* |
| 738 | ** Create a new entry in the gsvn.aFile[] array |
| 739 | */ |
| 740 | static ImportFile *svn_import_add_file(){ |
| 741 | ImportFile *pFile; |
| 742 | if( gsvn.nFile>=gsvn.nFileAlloc ){ |
| 743 | gsvn.nFileAlloc = gsvn.nFileAlloc*2 + 100; |
| 744 | gsvn.aFile = fossil_realloc(gsvn.aFile, gsvn.nFileAlloc*sizeof(gsvn.aFile[0])); |
| 745 | } |
| 746 | pFile = &gsvn.aFile[gsvn.nFile++]; |
| 747 | memset(pFile, 0, sizeof(*pFile)); |
| 748 | return pFile; |
| 749 | } |
| 750 | |
| 751 | /* |
| 752 | ** Load all file information out of the gsvn.zFrom check-in |
| 753 | */ |
| 754 | static void svn_import_prior_files(void){ |
| 755 | Manifest *p; |
| 756 | int rid; |
| 757 | ManifestFile *pOld; |
| 758 | ImportFile *pNew; |
| 759 | if( gsvn.fromLoaded ) return; |
| 760 | gsvn.fromLoaded = 1; |
| 761 | if( gsvn.zFrom==0 && gsvn.zPrevCheckin!=0 |
| 762 | && fossil_strcmp(gsvn.zBranch, gsvn.zPrevBranch)==0 |
| 763 | ){ |
| 764 | gsvn.zFrom = gsvn.zPrevCheckin; |
| 765 | gsvn.zPrevCheckin = 0; |
| 766 | } |
| 767 | if( gsvn.zFrom==0 ) return; |
| 768 | rid = fast_uuid_to_rid(gsvn.zFrom); |
| 769 | if( rid==0 ) return; |
| 770 | p = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 771 | if( p==0 ) return; |
| 772 | manifest_file_rewind(p); |
| 773 | while( (pOld = manifest_file_next(p, 0))!=0 ){ |
| 774 | pNew = import_add_file(); |
| 775 | pNew->zName = fossil_strdup(pOld->zName); |
| 776 | pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0; |
| 777 | pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0; |
| 778 | pNew->zUuid = fossil_strdup(pOld->zUuid); |
| 779 | pNew->isFrom = 1; |
| 780 | } |
| 781 | manifest_destroy(p); |
| 782 | } |
| 783 | |
| 784 | /* |
| 785 | ** Deallocate the state information. |
| 786 | ** |
| 787 | ** The azMerge[] and aFile[] arrays are zeroed but allocated space is |
| 788 | ** retained unless the freeAll flag is set. |
| 789 | */ |
| 790 | static void svn_import_reset(int freeAll){ |
| 791 | int i; |
| 792 | // gsvn.xFinish = 0; |
| 793 | // fossil_free(gsvn.zTag); gsvn.zTag = 0; |
| 794 | fossil_free(gsvn.zBranch); gsvn.zBranch = 0; |
| 795 | // fossil_free(gsvn.aData); gsvn.aData = 0; |
| 796 | // fossil_free(gsvn.zMark); gsvn.zMark = 0; |
| 797 | fossil_free(gsvn.zDate); gsvn.zDate = 0; |
| 798 | fossil_free(gsvn.zUser); gsvn.zUser = 0; |
| 799 | fossil_free(gsvn.zComment); gsvn.zComment = 0; |
| 800 | fossil_free(gsvn.zFrom); gsvn.zFrom = 0; |
| 801 | // fossil_free(gsvn.zFromMark); gsvn.zFromMark = 0; |
| 802 | for(i=0; i<gsvn.nMerge; i++){ |
| 803 | fossil_free(gsvn.azMerge[i]); gsvn.azMerge[i] = 0; |
| 804 | } |
| 805 | gsvn.nMerge = 0; |
| 806 | for(i=0; i<gsvn.nFile; i++){ |
| 807 | fossil_free(gsvn.aFile[i].zName); |
| 808 | fossil_free(gsvn.aFile[i].zUuid); |
| 809 | fossil_free(gsvn.aFile[i].zPrior); |
| 810 | } |
| 811 | memset(gsvn.aFile, 0, gsvn.nFile*sizeof(gsvn.aFile[0])); |
| 812 | gsvn.nFile = 0; |
| 813 | if( freeAll ){ |
| 814 | // fossil_free(gsvn.zPrevBranch); |
| 815 | // fossil_free(gsvn.zPrevCheckin); |
| 816 | fossil_free(gsvn.azMerge); |
| 817 | fossil_free(gsvn.aFile); |
| 818 | memset(&gsvn, 0, sizeof(gsvn)); |
| 819 | } |
| 820 | // gsvn.xFinish = finish_noop; |
| 821 | } |
| 822 | |
| 823 | static struct{ |
| 824 | int rev; /* SVN revision number */ |
| 825 | // int parentRev; /* SVN revision number of parent check-in */ |
| 826 | // char *zParentBranch; /* Name of branch of parent check-in */ |
| 827 | // char *zBranch; /* Name of a branch for a commit */ |
| 828 | char *zDate; /* Date/time stamp */ |
| 829 | char *zUser; /* User name */ |
| 830 | char *zComment; /* Comment of a commit */ |
| 831 | // int flatFlag; /* True if whole repo is a single file tree */ |
| 832 | const char *zTrunk; /* Name of trunk folder in repo root */ |
| 833 | int lenTrunk; /* String length of zTrunk */ |
| 834 | const char *zBranches; /* Name of branches folder in repo root */ |
| 835 | int lenBranches; /* String length of zBranches */ |
| 836 | const char *zTags; /* Name of tags folder in repo root */ |
| 837 | int lenTags; /* String length of zTags */ |
| 838 | } gsvn; |
| 839 | typedef struct { |
| 840 | char *zKey; |
| 841 | const char *zVal; |
| 842 | } KeyVal; |
| @@ -774,11 +874,11 @@ | |
| 874 | fossil_free(rec->aHeaders[i].zKey); |
| 875 | } |
| 876 | fossil_free(rec->aHeaders); |
| 877 | fossil_free(rec->aProps); |
| 878 | fossil_free(rec->pRawProps); |
| 879 | blob_reset(&rec->content); |
| 880 | } |
| 881 | |
| 882 | static int svn_read_headers(FILE *pIn, SvnRecord *rec){ |
| 883 | char zLine[1000]; |
| 884 | |
| @@ -903,11 +1003,11 @@ | |
| 1003 | rec->contentFlag = 0; |
| 1004 | } |
| 1005 | return 1; |
| 1006 | } |
| 1007 | |
| 1008 | static void svn_finish_revision( |
| 1009 | ){ |
| 1010 | Blob manifest; |
| 1011 | static Stmt insRev; |
| 1012 | static Stmt qParent; |
| 1013 | static Stmt qParent2; |
| @@ -1109,46 +1209,63 @@ | |
| 1209 | } |
| 1210 | zDiff += lenData; |
| 1211 | } |
| 1212 | } |
| 1213 | |
| 1214 | /* |
| 1215 | ** Extract the name of the branch or tag that the given path is on. |
| 1216 | ** Returns: 1 - It is on the trunk |
| 1217 | ** 2 - It is on a branch |
| 1218 | ** 3 - It is a tag |
| 1219 | ** 0 - It is none of the above |
| 1220 | */ |
| 1221 | static int *svn_parse_path(char *zPath, char **zBranch, char **zFile){ |
| 1222 | if( strncmp(zPath, gsvn.zTrunk, gsvn.lenTrunk)==0 ){ |
| 1223 | *zBranch = "trunk"; |
| 1224 | *zFile = zPath+gsvn.lenTrunk; |
| 1225 | return 1; |
| 1226 | }else |
| 1227 | if( strncmp(zPath, gsvn.zBranches, gsvn.lenBranches)==0 ){ |
| 1228 | *zFile = *zBranch = zPath+gsvn.lenBranches; |
| 1229 | while( **zFile && **zFile!='/' ){ (*zFile)++; } |
| 1230 | if( *zFile ){ |
| 1231 | **zFile = '\0'; |
| 1232 | (*zFile)++; |
| 1233 | }else{ |
| 1234 | *zFile = 0; |
| 1235 | } |
| 1236 | return 2; |
| 1237 | }else |
| 1238 | if( strncmp(zPath, gsvn.zTags, gsvn.lenTags)==0 ){ |
| 1239 | *zFile = *zBranch = zPath+gsvn.lenTags; |
| 1240 | while( **zFile && **zFile!='/' ){ (*zFile)++; } |
| 1241 | if( *zFile ){ |
| 1242 | **zFile = '\0'; |
| 1243 | (*zFile)++; |
| 1244 | }else{ |
| 1245 | *zFile = 0; |
| 1246 | } |
| 1247 | return 3; |
| 1248 | } |
| 1249 | *zFile = *zBranch = 0; |
| 1250 | return 0; |
| 1251 | } |
| 1252 | |
| 1253 | /* |
| 1254 | ** Read the svn-dump format from pIn and insert the corresponding |
| 1255 | ** content into the database. |
| 1256 | */ |
| 1257 | static void svn_dump_import(FILE *pIn){ |
| 1258 | SvnRecord rec; |
| 1259 | int ver; |
| 1260 | char *zTemp; |
| 1261 | const char *zUuid; |
| 1262 | Stmt addFile; |
| 1263 | Stmt delPath; |
| 1264 | Stmt addSrc; |
| 1265 | Stmt addBranch; |
| 1266 | Stmt cpyPath; |
| 1267 | |
| 1268 | /* version */ |
| 1269 | if( svn_read_rec(pIn, &rec) |
| 1270 | && (zTemp = svn_find_header(rec, "SVN-fs-dump-format-version")) ){ |
| 1271 | ver = atoi(zTemp); |
| @@ -1162,68 +1279,76 @@ | |
| 1279 | /* UUID */ |
| 1280 | if( !svn_read_rec(pIn, &rec) || !(zUuid = svn_find_header(rec, "UUID")) ){ |
| 1281 | fossil_fatal("Missing UUID!"); |
| 1282 | } |
| 1283 | svn_free_rec(&rec); |
| 1284 | |
| 1285 | /* content */ |
| 1286 | db_prepare(&addFile, |
| 1287 | "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" |
| 1288 | " VALUES(:path, :branch, :uuid, :perm)" |
| 1289 | ); |
| 1290 | db_prepare(&delPath, |
| 1291 | "DELETE FROM xfiles" |
| 1292 | " WHERE (tpath=:path OR (tpath>:path||'/' AND tpath<:path||'0'))" |
| 1293 | " AND tbranch=:branch" |
| 1294 | ); |
| 1295 | db_prepare(&addSrc, |
| 1296 | "INSERT INTO xsrc (tpath, tbranch, tsrc, tsrcbranch, tsrcrev)" |
| 1297 | " VALUES(:path, :branch, :srcpath, :srcbranch, :srcrev)" |
| 1298 | ); |
| 1299 | db_prepare(&addBranch, |
| 1300 | "INSERT INTO xchanges (tbranch, ttype) VALUES(:branch, :type)" |
| 1301 | ); |
| 1302 | db_prepare(&cpyPath, |
| 1303 | "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" |
| 1304 | " SELECT :path||substr(filename, length(:srcpath)+1), :branch, uuid, perm" |
| 1305 | " FROM xfoci" |
| 1306 | " WHERE chekinID=:rid AND filename>:srcpath||'/' AND filename<:srcpath||'0'" |
| 1307 | ); |
| 1308 | gsvn.rev = -1; |
| 1309 | while( svn_read_rec(pIn, &rec) ){ |
| 1310 | if( (zTemp = svn_find_header(rec, "Revision-number")) ){ /* revision node */ |
| 1311 | /* finish previous revision */ |
| 1312 | const char *zDate = NULL; |
| 1313 | if( gsvn.rev>=0 ){ |
| 1314 | svn_finish_revision(); |
| 1315 | fossil_free(gsvn.zUser); |
| 1316 | fossil_free(gsvn.zComment); |
| 1317 | fossil_free(gsvn.zDate); |
| 1318 | } |
| 1319 | /* start new revision */ |
| 1320 | gsvn.rev = atoi(zTemp); |
| 1321 | gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author")); |
| 1322 | gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log")); |
| 1323 | zDate = svn_find_prop(rec, "svn:date"); |
| 1324 | if( zDate ){ |
| 1325 | zDate = date_in_standard_format(zDate); |
| 1326 | } |
| 1327 | gsvn.zDate = zDate; |
| 1328 | fossil_print("\rImporting SVN revision: %d", gsvn.rev); |
| 1329 | }else |
| 1330 | if( (zTemp = svn_find_header(rec, "Node-path")) ){ /* file/dir node */ |
| 1331 | const char *zAction = svn_find_header(rec, "Node-action"); |
| 1332 | const char *zKind = svn_find_header(rec, "Node-kind"); |
| 1333 | const char *zSrcPath = svn_find_header(rec, "Node-copyfrom-path"); |
| 1334 | const char *zPerm = svn_find_prop(rec, "svn:executable") ? "x" : 0; |
| 1335 | char *zBranch; |
| 1336 | char *zFile; |
| 1337 | char *zSrcBranch; |
| 1338 | char *zSrcFile; |
| 1339 | int deltaFlag = 0; |
| 1340 | int srcRev = 0; |
| 1341 | int branchType = svn_parse_path(zTemp, &zBranch, &zFile); |
| 1342 | if( branchType==0 ){ |
| 1343 | svn_free_rec(&rec); |
| 1344 | continue; |
| 1345 | } |
| 1346 | db_bind_text(&addBranch, ":branch", zBranch); |
| 1347 | db_bind_int(&addBranch, ":type", branchType); |
| 1348 | db_step(&addBranch); |
| 1349 | db_reset(&addBranch); |
| 1350 | if( (zTemp = svn_find_header(rec, "Text-delta")) ){ |
| 1351 | deltaFlag = strncmp(zTemp, "true", 4)==0; |
| 1352 | } |
| 1353 | if( zSrcPath ){ |
| 1354 | zTemp = svn_find_header(rec, "Node-copyfrom-rev"); |
| @@ -1230,71 +1355,55 @@ | |
| 1355 | if( zTemp ){ |
| 1356 | srcRev = atoi(zTemp); |
| 1357 | }else{ |
| 1358 | fossil_fatal("Missing copyfrom-rev"); |
| 1359 | } |
| 1360 | if( svn_extract_branch(zSrcPath, &zSrcBranch, &zSrcFile)==0 ){ |
| 1361 | fossil_fatal("Copy from path outside the import paths"); |
| 1362 | } |
| 1363 | db_bind_text(&addSrc, ":path", zFile); |
| 1364 | db_bind_text(&addSrc, ":branch", zBranch); |
| 1365 | db_bind_text(&addSrc, ":srcpath", zSrcFile); |
| 1366 | db_bind_text(&addSrc, ":srcbranch", zSrcBranch); |
| 1367 | db_bind_int(&addSrc, ":srcrev", srcRev); |
| 1368 | db_step(&addSrc); |
| 1369 | db_reset(&addSrc); |
| 1370 | } |
| 1371 | if( strncmp(zAction, "delete", 6)==0 |
| 1372 | || strncmp(zAction, "replace", 7)==0 ) |
| 1373 | { |
| 1374 | db_bind_text(&delPath, ":path", zFile); |
| 1375 | db_bind_text(&delPath, ":branch", zBranch); |
| 1376 | db_step(&delPath); |
| 1377 | db_reset(&delPath); |
| 1378 | } /* no 'else' here since 'replace' does both a 'delete' and an 'add' */ |
| 1379 | if( strncmp(zAction, "add", 3)==0 |
| 1380 | || strncmp(zAction, "replace", 7)==0 ) |
| 1381 | { |
| 1382 | if( zKind==0 ){ |
| 1383 | fossil_fatal("Missing Node-kind"); |
| 1384 | }else if( strncmp(zKind, "dir", 3)==0 ){ |
| 1385 | if( zSrcPath ){ |
| 1386 | int srcRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" |
| 1387 | " WHERE trev<=%d AND tbranch=%Q", |
| 1388 | srcRev, zSrcBranch); |
| 1389 | db_bind_text(&cpyPath, ":path", zFile); |
| 1390 | db_bind_text(&cpyPath, ":branch", zBranch); |
| 1391 | db_bind_text(&cpyPath, ":srcpath", zSrcFile); |
| 1392 | db_bind_int(&cpyPath, ":rid", srcRid); |
| 1393 | db_step(&cpyPath); |
| 1394 | db_reset(&cpyPath); |
| 1395 | } |
| 1396 | }else{ |
| 1397 | int rid = 0; |
| 1398 | if( zSrcPath ){ |
| 1399 | int srcRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" |
| 1400 | " WHERE trev<=%d AND tbranch=%Q", |
| 1401 | srcRev, zSrcBranch); |
| 1402 | rid = db_int(0, "SELECT rid FROM xfoci" |
| 1403 | " WHERE chekinID=%d AND filename=%Q", |
| 1404 | srcRid, zSrcFile) |
| 1405 | } |
| 1406 | if( deltaFlag ){ |
| 1407 | Blob deltaSrc; |
| 1408 | Blob target; |
| 1409 | if( rid!=0 ){ |
| @@ -1338,11 +1447,11 @@ | |
| 1447 | db_step(&addHist); |
| 1448 | db_reset(&addHist); |
| 1449 | bHasFiles = 1; |
| 1450 | } |
| 1451 | }else |
| 1452 | if( strncmp(zAction, "delete", 6)!=0 ){ /* already did this one above */ |
| 1453 | fossil_fatal("Unknown Node-action"); |
| 1454 | } |
| 1455 | }else{ |
| 1456 | fossil_fatal("Unknown record type"); |
| 1457 | } |
| @@ -1374,18 +1483,19 @@ | |
| 1483 | ** The following formats are currently understood by this command |
| 1484 | ** |
| 1485 | ** git Import from the git-fast-export file format |
| 1486 | ** |
| 1487 | ** svn Import from the svnadmin-dump file format. The default |
| 1488 | ** behaviour (unless overridden by --flat) is to treat 3 folders |
| 1489 | ** in the SVN root as special, following the common layout of |
| 1490 | ** SVN repositories. These are (by default) trunk/, branches/ |
| 1491 | ** and tags/ |
| 1492 | ** Options: |
| 1493 | ** --trunk FOLDER Name of trunk folder |
| 1494 | ** --branches FOLDER Name of branches folder |
| 1495 | ** --tags FOLDER Name of tags folder |
| 1496 | ** --base PATH Path to project root in repository |
| 1497 | ** --flat The whole dump is a single branch |
| 1498 | ** |
| 1499 | ** The --incremental option allows an existing repository to be extended |
| 1500 | ** with new content. |
| 1501 | ** |
| @@ -1396,18 +1506,18 @@ | |
| 1506 | */ |
| 1507 | void import_cmd(void){ |
| 1508 | char *zPassword; |
| 1509 | FILE *pIn; |
| 1510 | Stmt q; |
| 1511 | const char *zBase = find_option("base", 0, 1); |
| 1512 | int lenFilter; |
| 1513 | int forceFlag = find_option("force", "f", 0)!=0; |
| 1514 | int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1515 | gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1516 | gsvn.zBranches = find_option("branches", 0, 1); |
| 1517 | gsvn.zTags = find_option("tags", 0, 1); |
| 1518 | int flatFlag = find_option("flat", 0, 0)!=0; |
| 1519 | |
| 1520 | verify_all_options(); |
| 1521 | if( g.argc!=4 && g.argc!=5 ){ |
| 1522 | usage("FORMAT REPOSITORY-NAME"); |
| 1523 | } |
| @@ -1464,54 +1574,59 @@ | |
| 1574 | db_finalize(&q); |
| 1575 | }else |
| 1576 | if( strncmp(g.argv[2], "svn", 3)==0 ){ |
| 1577 | db_multi_exec( |
| 1578 | "CREATE TEMP TABLE xrevisions(" |
| 1579 | " trev INTEGER, tbranch TEXT, trid INT, PRIMARY KEY(tbranch, trev)" |
| 1580 | ");" |
| 1581 | "CREATE TEMP TABLE xfiles(" |
| 1582 | " tpath TEXT, tbranch TEXT, tuuid TEXT, tperm TEXT," |
| 1583 | " UNIQUE (tbranch, tpath) ON CONFLICT REPLACE" |
| 1584 | ");" |
| 1585 | "CREATE TEMP TABLE xsrc(" |
| 1586 | " tpath TEXT, tbranch TEXT, tsrc TEXT, tsrcbranch TEXT, tsrcrev INT" |
| 1587 | ");" |
| 1588 | "CREATE TEMP TABLE xchanged(" |
| 1589 | " tbranch TEXT, ttype INT," |
| 1590 | " UNIQUE (tbranch) ON CONFLICT REPLACE" |
| 1591 | ");" |
| 1592 | "CREATE VIRTUAL TABLE temp.xfoci USING files_of_checkin;" |
| 1593 | ); |
| 1594 | if( zBase==0 ){ zBase = ""; } |
| 1595 | if( strlen(zBase)>0 ){ |
| 1596 | if( zBase[strlen(zBase)-1]!='/' ){ |
| 1597 | zBase = mprintf("%s/", zBase); |
| 1598 | } |
| 1599 | if( flatFlag ){ |
| 1600 | gsvn.zTrunk = zBase; |
| 1601 | gsvn.zBranches = 0; |
| 1602 | gsvn.zTags = 0; |
| 1603 | gsvn.lenTrunk = strlen(zBase); |
| 1604 | gsvn.lenBranches = 0; |
| 1605 | gsvn.lenTags = 0; |
| 1606 | }else{ |
| 1607 | if( gsvn.zTrunk==0 ){ gsvn.zTrunk = "trunk/"; } |
| 1608 | if( gsvn.zBranches==0 ){ gsvn.zBranches = "branches/"; } |
| 1609 | if( gsvn.zTags==0 ){ gsvn.zTags = "tags/"; } |
| 1610 | gsvn.zTrunk = mprintf("%s%s", zBase, gsvn.zTrunk); |
| 1611 | gsvn.zBranches = mprintf("%s%s", zBase, gsvn.zBranches); |
| 1612 | gsvn.zTags = mprintf("%s%s", zBase, gsvn.zTags); |
| 1613 | gsvn.lenTrunk = strlen(gsvn.zTrunk); |
| 1614 | gsvn.lenBranches = strlen(gsvn.zBranches); |
| 1615 | gsvn.lenTags = strlen(gsvn.zTags); |
| 1616 | if( gsvn.zTrunk[gsvn.lenTrunk-1]!='/' ){ |
| 1617 | gsvn.zTrunk = mprintf("%s/", gsvn.zTrunk); |
| 1618 | gsvn.lenTrunk++; |
| 1619 | } |
| 1620 | if( gsvn.zBranches[gsvn.lenBranches-1]!='/' ){ |
| 1621 | gsvn.zBranches = mprintf("%s/", gsvn.zBranches); |
| 1622 | gsvn.lenBranches++; |
| 1623 | } |
| 1624 | if( gsvn.zTags[gsvn.lenTags-1]!='/' ){ |
| 1625 | gsvn.zTags = mprintf("%s/", gsvn.zTags); |
| 1626 | gsvn.lenTags++; |
| 1627 | } |
| 1628 | } |
| 1629 | svn_dump_import(pIn); |
| 1630 | } |
| 1631 | |
| 1632 | verify_cancel(); |
| 1633 |