Fossil SCM

fossil-scm / src / add.c
Blame History Raw 1164 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 "add.h"
23
#include <assert.h>
24
#include <dirent.h>
25
#include "cygsup.h"
26
27
/*
28
** This routine returns the names of files in a working check-out that
29
** are created by Fossil itself, and hence should not be added, deleted,
30
** or merge, and should be omitted from "clean" and "extras" lists.
31
**
32
** Return the N-th name. The first name has N==0. When all names have
33
** been used, return 0.
34
*/
35
const char *fossil_reserved_name(int N, int omitRepo){
36
/* Possible names of the local per-check-out database file and
37
** its associated journals
38
*/
39
static const char *const azName[] = {
40
"_FOSSIL_",
41
"_FOSSIL_-journal",
42
"_FOSSIL_-wal",
43
"_FOSSIL_-shm",
44
".fslckout",
45
".fslckout-journal",
46
".fslckout-wal",
47
".fslckout-shm",
48
49
/* The use of ".fos" as the name of the check-out database is
50
** deprecated. Use ".fslckout" instead. At some point, the following
51
** entries should be removed. 2012-02-04 */
52
".fos",
53
".fos-journal",
54
".fos-wal",
55
".fos-shm",
56
};
57
58
/* Possible names of auxiliary files generated when the "manifest" property
59
** is used
60
*/
61
static const struct {
62
const char *fname;
63
int flg;
64
}aManifestflags[] = {
65
{ "manifest", MFESTFLG_RAW },
66
{ "manifest.uuid", MFESTFLG_UUID },
67
{ "manifest.tags", MFESTFLG_TAGS }
68
};
69
static const char *azManifests[3];
70
71
/*
72
** Names of repository files, if they exist in the check-out.
73
*/
74
static const char *azRepo[4] = { 0, 0, 0, 0 };
75
76
/* Cached setting "manifest" */
77
static int cachedManifest = -1;
78
static int numManifests;
79
80
if( cachedManifest == -1 ){
81
int i;
82
Blob repo;
83
cachedManifest = db_get_manifest_setting(0);
84
numManifests = 0;
85
for(i=0; i<count(aManifestflags); i++){
86
if( cachedManifest&aManifestflags[i].flg ) {
87
azManifests[numManifests++] = aManifestflags[i].fname;
88
}
89
}
90
blob_zero(&repo);
91
if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){
92
const char *zRepo = blob_str(&repo);
93
azRepo[0] = zRepo;
94
azRepo[1] = mprintf("%s-journal", zRepo);
95
azRepo[2] = mprintf("%s-wal", zRepo);
96
azRepo[3] = mprintf("%s-shm", zRepo);
97
}
98
}
99
100
if( N<0 ) return 0;
101
if( N<count(azName) ) return azName[N];
102
N -= count(azName);
103
if( cachedManifest ){
104
if( N<numManifests ) return azManifests[N];
105
N -= numManifests;
106
}
107
if( !omitRepo && N<count(azRepo) ) return azRepo[N];
108
return 0;
109
}
110
111
/*
112
** Return a list of all reserved filenames as an SQL list.
113
*/
114
const char *fossil_all_reserved_names(int omitRepo){
115
static char *zAll = 0;
116
if( zAll==0 ){
117
Blob x;
118
int i;
119
const char *z;
120
blob_zero(&x);
121
for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){
122
if( i>0 ) blob_append(&x, ",", 1);
123
blob_appendf(&x, "'%q'", z);
124
}
125
zAll = blob_str(&x);
126
}
127
return zAll;
128
}
129
130
/*
131
** COMMAND: test-reserved-names
132
**
133
** Usage: %fossil test-reserved-names [-omitrepo]
134
**
135
** Show all reserved filenames for the current check-out.
136
*/
137
void test_reserved_names(void){
138
int i;
139
const char *z;
140
int omitRepo = find_option("omitrepo",0,0)!=0;
141
142
/* We should be done with options.. */
143
verify_all_options();
144
145
db_must_be_within_tree();
146
for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){
147
fossil_print("%3d: %s\n", i, z);
148
}
149
fossil_print("ALL: (%s)\n", fossil_all_reserved_names(omitRepo));
150
}
151
152
/*
153
** Add a single file named zName to the VFILE table with vid.
154
**
155
** Omit any file whose name is pOmit.
156
*/
157
static int add_one_file(
158
const char *zPath, /* Tree-name of file to add. */
159
int vid /* Add to this VFILE */
160
){
161
int doSkip = 0;
162
if( !file_is_simple_pathname(zPath, 1) ){
163
fossil_warning("filename contains illegal characters: %s", zPath);
164
return 0;
165
}
166
if( db_exists("SELECT 1 FROM vfile"
167
" WHERE pathname=%Q %s", zPath, filename_collation()) ){
168
db_multi_exec("UPDATE vfile SET deleted=0"
169
" WHERE pathname=%Q %s AND deleted",
170
zPath, filename_collation());
171
}else{
172
char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
173
int isExe = file_isexe(zFullname, RepoFILE);
174
int isLink = file_islink(0);
175
if( file_nondir_objects_on_path(g.zLocalRoot, zFullname) ){
176
/* Do not add unsafe files to the vfile */
177
doSkip = 1;
178
}else{
179
db_multi_exec(
180
"INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)"
181
"VALUES(%d,0,0,0,%Q,%d,%d,NULL)",
182
vid, zPath, isExe, isLink);
183
}
184
fossil_free(zFullname);
185
}
186
if( db_changes() && !doSkip ){
187
fossil_print("ADDED %s\n", zPath);
188
return 1;
189
}else{
190
fossil_print("SKIP %s\n", zPath);
191
return 0;
192
}
193
}
194
195
/*
196
** Add all files in the sfile temp table.
197
**
198
** Automatically exclude the repository file and any other files
199
** with reserved names. Also exclude files that are beneath an
200
** existing symlink.
201
*/
202
static int add_files_in_sfile(int vid){
203
const char *zRepo; /* Name of the repository database file */
204
int nAdd = 0; /* Number of files added */
205
int i; /* Loop counter */
206
const char *zReserved; /* Name of a reserved file */
207
Blob repoName; /* Treename of the repository */
208
Stmt loop; /* SQL to loop over all files to add */
209
int (*xCmp)(const char*,const char*);
210
211
if( !file_tree_name(g.zRepositoryName, &repoName, 0, 0) ){
212
blob_zero(&repoName);
213
zRepo = "";
214
}else{
215
zRepo = blob_str(&repoName);
216
}
217
if( filenames_are_case_sensitive() ){
218
xCmp = fossil_strcmp;
219
}else{
220
xCmp = fossil_stricmp;
221
}
222
db_prepare(&loop,
223
"SELECT pathname FROM sfile"
224
" WHERE pathname NOT IN ("
225
"SELECT sfile.pathname FROM vfile, sfile"
226
" WHERE vfile.islink"
227
" AND NOT vfile.deleted"
228
" AND sfile.pathname>(vfile.pathname||'/')"
229
" AND sfile.pathname<(vfile.pathname||'0'))"
230
" ORDER BY pathname");
231
while( db_step(&loop)==SQLITE_ROW ){
232
const char *zToAdd = db_column_text(&loop, 0);
233
if( fossil_strcmp(zToAdd, zRepo)==0 ) continue;
234
if( strchr(zToAdd,'/') ){
235
if( file_is_reserved_name(zToAdd, -1) ) continue;
236
}else{
237
for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){
238
if( xCmp(zToAdd, zReserved)==0 ) break;
239
}
240
if( zReserved ) continue;
241
}
242
nAdd += add_one_file(zToAdd, vid);
243
}
244
db_finalize(&loop);
245
blob_reset(&repoName);
246
return nAdd;
247
}
248
249
/*
250
** Resets the ADDED/DELETED state of a check-out, such that all
251
** newly-added (but not yet committed) files are no longer added and
252
** newly-removed (but not yet committed) files are no longer
253
** removed. If bIsAdd is true, it operates on the "add" state, else it
254
** operates on the "rm" state.
255
**
256
** If bDryRun is true it outputs what it would have done, but does not
257
** actually do it. In this case it rolls back the transaction it
258
** starts (so don't start a transaction before calling this).
259
**
260
** If bVerbose is true it outputs the name of each reset entry.
261
**
262
** This is intended to be called only in the context of the
263
** add/rm/addremove commands, after a call to verify_all_options().
264
**
265
** Un-added files are not modified but any un-rm'd files which are
266
** missing from the check-out are restored from the repo. un-rm'd files
267
** which exist in the check-out are left as-is, rather than restoring
268
** them using vfile_to_disk(), to avoid overwriting any local changes
269
** made to those files.
270
*/
271
static void addremove_reset(int bIsAdd, int bDryRun, int bVerbose){
272
int nReset = 0; /* # of entries which get reset */
273
Stmt stmt; /* vfile loop query */
274
275
db_begin_transaction();
276
db_prepare(&stmt, "SELECT id, pathname FROM vfile "
277
"WHERE %s ORDER BY pathname",
278
bIsAdd==0 ? "deleted<>0" : "rid=0"/*safe-for-%s*/);
279
while( db_step(&stmt)==SQLITE_ROW ){
280
/* This loop exists only so we can restore the contents of un-rm'd
281
** files and support verbose mode. All manipulation of vfile's
282
** contents happens after the loop. For the ADD case in non-verbose
283
** mode we "could" skip this loop entirely.
284
*/
285
int const id = db_column_int(&stmt, 0);
286
char const * zPathname = db_column_text(&stmt, 1);
287
Blob relName = empty_blob;
288
if(bIsAdd==0 || bVerbose!=0){
289
/* Make filename relative... */
290
char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
291
file_relative_name(zFullName, &relName, 0);
292
fossil_free(zFullName);
293
}
294
if(bIsAdd==0){
295
/* Restore contents of missing un-rm'd files. We don't do this
296
** unconditionally because we might cause data loss if a file
297
** is modified, rm'd, then un-rm'd.
298
*/
299
++nReset;
300
if(!file_isfile_or_link(blob_str(&relName))){
301
if(bDryRun==0){
302
vfile_to_disk(0, id, 0, 0);
303
if(bVerbose){
304
fossil_print("Restored missing file: %b\n", &relName);
305
}
306
}else{
307
fossil_print("Dry-run: not restoring missing file: %b\n", &relName);
308
}
309
}
310
if(bVerbose){
311
fossil_print("Un-removed: %b\n", &relName);
312
}
313
}else{
314
/* un-add... */
315
++nReset;
316
if(bVerbose){
317
fossil_print("Un-added: %b\n", &relName);
318
}
319
}
320
blob_reset(&relName);
321
}
322
db_finalize(&stmt);
323
if(nReset>0){
324
if(bIsAdd==0){
325
if(bDryRun==0){
326
db_exec_sql("UPDATE vfile SET deleted=0 WHERE deleted<>0");
327
}
328
fossil_print("Un-removed %d file(s).\n", nReset);
329
}else{
330
if(bDryRun==0){
331
db_exec_sql("DELETE FROM vfile WHERE rid=0");
332
}
333
fossil_print("Un-added %d file(s).\n", nReset);
334
}
335
}
336
db_end_transaction(bDryRun ? 1 : 0);
337
}
338
339
340
/*
341
** COMMAND: add
342
**
343
** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...?
344
**
345
** Make arrangements to add one or more files or directories to the
346
** current check-out at the next [[commit]].
347
**
348
** When adding files or directories recursively, filenames that begin
349
** with "." are excluded by default. To include such files, add
350
** the "--dotfiles" option to the command-line.
351
**
352
** The --ignore and --clean options are comma-separated lists of glob patterns
353
** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
354
** option does not appear on the command line then the "ignore-glob" setting
355
** is used. If the --clean option does not appear on the command line then
356
** the "clean-glob" setting is used.
357
**
358
** When attempting to explicitly add files on the command line, and if those
359
** match "ignore-glob", a confirmation is asked first. This can be prevented
360
** using the -f|--force option.
361
**
362
** The --case-sensitive option determines whether or not filenames should
363
** be treated case sensitive or not. If the option is not given, the default
364
** depends on the global setting, or the operating system default, if not set.
365
**
366
** Options:
367
** --case-sensitive BOOL Override the case-sensitive setting
368
** --dotfiles Include files beginning with a dot (".")
369
** -f|--force Add files without prompting
370
** --ignore CSG Ignore unmanaged files matching patterns from
371
** the Comma Separated Glob (CSG) pattern list
372
** --clean CSG Also ignore files matching patterns from
373
** the Comma Separated Glob (CSG) list
374
** --reset Reset the ADDED state of a check-out, such
375
** that all newly-added (but not yet committed)
376
** files are no longer added. No flags other
377
** than --verbose and --dry-run may be used
378
** with --reset.
379
** --allow-reserved Permit filenames which are reserved on
380
** Windows platforms. Such files cannot be
381
** checked out on Windows, so use with care.
382
**
383
** The following options are only valid with --reset:
384
** -v|--verbose Output information about each --reset file
385
** -n|--dry-run Display instead of run actions
386
**
387
** See also: [[addremove]], [[rm]]
388
*/
389
void add_cmd(void){
390
int i; /* Loop counter */
391
int vid; /* Currently checked-out version */
392
int nRoot; /* Full path characters in g.zLocalRoot */
393
const char *zCleanFlag; /* The --clean option or clean-glob setting */
394
const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */
395
Glob *pIgnore, *pClean; /* Ignore everything matching the glob patterns */
396
unsigned scanFlags = 0; /* Flags passed to vfile_scan() */
397
int forceFlag;
398
int allowReservedFlag = 0; /* --allow-reserved flag */
399
400
if(0!=find_option("reset",0,0)){
401
int const verboseFlag = find_option("verbose","v",0)!=0;
402
int const dryRunFlag = find_option("dry-run","n",0)!=0;
403
db_must_be_within_tree();
404
verify_all_options();
405
addremove_reset(1, dryRunFlag, verboseFlag);
406
return;
407
}
408
409
zCleanFlag = find_option("clean",0,1);
410
zIgnoreFlag = find_option("ignore",0,1);
411
forceFlag = find_option("force","f",0)!=0;
412
if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
413
allowReservedFlag = find_option("allow-reserved",0,0)!=0;
414
415
/* We should be done with options.. */
416
verify_all_options();
417
418
db_must_be_within_tree();
419
if( zCleanFlag==0 ){
420
zCleanFlag = db_get("clean-glob", 0);
421
}
422
if( zIgnoreFlag==0 ){
423
zIgnoreFlag = db_get("ignore-glob", 0);
424
}
425
if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
426
vid = db_lget_int("checkout",0);
427
db_begin_transaction();
428
db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
429
filename_collation());
430
pClean = glob_create(zCleanFlag);
431
pIgnore = glob_create(zIgnoreFlag);
432
nRoot = strlen(g.zLocalRoot);
433
434
/* Load the names of all files that are to be added into sfile temp table */
435
for(i=2; i<g.argc; i++){
436
char *zName;
437
int isDir;
438
Blob fullName = empty_blob;
439
440
/* file_tree_name() throws a fatal error if g.argv[i] is outside of the
441
** check-out. */
442
file_tree_name(g.argv[i], &fullName, 0, 1);
443
blob_reset(&fullName);
444
file_canonical_name(g.argv[i], &fullName, 0);
445
zName = blob_str(&fullName);
446
isDir = file_isdir(zName, RepoFILE);
447
if( isDir==1 ){
448
vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
449
}else if( isDir==0 ){
450
fossil_warning("not found: %s", zName);
451
}else{
452
char *zTreeName = file_case_preferred_name(g.zLocalRoot,&zName[nRoot]);
453
if( !forceFlag && glob_match(pIgnore, zTreeName) ){
454
Blob ans;
455
char cReply;
456
char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". "
457
"Add it (a=all/y/N)? ", zTreeName);
458
prompt_user(prompt, &ans);
459
fossil_free(prompt);
460
cReply = blob_str(&ans)[0];
461
blob_reset(&ans);
462
if( cReply=='a' || cReply=='A' ){
463
forceFlag = 1;
464
}else if( cReply!='y' && cReply!='Y' ){
465
blob_reset(&fullName);
466
continue;
467
}
468
}
469
db_multi_exec(
470
"INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)",
471
zTreeName
472
);
473
fossil_free(zTreeName);
474
}
475
blob_reset(&fullName);
476
}
477
glob_free(pIgnore);
478
glob_free(pClean);
479
480
/** Check for Windows-reserved names and warn or exit, as
481
** appropriate. Note that the 'add' internal machinery already
482
** _silently_ skips over any names for which
483
** file_is_reserved_name() returns true or which is in the
484
** fossil_reserved_name() list. We do not need to warn for those,
485
** as they're outright verboten. */
486
if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){
487
int reservedCount = 0;
488
Stmt q = empty_Stmt;
489
db_prepare(&q,"SELECT pathname FROM sfile "
490
"WHERE win_reserved(pathname)");
491
while( db_step(&q)==SQLITE_ROW ){
492
const char * zName = db_column_text(&q, 0);
493
++reservedCount;
494
if(allowReservedFlag){
495
fossil_warning("WARNING: Windows-reserved "
496
"filename: %s", zName);
497
}else{
498
fossil_warning("ERROR: Windows-reserved filename: %s", zName);
499
}
500
}
501
db_finalize(&q);
502
if(allowReservedFlag==0){
503
fossil_fatal("ERROR: %d Windows-reserved filename(s) added. "
504
"Use --allow-reserved to permit such names.",
505
reservedCount);
506
}
507
}
508
add_files_in_sfile(vid);
509
db_end_transaction(0);
510
}
511
512
/*
513
** This function adds a file to list of files to delete from disk after
514
** the other actions required for the parent operation have completed
515
** successfully. The first time it is called for the current process,
516
** it creates a temporary table named "fremove", to keep track of these
517
** files.
518
*/
519
static void add_file_to_remove(
520
const char *zOldName /* The old name of the file on disk. */
521
){
522
static int tableCreated = 0;
523
Blob fullOldName;
524
if( !tableCreated ){
525
db_multi_exec("CREATE TEMP TABLE fremove(x TEXT PRIMARY KEY %s)",
526
filename_collation());
527
tableCreated = 1;
528
}
529
file_tree_name(zOldName, &fullOldName, 1, 1);
530
db_multi_exec("INSERT INTO fremove VALUES('%q');", blob_str(&fullOldName));
531
blob_reset(&fullOldName);
532
}
533
534
/*
535
** This function deletes files from the check-out, using the file names
536
** contained in the temporary table "fremove". The temporary table is
537
** created on demand by the add_file_to_remove() function.
538
**
539
** If dryRunFlag is non-zero, no files will be removed; however, their
540
** names will still be output.
541
**
542
** The temporary table "fremove" is dropped after being processed.
543
*/
544
static void process_files_to_remove(
545
int dryRunFlag /* Zero to actually operate on the file-system. */
546
){
547
Stmt remove;
548
if( db_table_exists("temp", "fremove") ){
549
db_prepare(&remove, "SELECT x FROM fremove ORDER BY x;");
550
while( db_step(&remove)==SQLITE_ROW ){
551
const char *zOldName = db_column_text(&remove, 0);
552
if( !dryRunFlag ){
553
file_delete(zOldName);
554
}
555
fossil_print("DELETED_FILE %s\n", zOldName);
556
}
557
db_finalize(&remove);
558
db_multi_exec("DROP TABLE fremove;");
559
}
560
}
561
562
/*
563
** COMMAND: rm
564
** COMMAND: delete
565
** COMMAND: forget*
566
**
567
** Usage: %fossil rm|delete|forget FILE1 ?FILE2 ...?
568
**
569
** Remove one or more files or directories from the repository.
570
**
571
** The 'rm' and 'delete' commands do NOT normally remove the files from
572
** disk. They just mark the files as no longer being part of the project.
573
** In other words, future changes to the named files will not be versioned.
574
** However, the default behavior of this command may be overridden via the
575
** command line options listed below and/or the 'mv-rm-files' setting.
576
**
577
** The 'forget' command never removes files from disk, even when the command
578
** line options and/or the 'mv-rm-files' setting would otherwise require it
579
** to do so.
580
**
581
** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files"
582
** setting is non-zero, files WILL BE removed from disk as well.
583
** This does NOT apply to the 'forget' command.
584
**
585
** Options:
586
** --soft Skip removing files from the check-out.
587
** This supersedes the --hard option.
588
** --hard Remove files from the check-out
589
** --case-sensitive BOOL Override the case-sensitive setting
590
** -n|--dry-run If given, display instead of run actions.
591
** --reset Reset the DELETED state of a check-out, such
592
** that all newly-rm'd (but not yet committed)
593
** files are no longer removed. No flags other
594
** than --verbose or --dry-run may be used with
595
** --reset.
596
** -v|--verbose Outputs information about each --reset file.
597
** Only usable with --reset.
598
**
599
** See also: [[addremove]], [[add]]
600
*/
601
void delete_cmd(void){
602
int i;
603
int removeFiles;
604
int dryRunFlag = find_option("dry-run","n",0)!=0;
605
int softFlag;
606
int hardFlag;
607
Stmt loop;
608
609
if(0!=find_option("reset",0,0)){
610
int const verboseFlag = find_option("verbose","v",0)!=0;
611
db_must_be_within_tree();
612
verify_all_options();
613
addremove_reset(0, dryRunFlag, verboseFlag);
614
return;
615
}
616
617
softFlag = find_option("soft",0,0)!=0;
618
hardFlag = find_option("hard",0,0)!=0;
619
620
/* We should be done with options.. */
621
verify_all_options();
622
623
db_must_be_within_tree();
624
db_begin_transaction();
625
if( g.argv[1][0]=='f' ){ /* i.e. "forget" */
626
removeFiles = 0;
627
}else if( softFlag ){
628
removeFiles = 0;
629
}else if( hardFlag ){
630
removeFiles = 1;
631
}else{
632
removeFiles = db_get_boolean("mv-rm-files",0);
633
}
634
db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
635
filename_collation());
636
for(i=2; i<g.argc; i++){
637
Blob treeName;
638
char *zTreeName;
639
640
file_tree_name(g.argv[i], &treeName, 0, 1);
641
zTreeName = blob_str(&treeName);
642
db_multi_exec(
643
"INSERT OR IGNORE INTO sfile"
644
" SELECT pathname FROM vfile"
645
" WHERE (pathname=%Q %s"
646
" OR (pathname>'%q/' %s AND pathname<'%q0' %s))"
647
" AND NOT deleted",
648
zTreeName, filename_collation(), zTreeName,
649
filename_collation(), zTreeName, filename_collation()
650
);
651
blob_reset(&treeName);
652
}
653
654
db_prepare(&loop, "SELECT pathname FROM sfile");
655
while( db_step(&loop)==SQLITE_ROW ){
656
fossil_print("DELETED %s\n", db_column_text(&loop, 0));
657
if( removeFiles ) add_file_to_remove(db_column_text(&loop, 0));
658
}
659
db_finalize(&loop);
660
if( !dryRunFlag ){
661
db_multi_exec(
662
"UPDATE vfile SET deleted=1 WHERE pathname IN sfile;"
663
"DELETE FROM vfile WHERE rid=0 AND deleted;"
664
);
665
}
666
db_end_transaction(0);
667
if( removeFiles ) process_files_to_remove(dryRunFlag);
668
}
669
670
/*
671
** Capture the command-line --case-sensitive option.
672
*/
673
static const char *zCaseSensitive = 0;
674
void capture_case_sensitive_option(void){
675
if( zCaseSensitive==0 ){
676
zCaseSensitive = find_option("case-sensitive",0,1);
677
}
678
}
679
680
/*
681
** This routine determines if files should be case-sensitive or not.
682
** In other words, this routine determines if two filenames that
683
** differ only in case should be considered the same name or not.
684
**
685
** The case-sensitive setting determines the default value. If
686
** the case-sensitive setting is undefined, then case sensitivity
687
** defaults off for Cygwin, Mac and Windows and on for all other unix.
688
** If case-sensitivity is enabled in the windows kernel, the Cygwin port
689
** of fossil.exe can detect that, and modifies the default to 'on'.
690
**
691
** The "--case-sensitive BOOL" command-line option overrides any
692
** setting.
693
*/
694
int filenames_are_case_sensitive(void){
695
static int caseSensitive;
696
static int once = 1;
697
698
if( once ){
699
once = 0;
700
if( zCaseSensitive ){
701
caseSensitive = is_truth(zCaseSensitive);
702
}else{
703
#if defined(_WIN32) || defined(__DARWIN__) || defined(__APPLE__)
704
caseSensitive = 0; /* Mac and Windows */
705
#elif defined(__CYGWIN__)
706
/* Cygwin can be configured to be case-sensitive, check this. */
707
void *hKey;
708
int value = 1, length = sizeof(int);
709
caseSensitive = 0; /* Cygwin default */
710
if( (RegOpenKeyExW((void *)0x80000002, L"SYSTEM\\CurrentControlSet\\"
711
"Control\\Session Manager\\kernel", 0, 1, (void *)&hKey)
712
== 0) && (RegQueryValueExW(hKey, L"obcaseinsensitive",
713
0, NULL, (void *)&value, (void *)&length) == 0) && !value ){
714
caseSensitive = 1;
715
}
716
#else
717
caseSensitive = 1; /* Unix */
718
#endif
719
caseSensitive = db_get_boolean("case-sensitive",caseSensitive);
720
}
721
if( !caseSensitive && g.localOpen ){
722
db_multi_exec(
723
"CREATE INDEX IF NOT EXISTS localdb.vfile_nocase"
724
" ON vfile(pathname COLLATE nocase)"
725
);
726
}
727
}
728
return caseSensitive;
729
}
730
731
/*
732
** Return one of two things:
733
**
734
** "" (empty string) if filenames are case sensitive
735
**
736
** "COLLATE nocase" if filenames are not case sensitive.
737
*/
738
const char *filename_collation(void){
739
return filenames_are_case_sensitive() ? "" : "COLLATE nocase";
740
}
741
742
/*
743
** COMMAND: addremove
744
**
745
** Usage: %fossil addremove ?OPTIONS?
746
**
747
** Do all necessary "[[add]]" and "[[rm]]" commands to synchronize the
748
** repository with the content of the working check-out:
749
**
750
** * All files in the check-out but not in the repository (that is,
751
** all files displayed using the "extras" command) are added as
752
** if by the "[[add]]" command.
753
**
754
** * All files in the repository but missing from the check-out (that is,
755
** all files that show as MISSING with the "status" command) are
756
** removed as if by the "[[rm]]" command.
757
**
758
** Note that this command does not "commit", as that is a separate step.
759
**
760
** Files and directories whose names begin with "." are ignored unless
761
** the --dotfiles option is used.
762
**
763
** The --ignore option overrides the "ignore-glob" setting, as do the
764
** --case-sensitive option with the "case-sensitive" setting and the
765
** --clean option with the "clean-glob" setting. See the documentation
766
** on the "settings" command for further information.
767
**
768
** The -n|--dry-run option shows what would happen without actually doing
769
** anything.
770
**
771
** This command can be used to track third party software.
772
**
773
** Options:
774
** --case-sensitive BOOL Override the case-sensitive setting
775
** --dotfiles Include files beginning with a dot (".")
776
** --ignore CSG Ignore unmanaged files matching patterns from
777
** the Comma Separated Glob (CSG) list
778
** --clean CSG Also ignore files matching patterns from
779
** the Comma Separated Glob (CSG) list
780
** -n|--dry-run If given, display instead of run actions
781
** --reset Reset the ADDED/DELETED state of a check-out,
782
** such that all newly-added (but not yet committed)
783
** files are no longer added and all newly-removed
784
** (but not yet committed) files are no longer
785
** removed. No flags other than --verbose and
786
** --dry-run may be used with --reset.
787
** -v|--verbose Outputs information about each --reset file.
788
** Only usable with --reset.
789
**
790
** See also: [[add]], [[rm]]
791
*/
792
void addremove_cmd(void){
793
Blob path;
794
const char *zCleanFlag;
795
const char *zIgnoreFlag;
796
unsigned scanFlags;
797
int dryRunFlag = find_option("dry-run","n",0)!=0;
798
int n;
799
Stmt q;
800
int vid;
801
int nAdd = 0;
802
int nDelete = 0;
803
Glob *pIgnore, *pClean;
804
805
if( !dryRunFlag ){
806
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
807
}
808
809
if(0!=find_option("reset",0,0)){
810
int const verboseFlag = find_option("verbose","v",0)!=0;
811
db_must_be_within_tree();
812
verify_all_options();
813
addremove_reset(0, dryRunFlag, verboseFlag);
814
addremove_reset(1, dryRunFlag, verboseFlag);
815
return;
816
}
817
818
zCleanFlag = find_option("clean",0,1);
819
zIgnoreFlag = find_option("ignore",0,1);
820
scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
821
822
/* We should be done with options.. */
823
verify_all_options();
824
825
/* Fail if unprocessed arguments are present, in case user expect the
826
** addremove command to accept a list of file or directory.
827
*/
828
if( g.argc>2 ){
829
fossil_fatal(
830
"%s: Can only work on the entire check-out, no arguments supported.",
831
g.argv[1]);
832
}
833
db_must_be_within_tree();
834
if( zCleanFlag==0 ){
835
zCleanFlag = db_get("clean-glob", 0);
836
}
837
if( zIgnoreFlag==0 ){
838
zIgnoreFlag = db_get("ignore-glob", 0);
839
}
840
if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
841
vid = db_lget_int("checkout",0);
842
db_begin_transaction();
843
844
/* step 1:
845
** Populate the temp table "sfile" with the names of all unmanaged
846
** files currently in the check-out, except for files that match the
847
** --ignore or ignore-glob patterns and dot-files. Then add all of
848
** the files in the sfile temp table to the set of managed files.
849
*/
850
db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
851
filename_collation());
852
n = strlen(g.zLocalRoot);
853
blob_init(&path, g.zLocalRoot, n-1);
854
/* now we read the complete file structure into a temp table */
855
pClean = glob_create(zCleanFlag);
856
pIgnore = glob_create(zIgnoreFlag);
857
vfile_scan(&path, blob_size(&path), scanFlags, pClean, pIgnore, RepoFILE);
858
glob_free(pIgnore);
859
glob_free(pClean);
860
nAdd = add_files_in_sfile(vid);
861
862
/* step 2: search for missing files */
863
db_prepare(&q,
864
"SELECT pathname, %Q || pathname, deleted FROM vfile"
865
" WHERE NOT deleted"
866
" ORDER BY 1",
867
g.zLocalRoot
868
);
869
while( db_step(&q)==SQLITE_ROW ){
870
const char *zFile;
871
const char *zPath;
872
873
zFile = db_column_text(&q, 0);
874
zPath = db_column_text(&q, 1);
875
if( !file_isfile_or_link(zPath) ){
876
if( !dryRunFlag ){
877
db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
878
}
879
fossil_print("DELETED %s\n", zFile);
880
nDelete++;
881
}
882
}
883
db_finalize(&q);
884
/* show command summary */
885
fossil_print("added %d files, deleted %d files\n", nAdd, nDelete);
886
if(dryRunFlag!=0){
887
fossil_print("Dry-run mode: no changes were made.\n");
888
}
889
db_end_transaction(dryRunFlag);
890
}
891
892
/*
893
** Rename a single file.
894
**
895
** The original name of the file is zOrig. The new filename is zNew.
896
*/
897
static void mv_one_file(
898
int vid,
899
const char *zOrig,
900
const char *zNew,
901
int dryRunFlag,
902
int moveFiles
903
){
904
int x = db_int(-1, "SELECT deleted FROM vfile WHERE pathname=%Q %s",
905
zNew, filename_collation());
906
if( x>=0 ){
907
if( x==0 ){
908
if( !filenames_are_case_sensitive() && fossil_stricmp(zOrig,zNew)==0 ){
909
/* Case change only */
910
}else{
911
fossil_fatal("cannot rename '%s' to '%s' since another file named '%s'"
912
" is currently under management", zOrig, zNew, zNew);
913
}
914
}else{
915
fossil_fatal("cannot rename '%s' to '%s' since the delete of '%s' has "
916
"not yet been committed", zOrig, zNew, zNew);
917
}
918
}
919
if( moveFiles ){
920
if( file_size(zNew, ExtFILE) != -1 ){
921
fossil_fatal("cannot rename '%s' to '%s' on disk since another file"
922
" named '%s' already exists", zOrig, zNew, zNew);
923
}
924
}
925
fossil_print("RENAME %s %s\n", zOrig, zNew);
926
if( !dryRunFlag ){
927
db_multi_exec(
928
"UPDATE vfile SET pathname='%q' WHERE pathname='%q' %s AND vid=%d",
929
zNew, zOrig, filename_collation(), vid
930
);
931
}
932
}
933
934
/*
935
** This function adds a file to list of files to move on disk after the
936
** other actions required for the parent operation have completed
937
** successfully. The first time it is called for the current process,
938
** it creates a temporary table named "fmove", to keep track of these
939
** files.
940
*/
941
static void add_file_to_move(
942
const char *zOldName, /* The old name of the file on disk. */
943
const char *zNewName /* The new name of the file on disk. */
944
){
945
static int tableCreated = 0;
946
Blob fullOldName;
947
Blob fullNewName;
948
char *zOld, *zNew;
949
if( !tableCreated ){
950
db_multi_exec("CREATE TEMP TABLE fmove(x TEXT PRIMARY KEY %s, y TEXT %s)",
951
filename_collation(), filename_collation());
952
tableCreated = 1;
953
}
954
file_tree_name(zOldName, &fullOldName, 1, 1);
955
zOld = blob_str(&fullOldName);
956
file_tree_name(zNewName, &fullNewName, 1, 1);
957
zNew = blob_str(&fullNewName);
958
if( filenames_are_case_sensitive() || fossil_stricmp(zOld,zNew)!=0 ){
959
db_multi_exec("INSERT INTO fmove VALUES('%q','%q');", zOld, zNew);
960
}
961
blob_reset(&fullNewName);
962
blob_reset(&fullOldName);
963
}
964
965
/*
966
** This function moves files within the check-out, using the file names
967
** contained in the temporary table "fmove". The temporary table is
968
** created on demand by the add_file_to_move() function.
969
**
970
** If dryRunFlag is non-zero, no files will be moved; however, their
971
** names will still be output.
972
**
973
** The temporary table "fmove" is dropped after being processed.
974
*/
975
static void process_files_to_move(
976
int dryRunFlag /* Zero to actually operate on the file-system. */
977
){
978
Stmt move;
979
if( db_table_exists("temp", "fmove") ){
980
db_prepare(&move, "SELECT x, y FROM fmove ORDER BY x;");
981
while( db_step(&move)==SQLITE_ROW ){
982
const char *zOldName = db_column_text(&move, 0);
983
const char *zNewName = db_column_text(&move, 1);
984
if( !dryRunFlag ){
985
int isOldDir = file_isdir(zOldName, RepoFILE);
986
if( isOldDir==1 ){
987
int isNewDir = file_isdir(zNewName, RepoFILE);
988
if( isNewDir==0 ){
989
file_rename(zOldName, zNewName, isOldDir, isNewDir);
990
}
991
}else{
992
if( file_islink(zOldName) ){
993
symlink_copy(zOldName, zNewName);
994
}else{
995
file_copy(zOldName, zNewName);
996
}
997
file_delete(zOldName);
998
}
999
}
1000
fossil_print("MOVED_FILE %s\n", zOldName);
1001
}
1002
db_finalize(&move);
1003
db_multi_exec("DROP TABLE fmove;");
1004
}
1005
}
1006
1007
/*
1008
** COMMAND: mv
1009
** COMMAND: rename*
1010
**
1011
** Usage: %fossil mv|rename ?OPTIONS? OLDNAME NEWNAME
1012
** or: %fossil mv|rename ?OPTIONS? OLDNAME... DIR
1013
**
1014
** Move or rename one or more files or directories within the repository tree.
1015
** You can either rename a file or directory or move it to another subdirectory.
1016
**
1017
** The 'mv' command does NOT normally rename or move the files on disk.
1018
** This command merely records the fact that file names have changed so
1019
** that appropriate notations can be made at the next [[commit]].
1020
** However, the default behavior of this command may be overridden via
1021
** command line options listed below and/or the 'mv-rm-files' setting.
1022
**
1023
** The 'rename' command never renames or moves files on disk, even when the
1024
** command line options and/or the 'mv-rm-files' setting would otherwise
1025
** require it to do so.
1026
**
1027
** WARNING: If the "--hard" option is specified -OR- the "mv-rm-files"
1028
** setting is non-zero, files WILL BE renamed or moved on disk
1029
** as well. This does NOT apply to the 'rename' command.
1030
**
1031
** Options:
1032
** --soft Skip moving files within the check-out.
1033
** This supersedes the --hard option.
1034
** --hard Move files within the check-out
1035
** --case-sensitive BOOL Override the case-sensitive setting
1036
** -n|--dry-run If given, display instead of run actions
1037
**
1038
** See also: [[changes]], [[status]]
1039
*/
1040
void mv_cmd(void){
1041
int i;
1042
int vid;
1043
int moveFiles;
1044
int dryRunFlag;
1045
int softFlag;
1046
int hardFlag;
1047
int origType;
1048
int destType;
1049
char *zDest;
1050
Blob dest;
1051
Stmt q;
1052
1053
db_must_be_within_tree();
1054
dryRunFlag = find_option("dry-run","n",0)!=0;
1055
softFlag = find_option("soft",0,0)!=0;
1056
hardFlag = find_option("hard",0,0)!=0;
1057
1058
/* We should be done with options.. */
1059
verify_all_options();
1060
1061
vid = db_lget_int("checkout", 0);
1062
if( vid==0 ){
1063
fossil_fatal("no check-out in which to rename files");
1064
}
1065
if( g.argc<4 ){
1066
usage("OLDNAME NEWNAME");
1067
}
1068
zDest = file_case_preferred_name(".",g.argv[g.argc-1]);
1069
db_begin_transaction();
1070
if( g.argv[1][0]=='r' ){ /* i.e. "rename" */
1071
moveFiles = 0;
1072
}else if( softFlag ){
1073
moveFiles = 0;
1074
}else if( hardFlag ){
1075
moveFiles = 1;
1076
}else{
1077
moveFiles = db_get_boolean("mv-rm-files",0);
1078
}
1079
file_tree_name(zDest, &dest, 0, 1);
1080
db_multi_exec(
1081
"UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
1082
);
1083
db_multi_exec(
1084
"CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
1085
);
1086
if( g.argc!=4 ){
1087
origType = -1;
1088
}else{
1089
origType = (file_isdir(g.argv[2], RepoFILE) == 1);
1090
}
1091
destType = file_isdir(zDest, RepoFILE);
1092
if( origType==-1 && destType!=1 ){
1093
usage("OLDNAME NEWNAME");
1094
}else if( origType==1 && destType==2 ){
1095
fossil_fatal("cannot rename '%s' to '%s' since another file named"
1096
" '%s' exists", g.argv[2], zDest, zDest);
1097
}else if( origType==0 && destType!=1 ){
1098
Blob orig;
1099
file_tree_name(g.argv[2], &orig, 0, 1);
1100
db_multi_exec(
1101
"INSERT INTO mv VALUES(%B,%B)", &orig, &dest
1102
);
1103
}else{
1104
if( blob_eq(&dest, ".") ){
1105
blob_reset(&dest);
1106
}else{
1107
blob_append(&dest, "/", 1);
1108
}
1109
for(i=2; i<g.argc-1; i++){
1110
Blob orig;
1111
char *zOrig;
1112
int nOrig;
1113
file_tree_name(g.argv[i], &orig, 0, 1);
1114
zOrig = blob_str(&orig);
1115
nOrig = blob_size(&orig);
1116
db_prepare(&q,
1117
"SELECT pathname FROM vfile"
1118
" WHERE vid=%d"
1119
" AND (pathname='%q' %s OR (pathname>'%q/' %s AND pathname<'%q0' %s))"
1120
" ORDER BY 1",
1121
vid, zOrig, filename_collation(), zOrig, filename_collation(),
1122
zOrig, filename_collation()
1123
);
1124
while( db_step(&q)==SQLITE_ROW ){
1125
const char *zPath = db_column_text(&q, 0);
1126
int nPath = db_column_bytes(&q, 0);
1127
const char *zTail;
1128
if( nPath==nOrig ){
1129
zTail = file_tail(zPath);
1130
}else if( origType!=0 && destType==1 ){
1131
zTail = &zPath[nOrig-strlen(file_tail(zOrig))];
1132
}else{
1133
zTail = &zPath[nOrig+1];
1134
}
1135
db_multi_exec(
1136
"INSERT INTO mv VALUES('%q','%q%q')",
1137
zPath, blob_str(&dest), zTail
1138
);
1139
}
1140
db_finalize(&q);
1141
}
1142
}
1143
db_prepare(&q, "SELECT f, t FROM mv ORDER BY f");
1144
while( db_step(&q)==SQLITE_ROW ){
1145
const char *zFrom = db_column_text(&q, 0);
1146
const char *zTo = db_column_text(&q, 1);
1147
mv_one_file(vid, zFrom, zTo, dryRunFlag, moveFiles);
1148
if( moveFiles ) add_file_to_move(zFrom, zTo);
1149
}
1150
db_finalize(&q);
1151
undo_reset();
1152
db_end_transaction(0);
1153
if( moveFiles ) process_files_to_move(dryRunFlag);
1154
fossil_free(zDest);
1155
}
1156
1157
/*
1158
** Function for stash_apply to be able to restore a file and indicate
1159
** newly ADDED state.
1160
*/
1161
int stash_add_files_in_sfile(int vid){
1162
return add_files_in_sfile(vid);
1163
}
1164

Keyboard Shortcuts

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