Fossil SCM

fossil-scm / src / allrepo.c
Blame History Raw 547 lines
1
/*
2
** Copyright (c) 2008 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 to implement the "all" command-line method.
19
*/
20
#include "config.h"
21
#include "allrepo.h"
22
#include <assert.h>
23
24
/*
25
** Build a string that contains all of the command-line options
26
** specified as arguments. collect_argument() is used for stand-alone
27
** options and collect_argument_value() is used for options that are
28
** followed by an argument value.
29
*/
30
static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){
31
const char *z = find_option(zArg, zShort, 0);
32
if( z!=0 ){
33
blob_appendf(pExtra, " %s", z);
34
}
35
}
36
static void collect_argument_value(
37
Blob *pExtra, const char *zArg, const char *zShort
38
){
39
const char *zValue = find_option(zArg, zShort, 1);
40
if( zValue ){
41
if( zValue[0] ){
42
blob_appendf(pExtra, " --%s %$", zArg, zValue);
43
}else{
44
blob_appendf(pExtra, " --%s \"\"", zArg);
45
}
46
}
47
}
48
static void collect_argv(Blob *pExtra, int iStart){
49
int i;
50
for(i=iStart; i<g.argc; i++){
51
blob_appendf(pExtra, " %s", g.argv[i]);
52
}
53
}
54
55
/*
56
** COMMAND: all abbrv-subcom
57
**
58
** Usage: %fossil all SUBCOMMAND ...
59
**
60
** The ~/.fossil file records the location of all repositories for a
61
** user. This command performs certain operations on all repositories
62
** that can be useful before or after a period of disconnected operation.
63
**
64
** On Win32 systems, the file is named "_fossil" and is located in
65
** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%.
66
**
67
** Available operations are:
68
**
69
** backup Backup all repositories. The argument must be the name of
70
** a directory into which all backup repositories are written.
71
**
72
** cache Manages the cache used for potentially expensive web
73
** pages. Any additional arguments are passed on verbatim
74
** to the cache command.
75
**
76
** changes Shows all local check-outs that have uncommitted changes.
77
** This operation has no additional options.
78
**
79
** clean Delete all "extra" files in all local check-outs. Extreme
80
** caution should be exercised with this command because its
81
** effects cannot be undone. Use of the --dry-run option to
82
** carefully review the local check-outs to be operated upon
83
** and the --whatif option to carefully review the files to
84
** be deleted beforehand is highly recommended. The command
85
** line options supported by the clean command itself, if any
86
** are present, are passed along verbatim.
87
**
88
** config Only the "config pull AREA" command works.
89
**
90
** dbstat Run the "dbstat" command on all repositories.
91
**
92
** extras Shows "extra" files from all local check-outs. The command
93
** line options supported by the extra command itself, if any
94
** are present, are passed along verbatim.
95
**
96
** fts-config Run the "fts-config" command on all repositories.
97
**
98
** git CMD Do the "git export" or "git status" command (whichever
99
** is specified by CMD) on all repositories for which
100
** a Git mirror has been previously established.
101
**
102
** info Run the "info" command on all repositories.
103
**
104
** pull Run a "pull" operation on all repositories. Only the
105
** --verbose and --share-links options are supported.
106
**
107
** push Run a "push" on all repositories. Only the --verbose
108
** option is supported.
109
**
110
** rebuild Rebuild on all repositories. The command line options
111
** supported by the rebuild command itself, if any are
112
** present, are passed along verbatim. The --force option
113
** is not supported.
114
**
115
** remote Show remote hosts for all repositories.
116
**
117
** repack Look for extra compression in all repositories.
118
**
119
** sync Run a "sync" on all repositories. Only the --verbose
120
** and --unversioned and --share-links options are supported.
121
**
122
** set[tings] Run the "settings" command on all repositories.
123
** This command is useful for settings like "max-loadavg" which
124
** you usually want to be the same across all repositories
125
** on a server.
126
**
127
** unset Run the "unset" command on all repositories
128
**
129
** server Run the "server" commands on all repositories.
130
** The root URI gives a listing of all repos.
131
**
132
** ui Run the "ui" command on all repositories. Like "server"
133
** but bind to the loopback TCP address only, enable
134
** the --localauth option and automatically launch a
135
** web-browser
136
**
137
** whatis Run the "whatis" command on all repositories. Only
138
** show output for repositories that have a match.
139
**
140
**
141
** In addition, the following maintenance operations are supported:
142
**
143
** add Add all the repositories named to the set of repositories
144
** tracked by Fossil. Normally Fossil is able to keep up with
145
** this list by itself, but sometimes it can benefit from this
146
** hint if you rename repositories.
147
**
148
** ignore Arguments are repositories that should be ignored by
149
** subsequent clean, extras, list, pull, push, rebuild, and
150
** sync operations. The -c|--ckout option causes the listed
151
** local check-outs to be ignored instead.
152
**
153
** list | ls Display the location of all repositories. The -c|--ckout
154
** option causes all local check-outs to be listed instead.
155
**
156
** Repositories are automatically added to the set of known repositories
157
** when one of the following commands are run against the repository:
158
** clone, info, pull, push, or sync. Even previously ignored repositories
159
** are added back to the list of repositories by these commands.
160
**
161
** Options:
162
** --dry-run Just display commands that would have run
163
** --showfile Show the repository or check-out being operated upon
164
** --stop-on-error Halt immediately if any subprocess fails
165
** -s|--stop Shorthand for "--stop-on-error"
166
*/
167
void all_cmd(void){
168
Stmt q;
169
const char *zCmd;
170
char *zSyscmd = 0;
171
Blob extra;
172
int bHalted = 0;
173
int rc = 0;
174
int useCheckouts = 0;
175
int quiet = 0;
176
int dryRunFlag = 0;
177
int showFile = find_option("showfile",0,0)!=0;
178
int stopOnError;
179
int nToDel = 0;
180
int showLabel = 0;
181
182
(void)find_option("dontstop",0,0); /* Legacy. Now the default */
183
stopOnError = find_option("stop-on-error",0,0)!=0;
184
if( find_option("stop","s",0)!=0 ) stopOnError = 1;
185
dryRunFlag = find_option("dry-run","n",0)!=0;
186
if( !dryRunFlag ){
187
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
188
}
189
190
if( g.argc<3 ){
191
usage("SUBCOMMAND ...");
192
}
193
db_open_config(1, 0);
194
blob_zero(&extra);
195
zCmd = g.argv[2];
196
if( !login_is_nobody() ) blob_appendf(&extra, " -U %s", g.zLogin);
197
if( fossil_strcmp(zCmd, "ui")==0
198
|| fossil_strcmp(zCmd, "server")==0 ){
199
g.argv[1] = g.argv[2];
200
g.argv[2] = "/";
201
cmd_webserver();
202
return;
203
}
204
if( fossil_strcmp(zCmd, "list")==0 || fossil_strcmp(zCmd,"ls")==0 ){
205
zCmd = "list";
206
useCheckouts = find_option("ckout","c",0)!=0;
207
}else if( fossil_strcmp(zCmd, "backup")==0 ){
208
char *zDest;
209
zCmd = "backup -R";
210
collect_argument(&extra, "overwrite",0);
211
if( g.argc!=4 ) usage("backup DIRECTORY");
212
zDest = g.argv[3];
213
if( file_isdir(zDest, ExtFILE)!=1 ){
214
fossil_fatal("argument to \"fossil all backup\" must be a directory");
215
}
216
blob_appendf(&extra, " %$", zDest);
217
}else if( fossil_strcmp(zCmd, "clean")==0 ){
218
zCmd = "clean --chdir";
219
collect_argument(&extra, "allckouts",0);
220
collect_argument_value(&extra, "case-sensitive", 0);
221
collect_argument_value(&extra, "clean", 0);
222
collect_argument(&extra, "dirsonly",0);
223
collect_argument(&extra, "disable-undo",0);
224
collect_argument(&extra, "dotfiles",0);
225
collect_argument(&extra, "emptydirs",0);
226
collect_argument(&extra, "force","f");
227
collect_argument_value(&extra, "ignore", 0);
228
collect_argument_value(&extra, "keep", 0);
229
collect_argument(&extra, "no-prompt",0);
230
collect_argument(&extra, "temp",0);
231
collect_argument(&extra, "verbose","v");
232
collect_argument(&extra, "whatif",0);
233
useCheckouts = 1;
234
}else if( fossil_strcmp(zCmd, "config")==0 ){
235
zCmd = "config -R";
236
collect_argv(&extra, 3);
237
(void)find_option("legacy",0,0);
238
(void)find_option("overwrite",0,0);
239
verify_all_options();
240
if( g.argc!=5 || fossil_strcmp(g.argv[3],"pull")!=0 ){
241
usage("configure pull AREA ?OPTIONS?");
242
}
243
}else if( fossil_strcmp(zCmd, "dbstat")==0 ){
244
zCmd = "dbstat --omit-version-info -R";
245
showLabel = 1;
246
quiet = 1;
247
collect_argument(&extra, "brief", "b");
248
collect_argument(&extra, "db-check", 0);
249
collect_argument(&extra, "db-verify", 0);
250
}else if( fossil_strcmp(zCmd, "extras")==0 ){
251
if( showFile ){
252
zCmd = "extras --chdir";
253
}else{
254
zCmd = "extras --header --chdir";
255
}
256
collect_argument(&extra, "abs-paths",0);
257
collect_argument_value(&extra, "case-sensitive", 0);
258
collect_argument(&extra, "dotfiles",0);
259
collect_argument_value(&extra, "ignore", 0);
260
collect_argument(&extra, "rel-paths",0);
261
useCheckouts = 1;
262
stopOnError = 0;
263
quiet = 1;
264
}else if( fossil_strcmp(zCmd, "git")==0 ){
265
if( g.argc<4 ){
266
usage("git (export|status)");
267
}else{
268
if( fossil_strcmp(g.argv[3], "export")==0 ){
269
zCmd = "git export --if-mirrored -R";
270
}else if( fossil_strcmp(g.argv[3], "status")==0 ){
271
zCmd = "git status --by-all -q -R";
272
quiet = 1;
273
}else{
274
usage("git (export|status)");
275
}
276
}
277
}else if( fossil_strcmp(zCmd, "push")==0 ){
278
zCmd = "push -autourl -R";
279
collect_argument(&extra, "verbose","v");
280
}else if( fossil_strcmp(zCmd, "pull")==0 ){
281
zCmd = "pull -autourl -R";
282
collect_argument(&extra, "verbose","v");
283
collect_argument(&extra, "share-links",0);
284
}else if( fossil_strcmp(zCmd, "rebuild")==0 ){
285
zCmd = "rebuild";
286
collect_argument(&extra, "analyze",0);
287
collect_argument(&extra, "cluster",0);
288
collect_argument(&extra, "compress",0);
289
collect_argument(&extra, "compress-only",0);
290
collect_argument(&extra, "noverify",0);
291
collect_argument_value(&extra, "pagesize", 0);
292
collect_argument(&extra, "vacuum",0);
293
collect_argument(&extra, "deanalyze",0); /* Deprecated */
294
collect_argument(&extra, "analyze",0);
295
collect_argument(&extra, "wal",0);
296
collect_argument(&extra, "stats",0);
297
collect_argument(&extra, "index",0);
298
collect_argument(&extra, "noindex",0);
299
collect_argument(&extra, "ifneeded", 0);
300
}else if( fossil_strcmp(zCmd, "remote")==0 ){
301
showLabel = 1;
302
quiet = 1;
303
collect_argument(&extra, "show-passwords", 0);
304
if( g.argc==3 ){
305
zCmd = "remote -R";
306
}else if( g.argc!=4 ){
307
usage("remote ?config-data|list|ls?");
308
}else if( fossil_strcmp(g.argv[3],"ls")==0
309
|| fossil_strcmp(g.argv[3],"list")==0 ){
310
zCmd = "remote ls -R";
311
}else if( fossil_strcmp(g.argv[3],"ls")==0
312
|| fossil_strcmp(g.argv[3],"list")==0 ){
313
zCmd = "remote ls -R";
314
}else if( fossil_strcmp(g.argv[3],"config-data")==0 ){
315
zCmd = "remote config-data -R";
316
}else{
317
usage("remote ?config-data|list|ls?");
318
}
319
}else if( fossil_strcmp(zCmd, "repack")==0 ){
320
zCmd = "repack";
321
}else if( fossil_strcmp(zCmd, "set")==0
322
|| fossil_strcmp(zCmd, "setting")==0
323
|| fossil_strcmp(zCmd, "settings")==0 ){
324
zCmd = "settings -R";
325
collect_argument(&extra, "changed", 0);
326
collect_argv(&extra, 3);
327
}else if( fossil_strcmp(zCmd, "unset")==0 ){
328
zCmd = "unset -R";
329
collect_argv(&extra, 3);
330
}else if( fossil_strcmp(zCmd, "fts-config")==0 ){
331
zCmd = "fts-config -R";
332
collect_argv(&extra, 3);
333
}else if( fossil_strcmp(zCmd, "sync")==0 ){
334
zCmd = "sync -autourl -R";
335
collect_argument(&extra, "share-links",0);
336
collect_argument(&extra, "verbose","v");
337
collect_argument(&extra, "unversioned","u");
338
collect_argument(&extra, "all",0);
339
collect_argument(&extra, "quiet","q");
340
collect_argument(&extra, "ping",0);
341
}else if( fossil_strcmp(zCmd, "test-integrity")==0 ){
342
collect_argument(&extra, "db-only", "d");
343
collect_argument(&extra, "parse", 0);
344
collect_argument(&extra, "quick", "q");
345
zCmd = "test-integrity";
346
}else if( fossil_strcmp(zCmd, "test-orphans")==0 ){
347
zCmd = "test-orphans -R";
348
}else if( fossil_strcmp(zCmd, "test-missing")==0 ){
349
zCmd = "test-missing -q -R";
350
collect_argument(&extra, "notshunned",0);
351
}else if( fossil_strcmp(zCmd, "changes")==0 ){
352
zCmd = "changes --quiet --header --chdir";
353
useCheckouts = 1;
354
stopOnError = 0;
355
quiet = 1;
356
}else if( fossil_strcmp(zCmd, "ignore")==0 ){
357
int j;
358
Blob fn = BLOB_INITIALIZER;
359
Blob sql = BLOB_INITIALIZER;
360
useCheckouts = find_option("ckout","c",0)!=0;
361
verify_all_options();
362
db_begin_transaction();
363
for(j=3; j<g.argc; j++, blob_reset(&sql), blob_reset(&fn)){
364
file_canonical_name(g.argv[j], &fn, useCheckouts?1:0);
365
blob_append_sql(&sql,
366
"DELETE FROM global_config WHERE name GLOB '%s:%q'",
367
useCheckouts?"ckout":"repo", blob_str(&fn)
368
);
369
if( dryRunFlag ){
370
fossil_print("%s\n", blob_sql_text(&sql));
371
}else{
372
db_unprotect(PROTECT_CONFIG);
373
db_multi_exec("%s", blob_sql_text(&sql));
374
db_protect_pop();
375
}
376
}
377
db_end_transaction(0);
378
blob_reset(&sql);
379
blob_reset(&fn);
380
blob_reset(&extra);
381
return;
382
}else if( fossil_strcmp(zCmd, "add")==0 ){
383
int j;
384
Blob fn = BLOB_INITIALIZER;
385
Blob sql = BLOB_INITIALIZER;
386
verify_all_options();
387
db_begin_transaction();
388
for(j=3; j<g.argc; j++, blob_reset(&fn), blob_reset(&sql)){
389
sqlite3 *db;
390
int rc;
391
const char *z;
392
file_canonical_name(g.argv[j], &fn, 0);
393
z = blob_str(&fn);
394
if( !file_isfile(z, ExtFILE) ) continue;
395
g.dbIgnoreErrors++;
396
rc = sqlite3_open(z, &db);
397
if( rc!=SQLITE_OK ){ sqlite3_close(db); g.dbIgnoreErrors--; continue; }
398
rc = sqlite3_exec(db, "SELECT rcvid FROM blob, delta LIMIT 1", 0, 0, 0);
399
sqlite3_close(db);
400
g.dbIgnoreErrors--;
401
if( rc!=SQLITE_OK ) continue;
402
blob_append_sql(&sql,
403
"INSERT OR IGNORE INTO global_config(name,value)"
404
"VALUES('repo:%q',1)", z
405
);
406
if( dryRunFlag ){
407
fossil_print("%s\n", blob_sql_text(&sql));
408
}else{
409
db_unprotect(PROTECT_CONFIG);
410
db_multi_exec("%s", blob_sql_text(&sql));
411
db_protect_pop();
412
}
413
}
414
db_end_transaction(0);
415
blob_reset(&sql);
416
blob_reset(&fn);
417
blob_reset(&extra);
418
return;
419
}else if( fossil_strcmp(zCmd, "info")==0 ){
420
zCmd = "info";
421
showLabel = 1;
422
quiet = 1;
423
}else if( fossil_strcmp(zCmd, "cache")==0 ){
424
zCmd = "cache -R";
425
showLabel = 1;
426
collect_argv(&extra, 3);
427
}else if( fossil_strcmp(zCmd, "whatis")==0 ){
428
zCmd = "whatis -q -R";
429
quiet = 1;
430
collect_argument(&extra, "file", "f");
431
collect_argument_value(&extra, "type", 0);
432
collect_argv(&extra, 3);
433
}else{
434
fossil_fatal("\"all\" subcommand should be one of: "
435
"add cache changes clean dbstat extras fts-config git ignore "
436
"info list ls pull push rebuild remote "
437
"server settings sync ui unset whatis");
438
}
439
verify_all_options();
440
db_multi_exec(
441
"CREATE TEMP TABLE repolist(\n"
442
" name TEXT, -- Filename\n"
443
" tag TEXT, -- Key for the GLOBAL_CONFIG table entry\n"
444
" inode TEXT -- Unique identifier for this file\n"
445
");\n"
446
447
/* The seenFile() table holds inode names for entries that have
448
** already been processed. */
449
"CREATE TEMP TABLE seenFile(x TEXT COLLATE nocase);\n"
450
451
/* The toDel() table holds the "tag" for entries that need to be
452
** deleted because they are redundant or no longer exist */
453
"CREATE TEMP TABLE toDel(x TEXT);\n"
454
);
455
sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
456
file_inode_sql_func, 0, 0);
457
if( useCheckouts ){
458
db_multi_exec(
459
"INSERT INTO repolist "
460
"SELECT substr(name, 7), name, inode(substr(name,7))"
461
" FROM global_config"
462
" WHERE substr(name, 1, 6)=='ckout:'"
463
" ORDER BY 1"
464
);
465
}else{
466
db_multi_exec(
467
"INSERT INTO repolist "
468
"SELECT substr(name, 6), name, inode(substr(name,6))"
469
" FROM global_config"
470
" WHERE substr(name, 1, 5)=='repo:'"
471
" ORDER BY 1"
472
);
473
}
474
db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1");
475
while( db_step(&q)==SQLITE_ROW ){
476
const char *zFilename = db_column_text(&q, 0);
477
const char *zInode = db_column_text(&q,2);
478
#if !USE_SEE
479
if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue;
480
#endif
481
if( file_access(zFilename, F_OK)
482
|| !file_is_canonical(zFilename)
483
|| (useCheckouts && file_isdir(zFilename, ExtFILE)!=1)
484
|| db_exists("SELECT 1 FROM temp.seenFile where x=%Q", zInode)
485
){
486
db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1));
487
nToDel++;
488
continue;
489
}
490
db_multi_exec("INSERT INTO seenFile(x) VALUES(%Q)", zInode);
491
if( zCmd[0]=='l' ){
492
fossil_print("%s\n", zFilename);
493
continue;
494
}else if( showFile ){
495
fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository",
496
zFilename);
497
}
498
zSyscmd = mprintf("%$ %s %$%s",
499
g.nameOfExe, zCmd, zFilename, blob_str(&extra));
500
if( showLabel ){
501
int len = (int)strlen(zFilename);
502
int nStar = 80 - (len + 15);
503
if( nStar<2 ) nStar = 1;
504
fossil_print("%.13c %s %.*c\n", '*', zFilename, nStar, '*');
505
fflush(stdout);
506
}
507
if( !quiet || dryRunFlag ){
508
fossil_print("%s\n", zSyscmd);
509
fflush(stdout);
510
}
511
rc = dryRunFlag ? 0 : fossil_system(zSyscmd);
512
if( rc ){
513
if( stopOnError ){
514
bHalted = 1;
515
break;
516
}
517
/* If there is an error, pause briefly, but do not stop. The brief
518
** pause is so that if the prior command failed with Ctrl-C then there
519
** will be time to stop the whole thing with a second Ctrl-C. */
520
sqlite3_sleep(330);
521
}
522
fossil_free(zSyscmd);
523
}
524
db_finalize(&q);
525
526
blob_reset(&extra);
527
528
/* If any repositories whose names appear in the ~/.fossil file could not
529
** be found, remove those names from the ~/.fossil file.
530
*/
531
if( nToDel>0 ){
532
const char *zSql = "DELETE FROM global_config WHERE name IN toDel";
533
if( dryRunFlag ){
534
fossil_print("%s\n", zSql);
535
}else{
536
db_unprotect(PROTECT_CONFIG);
537
db_multi_exec("%s", zSql /*safe-for-%s*/ );
538
db_protect_pop();
539
}
540
}
541
542
if( stopOnError && bHalted ){
543
fossil_fatal("STOPPED: non-zero result code (%d) from\nSTOPPED: %s",
544
rc, zSyscmd);
545
}
546
}
547

Keyboard Shortcuts

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