Fossil SCM
Partial implementation of the "fossil import" command. It is incomplete and does not work.
Commit
ba837f5e88d182411e613be5408a94446d137274
Parent
7d2d1d3228232b1…
1 file changed
+326
-1
+326
-1
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -20,10 +20,309 @@ | ||
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "import.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | +/* | |
| 26 | +** State information about an on-going fast-import parse. | |
| 27 | +*/ | |
| 28 | +static struct { | |
| 29 | + void (*xFinish)(void); /* Function to finish a prior record */ | |
| 30 | + int nData; /* Bytes of data */ | |
| 31 | + char *zTag; /* Name of a tag */ | |
| 32 | + char *zBranch; /* Name of a branch for a commit */ | |
| 33 | + char *aData; /* Data content */ | |
| 34 | + char *zMark; /* The current mark */ | |
| 35 | + char *zDate; /* Date/time stamp */ | |
| 36 | + char *zUser; /* User name */ | |
| 37 | + char *zComment; /* Comment of a commit */ | |
| 38 | + char *zFrom; /* from value */ | |
| 39 | + int nMerge; /* Number of merge values */ | |
| 40 | + int nMergeAlloc; /* Number of slots in azMerge[] */ | |
| 41 | + char **azMerge; /* Merge values */ | |
| 42 | + int nFile; /* Number of aFile values */ | |
| 43 | + int nFileAlloc; /* Number of slots in aFile[] */ | |
| 44 | + ManifestFile *aFile; /* Information about files in a commit */ | |
| 45 | +} gg; | |
| 46 | + | |
| 47 | +/* | |
| 48 | +** A no-op "xFinish" method | |
| 49 | +*/ | |
| 50 | +static void finish_noop(void){} | |
| 51 | + | |
| 52 | +/* | |
| 53 | +** Deallocate the state information. | |
| 54 | +** | |
| 55 | +** The azMerge[] and aFile[] arrays are zeroed by allocated space is | |
| 56 | +** retained unless the freeAll flag is set. | |
| 57 | +*/ | |
| 58 | +static void import_reset(int freeAll){ | |
| 59 | + int i; | |
| 60 | + gg.xFinish = 0; | |
| 61 | + fossil_free(gg.zTag); gg.zTag = 0; | |
| 62 | + fossil_free(gg.zBranch); gg.zBranch = 0; | |
| 63 | + fossil_free(gg.aData); gg.aData = 0; | |
| 64 | + fossil_free(gg.zMark); gg.zMark = 0; | |
| 65 | + fossil_free(gg.zDate); gg.zDate = 0; | |
| 66 | + fossil_free(gg.zUser); gg.zUser = 0; | |
| 67 | + fossil_free(gg.zComment); gg.zComment = 0; | |
| 68 | + fossil_free(gg.zFrom); gg.zFrom = 0; | |
| 69 | + for(i=0; i<gg.nMerge; i++){ | |
| 70 | + fossil_free(gg.azMerge[i]); gg.azMerge[i] = 0; | |
| 71 | + } | |
| 72 | + gg.nMerge = 0; | |
| 73 | + for(i=0; i<gg.nFile; i++){ | |
| 74 | + fossil_free(gg.aFile[i].zName); | |
| 75 | + fossil_free(gg.aFile[i].zUuid); | |
| 76 | + fossil_free(gg.aFile[i].zPerm); | |
| 77 | + fossil_free(gg.aFile[i].zPrior); | |
| 78 | + } | |
| 79 | + memset(gg.aFile, 0, gg.nFile*sizeof(gg.aFile[0])); | |
| 80 | + gg.nFile = 0; | |
| 81 | + if( freeAll ){ | |
| 82 | + fossil_free(gg.azMerge); | |
| 83 | + fossil_free(gg.aFile); | |
| 84 | + memset(&gg, 0, sizeof(gg)); | |
| 85 | + } | |
| 86 | + gg.xFinish = finish_noop; | |
| 87 | +} | |
| 88 | + | |
| 89 | +/* | |
| 90 | +** Insert an artifact into the BLOB table if it isn't there already. | |
| 91 | +** If zMark is not zero, create a cross-reference from that mark back | |
| 92 | +** to the newly inserted artifact. | |
| 93 | +*/ | |
| 94 | +static int fast_insert_content(Blob *pContent, const char *zMark){ | |
| 95 | + Blob hash; | |
| 96 | + Blob cmpr; | |
| 97 | + int rid; | |
| 98 | + | |
| 99 | + sha1sum_blob(pContent, &hash); | |
| 100 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash); | |
| 101 | + if( rid==0 ){ | |
| 102 | + static Stmt ins; | |
| 103 | + db_static_prepare(&ins, | |
| 104 | + "INSERT INTO blob(uuid, size, content) VALUES(:uuid, :size, :content)" | |
| 105 | + ); | |
| 106 | + db_bind_text(&ins, ":uuid", blob_str(&hash)); | |
| 107 | + db_bind_int(&ins, ":size", gg.nData); | |
| 108 | + blob_compress(pContent, &cmpr); | |
| 109 | + db_bind_blob(&ins, ":content", &cmpr); | |
| 110 | + db_step(&ins); | |
| 111 | + db_reset(&ins); | |
| 112 | + blob_reset(&cmpr); | |
| 113 | + rid = db_last_insert_rowid(); | |
| 114 | + } | |
| 115 | + if( zMark ){ | |
| 116 | + db_multi_exec( | |
| 117 | + "INSERT INTO xtag(tname, trid, tuuid)" | |
| 118 | + "VALUES(%Q,%d,%B)", | |
| 119 | + zMark, rid, &hash | |
| 120 | + ); | |
| 121 | + db_multi_exec( | |
| 122 | + "INSERT INTO xtag(tname, trid, tuuid)" | |
| 123 | + "VALUES(%B,%d,%B)", | |
| 124 | + &hash, rid, &hash | |
| 125 | + ); | |
| 126 | + } | |
| 127 | + blob_reset(&hash); | |
| 128 | + return rid; | |
| 129 | +} | |
| 130 | + | |
| 131 | +/* | |
| 132 | +** Use data accumulated in gg from a "blob" record to add a new file | |
| 133 | +** to the BLOB table. | |
| 134 | +*/ | |
| 135 | +static void finish_blob(void){ | |
| 136 | + Blob content; | |
| 137 | + | |
| 138 | + if( gg.nData>0 ){ | |
| 139 | + blob_init(&content, gg.aData, gg.nData); | |
| 140 | + fast_insert_content(&content, gg.zMark); | |
| 141 | + blob_reset(&content); | |
| 142 | + } | |
| 143 | + import_reset(0); | |
| 144 | +} | |
| 145 | + | |
| 146 | +/* | |
| 147 | +** Use data accumulated in gg from a "tag" record to add a new | |
| 148 | +** control artifact to the BLOB table. | |
| 149 | +*/ | |
| 150 | +static void finish_tag(void){ | |
| 151 | + Blob record, cksum; | |
| 152 | + if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ | |
| 153 | + blob_zero(&record); | |
| 154 | + blob_appendf(&record, "D %z\n", gg.zDate); | |
| 155 | + blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); | |
| 156 | + blob_appendf(&record, "U %F\n", gg.zUser); | |
| 157 | + md5sum_blob(&record, &cksum); | |
| 158 | + blob_appendf(&record, "Z %b\n", &cksum); | |
| 159 | + fast_insert_content(&record, 0); | |
| 160 | + blob_reset(&record); | |
| 161 | + blob_reset(&cksum); | |
| 162 | + } | |
| 163 | + import_reset(0); | |
| 164 | +} | |
| 165 | + | |
| 166 | +/* | |
| 167 | +** Use data accumulated in gg from a "commit" record to add a new | |
| 168 | +** manifest artifact to the BLOB table. | |
| 169 | +*/ | |
| 170 | +static void finish_commit(void){ | |
| 171 | + /* TBD... */ | |
| 172 | + import_reset(0); | |
| 173 | +} | |
| 174 | + | |
| 175 | + | |
| 176 | +/* | |
| 177 | +** Turn the first \n in the input string into a \000 | |
| 178 | +*/ | |
| 179 | +static void trim_newline(char *z){ | |
| 180 | + while( z[0] && z[0]!='\n' ){ z++; } | |
| 181 | + z[0] = 0; | |
| 182 | +} | |
| 183 | + | |
| 184 | +/* | |
| 185 | +** Convert a "mark" or "committish" into the UUID. | |
| 186 | +*/ | |
| 187 | +static char *resolve_committish(const char *zCommittish){ | |
| 188 | + char *zRes; | |
| 189 | + | |
| 190 | + zRes = db_text(0, "SELECT tuuid FROM xtag WHERE tname=%Q", zCommittish); | |
| 191 | + return zRes; | |
| 192 | +} | |
| 193 | + | |
| 194 | + | |
| 195 | +/* | |
| 196 | +** Read the git-fast-import format from pIn and insert the corresponding | |
| 197 | +** content into the database. | |
| 198 | +*/ | |
| 199 | +static void git_fast_import(FILE *pIn){ | |
| 200 | + char zLine[1000]; | |
| 201 | + gg.xFinish = finish_noop; | |
| 202 | + while( fgets(zLine, sizeof(zLine), pIn) ){ | |
| 203 | + if( zLine[0]=='\n' || zLine[0]=='#' ) continue; | |
| 204 | + if( memcmp(zLine, "blob", 4)==0 ){ | |
| 205 | + gg.xFinish(); | |
| 206 | + gg.xFinish = finish_blob; | |
| 207 | + }else | |
| 208 | + if( memcmp(zLine, "commit ", 7)==0 ){ | |
| 209 | + gg.xFinish(); | |
| 210 | + gg.xFinish = finish_commit; | |
| 211 | + trim_newline(&zLine[7]); | |
| 212 | + gg.zBranch = mprintf("%s", &zLine[7]); | |
| 213 | + }else | |
| 214 | + if( memcmp(zLine, "tag ", 4)==0 ){ | |
| 215 | + gg.xFinish(); | |
| 216 | + gg.xFinish = finish_tag; | |
| 217 | + trim_newline(&zLine[4]); | |
| 218 | + gg.zTag = mprintf("%s", &zLine[4]); | |
| 219 | + }else | |
| 220 | + if( memcmp(zLine, "reset ", 4)==0 ){ | |
| 221 | + gg.xFinish(); | |
| 222 | + }else | |
| 223 | + if( memcmp(zLine, "checkpoint", 10)==0 ){ | |
| 224 | + gg.xFinish(); | |
| 225 | + }else | |
| 226 | + if( memcmp(zLine, "feature", 7)==0 ){ | |
| 227 | + gg.xFinish(); | |
| 228 | + }else | |
| 229 | + if( memcmp(zLine, "option", 6)==0 ){ | |
| 230 | + gg.xFinish(); | |
| 231 | + }else | |
| 232 | + if( memcmp(zLine, "progress ", 9)==0 ){ | |
| 233 | + gg.xFinish(); | |
| 234 | + trim_newline(&zLine[9]); | |
| 235 | + printf("%s\n", &zLine[9]); | |
| 236 | + fflush(stdout); | |
| 237 | + }else | |
| 238 | + if( memcmp(zLine, "data ", 5)==0 ){ | |
| 239 | + fossil_free(gg.aData); gg.aData = 0; | |
| 240 | + gg.nData = atoi(&zLine[5]); | |
| 241 | + if( gg.nData ){ | |
| 242 | + int got; | |
| 243 | + gg.aData = fossil_malloc( gg.nData+1 ); | |
| 244 | + got = fread(gg.aData, 1, gg.nData, pIn); | |
| 245 | + if( got!=gg.nData ){ | |
| 246 | + fossil_fatal("short read: got %d of %d bytes", got, gg.nData); | |
| 247 | + } | |
| 248 | + if( gg.zComment==0 && gg.xFinish==finish_commit ){ | |
| 249 | + gg.zComment = gg.aData; | |
| 250 | + gg.aData = 0; | |
| 251 | + gg.nData = 0; | |
| 252 | + } | |
| 253 | + } | |
| 254 | + }else | |
| 255 | + if( memcmp(zLine, "author ", 7)==0 ){ | |
| 256 | + /* No-op */ | |
| 257 | + }else | |
| 258 | + if( memcmp(zLine, "mark ", 5)==0 ){ | |
| 259 | + trim_newline(&zLine[5]); | |
| 260 | + fossil_free(gg.zMark); | |
| 261 | + gg.zMark = mprintf("%s", &zLine[5]); | |
| 262 | + }else | |
| 263 | + if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",9)==0 ){ | |
| 264 | + int i; | |
| 265 | + char *z; | |
| 266 | + sqlite3_int64 secSince1970; | |
| 267 | + | |
| 268 | + for(i=0; zLine[i] && zLine[i]!='<'; i++){} | |
| 269 | + if( zLine[i]==0 ) goto malformed_line; | |
| 270 | + z = &zLine[i+1]; | |
| 271 | + for(i=i+1; zLine[i] && zLine[i]!='>'; i++){} | |
| 272 | + if( zLine[i]==0 ) goto malformed_line; | |
| 273 | + zLine[i] = 0; | |
| 274 | + fossil_free(gg.zUser); | |
| 275 | + gg.zUser = mprintf("%s", z); | |
| 276 | + secSince1970 = 0; | |
| 277 | + for(i=i+1; fossil_isdigit(zLine[i]); i++){ | |
| 278 | + secSince1970 = secSince1970*10 + zLine[i] - '0'; | |
| 279 | + } | |
| 280 | + fossil_free(gg.zDate); | |
| 281 | + gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970); | |
| 282 | + gg.zDate[10] = 'T'; | |
| 283 | + }else | |
| 284 | + if( memcmp(zLine, "from ", 5)==0 ){ | |
| 285 | + trim_newline(&zLine[5]); | |
| 286 | + fossil_free(gg.zFrom); | |
| 287 | + gg.zFrom = resolve_committish(&zLine[5]); | |
| 288 | + }else | |
| 289 | + if( memcmp(zLine, "merge ", 6)==0 ){ | |
| 290 | + trim_newline(&zLine[6]); | |
| 291 | + if( gg.nMerge>=gg.nMergeAlloc ){ | |
| 292 | + gg.nMergeAlloc = gg.nMergeAlloc*2 + 10; | |
| 293 | + gg.azMerge = fossil_realloc(gg.azMerge, gg.nMergeAlloc*sizeof(char*)); | |
| 294 | + } | |
| 295 | + gg.azMerge[gg.nMerge] = resolve_committish(&zLine[5]); | |
| 296 | + if( gg.azMerge[gg.nMerge] ) gg.nMerge++; | |
| 297 | + }else | |
| 298 | + if( memcmp(zLine, "M ", 2)==0 ){ | |
| 299 | + }else | |
| 300 | + if( memcmp(zLine, "D ", 2)==0 ){ | |
| 301 | + }else | |
| 302 | + if( memcmp(zLine, "C ", 2)==0 ){ | |
| 303 | + }else | |
| 304 | + if( memcmp(zLine, "R ", 2)==0 ){ | |
| 305 | + }else | |
| 306 | + if( memcmp(zLine, "deleteall", 9)==0 ){ | |
| 307 | + }else | |
| 308 | + if( memcmp(zLine, "N ", 2)==0 ){ | |
| 309 | + }else | |
| 310 | + | |
| 311 | + { | |
| 312 | + goto malformed_line; | |
| 313 | + } | |
| 314 | + } | |
| 315 | + gg.xFinish(); | |
| 316 | + import_reset(1); | |
| 317 | + return; | |
| 318 | + | |
| 319 | +malformed_line: | |
| 320 | + trim_newline(zLine); | |
| 321 | + fossil_fatal("bad fast-import line: [%s]", zLine); | |
| 322 | + return; | |
| 323 | +} | |
| 25 | 324 | |
| 26 | 325 | /* |
| 27 | 326 | ** COMMAND: import |
| 28 | 327 | ** |
| 29 | 328 | ** Usage: %fossil import NEW-REPOSITORY |
| @@ -31,7 +330,33 @@ | ||
| 31 | 330 | ** Read text generated by the git-fast-export command and use it to |
| 32 | 331 | ** construct a new Fossil repository named by the NEW-REPOSITORY |
| 33 | 332 | ** argument. The get-fast-export text is read from standard input. |
| 34 | 333 | */ |
| 35 | 334 | void git_import_cmd(void){ |
| 36 | - fossil_fatal("not yet implemented...."); | |
| 335 | + char *zPassword; | |
| 336 | + fossil_fatal("this command is still under development...."); | |
| 337 | + if( g.argc!=3 ){ | |
| 338 | + usage("REPOSITORY-NAME"); | |
| 339 | + } | |
| 340 | + db_create_repository(g.argv[2]); | |
| 341 | + db_open_repository(g.argv[2]); | |
| 342 | + db_open_config(0); | |
| 343 | + db_multi_exec( | |
| 344 | + "ATTACH ':memory:' AS mem1;" | |
| 345 | + "CREATE TABLE mem1.xtag(tname TEXT UNIQUE, trid INT, tuuid TEXT);" | |
| 346 | + ); | |
| 347 | + db_begin_transaction(); | |
| 348 | + db_initial_setup(0, 0, 1); | |
| 349 | + git_fast_import(stdin); | |
| 350 | + db_end_transaction(0); | |
| 351 | + db_begin_transaction(); | |
| 352 | + printf("Rebuilding repository meta-data...\n"); | |
| 353 | + rebuild_db(0, 1); | |
| 354 | + printf("Vacuuming..."); fflush(stdout); | |
| 355 | + db_multi_exec("VACUUM"); | |
| 356 | + printf(" ok\n"); | |
| 357 | + printf("project-id: %s\n", db_get("project-code", 0)); | |
| 358 | + printf("server-id: %s\n", db_get("server-code", 0)); | |
| 359 | + zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); | |
| 360 | + printf("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); | |
| 361 | + db_end_transaction(0); | |
| 37 | 362 | } |
| 38 | 363 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -20,10 +20,309 @@ | |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "import.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | |
| 26 | /* |
| 27 | ** COMMAND: import |
| 28 | ** |
| 29 | ** Usage: %fossil import NEW-REPOSITORY |
| @@ -31,7 +330,33 @@ | |
| 31 | ** Read text generated by the git-fast-export command and use it to |
| 32 | ** construct a new Fossil repository named by the NEW-REPOSITORY |
| 33 | ** argument. The get-fast-export text is read from standard input. |
| 34 | */ |
| 35 | void git_import_cmd(void){ |
| 36 | fossil_fatal("not yet implemented...."); |
| 37 | } |
| 38 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -20,10 +20,309 @@ | |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "import.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** State information about an on-going fast-import parse. |
| 27 | */ |
| 28 | static struct { |
| 29 | void (*xFinish)(void); /* Function to finish a prior record */ |
| 30 | int nData; /* Bytes of data */ |
| 31 | char *zTag; /* Name of a tag */ |
| 32 | char *zBranch; /* Name of a branch for a commit */ |
| 33 | char *aData; /* Data content */ |
| 34 | char *zMark; /* The current mark */ |
| 35 | char *zDate; /* Date/time stamp */ |
| 36 | char *zUser; /* User name */ |
| 37 | char *zComment; /* Comment of a commit */ |
| 38 | char *zFrom; /* from value */ |
| 39 | int nMerge; /* Number of merge values */ |
| 40 | int nMergeAlloc; /* Number of slots in azMerge[] */ |
| 41 | char **azMerge; /* Merge values */ |
| 42 | int nFile; /* Number of aFile values */ |
| 43 | int nFileAlloc; /* Number of slots in aFile[] */ |
| 44 | ManifestFile *aFile; /* Information about files in a commit */ |
| 45 | } gg; |
| 46 | |
| 47 | /* |
| 48 | ** A no-op "xFinish" method |
| 49 | */ |
| 50 | static void finish_noop(void){} |
| 51 | |
| 52 | /* |
| 53 | ** Deallocate the state information. |
| 54 | ** |
| 55 | ** The azMerge[] and aFile[] arrays are zeroed by allocated space is |
| 56 | ** retained unless the freeAll flag is set. |
| 57 | */ |
| 58 | static void import_reset(int freeAll){ |
| 59 | int i; |
| 60 | gg.xFinish = 0; |
| 61 | fossil_free(gg.zTag); gg.zTag = 0; |
| 62 | fossil_free(gg.zBranch); gg.zBranch = 0; |
| 63 | fossil_free(gg.aData); gg.aData = 0; |
| 64 | fossil_free(gg.zMark); gg.zMark = 0; |
| 65 | fossil_free(gg.zDate); gg.zDate = 0; |
| 66 | fossil_free(gg.zUser); gg.zUser = 0; |
| 67 | fossil_free(gg.zComment); gg.zComment = 0; |
| 68 | fossil_free(gg.zFrom); gg.zFrom = 0; |
| 69 | for(i=0; i<gg.nMerge; i++){ |
| 70 | fossil_free(gg.azMerge[i]); gg.azMerge[i] = 0; |
| 71 | } |
| 72 | gg.nMerge = 0; |
| 73 | for(i=0; i<gg.nFile; i++){ |
| 74 | fossil_free(gg.aFile[i].zName); |
| 75 | fossil_free(gg.aFile[i].zUuid); |
| 76 | fossil_free(gg.aFile[i].zPerm); |
| 77 | fossil_free(gg.aFile[i].zPrior); |
| 78 | } |
| 79 | memset(gg.aFile, 0, gg.nFile*sizeof(gg.aFile[0])); |
| 80 | gg.nFile = 0; |
| 81 | if( freeAll ){ |
| 82 | fossil_free(gg.azMerge); |
| 83 | fossil_free(gg.aFile); |
| 84 | memset(&gg, 0, sizeof(gg)); |
| 85 | } |
| 86 | gg.xFinish = finish_noop; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | ** Insert an artifact into the BLOB table if it isn't there already. |
| 91 | ** If zMark is not zero, create a cross-reference from that mark back |
| 92 | ** to the newly inserted artifact. |
| 93 | */ |
| 94 | static int fast_insert_content(Blob *pContent, const char *zMark){ |
| 95 | Blob hash; |
| 96 | Blob cmpr; |
| 97 | int rid; |
| 98 | |
| 99 | sha1sum_blob(pContent, &hash); |
| 100 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &hash); |
| 101 | if( rid==0 ){ |
| 102 | static Stmt ins; |
| 103 | db_static_prepare(&ins, |
| 104 | "INSERT INTO blob(uuid, size, content) VALUES(:uuid, :size, :content)" |
| 105 | ); |
| 106 | db_bind_text(&ins, ":uuid", blob_str(&hash)); |
| 107 | db_bind_int(&ins, ":size", gg.nData); |
| 108 | blob_compress(pContent, &cmpr); |
| 109 | db_bind_blob(&ins, ":content", &cmpr); |
| 110 | db_step(&ins); |
| 111 | db_reset(&ins); |
| 112 | blob_reset(&cmpr); |
| 113 | rid = db_last_insert_rowid(); |
| 114 | } |
| 115 | if( zMark ){ |
| 116 | db_multi_exec( |
| 117 | "INSERT INTO xtag(tname, trid, tuuid)" |
| 118 | "VALUES(%Q,%d,%B)", |
| 119 | zMark, rid, &hash |
| 120 | ); |
| 121 | db_multi_exec( |
| 122 | "INSERT INTO xtag(tname, trid, tuuid)" |
| 123 | "VALUES(%B,%d,%B)", |
| 124 | &hash, rid, &hash |
| 125 | ); |
| 126 | } |
| 127 | blob_reset(&hash); |
| 128 | return rid; |
| 129 | } |
| 130 | |
| 131 | /* |
| 132 | ** Use data accumulated in gg from a "blob" record to add a new file |
| 133 | ** to the BLOB table. |
| 134 | */ |
| 135 | static void finish_blob(void){ |
| 136 | Blob content; |
| 137 | |
| 138 | if( gg.nData>0 ){ |
| 139 | blob_init(&content, gg.aData, gg.nData); |
| 140 | fast_insert_content(&content, gg.zMark); |
| 141 | blob_reset(&content); |
| 142 | } |
| 143 | import_reset(0); |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** Use data accumulated in gg from a "tag" record to add a new |
| 148 | ** control artifact to the BLOB table. |
| 149 | */ |
| 150 | static void finish_tag(void){ |
| 151 | Blob record, cksum; |
| 152 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 153 | blob_zero(&record); |
| 154 | blob_appendf(&record, "D %z\n", gg.zDate); |
| 155 | blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); |
| 156 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 157 | md5sum_blob(&record, &cksum); |
| 158 | blob_appendf(&record, "Z %b\n", &cksum); |
| 159 | fast_insert_content(&record, 0); |
| 160 | blob_reset(&record); |
| 161 | blob_reset(&cksum); |
| 162 | } |
| 163 | import_reset(0); |
| 164 | } |
| 165 | |
| 166 | /* |
| 167 | ** Use data accumulated in gg from a "commit" record to add a new |
| 168 | ** manifest artifact to the BLOB table. |
| 169 | */ |
| 170 | static void finish_commit(void){ |
| 171 | /* TBD... */ |
| 172 | import_reset(0); |
| 173 | } |
| 174 | |
| 175 | |
| 176 | /* |
| 177 | ** Turn the first \n in the input string into a \000 |
| 178 | */ |
| 179 | static void trim_newline(char *z){ |
| 180 | while( z[0] && z[0]!='\n' ){ z++; } |
| 181 | z[0] = 0; |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | ** Convert a "mark" or "committish" into the UUID. |
| 186 | */ |
| 187 | static char *resolve_committish(const char *zCommittish){ |
| 188 | char *zRes; |
| 189 | |
| 190 | zRes = db_text(0, "SELECT tuuid FROM xtag WHERE tname=%Q", zCommittish); |
| 191 | return zRes; |
| 192 | } |
| 193 | |
| 194 | |
| 195 | /* |
| 196 | ** Read the git-fast-import format from pIn and insert the corresponding |
| 197 | ** content into the database. |
| 198 | */ |
| 199 | static void git_fast_import(FILE *pIn){ |
| 200 | char zLine[1000]; |
| 201 | gg.xFinish = finish_noop; |
| 202 | while( fgets(zLine, sizeof(zLine), pIn) ){ |
| 203 | if( zLine[0]=='\n' || zLine[0]=='#' ) continue; |
| 204 | if( memcmp(zLine, "blob", 4)==0 ){ |
| 205 | gg.xFinish(); |
| 206 | gg.xFinish = finish_blob; |
| 207 | }else |
| 208 | if( memcmp(zLine, "commit ", 7)==0 ){ |
| 209 | gg.xFinish(); |
| 210 | gg.xFinish = finish_commit; |
| 211 | trim_newline(&zLine[7]); |
| 212 | gg.zBranch = mprintf("%s", &zLine[7]); |
| 213 | }else |
| 214 | if( memcmp(zLine, "tag ", 4)==0 ){ |
| 215 | gg.xFinish(); |
| 216 | gg.xFinish = finish_tag; |
| 217 | trim_newline(&zLine[4]); |
| 218 | gg.zTag = mprintf("%s", &zLine[4]); |
| 219 | }else |
| 220 | if( memcmp(zLine, "reset ", 4)==0 ){ |
| 221 | gg.xFinish(); |
| 222 | }else |
| 223 | if( memcmp(zLine, "checkpoint", 10)==0 ){ |
| 224 | gg.xFinish(); |
| 225 | }else |
| 226 | if( memcmp(zLine, "feature", 7)==0 ){ |
| 227 | gg.xFinish(); |
| 228 | }else |
| 229 | if( memcmp(zLine, "option", 6)==0 ){ |
| 230 | gg.xFinish(); |
| 231 | }else |
| 232 | if( memcmp(zLine, "progress ", 9)==0 ){ |
| 233 | gg.xFinish(); |
| 234 | trim_newline(&zLine[9]); |
| 235 | printf("%s\n", &zLine[9]); |
| 236 | fflush(stdout); |
| 237 | }else |
| 238 | if( memcmp(zLine, "data ", 5)==0 ){ |
| 239 | fossil_free(gg.aData); gg.aData = 0; |
| 240 | gg.nData = atoi(&zLine[5]); |
| 241 | if( gg.nData ){ |
| 242 | int got; |
| 243 | gg.aData = fossil_malloc( gg.nData+1 ); |
| 244 | got = fread(gg.aData, 1, gg.nData, pIn); |
| 245 | if( got!=gg.nData ){ |
| 246 | fossil_fatal("short read: got %d of %d bytes", got, gg.nData); |
| 247 | } |
| 248 | if( gg.zComment==0 && gg.xFinish==finish_commit ){ |
| 249 | gg.zComment = gg.aData; |
| 250 | gg.aData = 0; |
| 251 | gg.nData = 0; |
| 252 | } |
| 253 | } |
| 254 | }else |
| 255 | if( memcmp(zLine, "author ", 7)==0 ){ |
| 256 | /* No-op */ |
| 257 | }else |
| 258 | if( memcmp(zLine, "mark ", 5)==0 ){ |
| 259 | trim_newline(&zLine[5]); |
| 260 | fossil_free(gg.zMark); |
| 261 | gg.zMark = mprintf("%s", &zLine[5]); |
| 262 | }else |
| 263 | if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",9)==0 ){ |
| 264 | int i; |
| 265 | char *z; |
| 266 | sqlite3_int64 secSince1970; |
| 267 | |
| 268 | for(i=0; zLine[i] && zLine[i]!='<'; i++){} |
| 269 | if( zLine[i]==0 ) goto malformed_line; |
| 270 | z = &zLine[i+1]; |
| 271 | for(i=i+1; zLine[i] && zLine[i]!='>'; i++){} |
| 272 | if( zLine[i]==0 ) goto malformed_line; |
| 273 | zLine[i] = 0; |
| 274 | fossil_free(gg.zUser); |
| 275 | gg.zUser = mprintf("%s", z); |
| 276 | secSince1970 = 0; |
| 277 | for(i=i+1; fossil_isdigit(zLine[i]); i++){ |
| 278 | secSince1970 = secSince1970*10 + zLine[i] - '0'; |
| 279 | } |
| 280 | fossil_free(gg.zDate); |
| 281 | gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970); |
| 282 | gg.zDate[10] = 'T'; |
| 283 | }else |
| 284 | if( memcmp(zLine, "from ", 5)==0 ){ |
| 285 | trim_newline(&zLine[5]); |
| 286 | fossil_free(gg.zFrom); |
| 287 | gg.zFrom = resolve_committish(&zLine[5]); |
| 288 | }else |
| 289 | if( memcmp(zLine, "merge ", 6)==0 ){ |
| 290 | trim_newline(&zLine[6]); |
| 291 | if( gg.nMerge>=gg.nMergeAlloc ){ |
| 292 | gg.nMergeAlloc = gg.nMergeAlloc*2 + 10; |
| 293 | gg.azMerge = fossil_realloc(gg.azMerge, gg.nMergeAlloc*sizeof(char*)); |
| 294 | } |
| 295 | gg.azMerge[gg.nMerge] = resolve_committish(&zLine[5]); |
| 296 | if( gg.azMerge[gg.nMerge] ) gg.nMerge++; |
| 297 | }else |
| 298 | if( memcmp(zLine, "M ", 2)==0 ){ |
| 299 | }else |
| 300 | if( memcmp(zLine, "D ", 2)==0 ){ |
| 301 | }else |
| 302 | if( memcmp(zLine, "C ", 2)==0 ){ |
| 303 | }else |
| 304 | if( memcmp(zLine, "R ", 2)==0 ){ |
| 305 | }else |
| 306 | if( memcmp(zLine, "deleteall", 9)==0 ){ |
| 307 | }else |
| 308 | if( memcmp(zLine, "N ", 2)==0 ){ |
| 309 | }else |
| 310 | |
| 311 | { |
| 312 | goto malformed_line; |
| 313 | } |
| 314 | } |
| 315 | gg.xFinish(); |
| 316 | import_reset(1); |
| 317 | return; |
| 318 | |
| 319 | malformed_line: |
| 320 | trim_newline(zLine); |
| 321 | fossil_fatal("bad fast-import line: [%s]", zLine); |
| 322 | return; |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | ** COMMAND: import |
| 327 | ** |
| 328 | ** Usage: %fossil import NEW-REPOSITORY |
| @@ -31,7 +330,33 @@ | |
| 330 | ** Read text generated by the git-fast-export command and use it to |
| 331 | ** construct a new Fossil repository named by the NEW-REPOSITORY |
| 332 | ** argument. The get-fast-export text is read from standard input. |
| 333 | */ |
| 334 | void git_import_cmd(void){ |
| 335 | char *zPassword; |
| 336 | fossil_fatal("this command is still under development...."); |
| 337 | if( g.argc!=3 ){ |
| 338 | usage("REPOSITORY-NAME"); |
| 339 | } |
| 340 | db_create_repository(g.argv[2]); |
| 341 | db_open_repository(g.argv[2]); |
| 342 | db_open_config(0); |
| 343 | db_multi_exec( |
| 344 | "ATTACH ':memory:' AS mem1;" |
| 345 | "CREATE TABLE mem1.xtag(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 346 | ); |
| 347 | db_begin_transaction(); |
| 348 | db_initial_setup(0, 0, 1); |
| 349 | git_fast_import(stdin); |
| 350 | db_end_transaction(0); |
| 351 | db_begin_transaction(); |
| 352 | printf("Rebuilding repository meta-data...\n"); |
| 353 | rebuild_db(0, 1); |
| 354 | printf("Vacuuming..."); fflush(stdout); |
| 355 | db_multi_exec("VACUUM"); |
| 356 | printf(" ok\n"); |
| 357 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 358 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 359 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 360 | printf("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 361 | db_end_transaction(0); |
| 362 | } |
| 363 |