Fossil SCM
The "fossil import" command works well enough to import the Git self-hosting repository. There are still issues and no doubt many bugs yet to be found. But this is a good start.
Commit
6c827ff02e9b14f6c2659e25ec5d1dcf3e359371
Parent
ba837f5e88d1824…
1 file changed
+230
-16
+230
-16
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -40,10 +40,11 @@ | ||
| 40 | 40 | int nMergeAlloc; /* Number of slots in azMerge[] */ |
| 41 | 41 | char **azMerge; /* Merge values */ |
| 42 | 42 | int nFile; /* Number of aFile values */ |
| 43 | 43 | int nFileAlloc; /* Number of slots in aFile[] */ |
| 44 | 44 | ManifestFile *aFile; /* Information about files in a commit */ |
| 45 | + int fromLoaded; /* True zFrom content loaded into aFile[] */ | |
| 45 | 46 | } gg; |
| 46 | 47 | |
| 47 | 48 | /* |
| 48 | 49 | ** A no-op "xFinish" method |
| 49 | 50 | */ |
| @@ -149,11 +150,11 @@ | ||
| 149 | 150 | */ |
| 150 | 151 | static void finish_tag(void){ |
| 151 | 152 | Blob record, cksum; |
| 152 | 153 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 153 | 154 | blob_zero(&record); |
| 154 | - blob_appendf(&record, "D %z\n", gg.zDate); | |
| 155 | + blob_appendf(&record, "D %s\n", gg.zDate); | |
| 155 | 156 | blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); |
| 156 | 157 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 157 | 158 | md5sum_blob(&record, &cksum); |
| 158 | 159 | blob_appendf(&record, "Z %b\n", &cksum); |
| 159 | 160 | fast_insert_content(&record, 0); |
| @@ -160,28 +161,96 @@ | ||
| 160 | 161 | blob_reset(&record); |
| 161 | 162 | blob_reset(&cksum); |
| 162 | 163 | } |
| 163 | 164 | import_reset(0); |
| 164 | 165 | } |
| 166 | + | |
| 167 | +/* | |
| 168 | +** Compare two ManifestFile objects for sorting | |
| 169 | +*/ | |
| 170 | +static int mfile_cmp(const void *pLeft, const void *pRight){ | |
| 171 | + const ManifestFile *pA = (const ManifestFile*)pLeft; | |
| 172 | + const ManifestFile *pB = (const ManifestFile*)pRight; | |
| 173 | + return strcmp(pA->zName, pB->zName); | |
| 174 | +} | |
| 165 | 175 | |
| 166 | 176 | /* |
| 167 | 177 | ** Use data accumulated in gg from a "commit" record to add a new |
| 168 | 178 | ** manifest artifact to the BLOB table. |
| 169 | 179 | */ |
| 170 | 180 | static void finish_commit(void){ |
| 171 | - /* TBD... */ | |
| 181 | + int i; | |
| 182 | + Blob record, cksum; | |
| 183 | + qsort(gg.aFile, gg.nFile, sizeof(gg.aFile[0]), mfile_cmp); | |
| 184 | + blob_zero(&record); | |
| 185 | + blob_appendf(&record, "C %F\n", gg.zComment); | |
| 186 | + blob_appendf(&record, "D %s\n", gg.zDate); | |
| 187 | + for(i=0; i<gg.nFile; i++){ | |
| 188 | + blob_appendf(&record, "F %F %s", gg.aFile[i].zName, gg.aFile[i].zUuid); | |
| 189 | + if( gg.aFile[i].zPerm && gg.aFile[i].zPerm[0] ){ | |
| 190 | + blob_appendf(&record, " %s\n", gg.aFile[i].zPerm); | |
| 191 | + }else{ | |
| 192 | + blob_append(&record, "\n", 1); | |
| 193 | + } | |
| 194 | + } | |
| 195 | + if( gg.zFrom ){ | |
| 196 | + blob_appendf(&record, "P %s", gg.zFrom); | |
| 197 | + for(i=0; i<gg.nMerge; i++){ | |
| 198 | + blob_appendf(&record, " %s", gg.azMerge[i]); | |
| 199 | + } | |
| 200 | + blob_append(&record, "\n", 1); | |
| 201 | + } | |
| 202 | + blob_appendf(&record, "U %F\n", gg.zUser); | |
| 203 | + md5sum_blob(&record, &cksum); | |
| 204 | + blob_appendf(&record, "Z %b\n", &cksum); | |
| 205 | + fast_insert_content(&record, gg.zMark); | |
| 206 | + blob_reset(&record); | |
| 207 | + blob_reset(&cksum); | |
| 208 | + import_reset(0); | |
| 172 | 209 | import_reset(0); |
| 173 | 210 | } |
| 174 | - | |
| 175 | 211 | |
| 176 | 212 | /* |
| 177 | 213 | ** Turn the first \n in the input string into a \000 |
| 178 | 214 | */ |
| 179 | 215 | static void trim_newline(char *z){ |
| 180 | 216 | while( z[0] && z[0]!='\n' ){ z++; } |
| 181 | 217 | z[0] = 0; |
| 182 | 218 | } |
| 219 | + | |
| 220 | +/* | |
| 221 | +** Duplicate a string. | |
| 222 | +*/ | |
| 223 | +static char *import_strdup(const char *zOrig){ | |
| 224 | + char *z = 0; | |
| 225 | + if( zOrig ){ | |
| 226 | + int n = strlen(zOrig); | |
| 227 | + z = fossil_malloc( n+1 ); | |
| 228 | + memcpy(z, zOrig, n+1); | |
| 229 | + } | |
| 230 | + return z; | |
| 231 | +} | |
| 232 | + | |
| 233 | +/* | |
| 234 | +** Get a token from a line of text. Return a pointer to the first | |
| 235 | +** character of the token and zero-terminate the token. Make | |
| 236 | +** *pzIn point to the first character past the end of the zero | |
| 237 | +** terminator, or at the zero-terminator at EOL. | |
| 238 | +*/ | |
| 239 | +static char *next_token(char **pzIn){ | |
| 240 | + char *z = *pzIn; | |
| 241 | + int i; | |
| 242 | + if( z[0]==0 ) return z; | |
| 243 | + for(i=0; z[i] && z[i]!=' ' && z[i]!='\n'; i++){} | |
| 244 | + if( z[i] ){ | |
| 245 | + z[i] = 0; | |
| 246 | + *pzIn = &z[i+1]; | |
| 247 | + }else{ | |
| 248 | + *pzIn = &z[i]; | |
| 249 | + } | |
| 250 | + return z; | |
| 251 | +} | |
| 183 | 252 | |
| 184 | 253 | /* |
| 185 | 254 | ** Convert a "mark" or "committish" into the UUID. |
| 186 | 255 | */ |
| 187 | 256 | static char *resolve_committish(const char *zCommittish){ |
| @@ -189,17 +258,84 @@ | ||
| 189 | 258 | |
| 190 | 259 | zRes = db_text(0, "SELECT tuuid FROM xtag WHERE tname=%Q", zCommittish); |
| 191 | 260 | return zRes; |
| 192 | 261 | } |
| 193 | 262 | |
| 263 | +/* | |
| 264 | +** Create a new entry in the gg.aFile[] array | |
| 265 | +*/ | |
| 266 | +static ManifestFile *import_add_file(void){ | |
| 267 | + ManifestFile *pFile; | |
| 268 | + if( gg.nFile>=gg.nFileAlloc ){ | |
| 269 | + gg.nFileAlloc = gg.nFileAlloc*2 + 100; | |
| 270 | + gg.aFile = fossil_realloc(gg.aFile, gg.nFileAlloc*sizeof(gg.aFile[0])); | |
| 271 | + } | |
| 272 | + pFile = &gg.aFile[gg.nFile++]; | |
| 273 | + memset(pFile, 0, sizeof(*pFile)); | |
| 274 | + return pFile; | |
| 275 | +} | |
| 276 | + | |
| 277 | + | |
| 278 | +/* | |
| 279 | +** Load all file information out of the gg.zFrom check-in | |
| 280 | +*/ | |
| 281 | +static void import_prior_files(void){ | |
| 282 | + Manifest *p; | |
| 283 | + int rid; | |
| 284 | + ManifestFile *pOld, *pNew; | |
| 285 | + if( gg.fromLoaded ) return; | |
| 286 | + gg.fromLoaded = 1; | |
| 287 | + if( gg.zFrom==0 ) return; | |
| 288 | + rid = fast_uuid_to_rid(gg.zFrom); | |
| 289 | + if( rid==0 ) return; | |
| 290 | + p = manifest_get(rid, CFTYPE_MANIFEST); | |
| 291 | + if( p==0 ) return; | |
| 292 | + manifest_file_rewind(p); | |
| 293 | + while( (pOld = manifest_file_next(p, 0))!=0 ){ | |
| 294 | + pNew = import_add_file(); | |
| 295 | + pNew->zName = import_strdup(pOld->zName); | |
| 296 | + pNew->zPerm = import_strdup(pOld->zPerm); | |
| 297 | + pNew->zUuid = import_strdup(pOld->zUuid); | |
| 298 | + } | |
| 299 | + manifest_destroy(p); | |
| 300 | +} | |
| 301 | + | |
| 302 | +/* | |
| 303 | +** Locate a file in the gg.aFile[] array by its name. Begin the search | |
| 304 | +** with the *pI-th file. Update *pI to be one past the file found. | |
| 305 | +** Do not search past the mx-th file. | |
| 306 | +*/ | |
| 307 | +static ManifestFile *import_find_file(const char *zName, int *pI, int mx){ | |
| 308 | + int i = *pI; | |
| 309 | + int nName = strlen(zName); | |
| 310 | + while( i<mx ){ | |
| 311 | + const char *z = gg.aFile[i].zName; | |
| 312 | + if( memcmp(zName, z, nName)==0 && (z[nName]==0 || z[nName]=='/') ){ | |
| 313 | + *pI = i+1; | |
| 314 | + return &gg.aFile[i]; | |
| 315 | + } | |
| 316 | + i++; | |
| 317 | + } | |
| 318 | + return 0; | |
| 319 | +} | |
| 320 | + | |
| 194 | 321 | |
| 195 | 322 | /* |
| 196 | 323 | ** Read the git-fast-import format from pIn and insert the corresponding |
| 197 | 324 | ** content into the database. |
| 198 | 325 | */ |
| 199 | 326 | static void git_fast_import(FILE *pIn){ |
| 327 | + ManifestFile *pFile; | |
| 328 | + int i, mx; | |
| 329 | + char *z; | |
| 330 | + char *zUuid; | |
| 331 | + char *zName; | |
| 332 | + char *zPerm; | |
| 333 | + char *zFrom; | |
| 334 | + char *zTo; | |
| 200 | 335 | char zLine[1000]; |
| 336 | + | |
| 201 | 337 | gg.xFinish = finish_noop; |
| 202 | 338 | while( fgets(zLine, sizeof(zLine), pIn) ){ |
| 203 | 339 | if( zLine[0]=='\n' || zLine[0]=='#' ) continue; |
| 204 | 340 | if( memcmp(zLine, "blob", 4)==0 ){ |
| 205 | 341 | gg.xFinish(); |
| @@ -207,17 +343,21 @@ | ||
| 207 | 343 | }else |
| 208 | 344 | if( memcmp(zLine, "commit ", 7)==0 ){ |
| 209 | 345 | gg.xFinish(); |
| 210 | 346 | gg.xFinish = finish_commit; |
| 211 | 347 | trim_newline(&zLine[7]); |
| 212 | - gg.zBranch = mprintf("%s", &zLine[7]); | |
| 348 | + z = &zLine[7]; | |
| 349 | + for(i=strlen(z)-1; i>=0 && z[i]!='/'; i--){} | |
| 350 | + if( z[i+1]!=0 ) z += i+1; | |
| 351 | + gg.zBranch = import_strdup(z); | |
| 352 | + gg.fromLoaded = 0; | |
| 213 | 353 | }else |
| 214 | 354 | if( memcmp(zLine, "tag ", 4)==0 ){ |
| 215 | 355 | gg.xFinish(); |
| 216 | 356 | gg.xFinish = finish_tag; |
| 217 | 357 | trim_newline(&zLine[4]); |
| 218 | - gg.zTag = mprintf("%s", &zLine[4]); | |
| 358 | + gg.zTag = import_strdup(&zLine[4]); | |
| 219 | 359 | }else |
| 220 | 360 | if( memcmp(zLine, "reset ", 4)==0 ){ |
| 221 | 361 | gg.xFinish(); |
| 222 | 362 | }else |
| 223 | 363 | if( memcmp(zLine, "checkpoint", 10)==0 ){ |
| @@ -243,10 +383,11 @@ | ||
| 243 | 383 | gg.aData = fossil_malloc( gg.nData+1 ); |
| 244 | 384 | got = fread(gg.aData, 1, gg.nData, pIn); |
| 245 | 385 | if( got!=gg.nData ){ |
| 246 | 386 | fossil_fatal("short read: got %d of %d bytes", got, gg.nData); |
| 247 | 387 | } |
| 388 | + gg.aData[got] = 0; | |
| 248 | 389 | if( gg.zComment==0 && gg.xFinish==finish_commit ){ |
| 249 | 390 | gg.zComment = gg.aData; |
| 250 | 391 | gg.aData = 0; |
| 251 | 392 | gg.nData = 0; |
| 252 | 393 | } |
| @@ -256,27 +397,24 @@ | ||
| 256 | 397 | /* No-op */ |
| 257 | 398 | }else |
| 258 | 399 | if( memcmp(zLine, "mark ", 5)==0 ){ |
| 259 | 400 | trim_newline(&zLine[5]); |
| 260 | 401 | fossil_free(gg.zMark); |
| 261 | - gg.zMark = mprintf("%s", &zLine[5]); | |
| 402 | + gg.zMark = import_strdup(&zLine[5]); | |
| 262 | 403 | }else |
| 263 | - if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",9)==0 ){ | |
| 264 | - int i; | |
| 265 | - char *z; | |
| 404 | + if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",10)==0 ){ | |
| 266 | 405 | sqlite3_int64 secSince1970; |
| 267 | - | |
| 268 | 406 | for(i=0; zLine[i] && zLine[i]!='<'; i++){} |
| 269 | 407 | if( zLine[i]==0 ) goto malformed_line; |
| 270 | 408 | z = &zLine[i+1]; |
| 271 | 409 | for(i=i+1; zLine[i] && zLine[i]!='>'; i++){} |
| 272 | 410 | if( zLine[i]==0 ) goto malformed_line; |
| 273 | 411 | zLine[i] = 0; |
| 274 | 412 | fossil_free(gg.zUser); |
| 275 | - gg.zUser = mprintf("%s", z); | |
| 413 | + gg.zUser = import_strdup(z); | |
| 276 | 414 | secSince1970 = 0; |
| 277 | - for(i=i+1; fossil_isdigit(zLine[i]); i++){ | |
| 415 | + for(i=i+2; fossil_isdigit(zLine[i]); i++){ | |
| 278 | 416 | secSince1970 = secSince1970*10 + zLine[i] - '0'; |
| 279 | 417 | } |
| 280 | 418 | fossil_free(gg.zDate); |
| 281 | 419 | gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970); |
| 282 | 420 | gg.zDate[10] = 'T'; |
| @@ -294,20 +432,90 @@ | ||
| 294 | 432 | } |
| 295 | 433 | gg.azMerge[gg.nMerge] = resolve_committish(&zLine[5]); |
| 296 | 434 | if( gg.azMerge[gg.nMerge] ) gg.nMerge++; |
| 297 | 435 | }else |
| 298 | 436 | if( memcmp(zLine, "M ", 2)==0 ){ |
| 437 | + import_prior_files(); | |
| 438 | + z = &zLine[2]; | |
| 439 | + zPerm = next_token(&z); | |
| 440 | + zUuid = next_token(&z); | |
| 441 | + zName = next_token(&z); | |
| 442 | + i = 0; | |
| 443 | + pFile = import_find_file(zName, &i, gg.nFile); | |
| 444 | + if( pFile==0 ){ | |
| 445 | + pFile = import_add_file(); | |
| 446 | + pFile->zName = import_strdup(zName); | |
| 447 | + } | |
| 448 | + if( strcmp(zPerm, "100755")==0 ){ | |
| 449 | + pFile->zPerm = mprintf("x"); | |
| 450 | + } | |
| 451 | + pFile->zUuid = resolve_committish(zUuid); | |
| 299 | 452 | }else |
| 300 | 453 | if( memcmp(zLine, "D ", 2)==0 ){ |
| 454 | + import_prior_files(); | |
| 455 | + z = &zLine[2]; | |
| 456 | + zName = next_token(&z); | |
| 457 | + i = 0; | |
| 458 | + while( (pFile = import_find_file(zName, &i, gg.nFile))!=0 ){ | |
| 459 | + fossil_free(pFile->zName); | |
| 460 | + fossil_free(pFile->zPerm); | |
| 461 | + fossil_free(pFile->zUuid); | |
| 462 | + *pFile = gg.aFile[--gg.nFile]; | |
| 463 | + i--; | |
| 464 | + } | |
| 301 | 465 | }else |
| 302 | 466 | if( memcmp(zLine, "C ", 2)==0 ){ |
| 467 | + int nFrom; | |
| 468 | + import_prior_files(); | |
| 469 | + z = &zLine[2]; | |
| 470 | + zFrom = next_token(&z); | |
| 471 | + zTo = next_token(&z); | |
| 472 | + i = 0; | |
| 473 | + mx = gg.nFile; | |
| 474 | + nFrom = strlen(zFrom); | |
| 475 | + while( (pFile = import_find_file(zFrom, &i, mx))!=0 ){ | |
| 476 | + ManifestFile *pNew = import_add_file(); | |
| 477 | + pFile = &gg.aFile[i-1]; | |
| 478 | + if( strlen(pFile->zName)>nFrom ){ | |
| 479 | + pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]); | |
| 480 | + }else{ | |
| 481 | + pNew->zName = import_strdup(pFile->zName); | |
| 482 | + } | |
| 483 | + pNew->zPerm = import_strdup(pFile->zPerm); | |
| 484 | + pNew->zUuid = import_strdup(pFile->zUuid); | |
| 485 | + } | |
| 303 | 486 | }else |
| 304 | 487 | if( memcmp(zLine, "R ", 2)==0 ){ |
| 488 | + int nFrom; | |
| 489 | + import_prior_files(); | |
| 490 | + z = &zLine[2]; | |
| 491 | + zFrom = next_token(&z); | |
| 492 | + zTo = next_token(&z); | |
| 493 | + i = 0; | |
| 494 | + nFrom = strlen(zFrom); | |
| 495 | + while( (pFile = import_find_file(zFrom, &i, gg.nFile))!=0 ){ | |
| 496 | + ManifestFile *pNew = import_add_file(); | |
| 497 | + pFile = &gg.aFile[i-1]; | |
| 498 | + if( strlen(pFile->zName)>nFrom ){ | |
| 499 | + pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]); | |
| 500 | + }else{ | |
| 501 | + pNew->zName = import_strdup(pFile->zName); | |
| 502 | + } | |
| 503 | + pNew->zPrior = pFile->zName; | |
| 504 | + pNew->zPerm = pFile->zPerm; | |
| 505 | + pNew->zUuid = pFile->zUuid; | |
| 506 | + gg.nFile--; | |
| 507 | + *pFile = *pNew; | |
| 508 | + memset(pNew, 0, sizeof(*pNew)); | |
| 509 | + } | |
| 510 | + fossil_fatal("cannot handle R records, use --full-tree"); | |
| 305 | 511 | }else |
| 306 | 512 | if( memcmp(zLine, "deleteall", 9)==0 ){ |
| 513 | + gg.fromLoaded = 1; | |
| 307 | 514 | }else |
| 308 | 515 | if( memcmp(zLine, "N ", 2)==0 ){ |
| 516 | + /* No-op */ | |
| 309 | 517 | }else |
| 310 | 518 | |
| 311 | 519 | { |
| 312 | 520 | goto malformed_line; |
| 313 | 521 | } |
| @@ -331,13 +539,18 @@ | ||
| 331 | 539 | ** construct a new Fossil repository named by the NEW-REPOSITORY |
| 332 | 540 | ** argument. The get-fast-export text is read from standard input. |
| 333 | 541 | */ |
| 334 | 542 | void git_import_cmd(void){ |
| 335 | 543 | char *zPassword; |
| 336 | - fossil_fatal("this command is still under development...."); | |
| 337 | - if( g.argc!=3 ){ | |
| 544 | + FILE *pIn; | |
| 545 | + if( g.argc!=3 && g.argc!=4 ){ | |
| 338 | 546 | usage("REPOSITORY-NAME"); |
| 547 | + } | |
| 548 | + if( g.argc==4 ){ | |
| 549 | + pIn = fopen(g.argv[3], "rb"); | |
| 550 | + }else{ | |
| 551 | + pIn = stdin; | |
| 339 | 552 | } |
| 340 | 553 | db_create_repository(g.argv[2]); |
| 341 | 554 | db_open_repository(g.argv[2]); |
| 342 | 555 | db_open_config(0); |
| 343 | 556 | db_multi_exec( |
| @@ -344,19 +557,20 @@ | ||
| 344 | 557 | "ATTACH ':memory:' AS mem1;" |
| 345 | 558 | "CREATE TABLE mem1.xtag(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 346 | 559 | ); |
| 347 | 560 | db_begin_transaction(); |
| 348 | 561 | db_initial_setup(0, 0, 1); |
| 349 | - git_fast_import(stdin); | |
| 562 | + git_fast_import(pIn); | |
| 350 | 563 | db_end_transaction(0); |
| 351 | 564 | db_begin_transaction(); |
| 352 | 565 | printf("Rebuilding repository meta-data...\n"); |
| 353 | 566 | rebuild_db(0, 1); |
| 567 | + verify_cancel(); | |
| 568 | + db_end_transaction(0); | |
| 354 | 569 | printf("Vacuuming..."); fflush(stdout); |
| 355 | 570 | db_multi_exec("VACUUM"); |
| 356 | 571 | printf(" ok\n"); |
| 357 | 572 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 358 | 573 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 359 | 574 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 360 | 575 | printf("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 361 | - db_end_transaction(0); | |
| 362 | 576 | } |
| 363 | 577 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -40,10 +40,11 @@ | |
| 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 | */ |
| @@ -149,11 +150,11 @@ | |
| 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,28 +161,96 @@ | |
| 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){ |
| @@ -189,17 +258,84 @@ | |
| 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(); |
| @@ -207,17 +343,21 @@ | |
| 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 ){ |
| @@ -243,10 +383,11 @@ | |
| 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 | } |
| @@ -256,27 +397,24 @@ | |
| 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'; |
| @@ -294,20 +432,90 @@ | |
| 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 | } |
| @@ -331,13 +539,18 @@ | |
| 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,19 +557,20 @@ | |
| 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 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -40,10 +40,11 @@ | |
| 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 | int fromLoaded; /* True zFrom content loaded into aFile[] */ |
| 46 | } gg; |
| 47 | |
| 48 | /* |
| 49 | ** A no-op "xFinish" method |
| 50 | */ |
| @@ -149,11 +150,11 @@ | |
| 150 | */ |
| 151 | static void finish_tag(void){ |
| 152 | Blob record, cksum; |
| 153 | if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){ |
| 154 | blob_zero(&record); |
| 155 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 156 | blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom); |
| 157 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 158 | md5sum_blob(&record, &cksum); |
| 159 | blob_appendf(&record, "Z %b\n", &cksum); |
| 160 | fast_insert_content(&record, 0); |
| @@ -160,28 +161,96 @@ | |
| 161 | blob_reset(&record); |
| 162 | blob_reset(&cksum); |
| 163 | } |
| 164 | import_reset(0); |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | ** Compare two ManifestFile objects for sorting |
| 169 | */ |
| 170 | static int mfile_cmp(const void *pLeft, const void *pRight){ |
| 171 | const ManifestFile *pA = (const ManifestFile*)pLeft; |
| 172 | const ManifestFile *pB = (const ManifestFile*)pRight; |
| 173 | return strcmp(pA->zName, pB->zName); |
| 174 | } |
| 175 | |
| 176 | /* |
| 177 | ** Use data accumulated in gg from a "commit" record to add a new |
| 178 | ** manifest artifact to the BLOB table. |
| 179 | */ |
| 180 | static void finish_commit(void){ |
| 181 | int i; |
| 182 | Blob record, cksum; |
| 183 | qsort(gg.aFile, gg.nFile, sizeof(gg.aFile[0]), mfile_cmp); |
| 184 | blob_zero(&record); |
| 185 | blob_appendf(&record, "C %F\n", gg.zComment); |
| 186 | blob_appendf(&record, "D %s\n", gg.zDate); |
| 187 | for(i=0; i<gg.nFile; i++){ |
| 188 | blob_appendf(&record, "F %F %s", gg.aFile[i].zName, gg.aFile[i].zUuid); |
| 189 | if( gg.aFile[i].zPerm && gg.aFile[i].zPerm[0] ){ |
| 190 | blob_appendf(&record, " %s\n", gg.aFile[i].zPerm); |
| 191 | }else{ |
| 192 | blob_append(&record, "\n", 1); |
| 193 | } |
| 194 | } |
| 195 | if( gg.zFrom ){ |
| 196 | blob_appendf(&record, "P %s", gg.zFrom); |
| 197 | for(i=0; i<gg.nMerge; i++){ |
| 198 | blob_appendf(&record, " %s", gg.azMerge[i]); |
| 199 | } |
| 200 | blob_append(&record, "\n", 1); |
| 201 | } |
| 202 | blob_appendf(&record, "U %F\n", gg.zUser); |
| 203 | md5sum_blob(&record, &cksum); |
| 204 | blob_appendf(&record, "Z %b\n", &cksum); |
| 205 | fast_insert_content(&record, gg.zMark); |
| 206 | blob_reset(&record); |
| 207 | blob_reset(&cksum); |
| 208 | import_reset(0); |
| 209 | import_reset(0); |
| 210 | } |
| 211 | |
| 212 | /* |
| 213 | ** Turn the first \n in the input string into a \000 |
| 214 | */ |
| 215 | static void trim_newline(char *z){ |
| 216 | while( z[0] && z[0]!='\n' ){ z++; } |
| 217 | z[0] = 0; |
| 218 | } |
| 219 | |
| 220 | /* |
| 221 | ** Duplicate a string. |
| 222 | */ |
| 223 | static char *import_strdup(const char *zOrig){ |
| 224 | char *z = 0; |
| 225 | if( zOrig ){ |
| 226 | int n = strlen(zOrig); |
| 227 | z = fossil_malloc( n+1 ); |
| 228 | memcpy(z, zOrig, n+1); |
| 229 | } |
| 230 | return z; |
| 231 | } |
| 232 | |
| 233 | /* |
| 234 | ** Get a token from a line of text. Return a pointer to the first |
| 235 | ** character of the token and zero-terminate the token. Make |
| 236 | ** *pzIn point to the first character past the end of the zero |
| 237 | ** terminator, or at the zero-terminator at EOL. |
| 238 | */ |
| 239 | static char *next_token(char **pzIn){ |
| 240 | char *z = *pzIn; |
| 241 | int i; |
| 242 | if( z[0]==0 ) return z; |
| 243 | for(i=0; z[i] && z[i]!=' ' && z[i]!='\n'; i++){} |
| 244 | if( z[i] ){ |
| 245 | z[i] = 0; |
| 246 | *pzIn = &z[i+1]; |
| 247 | }else{ |
| 248 | *pzIn = &z[i]; |
| 249 | } |
| 250 | return z; |
| 251 | } |
| 252 | |
| 253 | /* |
| 254 | ** Convert a "mark" or "committish" into the UUID. |
| 255 | */ |
| 256 | static char *resolve_committish(const char *zCommittish){ |
| @@ -189,17 +258,84 @@ | |
| 258 | |
| 259 | zRes = db_text(0, "SELECT tuuid FROM xtag WHERE tname=%Q", zCommittish); |
| 260 | return zRes; |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** Create a new entry in the gg.aFile[] array |
| 265 | */ |
| 266 | static ManifestFile *import_add_file(void){ |
| 267 | ManifestFile *pFile; |
| 268 | if( gg.nFile>=gg.nFileAlloc ){ |
| 269 | gg.nFileAlloc = gg.nFileAlloc*2 + 100; |
| 270 | gg.aFile = fossil_realloc(gg.aFile, gg.nFileAlloc*sizeof(gg.aFile[0])); |
| 271 | } |
| 272 | pFile = &gg.aFile[gg.nFile++]; |
| 273 | memset(pFile, 0, sizeof(*pFile)); |
| 274 | return pFile; |
| 275 | } |
| 276 | |
| 277 | |
| 278 | /* |
| 279 | ** Load all file information out of the gg.zFrom check-in |
| 280 | */ |
| 281 | static void import_prior_files(void){ |
| 282 | Manifest *p; |
| 283 | int rid; |
| 284 | ManifestFile *pOld, *pNew; |
| 285 | if( gg.fromLoaded ) return; |
| 286 | gg.fromLoaded = 1; |
| 287 | if( gg.zFrom==0 ) return; |
| 288 | rid = fast_uuid_to_rid(gg.zFrom); |
| 289 | if( rid==0 ) return; |
| 290 | p = manifest_get(rid, CFTYPE_MANIFEST); |
| 291 | if( p==0 ) return; |
| 292 | manifest_file_rewind(p); |
| 293 | while( (pOld = manifest_file_next(p, 0))!=0 ){ |
| 294 | pNew = import_add_file(); |
| 295 | pNew->zName = import_strdup(pOld->zName); |
| 296 | pNew->zPerm = import_strdup(pOld->zPerm); |
| 297 | pNew->zUuid = import_strdup(pOld->zUuid); |
| 298 | } |
| 299 | manifest_destroy(p); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** Locate a file in the gg.aFile[] array by its name. Begin the search |
| 304 | ** with the *pI-th file. Update *pI to be one past the file found. |
| 305 | ** Do not search past the mx-th file. |
| 306 | */ |
| 307 | static ManifestFile *import_find_file(const char *zName, int *pI, int mx){ |
| 308 | int i = *pI; |
| 309 | int nName = strlen(zName); |
| 310 | while( i<mx ){ |
| 311 | const char *z = gg.aFile[i].zName; |
| 312 | if( memcmp(zName, z, nName)==0 && (z[nName]==0 || z[nName]=='/') ){ |
| 313 | *pI = i+1; |
| 314 | return &gg.aFile[i]; |
| 315 | } |
| 316 | i++; |
| 317 | } |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | |
| 322 | /* |
| 323 | ** Read the git-fast-import format from pIn and insert the corresponding |
| 324 | ** content into the database. |
| 325 | */ |
| 326 | static void git_fast_import(FILE *pIn){ |
| 327 | ManifestFile *pFile; |
| 328 | int i, mx; |
| 329 | char *z; |
| 330 | char *zUuid; |
| 331 | char *zName; |
| 332 | char *zPerm; |
| 333 | char *zFrom; |
| 334 | char *zTo; |
| 335 | char zLine[1000]; |
| 336 | |
| 337 | gg.xFinish = finish_noop; |
| 338 | while( fgets(zLine, sizeof(zLine), pIn) ){ |
| 339 | if( zLine[0]=='\n' || zLine[0]=='#' ) continue; |
| 340 | if( memcmp(zLine, "blob", 4)==0 ){ |
| 341 | gg.xFinish(); |
| @@ -207,17 +343,21 @@ | |
| 343 | }else |
| 344 | if( memcmp(zLine, "commit ", 7)==0 ){ |
| 345 | gg.xFinish(); |
| 346 | gg.xFinish = finish_commit; |
| 347 | trim_newline(&zLine[7]); |
| 348 | z = &zLine[7]; |
| 349 | for(i=strlen(z)-1; i>=0 && z[i]!='/'; i--){} |
| 350 | if( z[i+1]!=0 ) z += i+1; |
| 351 | gg.zBranch = import_strdup(z); |
| 352 | gg.fromLoaded = 0; |
| 353 | }else |
| 354 | if( memcmp(zLine, "tag ", 4)==0 ){ |
| 355 | gg.xFinish(); |
| 356 | gg.xFinish = finish_tag; |
| 357 | trim_newline(&zLine[4]); |
| 358 | gg.zTag = import_strdup(&zLine[4]); |
| 359 | }else |
| 360 | if( memcmp(zLine, "reset ", 4)==0 ){ |
| 361 | gg.xFinish(); |
| 362 | }else |
| 363 | if( memcmp(zLine, "checkpoint", 10)==0 ){ |
| @@ -243,10 +383,11 @@ | |
| 383 | gg.aData = fossil_malloc( gg.nData+1 ); |
| 384 | got = fread(gg.aData, 1, gg.nData, pIn); |
| 385 | if( got!=gg.nData ){ |
| 386 | fossil_fatal("short read: got %d of %d bytes", got, gg.nData); |
| 387 | } |
| 388 | gg.aData[got] = 0; |
| 389 | if( gg.zComment==0 && gg.xFinish==finish_commit ){ |
| 390 | gg.zComment = gg.aData; |
| 391 | gg.aData = 0; |
| 392 | gg.nData = 0; |
| 393 | } |
| @@ -256,27 +397,24 @@ | |
| 397 | /* No-op */ |
| 398 | }else |
| 399 | if( memcmp(zLine, "mark ", 5)==0 ){ |
| 400 | trim_newline(&zLine[5]); |
| 401 | fossil_free(gg.zMark); |
| 402 | gg.zMark = import_strdup(&zLine[5]); |
| 403 | }else |
| 404 | if( memcmp(zLine, "tagger ", 7)==0 || memcmp(zLine, "committer ",10)==0 ){ |
| 405 | sqlite3_int64 secSince1970; |
| 406 | for(i=0; zLine[i] && zLine[i]!='<'; i++){} |
| 407 | if( zLine[i]==0 ) goto malformed_line; |
| 408 | z = &zLine[i+1]; |
| 409 | for(i=i+1; zLine[i] && zLine[i]!='>'; i++){} |
| 410 | if( zLine[i]==0 ) goto malformed_line; |
| 411 | zLine[i] = 0; |
| 412 | fossil_free(gg.zUser); |
| 413 | gg.zUser = import_strdup(z); |
| 414 | secSince1970 = 0; |
| 415 | for(i=i+2; fossil_isdigit(zLine[i]); i++){ |
| 416 | secSince1970 = secSince1970*10 + zLine[i] - '0'; |
| 417 | } |
| 418 | fossil_free(gg.zDate); |
| 419 | gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970); |
| 420 | gg.zDate[10] = 'T'; |
| @@ -294,20 +432,90 @@ | |
| 432 | } |
| 433 | gg.azMerge[gg.nMerge] = resolve_committish(&zLine[5]); |
| 434 | if( gg.azMerge[gg.nMerge] ) gg.nMerge++; |
| 435 | }else |
| 436 | if( memcmp(zLine, "M ", 2)==0 ){ |
| 437 | import_prior_files(); |
| 438 | z = &zLine[2]; |
| 439 | zPerm = next_token(&z); |
| 440 | zUuid = next_token(&z); |
| 441 | zName = next_token(&z); |
| 442 | i = 0; |
| 443 | pFile = import_find_file(zName, &i, gg.nFile); |
| 444 | if( pFile==0 ){ |
| 445 | pFile = import_add_file(); |
| 446 | pFile->zName = import_strdup(zName); |
| 447 | } |
| 448 | if( strcmp(zPerm, "100755")==0 ){ |
| 449 | pFile->zPerm = mprintf("x"); |
| 450 | } |
| 451 | pFile->zUuid = resolve_committish(zUuid); |
| 452 | }else |
| 453 | if( memcmp(zLine, "D ", 2)==0 ){ |
| 454 | import_prior_files(); |
| 455 | z = &zLine[2]; |
| 456 | zName = next_token(&z); |
| 457 | i = 0; |
| 458 | while( (pFile = import_find_file(zName, &i, gg.nFile))!=0 ){ |
| 459 | fossil_free(pFile->zName); |
| 460 | fossil_free(pFile->zPerm); |
| 461 | fossil_free(pFile->zUuid); |
| 462 | *pFile = gg.aFile[--gg.nFile]; |
| 463 | i--; |
| 464 | } |
| 465 | }else |
| 466 | if( memcmp(zLine, "C ", 2)==0 ){ |
| 467 | int nFrom; |
| 468 | import_prior_files(); |
| 469 | z = &zLine[2]; |
| 470 | zFrom = next_token(&z); |
| 471 | zTo = next_token(&z); |
| 472 | i = 0; |
| 473 | mx = gg.nFile; |
| 474 | nFrom = strlen(zFrom); |
| 475 | while( (pFile = import_find_file(zFrom, &i, mx))!=0 ){ |
| 476 | ManifestFile *pNew = import_add_file(); |
| 477 | pFile = &gg.aFile[i-1]; |
| 478 | if( strlen(pFile->zName)>nFrom ){ |
| 479 | pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]); |
| 480 | }else{ |
| 481 | pNew->zName = import_strdup(pFile->zName); |
| 482 | } |
| 483 | pNew->zPerm = import_strdup(pFile->zPerm); |
| 484 | pNew->zUuid = import_strdup(pFile->zUuid); |
| 485 | } |
| 486 | }else |
| 487 | if( memcmp(zLine, "R ", 2)==0 ){ |
| 488 | int nFrom; |
| 489 | import_prior_files(); |
| 490 | z = &zLine[2]; |
| 491 | zFrom = next_token(&z); |
| 492 | zTo = next_token(&z); |
| 493 | i = 0; |
| 494 | nFrom = strlen(zFrom); |
| 495 | while( (pFile = import_find_file(zFrom, &i, gg.nFile))!=0 ){ |
| 496 | ManifestFile *pNew = import_add_file(); |
| 497 | pFile = &gg.aFile[i-1]; |
| 498 | if( strlen(pFile->zName)>nFrom ){ |
| 499 | pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]); |
| 500 | }else{ |
| 501 | pNew->zName = import_strdup(pFile->zName); |
| 502 | } |
| 503 | pNew->zPrior = pFile->zName; |
| 504 | pNew->zPerm = pFile->zPerm; |
| 505 | pNew->zUuid = pFile->zUuid; |
| 506 | gg.nFile--; |
| 507 | *pFile = *pNew; |
| 508 | memset(pNew, 0, sizeof(*pNew)); |
| 509 | } |
| 510 | fossil_fatal("cannot handle R records, use --full-tree"); |
| 511 | }else |
| 512 | if( memcmp(zLine, "deleteall", 9)==0 ){ |
| 513 | gg.fromLoaded = 1; |
| 514 | }else |
| 515 | if( memcmp(zLine, "N ", 2)==0 ){ |
| 516 | /* No-op */ |
| 517 | }else |
| 518 | |
| 519 | { |
| 520 | goto malformed_line; |
| 521 | } |
| @@ -331,13 +539,18 @@ | |
| 539 | ** construct a new Fossil repository named by the NEW-REPOSITORY |
| 540 | ** argument. The get-fast-export text is read from standard input. |
| 541 | */ |
| 542 | void git_import_cmd(void){ |
| 543 | char *zPassword; |
| 544 | FILE *pIn; |
| 545 | if( g.argc!=3 && g.argc!=4 ){ |
| 546 | usage("REPOSITORY-NAME"); |
| 547 | } |
| 548 | if( g.argc==4 ){ |
| 549 | pIn = fopen(g.argv[3], "rb"); |
| 550 | }else{ |
| 551 | pIn = stdin; |
| 552 | } |
| 553 | db_create_repository(g.argv[2]); |
| 554 | db_open_repository(g.argv[2]); |
| 555 | db_open_config(0); |
| 556 | db_multi_exec( |
| @@ -344,19 +557,20 @@ | |
| 557 | "ATTACH ':memory:' AS mem1;" |
| 558 | "CREATE TABLE mem1.xtag(tname TEXT UNIQUE, trid INT, tuuid TEXT);" |
| 559 | ); |
| 560 | db_begin_transaction(); |
| 561 | db_initial_setup(0, 0, 1); |
| 562 | git_fast_import(pIn); |
| 563 | db_end_transaction(0); |
| 564 | db_begin_transaction(); |
| 565 | printf("Rebuilding repository meta-data...\n"); |
| 566 | rebuild_db(0, 1); |
| 567 | verify_cancel(); |
| 568 | db_end_transaction(0); |
| 569 | printf("Vacuuming..."); fflush(stdout); |
| 570 | db_multi_exec("VACUUM"); |
| 571 | printf(" ok\n"); |
| 572 | printf("project-id: %s\n", db_get("project-code", 0)); |
| 573 | printf("server-id: %s\n", db_get("server-code", 0)); |
| 574 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 575 | printf("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 576 | } |
| 577 |