Fossil SCM

fossil-scm / src / checkout.c
Blame History Raw 656 lines
1
/*
2
** Copyright (c) 2007 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to check-out versions of the project
19
** from the local repository.
20
*/
21
#include "config.h"
22
#include "checkout.h"
23
#include <assert.h>
24
#include <zlib.h>
25
26
/*
27
** Check to see if there is an existing check-out that has been
28
** modified. Return values:
29
**
30
** 0: There is an existing check-out but it is unmodified
31
** 1: There is a modified check-out - there are unsaved changes
32
*/
33
int unsaved_changes(unsigned int cksigFlags){
34
int vid;
35
db_must_be_within_tree();
36
vid = db_lget_int("checkout",0);
37
vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE);
38
return db_exists("SELECT 1 FROM vfile WHERE chnged"
39
" OR coalesce(origname!=pathname,0)");
40
}
41
42
/*
43
** Undo the current check-out. Unlink all files from the disk.
44
** Clear the VFILE table.
45
**
46
** Also delete any directory that becomes empty as a result of deleting
47
** files due to this operation, as long as that directory is not the
48
** current working directory and is not on the empty-dirs list.
49
*/
50
void uncheckout(int vid){
51
char *zPwd;
52
if( vid<=0 ) return;
53
sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
54
file_dirname_sql_function, 0, 0);
55
sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0,
56
file_delete_sql_function, 0, 0);
57
sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
58
file_rmdir_sql_function, 0, 0);
59
db_multi_exec(
60
"CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
61
filename_collation()
62
);
63
db_multi_exec(
64
"INSERT OR IGNORE INTO dir_to_delete(name)"
65
" SELECT dirname(pathname) FROM vfile"
66
" WHERE vid=%d AND mrid>0",
67
vid
68
);
69
do{
70
db_multi_exec(
71
"INSERT OR IGNORE INTO dir_to_delete(name)"
72
" SELECT dirname(name) FROM dir_to_delete;"
73
);
74
}while( db_changes() );
75
db_multi_exec(
76
"SELECT unlink(%Q||pathname) FROM vfile"
77
" WHERE vid=%d AND mrid>0;",
78
g.zLocalRoot, vid
79
);
80
ensure_empty_dirs_created(1);
81
zPwd = file_getcwd(0,0);
82
db_multi_exec(
83
"SELECT rmdir(%Q||name) FROM dir_to_delete"
84
" WHERE (%Q||name)<>%Q ORDER BY name DESC",
85
g.zLocalRoot, g.zLocalRoot, zPwd
86
);
87
fossil_free(zPwd);
88
db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
89
}
90
91
92
/*
93
** Given the abbreviated hash of a version, load the content of that
94
** version in the VFILE table. Return the VID for the version.
95
**
96
** If anything goes wrong, panic.
97
*/
98
int load_vfile(const char *zName, int forceMissingFlag){
99
Blob uuid;
100
int vid;
101
102
blob_init(&uuid, zName, -1);
103
if( name_to_uuid(&uuid, 1, "ci") ){
104
fossil_fatal("%s", g.zErrMsg);
105
}
106
vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
107
if( vid==0 ){
108
fossil_fatal("no such check-in: %s", g.argv[2]);
109
}
110
if( !is_a_version(vid) ){
111
fossil_fatal("object [%S] is not a check-in", blob_str(&uuid));
112
}
113
if( load_vfile_from_rid(vid) && !forceMissingFlag ){
114
fossil_fatal("missing content, unable to check out");
115
};
116
return vid;
117
}
118
119
/*
120
** Set or clear the vfile.isexe flag for a file.
121
*/
122
static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
123
static Stmt s;
124
db_static_prepare(&s,
125
"UPDATE vfile SET isexe=:isexe"
126
" WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
127
);
128
db_bind_int(&s, ":isexe", onoff);
129
db_bind_int(&s, ":vid", vid);
130
db_bind_text(&s, ":path", zFilename);
131
db_step(&s);
132
db_reset(&s);
133
}
134
135
/*
136
** Set or clear the execute permission bit (as appropriate) for all
137
** files in the current check-out, and replace files that have
138
** symlink bit with actual symlinks.
139
*/
140
void checkout_set_all_exe(int vid){
141
Blob filename;
142
int baseLen;
143
Manifest *pManifest;
144
ManifestFile *pFile;
145
146
/* Check the EXE permission status of all files
147
*/
148
pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0);
149
if( pManifest==0 ) return;
150
blob_zero(&filename);
151
blob_appendf(&filename, "%s", g.zLocalRoot);
152
baseLen = blob_size(&filename);
153
manifest_file_rewind(pManifest);
154
while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
155
int isExe;
156
blob_append(&filename, pFile->zName, -1);
157
isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
158
file_setexe(blob_str(&filename), isExe);
159
set_or_clear_isexe(pFile->zName, vid, isExe);
160
blob_resize(&filename, baseLen);
161
}
162
blob_reset(&filename);
163
manifest_destroy(pManifest);
164
}
165
166
167
/*
168
** If the "manifest" setting is true, then automatically generate
169
** files named "manifest" and "manifest.uuid" containing, respectively,
170
** the text of the manifest and the artifact ID of the manifest.
171
** If the manifest setting is set, but is not a boolean value, then treat
172
** each character as a flag to enable writing "manifest", "manifest.uuid" or
173
** "manifest.tags".
174
*/
175
void manifest_to_disk(int vid){
176
char *zManFile;
177
int flg;
178
179
flg = db_get_manifest_setting(0);
180
181
if( flg & MFESTFLG_RAW ){
182
Blob manifest = BLOB_INITIALIZER;
183
content_get(vid, &manifest);
184
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
185
zManFile = mprintf("%smanifest", g.zLocalRoot);
186
blob_write_to_file(&manifest, zManFile);
187
free(zManFile);
188
blob_reset(&manifest);
189
}else{
190
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
191
zManFile = mprintf("%smanifest", g.zLocalRoot);
192
file_delete(zManFile);
193
free(zManFile);
194
}
195
}
196
if( flg & MFESTFLG_UUID ){
197
Blob hash;
198
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
199
blob_set_dynamic(&hash, rid_to_uuid(vid));
200
blob_append(&hash, "\n", 1);
201
blob_write_to_file(&hash, zManFile);
202
free(zManFile);
203
blob_reset(&hash);
204
}else{
205
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
206
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
207
file_delete(zManFile);
208
free(zManFile);
209
}
210
}
211
if( flg & MFESTFLG_TAGS ){
212
Blob taglist = BLOB_INITIALIZER;
213
zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
214
get_checkin_taglist(vid, &taglist);
215
blob_write_to_file(&taglist, zManFile);
216
free(zManFile);
217
blob_reset(&taglist);
218
}else{
219
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
220
zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
221
file_delete(zManFile);
222
free(zManFile);
223
}
224
}
225
}
226
227
/*
228
** Find the branch name and all symbolic tags for a particular check-in
229
** identified by "rid".
230
**
231
** The branch name is actually only extracted if this procedure is run
232
** from within a local check-out. And the branch name is not the branch
233
** name for "rid" but rather the branch name for the current check-out.
234
** It is unclear if the rid parameter is always the same as the current
235
** check-out.
236
*/
237
void get_checkin_taglist(int rid, Blob *pOut){
238
Stmt stmt;
239
char *zCurrent;
240
blob_reset(pOut);
241
zCurrent = db_text(0, "SELECT value FROM tagxref"
242
" WHERE rid=%d AND tagid=%d", rid, TAG_BRANCH);
243
blob_appendf(pOut, "branch %s\n", zCurrent);
244
db_prepare(&stmt, "SELECT substr(tagname, 5)"
245
" FROM tagxref, tag"
246
" WHERE tagxref.rid=%d"
247
" AND tagxref.tagtype>0"
248
" AND tag.tagid=tagxref.tagid"
249
" AND tag.tagname GLOB 'sym-*'", rid);
250
while( db_step(&stmt)==SQLITE_ROW ){
251
const char *zName;
252
zName = db_column_text(&stmt, 0);
253
blob_appendf(pOut, "tag %s\n", zName);
254
}
255
db_reset(&stmt);
256
db_finalize(&stmt);
257
}
258
259
260
/*
261
** COMMAND: checkout*
262
** COMMAND: co#
263
**
264
** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
265
** or: %fossil co ?VERSION | --latest? ?OPTIONS?
266
**
267
** NOTE: Most people use "fossil update" instead of "fossil checkout" for
268
** day-to-day operations. If you are new to Fossil and trying to learn your
269
** way around, it is recommended that you become familiar with the
270
** "fossil update" command first.
271
**
272
** This command changes the current check-out to the version specified
273
** as an argument. The command aborts if there are edited files in the
274
** current check-out unless the --force option is used. The --keep option
275
** leaves files on disk unchanged, except the manifest and manifest.uuid
276
** files.
277
**
278
** The --latest flag can be used in place of VERSION to check-out the
279
** latest version in the repository.
280
**
281
** Options:
282
** -f|--force Ignore edited files in the current check-out
283
** -k|--keep Only update the manifest file(s)
284
** --force-missing Force check-out even if content is missing
285
** --prompt Prompt before overwriting when --force is used
286
** --setmtime Set timestamps of all files to match their SCM-side
287
** times (the timestamp of the last check-in which modified
288
** them)
289
**
290
** See also: [[update]]
291
*/
292
void checkout_cmd(void){
293
int forceFlag; /* Force check-out even if edits exist */
294
int forceMissingFlag; /* Force check-out even if missing content */
295
int keepFlag; /* Do not change any files on disk */
296
int latestFlag; /* Check out the latest version */
297
char *zVers; /* Version to check out */
298
int promptFlag; /* True to prompt before overwriting */
299
int vid, prior;
300
int setmtimeFlag; /* --setmtime. Set mtimes on files */
301
Blob cksum1, cksum1b, cksum2;
302
303
db_must_be_within_tree();
304
db_begin_transaction();
305
forceMissingFlag = find_option("force-missing",0,0)!=0;
306
keepFlag = find_option("keep","k",0)!=0;
307
forceFlag = find_option("force","f",0)!=0;
308
latestFlag = find_option("latest",0,0)!=0;
309
promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
310
setmtimeFlag = find_option("setmtime",0,0)!=0;
311
312
if( keepFlag != 0 ){
313
/* After flag collection, in order not to affect promptFlag */
314
forceFlag=1;
315
}
316
317
/* We should be done with options.. */
318
verify_all_options();
319
320
if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
321
usage("VERSION|--latest ?--force? ?--keep?");
322
}
323
if( !forceFlag && unsaved_changes(0) ){
324
fossil_fatal("there are unsaved changes in the current check-out");
325
}
326
if( forceFlag ){
327
db_multi_exec("DELETE FROM vfile");
328
prior = 0;
329
}else{
330
prior = db_lget_int("checkout",0);
331
}
332
if( latestFlag ){
333
compute_leaves(db_lget_int("checkout",0), 1);
334
zVers = db_text(0, "SELECT uuid FROM leaves, event, blob"
335
" WHERE event.objid=leaves.rid AND blob.rid=leaves.rid"
336
" ORDER BY event.mtime DESC");
337
if( zVers==0 ){
338
zVers = db_text(0, "SELECT uuid FROM event, blob"
339
" WHERE event.objid=blob.rid AND event.type='ci'"
340
" ORDER BY event.mtime DESC");
341
}
342
if( zVers==0 ){
343
db_end_transaction(0);
344
return;
345
}
346
}else{
347
zVers = g.argv[2];
348
}
349
vid = load_vfile(zVers, forceMissingFlag);
350
if( prior==vid ){
351
if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME);
352
db_end_transaction(0);
353
return;
354
}
355
if( !keepFlag ){
356
uncheckout(prior);
357
}
358
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
359
if( !keepFlag ){
360
vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
361
}
362
checkout_set_all_exe(vid);
363
ensure_empty_dirs_created(0);
364
db_set_checkout(vid, 1);
365
undo_reset();
366
db_multi_exec("DELETE FROM vmerge");
367
if( !keepFlag && db_get_boolean("repo-cksum",1) ){
368
vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
369
vfile_aggregate_checksum_disk(vid, &cksum2);
370
if( blob_compare(&cksum1, &cksum2) ){
371
fossil_print("WARNING: manifest checksum does not agree with disk\n");
372
}
373
if( blob_size(&cksum1b) && blob_compare(&cksum1, &cksum1b) ){
374
fossil_print("WARNING: manifest checksum does not agree with manifest\n");
375
}
376
}
377
if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME);
378
db_end_transaction(0);
379
}
380
381
/*
382
** Unlink the local database file
383
*/
384
static void unlink_local_database(int manifestOnly){
385
const char *zReserved;
386
int i;
387
for(i=0; (zReserved = fossil_reserved_name(i, 1))!=0; i++){
388
if( manifestOnly==0 || zReserved[0]=='m' ){
389
char *z;
390
z = mprintf("%s%s", g.zLocalRoot, zReserved);
391
file_delete(z);
392
free(z);
393
}
394
}
395
}
396
397
/*
398
** COMMAND: close*
399
**
400
** Usage: %fossil close ?OPTIONS?
401
**
402
** The opposite of "[[open]]". Close the current database connection.
403
** Require a -f or --force flag if there are unsaved changes in the
404
** current check-out or if there is non-empty stash.
405
**
406
** Options:
407
** -f|--force Necessary to close a check-out with uncommitted changes
408
**
409
** See also: [[open]]
410
*/
411
void close_cmd(void){
412
int forceFlag = find_option("force","f",0)!=0;
413
db_must_be_within_tree();
414
415
/* We should be done with options.. */
416
verify_all_options();
417
418
if( !forceFlag && unsaved_changes(0) ){
419
fossil_fatal("there are unsaved changes in the current check-out");
420
}
421
if( !forceFlag
422
&& db_table_exists("localdb","stash")
423
&& db_exists("SELECT 1 FROM localdb.stash")
424
){
425
fossil_fatal("closing the check-out will delete your stash");
426
}
427
if( db_is_writeable("repository") ){
428
db_unset_mprintf(1, "ckout:%q", g.zLocalRoot);
429
}
430
unlink_local_database(1);
431
db_close(1);
432
unlink_local_database(0);
433
}
434
435
436
/*
437
** COMMAND: get
438
**
439
** Usage: %fossil get URL ?VERSION? ?OPTIONS?
440
**
441
** Download a single check-in from a remote repository named URL and
442
** unpack all of the files into a subdirectory. The specific check-in
443
** to download is identified by VERSION. If VERSION is omitted, the
444
** latest trunk check-in is used. The URL can be a traditional "https:",
445
** "ssh:", or "file:" URL similar to the examples shown below, or it can
446
** be the name of a local repository/
447
**
448
** * https://domain.com/project
449
** * ssh://my-server/project.fossil
450
** * file:/home/user/Fossils/project.fossil
451
**
452
** This command works by downloading an SQL archive of the requested
453
** check-in and then extracting all the files from the archive.
454
**
455
** Options:
456
** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ." to
457
** extract into the local directory. If this option is
458
** omitted, Fossil invents a subdirectory name derived
459
** from base filename in the URL and from the VERSION.
460
** -f|--force Overwrite existing files
461
** --list List all the files that would have been checked
462
** out but do not actually write anything to the
463
** filesystem.
464
** --sqlar ARCHIVE Leave the check-out in an SQL-archive named ARCHIVE
465
** rather than unpacking into separate files.
466
** -v|--verbose Show all files as they are extracted
467
*/
468
void get_cmd(void){
469
int forceFlag = find_option("force","f",0)!=0;
470
int bVerbose = find_option("verbose","v",0)!=0;
471
int bQuiet = g.fQuiet;
472
int bDebug = find_option("debug",0,0)!=0;
473
int bList = find_option("list",0,0)!=0;
474
const char *zSqlArchive = find_option("sqlar",0,1);
475
const char *z;
476
char *zDest = 0; /* Where to store results */
477
char *zSql; /* SQL used to query the results */
478
const char *zUrl; /* Url to get */
479
const char *zVers; /* Version name to get */
480
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
481
Blob in, out; /* I/O for the HTTP request */
482
Blob file; /* A file to extract */
483
sqlite3 *db; /* Database containing downloaded sqlar */
484
sqlite3_stmt *pStmt; /* Statement for querying the database */
485
int rc; /* Result of subroutine calls */
486
int nFile = 0; /* Number of files written */
487
int nDir = 0; /* Number of directories written */
488
i64 nByte = 0; /* Number of bytes written */
489
490
z = find_option("dest",0,1);
491
if( z ) zDest = fossil_strdup(z);
492
verify_all_options();
493
if( g.argc<3 || g.argc>4 ){
494
usage("URL ?VERSION? ?OPTIONS?");
495
}
496
zUrl = g.argv[2];
497
zVers = g.argc==4 ? g.argv[3] : db_main_branch();
498
499
/* Parse the URL of the repository */
500
url_parse(zUrl, 0);
501
502
/* Construct an appropriate name for the destination directory */
503
if( zDest==0 ){
504
int i;
505
const char *zTail;
506
const char *zDot;
507
int n;
508
if( g.url.isFile ){
509
zTail = file_tail(g.url.name);
510
}else{
511
zTail = file_tail(g.url.path);
512
}
513
zDot = strchr(zTail,'.');
514
if( zDot==0 ) zDot = zTail+strlen(zTail);
515
n = (int)(zDot - zTail);
516
zDest = mprintf("%.*s-%s", n, zTail, zVers);
517
for(i=0; zDest[i]; i++){
518
char c = zDest[i];
519
if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
520
zDest[i] = '-';
521
}
522
}
523
}
524
if( bDebug ){
525
fossil_print("dest = %s\n", zDest);
526
}
527
528
/* Error checking */
529
if( zDest!=file_tail(zDest) ){
530
fossil_fatal("--dest must be a simple directory name, not a path");
531
}
532
if( zVers!=file_tail(zVers) ){
533
fossil_fatal("The \"fossil get\" command does not currently work with"
534
" version names that contain \"/\". This will be fixed in"
535
" a future release.");
536
}
537
/* To relax the restrictions above, change the subpath URL formula below
538
** to use query parameters. Ex: /sqlar?r=%t&name=%t */
539
540
if( !forceFlag ){
541
if( zSqlArchive ){
542
if( file_isdir(zSqlArchive, ExtFILE)>0 ){
543
fossil_fatal("file already exists: \"%s\"", zSqlArchive);
544
}
545
}else if( file_isdir(zDest, ExtFILE)>0 ){
546
if( fossil_strcmp(zDest,".")==0 ){
547
if( file_directory_list(zDest,0,1,1,0) ){
548
fossil_fatal("current directory is not empty");
549
}
550
}else{
551
fossil_fatal("\"%s\" already exists", zDest);
552
}
553
}
554
}
555
556
/* Construct a subpath on the URL if necessary */
557
if( g.url.isFile ){
558
g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
559
}else{
560
g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
561
}
562
563
if( bDebug ){
564
urlparse_print(0);
565
}
566
567
/* Fetch the ZIP archive for the requested check-in */
568
blob_init(&in, 0, 0);
569
blob_init(&out, 0, 0);
570
if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
571
if( bQuiet ) mHttpFlags |= HTTP_QUIET;
572
rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
573
if( rc
574
|| out.nUsed<512
575
|| (out.nUsed%512)!=0
576
|| memcmp(out.aData,"SQLite format 3",16)!=0
577
){
578
fossil_fatal("Server did not return the requested check-in.");
579
}
580
581
if( zSqlArchive ){
582
blob_write_to_file(&out, zSqlArchive);
583
if( bVerbose ) fossil_print("%s\n", zSqlArchive);
584
return;
585
}
586
587
rc = sqlite3_open(":memory:", &db);
588
if( rc==SQLITE_OK ){
589
int sz = blob_size(&out);
590
rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
591
SQLITE_DESERIALIZE_READONLY);
592
}
593
if( rc!=SQLITE_OK ){
594
fossil_fatal("Cannot create an in-memory database: %s",
595
sqlite3_errmsg(db));
596
}
597
zSql = mprintf("SELECT name, mode, sz, data, mtime FROM sqlar"
598
" WHERE name GLOB '%q*'", zDest);
599
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
600
fossil_free(zSql);
601
if( rc!=0 ){
602
fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
603
}
604
blob_init(&file, 0, 0);
605
while( sqlite3_step(pStmt)==SQLITE_ROW ){
606
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
607
int mode = sqlite3_column_int(pStmt, 1);
608
int sz = sqlite3_column_int(pStmt, 2);
609
if( bList ){
610
fossil_print("%s\n", zFilename);
611
}else if( mode & 0x4000 ){
612
/* A directory name */
613
nDir++;
614
file_mkdir(zFilename, ExtFILE, 1);
615
}else{
616
/* A file */
617
unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
618
unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
619
unsigned long int nOut2 = (unsigned long int)sz;
620
nFile++;
621
nByte += sz;
622
blob_resize(&file, sz);
623
if( nIn<sz ){
624
rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
625
inBuf, nIn);
626
if( rc!=Z_OK ){
627
fossil_fatal("Failed to uncompress file %s", zFilename);
628
}
629
}else{
630
memcpy(blob_buffer(&file), inBuf, sz);
631
}
632
blob_write_to_file(&file, zFilename);
633
if( mode & 0x40 ){
634
file_setexe(zFilename, 1);
635
}
636
blob_zero(&file);
637
file_set_mtime(zFilename, sqlite3_column_int64(pStmt,4));
638
if( bVerbose ){
639
fossil_print("%s\n", zFilename);
640
}
641
}
642
}
643
sqlite3_finalize(pStmt);
644
sqlite3_close(db);
645
blob_zero(&out);
646
if( !bVerbose && !bQuiet && nFile>0 && zDest ){
647
fossil_print("%d files (%,lld bytes) written into %s",
648
nFile, nByte, zDest);
649
if( nDir>1 ){
650
fossil_print(" and %d subdirectories\n", nDir-1);
651
}else{
652
fossil_print("\n");
653
}
654
}
655
}
656

Keyboard Shortcuts

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