| | @@ -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> |
| | @@ -716,46 +716,822 @@ |
| 716 | 716 | malformed_line: |
| 717 | 717 | trim_newline(zLine); |
| 718 | 718 | fossil_fatal("bad fast-import line: [%s]", zLine); |
| 719 | 719 | return; |
| 720 | 720 | } |
| 721 | + |
| 722 | +static struct{ |
| 723 | + int rev; /* SVN revision number */ |
| 724 | + char *zDate; /* Date/time stamp */ |
| 725 | + char *zUser; /* User name */ |
| 726 | + char *zComment; /* Comment of a commit */ |
| 727 | + const char *zTrunk; /* Name of trunk folder in repo root */ |
| 728 | + int lenTrunk; /* String length of zTrunk */ |
| 729 | + const char *zBranches; /* Name of branches folder in repo root */ |
| 730 | + int lenBranches; /* String length of zBranches */ |
| 731 | + const char *zTags; /* Name of tags folder in repo root */ |
| 732 | + int lenTags; /* String length of zTags */ |
| 733 | + Bag newBranches; /* Branches that were created in this revision */ |
| 734 | + int incrFlag; /* Add svn-rev-nn tags on every checkin */ |
| 735 | +} gsvn; |
| 736 | +typedef struct { |
| 737 | + char *zKey; |
| 738 | + char *zVal; |
| 739 | +} KeyVal; |
| 740 | +typedef struct { |
| 741 | + KeyVal *aHeaders; |
| 742 | + int nHeaders; |
| 743 | + char *pRawProps; |
| 744 | + KeyVal *aProps; |
| 745 | + int nProps; |
| 746 | + Blob content; |
| 747 | + int contentFlag; |
| 748 | +} SvnRecord; |
| 749 | + |
| 750 | +#define svn_find_header(rec, zHeader) \ |
| 751 | + svn_find_keyval((rec).aHeaders, (rec).nHeaders, (zHeader)) |
| 752 | +#define svn_find_prop(rec, zProp) \ |
| 753 | + svn_find_keyval((rec).aProps, (rec).nProps, (zProp)) |
| 754 | +static char *svn_find_keyval( |
| 755 | + KeyVal *aKeyVal, |
| 756 | + int nKeyVal, |
| 757 | + const char *zKey |
| 758 | +){ |
| 759 | + int i; |
| 760 | + for(i=0; i<nKeyVal; i++){ |
| 761 | + if( fossil_strcmp(aKeyVal[i].zKey, zKey)==0 ){ |
| 762 | + return aKeyVal[i].zVal; |
| 763 | + } |
| 764 | + } |
| 765 | + return 0; |
| 766 | +} |
| 767 | + |
| 768 | +static void svn_free_rec(SvnRecord *rec){ |
| 769 | + int i; |
| 770 | + for(i=0; i<rec->nHeaders; i++){ |
| 771 | + fossil_free(rec->aHeaders[i].zKey); |
| 772 | + } |
| 773 | + fossil_free(rec->aHeaders); |
| 774 | + fossil_free(rec->aProps); |
| 775 | + fossil_free(rec->pRawProps); |
| 776 | + blob_reset(&rec->content); |
| 777 | +} |
| 778 | + |
| 779 | +static int svn_read_headers(FILE *pIn, SvnRecord *rec){ |
| 780 | + char zLine[1000]; |
| 781 | + |
| 782 | + rec->aHeaders = 0; |
| 783 | + rec->nHeaders = 0; |
| 784 | + while( fgets(zLine, sizeof(zLine), pIn) ){ |
| 785 | + if( zLine[0]!='\n' ) break; |
| 786 | + } |
| 787 | + if( feof(pIn) ) return 0; |
| 788 | + do{ |
| 789 | + char *sep; |
| 790 | + if( zLine[0]=='\n' ) break; |
| 791 | + rec->nHeaders += 1; |
| 792 | + rec->aHeaders = fossil_realloc(rec->aHeaders, |
| 793 | + sizeof(rec->aHeaders[0])*rec->nHeaders); |
| 794 | + rec->aHeaders[rec->nHeaders-1].zKey = mprintf("%s", zLine); |
| 795 | + sep = strchr(rec->aHeaders[rec->nHeaders-1].zKey, ':'); |
| 796 | + if( !sep ){ |
| 797 | + trim_newline(zLine); |
| 798 | + fossil_fatal("bad header line: [%s]", zLine); |
| 799 | + } |
| 800 | + *sep = 0; |
| 801 | + rec->aHeaders[rec->nHeaders-1].zVal = sep+1; |
| 802 | + sep = strchr(rec->aHeaders[rec->nHeaders-1].zVal, '\n'); |
| 803 | + *sep = 0; |
| 804 | + while(rec->aHeaders[rec->nHeaders-1].zVal |
| 805 | + && fossil_isspace(*(rec->aHeaders[rec->nHeaders-1].zVal)) ) |
| 806 | + { |
| 807 | + rec->aHeaders[rec->nHeaders-1].zVal++; |
| 808 | + } |
| 809 | + }while( fgets(zLine, sizeof(zLine), pIn) ); |
| 810 | + if( zLine[0]!='\n' ){ |
| 811 | + trim_newline(zLine); |
| 812 | + fossil_fatal("svn-dump data ended unexpectedly"); |
| 813 | + } |
| 814 | + return 1; |
| 815 | +} |
| 816 | + |
| 817 | +static void svn_read_props(FILE *pIn, SvnRecord *rec){ |
| 818 | + int nRawProps = 0; |
| 819 | + char *pRawProps; |
| 820 | + const char *zLen; |
| 821 | + |
| 822 | + rec->pRawProps = 0; |
| 823 | + rec->aProps = 0; |
| 824 | + rec->nProps = 0; |
| 825 | + zLen = svn_find_header(*rec, "Prop-content-length"); |
| 826 | + if( zLen ){ |
| 827 | + nRawProps = atoi(zLen); |
| 828 | + } |
| 829 | + if( nRawProps ){ |
| 830 | + int got; |
| 831 | + char *zLine; |
| 832 | + rec->pRawProps = pRawProps = fossil_malloc( nRawProps ); |
| 833 | + got = fread(rec->pRawProps, 1, nRawProps, pIn); |
| 834 | + if( got!=nRawProps ){ |
| 835 | + fossil_fatal("short read: got %d of %d bytes", got, nRawProps); |
| 836 | + } |
| 837 | + if( memcmp(&pRawProps[got-10], "PROPS-END\n", 10)!=0 ){ |
| 838 | + fossil_fatal("svn-dump data ended unexpectedly"); |
| 839 | + } |
| 840 | + zLine = pRawProps; |
| 841 | + while( zLine<(pRawProps+nRawProps-10) ){ |
| 842 | + char *eol; |
| 843 | + int propLen; |
| 844 | + if( zLine[0]=='D' ){ |
| 845 | + propLen = atoi(&zLine[2]); |
| 846 | + eol = strchr(zLine, '\n'); |
| 847 | + zLine = eol+1+propLen+1; |
| 848 | + }else{ |
| 849 | + if( zLine[0]!='K' ){ |
| 850 | + fossil_fatal("svn-dump data format broken"); |
| 851 | + } |
| 852 | + propLen = atoi(&zLine[2]); |
| 853 | + eol = strchr(zLine, '\n'); |
| 854 | + zLine = eol+1; |
| 855 | + eol = zLine+propLen; |
| 856 | + if( *eol!='\n' ){ |
| 857 | + fossil_fatal("svn-dump data format broken"); |
| 858 | + } |
| 859 | + *eol = 0; |
| 860 | + rec->nProps += 1; |
| 861 | + rec->aProps = fossil_realloc(rec->aProps, |
| 862 | + sizeof(rec->aProps[0])*rec->nProps); |
| 863 | + rec->aProps[rec->nProps-1].zKey = zLine; |
| 864 | + zLine = eol+1; |
| 865 | + if( zLine[0]!='V' ){ |
| 866 | + fossil_fatal("svn-dump data format broken"); |
| 867 | + } |
| 868 | + propLen = atoi(&zLine[2]); |
| 869 | + eol = strchr(zLine, '\n'); |
| 870 | + zLine = eol+1; |
| 871 | + eol = zLine+propLen; |
| 872 | + if( *eol!='\n' ){ |
| 873 | + fossil_fatal("svn-dump data format broken"); |
| 874 | + } |
| 875 | + *eol = 0; |
| 876 | + rec->aProps[rec->nProps-1].zVal = zLine; |
| 877 | + zLine = eol+1; |
| 878 | + } |
| 879 | + } |
| 880 | + } |
| 881 | +} |
| 882 | + |
| 883 | +static int svn_read_rec(FILE *pIn, SvnRecord *rec){ |
| 884 | + const char *zLen; |
| 885 | + int nLen = 0; |
| 886 | + if( svn_read_headers(pIn, rec)==0 ) return 0; |
| 887 | + svn_read_props(pIn, rec); |
| 888 | + blob_zero(&rec->content); |
| 889 | + zLen = svn_find_header(*rec, "Text-content-length"); |
| 890 | + if( zLen ){ |
| 891 | + rec->contentFlag = 1; |
| 892 | + nLen = atoi(zLen); |
| 893 | + blob_read_from_channel(&rec->content, pIn, nLen); |
| 894 | + if( blob_size(&rec->content)!=nLen ){ |
| 895 | + fossil_fatal("short read: got %d of %d bytes", |
| 896 | + blob_size(&rec->content), nLen |
| 897 | + ); |
| 898 | + } |
| 899 | + }else{ |
| 900 | + rec->contentFlag = 0; |
| 901 | + } |
| 902 | + return 1; |
| 903 | +} |
| 904 | + |
| 905 | +/* |
| 906 | +** Returns the UUID for the RID, or NULL if not found. |
| 907 | +** The returned string is allocated via db_text() and must be |
| 908 | +** free()d by the caller. |
| 909 | +*/ |
| 910 | +char * rid_to_uuid(int rid) |
| 911 | +{ |
| 912 | + return db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 913 | +} |
| 914 | + |
| 915 | +#define SVN_UNKNOWN 0 |
| 916 | +#define SVN_TRUNK 1 |
| 917 | +#define SVN_BRANCH 2 |
| 918 | +#define SVN_TAG 3 |
| 919 | + |
| 920 | +#define MAX_INT_32 (0x7FFFFFFFL) |
| 921 | + |
| 922 | +static void svn_finish_revision(){ |
| 923 | + Blob manifest; |
| 924 | + static Stmt getChanges; |
| 925 | + static Stmt getFiles; |
| 926 | + static Stmt setRid; |
| 927 | + Blob mcksum; |
| 928 | + |
| 929 | + blob_zero(&manifest); |
| 930 | + db_static_prepare(&getChanges, "SELECT tid, tname, ttype, tparent" |
| 931 | + " FROM xrevisions, xbranches ON (tbranch=tid)" |
| 932 | + " WHERE trid ISNULL"); |
| 933 | + db_static_prepare(&getFiles, "SELECT tpath, tuuid, tperm FROM xfiles" |
| 934 | + " WHERE tbranch=:branch ORDER BY tpath"); |
| 935 | + db_prepare(&setRid, "UPDATE xrevisions SET trid=:rid" |
| 936 | + " WHERE trev=%d AND tbranch=:branch", gsvn.rev); |
| 937 | + while( db_step(&getChanges)==SQLITE_ROW ){ |
| 938 | + int branchId = db_column_int(&getChanges, 0); |
| 939 | + const char *zBranch = db_column_text(&getChanges, 1); |
| 940 | + int branchType = db_column_int(&getChanges, 2); |
| 941 | + int parentRid = db_column_int(&getChanges, 3); |
| 942 | + int mergeRid = parentRid; |
| 943 | + Manifest *pParentManifest = 0; |
| 944 | + ManifestFile *pParentFile = 0; |
| 945 | + int sameAsParent = 1; |
| 946 | + int parentBranch = 0; |
| 947 | + if( !bag_find(&gsvn.newBranches, branchId) ){ |
| 948 | + parentRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" |
| 949 | + " WHERE trev<%d AND tbranch=%d", |
| 950 | + gsvn.rev, branchId); |
| 951 | + } |
| 952 | + if( parentRid>0 ){ |
| 953 | + pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); |
| 954 | + pParentFile = manifest_file_next(pParentManifest, 0); |
| 955 | + parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", |
| 956 | + parentRid); |
| 957 | + if( parentBranch!=branchId && branchType!=SVN_TAG ){ |
| 958 | + sameAsParent = 0; |
| 959 | + } |
| 960 | + } |
| 961 | + if( mergeRid<MAX_INT_32 ){ |
| 962 | + if( gsvn.zComment ){ |
| 963 | + blob_appendf(&manifest, "C %F\n", gsvn.zComment); |
| 964 | + }else{ |
| 965 | + blob_append(&manifest, "C (no\\scomment)\n", 16); |
| 966 | + } |
| 967 | + blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 968 | + db_bind_int(&getFiles, ":branch", branchId); |
| 969 | + while( db_step(&getFiles)==SQLITE_ROW ){ |
| 970 | + const char *zFile = db_column_text(&getFiles, 0); |
| 971 | + const char *zUuid = db_column_text(&getFiles, 1); |
| 972 | + const char *zPerm = db_column_text(&getFiles, 2); |
| 973 | + if( zPerm ){ |
| 974 | + blob_appendf(&manifest, "F %F %s %s\n", zFile, zUuid, zPerm); |
| 975 | + }else{ |
| 976 | + blob_appendf(&manifest, "F %F %s\n", zFile, zUuid); |
| 977 | + } |
| 978 | + if( sameAsParent ){ |
| 979 | + if( !pParentFile |
| 980 | + || fossil_strcmp(pParentFile->zName,zFile)!=0 |
| 981 | + || fossil_strcmp(pParentFile->zUuid,zUuid)!=0 |
| 982 | + || fossil_strcmp(pParentFile->zPerm,zPerm)!=0 |
| 983 | + ){ |
| 984 | + sameAsParent = 0; |
| 985 | + }else{ |
| 986 | + pParentFile = manifest_file_next(pParentManifest, 0); |
| 987 | + } |
| 988 | + } |
| 989 | + } |
| 990 | + if( pParentFile ){ |
| 991 | + sameAsParent = 0; |
| 992 | + } |
| 993 | + db_reset(&getFiles); |
| 994 | + if( !sameAsParent ){ |
| 995 | + if( parentRid>0 ){ |
| 996 | + char *zParentUuid = rid_to_uuid(parentRid); |
| 997 | + if( parentRid==mergeRid || mergeRid==0){ |
| 998 | + char *zParentBranch = |
| 999 | + db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", |
| 1000 | + parentBranch |
| 1001 | + ); |
| 1002 | + blob_appendf(&manifest, "P %s\n", zParentUuid); |
| 1003 | + blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 1004 | + blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 1005 | + if( gsvn.incrFlag ){ |
| 1006 | + blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1007 | + } |
| 1008 | + blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch); |
| 1009 | + fossil_free(zParentBranch); |
| 1010 | + }else{ |
| 1011 | + char *zMergeUuid = rid_to_uuid(mergeRid); |
| 1012 | + blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); |
| 1013 | + if( gsvn.incrFlag ){ |
| 1014 | + blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1015 | + } |
| 1016 | + fossil_free(zMergeUuid); |
| 1017 | + } |
| 1018 | + fossil_free(zParentUuid); |
| 1019 | + }else{ |
| 1020 | + blob_appendf(&manifest, "T *branch * %F\n", zBranch); |
| 1021 | + blob_appendf(&manifest, "T *sym-%F *\n", zBranch); |
| 1022 | + if( gsvn.incrFlag ){ |
| 1023 | + blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); |
| 1024 | + } |
| 1025 | + } |
| 1026 | + }else if( branchType==SVN_TAG ){ |
| 1027 | + char *zParentUuid = rid_to_uuid(parentRid); |
| 1028 | + blob_reset(&manifest); |
| 1029 | + blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1030 | + blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid); |
| 1031 | + fossil_free(zParentUuid); |
| 1032 | + } |
| 1033 | + }else{ |
| 1034 | + char *zParentUuid = rid_to_uuid(parentRid); |
| 1035 | + blob_appendf(&manifest, "D %s\n", gsvn.zDate); |
| 1036 | + if( branchType!=SVN_TAG ){ |
| 1037 | + blob_appendf(&manifest, "T +closed %s\n", zParentUuid); |
| 1038 | + }else{ |
| 1039 | + blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid); |
| 1040 | + } |
| 1041 | + fossil_free(zParentUuid); |
| 1042 | + } |
| 1043 | + if( gsvn.zUser ){ |
| 1044 | + blob_appendf(&manifest, "U %F\n", gsvn.zUser); |
| 1045 | + }else{ |
| 1046 | + const char *zUserOvrd = find_option("user-override",0,1); |
| 1047 | + blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name()); |
| 1048 | + } |
| 1049 | + md5sum_blob(&manifest, &mcksum); |
| 1050 | + blob_appendf(&manifest, "Z %b\n", &mcksum); |
| 1051 | + blob_reset(&mcksum); |
| 1052 | + if( !sameAsParent ){ |
| 1053 | + int rid = content_put(&manifest); |
| 1054 | + db_bind_int(&setRid, ":branch", branchId); |
| 1055 | + db_bind_int(&setRid, ":rid", rid); |
| 1056 | + db_step(&setRid); |
| 1057 | + db_reset(&setRid); |
| 1058 | + }else if( branchType==SVN_TAG ){ |
| 1059 | + content_put(&manifest); |
| 1060 | + db_bind_int(&setRid, ":branch", branchId); |
| 1061 | + db_bind_int(&setRid, ":rid", parentRid); |
| 1062 | + db_step(&setRid); |
| 1063 | + db_reset(&setRid); |
| 1064 | + }else if( mergeRid==MAX_INT_32 ){ |
| 1065 | + content_put(&manifest); |
| 1066 | + db_multi_exec("DELETE FROM xrevisions WHERE tbranch=%d AND trev=%d", |
| 1067 | + branchId, gsvn.rev); |
| 1068 | + }else{ |
| 1069 | + db_multi_exec("DELETE FROM xrevisions WHERE tbranch=%d AND trev=%d", |
| 1070 | + branchId, gsvn.rev); |
| 1071 | + } |
| 1072 | + blob_reset(&manifest); |
| 1073 | + manifest_destroy(pParentManifest); |
| 1074 | + } |
| 1075 | + db_reset(&getChanges); |
| 1076 | + db_finalize(&setRid); |
| 1077 | +} |
| 1078 | + |
| 1079 | +static u64 svn_get_varint(const char **pz){ |
| 1080 | + unsigned int v = 0; |
| 1081 | + do{ |
| 1082 | + v = (v<<7) | ((*pz)[0]&0x7f); |
| 1083 | + }while( (*pz)++[0]&0x80 ); |
| 1084 | + return v; |
| 1085 | +} |
| 1086 | + |
| 1087 | +static void svn_apply_svndiff(Blob *pDiff, Blob *pSrc, Blob *pOut){ |
| 1088 | + const char *zDiff = blob_buffer(pDiff); |
| 1089 | + char *zOut; |
| 1090 | + if( blob_size(pDiff)<4 || memcmp(zDiff, "SVN", 4)!=0 ){ |
| 1091 | + fossil_fatal("Invalid svndiff0 format"); |
| 1092 | + } |
| 1093 | + zDiff += 4; |
| 1094 | + blob_zero(pOut); |
| 1095 | + while( zDiff<(blob_buffer(pDiff)+blob_size(pDiff)) ){ |
| 1096 | + u64 lenOut, lenInst, lenData, lenOld; |
| 1097 | + const char *zInst; |
| 1098 | + const char *zData; |
| 1099 | + |
| 1100 | + u64 offSrc = svn_get_varint(&zDiff); |
| 1101 | + /*lenSrc =*/ svn_get_varint(&zDiff); |
| 1102 | + lenOut = svn_get_varint(&zDiff); |
| 1103 | + lenInst = svn_get_varint(&zDiff); |
| 1104 | + lenData = svn_get_varint(&zDiff); |
| 1105 | + zInst = zDiff; |
| 1106 | + zData = zInst+lenInst; |
| 1107 | + lenOld = blob_size(pOut); |
| 1108 | + blob_resize(pOut, lenOut+lenOld); |
| 1109 | + zOut = blob_buffer(pOut)+lenOld; |
| 1110 | + while( zDiff<zInst+lenInst ){ |
| 1111 | + u64 lenCpy = (*zDiff)&0x3f; |
| 1112 | + const char *zCpy; |
| 1113 | + switch( (*zDiff)&0xC0 ){ |
| 1114 | + case 0x00: zCpy = blob_buffer(pSrc)+offSrc; break; |
| 1115 | + case 0x40: zCpy = blob_buffer(pOut); break; |
| 1116 | + case 0x80: zCpy = zData; break; |
| 1117 | + default: fossil_fatal("Invalid svndiff0 instruction"); |
| 1118 | + } |
| 1119 | + zDiff++; |
| 1120 | + if( lenCpy==0 ){ |
| 1121 | + lenCpy = svn_get_varint(&zDiff); |
| 1122 | + } |
| 1123 | + if( zCpy!=zData ){ |
| 1124 | + zCpy += svn_get_varint(&zDiff); |
| 1125 | + }else{ |
| 1126 | + zData += lenCpy; |
| 1127 | + } |
| 1128 | + while( lenCpy-- > 0 ){ |
| 1129 | + *zOut++ = *zCpy++; |
| 1130 | + } |
| 1131 | + } |
| 1132 | + zDiff += lenData; |
| 1133 | + } |
| 1134 | +} |
| 1135 | + |
| 1136 | +/* |
| 1137 | +** Extract the branch or tag that the given path is on. Return the branch ID. |
| 1138 | + */ |
| 1139 | +static int svn_parse_path(char *zPath, char **zFile, int *type){ |
| 1140 | + char *zBranch = 0; |
| 1141 | + int branchId = 0; |
| 1142 | + *type = SVN_UNKNOWN; |
| 1143 | + *zFile = 0; |
| 1144 | + if( gsvn.lenTrunk==0 ){ |
| 1145 | + zBranch = "trunk"; |
| 1146 | + *zFile = zPath; |
| 1147 | + *type = SVN_TRUNK; |
| 1148 | + }else |
| 1149 | + if( strncmp(zPath, gsvn.zTrunk, gsvn.lenTrunk-1)==0 ){ |
| 1150 | + if( zPath[gsvn.lenTrunk-1]=='/' || zPath[gsvn.lenTrunk-1]==0 ){ |
| 1151 | + zBranch = "trunk"; |
| 1152 | + *zFile = zPath+gsvn.lenTrunk; |
| 1153 | + *type = SVN_TRUNK; |
| 1154 | + }else{ |
| 1155 | + zBranch = 0; |
| 1156 | + *type = SVN_UNKNOWN; |
| 1157 | + } |
| 1158 | + }else{ |
| 1159 | + if( strncmp(zPath, gsvn.zBranches, gsvn.lenBranches)==0 ){ |
| 1160 | + *zFile = zBranch = zPath+gsvn.lenBranches; |
| 1161 | + *type = SVN_BRANCH; |
| 1162 | + }else |
| 1163 | + if( strncmp(zPath, gsvn.zTags, gsvn.lenTags)==0 ){ |
| 1164 | + *zFile = zBranch = zPath+gsvn.lenTags; |
| 1165 | + *type = SVN_TAG; |
| 1166 | + }else{ /* Not a branch, tag or trunk */ |
| 1167 | + return 0; |
| 1168 | + } |
| 1169 | + while( **zFile && **zFile!='/' ){ (*zFile)++; } |
| 1170 | + if( **zFile ){ |
| 1171 | + **zFile = '\0'; |
| 1172 | + (*zFile)++; |
| 1173 | + } |
| 1174 | + } |
| 1175 | + if( *type!=SVN_UNKNOWN ){ |
| 1176 | + branchId = db_int(0, |
| 1177 | + "SELECT tid FROM xbranches WHERE tname=%Q AND ttype=%d", |
| 1178 | + zBranch, *type); |
| 1179 | + if( branchId==0 ){ |
| 1180 | + db_multi_exec("INSERT INTO xbranches (tname, ttype) VALUES(%Q, %d)", |
| 1181 | + zBranch, *type); |
| 1182 | + branchId = db_last_insert_rowid(); |
| 1183 | + } |
| 1184 | + } |
| 1185 | + return branchId; |
| 1186 | +} |
| 1187 | + |
| 1188 | +/* |
| 1189 | +** Read the svn-dump format from pIn and insert the corresponding |
| 1190 | +** content into the database. |
| 1191 | +*/ |
| 1192 | +static void svn_dump_import(FILE *pIn){ |
| 1193 | + SvnRecord rec; |
| 1194 | + int ver; |
| 1195 | + char *zTemp; |
| 1196 | + const char *zUuid; |
| 1197 | + Stmt addFile; |
| 1198 | + Stmt delPath; |
| 1199 | + Stmt addRev; |
| 1200 | + Stmt cpyPath; |
| 1201 | + Stmt cpyRoot; |
| 1202 | + Stmt revSrc; |
| 1203 | + |
| 1204 | + /* version */ |
| 1205 | + if( svn_read_rec(pIn, &rec) |
| 1206 | + && (zTemp = svn_find_header(rec, "SVN-fs-dump-format-version")) ){ |
| 1207 | + ver = atoi(zTemp); |
| 1208 | + if( ver!=2 && ver!=3 ){ |
| 1209 | + fossil_fatal("Unknown svn-dump format version: %d", ver); |
| 1210 | + } |
| 1211 | + }else{ |
| 1212 | + fossil_fatal("Input is not an svn-dump!"); |
| 1213 | + } |
| 1214 | + svn_free_rec(&rec); |
| 1215 | + /* UUID */ |
| 1216 | + if( !svn_read_rec(pIn, &rec) || !(zUuid = svn_find_header(rec, "UUID")) ){ |
| 1217 | + /* Removed the following line since UUID is not actually used |
| 1218 | + fossil_fatal("Missing UUID!"); */ |
| 1219 | + } |
| 1220 | + svn_free_rec(&rec); |
| 1221 | + |
| 1222 | + /* content */ |
| 1223 | + db_prepare(&addFile, |
| 1224 | + "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" |
| 1225 | + " VALUES(:path, :branch, (SELECT uuid FROM blob WHERE rid=:rid), :perm)" |
| 1226 | + ); |
| 1227 | + db_prepare(&delPath, |
| 1228 | + "DELETE FROM xfiles" |
| 1229 | + " WHERE (tpath=:path OR (tpath>:path||'/' AND tpath<:path||'0'))" |
| 1230 | + " AND tbranch=:branch" |
| 1231 | + ); |
| 1232 | + db_prepare(&addRev, |
| 1233 | + "INSERT OR IGNORE INTO xrevisions (trev, tbranch) VALUES(:rev, :branch)" |
| 1234 | + ); |
| 1235 | + db_prepare(&cpyPath, |
| 1236 | + "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" |
| 1237 | + " SELECT :path||:sep||substr(filename, length(:srcpath)+2), :branch, uuid, perm" |
| 1238 | + " FROM xfoci" |
| 1239 | + " WHERE checkinID=:rid" |
| 1240 | + " AND filename>:srcpath||'/'" |
| 1241 | + " AND filename<:srcpath||'0'" |
| 1242 | + ); |
| 1243 | + db_prepare(&cpyRoot, |
| 1244 | + "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" |
| 1245 | + " SELECT :path||:sep||filename, :branch, uuid, perm" |
| 1246 | + " FROM xfoci" |
| 1247 | + " WHERE checkinID=:rid" |
| 1248 | + ); |
| 1249 | + db_prepare(&revSrc, |
| 1250 | + "UPDATE xrevisions SET tparent=:parent" |
| 1251 | + " WHERE trev=:rev AND tbranch=:branch AND tparent<:parent" |
| 1252 | + ); |
| 1253 | + gsvn.rev = -1; |
| 1254 | + bag_init(&gsvn.newBranches); |
| 1255 | + while( svn_read_rec(pIn, &rec) ){ |
| 1256 | + if( (zTemp = svn_find_header(rec, "Revision-number")) ){ /* revision node */ |
| 1257 | + /* finish previous revision */ |
| 1258 | + char *zDate = NULL; |
| 1259 | + if( gsvn.rev>=0 ){ |
| 1260 | + svn_finish_revision(); |
| 1261 | + fossil_free(gsvn.zUser); |
| 1262 | + fossil_free(gsvn.zComment); |
| 1263 | + fossil_free(gsvn.zDate); |
| 1264 | + bag_clear(&gsvn.newBranches); |
| 1265 | + } |
| 1266 | + /* start new revision */ |
| 1267 | + gsvn.rev = atoi(zTemp); |
| 1268 | + gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author")); |
| 1269 | + gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log")); |
| 1270 | + zDate = svn_find_prop(rec, "svn:date"); |
| 1271 | + if( zDate ){ |
| 1272 | + gsvn.zDate = date_in_standard_format(zDate); |
| 1273 | + }else{ |
| 1274 | + gsvn.zDate = date_in_standard_format("now"); |
| 1275 | + } |
| 1276 | + db_bind_int(&addRev, ":rev", gsvn.rev); |
| 1277 | + fossil_print("\rImporting SVN revision: %d", gsvn.rev); |
| 1278 | + }else |
| 1279 | + if( (zTemp = svn_find_header(rec, "Node-path")) ){ /* file/dir node */ |
| 1280 | + char *zFile; |
| 1281 | + int branchType; |
| 1282 | + int branchId = svn_parse_path(zTemp, &zFile, &branchType); |
| 1283 | + char *zAction = svn_find_header(rec, "Node-action"); |
| 1284 | + char *zKind = svn_find_header(rec, "Node-kind"); |
| 1285 | + char *zPerm = svn_find_prop(rec, "svn:executable") ? "x" : 0; |
| 1286 | + int deltaFlag = 0; |
| 1287 | + int srcRev = 0; |
| 1288 | + if( branchId==0 ){ |
| 1289 | + svn_free_rec(&rec); |
| 1290 | + continue; |
| 1291 | + } |
| 1292 | + if( (zTemp = svn_find_header(rec, "Text-delta")) ){ |
| 1293 | + deltaFlag = strncmp(zTemp, "true", 4)==0; |
| 1294 | + } |
| 1295 | + if( strncmp(zAction, "delete", 6)==0 |
| 1296 | + || strncmp(zAction, "replace", 7)==0 ) |
| 1297 | + { |
| 1298 | + db_bind_int(&addRev, ":branch", branchId); |
| 1299 | + db_step(&addRev); |
| 1300 | + db_reset(&addRev); |
| 1301 | + if( zFile[0]!=0 ){ |
| 1302 | + db_bind_text(&delPath, ":path", zFile); |
| 1303 | + db_bind_int(&delPath, ":branch", branchId); |
| 1304 | + db_step(&delPath); |
| 1305 | + db_reset(&delPath); |
| 1306 | + }else{ |
| 1307 | + db_multi_exec("DELETE FROM xfiles WHERE tbranch=%d", branchId); |
| 1308 | + db_bind_int(&revSrc, ":parent", MAX_INT_32); |
| 1309 | + db_bind_int(&revSrc, ":rev", gsvn.rev); |
| 1310 | + db_bind_int(&revSrc, ":branch", branchId); |
| 1311 | + db_step(&revSrc); |
| 1312 | + db_reset(&revSrc); |
| 1313 | + } |
| 1314 | + } /* no 'else' here since 'replace' does both a 'delete' and an 'add' */ |
| 1315 | + if( strncmp(zAction, "add", 3)==0 |
| 1316 | + || strncmp(zAction, "replace", 7)==0 ) |
| 1317 | + { |
| 1318 | + char *zSrcPath = svn_find_header(rec, "Node-copyfrom-path"); |
| 1319 | + char *zSrcFile; |
| 1320 | + int srcRid = 0; |
| 1321 | + if( zSrcPath ){ |
| 1322 | + int srcBranch; |
| 1323 | + zTemp = svn_find_header(rec, "Node-copyfrom-rev"); |
| 1324 | + if( zTemp ){ |
| 1325 | + srcRev = atoi(zTemp); |
| 1326 | + }else{ |
| 1327 | + fossil_fatal("Missing copyfrom-rev"); |
| 1328 | + } |
| 1329 | + srcBranch = svn_parse_path(zSrcPath, &zSrcFile, &branchType); |
| 1330 | + if( srcBranch==0 ){ |
| 1331 | + fossil_fatal("Copy from path outside the import paths"); |
| 1332 | + } |
| 1333 | + srcRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" |
| 1334 | + " WHERE trev<=%d AND tbranch=%d", |
| 1335 | + srcRev, srcBranch); |
| 1336 | + if( srcRid>0 && srcBranch!=branchId ){ |
| 1337 | + db_bind_int(&addRev, ":branch", branchId); |
| 1338 | + db_step(&addRev); |
| 1339 | + db_reset(&addRev); |
| 1340 | + db_bind_int(&revSrc, ":parent", srcRid); |
| 1341 | + db_bind_int(&revSrc, ":rev", gsvn.rev); |
| 1342 | + db_bind_int(&revSrc, ":branch", branchId); |
| 1343 | + db_step(&revSrc); |
| 1344 | + db_reset(&revSrc); |
| 1345 | + } |
| 1346 | + } |
| 1347 | + if( zKind==0 ){ |
| 1348 | + fossil_fatal("Missing Node-kind"); |
| 1349 | + }else if( strncmp(zKind, "dir", 3)==0 ){ |
| 1350 | + if( zSrcPath ){ |
| 1351 | + if( srcRid>0 ){ |
| 1352 | + if( zSrcFile[0]==0 ){ |
| 1353 | + db_bind_text(&cpyRoot, ":path", zFile); |
| 1354 | + if( zFile[0]!=0 ){ |
| 1355 | + db_bind_text(&cpyRoot, ":sep", "/"); |
| 1356 | + }else{ |
| 1357 | + db_bind_text(&cpyRoot, ":sep", ""); |
| 1358 | + } |
| 1359 | + db_bind_int(&cpyRoot, ":branch", branchId); |
| 1360 | + db_bind_int(&cpyRoot, ":rid", srcRid); |
| 1361 | + db_step(&cpyRoot); |
| 1362 | + db_reset(&cpyRoot); |
| 1363 | + }else{ |
| 1364 | + db_bind_text(&cpyPath, ":path", zFile); |
| 1365 | + if( zFile[0]!=0 ){ |
| 1366 | + db_bind_text(&cpyPath, ":sep", "/"); |
| 1367 | + }else{ |
| 1368 | + db_bind_text(&cpyPath, ":sep", ""); |
| 1369 | + } |
| 1370 | + db_bind_int(&cpyPath, ":branch", branchId); |
| 1371 | + db_bind_text(&cpyPath, ":srcpath", zSrcFile); |
| 1372 | + db_bind_int(&cpyPath, ":rid", srcRid); |
| 1373 | + db_step(&cpyPath); |
| 1374 | + db_reset(&cpyPath); |
| 1375 | + } |
| 1376 | + } |
| 1377 | + } |
| 1378 | + if( zFile[0]==0 ){ |
| 1379 | + bag_insert(&gsvn.newBranches, branchId); |
| 1380 | + } |
| 1381 | + }else{ |
| 1382 | + int rid = 0; |
| 1383 | + if( zSrcPath ){ |
| 1384 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1385 | + " SELECT uuid FROM xfoci" |
| 1386 | + " WHERE checkinID=%d AND filename=%Q" |
| 1387 | + ")", |
| 1388 | + srcRid, zSrcFile); |
| 1389 | + } |
| 1390 | + if( deltaFlag ){ |
| 1391 | + Blob deltaSrc; |
| 1392 | + Blob target; |
| 1393 | + if( rid!=0 ){ |
| 1394 | + content_get(rid, &deltaSrc); |
| 1395 | + }else{ |
| 1396 | + blob_zero(&deltaSrc); |
| 1397 | + } |
| 1398 | + svn_apply_svndiff(&rec.content, &deltaSrc, &target); |
| 1399 | + rid = content_put(&target); |
| 1400 | + }else if( rec.contentFlag ){ |
| 1401 | + rid = content_put(&rec.content); |
| 1402 | + } |
| 1403 | + db_bind_text(&addFile, ":path", zFile); |
| 1404 | + db_bind_int(&addFile, ":branch", branchId); |
| 1405 | + db_bind_int(&addFile, ":rid", rid); |
| 1406 | + db_bind_text(&addFile, ":perm", zPerm); |
| 1407 | + db_step(&addFile); |
| 1408 | + db_reset(&addFile); |
| 1409 | + db_bind_int(&addRev, ":branch", branchId); |
| 1410 | + db_step(&addRev); |
| 1411 | + db_reset(&addRev); |
| 1412 | + } |
| 1413 | + }else |
| 1414 | + if( strncmp(zAction, "change", 6)==0 ){ |
| 1415 | + int rid = 0; |
| 1416 | + if( zKind==0 ){ |
| 1417 | + fossil_fatal("Missing Node-kind"); |
| 1418 | + } |
| 1419 | + if( strncmp(zKind, "dir", 3)!=0 ){ |
| 1420 | + if( deltaFlag ){ |
| 1421 | + Blob deltaSrc; |
| 1422 | + Blob target; |
| 1423 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" |
| 1424 | + " SELECT uuid FROM xfiles" |
| 1425 | + " WHERE tpath=%Q AND tbranch=%d" |
| 1426 | + ")", zFile, branchId); |
| 1427 | + content_get(rid, &deltaSrc); |
| 1428 | + svn_apply_svndiff(&rec.content, &deltaSrc, &target); |
| 1429 | + rid = content_put(&target); |
| 1430 | + }else{ |
| 1431 | + rid = content_put(&rec.content); |
| 1432 | + } |
| 1433 | + db_bind_text(&addFile, ":path", zFile); |
| 1434 | + db_bind_int(&addFile, ":branch", branchId); |
| 1435 | + db_bind_int(&addFile, ":rid", rid); |
| 1436 | + db_bind_text(&addFile, ":perm", zPerm); |
| 1437 | + db_step(&addFile); |
| 1438 | + db_reset(&addFile); |
| 1439 | + db_bind_int(&addRev, ":branch", branchId); |
| 1440 | + db_step(&addRev); |
| 1441 | + db_reset(&addRev); |
| 1442 | + } |
| 1443 | + }else |
| 1444 | + if( strncmp(zAction, "delete", 6)!=0 ){ /* already did this one above */ |
| 1445 | + fossil_fatal("Unknown Node-action"); |
| 1446 | + } |
| 1447 | + }else{ |
| 1448 | + fossil_fatal("Unknown record type"); |
| 1449 | + } |
| 1450 | + svn_free_rec(&rec); |
| 1451 | + } |
| 1452 | + svn_finish_revision(); |
| 1453 | + fossil_free(gsvn.zUser); |
| 1454 | + fossil_free(gsvn.zComment); |
| 1455 | + fossil_free(gsvn.zDate); |
| 1456 | + db_finalize(&addFile); |
| 1457 | + db_finalize(&delPath); |
| 1458 | + db_finalize(&addRev); |
| 1459 | + db_finalize(&cpyPath); |
| 1460 | + db_finalize(&cpyRoot); |
| 1461 | + db_finalize(&revSrc); |
| 1462 | + fossil_print(" Done!\n"); |
| 1463 | +} |
| 721 | 1464 | |
| 722 | 1465 | /* |
| 723 | 1466 | ** COMMAND: import |
| 724 | 1467 | ** |
| 725 | | -** Usage: %fossil import ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1468 | +** Usage: %fossil import ?--git? ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 1469 | +** or: %fossil import --svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? |
| 726 | 1470 | ** |
| 727 | 1471 | ** Read interchange format generated by another VCS and use it to |
| 728 | 1472 | ** construct a new Fossil repository named by the NEW-REPOSITORY |
| 729 | 1473 | ** argument. If no input file is supplied the interchange format |
| 730 | 1474 | ** data is read from standard input. |
| 731 | 1475 | ** |
| 732 | | -** The git-fast-export file format is currently the only VCS interchange |
| 733 | | -** format that is understood, though other interchange formats may be added |
| 734 | | -** in the future. |
| 1476 | +** The following formats are currently understood by this command |
| 1477 | +** |
| 1478 | +** --git Import from the git-fast-export file format (default) |
| 1479 | +** |
| 1480 | +** --svn Import from the svnadmin-dump file format. The default |
| 1481 | +** behaviour (unless overridden by --flat) is to treat 3 |
| 1482 | +** folders in the SVN root as special, following the |
| 1483 | +** common layout of SVN repositories. These are (by |
| 1484 | +** default) trunk/, branches/ and tags/ |
| 1485 | +** Options: |
| 1486 | +** --trunk FOLDER Name of trunk folder |
| 1487 | +** --branches FOLDER Name of branches folder |
| 1488 | +** --tags FOLDER Name of tags folder |
| 1489 | +** --base PATH Path to project root in repository |
| 1490 | +** --flat The whole dump is a single branch |
| 1491 | +** |
| 1492 | +** Common Options: |
| 1493 | +** -i|--incremental allow importing into an existing repository |
| 1494 | +** -f|--force overwrite repository if already exist |
| 735 | 1495 | ** |
| 736 | 1496 | ** The --incremental option allows an existing repository to be extended |
| 737 | 1497 | ** with new content. |
| 738 | 1498 | ** |
| 739 | | -** Options: |
| 740 | | -** --git import from the git-fast-export file format (default) |
| 741 | | -** --incremental allow importing into an existing repository |
| 742 | 1499 | ** |
| 743 | 1500 | ** See also: export |
| 744 | 1501 | */ |
| 745 | 1502 | void import_cmd(void){ |
| 746 | 1503 | char *zPassword; |
| 747 | 1504 | FILE *pIn; |
| 748 | 1505 | Stmt q; |
| 749 | 1506 | int forceFlag = find_option("force", "f", 0)!=0; |
| 750 | | - int incrFlag = find_option("incremental", "i", 0)!=0; |
| 751 | 1507 | int svnFlag = find_option("svn", 0, 0)!=0; |
| 752 | 1508 | |
| 753 | | - find_option("git",0,0); /* Skip the --git option for now */ |
| 1509 | + /* Options common to all input formats */ |
| 1510 | + int incrFlag = find_option("incremental", "i", 0)!=0; |
| 1511 | + |
| 1512 | + /* Options for --svn only */ |
| 1513 | + const char *zBase=""; |
| 1514 | + int flatFlag=0; |
| 1515 | + |
| 1516 | + if( svnFlag ){ |
| 1517 | + /* Get --svn related options here, so verify_all_options() fail when svn |
| 1518 | + * only option are specify with --git |
| 1519 | + */ |
| 1520 | + zBase = find_option("base", 0, 1); |
| 1521 | + flatFlag = find_option("flat", 0, 0)!=0; |
| 1522 | + gsvn.zTrunk = find_option("trunk", 0, 1); |
| 1523 | + gsvn.zBranches = find_option("branches", 0, 1); |
| 1524 | + gsvn.zTags = find_option("tags", 0, 1); |
| 1525 | + gsvn.incrFlag = incrFlag; |
| 1526 | + }else{ |
| 1527 | + find_option("git",0,0); /* Skip the --git option for now */ |
| 1528 | + } |
| 754 | 1529 | verify_all_options(); |
| 755 | | - if( g.argc!=3 && g.argc!=4 ){ |
| 756 | | - usage("NEW-REPOSITORY ?INPUT-FILE?"); |
| 1530 | + |
| 1531 | + if( g.argc!=3 && g.argc!=4 ){ |
| 1532 | + usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| 757 | 1533 | } |
| 758 | 1534 | if( g.argc==4 ){ |
| 759 | 1535 | pIn = fossil_fopen(g.argv[3], "rb"); |
| 760 | 1536 | }else{ |
| 761 | 1537 | pIn = stdin; |
| | @@ -770,11 +1546,63 @@ |
| 770 | 1546 | |
| 771 | 1547 | db_begin_transaction(); |
| 772 | 1548 | if( !incrFlag ) db_initial_setup(0, 0, 0, 1); |
| 773 | 1549 | |
| 774 | 1550 | if( svnFlag ){ |
| 775 | | - fossil_fatal("--svn format not implemented yet"); |
| 1551 | + db_multi_exec( |
| 1552 | + "CREATE TEMP TABLE xrevisions(" |
| 1553 | + " trev INTEGER, tbranch INT, trid INT, tparent INT DEFAULT 0," |
| 1554 | + " UNIQUE(tbranch, trev)" |
| 1555 | + ");" |
| 1556 | + "CREATE INDEX temp.i_xrevisions ON xrevisions(trid);" |
| 1557 | + "CREATE TEMP TABLE xfiles(" |
| 1558 | + " tpath TEXT, tbranch INT, tuuid TEXT, tperm TEXT," |
| 1559 | + " UNIQUE (tbranch, tpath) ON CONFLICT REPLACE" |
| 1560 | + ");" |
| 1561 | + "CREATE TEMP TABLE xbranches(" |
| 1562 | + " tid INTEGER PRIMARY KEY, tname TEXT, ttype INT," |
| 1563 | + " UNIQUE(tname, ttype)" |
| 1564 | + ");" |
| 1565 | + "CREATE VIRTUAL TABLE temp.xfoci USING files_of_checkin;" |
| 1566 | + ); |
| 1567 | + if( zBase==0 ){ zBase = ""; } |
| 1568 | + if( strlen(zBase)>0 ){ |
| 1569 | + if( zBase[strlen(zBase)-1]!='/' ){ |
| 1570 | + zBase = mprintf("%s/", zBase); |
| 1571 | + } |
| 1572 | + } |
| 1573 | + if( flatFlag ){ |
| 1574 | + gsvn.zTrunk = zBase; |
| 1575 | + gsvn.zBranches = 0; |
| 1576 | + gsvn.zTags = 0; |
| 1577 | + gsvn.lenTrunk = strlen(zBase); |
| 1578 | + gsvn.lenBranches = 0; |
| 1579 | + gsvn.lenTags = 0; |
| 1580 | + }else{ |
| 1581 | + if( gsvn.zTrunk==0 ){ gsvn.zTrunk = "trunk/"; } |
| 1582 | + if( gsvn.zBranches==0 ){ gsvn.zBranches = "branches/"; } |
| 1583 | + if( gsvn.zTags==0 ){ gsvn.zTags = "tags/"; } |
| 1584 | + gsvn.zTrunk = mprintf("%s%s", zBase, gsvn.zTrunk); |
| 1585 | + gsvn.zBranches = mprintf("%s%s", zBase, gsvn.zBranches); |
| 1586 | + gsvn.zTags = mprintf("%s%s", zBase, gsvn.zTags); |
| 1587 | + gsvn.lenTrunk = strlen(gsvn.zTrunk); |
| 1588 | + gsvn.lenBranches = strlen(gsvn.zBranches); |
| 1589 | + gsvn.lenTags = strlen(gsvn.zTags); |
| 1590 | + if( gsvn.zTrunk[gsvn.lenTrunk-1]!='/' ){ |
| 1591 | + gsvn.zTrunk = mprintf("%s/", gsvn.zTrunk); |
| 1592 | + gsvn.lenTrunk++; |
| 1593 | + } |
| 1594 | + if( gsvn.zBranches[gsvn.lenBranches-1]!='/' ){ |
| 1595 | + gsvn.zBranches = mprintf("%s/", gsvn.zBranches); |
| 1596 | + gsvn.lenBranches++; |
| 1597 | + } |
| 1598 | + if( gsvn.zTags[gsvn.lenTags-1]!='/' ){ |
| 1599 | + gsvn.zTags = mprintf("%s/", gsvn.zTags); |
| 1600 | + gsvn.lenTags++; |
| 1601 | + } |
| 1602 | + } |
| 1603 | + svn_dump_import(pIn); |
| 776 | 1604 | }else{ |
| 777 | 1605 | /* The following temp-tables are used to hold information needed for |
| 778 | 1606 | ** the import. |
| 779 | 1607 | ** |
| 780 | 1608 | ** The XMARK table provides a mapping from fast-import "marks" and symbols |
| | @@ -807,10 +1635,12 @@ |
| 807 | 1635 | fast_insert_content(&record, 0, 0); |
| 808 | 1636 | import_reset(0); |
| 809 | 1637 | } |
| 810 | 1638 | db_finalize(&q); |
| 811 | 1639 | } |
| 1640 | + |
| 1641 | + verify_cancel(); |
| 812 | 1642 | db_end_transaction(0); |
| 813 | 1643 | db_begin_transaction(); |
| 814 | 1644 | fossil_print("Rebuilding repository meta-data...\n"); |
| 815 | 1645 | rebuild_db(0, 1, !incrFlag); |
| 816 | 1646 | verify_cancel(); |
| 817 | 1647 | |