Fossil SCM

fossil-scm / src / checkin.c
Blame History Raw 3329 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-in versions of the project
19
** from the local repository.
20
*/
21
#include "config.h"
22
#include "checkin.h"
23
#include <assert.h>
24
25
/*
26
** Change filter options.
27
*/
28
enum {
29
/* Zero-based bit indexes. */
30
CB_EDITED , CB_UPDATED , CB_CHANGED, CB_MISSING , CB_ADDED, CB_DELETED,
31
CB_RENAMED, CB_CONFLICT, CB_META , CB_UNCHANGED, CB_EXTRA, CB_MERGE ,
32
CB_RELPATH, CB_CLASSIFY, CB_MTIME , CB_SIZE , CB_FATAL, CB_COMMENT,
33
34
/* Bitmask values. */
35
C_EDITED = 1 << CB_EDITED, /* Edited, merged, and conflicted files. */
36
C_UPDATED = 1 << CB_UPDATED, /* Files updated by merge/integrate. */
37
C_CHANGED = 1 << CB_CHANGED, /* Treated the same as the above two. */
38
C_MISSING = 1 << CB_MISSING, /* Missing and non- files. */
39
C_ADDED = 1 << CB_ADDED, /* Added files. */
40
C_DELETED = 1 << CB_DELETED, /* Deleted files. */
41
C_RENAMED = 1 << CB_RENAMED, /* Renamed files. */
42
C_CONFLICT = 1 << CB_CONFLICT, /* Files having merge conflicts. */
43
C_META = 1 << CB_META, /* Files with metadata changes. */
44
C_UNCHANGED = 1 << CB_UNCHANGED, /* Unchanged files. */
45
C_EXTRA = 1 << CB_EXTRA, /* Unmanaged files. */
46
C_MERGE = 1 << CB_MERGE, /* Merge contributors. */
47
C_FILTER = C_EDITED | C_UPDATED | C_CHANGED | C_MISSING | C_ADDED
48
| C_DELETED | C_RENAMED | C_CONFLICT | C_META | C_UNCHANGED
49
| C_EXTRA | C_MERGE, /* All filter bits. */
50
C_ALL = C_FILTER & ~(C_EXTRA | C_MERGE),/* All managed files. */
51
C_DIFFER = C_FILTER & ~(C_UNCHANGED | C_MERGE),/* All differences. */
52
C_RELPATH = 1 << CB_RELPATH, /* Show relative paths. */
53
C_CLASSIFY = 1 << CB_CLASSIFY, /* Show file change types. */
54
C_DEFAULT = (C_ALL & ~C_UNCHANGED) | C_MERGE | C_CLASSIFY,
55
C_MTIME = 1 << CB_MTIME, /* Show file modification time. */
56
C_SIZE = 1 << CB_SIZE, /* Show file size in bytes. */
57
C_FATAL = 1 << CB_FATAL, /* Fail on MISSING/NOT_A_FILE. */
58
C_COMMENT = 1 << CB_COMMENT, /* Precede each line with "# ". */
59
};
60
61
/*
62
** Create a TEMP table named SFILE and add all unmanaged files named on
63
** the command-line to that table. If directories are named, then add
64
** all unmanaged files contained underneath those directories. If there
65
** are no files or directories named on the command-line, then add all
66
** unmanaged files anywhere in the check-out.
67
**
68
** This routine never follows symlinks. It always treats symlinks as
69
** object unto themselves.
70
*/
71
static void locate_unmanaged_files(
72
int argc, /* Number of command-line arguments to examine */
73
char **argv, /* values of command-line arguments */
74
unsigned scanFlags, /* Zero or more SCAN_xxx flags */
75
Glob *pIgnore /* Do not add files that match this GLOB */
76
){
77
Blob name; /* Name of a candidate file or directory */
78
char *zName; /* Name of a candidate file or directory */
79
int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */
80
int i; /* Loop counter */
81
int nRoot; /* length of g.zLocalRoot */
82
83
db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s,"
84
" mtime INTEGER, size INTEGER)", filename_collation());
85
nRoot = (int)strlen(g.zLocalRoot);
86
if( argc==0 ){
87
blob_init(&name, g.zLocalRoot, nRoot - 1);
88
vfile_scan(&name, blob_size(&name), scanFlags, pIgnore, 0, SymFILE);
89
blob_reset(&name);
90
}else{
91
for(i=0; i<argc; i++){
92
file_canonical_name(argv[i], &name, 0);
93
zName = blob_str(&name);
94
isDir = file_isdir(zName, SymFILE);
95
if( isDir==1 ){
96
vfile_scan(&name, nRoot-1, scanFlags, pIgnore, 0, SymFILE);
97
}else if( isDir==0 ){
98
fossil_warning("not found: %s", &zName[nRoot]);
99
}else if( file_access(zName, R_OK) ){
100
fossil_fatal("cannot open %s", &zName[nRoot]);
101
}else{
102
/* Only add unmanaged file paths specified on the command line. */
103
db_multi_exec(
104
"INSERT OR IGNORE INTO sfile(pathname)"
105
" SELECT %Q WHERE NOT EXISTS"
106
" (SELECT 1 FROM vfile WHERE pathname=%Q)",
107
&zName[nRoot], &zName[nRoot]
108
);
109
}
110
blob_reset(&name);
111
}
112
}
113
}
114
115
/*
116
** Generate text describing all changes.
117
**
118
** We assume that vfile_check_signature has been run.
119
*/
120
static void status_report(
121
Blob *report, /* Append the status report here */
122
unsigned flags /* Filter and other configuration flags */
123
){
124
Stmt q;
125
int nErr = 0;
126
Blob rewrittenOrigName, rewrittenPathname;
127
Blob sql = BLOB_INITIALIZER, where = BLOB_INITIALIZER;
128
const char *zName;
129
int i;
130
131
/* Skip the file report if no files are requested at all. */
132
if( !(flags & (C_ALL | C_EXTRA)) ){
133
goto skipFiles;
134
}
135
136
/* Assemble the path-limiting WHERE clause, if any. */
137
blob_zero(&where);
138
for(i=2; i<g.argc; i++){
139
Blob fname;
140
file_tree_name(g.argv[i], &fname, 0, 1);
141
zName = blob_str(&fname);
142
if( fossil_strcmp(zName, ".")==0 ){
143
blob_reset(&where);
144
break;
145
}
146
blob_append_sql(&where,
147
" %s (pathname=%Q %s) "
148
"OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
149
(blob_size(&where)>0) ? "OR" : "AND", zName,
150
filename_collation(), zName, filename_collation(),
151
zName, filename_collation()
152
);
153
}
154
155
/* Obtain the list of managed files if appropriate. */
156
blob_zero(&sql);
157
if( flags & C_ALL ){
158
/* Start with a list of all managed files. */
159
blob_append_sql(&sql,
160
"SELECT pathname, %s as mtime, %s as size, deleted, chnged, rid,"
161
" coalesce(origname!=pathname,0) AS renamed, 1 AS managed,"
162
" origname"
163
" FROM vfile LEFT JOIN blob USING (rid)"
164
" WHERE is_selected(id)%s",
165
flags & C_MTIME ? "datetime(checkin_mtime(:vid, rid), "
166
"'unixepoch', toLocal())" : "''" /*safe-for-%s*/,
167
flags & C_SIZE ? "coalesce(blob.size, 0)" : "0" /*safe-for-%s*/,
168
blob_sql_text(&where));
169
170
/* Exclude unchanged files unless requested. */
171
if( !(flags & C_UNCHANGED) ){
172
blob_append_sql(&sql,
173
" AND (chnged OR deleted OR rid=0 OR pathname!=origname)");
174
}
175
}
176
177
/* If C_EXTRA, add unmanaged files to the query result too. */
178
if( flags & C_EXTRA ){
179
if( blob_size(&sql) ){
180
blob_append_sql(&sql, " UNION ALL");
181
}
182
blob_append_sql(&sql,
183
" SELECT pathname, %s, %s, 0, 0, 0, 0, 0, NULL"
184
" FROM sfile WHERE pathname NOT IN (%s)%s",
185
flags & C_MTIME ? "datetime(mtime, 'unixepoch', toLocal())" : "''",
186
flags & C_SIZE ? "size" : "0",
187
fossil_all_reserved_names(0), blob_sql_text(&where));
188
}
189
blob_reset(&where);
190
191
/* Pre-create the "ok" temporary table so the checkin_mtime() SQL function
192
* does not lead to SQLITE_ABORT_ROLLBACK during execution of the OP_OpenRead
193
* SQLite opcode. checkin_mtime() calls mtime_of_manifest_file() which
194
* creates a temporary table if it doesn't already exist, thus invalidating
195
* the prepared statement in the middle of its execution. */
196
db_multi_exec("CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)");
197
198
/* Append an ORDER BY clause then compile the query. */
199
blob_append_sql(&sql, " ORDER BY pathname");
200
db_prepare(&q, "%s", blob_sql_text(&sql));
201
blob_reset(&sql);
202
203
/* Bind the check-out version ID to the query if needed. */
204
if( (flags & C_ALL) && (flags & C_MTIME) ){
205
db_bind_int(&q, ":vid", db_lget_int("checkout", 0));
206
}
207
208
/* Execute the query and assemble the report. */
209
blob_zero(&rewrittenPathname);
210
blob_zero(&rewrittenOrigName);
211
while( db_step(&q)==SQLITE_ROW ){
212
const char *zPathname = db_column_text(&q, 0);
213
const char *zClass = 0;
214
int isManaged = db_column_int(&q, 7);
215
const char *zMtime = db_column_text(&q, 1);
216
int size = db_column_int(&q, 2);
217
int isDeleted = db_column_int(&q, 3);
218
int isChnged = db_column_int(&q, 4);
219
int isNew = isManaged && !db_column_int(&q, 5);
220
int isRenamed = db_column_int(&q, 6);
221
const char *zOrigName = 0;
222
char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
223
int isMissing = !file_isfile_or_link(zFullName);
224
225
/* Determine the file change classification, if any. */
226
if( isDeleted ){
227
if( flags & C_DELETED ){
228
zClass = "DELETED";
229
}
230
}else if( isMissing ){
231
if( file_access(zFullName, F_OK)==0 ){
232
if( flags & C_MISSING ){
233
zClass = "NOT_A_FILE";
234
}
235
if( flags & C_FATAL ){
236
fossil_warning("not a file: %s", zFullName);
237
nErr++;
238
}
239
}else{
240
if( flags & C_MISSING ){
241
zClass = "MISSING";
242
}
243
if( flags & C_FATAL ){
244
fossil_warning("missing file: %s", zFullName);
245
nErr++;
246
}
247
}
248
}else if( isNew ){
249
if( flags & C_ADDED ){
250
zClass = "ADDED";
251
}
252
}else if( (flags & (C_UPDATED | C_CHANGED)) && isChnged==2 ){
253
zClass = "UPDATED_BY_MERGE";
254
}else if( (flags & C_ADDED) && isChnged==3 ){
255
zClass = "ADDED_BY_MERGE";
256
}else if( (flags & (C_UPDATED | C_CHANGED)) && isChnged==4 ){
257
zClass = "UPDATED_BY_INTEGRATE";
258
}else if( (flags & C_ADDED) && isChnged==5 ){
259
zClass = "ADDED_BY_INTEGRATE";
260
}else if( (flags & C_META) && isChnged==6 ){
261
zClass = "EXECUTABLE";
262
}else if( (flags & C_META) && isChnged==7 ){
263
zClass = "SYMLINK";
264
}else if( (flags & C_META) && isChnged==8 ){
265
zClass = "UNEXEC";
266
}else if( (flags & C_META) && isChnged==9 ){
267
zClass = "UNLINK";
268
}else if( (flags & C_CONFLICT) && isChnged && !file_islink(zFullName)
269
&& file_contains_merge_marker(zFullName) ){
270
zClass = "CONFLICT";
271
}else if( (flags & (C_EDITED | C_CHANGED)) && isChnged
272
&& (isChnged<2 || isChnged>9) ){
273
zClass = "EDITED";
274
}else if( (flags & C_UNCHANGED) && isManaged && !isNew
275
&& !isChnged && !isRenamed ){
276
zClass = "UNCHANGED";
277
}else if( (flags & C_EXTRA) && !isManaged ){
278
zClass = "EXTRA";
279
}
280
if( (flags & C_RENAMED) && isRenamed ){
281
zOrigName = db_column_text(&q,8);
282
if( zClass==0 ){
283
zClass = "RENAMED";
284
}
285
}
286
287
/* Only report files for which a change classification was determined. */
288
if( zClass ){
289
if( flags & C_COMMENT ){
290
blob_append(report, "# ", 2);
291
}
292
if( flags & C_CLASSIFY ){
293
blob_appendf(report, "%-10s ", zClass);
294
}
295
if( flags & C_MTIME ){
296
blob_append(report, zMtime, -1);
297
blob_append(report, " ", 2);
298
}
299
if( flags & C_SIZE ){
300
blob_appendf(report, "%7d ", size);
301
}
302
if( flags & C_RELPATH ){
303
/* If C_RELPATH, display paths relative to current directory. */
304
file_relative_name(zFullName, &rewrittenPathname, 0);
305
zPathname = blob_str(&rewrittenPathname);
306
if( zPathname[0]=='.' && zPathname[1]=='/' ){
307
zPathname += 2; /* no unnecessary ./ prefix */
308
}
309
if( (flags & (C_FILTER ^ C_RENAMED)) && zOrigName ){
310
char *zOrigFullName = mprintf("%s%s", g.zLocalRoot, zOrigName);
311
file_relative_name(zOrigFullName, &rewrittenOrigName, 0);
312
zOrigName = blob_str(&rewrittenOrigName);
313
fossil_free(zOrigFullName);
314
if( zOrigName[0]=='.' && zOrigName[1]=='/' ){
315
zOrigName += 2; /* no unnecessary ./ prefix */
316
}
317
}
318
}
319
if( (flags & (C_FILTER ^ C_RENAMED)) && zOrigName ){
320
blob_appendf(report, "%s -> ", zOrigName);
321
}
322
blob_appendf(report, "%s\n", zPathname);
323
}
324
free(zFullName);
325
}
326
blob_reset(&rewrittenPathname);
327
blob_reset(&rewrittenOrigName);
328
db_finalize(&q);
329
330
/* If C_MERGE, put merge contributors at the end of the report. */
331
skipFiles:
332
if( flags & C_MERGE ){
333
db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0" );
334
while( db_step(&q)==SQLITE_ROW ){
335
if( flags & C_COMMENT ){
336
blob_append(report, "# ", 2);
337
}
338
if( flags & C_CLASSIFY ){
339
const char *zClass;
340
switch( db_column_int(&q, 1) ){
341
case -1: zClass = "CHERRYPICK" ; break;
342
case -2: zClass = "BACKOUT" ; break;
343
case -4: zClass = "INTEGRATE" ; break;
344
default: zClass = "MERGED_WITH"; break;
345
}
346
blob_appendf(report, "%-10s ", zClass);
347
}
348
blob_append(report, db_column_text(&q, 0), -1);
349
blob_append(report, "\n", 1);
350
}
351
db_finalize(&q);
352
}
353
if( nErr ){
354
fossil_fatal("aborting due to prior errors");
355
}
356
}
357
358
/*
359
** Use the "relative-paths" setting and the --abs-paths and
360
** --rel-paths command line options to determine whether the
361
** status report should be shown relative to the current
362
** working directory.
363
*/
364
static int determine_cwd_relative_option()
365
{
366
int relativePaths = db_get_boolean("relative-paths", 1);
367
int absPathOption = find_option("abs-paths", 0, 0)!=0;
368
int relPathOption = find_option("rel-paths", 0, 0)!=0;
369
if( absPathOption ){ relativePaths = 0; }
370
if( relPathOption ){ relativePaths = 1; }
371
return relativePaths;
372
}
373
374
/*
375
** COMMAND: changes
376
** COMMAND: status
377
**
378
** Usage: %fossil changes|status ?OPTIONS? ?PATHS ...?
379
**
380
** Report the change status of files in the current check-out. If one or
381
** more PATHS are specified, only changes among the named files and
382
** directories are reported. Directories are searched recursively.
383
**
384
** The status command is similar to the changes command, except it lacks
385
** several of the options supported by changes and it has its own header
386
** and footer information. The header information is a subset of that
387
** shown by the info command, and the footer shows if there are any forks.
388
** Change type classification is always enabled for the status command.
389
**
390
** Each line of output is the name of a changed file, with paths shown
391
** according to the "relative-paths" setting, unless overridden by the
392
** --abs-paths or --rel-paths options.
393
**
394
** By default, all changed files are selected for display. This behavior
395
** can be overridden by using one or more filter options (listed below),
396
** in which case only files with the specified change type(s) are shown.
397
** As a special case, the --no-merge option does not inhibit this default.
398
** This default shows exactly the set of changes that would be checked-
399
** in by the commit command.
400
**
401
** If no filter options are used, or if the --merge option is used, the
402
** artifact hash of each merge contributor check-in version is displayed at
403
** the end of the report. The --no-merge option is useful to display the
404
** default set of changed files without the merge contributors.
405
**
406
** If change type classification is enabled, each output line starts with
407
** a code describing the file's change type, e.g. EDITED or RENAMED. It
408
** is enabled by default unless exactly one change type is selected. For
409
** the purposes of determining the default, --changed counts as selecting
410
** one change type. The default can be overridden by the --classify or
411
** --no-classify options.
412
**
413
** --edited and --updated produce disjoint sets. --updated shows a file
414
** only when it is identical to that of its merge contributor, and the
415
** change type classification is UPDATED_BY_MERGE or UPDATED_BY_INTEGRATE.
416
** If the file had to be merged with any other changes, it is considered
417
** to be merged or conflicted and therefore will be shown by --edited, not
418
** --updated, with types EDITED or CONFLICT. The --changed option can be
419
** used to display the union of --edited and --updated.
420
**
421
** --differ is so named because it lists all the differences between the
422
** checked-out version and the check-out directory. In addition to the
423
** default changes (excluding --merge), it lists extra files which (if
424
** ignore-glob is set correctly) may be worth adding. Prior to doing a
425
** commit, it is good practice to check --differ to see not only which
426
** changes would be committed but also if any files should be added.
427
**
428
** If both --merge and --no-merge are used, --no-merge has priority. The
429
** same is true of --classify and --no-classify.
430
**
431
** The "fossil changes --extra" command is equivalent to "fossil extras".
432
**
433
** General options:
434
** --abs-paths Display absolute pathnames
435
** -b|--brief Show a single keyword for the status
436
** --rel-paths Display pathnames relative to the current working
437
** directory
438
** --hash Verify file status using hashing rather than
439
** relying on file mtimes
440
** --case-sensitive BOOL Override case-sensitive setting
441
** --dotfiles Include unmanaged files beginning with a dot
442
** --ignore <CSG> Ignore unmanaged files matching CSG glob patterns
443
**
444
** Options specific to the changes command:
445
** --header Identify the repository if report is non-empty
446
** -v|--verbose Say "(none)" if the change report is empty
447
** --classify Start each line with the file's change type
448
** --no-classify Do not print file change types
449
**
450
** Filter options:
451
** --edited Display edited, merged, and conflicted files
452
** --updated Display files updated by merge/integrate
453
** --changed Combination of the above two options
454
** --missing Display missing files
455
** --added Display added files
456
** --deleted Display deleted files
457
** --renamed Display renamed files
458
** --conflict Display files having merge conflicts
459
** --meta Display files with metadata changes
460
** --unchanged Display unchanged files
461
** --all Display all managed files, i.e. all of the above
462
** --extra Display unmanaged files
463
** --differ Display modified and extra files
464
** --merge Display merge contributors
465
** --no-merge Do not display merge contributors
466
**
467
** See also: [[extras]], [[ls]]
468
*/
469
void status_cmd(void){
470
/* Affirmative and negative flag option tables. */
471
static const struct {
472
const char *option; /* Flag name. */
473
unsigned mask; /* Flag bits. */
474
} flagDefs[] = {
475
{"edited" , C_EDITED }, {"updated" , C_UPDATED },
476
{"changed" , C_CHANGED }, {"missing" , C_MISSING },
477
{"added" , C_ADDED }, {"deleted" , C_DELETED },
478
{"renamed" , C_RENAMED }, {"conflict" , C_CONFLICT },
479
{"meta" , C_META }, {"unchanged" , C_UNCHANGED},
480
{"all" , C_ALL }, {"extra" , C_EXTRA },
481
{"differ" , C_DIFFER }, {"merge" , C_MERGE },
482
{"classify", C_CLASSIFY},
483
}, noFlagDefs[] = {
484
{"no-merge", C_MERGE }, {"no-classify", C_CLASSIFY },
485
};
486
487
Blob report = BLOB_INITIALIZER;
488
enum {CHANGES, STATUS} command = *g.argv[1]=='s' ? STATUS : CHANGES;
489
/* --sha1sum is an undocumented alias for --hash for backwards compatibility */
490
int useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
491
int showHdr = command==CHANGES && find_option("header", 0, 0);
492
int verboseFlag = command==CHANGES && find_option("verbose", "v", 0);
493
const char *zIgnoreFlag = find_option("ignore", 0, 1);
494
unsigned scanFlags = 0;
495
unsigned flags = 0;
496
int vid, i;
497
498
fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown");
499
500
if( find_option("brief","b",0) ){
501
/* The --brief or -b option is special. It cannot be used with any
502
** other options. It outputs a single keyword which indicates the
503
** fossil status, for use by shell scripts. The output might be
504
** one of:
505
**
506
** clean The current working directory is within an
507
** unmodified fossil check-out.
508
**
509
** dirty The pwd is within a fossil check-out that has
510
** uncommitted changes
511
**
512
** none The pwd is not within a fossil check-out.
513
*/
514
int chnged;
515
if( g.argc>2 ){
516
fossil_fatal("No other arguments or options may occur with --brief");
517
}
518
if( db_open_local(0)==0 ){
519
fossil_print("none\n");
520
return;
521
}
522
vid = db_lget_int("checkout", 0);
523
vfile_check_signature(vid, 0);
524
chnged = db_int(0,
525
"SELECT 1 FROM vfile"
526
" WHERE vid=%d"
527
" AND (chnged>0 OR deleted OR rid==0)",
528
vid
529
);
530
if( chnged ){
531
fossil_print("dirty\n");
532
}else{
533
fossil_print("clean\n");
534
}
535
return;
536
}
537
538
/* Load affirmative flag options. */
539
for( i=0; i<count(flagDefs); ++i ){
540
if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
541
&& find_option(flagDefs[i].option, 0, 0) ){
542
flags |= flagDefs[i].mask;
543
}
544
}
545
546
/* If no filter options are specified, enable defaults. */
547
if( !(flags & C_FILTER) ){
548
flags |= C_DEFAULT;
549
}
550
551
/* If more than one filter is enabled, enable classification. This is tricky.
552
* Having one filter means flags masked by C_FILTER is a power of two. If a
553
* number masked by one less than itself is zero, it's either zero or a power
554
* of two. It's already known to not be zero because of the above defaults.
555
* Unlike --all, --changed is a single filter, i.e. it sets only one bit.
556
* Also force classification for the status command. */
557
if( command==STATUS || (flags & (flags-1) & C_FILTER) ){
558
flags |= C_CLASSIFY;
559
}
560
561
/* Negative flag options override defaults applied above. */
562
for( i=0; i<count(noFlagDefs); ++i ){
563
if( (command==CHANGES || !(noFlagDefs[i].mask & C_CLASSIFY))
564
&& find_option(noFlagDefs[i].option, 0, 0) ){
565
flags &= ~noFlagDefs[i].mask;
566
}
567
}
568
569
/* Confirm current working directory is within check-out. */
570
db_must_be_within_tree();
571
572
/* Get check-out version. */
573
vid = db_lget_int("checkout", 0);
574
575
/* Relative path flag determination is done by a shared function. */
576
if( determine_cwd_relative_option() ){
577
flags |= C_RELPATH;
578
}
579
580
/* If --ignore is not specified, use the ignore-glob setting. */
581
if( !zIgnoreFlag ){
582
zIgnoreFlag = db_get("ignore-glob", 0);
583
}
584
585
/* Get the --dotfiles argument, or read it from the dotfiles setting. */
586
if( find_option("dotfiles", 0, 0) || db_get_boolean("dotfiles", 0) ){
587
scanFlags = SCAN_ALL;
588
}
589
590
/* We should be done with options. */
591
verify_all_options();
592
593
/* Check for changed files. */
594
vfile_check_signature(vid, useHash ? CKSIG_HASH : 0);
595
596
/* Search for unmanaged files if requested. */
597
if( flags & C_EXTRA ){
598
Glob *pIgnore = glob_create(zIgnoreFlag);
599
locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
600
glob_free(pIgnore);
601
}
602
603
/* The status command prints general information before the change list. */
604
if( command==STATUS ){
605
fossil_print("repository: %s\n", db_repository_filename());
606
fossil_print("local-root: %s\n", g.zLocalRoot);
607
if( g.zConfigDbName ){
608
fossil_print("config-db: %s\n", g.zConfigDbName);
609
}
610
if( vid ){
611
show_common_info(vid, "checkout:", 1, 1);
612
}
613
db_record_repository_filename(0);
614
}
615
616
/* Find and print all requested changes. */
617
blob_zero(&report);
618
status_report(&report, flags);
619
if( blob_size(&report) ){
620
if( showHdr ){
621
fossil_print(
622
"Changes for %s at %s:\n", db_get("project-name", "<unnamed>"),
623
g.zLocalRoot);
624
}
625
blob_write_to_file(&report, "-");
626
}else if( verboseFlag ){
627
fossil_print(" (none)\n");
628
}
629
blob_reset(&report);
630
631
/* The status command ends with warnings about ambiguous leaves (forks). */
632
if( command==STATUS ){
633
leaf_ambiguity_warning(vid, vid);
634
}
635
}
636
637
/* zIn is a string that is guaranteed to be followed by \n. Return
638
** a pointer to the next line after the \n. The returned value might
639
** point to the \000 string terminator.
640
*/
641
static const char *next_line(const char *zIn){
642
const char *z = strchr(zIn, '\n');
643
assert( z!=0 );
644
return z+1;
645
}
646
647
/* zIn is a non-empty list of filenames in sorted order and separated
648
** by \n. There might be a cluster of lines that have the same n-character
649
** prefix. Return a pointer to the start of the last line of that
650
** cluster. The return value might be zIn if the first line of zIn is
651
** unique in its first n character.
652
*/
653
static const char *last_line(const char *zIn, int n){
654
const char *zLast = zIn;
655
const char *z;
656
while( 1 ){
657
z = next_line(zLast);
658
if( z[0]==0 || (n>0 && strncmp(zIn, z, n)!=0) ) break;
659
zLast = z;
660
}
661
return zLast;
662
}
663
664
/*
665
** Print a section of a filelist hierarchy graph. This is a helper
666
** routine for print_filelist_as_tree() below.
667
*/
668
static const char *print_filelist_section(
669
const char *zIn, /* List of filenames, separated by \n */
670
const char *zLast, /* Last filename in the list to print */
671
const char *zPrefix, /* Prefix so put before each output line */
672
int nDir /* Ignore this many characters of directory name */
673
){
674
/* Unicode box-drawing characters: U+251C, U+2514, U+2502 */
675
const char *zENTRY = "\342\224\234\342\224\200\342\224\200 ";
676
const char *zLASTE = "\342\224\224\342\224\200\342\224\200 ";
677
const char *zCONTU = "\342\224\202 ";
678
const char *zBLANK = " ";
679
680
while( zIn<=zLast ){
681
int i;
682
for(i=nDir; zIn[i]!='\n' && zIn[i]!='/'; i++){}
683
if( zIn[i]=='/' ){
684
char *zSubPrefix;
685
const char *zSubLast = last_line(zIn, i+1);
686
zSubPrefix = mprintf("%s%s", zPrefix, zSubLast==zLast ? zBLANK : zCONTU);
687
fossil_print("%s%s%.*s\n", zPrefix, zSubLast==zLast ? zLASTE : zENTRY,
688
i-nDir, &zIn[nDir]);
689
zIn = print_filelist_section(zIn, zSubLast, zSubPrefix, i+1);
690
fossil_free(zSubPrefix);
691
}else{
692
fossil_print("%s%s%.*s\n", zPrefix, zIn==zLast ? zLASTE : zENTRY,
693
i-nDir, &zIn[nDir]);
694
zIn = next_line(zIn);
695
}
696
}
697
return zIn;
698
}
699
700
/*
701
** Input blob pList is a list of filenames, one filename per line,
702
** in sorted order and with / directory separators. Output this list
703
** as a tree in a manner similar to the "tree" command on Linux.
704
*/
705
static void print_filelist_as_tree(Blob *pList){
706
char *zAll;
707
const char *zLast;
708
fossil_print("%s\n", g.zLocalRoot);
709
zAll = blob_str(pList);
710
if( zAll[0] ){
711
zLast = last_line(zAll, 0);
712
print_filelist_section(zAll, zLast, "", 0);
713
}
714
}
715
716
/*
717
** Take care of -r version of ls command
718
*/
719
void ls_cmd_rev(
720
const char *zRev, /* Revision string given */
721
int verboseFlag, /* Verbose flag given */
722
int showAge, /* Age flag given */
723
int showFileHash, /* Show file hash flag given */
724
int showCkinHash, /* Show check-in hash flag given */
725
int showCkinInfo, /* Show check-in infos */
726
int timeOrder, /* Order by time flag given */
727
int treeFmt /* Show output in the tree format */
728
){
729
Stmt q;
730
char *zOrderBy = "pathname COLLATE nocase";
731
char *zName;
732
Blob where;
733
int rid;
734
int i;
735
Blob out;
736
737
/* Handle given file names */
738
blob_zero(&where);
739
for(i=2; i<g.argc; i++){
740
Blob fname;
741
file_tree_name(g.argv[i], &fname, 0, 1);
742
zName = blob_str(&fname);
743
if( fossil_strcmp(zName, ".")==0 ){
744
blob_reset(&where);
745
break;
746
}
747
blob_append_sql(&where,
748
" %s (pathname=%Q %s) "
749
"OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
750
(blob_size(&where)>0) ? "OR" : "AND (", zName,
751
filename_collation(), zName, filename_collation(),
752
zName, filename_collation()
753
);
754
}
755
if( blob_size(&where)>0 ){
756
blob_append_sql(&where, ")");
757
}
758
759
rid = symbolic_name_to_rid(zRev, "ci");
760
if( rid==0 ){
761
fossil_fatal("not a valid check-in: %s", zRev);
762
}
763
764
if( timeOrder ){
765
zOrderBy = "mtime DESC";
766
}
767
768
compute_fileage(rid,0);
769
if( showCkinInfo ){
770
db_prepare(&q,
771
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
772
" bfh.size, fileage.uuid, bch.uuid,\n"
773
" coalesce(e.ecomment, e.comment), coalesce(e.euser, e.user)\n"
774
" FROM fileage, blob bfh, blob bch, event e\n"
775
" WHERE bfh.rid=fileage.fid AND bch.rid=fileage.mid\n"
776
" AND e.objid = fileage.mid %s\n"
777
" ORDER BY %s;",
778
blob_sql_text(&where),
779
zOrderBy /*safe-for-%s*/
780
);
781
}else{
782
db_prepare(&q,
783
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
784
" bfh.size, fileage.uuid %s\n"
785
" FROM fileage, blob bfh %s\n"
786
" WHERE bfh.rid=fileage.fid %s %s\n"
787
" ORDER BY %s;",
788
showCkinHash ? ", bch.uuid" : "",
789
showCkinHash ? ", blob bch" : "",
790
showCkinHash ? "\n AND bch.rid=fileage.mid" : "",
791
blob_sql_text(&where),
792
zOrderBy /*safe-for-%s*/
793
);
794
}
795
blob_reset(&where);
796
if( treeFmt ) blob_init(&out, 0, 0);
797
798
while( db_step(&q)==SQLITE_ROW ){
799
const char *zTime = db_column_text(&q,0);
800
const char *zFile = db_column_text(&q,1);
801
int size = db_column_int(&q,2);
802
if( treeFmt ){
803
blob_appendf(&out, "%s\n", zFile);
804
}else if( verboseFlag ){
805
if( showCkinInfo ){
806
const char *zUuidC = db_column_text(&q,4);
807
const char *zComm = db_column_text(&q,5);
808
const char *zUser = db_column_text(&q,6);
809
fossil_print("%s [%S] %12s ", zTime, zUuidC, zUser);
810
if( showCkinInfo==2 ) fossil_print("%-20.20s ", zComm);
811
fossil_print("%s\n", zFile);
812
}else if( showFileHash ){
813
const char *zUuidF = db_column_text(&q,3);
814
fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidF, zFile);
815
}else if( showCkinHash ){
816
const char *zUuidC = db_column_text(&q,4);
817
fossil_print("%s %7d [%S] %s\n", zTime, size, zUuidC, zFile);
818
}else{
819
fossil_print("%s %7d %s\n", zTime, size, zFile);
820
}
821
}else if( showAge ){
822
fossil_print("%s %s\n", zTime, zFile);
823
}else{
824
fossil_print("%s\n", zFile);
825
}
826
}
827
db_finalize(&q);
828
if( treeFmt ){
829
print_filelist_as_tree(&out);
830
blob_reset(&out);
831
}
832
}
833
834
/*
835
** COMMAND: ls
836
**
837
** Usage: %fossil ls ?OPTIONS? ?PATHS ...?
838
**
839
** List all files in the current check-out. If PATHS is included, only the
840
** named files (or their children if directories) are shown.
841
**
842
** The ls command has grown by accretion, with multiple contributors, over
843
** many years, and is hence a little confused. The ls command is essentially
844
** two related commands in one, depending on whether or not the -r option
845
** is given or implied. The -r option selects a specific check-in
846
** version to list, in which case -R can be used to select the repository.
847
** The fine behavior of the --age, -v, and -t options is altered by the -r
848
** option as well, as explained below. The -h and -H options use an
849
** implicit "-r current" option if no -r is specified.
850
**
851
** The --age option displays file commit times. Like -r, --age has the
852
** side effect of making -t sort by commit time, not modification time.
853
**
854
** The -v option provides extra information about each file. Without -r,
855
** -v displays the change status, in the manner of the [[changes]] command.
856
** With -r, -v shows the commit time and size of the checked-in files.
857
** The -h option also shows the file hash prefix. The -H option shows
858
** the check-in hash prefix. The -v option added implicitly if either of the
859
** -h or -H options is used. An implicit "-r current" is also added if
860
** -h or -H are used and no -r is specified.
861
**
862
** The -t option changes the sort order. Without -t, files are sorted by
863
** path and name (case insensitive sort if -r). If neither --age nor -r
864
** are used, -t sorts by modification time, otherwise by commit time.
865
**
866
** Options:
867
** --age Show when each file was committed
868
** -h Show file hashes. Implies -v and -r
869
** -H Show check-in hashes. Implies -v and -r
870
** --hash Verify file status using hashing rather than
871
** relying on file sizes and mtimes. Implies -v
872
** -r VERSION The specific check-in to list
873
** -R|--repository REPO Extract info from repository REPO
874
** -t Sort output in time order
875
** --tree Tree format
876
** -v|--verbose Provide extra information about each file
877
**
878
** See also: [[changes]], [[extras]], [[status]], [[tree]]
879
*/
880
void ls_cmd(void){
881
int vid;
882
Stmt q;
883
int verboseFlag;
884
int showAge;
885
int treeFmt;
886
int timeOrder;
887
char *zOrderBy = "pathname";
888
Blob where;
889
int i;
890
int useHash = 0;
891
int showFHash = 0; /* Show file hash */
892
int showCHash = 0; /* Show check-in hash */
893
const char *zName;
894
const char *zRev;
895
896
verboseFlag = find_option("verbose","v", 0)!=0;
897
if( !verboseFlag ){
898
verboseFlag = find_option("l","l", 0)!=0; /* deprecated */
899
}
900
showAge = find_option("age",0,0)!=0;
901
zRev = find_option("r","r",1);
902
timeOrder = find_option("t","t",0)!=0;
903
useHash = find_option("hash",0,0)!=0;
904
if( useHash ) verboseFlag = 1;
905
showFHash = find_option("h","h",0)!=0;
906
showCHash = find_option("H","H",0)!=0;
907
if( showFHash || showCHash ){
908
if( showFHash && showCHash ){
909
fossil_fatal("the \"ls\" command cannot use both -h and -H at once");
910
}
911
verboseFlag = 1;
912
if( zRev==0 ) zRev = "current";
913
}
914
treeFmt = find_option("tree",0,0)!=0;
915
if( treeFmt ){
916
if( zRev==0 ) zRev = "current";
917
}
918
919
if( zRev!=0 ){
920
db_find_and_open_repository(0, 0);
921
verify_all_options();
922
ls_cmd_rev(zRev, verboseFlag, showAge, showFHash, showCHash, 0, timeOrder,
923
treeFmt);
924
return;
925
}else if( find_option("R",0,1)!=0 ){
926
fossil_fatal("the -r is required in addition to -R");
927
}
928
929
db_must_be_within_tree();
930
vid = db_lget_int("checkout", 0);
931
if( timeOrder ){
932
if( showAge ){
933
zOrderBy = mprintf("checkin_mtime(%d,rid) DESC", vid);
934
}else{
935
zOrderBy = "mtime DESC";
936
}
937
}
938
verify_all_options();
939
blob_zero(&where);
940
for(i=2; i<g.argc; i++){
941
Blob fname;
942
file_tree_name(g.argv[i], &fname, 0, 1);
943
zName = blob_str(&fname);
944
if( fossil_strcmp(zName, ".")==0 ){
945
blob_reset(&where);
946
break;
947
}
948
blob_append_sql(&where,
949
" %s (pathname=%Q %s) "
950
"OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
951
(blob_size(&where)>0) ? "OR" : "WHERE", zName,
952
filename_collation(), zName, filename_collation(),
953
zName, filename_collation()
954
);
955
}
956
vfile_check_signature(vid, useHash ? CKSIG_HASH : 0);
957
if( showAge ){
958
db_prepare(&q,
959
"SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0),"
960
" datetime(checkin_mtime(%d,rid),'unixepoch',toLocal())"
961
" FROM vfile %s"
962
" ORDER BY %s",
963
vid, blob_sql_text(&where), zOrderBy /*safe-for-%s*/
964
);
965
}else{
966
db_prepare(&q,
967
"SELECT pathname, deleted, rid, chnged,"
968
" coalesce(origname!=pathname,0), islink"
969
" FROM vfile %s"
970
" ORDER BY %s", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
971
);
972
}
973
blob_reset(&where);
974
while( db_step(&q)==SQLITE_ROW ){
975
const char *zPathname = db_column_text(&q,0);
976
int isDeleted = db_column_int(&q, 1);
977
int isNew = db_column_int(&q,2)==0;
978
int chnged = db_column_int(&q,3);
979
int renamed = db_column_int(&q,4);
980
int isLink = db_column_int(&q,5);
981
char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
982
const char *type = "";
983
if( verboseFlag ){
984
if( isNew ){
985
type = "ADDED ";
986
}else if( isDeleted ){
987
type = "DELETED ";
988
}else if( !file_isfile_or_link(zFullName) ){
989
if( file_access(zFullName, F_OK)==0 ){
990
type = "NOT_A_FILE ";
991
}else{
992
type = "MISSING ";
993
}
994
}else if( chnged ){
995
if( chnged==2 ){
996
type = "UPDATED_BY_MERGE ";
997
}else if( chnged==3 ){
998
type = "ADDED_BY_MERGE ";
999
}else if( chnged==4 ){
1000
type = "UPDATED_BY_INTEGRATE ";
1001
}else if( chnged==5 ){
1002
type = "ADDED_BY_INTEGRATE ";
1003
}else if( !isLink && file_contains_merge_marker(zFullName) ){
1004
type = "CONFLICT ";
1005
}else{
1006
type = "EDITED ";
1007
}
1008
}else if( renamed ){
1009
type = "RENAMED ";
1010
}else{
1011
type = "UNCHANGED ";
1012
}
1013
}
1014
if( showAge ){
1015
fossil_print("%s%s %s\n", type, db_column_text(&q, 5), zPathname);
1016
}else{
1017
fossil_print("%s%s\n", type, zPathname);
1018
}
1019
free(zFullName);
1020
}
1021
db_finalize(&q);
1022
}
1023
1024
/*
1025
** COMMAND: tree
1026
**
1027
** Usage: %fossil tree ?OPTIONS? ?PATHS ...?
1028
**
1029
** List all files in the current check-out much like the "tree"
1030
** command does. If PATHS is included, only the named files
1031
** (or their children if directories) are shown.
1032
**
1033
** Options:
1034
** -r VERSION The specific check-in to list
1035
** -R|--repository REPO Extract info from repository REPO
1036
**
1037
** See also: [[ls]]
1038
*/
1039
void tree_cmd(void){
1040
const char *zRev;
1041
1042
zRev = find_option("r","r",1);
1043
if( zRev==0 ) zRev = "current";
1044
db_find_and_open_repository(0, 0);
1045
verify_all_options();
1046
ls_cmd_rev(zRev,0,0,0,0,0,0,1);
1047
}
1048
1049
/*
1050
** COMMAND: extras
1051
**
1052
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
1053
**
1054
** Print a list of all files in the source tree that are not part of the
1055
** current check-out. See also the "clean" command. If paths are specified,
1056
** only files in the given directories will be listed.
1057
**
1058
** Files and subdirectories whose names begin with "." are normally
1059
** ignored but can be included by adding the --dotfiles option.
1060
**
1061
** Files whose names match any of the glob patterns in the "ignore-glob"
1062
** setting are ignored. This setting can be overridden by the --ignore
1063
** option, whose CSG argument is a comma-separated list of glob patterns.
1064
**
1065
** Pathnames are displayed according to the "relative-paths" setting,
1066
** unless overridden by the --abs-paths or --rel-paths options.
1067
**
1068
** Options:
1069
** --abs-paths Display absolute pathnames
1070
** --case-sensitive BOOL Override case-sensitive setting
1071
** --dotfiles Include files beginning with a dot (".")
1072
** --header Identify the repository if there are extras
1073
** --ignore CSG Ignore files matching patterns from the argument
1074
** --rel-paths Display pathnames relative to the current working
1075
** directory
1076
** --tree Show output in the tree format
1077
**
1078
** See also: [[changes]], [[clean]], [[status]]
1079
*/
1080
void extras_cmd(void){
1081
Blob report = BLOB_INITIALIZER;
1082
const char *zIgnoreFlag = find_option("ignore",0,1);
1083
unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
1084
unsigned flags = C_EXTRA;
1085
int showHdr = find_option("header",0,0)!=0;
1086
int treeFmt = find_option("tree",0,0)!=0;
1087
Glob *pIgnore;
1088
1089
if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
1090
db_must_be_within_tree();
1091
1092
if( determine_cwd_relative_option() ){
1093
flags |= C_RELPATH;
1094
}
1095
1096
if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
1097
1098
if( treeFmt ){
1099
flags &= ~C_RELPATH;
1100
}
1101
1102
/* We should be done with options.. */
1103
verify_all_options();
1104
1105
if( zIgnoreFlag==0 ){
1106
zIgnoreFlag = db_get("ignore-glob", 0);
1107
}
1108
pIgnore = glob_create(zIgnoreFlag);
1109
locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
1110
glob_free(pIgnore);
1111
1112
blob_zero(&report);
1113
status_report(&report, flags);
1114
if( blob_size(&report) ){
1115
if( showHdr ){
1116
fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"),
1117
g.zLocalRoot);
1118
}
1119
if( treeFmt ){
1120
print_filelist_as_tree(&report);
1121
}else{
1122
blob_write_to_file(&report, "-");
1123
}
1124
}
1125
blob_reset(&report);
1126
}
1127
1128
/*
1129
** COMMAND: clean
1130
**
1131
** Usage: %fossil clean ?OPTIONS? ?PATH ...?
1132
**
1133
** Delete all "extra" files in the source tree. "Extra" files are files
1134
** that are not officially part of the check-out. If one or more PATH
1135
** arguments appear, then only the files named, or files contained with
1136
** directories named, will be removed.
1137
**
1138
** If the --prompt option is used, prompts are issued to confirm the
1139
** permanent removal of each file. Otherwise, files are backed up to the
1140
** undo buffer prior to removal, and prompts are issued only for files
1141
** whose removal cannot be undone due to their large size or due to
1142
** --disable-undo being used.
1143
**
1144
** The --force option treats all prompts as having been answered yes,
1145
** whereas --no-prompt treats them as having been answered no.
1146
**
1147
** Files matching any glob pattern specified by the --clean option are
1148
** deleted without prompting, and the removal cannot be undone.
1149
**
1150
** No file that matches glob patterns specified by --ignore or --keep will
1151
** ever be deleted. Files and subdirectories whose names begin with "."
1152
** are automatically ignored unless the --dotfiles option is used.
1153
**
1154
** The default values for --clean, --ignore, and --keep are determined by
1155
** the (versionable) clean-glob, ignore-glob, and keep-glob settings.
1156
**
1157
** The --verily option ignores the keep-glob and ignore-glob settings and
1158
** turns on --force, --emptydirs, --dotfiles, and --disable-undo. Use the
1159
** --verily option when you really want to clean up everything. Extreme
1160
** care should be exercised when using the --verily option.
1161
**
1162
** Options:
1163
** --allckouts Check for empty directories within any check-outs
1164
** that may be nested within the current one. This
1165
** option should be used with great care because the
1166
** empty-dirs setting (and other applicable settings)
1167
** belonging to the other repositories, if any, will
1168
** not be checked.
1169
** --case-sensitive BOOL Override case-sensitive setting
1170
** --dirsonly Only remove empty directories. No files will
1171
** be removed. Using this option will automatically
1172
** enable the --emptydirs option as well.
1173
** --disable-undo WARNING: This option disables use of the undo
1174
** mechanism for this clean operation and should be
1175
** used with extreme caution.
1176
** --dotfiles Include files beginning with a dot (".")
1177
** --emptydirs Remove any empty directories that are not
1178
** explicitly exempted via the empty-dirs setting
1179
** or another applicable setting or command line
1180
** argument. Matching files, if any, are removed
1181
** prior to checking for any empty directories;
1182
** therefore, directories that contain only files
1183
** that were removed will be removed as well.
1184
** -f|--force Remove files without prompting
1185
** -i|--prompt Prompt before removing each file. This option
1186
** implies the --disable-undo option.
1187
** -x|--verily WARNING: Removes everything that is not a managed
1188
** file or the repository itself. This option
1189
** implies the --force, --emptydirs, --dotfiles, and
1190
** --disable-undo options. Furthermore, it
1191
** completely disregards the keep-glob
1192
** and ignore-glob settings. However, it does honor
1193
** the --ignore and --keep options.
1194
** --clean CSG WARNING: Never prompt to delete any files matching
1195
** this comma separated list of glob patterns. Also,
1196
** deletions of any files matching this pattern list
1197
** cannot be undone.
1198
** --ignore CSG Ignore files matching patterns from the
1199
** comma separated list of glob patterns
1200
** --keep <CSG> Keep files matching this comma separated
1201
** list of glob patterns
1202
** -n|--dry-run Delete nothing, but display what would have been
1203
** deleted
1204
** --no-prompt Do not prompt the user for input and assume an
1205
** answer of 'No' for every question
1206
** --temp Remove only Fossil-generated temporary files
1207
** -v|--verbose Show all files as they are removed
1208
**
1209
** See also: [[addremove]], [[extras]], [[status]]
1210
*/
1211
void clean_cmd(void){
1212
int allFileFlag, allDirFlag, dryRunFlag, verboseFlag;
1213
int emptyDirsFlag, dirsOnlyFlag;
1214
int disableUndo, noPrompt;
1215
int alwaysPrompt = 0;
1216
unsigned scanFlags = 0;
1217
int verilyFlag = 0;
1218
const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag;
1219
Glob *pIgnore, *pKeep, *pClean;
1220
int nRoot;
1221
1222
#ifndef UNDO_SIZE_LIMIT /* TODO: Setting? */
1223
#define UNDO_SIZE_LIMIT (10*1024*1024) /* 10MiB */
1224
#endif
1225
1226
undo_capture_command_line();
1227
dryRunFlag = find_option("dry-run","n",0)!=0;
1228
if( !dryRunFlag ){
1229
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
1230
}
1231
if( !dryRunFlag ){
1232
dryRunFlag = find_option("whatif",0,0)!=0;
1233
}
1234
disableUndo = find_option("disable-undo",0,0)!=0;
1235
noPrompt = find_option("no-prompt",0,0)!=0;
1236
alwaysPrompt = find_option("prompt","i",0)!=0;
1237
allFileFlag = allDirFlag = find_option("force","f",0)!=0;
1238
dirsOnlyFlag = find_option("dirsonly",0,0)!=0;
1239
emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag;
1240
if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
1241
if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
1242
if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED;
1243
zIgnoreFlag = find_option("ignore",0,1);
1244
verboseFlag = find_option("verbose","v",0)!=0;
1245
zKeepFlag = find_option("keep",0,1);
1246
zCleanFlag = find_option("clean",0,1);
1247
db_must_be_within_tree();
1248
if( find_option("verily","x",0)!=0 ){
1249
verilyFlag = allFileFlag = allDirFlag = 1;
1250
emptyDirsFlag = 1;
1251
disableUndo = 1;
1252
scanFlags |= SCAN_ALL;
1253
zCleanFlag = 0;
1254
}
1255
if( zIgnoreFlag==0 && !verilyFlag ){
1256
zIgnoreFlag = db_get("ignore-glob", 0);
1257
}
1258
if( zKeepFlag==0 && !verilyFlag ){
1259
zKeepFlag = db_get("keep-glob", 0);
1260
}
1261
if( zCleanFlag==0 && !verilyFlag ){
1262
zCleanFlag = db_get("clean-glob", 0);
1263
}
1264
if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
1265
verify_all_options();
1266
pIgnore = glob_create(zIgnoreFlag);
1267
pKeep = glob_create(zKeepFlag);
1268
pClean = glob_create(zCleanFlag);
1269
nRoot = (int)strlen(g.zLocalRoot);
1270
if( !dirsOnlyFlag ){
1271
Stmt q;
1272
Blob repo;
1273
if( !dryRunFlag && !disableUndo ) undo_begin();
1274
locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
1275
db_prepare(&q,
1276
"SELECT %Q || pathname FROM sfile"
1277
" WHERE pathname NOT IN (%s)"
1278
" ORDER BY 1",
1279
g.zLocalRoot, fossil_all_reserved_names(0)
1280
);
1281
if( file_tree_name(g.zRepositoryName, &repo, 0, 0) ){
1282
db_multi_exec("DELETE FROM sfile WHERE pathname=%B", &repo);
1283
}
1284
db_multi_exec("DELETE FROM sfile WHERE pathname IN"
1285
" (SELECT pathname FROM vfile)");
1286
while( db_step(&q)==SQLITE_ROW ){
1287
const char *zName = db_column_text(&q, 0);
1288
if( glob_match(pKeep, zName+nRoot) ){
1289
if( verboseFlag ){
1290
fossil_print("KEPT file \"%s\" not removed (due to --keep"
1291
" or \"keep-glob\")\n", zName+nRoot);
1292
}
1293
continue;
1294
}
1295
if( !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
1296
char *zPrompt = 0;
1297
char cReply;
1298
Blob ans = empty_blob;
1299
int undoRc = UNDO_NONE;
1300
if( alwaysPrompt ){
1301
zPrompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ",
1302
zName+nRoot);
1303
prompt_user(zPrompt, &ans);
1304
fossil_free(zPrompt);
1305
cReply = fossil_toupper(blob_str(&ans)[0]);
1306
blob_reset(&ans);
1307
if( cReply=='N' ) continue;
1308
if( cReply=='A' ){
1309
allFileFlag = 1;
1310
alwaysPrompt = 0;
1311
}else{
1312
undoRc = UNDO_SAVED_OK;
1313
}
1314
}else if( !disableUndo ){
1315
undoRc = undo_maybe_save(zName+nRoot, UNDO_SIZE_LIMIT);
1316
}
1317
if( undoRc!=UNDO_SAVED_OK ){
1318
if( allFileFlag ){
1319
cReply = 'Y';
1320
}else if( !noPrompt ){
1321
Blob ans;
1322
zPrompt = mprintf("\nWARNING: Deletion of this file will "
1323
"not be undoable via the 'undo'\n"
1324
" command because %s.\n\n"
1325
"Remove unmanaged file \"%s\" (a=all/y/N)? ",
1326
undo_save_message(undoRc), zName+nRoot);
1327
prompt_user(zPrompt, &ans);
1328
fossil_free(zPrompt);
1329
cReply = blob_str(&ans)[0];
1330
blob_reset(&ans);
1331
}else{
1332
cReply = 'N';
1333
}
1334
if( cReply=='a' || cReply=='A' ){
1335
allFileFlag = 1;
1336
}else if( cReply!='y' && cReply!='Y' ){
1337
continue;
1338
}
1339
}
1340
}
1341
if( dryRunFlag || file_delete(zName)==0 ){
1342
if( verboseFlag || dryRunFlag ){
1343
fossil_print("Removed unmanaged file: %s\n", zName+nRoot);
1344
}
1345
}else{
1346
fossil_print("Could not remove file: %s\n", zName+nRoot);
1347
}
1348
}
1349
db_finalize(&q);
1350
if( !dryRunFlag && !disableUndo ) undo_finish();
1351
}
1352
if( emptyDirsFlag ){
1353
Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0));
1354
Stmt q;
1355
Blob root;
1356
blob_init(&root, g.zLocalRoot, nRoot - 1);
1357
vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore,
1358
pEmptyDirs, RepoFILE);
1359
blob_reset(&root);
1360
db_prepare(&q,
1361
"SELECT %Q || x FROM dscan_temp"
1362
" WHERE x NOT IN (%s) AND y = 0"
1363
" ORDER BY 1 DESC",
1364
g.zLocalRoot, fossil_all_reserved_names(0)
1365
);
1366
while( db_step(&q)==SQLITE_ROW ){
1367
const char *zName = db_column_text(&q, 0);
1368
if( glob_match(pKeep, zName+nRoot) ){
1369
if( verboseFlag ){
1370
fossil_print("KEPT directory \"%s\" not removed (due to --keep"
1371
" or \"keep-glob\")\n", zName+nRoot);
1372
}
1373
continue;
1374
}
1375
if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
1376
char cReply;
1377
if( !noPrompt ){
1378
Blob ans;
1379
char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ",
1380
zName+nRoot);
1381
prompt_user(prompt, &ans);
1382
cReply = blob_str(&ans)[0];
1383
fossil_free(prompt);
1384
blob_reset(&ans);
1385
}else{
1386
cReply = 'N';
1387
}
1388
if( cReply=='a' || cReply=='A' ){
1389
allDirFlag = 1;
1390
}else if( cReply!='y' && cReply!='Y' ){
1391
continue;
1392
}
1393
}
1394
if( dryRunFlag || file_rmdir(zName)==0 ){
1395
if( verboseFlag || dryRunFlag ){
1396
fossil_print("Removed unmanaged directory: %s\n", zName+nRoot);
1397
}
1398
}else if( verboseFlag ){
1399
fossil_print("Could not remove directory: %s\n", zName+nRoot);
1400
}
1401
}
1402
db_finalize(&q);
1403
glob_free(pEmptyDirs);
1404
}
1405
glob_free(pClean);
1406
glob_free(pKeep);
1407
glob_free(pIgnore);
1408
}
1409
1410
/*
1411
** Prompt the user for a check-in or stash comment (given in pPrompt),
1412
** gather the response, then return the response in pComment.
1413
**
1414
** Lines of the prompt that begin with # are discarded. Excess whitespace
1415
** is removed from the reply.
1416
**
1417
** Appropriate encoding translations are made on windows.
1418
*/
1419
void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
1420
const char *zEditor;
1421
char *zCmd;
1422
char *zFile;
1423
Blob reply, line;
1424
char *zComment;
1425
int i;
1426
1427
zEditor = fossil_text_editor();
1428
if( zEditor==0 ){
1429
if( blob_size(pPrompt)>0 ){
1430
blob_append(pPrompt,
1431
"#\n"
1432
"# Since no default text editor is set using EDITOR or VISUAL\n"
1433
"# environment variables or the \"fossil set editor\" command,\n"
1434
"# and because no comment was specified using the \"-m\" or \"-M\"\n"
1435
"# command-line options, you will need to enter the comment below.\n"
1436
"# Type \".\" on a line by itself when you are done:\n", -1);
1437
}
1438
zFile = mprintf("-");
1439
}else{
1440
Blob fname;
1441
blob_zero(&fname);
1442
if( g.zLocalRoot!=0 ){
1443
file_relative_name(g.zLocalRoot, &fname, 1);
1444
zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'",
1445
blob_str(&fname));
1446
}else{
1447
file_tempname(&fname, "ci-comment",0);
1448
zFile = fossil_strdup(blob_str(&fname));
1449
}
1450
blob_reset(&fname);
1451
}
1452
#if defined(_WIN32)
1453
blob_add_cr(pPrompt);
1454
#endif
1455
if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile);
1456
if( zEditor ){
1457
char *z, *zEnd;
1458
zCmd = mprintf("%s %$", zEditor, zFile);
1459
fossil_print("%s\n", zCmd);
1460
if( fossil_system(zCmd) ){
1461
fossil_fatal("editor aborted: \"%s\"", zCmd);
1462
}
1463
blob_read_from_file(&reply, zFile, ExtFILE);
1464
z = blob_str(&reply);
1465
zEnd = strstr(z, "##########");
1466
if( zEnd ){
1467
/* Truncate the reply at any sequence of 10 or more # characters.
1468
** The diff for the -v option occurs after such a sequence. */
1469
blob_resize(&reply, (int)(zEnd - z));
1470
}
1471
}else{
1472
char zIn[300];
1473
blob_zero(&reply);
1474
while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
1475
if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
1476
break;
1477
}
1478
blob_append(&reply, zIn, -1);
1479
}
1480
}
1481
blob_to_utf8_no_bom(&reply, 1);
1482
blob_to_lf_only(&reply);
1483
file_delete(zFile);
1484
free(zFile);
1485
blob_zero(pComment);
1486
while( blob_line(&reply, &line) ){
1487
int i, n;
1488
char *z;
1489
n = blob_size(&line);
1490
z = blob_buffer(&line);
1491
for(i=0; i<n && fossil_isspace(z[i]); i++){}
1492
if( i<n && z[i]=='#' ) continue;
1493
if( i<n || blob_size(pComment)>0 ){
1494
blob_appendf(pComment, "%b", &line);
1495
}
1496
}
1497
blob_reset(&reply);
1498
zComment = blob_str(pComment);
1499
i = strlen(zComment);
1500
while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
1501
blob_resize(pComment, i);
1502
}
1503
1504
/*
1505
** Prepare a commit comment. Let the user modify it using the
1506
** editor specified in the global_config table or either
1507
** the VISUAL or EDITOR environment variable.
1508
**
1509
** Store the final commit comment in pComment. pComment is assumed
1510
** to be uninitialized - any prior content is overwritten.
1511
**
1512
** zInit is the text of the most recent failed attempt to check in
1513
** this same change. Use zInit to reinitialize the check-in comment
1514
** so that the user does not have to retype.
1515
**
1516
** zBranch is the name of a new branch that this check-in is forced into.
1517
** zBranch might be NULL or an empty string if no forcing occurs.
1518
**
1519
** parent_rid is the recordid of the parent check-in.
1520
*/
1521
static void prepare_commit_comment(
1522
Blob *pComment,
1523
char *zInit,
1524
CheckinInfo *p,
1525
int parent_rid,
1526
int dryRunFlag
1527
){
1528
Blob prompt;
1529
int wikiFlags;
1530
#if defined(_WIN32) || defined(__CYGWIN__)
1531
int bomSize;
1532
const unsigned char *bom = get_utf8_bom(&bomSize);
1533
blob_init(&prompt, (const char *) bom, bomSize);
1534
if( zInit && zInit[0]){
1535
blob_append(&prompt, zInit, -1);
1536
}
1537
#else
1538
blob_init(&prompt, zInit, -1);
1539
#endif
1540
blob_append(&prompt,
1541
"\n"
1542
"# Enter the commit message. Formatting rules:\n"
1543
"# * Lines beginning with # are ignored.\n",
1544
-1
1545
);
1546
wikiFlags = wiki_convert_flags(1);
1547
if( wikiFlags & WIKI_LINKSONLY ){
1548
blob_append(&prompt,"# * Hyperlinks inside of [...]\n", -1);
1549
if( wikiFlags & WIKI_NEWLINE ){
1550
blob_append(&prompt,
1551
"# * Newlines are significant and are displayed as written\n", -1);
1552
}else{
1553
blob_append(&prompt,
1554
"# * Newlines are interpreted as ordinary spaces\n",
1555
-1
1556
);
1557
}
1558
blob_append(&prompt,
1559
"# * All other text will be displayed as written\n", -1);
1560
}else{
1561
blob_append(&prompt,
1562
"# * Hyperlinks: [target] or [target|display-text]\n"
1563
"# * Blank lines cause a paragraph break\n"
1564
"# * Other text rendered as if it were HTML\n", -1
1565
);
1566
}
1567
blob_append(&prompt, "#\n", 2);
1568
1569
if( dryRunFlag ){
1570
blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
1571
"will be made to the repository\n#\n");
1572
}
1573
blob_appendf(&prompt, "# user: %s\n",
1574
p->zUserOvrd ? p->zUserOvrd : login_name());
1575
if( p->zBranch && p->zBranch[0] ){
1576
blob_appendf(&prompt, "# tags: %s\n#\n", p->zBranch);
1577
}else{
1578
char *zTags = info_tags_of_checkin(parent_rid, 1);
1579
if( zTags || p->azTag ){
1580
blob_append(&prompt, "# tags: ", 8);
1581
if(zTags){
1582
blob_appendf(&prompt, "%z%s", zTags, p->azTag ? ", " : "");
1583
}
1584
if(p->azTag){
1585
int i = 0;
1586
for( ; p->azTag[i]; ++i ){
1587
blob_appendf(&prompt, "%s%s", p->azTag[i],
1588
p->azTag[i+1] ? ", " : "");
1589
}
1590
}
1591
blob_appendf(&prompt, "\n#\n");
1592
}
1593
}
1594
status_report(&prompt, C_DEFAULT | C_FATAL | C_COMMENT);
1595
if( g.markPrivate ){
1596
blob_append(&prompt,
1597
"# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
1598
"# repositories.\n"
1599
"#\n", -1
1600
);
1601
}
1602
if( p->integrateFlag ){
1603
blob_append(&prompt,
1604
"#\n"
1605
"# All merged-in branches will be closed due to the --integrate flag\n"
1606
"#\n", -1
1607
);
1608
}
1609
if( p->verboseFlag ){
1610
DiffConfig DCfg;
1611
blob_appendf(&prompt,
1612
"#\n%.78c\n"
1613
"# The following diff is excluded from the commit message:\n#\n",
1614
'#'
1615
);
1616
diff_options(&DCfg, 0, 1);
1617
DCfg.diffFlags |= DIFF_VERBOSE;
1618
if( g.aCommitFile ){
1619
Stmt q;
1620
Blob sql = BLOB_INITIALIZER;
1621
FileDirList *diffFiles;
1622
int i;
1623
for(i=0; g.aCommitFile[i]!=0; ++i){}
1624
diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
1625
for(i=0; g.aCommitFile[i]!=0; ++i){
1626
blob_append_sql(&sql,
1627
"SELECT pathname, deleted, rid "
1628
"FROM vfile WHERE id=%d",
1629
g.aCommitFile[i]);
1630
db_prepare(&q, "%s", blob_sql_text(&sql));
1631
blob_reset(&sql);
1632
assert( db_step(&q)==SQLITE_ROW );
1633
diffFiles[i].zName = fossil_strdup(db_column_text(&q, 0));
1634
DCfg.diffFlags &= (~DIFF_FILE_MASK);
1635
if( db_column_int(&q, 1) ){
1636
DCfg.diffFlags |= DIFF_FILE_DELETED;
1637
}else if( db_column_int(&q, 2)==0 ){
1638
DCfg.diffFlags |= DIFF_FILE_ADDED;
1639
}
1640
db_finalize(&q);
1641
if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){
1642
diffFiles[0].zName[0] = '.';
1643
diffFiles[0].zName[1] = 0;
1644
break;
1645
}
1646
diffFiles[i].nName = strlen(diffFiles[i].zName);
1647
diffFiles[i].nUsed = 0;
1648
}
1649
diff_version_to_checkout(0, &DCfg, diffFiles, &prompt);
1650
for( i=0; diffFiles[i].zName; ++i ){
1651
fossil_free(diffFiles[i].zName);
1652
}
1653
fossil_free(diffFiles);
1654
}else{
1655
diff_version_to_checkout(0, &DCfg, 0, &prompt);
1656
}
1657
}
1658
prompt_for_user_comment(pComment, &prompt);
1659
blob_reset(&prompt);
1660
}
1661
1662
/*
1663
** Prepare text that describes a pending commit and write it into
1664
** a file at the root of the check-in. Return the name of that file.
1665
**
1666
** Space to hold the returned filename is obtained from fossil_malloc()
1667
** and should be freed by the caller. The caller should also unlink
1668
** the file when it is done.
1669
*/
1670
static char *prepare_commit_description_file(
1671
CheckinInfo *p, /* Information about this commit */
1672
int parent_rid, /* parent check-in */
1673
Blob *pComment, /* Check-in comment */
1674
int dryRunFlag /* True for a dry-run only */
1675
){
1676
Blob *pDesc;
1677
char *zTags;
1678
char *zFilename;
1679
const char *zMainBranch = db_main_branch();
1680
Blob desc;
1681
blob_init(&desc, 0, 0);
1682
pDesc = &desc;
1683
blob_appendf(pDesc, "checkout %s\n", g.zLocalRoot);
1684
blob_appendf(pDesc, "repository %s\n", g.zRepositoryName);
1685
blob_appendf(pDesc, "user %s\n",
1686
p->zUserOvrd ? p->zUserOvrd : login_name());
1687
blob_appendf(pDesc, "branch %s\n",
1688
(p->zBranch && p->zBranch[0]) ? p->zBranch : zMainBranch);
1689
zTags = info_tags_of_checkin(parent_rid, 1);
1690
if( zTags || p->azTag ){
1691
blob_append(pDesc, "tags ", -1);
1692
if(zTags){
1693
blob_appendf(pDesc, "%z%s", zTags, p->azTag ? ", " : "");
1694
}
1695
if(p->azTag){
1696
int i = 0;
1697
for( ; p->azTag[i]; ++i ){
1698
blob_appendf(pDesc, "%s%s", p->azTag[i],
1699
p->azTag[i+1] ? ", " : "");
1700
}
1701
}
1702
blob_appendf(pDesc, "\n");
1703
}
1704
status_report(pDesc, C_DEFAULT | C_FATAL);
1705
if( g.markPrivate ){
1706
blob_append(pDesc, "private-branch\n", -1);
1707
}
1708
if( p->integrateFlag ){
1709
blob_append(pDesc, "integrate\n", -1);
1710
}
1711
if( pComment && blob_size(pComment)>0 ){
1712
blob_appendf(pDesc, "checkin-comment\n%s\n", blob_str(pComment));
1713
}
1714
if( dryRunFlag ){
1715
zFilename = 0;
1716
fossil_print("******* Commit Description *******\n%s"
1717
"***** End Commit Description *****\n",
1718
blob_str(pDesc));
1719
}else{
1720
unsigned int r[2];
1721
sqlite3_randomness(sizeof(r), r);
1722
zFilename = mprintf("%scommit-description-%08x%08x.txt",
1723
g.zLocalRoot, r[0], r[1]);
1724
blob_write_to_file(pDesc, zFilename);
1725
}
1726
blob_reset(pDesc);
1727
return zFilename;
1728
}
1729
1730
1731
/*
1732
** Populate the Global.aCommitFile[] based on the command line arguments
1733
** to a [commit] command. Global.aCommitFile is an array of integers
1734
** sized at (N+1), where N is the number of arguments passed to [commit].
1735
** The contents are the [id] values from the vfile table corresponding
1736
** to the filenames passed as arguments.
1737
**
1738
** The last element of aCommitFile[] is always 0 - indicating the end
1739
** of the array.
1740
**
1741
** If there were no arguments passed to [commit], aCommitFile is not
1742
** allocated and remains NULL. Other parts of the code interpret this
1743
** to mean "all files".
1744
**
1745
** Returns 1 if there was a warning, 0 otherwise.
1746
*/
1747
int select_commit_files(void){
1748
int result = 0;
1749
assert( g.aCommitFile==0 );
1750
if( g.argc>2 ){
1751
int ii, jj=0;
1752
Blob fname;
1753
Stmt q;
1754
Bag toCommit;
1755
1756
blob_zero(&fname);
1757
bag_init(&toCommit);
1758
for(ii=2; ii<g.argc; ii++){
1759
int cnt = 0;
1760
file_tree_name(g.argv[ii], &fname, 0, 1);
1761
if( fossil_strcmp(blob_str(&fname),".")==0 ){
1762
bag_clear(&toCommit);
1763
return result;
1764
}
1765
db_prepare(&q,
1766
"SELECT id FROM vfile WHERE pathname=%Q %s"
1767
" OR (pathname>'%q/' %s AND pathname<'%q0' %s)",
1768
blob_str(&fname), filename_collation(), blob_str(&fname),
1769
filename_collation(), blob_str(&fname), filename_collation());
1770
while( db_step(&q)==SQLITE_ROW ){
1771
cnt++;
1772
bag_insert(&toCommit, db_column_int(&q, 0));
1773
}
1774
db_finalize(&q);
1775
if( cnt==0 ){
1776
fossil_warning("fossil knows nothing about: %s", g.argv[ii]);
1777
result = 1;
1778
}
1779
blob_reset(&fname);
1780
}
1781
g.aCommitFile = fossil_malloc( (bag_count(&toCommit)+1) *
1782
sizeof(g.aCommitFile[0]) );
1783
for(ii=bag_first(&toCommit); ii>0; ii=bag_next(&toCommit, ii)){
1784
g.aCommitFile[jj++] = ii;
1785
}
1786
g.aCommitFile[jj] = 0;
1787
bag_clear(&toCommit);
1788
}
1789
return result;
1790
}
1791
1792
/*
1793
** Returns true if the check-in identified by the first parameter is
1794
** older than the given (valid) date/time string, else returns false.
1795
** Also returns true if rid does not refer to a check-in, but it is not
1796
** intended to be used for that case.
1797
*/
1798
int checkin_is_younger(
1799
int rid, /* The record ID of the ancestor */
1800
const char *zDate /* Date & time of the current check-in */
1801
){
1802
return db_exists(
1803
"SELECT 1 FROM event"
1804
" WHERE datetime(mtime)>=%Q"
1805
" AND type='ci' AND objid=%d",
1806
zDate, rid
1807
) ? 0 : 1;
1808
}
1809
1810
/*
1811
** Make sure the current check-in with timestamp zDate is younger than its
1812
** ancestor identified rid and zUuid. Throw a fatal error if not.
1813
*/
1814
static void checkin_verify_younger(
1815
int rid, /* The record ID of the ancestor */
1816
const char *zUuid, /* The artifact hash of the ancestor */
1817
const char *zDate /* Date & time of the current check-in */
1818
){
1819
#ifndef FOSSIL_ALLOW_OUT_OF_ORDER_DATES
1820
if(checkin_is_younger(rid,zDate)==0){
1821
fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)"
1822
" Use --allow-older to override.", zUuid, zDate);
1823
}
1824
#endif
1825
}
1826
1827
1828
1829
/*
1830
** zDate should be a valid date string. Convert this string into the
1831
** format YYYY-MM-DDTHH:MM:SS. If the string is not a valid date,
1832
** print a fatal error and quit.
1833
*/
1834
char *date_in_standard_format(const char *zInputDate){
1835
char *zDate;
1836
if( g.perm.Setup && fossil_strcmp(zInputDate,"now")==0 ){
1837
zInputDate = PD("date_override","now");
1838
}
1839
zDate = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%f',%Q)",
1840
zInputDate);
1841
if( zDate[0]==0 ){
1842
fossil_fatal(
1843
"unrecognized date format (%s): use \"YYYY-MM-DD HH:MM:SS.SSS\"",
1844
zInputDate
1845
);
1846
}
1847
return zDate;
1848
}
1849
1850
/*
1851
** COMMAND: test-date-format
1852
**
1853
** Usage: %fossil test-date-format DATE-STRING...
1854
**
1855
** Convert the DATE-STRING into the standard format used in artifacts
1856
** and display the result.
1857
*/
1858
void test_date_format(void){
1859
int i;
1860
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
1861
for(i=2; i<g.argc; i++){
1862
fossil_print("%s -> %s\n", g.argv[i], date_in_standard_format(g.argv[i]));
1863
}
1864
}
1865
1866
#if INTERFACE
1867
/*
1868
** The following structure holds some of the information needed to construct a
1869
** check-in manifest.
1870
*/
1871
struct CheckinInfo {
1872
Blob *pComment; /* Check-in comment text */
1873
const char *zMimetype; /* Mimetype of check-in command. May be NULL */
1874
int verifyDate; /* Verify that child is younger */
1875
int closeFlag; /* Close the branch being committed */
1876
int integrateFlag; /* Close merged-in branches */
1877
int verboseFlag; /* Show diff in editor for check-in comment */
1878
Blob *pCksum; /* Repository checksum. May be 0 */
1879
const char *zDateOvrd; /* Date override. If 0 then use 'now' */
1880
const char *zUserOvrd; /* User override. If 0 then use login_name() */
1881
const char *zBranch; /* Branch name. May be 0 */
1882
const char *zColor; /* One-time background color. May be 0 */
1883
const char *zBrClr; /* Persistent branch color. May be 0 */
1884
const char **azTag; /* Tags to apply to this check-in */
1885
};
1886
#endif /* INTERFACE */
1887
1888
/*
1889
** Create a manifest.
1890
*/
1891
static void create_manifest(
1892
Blob *pOut, /* Write the manifest here */
1893
const char *zBaselineUuid, /* Hash of baseline, or zero */
1894
Manifest *pBaseline, /* Make it a delta manifest if not zero */
1895
int vid, /* BLOB.id for the parent check-in */
1896
CheckinInfo *p, /* Information about the check-in */
1897
int *pnFBcard /* OUT: Number of generated B- and F-cards */
1898
){
1899
char *zDate; /* Date of the check-in */
1900
char *zParentUuid = 0; /* Hash of parent check-in */
1901
Blob filename; /* A single filename */
1902
int nBasename; /* Size of base filename */
1903
Stmt q; /* Various queries */
1904
Blob mcksum; /* Manifest checksum */
1905
ManifestFile *pFile; /* File from the baseline */
1906
int nFBcard = 0; /* Number of B-cards and F-cards */
1907
int i; /* Loop counter */
1908
const char *zColor; /* Modified value of p->zColor */
1909
1910
assert( pBaseline==0 || pBaseline->zBaseline==0 );
1911
assert( pBaseline==0 || zBaselineUuid!=0 );
1912
blob_zero(pOut);
1913
if( vid ){
1914
zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d AND "
1915
"EXISTS(SELECT 1 FROM event WHERE event.type='ci' and event.objid=%d)",
1916
vid, vid);
1917
if( !zParentUuid ){
1918
fossil_fatal("Could not find a valid check-in for RID %d. "
1919
"Possible check-out/repo mismatch.", vid);
1920
}
1921
}
1922
if( pBaseline ){
1923
blob_appendf(pOut, "B %s\n", zBaselineUuid);
1924
manifest_file_rewind(pBaseline);
1925
pFile = manifest_file_next(pBaseline, 0);
1926
nFBcard++;
1927
}else{
1928
pFile = 0;
1929
}
1930
if( blob_size(p->pComment)!=0 ){
1931
blob_appendf(pOut, "C %F\n", blob_str(p->pComment));
1932
}else{
1933
blob_append(pOut, "C (no\\scomment)\n", 16);
1934
}
1935
zDate = date_in_standard_format(p->zDateOvrd ? p->zDateOvrd : "now");
1936
blob_appendf(pOut, "D %s\n", zDate);
1937
zDate[10] = ' ';
1938
db_prepare(&q,
1939
"SELECT pathname, uuid, origname, blob.rid, isexe, islink,"
1940
" is_selected(vfile.id)"
1941
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
1942
" WHERE (NOT deleted OR NOT is_selected(vfile.id))"
1943
" AND vfile.vid=%d"
1944
" ORDER BY if_selected(vfile.id, pathname, origname)",
1945
vid);
1946
blob_zero(&filename);
1947
blob_appendf(&filename, "%s", g.zLocalRoot);
1948
nBasename = blob_size(&filename);
1949
while( db_step(&q)==SQLITE_ROW ){
1950
const char *zName = db_column_text(&q, 0);
1951
const char *zUuid = db_column_text(&q, 1);
1952
const char *zOrig = db_column_text(&q, 2);
1953
int frid = db_column_int(&q, 3);
1954
int isExe = db_column_int(&q, 4);
1955
int isLink = db_column_int(&q, 5);
1956
int isSelected = db_column_int(&q, 6);
1957
const char *zPerm;
1958
int cmp;
1959
1960
blob_resize(&filename, nBasename);
1961
blob_append(&filename, zName, -1);
1962
1963
#if !defined(_WIN32)
1964
/* For unix, extract the "executable" and "symlink" permissions
1965
** directly from the filesystem. On windows, permissions are
1966
** unchanged from the original. However, only do this if the file
1967
** itself is actually selected to be part of this check-in.
1968
*/
1969
if( isSelected ){
1970
int mPerm;
1971
1972
mPerm = file_perm(blob_str(&filename), RepoFILE);
1973
isExe = ( mPerm==PERM_EXE );
1974
isLink = ( mPerm==PERM_LNK );
1975
}
1976
#endif
1977
if( isExe ){
1978
zPerm = " x";
1979
}else if( isLink ){
1980
zPerm = " l"; /* note: symlinks don't have executable bit on unix */
1981
}else{
1982
zPerm = "";
1983
}
1984
if( !g.markPrivate ) content_make_public(frid);
1985
while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){
1986
blob_appendf(pOut, "F %F\n", pFile->zName);
1987
pFile = manifest_file_next(pBaseline, 0);
1988
nFBcard++;
1989
}
1990
cmp = 1;
1991
if( pFile==0
1992
|| (cmp = fossil_strcmp(pFile->zName,zName))!=0
1993
|| fossil_strcmp(pFile->zUuid, zUuid)!=0
1994
){
1995
if( zOrig && !isSelected ){ zName = zOrig; zOrig = 0; }
1996
if( zOrig==0 || fossil_strcmp(zOrig,zName)==0 ){
1997
blob_appendf(pOut, "F %F %s%s\n", zName, zUuid, zPerm);
1998
}else{
1999
if( zPerm[0]==0 ){ zPerm = " w"; }
2000
blob_appendf(pOut, "F %F %s%s %F\n", zName, zUuid, zPerm, zOrig);
2001
}
2002
nFBcard++;
2003
}
2004
if( cmp==0 ) pFile = manifest_file_next(pBaseline,0);
2005
}
2006
blob_reset(&filename);
2007
db_finalize(&q);
2008
while( pFile ){
2009
blob_appendf(pOut, "F %F\n", pFile->zName);
2010
pFile = manifest_file_next(pBaseline, 0);
2011
nFBcard++;
2012
}
2013
if( p->zMimetype && p->zMimetype[0] ){
2014
blob_appendf(pOut, "N %F\n", p->zMimetype);
2015
}
2016
if( vid ){
2017
blob_appendf(pOut, "P %s", zParentUuid);
2018
if( p->verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate);
2019
free(zParentUuid);
2020
db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0 OR id<-2");
2021
while( db_step(&q)==SQLITE_ROW ){
2022
char *zMergeUuid;
2023
int mid = db_column_int(&q, 0);
2024
if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ){
2025
continue;
2026
}
2027
zMergeUuid = rid_to_uuid(mid);
2028
if( zMergeUuid ){
2029
blob_appendf(pOut, " %s", zMergeUuid);
2030
if( p->verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate);
2031
free(zMergeUuid);
2032
}
2033
}
2034
db_finalize(&q);
2035
blob_appendf(pOut, "\n");
2036
}
2037
free(zDate);
2038
2039
db_prepare(&q,
2040
"SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash, merge"
2041
" FROM vmerge"
2042
" WHERE (vmerge.id=-1 OR vmerge.id=-2)"
2043
" ORDER BY 1");
2044
while( db_step(&q)==SQLITE_ROW ){
2045
const char *zCherrypickUuid = db_column_text(&q, 0);
2046
int mid = db_column_int(&q, 1);
2047
if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ) continue;
2048
blob_appendf(pOut, "Q %s\n", zCherrypickUuid);
2049
}
2050
db_finalize(&q);
2051
2052
if( p->pCksum ) blob_appendf(pOut, "R %b\n", p->pCksum);
2053
zColor = p->zColor;
2054
if( p->zBranch && p->zBranch[0] ){
2055
/* Set tags for the new branch */
2056
if( p->zBrClr && p->zBrClr[0] ){
2057
zColor = 0;
2058
blob_appendf(pOut, "T *bgcolor * %F\n", p->zBrClr);
2059
}
2060
blob_appendf(pOut, "T *branch * %F\n", p->zBranch);
2061
blob_appendf(pOut, "T *sym-%F *\n", p->zBranch);
2062
}
2063
if( zColor && zColor[0] ){
2064
/* One-time background color */
2065
blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
2066
}
2067
if( p->closeFlag ){
2068
blob_appendf(pOut, "T +closed *\n");
2069
}
2070
db_prepare(&q, "SELECT mhash,merge FROM vmerge"
2071
" WHERE id %s ORDER BY 1",
2072
p->integrateFlag ? "IN(0,-4)" : "=(-4)");
2073
while( db_step(&q)==SQLITE_ROW ){
2074
const char *zIntegrateUuid = db_column_text(&q, 0);
2075
int rid = db_column_int(&q, 1);
2076
if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
2077
" WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
2078
#if 0
2079
/* Make sure the check-in manifest of the resulting merge child does not
2080
** include a +close tag referring to the leaf check-in on a private
2081
** branch, so as not to generate a missing artifact reference on
2082
** repository clones without that private branch. The merge command
2083
** should have dropped the --integrate option, at this point. */
2084
assert( !content_is_private(rid) );
2085
#endif
2086
blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
2087
}
2088
}
2089
db_finalize(&q);
2090
2091
if( p->azTag ){
2092
for(i=0; p->azTag[i]; i++){
2093
/* Add a symbolic tag to this check-in. The tag names have already
2094
** been sorted and converted using the %F format */
2095
assert( i==0 || strcmp(p->azTag[i-1], p->azTag[i])<=0 );
2096
blob_appendf(pOut, "T +sym-%s *\n", p->azTag[i]);
2097
}
2098
}
2099
if( p->zBranch && p->zBranch[0] ){
2100
/* For a new branch, cancel all prior propagating tags */
2101
db_prepare(&q,
2102
"SELECT tagname FROM tagxref, tag"
2103
" WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
2104
" AND tagtype==2 AND tagname GLOB 'sym-*'"
2105
" AND tagname!='sym-'||%Q"
2106
" ORDER BY tagname",
2107
vid, p->zBranch);
2108
while( db_step(&q)==SQLITE_ROW ){
2109
const char *zBrTag = db_column_text(&q, 0);
2110
blob_appendf(pOut, "T -%F *\n", zBrTag);
2111
}
2112
db_finalize(&q);
2113
}
2114
blob_appendf(pOut, "U %F\n", p->zUserOvrd ? p->zUserOvrd : login_name());
2115
md5sum_blob(pOut, &mcksum);
2116
blob_appendf(pOut, "Z %b\n", &mcksum);
2117
if( pnFBcard ) *pnFBcard = nFBcard;
2118
}
2119
2120
/*
2121
** Issue a warning and give the user an opportunity to abandon out
2122
** if a Unicode (UTF-16) byte-order-mark (BOM) or a \r\n line ending
2123
** is seen in a text file.
2124
**
2125
** Return 1 if the user pressed 'c'. In that case, the file will have
2126
** been converted to UTF-8 (if it was UTF-16) with LF line-endings,
2127
** and the original file will have been renamed to "<filename>-original".
2128
*/
2129
static int commit_warning(
2130
Blob *pContent, /* The content of the file being committed. */
2131
int crlfOk, /* Non-zero if CR/LF warnings should be disabled. */
2132
int binOk, /* Non-zero if binary warnings should be disabled. */
2133
int encodingOk, /* Non-zero if encoding warnings should be disabled. */
2134
int sizeOk, /* Non-zero if oversize warnings are disabled */
2135
int noPrompt, /* 0 to always prompt, 1 for 'N', 2 for 'Y'. */
2136
const char *zFilename, /* The full name of the file being committed. */
2137
Blob *pReason /* Reason for warning, if any (non-fatal only). */
2138
){
2139
int bReverse; /* UTF-16 byte order is reversed? */
2140
int fUnicode; /* return value of could_be_utf16() */
2141
int fBinary; /* does the blob content appear to be binary? */
2142
int lookFlags; /* output flags from looks_like_utf8/utf16() */
2143
int fHasAnyCr; /* the blob contains one or more CR chars */
2144
int fHasLoneCrOnly; /* all detected line endings are CR only */
2145
int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */
2146
int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */
2147
int fHasNul; /* contains NUL chars? */
2148
int fHasLong; /* overly long line? */
2149
char *zMsg; /* Warning message */
2150
Blob fname; /* Relative pathname of the file */
2151
static int allOk = 0; /* Set to true to disable this routine */
2152
2153
if( allOk ) return 0;
2154
if( sizeOk ){
2155
fUnicode = could_be_utf16(pContent, &bReverse);
2156
if( fUnicode ){
2157
lookFlags = looks_like_utf16(pContent, bReverse, LOOK_NUL);
2158
}else{
2159
lookFlags = looks_like_utf8(pContent, LOOK_NUL);
2160
if( !(lookFlags & LOOK_BINARY) && invalid_utf8(pContent) ){
2161
fHasInvalidUtf8 = 1;
2162
}
2163
}
2164
fHasAnyCr = (lookFlags & LOOK_CR);
2165
fBinary = (lookFlags & LOOK_BINARY);
2166
fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR);
2167
fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF);
2168
fHasNul = (lookFlags & LOOK_NUL);
2169
fHasLong = (lookFlags & LOOK_LONG);
2170
}else{
2171
fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0;
2172
fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0;
2173
}
2174
if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){
2175
const char *zWarning = 0;
2176
const char *zDisable = 0;
2177
const char *zConvert = "c=convert/";
2178
const char *zIn = "in";
2179
Blob ans;
2180
char cReply;
2181
2182
if( fBinary ){
2183
if( binOk ){
2184
return 0; /* We don't want binary warnings for this file. */
2185
}
2186
if( !fHasNul && fHasLong ){
2187
zWarning = "long lines";
2188
zConvert = ""; /* We cannot convert overlong lines. */
2189
}else{
2190
zWarning = "binary data";
2191
zConvert = ""; /* We cannot convert binary files. */
2192
}
2193
zDisable = "\"binary-glob\" setting";
2194
}else if( fUnicode && fHasAnyCr ){
2195
if( crlfOk && encodingOk ){
2196
return 0; /* We don't want CR/LF and Unicode warnings for this file. */
2197
}
2198
if( fHasLoneCrOnly ){
2199
zWarning = "CR line endings and Unicode";
2200
}else if( fHasCrLfOnly ){
2201
zWarning = "CR/LF line endings and Unicode";
2202
}else{
2203
zWarning = "mixed line endings and Unicode";
2204
}
2205
zDisable = "\"crlf-glob\" and \"encoding-glob\" settings";
2206
}else if( fHasInvalidUtf8 ){
2207
if( encodingOk ){
2208
return 0; /* We don't want encoding warnings for this file. */
2209
}
2210
zWarning = "invalid UTF-8";
2211
zDisable = "\"encoding-glob\" setting";
2212
}else if( fHasAnyCr ){
2213
if( crlfOk ){
2214
return 0; /* We don't want CR/LF warnings for this file. */
2215
}
2216
if( fHasLoneCrOnly ){
2217
zWarning = "CR line endings";
2218
}else if( fHasCrLfOnly ){
2219
zWarning = "CR/LF line endings";
2220
}else{
2221
zWarning = "mixed line endings";
2222
}
2223
zDisable = "\"crlf-glob\" setting";
2224
}else if( !sizeOk ){
2225
zWarning = "oversize";
2226
zIn = "file";
2227
}else{
2228
if( encodingOk ){
2229
return 0; /* We don't want encoding warnings for this file. */
2230
}
2231
zWarning = "Unicode";
2232
zDisable = "\"encoding-glob\" setting";
2233
}
2234
file_relative_name(zFilename, &fname, 0);
2235
if( !sizeOk ){
2236
zMsg = mprintf(
2237
"%s is more than %,lld bytes in size.\n"
2238
"Commit anyhow (a=all/y/N)? ",
2239
blob_str(&fname), db_large_file_size());
2240
}else{
2241
zMsg = mprintf(
2242
"%s contains %s. Use --no-warnings or the %s to"
2243
" disable this warning.\n"
2244
"Commit anyhow (a=all/%sy/N)? ",
2245
blob_str(&fname), zWarning, zDisable, zConvert);
2246
}
2247
if( noPrompt==0 ){
2248
prompt_user(zMsg, &ans);
2249
cReply = blob_str(&ans)[0];
2250
blob_reset(&ans);
2251
}else if( noPrompt==2 ){
2252
cReply = 'Y';
2253
}else{
2254
cReply = 'N';
2255
}
2256
fossil_free(zMsg);
2257
if( cReply=='a' || cReply=='A' ){
2258
allOk = 1;
2259
}else if( *zConvert && (cReply=='c' || cReply=='C') ){
2260
char *zOrig = file_newname(zFilename, "original", 1);
2261
FILE *f;
2262
blob_write_to_file(pContent, zOrig);
2263
fossil_free(zOrig);
2264
f = fossil_fopen(zFilename, "wb");
2265
if( f==0 ){
2266
fossil_warning("cannot open %s for writing", zFilename);
2267
}else{
2268
if( fUnicode ){
2269
int bomSize;
2270
const unsigned char *bom = get_utf8_bom(&bomSize);
2271
fwrite(bom, 1, bomSize, f);
2272
blob_to_utf8_no_bom(pContent, 0);
2273
}else if( fHasInvalidUtf8 ){
2274
blob_cp1252_to_utf8(pContent);
2275
}
2276
if( fHasAnyCr ){
2277
blob_to_lf_only(pContent);
2278
}
2279
fwrite(blob_buffer(pContent), 1, blob_size(pContent), f);
2280
fclose(f);
2281
}
2282
return 1;
2283
}else if( cReply!='y' && cReply!='Y' ){
2284
fossil_fatal("Abandoning commit due to %s %s %s",
2285
zWarning, zIn, blob_str(&fname));
2286
}else if( noPrompt==2 ){
2287
if( pReason ){
2288
blob_append(pReason, zWarning, -1);
2289
}
2290
return 1;
2291
}
2292
blob_reset(&fname);
2293
}
2294
return 0;
2295
}
2296
2297
/*
2298
** COMMAND: test-commit-warning
2299
**
2300
** Usage: %fossil test-commit-warning ?OPTIONS?
2301
**
2302
** Check each file in the check-out, including unmodified ones, using all
2303
** the pre-commit checks.
2304
**
2305
** Options:
2306
** --no-settings Do not consider any glob settings.
2307
** -v|--verbose Show per-file results for all pre-commit checks.
2308
**
2309
** See also: commit, extras
2310
*/
2311
void test_commit_warning(void){
2312
int rc = 0;
2313
int noSettings;
2314
int verboseFlag;
2315
i64 mxSize;
2316
Stmt q;
2317
noSettings = find_option("no-settings",0,0)!=0;
2318
verboseFlag = find_option("verbose","v",0)!=0;
2319
verify_all_options();
2320
db_must_be_within_tree();
2321
mxSize = db_large_file_size();
2322
db_prepare(&q,
2323
"SELECT %Q || pathname, pathname, %s, %s, %s FROM vfile"
2324
" WHERE NOT deleted",
2325
g.zLocalRoot,
2326
glob_expr("pathname", noSettings ? 0 : db_get("crlf-glob",
2327
db_get("crnl-glob",""))),
2328
glob_expr("pathname", noSettings ? 0 : db_get("binary-glob","")),
2329
glob_expr("pathname", noSettings ? 0 : db_get("encoding-glob",""))
2330
);
2331
while( db_step(&q)==SQLITE_ROW ){
2332
const char *zFullname;
2333
const char *zName;
2334
Blob content;
2335
Blob reason;
2336
int crlfOk, binOk, encodingOk, sizeOk;
2337
int fileRc;
2338
2339
zFullname = db_column_text(&q, 0);
2340
zName = db_column_text(&q, 1);
2341
crlfOk = db_column_int(&q, 2);
2342
binOk = db_column_int(&q, 3);
2343
encodingOk = db_column_int(&q, 4);
2344
sizeOk = mxSize<=0 || file_size(zFullname, ExtFILE)<=mxSize;
2345
blob_zero(&content);
2346
blob_read_from_file(&content, zFullname, RepoFILE);
2347
blob_zero(&reason);
2348
fileRc = commit_warning(&content, crlfOk, binOk, encodingOk, sizeOk, 2,
2349
zFullname, &reason);
2350
if( fileRc || verboseFlag ){
2351
fossil_print("%d\t%s\t%s\n", fileRc, zName, blob_str(&reason));
2352
}
2353
blob_reset(&reason);
2354
rc |= fileRc;
2355
}
2356
db_finalize(&q);
2357
fossil_print("%d\n", rc);
2358
}
2359
2360
/*
2361
** qsort() comparison routine for an array of pointers to strings.
2362
*/
2363
static int tagCmp(const void *a, const void *b){
2364
char **pA = (char**)a;
2365
char **pB = (char**)b;
2366
return fossil_strcmp(pA[0], pB[0]);
2367
}
2368
2369
/*
2370
** SETTING: verify-comments width=8 default=on
2371
**
2372
** This setting determines how much sanity checking, if any, the
2373
** "fossil commit" and "fossil amend" commands do against check-in
2374
** comments. Recognized values:
2375
**
2376
** on (Default) Check for bad syntax and/or broken hyperlinks
2377
** in check-in comments and offer the user a chance to
2378
** continue editing for interactive sessions, or simply
2379
** abort the commit if the comment was entered using -m or -M
2380
**
2381
** off Do not do syntax checking of any kind
2382
**
2383
** preview Do all the same checks as "on" but also always preview the
2384
** check-in comment to the user during interactive sessions
2385
** even if no obvious errors are found, and provide an
2386
** opportunity to accept or re-edit
2387
*/
2388
2389
#if INTERFACE
2390
#define COMCK_MARKUP 0x01 /* Check for mistakes */
2391
#define COMCK_PREVIEW 0x02 /* Always preview, even if no issues found */
2392
#endif /* INTERFACE */
2393
2394
/*
2395
** Check for possible formatting errors in the comment string pComment.
2396
**
2397
** If issues are found, write an appropriate error notice, probably also
2398
** including the complete text of the comment formatted to highlight the
2399
** problem, to stdout and return non-zero. The return value is some
2400
** combination of the COMCK_* flags, depending on what went wrong.
2401
**
2402
** If no issues are seen, do not output anything and return zero.
2403
*/
2404
int verify_comment(Blob *pComment, int mFlags){
2405
Blob in, html;
2406
int mResult;
2407
int rc = mFlags & COMCK_PREVIEW;
2408
int wFlags;
2409
2410
if( mFlags==0 ) return 0;
2411
blob_init(&in, blob_str(pComment), -1);
2412
blob_init(&html, 0, 0);
2413
wFlags = wiki_convert_flags(0);
2414
wFlags &= ~WIKI_NOBADLINKS;
2415
wFlags |= WIKI_MARK;
2416
mResult = wiki_convert(&in, &html, wFlags);
2417
if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP;
2418
if( rc ){
2419
int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM;
2420
Blob txt;
2421
if( terminal_is_vt100() ) htot |= HTOT_VT100;
2422
blob_init(&txt, 0, 0);
2423
html_to_plaintext(blob_str(&html), &txt, htot);
2424
if( rc & COMCK_MARKUP ){
2425
fossil_print("Possible format errors in the check-in comment:\n\n ");
2426
}else{
2427
fossil_print("Preview of the check-in comment:\n\n ");
2428
}
2429
if( wFlags & WIKI_NEWLINE ){
2430
Blob line;
2431
char *zIndent = "";
2432
while( blob_line(&txt, &line) ){
2433
fossil_print("%s%b", zIndent, &line);
2434
zIndent = " ";
2435
}
2436
fossil_print("\n");
2437
}else{
2438
comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
2439
}
2440
fossil_print("\n");
2441
fflush(stdout);
2442
blob_reset(&txt);
2443
}
2444
blob_reset(&html);
2445
blob_reset(&in);
2446
return rc;
2447
}
2448
2449
/*
2450
** COMMAND: ci#
2451
** COMMAND: commit
2452
**
2453
** Usage: %fossil commit ?OPTIONS? ?FILE...?
2454
** or: %fossil ci ?OPTIONS? ?FILE...?
2455
**
2456
** Create a new check-in containing all of the changes in the current
2457
** check-out. All changes are committed unless some subset of files
2458
** is specified on the command line, in which case only the named files
2459
** become part of the new check-in.
2460
**
2461
** You will be prompted to enter a check-in comment unless the comment
2462
** has been specified on the command-line using "-m" or "-M". The
2463
** text editor used is determined by the "editor" setting, or by the
2464
** "VISUAL" or "EDITOR" environment variables. Commit message text is
2465
** interpreted as fossil-wiki format. Potentially misformatted check-in
2466
** comment text is detected and reported unless the --no-verify-comment
2467
** option is used.
2468
**
2469
** The --branch option followed by a branch name causes the new
2470
** check-in to be placed in a newly-created branch with name specified.
2471
**
2472
** A check-in is not permitted to fork unless the --allow-fork option
2473
** appears. An empty check-in (i.e. with nothing changed) is not
2474
** allowed unless the --allow-empty option appears. A check-in may not
2475
** be older than its ancestor unless the --allow-older option appears.
2476
** If any files in the check-in appear to contain unresolved merge
2477
** conflicts, the check-in will not be allowed unless the
2478
** --allow-conflict option is present. In addition, the entire
2479
** check-in process may be aborted if a file contains content that
2480
** appears to be binary, Unicode text, or text with CR/LF line endings
2481
** unless the interactive user chooses to proceed. If there is no
2482
** interactive user or these warnings should be skipped for some other
2483
** reason, the --no-warnings option may be used. A check-in is not
2484
** allowed against a closed leaf.
2485
**
2486
** The --private option creates a private check-in that is never synced.
2487
** Children of private check-ins are automatically private.
2488
**
2489
** The --tag option applies the symbolic tag name to the check-in.
2490
** The --tag option can be repeated to assign multiple tags to a check-in.
2491
** For example: "... --tag release --tag version-1.2.3 ..."
2492
**
2493
** Options:
2494
** --allow-conflict Allow unresolved merge conflicts
2495
** --allow-empty Allow a commit with no changes
2496
** --allow-fork Allow the commit to fork
2497
** --allow-older Allow a commit older than its ancestor
2498
** --baseline Use a baseline manifest in the commit process
2499
** --bgcolor COLOR Apply COLOR to this one check-in only
2500
** --branch NEW-BRANCH-NAME Check in to this new branch
2501
** --branchcolor COLOR Apply given COLOR to the branch
2502
** --close Close the branch being committed
2503
** --date-override DATETIME Make DATETIME the time of the check-in.
2504
** Useful when importing historical check-ins
2505
** from another version control system.
2506
** --delta Use a delta manifest in the commit process
2507
** --editor NAME Text editor to use for check-in comment.
2508
** --hash Verify file status using hashing rather
2509
** than relying on filesystem mtimes
2510
** --if-changes Make this command a silent no-op if there
2511
** are no changes
2512
** --ignore-clock-skew If a clock skew is detected, ignore it and
2513
** behave as if the user had entered 'yes' to
2514
** the question of whether to proceed despite
2515
** the skew.
2516
** --ignore-oversize Do not warn the user about oversized files
2517
** --integrate Close all merged-in branches
2518
** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment
2519
** -M|--message-file FILE Read the check-in comment from FILE
2520
** -n|--dry-run Do not actually create a new check-in. Just
2521
** show what would have happened. For debugging.
2522
** -v|--verbose Show a diff in the commit message prompt
2523
** --no-prompt This option disables prompting the user for
2524
** input and assumes an answer of 'No' for every
2525
** question.
2526
** --no-warnings Omit all warnings about file contents
2527
** --no-verify Do not run before-commit hooks
2528
** --no-verify-comment Do not validate the check-in comment
2529
** --nosign Do not attempt to sign this commit with gpg
2530
** --nosync Do not auto-sync prior to committing
2531
** --override-lock Allow a check-in even though parent is locked
2532
** --private Never sync the resulting check-in and make
2533
** all descendants private too.
2534
** --proxy PROXY Use PROXY as http proxy during sync operation
2535
** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated.
2536
** --trace Debug tracing
2537
** --user-override USER Record USER as the login that created the
2538
** new check-in, rather that the current user.
2539
**
2540
** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]]
2541
*/
2542
void commit_cmd(void){
2543
int hasChanges; /* True if unsaved changes exist */
2544
int vid; /* blob-id of parent version */
2545
int nrid; /* blob-id of a modified file */
2546
int nvid; /* Blob-id of the new check-in */
2547
Blob comment; /* Check-in comment */
2548
const char *zComment; /* Check-in comment */
2549
Stmt q; /* Various queries */
2550
char *zUuid; /* Hash of the new check-in */
2551
int useHash = 0; /* True to verify file status using hashing */
2552
int noSign = 0; /* True to omit signing the manifest using GPG */
2553
int privateFlag = 0; /* True if the --private option is present */
2554
int privateParent = 0; /* True if the parent check-in is private */
2555
int isAMerge = 0; /* True if checking in a merge */
2556
int noWarningFlag = 0; /* True if skipping all warnings */
2557
int noVerify = 0; /* Do not run before-commit hooks */
2558
int bTrace = 0; /* Debug tracing */
2559
int noPrompt = 0; /* True if skipping all prompts */
2560
int forceFlag = 0; /* Undocumented: Disables all checks */
2561
int forceDelta = 0; /* Force a delta-manifest */
2562
int forceBaseline = 0; /* Force a baseline-manifest */
2563
int allowConflict = 0; /* Allow unresolve merge conflicts */
2564
int allowEmpty = 0; /* Allow a commit with no changes */
2565
int onlyIfChanges = 0; /* No-op if there are no changes */
2566
int allowFork = 0; /* Allow the commit to fork */
2567
int allowOlder = 0; /* Allow a commit older than its ancestor */
2568
int noVerifyCom = 0; /* Allow suspicious check-in comments */
2569
int useCksum; /* True if checksums should be computed and verified */
2570
int dryRunFlag; /* True for a test run. Debugging only */
2571
CheckinInfo sCiInfo; /* Information about this check-in */
2572
const char *zComFile; /* Read commit message from this file */
2573
int nTag = 0; /* Number of --tag arguments */
2574
const char *zTag; /* A single --tag argument */
2575
ManifestFile *pFile; /* File structure in the manifest */
2576
Manifest *pManifest; /* Manifest structure */
2577
Blob manifest; /* Manifest in baseline form */
2578
Blob cksum1, cksum2; /* Before and after commit checksums */
2579
Blob cksum1b; /* Checksum recorded in the manifest */
2580
int szD; /* Size of the delta manifest */
2581
int szB; /* Size of the baseline manifest */
2582
int nConflict = 0; /* Number of unresolved merge conflicts */
2583
int abortCommit = 0; /* Abort the commit due to text format conversions */
2584
Blob ans; /* Answer to continuation prompts */
2585
char cReply; /* First character of ans */
2586
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
2587
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
2588
int mxSize;
2589
char *zCurBranch = 0; /* The current branch name of checkout */
2590
char *zNewBranch = 0; /* The branch name after update */
2591
int ckComFlgs; /* Flags passed to verify_comment() */
2592
2593
memset(&sCiInfo, 0, sizeof(sCiInfo));
2594
url_proxy_options();
2595
/* --sha1sum is an undocumented alias for --hash for backwards compatibility */
2596
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2597
noSign = find_option("nosign",0,0)!=0;
2598
if( find_option("nosync",0,0) ) g.fNoSync = 1;
2599
privateFlag = find_option("private",0,0)!=0;
2600
forceDelta = find_option("delta",0,0)!=0;
2601
forceBaseline = find_option("baseline",0,0)!=0;
2602
db_must_be_within_tree();
2603
if( db_get_boolean("dont-commit",0) ){
2604
fossil_fatal("committing is prohibited: the 'dont-commit' option is set");
2605
}
2606
if( forceDelta ){
2607
if( forceBaseline ){
2608
fossil_fatal("cannot use --delta and --baseline together");
2609
}
2610
if( db_get_boolean("forbid-delta-manifests",0) ){
2611
fossil_fatal("delta manifests are prohibited in this repository");
2612
}
2613
}
2614
dryRunFlag = find_option("dry-run","n",0)!=0;
2615
if( !dryRunFlag ){
2616
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
2617
}
2618
zComment = find_option("comment","m",1);
2619
forceFlag = find_option("force", "f", 0)!=0;
2620
allowConflict = find_option("allow-conflict",0,0)!=0;
2621
allowEmpty = find_option("allow-empty",0,0)!=0;
2622
noVerifyCom = find_option("no-verify-comment",0,0)!=0;
2623
onlyIfChanges = find_option("if-changes",0,0)!=0;
2624
allowFork = find_option("allow-fork",0,0)!=0;
2625
if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
2626
allowOlder = find_option("allow-older",0,0)!=0;
2627
noPrompt = find_option("no-prompt", 0, 0)!=0;
2628
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
2629
noVerify = find_option("no-verify",0,0)!=0;
2630
bTrace = find_option("trace",0,0)!=0;
2631
sCiInfo.zBranch = find_option("branch","b",1);
2632
2633
/* NB: the --bgcolor and --branchcolor flags still work, but are
2634
** now undocumented, to discourage their use. --mimetype has never
2635
** been used for anything, so also leave it undocumented */
2636
sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/
2637
sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/
2638
sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/
2639
2640
sCiInfo.closeFlag = find_option("close",0,0)!=0;
2641
sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
2642
sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
2643
while( (zTag = find_option("tag",0,1))!=0 ){
2644
if( zTag[0]==0 ) continue;
2645
sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
2646
sizeof(char*)*(nTag+2));
2647
sCiInfo.azTag[nTag++] = zTag;
2648
sCiInfo.azTag[nTag] = 0;
2649
}
2650
zComFile = find_option("message-file", "M", 1);
2651
sCiInfo.zDateOvrd = find_option("date-override",0,1);
2652
sCiInfo.zUserOvrd = find_option("user-override",0,1);
2653
noSign = db_get_boolean("omitsign", 0)|noSign;
2654
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
2655
useCksum = db_get_boolean("repo-cksum", 1);
2656
bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
2657
mxSize = db_large_file_size();
2658
if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
2659
(void)fossil_text_editor();
2660
verify_all_options();
2661
2662
/* The --no-warnings flag and the --force flag each imply
2663
** the --no-verify-comment flag */
2664
if( noWarningFlag || forceFlag ){
2665
noVerifyCom = 1;
2666
}
2667
2668
/* Get the ID of the parent manifest artifact */
2669
vid = db_lget_int("checkout", 0);
2670
if( vid==0 ){
2671
useCksum = 1;
2672
if( privateFlag==0 && sCiInfo.zBranch==0 ) {
2673
sCiInfo.zBranch = db_main_branch();
2674
}
2675
}else{
2676
privateParent = content_is_private(vid);
2677
}
2678
2679
user_select();
2680
/*
2681
** Check that the user exists.
2682
*/
2683
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
2684
fossil_fatal("no such user: %s", g.zLogin);
2685
}
2686
2687
/*
2688
** Detect if the branch name has changed from the parent check-in
2689
** and prompt if necessary
2690
**/
2691
zCurBranch = db_text(0,
2692
" SELECT value FROM tagxref AS tx"
2693
" WHERE rid=(SELECT pid"
2694
" FROM tagxref LEFT JOIN event ON srcid=objid"
2695
" LEFT JOIN plink ON rid=cid"
2696
" WHERE rid=%d AND tagxref.tagid=%d"
2697
" AND srcid!=origid"
2698
" AND tagtype=2 AND coalesce(euser,user)!=%Q)"
2699
" AND tx.tagid=%d",
2700
vid, TAG_BRANCH, g.zLogin, TAG_BRANCH
2701
);
2702
if( zCurBranch!=0 && zCurBranch[0]!=0
2703
&& forceFlag==0
2704
&& noPrompt==0
2705
){
2706
zNewBranch = branch_of_rid(vid);
2707
fossil_warning(
2708
"WARNING: The parent check-in [%.10s] has been moved from branch\n"
2709
" '%s' over to branch '%s'.",
2710
rid_to_uuid(vid), zCurBranch, zNewBranch
2711
);
2712
prompt_user("Commit anyway? (y/N) ", &ans);
2713
cReply = blob_str(&ans)[0];
2714
blob_reset(&ans);
2715
if( cReply!='y' && cReply!='Y' ){
2716
fossil_fatal("Abandoning commit because branch has changed");
2717
}
2718
fossil_free(zNewBranch);
2719
fossil_free(zCurBranch);
2720
zCurBranch = branch_of_rid(vid);
2721
}
2722
if( zCurBranch==0 ) zCurBranch = branch_of_rid(vid);
2723
2724
/* Track the "private" status */
2725
g.markPrivate = privateFlag || privateParent;
2726
if( privateFlag && !privateParent ){
2727
/* Apply default branch name ("private") and color ("orange") if not
2728
** specified otherwise on the command-line, and if the parent is not
2729
** already private. */
2730
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2731
}
2732
2733
/* Do not allow the creation of a new branch using an existing open
2734
** branch name unless the --force flag is used */
2735
if( sCiInfo.zBranch!=0
2736
&& !forceFlag
2737
&& fossil_strcmp(sCiInfo.zBranch,"private")!=0
2738
&& branch_is_open(sCiInfo.zBranch)
2739
){
2740
fossil_fatal("an open branch named \"%s\" already exists - use --force"
2741
" to override", sCiInfo.zBranch);
2742
}
2743
2744
/* Escape special characters in tags and put all tags in sorted order */
2745
if( nTag ){
2746
int i;
2747
for(i=0; i<nTag; i++) sCiInfo.azTag[i] = mprintf("%F", sCiInfo.azTag[i]);
2748
qsort((void*)sCiInfo.azTag, nTag, sizeof(sCiInfo.azTag[0]), tagCmp);
2749
}
2750
2751
hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0);
2752
if( hasChanges==0 && onlyIfChanges ){
2753
/* "fossil commit --if-changes" is a no-op if there are no changes. */
2754
return;
2755
}
2756
2757
/*
2758
** Autosync if autosync is enabled and this is not a private check-in.
2759
*/
2760
if( !g.markPrivate ){
2761
int syncFlags = SYNC_PULL;
2762
if( vid!=0 && !allowFork && !forceFlag ){
2763
syncFlags |= SYNC_CKIN_LOCK;
2764
}
2765
if( autosync_loop(syncFlags, 1, "commit") ){
2766
fossil_exit(1);
2767
}
2768
}
2769
2770
/* So that older versions of Fossil (that do not understand delta-
2771
** manifest) can continue to use this repository, and because
2772
** delta manifests are usually a bad idea unless the repository
2773
** has a really large number of files, do not create a new
2774
** delta-manifest unless this repository already contains one or more
2775
** delta-manifests, or unless the delta-manifest is explicitly requested
2776
** by the --delta option.
2777
**
2778
** The forbid-delta-manifests setting prevents new delta manifests,
2779
** even if the --delta option is used.
2780
**
2781
** If the remote repository sent an avoid-delta-manifests pragma on
2782
** the autosync above, then also forbid delta manifests, even if the
2783
** --delta option is specified. The remote repo will send the
2784
** avoid-delta-manifests pragma if its "forbid-delta-manifests"
2785
** setting is enabled.
2786
*/
2787
if( !(forceDelta || db_get_boolean("seen-delta-manifest",0))
2788
|| db_get_boolean("forbid-delta-manifests",0)
2789
|| g.bAvoidDeltaManifests
2790
){
2791
forceBaseline = 1;
2792
}
2793
2794
/* Require confirmation to continue with the check-in if there is
2795
** clock skew. This helps to prevent timewarps.
2796
*/
2797
if( g.clockSkewSeen ){
2798
if( bIgnoreSkew!=0 ){
2799
cReply = 'y';
2800
fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
2801
}else if( !noPrompt ){
2802
prompt_user("continue in spite of time skew (y/N)? ", &ans);
2803
cReply = blob_str(&ans)[0];
2804
blob_reset(&ans);
2805
}else{
2806
fossil_print("Abandoning commit due to time skew\n");
2807
cReply = 'N';
2808
}
2809
if( cReply!='y' && cReply!='Y' ){
2810
fossil_exit(1);
2811
}
2812
}
2813
2814
/* There are two ways this command may be executed. If there are
2815
** no arguments following the word "commit", then all modified files
2816
** in the checked-out directory are committed. If one or more arguments
2817
** follows "commit", then only those files are committed.
2818
**
2819
** After the following function call has returned, the Global.aCommitFile[]
2820
** array is allocated to contain the "id" field from the vfile table
2821
** for each file to be committed. Or, if aCommitFile is NULL, all files
2822
** should be committed.
2823
*/
2824
if( select_commit_files() ){
2825
if( !noPrompt ){
2826
prompt_user("continue (y/N)? ", &ans);
2827
cReply = blob_str(&ans)[0];
2828
blob_reset(&ans);
2829
}else{
2830
cReply = 'N';
2831
}
2832
if( cReply!='y' && cReply!='Y' ){
2833
fossil_exit(1);
2834
}
2835
}
2836
isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0 OR id<-2");
2837
if( g.aCommitFile && isAMerge ){
2838
fossil_fatal("cannot do a partial commit of a merge");
2839
}
2840
2841
/* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA"
2842
** will generate a manifest that has two fileA entries, which is illegal.
2843
** When you think about it, the sequence above makes no sense. So detect
2844
** it and disallow it. Ticket [0ff64b0a5fc8].
2845
*/
2846
if( g.aCommitFile ){
2847
db_prepare(&q,
2848
"SELECT v1.pathname, v2.pathname"
2849
" FROM vfile AS v1, vfile AS v2"
2850
" WHERE is_selected(v1.id)"
2851
" AND v2.origname IS NOT NULL"
2852
" AND v2.origname=v1.pathname"
2853
" AND NOT is_selected(v2.id)");
2854
if( db_step(&q)==SQLITE_ROW ){
2855
const char *zFrom = db_column_text(&q, 0);
2856
const char *zTo = db_column_text(&q, 1);
2857
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
2858
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
2859
}
2860
db_finalize(&q);
2861
}
2862
2863
db_begin_transaction();
2864
db_record_repository_filename(0);
2865
if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
2866
fossil_fatal("nothing has changed; use --allow-empty to override");
2867
}
2868
2869
/* If none of the files that were named on the command line have
2870
** been modified, bail out now unless the --allow-empty or --force
2871
** flags is used.
2872
*/
2873
if( g.aCommitFile
2874
&& !allowEmpty
2875
&& !forceFlag
2876
&& !db_exists(
2877
"SELECT 1 FROM vfile "
2878
" WHERE is_selected(id)"
2879
" AND (chnged OR deleted OR rid=0 OR pathname!=origname)")
2880
){
2881
fossil_fatal("none of the selected files have changed; use "
2882
"--allow-empty to override.");
2883
}
2884
2885
/* This loop checks for potential forks and for check-ins against a
2886
** closed branch. The checks are repeated once after interactive
2887
** check-in comment editing.
2888
*/
2889
do{
2890
/*
2891
** Do not allow a commit that will cause a fork unless the --allow-fork
2892
** or --force flags is used, or unless this is a private check-in.
2893
** The initial commit MUST have tags "trunk" and "sym-trunk".
2894
*/
2895
if( sCiInfo.zBranch==0
2896
&& allowFork==0
2897
&& forceFlag==0
2898
&& g.markPrivate==0
2899
&& (vid==0 || !is_a_leaf(vid) || g.ckinLockFail)
2900
){
2901
if( g.ckinLockFail ){
2902
fossil_fatal("Might fork due to a check-in race with user \"%s\"\n"
2903
"Try \"update\" first, or --branch, or "
2904
"use --override-lock",
2905
g.ckinLockFail);
2906
}else{
2907
fossil_fatal("Would fork. \"update\" first or use --branch or "
2908
"--allow-fork.");
2909
}
2910
}
2911
2912
/*
2913
** Do not allow a commit against a closed leaf unless the commit
2914
** ends up on a different branch.
2915
*/
2916
if(
2917
/* parent check-in has the "closed" tag... */
2918
leaf_is_closed(vid)
2919
/* ... and the new check-in has no --branch option or the --branch
2920
** option does not actually change the branch */
2921
&& (sCiInfo.zBranch==0
2922
|| db_exists("SELECT 1 FROM tagxref"
2923
" WHERE tagid=%d AND rid=%d AND tagtype>0"
2924
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
2925
){
2926
fossil_fatal("cannot commit against a closed leaf");
2927
}
2928
2929
/* Require confirmation to continue with the check-in if the branch
2930
** has changed and the committer did not provide the same branch
2931
*/
2932
zNewBranch = branch_of_rid(vid);
2933
if( fossil_strcmp(zCurBranch, zNewBranch)!=0
2934
&& fossil_strcmp(sCiInfo.zBranch, zNewBranch)!=0
2935
&& forceFlag==0
2936
&& noPrompt==0
2937
){
2938
fossil_warning("parent check-in [%.10s] branch changed from '%s' to '%s'",
2939
rid_to_uuid(vid), zCurBranch, zNewBranch);
2940
prompt_user("continue (y/N)? ", &ans);
2941
cReply = blob_str(&ans)[0];
2942
blob_reset(&ans);
2943
if( cReply!='y' && cReply!='Y' ){
2944
fossil_fatal("Abandoning commit because branch has changed");
2945
}
2946
fossil_free(zCurBranch);
2947
zCurBranch = branch_of_rid(vid);
2948
}
2949
fossil_free(zNewBranch);
2950
2951
/* Always exit the loop on the second pass */
2952
if( bRecheck ) break;
2953
2954
2955
/* Figure out how much comment verification is requested */
2956
if( noVerifyCom ){
2957
ckComFlgs = 0;
2958
}else{
2959
const char *zVerComs = db_get("verify-comments","on");
2960
if( is_false(zVerComs) ){
2961
ckComFlgs = 0;
2962
}else if( strcmp(zVerComs,"preview")==0 ){
2963
ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
2964
}else{
2965
ckComFlgs = COMCK_MARKUP;
2966
}
2967
}
2968
2969
/* Get the check-in comment. This might involve prompting the
2970
** user for the check-in comment, in which case we should resync
2971
** to renew the check-in lock and repeat the checks for conflicts.
2972
*/
2973
if( zComment ){
2974
blob_zero(&comment);
2975
blob_append(&comment, zComment, -1);
2976
ckComFlgs &= ~COMCK_PREVIEW;
2977
if( verify_comment(&comment, ckComFlgs) ){
2978
fossil_fatal("Commit aborted; "
2979
"use --no-verify-comment to override");
2980
}
2981
}else if( zComFile ){
2982
blob_zero(&comment);
2983
blob_read_from_file(&comment, zComFile, ExtFILE);
2984
blob_to_utf8_no_bom(&comment, 1);
2985
ckComFlgs &= ~COMCK_PREVIEW;
2986
if( verify_comment(&comment, ckComFlgs) ){
2987
fossil_fatal("Commit aborted; "
2988
"use --no-verify-comment to override");
2989
}
2990
}else if( !noPrompt ){
2991
while( 1/*exit-by-break*/ ){
2992
int rc;
2993
char *zInit;
2994
zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2995
prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2996
db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2997
if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
2998
if( rc==COMCK_PREVIEW ){
2999
prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans);
3000
}else{
3001
prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
3002
}
3003
cReply = blob_str(&ans)[0];
3004
cReply = fossil_tolower(cReply);
3005
blob_reset(&ans);
3006
if( cReply=='a' ){
3007
fossil_fatal("Commit aborted.");
3008
}
3009
if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
3010
fossil_free(zInit);
3011
continue;
3012
}
3013
}
3014
if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
3015
prompt_user("unchanged check-in comment. continue (y/N)? ", &ans);
3016
cReply = blob_str(&ans)[0];
3017
blob_reset(&ans);
3018
if( cReply!='y' && cReply!='Y' ){
3019
fossil_fatal("Commit aborted.");
3020
}
3021
}
3022
fossil_free(zInit);
3023
break;
3024
}
3025
3026
db_end_transaction(0);
3027
db_begin_transaction();
3028
if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
3029
/* Do another auto-pull, renewing the check-in lock. Then set
3030
** bRecheck so that we loop back above to verify that the check-in
3031
** is still not against a closed branch and still won't fork. */
3032
int syncFlags = SYNC_PULL|SYNC_CKIN_LOCK;
3033
if( autosync_loop(syncFlags, 1, "commit") ){
3034
fossil_fatal("Auto-pull failed. Commit aborted.");
3035
}
3036
bRecheck = 1;
3037
}
3038
}else{
3039
blob_zero(&comment);
3040
}
3041
}while( bRecheck );
3042
3043
if( blob_size(&comment)==0 ){
3044
if( !dryRunFlag ){
3045
if( !noPrompt ){
3046
prompt_user("empty check-in comment. continue (y/N)? ", &ans);
3047
cReply = blob_str(&ans)[0];
3048
blob_reset(&ans);
3049
}else{
3050
cReply = 'N';
3051
}
3052
if( cReply!='y' && cReply!='Y' ){
3053
fossil_fatal("Abandoning commit due to empty check-in comment\n");
3054
}
3055
}
3056
}
3057
3058
if( !noVerify && hook_exists("before-commit") ){
3059
/* Run before-commit hooks */
3060
char *zAuxFile;
3061
zAuxFile = prepare_commit_description_file(
3062
&sCiInfo, vid, &comment, dryRunFlag);
3063
if( zAuxFile ){
3064
int rc = hook_run("before-commit",zAuxFile,bTrace);
3065
file_delete(zAuxFile);
3066
fossil_free(zAuxFile);
3067
if( rc ){
3068
fossil_fatal("Before-commit hook failed\n");
3069
}
3070
}
3071
}
3072
3073
/*
3074
** Step 1: Compute an aggregate MD5 checksum over the disk image
3075
** of every file in vid. The file names are part of the checksum.
3076
** The resulting checksum is the same as is expected on the R-card
3077
** of a manifest.
3078
*/
3079
if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1);
3080
3081
/* Step 2: Insert records for all modified files into the blob
3082
** table. If there were arguments passed to this command, only
3083
** the identified files are inserted (if they have been modified).
3084
*/
3085
db_prepare(&q,
3086
"SELECT id, %Q || pathname, mrid, %s, %s, %s FROM vfile "
3087
"WHERE chnged<>0 AND NOT deleted AND is_selected(id)",
3088
g.zLocalRoot,
3089
glob_expr("pathname", db_get("crlf-glob",db_get("crnl-glob",""))),
3090
glob_expr("pathname", db_get("binary-glob","")),
3091
glob_expr("pathname", db_get("encoding-glob",""))
3092
);
3093
while( db_step(&q)==SQLITE_ROW ){
3094
int id, rid;
3095
const char *zFullname;
3096
Blob content;
3097
int crlfOk, binOk, encodingOk, sizeOk;
3098
3099
id = db_column_int(&q, 0);
3100
zFullname = db_column_text(&q, 1);
3101
rid = db_column_int(&q, 2);
3102
crlfOk = db_column_int(&q, 3);
3103
binOk = db_column_int(&q, 4);
3104
encodingOk = db_column_int(&q, 5);
3105
sizeOk = mxSize<=0 || file_size(zFullname, ExtFILE)<=mxSize;
3106
3107
blob_zero(&content);
3108
blob_read_from_file(&content, zFullname, RepoFILE);
3109
/* Do not emit any warnings when they are disabled. */
3110
if( !noWarningFlag ){
3111
abortCommit |= commit_warning(&content, crlfOk, binOk,
3112
encodingOk, sizeOk, noPrompt,
3113
zFullname, 0);
3114
}
3115
if( contains_merge_marker(&content) ){
3116
Blob fname; /* Relative pathname of the file */
3117
3118
nConflict++;
3119
file_relative_name(zFullname, &fname, 0);
3120
fossil_print("possible unresolved merge conflict in %s\n",
3121
blob_str(&fname));
3122
blob_reset(&fname);
3123
}
3124
nrid = content_put(&content);
3125
blob_reset(&content);
3126
if( nrid!=rid ){
3127
if( rid>0 ){
3128
content_deltify(rid, &nrid, 1, 0);
3129
}
3130
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d, mhash=NULL WHERE id=%d",
3131
nrid,nrid,id);
3132
db_add_unsent(nrid);
3133
}
3134
}
3135
db_finalize(&q);
3136
if( nConflict && !allowConflict ){
3137
fossil_fatal("abort due to unresolved merge conflicts; "
3138
"use --allow-conflict to override");
3139
}else if( abortCommit ){
3140
fossil_fatal("one or more files were converted on your request; "
3141
"please re-test before committing");
3142
}
3143
3144
/* Create the new manifest */
3145
sCiInfo.pComment = &comment;
3146
sCiInfo.pCksum = useCksum ? &cksum1 : 0;
3147
sCiInfo.verifyDate = !allowOlder && !forceFlag;
3148
if( forceDelta ){
3149
blob_zero(&manifest);
3150
}else{
3151
create_manifest(&manifest, 0, 0, vid, &sCiInfo, &szB);
3152
}
3153
3154
/* See if a delta-manifest would be more appropriate */
3155
if( !forceBaseline ){
3156
const char *zBaselineUuid;
3157
Manifest *pParent;
3158
Manifest *pBaseline;
3159
pParent = manifest_get(vid, CFTYPE_MANIFEST, 0);
3160
if( pParent && pParent->zBaseline ){
3161
zBaselineUuid = pParent->zBaseline;
3162
pBaseline = manifest_get_by_name(zBaselineUuid, 0);
3163
}else{
3164
zBaselineUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
3165
pBaseline = pParent;
3166
}
3167
if( pBaseline ){
3168
Blob delta;
3169
create_manifest(&delta, zBaselineUuid, pBaseline, vid, &sCiInfo, &szD);
3170
/*
3171
** At this point, two manifests have been constructed, either of
3172
** which would work for this check-in. The first manifest (held
3173
** in the "manifest" variable) is a baseline manifest and the second
3174
** (held in variable named "delta") is a delta manifest. The
3175
** question now is: which manifest should we use?
3176
**
3177
** Let B be the number of F-cards in the baseline manifest and
3178
** let D be the number of F-cards in the delta manifest, plus one for
3179
** the B-card. (B is held in the szB variable and D is held in the
3180
** szD variable.) Assume that all delta manifests adds X new F-cards.
3181
** Then to minimize the total number of F- and B-cards in the repository,
3182
** we should use the delta manifest if and only if:
3183
**
3184
** D*D < B*X - X*X
3185
**
3186
** X is an unknown here, but for most repositories, we will not be
3187
** far wrong if we assume X=3.
3188
*/
3189
if( forceDelta || (szD*szD)<(szB*3-9) ){
3190
blob_reset(&manifest);
3191
manifest = delta;
3192
}else{
3193
blob_reset(&delta);
3194
}
3195
}else if( forceDelta ){
3196
fossil_fatal("unable to find a baseline-manifest for the delta");
3197
}
3198
}
3199
if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){
3200
if( !noPrompt ){
3201
prompt_user("unable to sign manifest. continue (y/N)? ", &ans);
3202
cReply = blob_str(&ans)[0];
3203
blob_reset(&ans);
3204
}else{
3205
cReply = 'N';
3206
}
3207
if( cReply!='y' && cReply!='Y' ){
3208
fossil_fatal("Abandoning commit due to manifest signing failure\n");
3209
}
3210
}
3211
3212
/* If the -n|--dry-run option is specified, output the manifest file
3213
** and rollback the transaction.
3214
*/
3215
if( dryRunFlag ){
3216
blob_write_to_file(&manifest, "");
3217
}
3218
3219
nvid = content_put(&manifest);
3220
if( nvid==0 ){
3221
fossil_fatal("trouble committing manifest: %s", g.zErrMsg);
3222
}
3223
db_add_unsent(nvid);
3224
if( manifest_crosslink(nvid, &manifest,
3225
dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){
3226
fossil_fatal("%s", g.zErrMsg);
3227
}
3228
assert( blob_is_reset(&manifest) );
3229
content_deltify(vid, &nvid, 1, 0);
3230
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
3231
3232
db_prepare(&q, "SELECT mhash,merge FROM vmerge WHERE id=-4");
3233
while( db_step(&q)==SQLITE_ROW ){
3234
const char *zIntegrateUuid = db_column_text(&q, 0);
3235
if( is_a_leaf(db_column_int(&q, 1)) ){
3236
fossil_print("Closed: %s\n", zIntegrateUuid);
3237
}else{
3238
fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid);
3239
}
3240
}
3241
db_finalize(&q);
3242
3243
fossil_print("New_Version: %s\n", zUuid);
3244
3245
/* Update the vfile and vmerge tables */
3246
db_multi_exec(
3247
"DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);"
3248
"DELETE FROM vmerge;"
3249
"UPDATE vfile SET vid=%d;"
3250
"UPDATE vfile SET rid=mrid, mhash=NULL, chnged=0, deleted=0, origname=NULL"
3251
" WHERE is_selected(id);"
3252
, vid, nvid
3253
);
3254
db_set_checkout(nvid, !dryRunFlag);
3255
3256
/* Update the isexe and islink columns of the vfile table */
3257
db_prepare(&q,
3258
"UPDATE vfile SET isexe=:exec, islink=:link"
3259
" WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)"
3260
);
3261
db_bind_int(&q, ":vid", nvid);
3262
pManifest = manifest_get(nvid, CFTYPE_MANIFEST, 0);
3263
manifest_file_rewind(pManifest);
3264
while( (pFile = manifest_file_next(pManifest, 0)) ){
3265
db_bind_int(&q, ":exec", pFile->zPerm && strstr(pFile->zPerm, "x"));
3266
db_bind_int(&q, ":link", pFile->zPerm && strstr(pFile->zPerm, "l"));
3267
db_bind_text(&q, ":path", pFile->zName);
3268
db_step(&q);
3269
db_reset(&q);
3270
}
3271
db_finalize(&q);
3272
manifest_destroy(pManifest);
3273
3274
if( useCksum ){
3275
/* Verify that the repository checksum matches the expected checksum
3276
** calculated before the check-in started (and stored as the R record
3277
** of the manifest file).
3278
*/
3279
vfile_aggregate_checksum_repository(nvid, &cksum2);
3280
if( blob_compare(&cksum1, &cksum2) ){
3281
vfile_compare_repository_to_disk(nvid);
3282
fossil_fatal("working check-out does not match what would have ended "
3283
"up in the repository: %b versus %b",
3284
&cksum1, &cksum2);
3285
}
3286
3287
/* Verify that the manifest checksum matches the expected checksum */
3288
vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
3289
if( blob_compare(&cksum1, &cksum1b) ){
3290
fossil_fatal("manifest checksum self-test failed: "
3291
"%b versus %b", &cksum1, &cksum1b);
3292
}
3293
if( blob_compare(&cksum1, &cksum2) ){
3294
fossil_fatal(
3295
"working check-out does not match manifest after commit: "
3296
"%b versus %b", &cksum1, &cksum2);
3297
}
3298
3299
/* Verify that the commit did not modify any disk images. */
3300
vfile_aggregate_checksum_disk(nvid, &cksum2);
3301
if( blob_compare(&cksum1, &cksum2) ){
3302
fossil_fatal("working check-out before and after commit does not match");
3303
}
3304
}
3305
3306
/* Clear the undo/redo stack */
3307
undo_reset();
3308
3309
/* Commit */
3310
db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
3311
db_multi_exec("PRAGMA repository.application_id=252006673;");
3312
db_multi_exec("PRAGMA localdb.application_id=252006674;");
3313
if( dryRunFlag ){
3314
db_end_transaction(1);
3315
return;
3316
}
3317
db_end_transaction(0);
3318
3319
if( !g.markPrivate ){
3320
int syncFlags = SYNC_PUSH | SYNC_PULL | SYNC_IFABLE;
3321
autosync_loop(syncFlags, 0, "commit");
3322
}
3323
if( count_nonbranch_children(vid)>1 ){
3324
fossil_print("**** warning: a fork has occurred *****\n");
3325
}else{
3326
leaf_ambiguity_warning(nvid,nvid);
3327
}
3328
}
3329

Keyboard Shortcuts

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