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.

drh 2010-11-10 00:43 trunk
Commit 6c827ff02e9b14f6c2659e25ec5d1dcf3e359371
1 file changed +230 -16
+230 -16
--- src/import.c
+++ src/import.c
@@ -40,10 +40,11 @@
4040
int nMergeAlloc; /* Number of slots in azMerge[] */
4141
char **azMerge; /* Merge values */
4242
int nFile; /* Number of aFile values */
4343
int nFileAlloc; /* Number of slots in aFile[] */
4444
ManifestFile *aFile; /* Information about files in a commit */
45
+ int fromLoaded; /* True zFrom content loaded into aFile[] */
4546
} gg;
4647
4748
/*
4849
** A no-op "xFinish" method
4950
*/
@@ -149,11 +150,11 @@
149150
*/
150151
static void finish_tag(void){
151152
Blob record, cksum;
152153
if( gg.zDate && gg.zTag && gg.zFrom && gg.zUser ){
153154
blob_zero(&record);
154
- blob_appendf(&record, "D %z\n", gg.zDate);
155
+ blob_appendf(&record, "D %s\n", gg.zDate);
155156
blob_appendf(&record, "T +%F %s\n", gg.zTag, gg.zFrom);
156157
blob_appendf(&record, "U %F\n", gg.zUser);
157158
md5sum_blob(&record, &cksum);
158159
blob_appendf(&record, "Z %b\n", &cksum);
159160
fast_insert_content(&record, 0);
@@ -160,28 +161,96 @@
160161
blob_reset(&record);
161162
blob_reset(&cksum);
162163
}
163164
import_reset(0);
164165
}
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
+}
165175
166176
/*
167177
** Use data accumulated in gg from a "commit" record to add a new
168178
** manifest artifact to the BLOB table.
169179
*/
170180
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);
172209
import_reset(0);
173210
}
174
-
175211
176212
/*
177213
** Turn the first \n in the input string into a \000
178214
*/
179215
static void trim_newline(char *z){
180216
while( z[0] && z[0]!='\n' ){ z++; }
181217
z[0] = 0;
182218
}
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
+}
183252
184253
/*
185254
** Convert a "mark" or "committish" into the UUID.
186255
*/
187256
static char *resolve_committish(const char *zCommittish){
@@ -189,17 +258,84 @@
189258
190259
zRes = db_text(0, "SELECT tuuid FROM xtag WHERE tname=%Q", zCommittish);
191260
return zRes;
192261
}
193262
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
+
194321
195322
/*
196323
** Read the git-fast-import format from pIn and insert the corresponding
197324
** content into the database.
198325
*/
199326
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;
200335
char zLine[1000];
336
+
201337
gg.xFinish = finish_noop;
202338
while( fgets(zLine, sizeof(zLine), pIn) ){
203339
if( zLine[0]=='\n' || zLine[0]=='#' ) continue;
204340
if( memcmp(zLine, "blob", 4)==0 ){
205341
gg.xFinish();
@@ -207,17 +343,21 @@
207343
}else
208344
if( memcmp(zLine, "commit ", 7)==0 ){
209345
gg.xFinish();
210346
gg.xFinish = finish_commit;
211347
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;
213353
}else
214354
if( memcmp(zLine, "tag ", 4)==0 ){
215355
gg.xFinish();
216356
gg.xFinish = finish_tag;
217357
trim_newline(&zLine[4]);
218
- gg.zTag = mprintf("%s", &zLine[4]);
358
+ gg.zTag = import_strdup(&zLine[4]);
219359
}else
220360
if( memcmp(zLine, "reset ", 4)==0 ){
221361
gg.xFinish();
222362
}else
223363
if( memcmp(zLine, "checkpoint", 10)==0 ){
@@ -243,10 +383,11 @@
243383
gg.aData = fossil_malloc( gg.nData+1 );
244384
got = fread(gg.aData, 1, gg.nData, pIn);
245385
if( got!=gg.nData ){
246386
fossil_fatal("short read: got %d of %d bytes", got, gg.nData);
247387
}
388
+ gg.aData[got] = 0;
248389
if( gg.zComment==0 && gg.xFinish==finish_commit ){
249390
gg.zComment = gg.aData;
250391
gg.aData = 0;
251392
gg.nData = 0;
252393
}
@@ -256,27 +397,24 @@
256397
/* No-op */
257398
}else
258399
if( memcmp(zLine, "mark ", 5)==0 ){
259400
trim_newline(&zLine[5]);
260401
fossil_free(gg.zMark);
261
- gg.zMark = mprintf("%s", &zLine[5]);
402
+ gg.zMark = import_strdup(&zLine[5]);
262403
}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 ){
266405
sqlite3_int64 secSince1970;
267
-
268406
for(i=0; zLine[i] && zLine[i]!='<'; i++){}
269407
if( zLine[i]==0 ) goto malformed_line;
270408
z = &zLine[i+1];
271409
for(i=i+1; zLine[i] && zLine[i]!='>'; i++){}
272410
if( zLine[i]==0 ) goto malformed_line;
273411
zLine[i] = 0;
274412
fossil_free(gg.zUser);
275
- gg.zUser = mprintf("%s", z);
413
+ gg.zUser = import_strdup(z);
276414
secSince1970 = 0;
277
- for(i=i+1; fossil_isdigit(zLine[i]); i++){
415
+ for(i=i+2; fossil_isdigit(zLine[i]); i++){
278416
secSince1970 = secSince1970*10 + zLine[i] - '0';
279417
}
280418
fossil_free(gg.zDate);
281419
gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970);
282420
gg.zDate[10] = 'T';
@@ -294,20 +432,90 @@
294432
}
295433
gg.azMerge[gg.nMerge] = resolve_committish(&zLine[5]);
296434
if( gg.azMerge[gg.nMerge] ) gg.nMerge++;
297435
}else
298436
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);
299452
}else
300453
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
+ }
301465
}else
302466
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
+ }
303486
}else
304487
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");
305511
}else
306512
if( memcmp(zLine, "deleteall", 9)==0 ){
513
+ gg.fromLoaded = 1;
307514
}else
308515
if( memcmp(zLine, "N ", 2)==0 ){
516
+ /* No-op */
309517
}else
310518
311519
{
312520
goto malformed_line;
313521
}
@@ -331,13 +539,18 @@
331539
** construct a new Fossil repository named by the NEW-REPOSITORY
332540
** argument. The get-fast-export text is read from standard input.
333541
*/
334542
void git_import_cmd(void){
335543
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 ){
338546
usage("REPOSITORY-NAME");
547
+ }
548
+ if( g.argc==4 ){
549
+ pIn = fopen(g.argv[3], "rb");
550
+ }else{
551
+ pIn = stdin;
339552
}
340553
db_create_repository(g.argv[2]);
341554
db_open_repository(g.argv[2]);
342555
db_open_config(0);
343556
db_multi_exec(
@@ -344,19 +557,20 @@
344557
"ATTACH ':memory:' AS mem1;"
345558
"CREATE TABLE mem1.xtag(tname TEXT UNIQUE, trid INT, tuuid TEXT);"
346559
);
347560
db_begin_transaction();
348561
db_initial_setup(0, 0, 1);
349
- git_fast_import(stdin);
562
+ git_fast_import(pIn);
350563
db_end_transaction(0);
351564
db_begin_transaction();
352565
printf("Rebuilding repository meta-data...\n");
353566
rebuild_db(0, 1);
567
+ verify_cancel();
568
+ db_end_transaction(0);
354569
printf("Vacuuming..."); fflush(stdout);
355570
db_multi_exec("VACUUM");
356571
printf(" ok\n");
357572
printf("project-id: %s\n", db_get("project-code", 0));
358573
printf("server-id: %s\n", db_get("server-code", 0));
359574
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
360575
printf("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
361
- db_end_transaction(0);
362576
}
363577
--- 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button