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