Fossil SCM

fossil-scm / src / update.c
Blame History Raw 1061 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 merge the changes in the current
19
** check-out into a different version and switch to that version.
20
*/
21
#include "config.h"
22
#include "update.h"
23
#include <assert.h>
24
25
/*
26
** Return true if artifact rid is a version
27
*/
28
int is_a_version(int rid){
29
return db_exists("SELECT 1 FROM event WHERE objid=%d AND type='ci'", rid);
30
}
31
32
/* This variable is set if we are doing an internal update. It is clear
33
** when running the "update" command.
34
*/
35
static int internalUpdate = 0;
36
static int internalConflictCnt = 0;
37
38
/*
39
** Do an update to version vid.
40
**
41
** Start an undo session but do not terminate it. Do not autosync.
42
*/
43
int update_to(int vid){
44
int savedArgc;
45
char **savedArgv;
46
char *newArgv[3];
47
newArgv[0] = g.argv[0];
48
newArgv[1] = "update";
49
newArgv[2] = 0;
50
savedArgv = g.argv;
51
savedArgc = g.argc;
52
g.argc = 2;
53
g.argv = newArgv;
54
internalUpdate = vid;
55
internalConflictCnt = 0;
56
update_cmd();
57
g.argc = savedArgc;
58
g.argv = savedArgv;
59
return internalConflictCnt;
60
}
61
62
/*
63
** COMMAND: update
64
**
65
** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...?
66
**
67
** Change the version of the current check-out to VERSION. Any
68
** uncommitted changes are retained and applied to the new check-out.
69
**
70
** The VERSION argument can be a specific version or tag or branch
71
** name. If the VERSION argument is omitted, then the leaf of the
72
** subtree that begins at the current version is used, if there is
73
** only a single leaf. VERSION can also be "current" to select the
74
** leaf of the current version or "latest" to select the most recent
75
** check-in.
76
**
77
** If one or more FILES are listed after the VERSION then only the
78
** named files are candidates to be updated, and any updates to them
79
** will be treated as edits to the current version. Using a directory
80
** name for one of the FILES arguments is the same as using every
81
** subdirectory and file beneath that directory.
82
**
83
** If FILES is omitted, all files in the current check-out are subject
84
** to being updated and the version of the current check-out is changed
85
** to VERSION. Any uncommitted changes are retained and applied to the
86
** new check-out.
87
**
88
** The -n or --dry-run option causes this command to do a "dry run".
89
** It prints out what would have happened but does not actually make
90
** any changes to the current check-out or the repository.
91
**
92
** The -v or --verbose option prints status information about
93
** unchanged files in addition to those files that actually do change.
94
**
95
** Options:
96
** --case-sensitive BOOL Override case-sensitive setting
97
** --debug Print debug information on stdout
98
** -n|--dry-run If given, display instead of run actions
99
** --force-missing Force update if missing content after sync
100
** -K|--keep-merge-files On merge conflict, retain the temporary files
101
** used for merging, named *-baseline, *-original,
102
** and *-merge.
103
** --latest Acceptable in place of VERSION, update to
104
** latest version
105
** --nosync Do not auto-sync prior to update
106
** --proxy PROXY Use PROXY as http proxy during sync operation
107
** --setmtime Set timestamps of all files to match their
108
** SCM-side times (the timestamp of the last
109
** check-in which modified them).
110
** -v|--verbose Print status information about all files
111
** -W|--width WIDTH Width of lines (default is to auto-detect).
112
** Must be more than 20 or 0 (= no limit,
113
** resulting in a single line per entry).
114
**
115
** See also: [[revert]]
116
*/
117
void update_cmd(void){
118
int vid; /* Current version */
119
int tid=0; /* Target version - version we are changing to */
120
Stmt q;
121
int latestFlag; /* --latest. Pick the latest version if true */
122
int dryRunFlag; /* -n or --dry-run. Do a dry run */
123
int verboseFlag; /* -v or --verbose. Output extra information */
124
int forceMissingFlag; /* --force-missing. Continue if missing content */
125
int debugFlag; /* --debug option */
126
int setmtimeFlag; /* --setmtime. Set mtimes on files */
127
int keepMergeFlag; /* True if --keep-merge-files is present */
128
int nChng; /* Number of file renames */
129
int *aChng; /* Array of file renames */
130
int i; /* Loop counter */
131
int nConflict = 0; /* Number of merge conflicts */
132
int nOverwrite = 0; /* Number of unmanaged files overwritten */
133
int nUpdate = 0; /* Number of changes of any kind */
134
int bNosync = 0; /* --nosync. Omit the auto-sync */
135
int width; /* Width of printed comment lines */
136
Stmt mtimeXfer; /* Statement to transfer mtimes */
137
const char *zWidth; /* Width option string value */
138
const char *zCurBrName; /* Current branch name */
139
const char *zNewBrName; /* New branch name */
140
const char *zBrChgMsg = ""; /* Message to display if branch changes */
141
142
if( !internalUpdate ){
143
undo_capture_command_line();
144
url_proxy_options();
145
}
146
zWidth = find_option("width","W",1);
147
if( zWidth ){
148
width = atoi(zWidth);
149
if( (width!=0) && (width<=20) ){
150
fossil_fatal("-W|--width value must be >20 or 0");
151
}
152
}else{
153
width = -1;
154
}
155
latestFlag = find_option("latest",0, 0)!=0;
156
dryRunFlag = find_option("dry-run","n",0)!=0;
157
if( !dryRunFlag ){
158
dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */
159
}
160
verboseFlag = find_option("verbose","v",0)!=0;
161
forceMissingFlag = find_option("force-missing",0,0)!=0;
162
debugFlag = find_option("debug",0,0)!=0;
163
setmtimeFlag = find_option("setmtime",0,0)!=0;
164
keepMergeFlag = find_option("keep-merge-files", "K",0)!=0;
165
bNosync = find_option("nosync",0,0)!=0;
166
167
/* We should be done with options.. */
168
verify_all_options();
169
170
db_must_be_within_tree();
171
vid = db_lget_int("checkout", 0);
172
zCurBrName = branch_of_rid(vid);
173
user_select();
174
if( !dryRunFlag && !internalUpdate && !bNosync ){
175
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){
176
fossil_fatal("update abandoned due to sync failure");
177
}
178
}
179
180
/* Create any empty directories now, as well as after the update,
181
** so changes in settings are reflected now */
182
if( !dryRunFlag ) ensure_empty_dirs_created(0);
183
184
if( internalUpdate ){
185
tid = internalUpdate;
186
}else if( g.argc>=3 ){
187
if( fossil_strcmp(g.argv[2], "current")==0 ){
188
/* If VERSION is "current", then use the same algorithm to find the
189
** target as if VERSION were omitted. */
190
}else if( fossil_strcmp(g.argv[2], "latest")==0 ){
191
/* If VERSION is "latest", then use the same algorithm to find the
192
** target as if VERSION were omitted and the --latest flag is present.
193
*/
194
latestFlag = 1;
195
}else{
196
tid = name_to_typed_rid(g.argv[2],"ci");
197
if( tid==0 || !is_a_version(tid) ){
198
fossil_fatal("no such check-in: %s", g.argv[2]);
199
}
200
}
201
}
202
203
/* If no VERSION is specified on the command-line, then look for a
204
** descendant of the current version. If there are multiple descendants,
205
** look for one from the same branch as the current version. If there
206
** are still multiple descendants, show them all and refuse to update
207
** until the user selects one.
208
*/
209
if( tid==0 ){
210
int closeCode = 1;
211
compute_leaves(vid, closeCode);
212
if( !db_exists("SELECT 1 FROM leaves") ){
213
closeCode = 0;
214
compute_leaves(vid, closeCode);
215
}
216
if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){
217
db_multi_exec(
218
"DELETE FROM leaves WHERE rid NOT IN"
219
" (SELECT leaves.rid FROM leaves, tagxref"
220
" WHERE leaves.rid=tagxref.rid AND tagxref.tagid=%d"
221
" AND tagxref.value==(SELECT value FROM tagxref"
222
" WHERE tagid=%d AND rid=%d))",
223
TAG_BRANCH, TAG_BRANCH, vid
224
);
225
if( db_int(0, "SELECT count(*) FROM leaves")>1 ){
226
compute_leaves(vid, closeCode);
227
db_prepare(&q,
228
"%s "
229
" AND event.objid IN leaves"
230
" ORDER BY event.mtime DESC",
231
timeline_query_for_tty()
232
);
233
print_timeline(&q, -100, width, 0, 0);
234
db_finalize(&q);
235
fossil_fatal("Multiple descendants");
236
}
237
}
238
tid = db_int(0, "SELECT rid FROM leaves, event"
239
" WHERE event.objid=leaves.rid"
240
" ORDER BY event.mtime DESC");
241
if( tid==0 ) tid = vid;
242
}
243
244
if( tid==0 ){
245
return;
246
}
247
248
db_begin_transaction();
249
db_multi_exec(
250
"CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
251
filename_collation()
252
);
253
vfile_check_signature(vid, CKSIG_ENOTFILE);
254
if( !dryRunFlag && !internalUpdate ) undo_begin();
255
if( load_vfile_from_rid(tid) && !forceMissingFlag ){
256
fossil_fatal("missing content, unable to update");
257
};
258
259
/*
260
** The record.fn field is used to match files against each other. The
261
** FV table contains one row for each unique filename in
262
** in the current check-out, the pivot, and the version being merged.
263
*/
264
db_multi_exec(
265
"DROP TABLE IF EXISTS fv;"
266
"CREATE TEMP TABLE fv("
267
" fn TEXT %s PRIMARY KEY," /* The filename relative to root */
268
" idv INTEGER," /* VFILE entry for current version */
269
" idt INTEGER," /* VFILE entry for target version */
270
" chnged BOOLEAN," /* True if current version has been edited */
271
" islinkv BOOLEAN," /* True if current file is a link */
272
" islinkt BOOLEAN," /* True if target file is a link */
273
" ridv INTEGER," /* Record ID for current version */
274
" ridt INTEGER," /* Record ID for target */
275
" isexe BOOLEAN," /* Does target have execute permission? */
276
" deleted BOOLEAN DEFAULT 0,"/* File marked by "rm" to become unmanaged */
277
" fnt TEXT %s" /* Filename of same file on target version */
278
");",
279
filename_collation(), filename_collation()
280
);
281
282
/* Add files found in the current version
283
*/
284
db_multi_exec(
285
"INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged,deleted)"
286
" SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged, deleted"
287
" FROM vfile WHERE vid=%d",
288
vid
289
);
290
291
/* Compute file name changes on V->T. Record name changes in files that
292
** have changed locally.
293
*/
294
if( vid ){
295
find_filename_changes(vid, tid, 1, &nChng, &aChng, debugFlag ? "V->T": 0);
296
if( nChng ){
297
for(i=0; i<nChng; i++){
298
db_multi_exec(
299
"UPDATE fv"
300
" SET fnt=(SELECT name FROM filename WHERE fnid=%d)"
301
" WHERE fn=(SELECT name FROM filename WHERE fnid=%d) AND chnged",
302
aChng[i*2+1], aChng[i*2]
303
);
304
}
305
fossil_free(aChng);
306
}
307
}
308
309
/* Add files found in the target version T but missing from the current
310
** version V.
311
*/
312
db_multi_exec(
313
"INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
314
" SELECT pathname, pathname, 0, 0, 0, 0, isexe, 0 FROM vfile"
315
" WHERE vid=%d"
316
" AND pathname %s NOT IN (SELECT fnt FROM fv)",
317
tid, filename_collation()
318
);
319
320
/*
321
** Compute the file version ids for T
322
*/
323
db_multi_exec(
324
"UPDATE fv SET"
325
" idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnt=pathname),0),"
326
" ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnt=pathname),0)",
327
tid, tid
328
);
329
330
/*
331
** Add islink information
332
*/
333
db_multi_exec(
334
"UPDATE fv SET"
335
" islinkv=coalesce((SELECT islink FROM vfile"
336
" WHERE vid=%d AND fnt=pathname),0),"
337
" islinkt=coalesce((SELECT islink FROM vfile"
338
" WHERE vid=%d AND fnt=pathname),0)",
339
vid, tid
340
);
341
342
343
if( debugFlag ){
344
db_prepare(&q,
345
"SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe,"
346
" islinkv, islinkt FROM fv"
347
);
348
while( db_step(&q)==SQLITE_ROW ){
349
fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d"
350
" islinkv=%d islinkt=%d\n",
351
db_column_int(&q, 0),
352
db_column_int(&q, 4),
353
db_column_int(&q, 5),
354
db_column_int(&q, 3),
355
db_column_int(&q, 6),
356
db_column_int(&q, 7),
357
db_column_int(&q, 8));
358
fossil_print(" fnv = [%s]\n", db_column_text(&q, 1));
359
fossil_print(" fnt = [%s]\n", db_column_text(&q, 2));
360
}
361
db_finalize(&q);
362
}
363
364
/* If FILES appear on the command-line, remove from the "fv" table
365
** every entry that is not named on the command-line or which is not
366
** in a directory named on the command-line.
367
*/
368
if( g.argc>=4 ){
369
Blob sql; /* SQL statement to purge unwanted entries */
370
Blob treename; /* Normalized filename */
371
int i; /* Loop counter */
372
const char *zSep; /* Term separator */
373
374
blob_zero(&sql);
375
blob_append(&sql, "DELETE FROM fv WHERE ", -1);
376
zSep = "";
377
for(i=3; i<g.argc; i++){
378
file_tree_name(g.argv[i], &treename, 0, 1);
379
if( file_isdir(g.argv[i], RepoFILE)==1 ){
380
if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){
381
blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ",
382
zSep /*safe-for-%s*/, blob_str(&treename));
383
}else{
384
blob_reset(&sql);
385
break;
386
}
387
}else{
388
blob_append_sql(&sql, "%sfn<>%Q ",
389
zSep /*safe-for-%s*/, blob_str(&treename));
390
}
391
zSep = "AND ";
392
blob_reset(&treename);
393
}
394
db_multi_exec("%s", blob_sql_text(&sql));
395
blob_reset(&sql);
396
}
397
398
/*
399
** Alter the content of the check-out so that it conforms with the
400
** target
401
*/
402
db_prepare(&q,
403
"SELECT fn, idv, ridv, idt, ridt, chnged, fnt,"
404
" isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1"
405
);
406
db_prepare(&mtimeXfer,
407
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
408
" WHERE id=:idt"
409
);
410
assert( g.zLocalRoot!=0 );
411
assert( strlen(g.zLocalRoot)>0 );
412
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
413
merge_info_init();
414
while( db_step(&q)==SQLITE_ROW ){
415
const char *zName = db_column_text(&q, 0); /* The filename from root */
416
int idv = db_column_int(&q, 1); /* VFILE entry for current */
417
int ridv = db_column_int(&q, 2); /* RecordID for current */
418
int idt = db_column_int(&q, 3); /* VFILE entry for target */
419
int ridt = db_column_int(&q, 4); /* RecordID for target */
420
int chnged = db_column_int(&q, 5); /* Current is edited */
421
const char *zNewName = db_column_text(&q,6);/* New filename */
422
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
423
int islinkv = db_column_int(&q, 8); /* Is current file is a link */
424
int islinkt = db_column_int(&q, 9); /* Is target file is a link */
425
int deleted = db_column_int(&q, 10); /* Marked for deletion */
426
char *zFullPath; /* Full pathname of the file */
427
char *zFullNewPath; /* Full pathname of dest */
428
char nameChng; /* True if the name changed */
429
const char *zOp = 0; /* Type of change. */
430
i64 sz = 0; /* Size of the file */
431
int nc = 0; /* Number of conflicts */
432
const char *zErrMsg = 0; /* Error message */
433
434
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
435
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
436
sz = file_size(zFullNewPath, ExtFILE);
437
nameChng = fossil_strcmp(zName, zNewName);
438
nUpdate++;
439
if( deleted ){
440
db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
441
}
442
if( idv>0 && ridv==0 && idt>0 && ridt>0 ){
443
/* Conflict. This file has been added to the current check-out
444
** but also exists in the target check-out. Use the current version.
445
*/
446
fossil_print("CONFLICT %s\n", zName);
447
nConflict++;
448
zOp = "CONFLICT";
449
nc = 1;
450
zErrMsg = "duplicate file";
451
}else if( idt>0 && idv==0 ){
452
/* File added in the target. */
453
if( file_isfile_or_link(zFullPath) ){
454
/* Name of backup file with Original content */
455
char *zOrig = file_newname(zFullPath, "original", 1);
456
/* Backup previously unmanaged file before being overwritten */
457
file_copy(zFullPath, zOrig);
458
fossil_print("ADD %s - overwrites an unmanaged file", zName);
459
if( !dryRunFlag ){
460
fossil_print(", original copy backed up as \"%s\"", zOrig);
461
file_delete(zFullPath);
462
}
463
fossil_print("\n");
464
fossil_free(zOrig);
465
nOverwrite++;
466
nc = 1;
467
zOp = "CONFLICT";
468
zErrMsg = "new file overwrites unmanaged file";
469
}else{
470
fossil_print("ADD %s\n", zName);
471
}
472
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
473
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
474
}else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){
475
/* The file is unedited. Change it to the target version */
476
if( deleted ){
477
fossil_print("UPDATE %s - change to unmanaged file\n", zName);
478
}else{
479
fossil_print("UPDATE %s\n", zName);
480
}
481
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
482
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
483
zOp = "UPDATE";
484
}else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
485
/* The file missing from the local check-out. Restore it to the
486
** version that appears in the target. */
487
fossil_print("UPDATE %s\n", zName);
488
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
489
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
490
zOp = "UPDATE";
491
}else if( idt==0 && idv>0 ){
492
if( ridv==0 ){
493
/* Added in current check-out. Continue to hold the file as
494
** as an addition */
495
db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
496
}else if( chnged ){
497
/* Edited locally but deleted from the target. Do not track the
498
** file but keep the edited version around. */
499
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
500
zName);
501
zOp = "CONFLICT";
502
zErrMsg = "edited locally but deleted by update";
503
nc = 1;
504
nConflict++;
505
}else{
506
fossil_print("REMOVE %s\n", zName);
507
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
508
if( !dryRunFlag ){
509
char *zDir;
510
file_delete(zFullPath);
511
zDir = file_dirname(zName);
512
while( zDir!=0 ){
513
char *zNext;
514
db_multi_exec("INSERT OR IGNORE INTO dir_to_delete(name)"
515
"VALUES(%Q)", zDir);
516
zNext = db_changes() ? file_dirname(zDir) : 0;
517
fossil_free(zDir);
518
zDir = zNext;
519
}
520
}
521
}
522
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
523
if( nameChng ){
524
fossil_print("MERGE %s -> %s\n", zName, zNewName);
525
}else{
526
fossil_print("MERGE %s\n", zName);
527
}
528
if( islinkv || islinkt ){
529
fossil_print("***** Cannot merge symlink %s\n", zNewName);
530
zOp = "CONFLICT";
531
nConflict++;
532
}else{
533
/* Merge the changes in the current tree into the target version */
534
Blob r, t, v;
535
int rc;
536
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
537
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
538
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
539
content_get(ridt, &t);
540
content_get(ridv, &v);
541
rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
542
if( rc>=0 ){
543
if( !dryRunFlag ){
544
blob_write_to_file(&r, zFullNewPath);
545
file_setexe(zFullNewPath, isexe);
546
}
547
if( rc>0 ){
548
nc = rc;
549
zOp = "CONFLICT";
550
zErrMsg = "merge conflicts";
551
fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
552
nConflict++;
553
}else{
554
zOp = "MERGE";
555
}
556
}else{
557
if( !dryRunFlag ){
558
if( !keepMergeFlag ){
559
/* Name of backup file with Original content */
560
char *zOrig = file_newname(zFullPath, "original", 1);
561
/* Backup non-mergeable binary file when --keep-merge-files is
562
not specified */
563
file_copy(zFullPath, zOrig);
564
fossil_free(zOrig);
565
}
566
blob_write_to_file(&t, zFullNewPath);
567
file_setexe(zFullNewPath, isexe);
568
}
569
fossil_print("***** Cannot merge binary file %s", zNewName);
570
if( !dryRunFlag ){
571
fossil_print(", original copy backed up locally");
572
}
573
fossil_print("\n");
574
nConflict++;
575
zOp = "ERROR";
576
zErrMsg = "cannot merge binary file";
577
nc = 1;
578
}
579
blob_reset(&v);
580
blob_reset(&t);
581
blob_reset(&r);
582
}
583
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
584
}else{
585
nUpdate--;
586
if( chnged ){
587
if( verboseFlag ) fossil_print("EDITED %s\n", zName);
588
}else{
589
db_bind_int(&mtimeXfer, ":idv", idv);
590
db_bind_int(&mtimeXfer, ":idt", idt);
591
db_step(&mtimeXfer);
592
db_reset(&mtimeXfer);
593
if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
594
}
595
}
596
if( zOp!=0 ){
597
db_multi_exec(
598
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
599
"VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)",
600
/* op */ zOp,
601
/* fnp */ zName,
602
/* ridp */ ridv,
603
/* fn */ zNewName,
604
/* sz */ sz,
605
/* fnm */ zName,
606
/* ridm */ ridt,
607
/* fnr */ zNewName,
608
/* nc */ nc,
609
/* msg */ zErrMsg
610
);
611
}
612
free(zFullPath);
613
free(zFullNewPath);
614
}
615
db_finalize(&q);
616
db_finalize(&mtimeXfer);
617
fossil_print("%.79c\n",'-');
618
zNewBrName = branch_of_rid(tid);
619
if( g.argc<3 && fossil_strcmp(zCurBrName, zNewBrName)!=0 ){
620
zBrChgMsg = mprintf(" Branch changed from %s to %s.",
621
zCurBrName, zNewBrName);
622
}
623
if( nUpdate==0 ){
624
show_common_info(tid, "checkout:", 1, 0);
625
fossil_print("%-13s None. Already up-to-date.%s\n", "changes:", zBrChgMsg);
626
}else{
627
fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid),
628
db_text("", "SELECT datetime(mtime) || ' UTC' FROM event "
629
" WHERE objid=%d", vid));
630
show_common_info(tid, "updated-to:", 1, 0);
631
fossil_print("%-13s %d file%s modified.%s\n", "changes:",
632
nUpdate, nUpdate>1 ? "s" : "", zBrChgMsg);
633
}
634
635
/* Report on conflicts
636
*/
637
if( !dryRunFlag ){
638
Stmt q;
639
int nMerge = 0;
640
db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
641
while( db_step(&q)==SQLITE_ROW ){
642
const char *zLabel = "merge";
643
switch( db_column_int(&q, 1) ){
644
case -1: zLabel = "cherrypick merge"; break;
645
case -2: zLabel = "backout merge"; break;
646
}
647
fossil_warning("uncommitted %s against %S.",
648
zLabel, db_column_text(&q, 0));
649
nMerge++;
650
}
651
db_finalize(&q);
652
leaf_ambiguity_warning(tid, tid);
653
654
if( nConflict ){
655
if( internalUpdate ){
656
internalConflictCnt = nConflict;
657
nConflict = 0;
658
}else{
659
fossil_warning("WARNING: %d merge conflicts", nConflict);
660
}
661
}
662
if( nOverwrite ){
663
fossil_warning("WARNING: %d unmanaged files were overwritten",
664
nOverwrite);
665
}
666
if( nMerge ){
667
fossil_warning("WARNING: %d uncommitted prior merges", nMerge);
668
}
669
}
670
671
/*
672
** Clean up the mid and pid VFILE entries. Then commit the changes.
673
*/
674
if( dryRunFlag ){
675
db_end_transaction(1); /* With --dry-run, rollback changes */
676
fossil_warning("\nREMINDER: this was a dry run -"
677
" no files were actually changed "
678
"(checkout is still %.10s).", rid_to_uuid(vid));
679
}else{
680
char *zPwd;
681
ensure_empty_dirs_created(1);
682
sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
683
file_rmdir_sql_function, 0, 0);
684
zPwd = file_getcwd(0,0);
685
db_multi_exec(
686
"SELECT rmdir(%Q||name) FROM dir_to_delete"
687
" WHERE (%Q||name)<>%Q ORDER BY name DESC",
688
g.zLocalRoot, g.zLocalRoot, zPwd
689
);
690
fossil_free(zPwd);
691
if( g.argc<=3 ){
692
/* All files updated. Shift the current check-out to the target. */
693
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
694
checkout_set_all_exe(tid);
695
db_set_checkout(tid, 1);
696
}else{
697
/* A subset of files have been checked out. Keep the current
698
** check-out unchanged. */
699
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
700
}
701
if( !internalUpdate ) undo_finish();
702
if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME);
703
db_end_transaction(0);
704
}
705
}
706
707
/*
708
** Create empty directories specified by the empty-dirs setting.
709
*/
710
void ensure_empty_dirs_created(int clearDirTable){
711
char *zEmptyDirs = db_get("empty-dirs", 0);
712
if( zEmptyDirs!=0 ){
713
int i;
714
Glob *pGlob = glob_create(zEmptyDirs);
715
716
for(i=0; pGlob!=0 && i<pGlob->nPattern; i++){
717
const char *zDir = pGlob->azPattern[i];
718
char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir);
719
switch( file_isdir(zPath, RepoFILE) ){
720
case 0: { /* doesn't exist */
721
fossil_free(zPath);
722
zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir);
723
if( file_mkfolder(zPath, RepoFILE, 0, 1)!=0 ) {
724
fossil_warning("couldn't create directory %s as "
725
"required by empty-dirs setting", zDir);
726
}
727
break;
728
}
729
case 1: { /* exists, and is a directory */
730
/* make sure this directory is not on the delete list */
731
if( clearDirTable ){
732
db_multi_exec(
733
"DELETE FROM dir_to_delete WHERE name=%Q", zDir
734
);
735
}
736
break;
737
}
738
case 2: { /* exists, but isn't a directory */
739
fossil_warning("file %s found, but a directory is required "
740
"by empty-dirs setting", zDir);
741
}
742
}
743
fossil_free(zPath);
744
}
745
glob_free(pGlob);
746
}
747
}
748
749
/*
750
** Get the manifest record for a given revision, or the current check-out if
751
** zRevision is NULL.
752
*/
753
Manifest *historical_manifest(
754
const char *zRevision /* The check-in to query, or NULL for current */
755
){
756
int vid;
757
Manifest *pManifest;
758
759
/* Determine the check-in manifest artifact ID. Panic on failure. */
760
if( zRevision ){
761
vid = name_to_typed_rid(zRevision, "ci");
762
}else if( !g.localOpen ){
763
vid = name_to_typed_rid(db_main_branch(), "ci");
764
}else{
765
vid = db_lget_int("checkout", 0);
766
if( !is_a_version(vid) ){
767
if( vid==0 ) return 0;
768
zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
769
if( zRevision ){
770
fossil_fatal("check-out artifact is not a check-in: %s", zRevision);
771
}else{
772
fossil_fatal("invalid check-out artifact ID: %d", vid);
773
}
774
}
775
}
776
777
/* Parse the manifest, given its artifact ID. Panic on failure. */
778
if( !(pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0)) ){
779
if( zRevision ){
780
fossil_fatal("could not parse manifest for check-in: %s", zRevision);
781
}else{
782
fossil_fatal("could not parse manifest for current check-out");
783
}
784
}
785
786
/* Return the manifest pointer. The caller must use manifest_destroy() to
787
* clean up when finished using the manifest. */
788
return pManifest;
789
}
790
791
/*
792
** Get the contents of a file within the check-in "zRevision". If
793
** zRevision==NULL then get the file content for the current check-out.
794
*/
795
int historical_blob(
796
const char *zRevision, /* The check-in containing the file */
797
const char *zFile, /* Full treename of the file */
798
Blob *pBlob, /* Put the content here */
799
int fatal /* If nonzero, panic if file/artifact not found */
800
){
801
int result = 0;
802
803
/* Get the manifest for the requested check-in version. This call unavoidably
804
* panics on failure even if fatal is not set. */
805
Manifest *pManifest = historical_manifest(zRevision);
806
807
/* Try to find the file record within the manifest. */
808
ManifestFile *pFile = manifest_file_find(pManifest, zFile);
809
810
if( !pFile ){
811
/* Process file-not-found errors. */
812
if( fatal ){
813
if( zRevision ){
814
fossil_fatal("file %s does not exist in check-in %s", zFile, zRevision);
815
}else{
816
fossil_fatal("no such file: %s", zFile);
817
}
818
}
819
}else{
820
/* Get the file's contents. */
821
result = content_get(fast_uuid_to_rid(pFile->zUuid), pBlob);
822
823
/* Process artifact-not-found errors. */
824
if( !result && fatal ){
825
if( zRevision ){
826
fossil_fatal("missing artifact %s for file %s in check-in %s",
827
pFile->zUuid, zFile, zRevision);
828
}else{
829
fossil_fatal("missing artifact %s for file %s", pFile->zUuid, zFile);
830
}
831
}
832
}
833
834
/* Deallocate the parsed manifest structure. */
835
manifest_destroy(pManifest);
836
837
/* Return 1 on success and (assuming fatal is not set) 0 if not found. */
838
return result;
839
}
840
841
/*
842
** COMMAND: revert
843
**
844
** Usage: %fossil revert ?OPTIONS? ?FILE ...?
845
**
846
** Revert to the current repository version of FILE, or to
847
** the baseline VERSION specified with -r flag.
848
**
849
** If FILE was part of a rename operation, both the original file
850
** and the renamed file are reverted.
851
**
852
** Using a directory name for any of the FILE arguments is the same
853
** as using every subdirectory and file beneath that directory.
854
**
855
** Revert all files if no file name is provided.
856
**
857
** If a file is reverted accidentally, it can be restored using
858
** the "fossil undo" command.
859
**
860
** Options:
861
** --noundo Do not record changes in the undo/redo log.
862
** -r|--revision VERSION Revert given FILE(s) back to given
863
** VERSION
864
**
865
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
866
*/
867
void revert_cmd(void){
868
Manifest *pCoManifest; /* Manifest of current check-out */
869
Manifest *pRvManifest; /* Manifest of selected revert version */
870
ManifestFile *pCoFile; /* File within current check-out manifest */
871
ManifestFile *pRvFile; /* File within revert version manifest */
872
const char *zFile; /* Filename relative to check-out root */
873
const char *zRevision; /* Selected revert version, NULL if current */
874
Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
875
int useUndo = 1; /* True to record changes in UNDO */
876
int i;
877
Stmt q;
878
int revertAll = 0;
879
int revisionOptNotSupported = 0;
880
881
undo_capture_command_line();
882
zRevision = find_option("revision", "r", 1);
883
useUndo = find_option("noundo", 0, 0)==0;
884
verify_all_options();
885
886
if( g.argc<2 ){
887
usage("?OPTIONS? [FILE] ...");
888
}
889
if( zRevision && g.argc<3 ){
890
fossil_fatal("directories or the entire tree can only be reverted"
891
" back to current version");
892
}
893
db_must_be_within_tree();
894
895
/* Get manifests of revert version and (if different) current check-out. */
896
pRvManifest = historical_manifest(zRevision);
897
pCoManifest = zRevision ? historical_manifest(0) : 0;
898
899
db_begin_transaction();
900
if( useUndo ){
901
undo_begin();
902
}else{
903
undo_reset();
904
}
905
db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
906
907
if( g.argc>2 ){
908
for(i=2; i<g.argc; i++){
909
Blob fname;
910
zFile = mprintf("%/", g.argv[i]);
911
blob_zero(&fname);
912
file_tree_name(zFile, &fname, 0, 1);
913
if( blob_eq(&fname, ".") ){
914
if( zRevision ){
915
revisionOptNotSupported = 1;
916
break;
917
}
918
revertAll = 1;
919
break;
920
}else if( db_exists(
921
"SELECT pathname"
922
" FROM vfile"
923
" WHERE (substr(pathname,1,length('%q/'))='%q/'"
924
" OR substr(origname,1,length('%q/'))='%q/');",
925
blob_str(&fname), blob_str(&fname),
926
blob_str(&fname), blob_str(&fname)) ){
927
int vid;
928
vid = db_lget_int("checkout", 0);
929
vfile_check_signature(vid, 0);
930
931
if( zRevision ){
932
revisionOptNotSupported = 1;
933
break;
934
}
935
db_multi_exec(
936
"INSERT OR IGNORE INTO torevert"
937
" SELECT pathname"
938
" FROM vfile"
939
" WHERE (substr(pathname,1,length('%q/'))='%q/'"
940
" OR substr(origname,1,length('%q/'))='%q/')"
941
" AND (chnged OR deleted OR rid=0 OR pathname!=origname);",
942
blob_str(&fname), blob_str(&fname),
943
blob_str(&fname), blob_str(&fname)
944
);
945
}else{
946
db_multi_exec(
947
"REPLACE INTO torevert VALUES(%B);"
948
"INSERT OR IGNORE INTO torevert"
949
" SELECT pathname"
950
" FROM vfile"
951
" WHERE origname=%B;",
952
&fname, &fname
953
);
954
}
955
blob_reset(&fname);
956
}
957
}else{
958
revertAll = 1;
959
}
960
961
if( revisionOptNotSupported ){
962
fossil_fatal("directories or the entire tree can only be reverted"
963
" back to current version");
964
}
965
966
if ( revertAll ){
967
int vid;
968
vid = db_lget_int("checkout", 0);
969
vfile_check_signature(vid, 0);
970
db_multi_exec(
971
"DELETE FROM vmerge;"
972
"INSERT OR IGNORE INTO torevert "
973
" SELECT pathname"
974
" FROM vfile "
975
" WHERE chnged OR deleted OR rid=0 OR pathname!=origname;"
976
);
977
}
978
979
db_multi_exec(
980
"INSERT OR IGNORE INTO torevert"
981
" SELECT origname"
982
" FROM vfile"
983
" WHERE origname!=pathname AND pathname IN (SELECT name FROM torevert);"
984
);
985
blob_zero(&record);
986
db_prepare(&q, "SELECT name FROM torevert");
987
if( zRevision==0 ){
988
int vid = db_lget_int("checkout", 0);
989
zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
990
}
991
while( db_step(&q)==SQLITE_ROW ){
992
char *zFull;
993
zFile = db_column_text(&q, 0);
994
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
995
pRvFile = pRvManifest? manifest_file_find(pRvManifest, zFile) : 0;
996
if( !pRvFile ){
997
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
998
zFile, zFile)==0 ){
999
fossil_print("UNMANAGE %s\n", zFile);
1000
}else{
1001
if( useUndo ) undo_save(zFile);
1002
file_delete(zFull);
1003
fossil_print("DELETE %s\n", zFile);
1004
}
1005
db_multi_exec(
1006
"UPDATE OR REPLACE vfile"
1007
" SET pathname=origname, origname=NULL"
1008
" WHERE pathname=%Q AND origname!=pathname;"
1009
"DELETE FROM vfile WHERE pathname=%Q",
1010
zFile, zFile
1011
);
1012
}else if( file_unsafe_in_tree_path(zFull) ){
1013
/* Ignore this file */
1014
}else{
1015
sqlite3_int64 mtime;
1016
int rvChnged = 0;
1017
int rvPerm = manifest_file_mperm(pRvFile);
1018
1019
/* Determine if reverted-to file is different than checked-out file. */
1020
if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){
1021
rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
1022
|| fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
1023
}
1024
1025
/* Get contents of reverted-to file. */
1026
content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);
1027
1028
if( useUndo ) undo_save(zFile);
1029
if( file_size(zFull, RepoFILE)>=0
1030
&& (rvPerm==PERM_LNK || file_islink(0))
1031
){
1032
file_delete(zFull);
1033
}
1034
if( rvPerm==PERM_LNK ){
1035
symlink_create(blob_str(&record), zFull);
1036
}else{
1037
blob_write_to_file(&record, zFull);
1038
}
1039
file_setexe(zFull, rvPerm==PERM_EXE);
1040
fossil_print("REVERT %s\n", zFile);
1041
mtime = file_mtime(zFull, RepoFILE);
1042
db_multi_exec(
1043
"UPDATE vfile"
1044
" SET mtime=%lld, chnged=%d, deleted=0, isexe=%d, islink=%d,"
1045
" mrid=rid, mhash=NULL"
1046
" WHERE pathname=%Q OR origname=%Q",
1047
mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
1048
);
1049
}
1050
blob_reset(&record);
1051
free(zFull);
1052
}
1053
db_finalize(&q);
1054
if( useUndo) undo_finish();
1055
db_end_transaction(0);
1056
1057
/* Deallocate parsed manifest structures. */
1058
manifest_destroy(pRvManifest);
1059
manifest_destroy(pCoManifest);
1060
}
1061

Keyboard Shortcuts

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