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 check-out versions of the project
19 ** from the local repository.
20 */
21 #include "config.h"
22 #include "checkout.h"
23 #include <assert.h>
24
25 /*
26 ** Check to see if there is an existing checkout that has been
27 ** modified. Return values:
28 **
29 ** 0: There is an existing checkout but it is unmodified
30 ** 1: There is a modified checkout - there are unsaved changes
31 */
unsaved_changes(unsigned int cksigFlags)32 int unsaved_changes(unsigned int cksigFlags){
33 int vid;
34 db_must_be_within_tree();
35 vid = db_lget_int("checkout",0);
36 vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE);
37 return db_exists("SELECT 1 FROM vfile WHERE chnged"
38 " OR coalesce(origname!=pathname,0)");
39 }
40
41 /*
42 ** Undo the current check-out. Unlink all files from the disk.
43 ** Clear the VFILE table.
44 **
45 ** Also delete any directory that becomes empty as a result of deleting
46 ** files due to this operation, as long as that directory is not the
47 ** current working directory and is not on the empty-dirs list.
48 */
uncheckout(int vid)49 void uncheckout(int vid){
50 char *zPwd;
51 if( vid<=0 ) return;
52 sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
53 file_dirname_sql_function, 0, 0);
54 sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0,
55 file_delete_sql_function, 0, 0);
56 sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
57 file_rmdir_sql_function, 0, 0);
58 db_multi_exec(
59 "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
60 filename_collation()
61 );
62 db_multi_exec(
63 "INSERT OR IGNORE INTO dir_to_delete(name)"
64 " SELECT dirname(pathname) FROM vfile"
65 " WHERE vid=%d AND mrid>0",
66 vid
67 );
68 do{
69 db_multi_exec(
70 "INSERT OR IGNORE INTO dir_to_delete(name)"
71 " SELECT dirname(name) FROM dir_to_delete;"
72 );
73 }while( db_changes() );
74 db_multi_exec(
75 "SELECT unlink(%Q||pathname) FROM vfile"
76 " WHERE vid=%d AND mrid>0;",
77 g.zLocalRoot, vid
78 );
79 ensure_empty_dirs_created(1);
80 zPwd = file_getcwd(0,0);
81 db_multi_exec(
82 "SELECT rmdir(%Q||name) FROM dir_to_delete"
83 " WHERE (%Q||name)<>%Q ORDER BY name DESC",
84 g.zLocalRoot, g.zLocalRoot, zPwd
85 );
86 fossil_free(zPwd);
87 db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
88 }
89
90
91 /*
92 ** Given the abbreviated hash of a version, load the content of that
93 ** version in the VFILE table. Return the VID for the version.
94 **
95 ** If anything goes wrong, panic.
96 */
load_vfile(const char * zName,int forceMissingFlag)97 int load_vfile(const char *zName, int forceMissingFlag){
98 Blob uuid;
99 int vid;
100
101 blob_init(&uuid, zName, -1);
102 if( name_to_uuid(&uuid, 1, "ci") ){
103 fossil_fatal("%s", g.zErrMsg);
104 }
105 vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
106 if( vid==0 ){
107 fossil_fatal("no such check-in: %s", g.argv[2]);
108 }
109 if( !is_a_version(vid) ){
110 fossil_fatal("object [%S] is not a check-in", blob_str(&uuid));
111 }
112 if( load_vfile_from_rid(vid) && !forceMissingFlag ){
113 fossil_fatal("missing content, unable to checkout");
114 };
115 return vid;
116 }
117
118 /*
119 ** Set or clear the vfile.isexe flag for a file.
120 */
set_or_clear_isexe(const char * zFilename,int vid,int onoff)121 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
122 static Stmt s;
123 db_static_prepare(&s,
124 "UPDATE vfile SET isexe=:isexe"
125 " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
126 );
127 db_bind_int(&s, ":isexe", onoff);
128 db_bind_int(&s, ":vid", vid);
129 db_bind_text(&s, ":path", zFilename);
130 db_step(&s);
131 db_reset(&s);
132 }
133
134 /*
135 ** Set or clear the execute permission bit (as appropriate) for all
136 ** files in the current check-out, and replace files that have
137 ** symlink bit with actual symlinks.
138 */
checkout_set_all_exe(int vid)139 void checkout_set_all_exe(int vid){
140 Blob filename;
141 int baseLen;
142 Manifest *pManifest;
143 ManifestFile *pFile;
144
145 /* Check the EXE permission status of all files
146 */
147 pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0);
148 if( pManifest==0 ) return;
149 blob_zero(&filename);
150 blob_appendf(&filename, "%s", g.zLocalRoot);
151 baseLen = blob_size(&filename);
152 manifest_file_rewind(pManifest);
153 while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
154 int isExe;
155 blob_append(&filename, pFile->zName, -1);
156 isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
157 file_setexe(blob_str(&filename), isExe);
158 set_or_clear_isexe(pFile->zName, vid, isExe);
159 blob_resize(&filename, baseLen);
160 }
161 blob_reset(&filename);
162 manifest_destroy(pManifest);
163 }
164
165
166 /*
167 ** If the "manifest" setting is true, then automatically generate
168 ** files named "manifest" and "manifest.uuid" containing, respectively,
169 ** the text of the manifest and the artifact ID of the manifest.
170 ** If the manifest setting is set, but is not a boolean value, then treat
171 ** each character as a flag to enable writing "manifest", "manifest.uuid" or
172 ** "manifest.tags".
173 */
manifest_to_disk(int vid)174 void manifest_to_disk(int vid){
175 char *zManFile;
176 Blob manifest;
177 Blob taglist;
178 int flg;
179
180 flg = db_get_manifest_setting();
181
182 if( flg & MFESTFLG_RAW ){
183 blob_zero(&manifest);
184 content_get(vid, &manifest);
185 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
186 zManFile = mprintf("%smanifest", g.zLocalRoot);
187 blob_write_to_file(&manifest, zManFile);
188 free(zManFile);
189 }else{
190 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
191 zManFile = mprintf("%smanifest", g.zLocalRoot);
192 file_delete(zManFile);
193 free(zManFile);
194 }
195 }
196 if( flg & MFESTFLG_UUID ){
197 Blob hash;
198 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
199 blob_set_dynamic(&hash, rid_to_uuid(vid));
200 blob_append(&hash, "\n", 1);
201 blob_write_to_file(&hash, zManFile);
202 free(zManFile);
203 blob_reset(&hash);
204 }else{
205 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
206 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
207 file_delete(zManFile);
208 free(zManFile);
209 }
210 }
211 if( flg & MFESTFLG_TAGS ){
212 blob_zero(&taglist);
213 zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
214 get_checkin_taglist(vid, &taglist);
215 blob_write_to_file(&taglist, zManFile);
216 free(zManFile);
217 blob_reset(&taglist);
218 }else{
219 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
220 zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
221 file_delete(zManFile);
222 free(zManFile);
223 }
224 }
225 }
226
227 /*
228 ** Find the branch name and all symbolic tags for a particular check-in
229 ** identified by "rid".
230 **
231 ** The branch name is actually only extracted if this procedure is run
232 ** from within a local check-out. And the branch name is not the branch
233 ** name for "rid" but rather the branch name for the current check-out.
234 ** It is unclear if the rid parameter is always the same as the current
235 ** check-out.
236 */
get_checkin_taglist(int rid,Blob * pOut)237 void get_checkin_taglist(int rid, Blob *pOut){
238 Stmt stmt;
239 char *zCurrent;
240 blob_reset(pOut);
241 zCurrent = db_text(0, "SELECT value FROM tagxref"
242 " WHERE rid=%d AND tagid=%d", rid, TAG_BRANCH);
243 blob_appendf(pOut, "branch %s\n", zCurrent);
244 db_prepare(&stmt, "SELECT substr(tagname, 5)"
245 " FROM tagxref, tag"
246 " WHERE tagxref.rid=%d"
247 " AND tagxref.tagtype>0"
248 " AND tag.tagid=tagxref.tagid"
249 " AND tag.tagname GLOB 'sym-*'", rid);
250 while( db_step(&stmt)==SQLITE_ROW ){
251 const char *zName;
252 zName = db_column_text(&stmt, 0);
253 blob_appendf(pOut, "tag %s\n", zName);
254 }
255 db_reset(&stmt);
256 db_finalize(&stmt);
257 }
258
259
260 /*
261 ** COMMAND: checkout*
262 ** COMMAND: co*
263 **
264 ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
265 ** or: %fossil co ?VERSION | --latest? ?OPTIONS?
266 **
267 ** NOTE: Most people use "fossil update" instead of "fossil checkout" for
268 ** day-to-day operations. If you are new to Fossil and trying to learn your
269 ** way around, it is recommended that you become familiar with the
270 ** "fossil update" command first.
271 **
272 ** This command changes the current check-out to the version specified
273 ** as an argument. The command aborts if there are edited files in the
274 ** current checkout unless the --force option is used. The --keep option
275 ** leaves files on disk unchanged, except the manifest and manifest.uuid
276 ** files.
277 **
278 ** The --latest flag can be used in place of VERSION to checkout the
279 ** latest version in the repository.
280 **
281 ** Options:
282 ** --force Ignore edited files in the current checkout
283 ** --keep Only update the manifest and manifest.uuid files
284 ** --force-missing Force checkout even if content is missing
285 ** --setmtime Set timestamps of all files to match their SCM-side
286 ** times (the timestamp of the last checkin which modified
287 ** them)
288 **
289 ** See also: [[update]]
290 */
checkout_cmd(void)291 void checkout_cmd(void){
292 int forceFlag; /* Force checkout even if edits exist */
293 int forceMissingFlag; /* Force checkout even if missing content */
294 int keepFlag; /* Do not change any files on disk */
295 int latestFlag; /* Checkout the latest version */
296 char *zVers; /* Version to checkout */
297 int promptFlag; /* True to prompt before overwriting */
298 int vid, prior;
299 int setmtimeFlag; /* --setmtime. Set mtimes on files */
300 Blob cksum1, cksum1b, cksum2;
301
302 db_must_be_within_tree();
303 db_begin_transaction();
304 forceFlag = find_option("force","f",0)!=0;
305 forceMissingFlag = find_option("force-missing",0,0)!=0;
306 keepFlag = find_option("keep",0,0)!=0;
307 latestFlag = find_option("latest",0,0)!=0;
308 promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
309 setmtimeFlag = find_option("setmtime",0,0)!=0;
310
311 /* We should be done with options.. */
312 verify_all_options();
313
314 if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
315 usage("VERSION|--latest ?--force? ?--keep?");
316 }
317 if( !forceFlag && unsaved_changes(0) ){
318 fossil_fatal("there are unsaved changes in the current checkout");
319 }
320 if( forceFlag ){
321 db_multi_exec("DELETE FROM vfile");
322 prior = 0;
323 }else{
324 prior = db_lget_int("checkout",0);
325 }
326 if( latestFlag ){
327 compute_leaves(db_lget_int("checkout",0), 1);
328 zVers = db_text(0, "SELECT uuid FROM leaves, event, blob"
329 " WHERE event.objid=leaves.rid AND blob.rid=leaves.rid"
330 " ORDER BY event.mtime DESC");
331 if( zVers==0 ){
332 zVers = db_text(0, "SELECT uuid FROM event, blob"
333 " WHERE event.objid=blob.rid AND event.type='ci'"
334 " ORDER BY event.mtime DESC");
335 }
336 if( zVers==0 ){
337 db_end_transaction(0);
338 return;
339 }
340 }else{
341 zVers = g.argv[2];
342 }
343 vid = load_vfile(zVers, forceMissingFlag);
344 if( prior==vid ){
345 if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME);
346 db_end_transaction(0);
347 return;
348 }
349 if( !keepFlag ){
350 uncheckout(prior);
351 }
352 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
353 if( !keepFlag ){
354 vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
355 }
356 checkout_set_all_exe(vid);
357 manifest_to_disk(vid);
358 ensure_empty_dirs_created(0);
359 db_set_checkout(vid);
360 undo_reset();
361 db_multi_exec("DELETE FROM vmerge");
362 if( !keepFlag && db_get_boolean("repo-cksum",1) ){
363 vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
364 vfile_aggregate_checksum_disk(vid, &cksum2);
365 if( blob_compare(&cksum1, &cksum2) ){
366 fossil_print("WARNING: manifest checksum does not agree with disk\n");
367 }
368 if( blob_size(&cksum1b) && blob_compare(&cksum1, &cksum1b) ){
369 fossil_print("WARNING: manifest checksum does not agree with manifest\n");
370 }
371 }
372 if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME);
373 db_end_transaction(0);
374 }
375
376 /*
377 ** Unlink the local database file
378 */
unlink_local_database(int manifestOnly)379 static void unlink_local_database(int manifestOnly){
380 const char *zReserved;
381 int i;
382 for(i=0; (zReserved = fossil_reserved_name(i, 1))!=0; i++){
383 if( manifestOnly==0 || zReserved[0]=='m' ){
384 char *z;
385 z = mprintf("%s%s", g.zLocalRoot, zReserved);
386 file_delete(z);
387 free(z);
388 }
389 }
390 }
391
392 /*
393 ** COMMAND: close*
394 **
395 ** Usage: %fossil close ?OPTIONS?
396 **
397 ** The opposite of "[[open]]". Close the current database connection.
398 ** Require a -f or --force flag if there are unsaved changes in the
399 ** current check-out or if there is non-empty stash.
400 **
401 ** Options:
402 ** -f|--force necessary to close a check out with uncommitted changes
403 **
404 ** See also: [[open]]
405 */
close_cmd(void)406 void close_cmd(void){
407 int forceFlag = find_option("force","f",0)!=0;
408 db_must_be_within_tree();
409
410 /* We should be done with options.. */
411 verify_all_options();
412
413 if( !forceFlag && unsaved_changes(0) ){
414 fossil_fatal("there are unsaved changes in the current checkout");
415 }
416 if( !forceFlag
417 && db_table_exists("localdb","stash")
418 && db_exists("SELECT 1 FROM localdb.stash")
419 ){
420 fossil_fatal("closing the checkout will delete your stash");
421 }
422 if( db_is_writeable("repository") ){
423 db_unset_mprintf(1, "ckout:%q", g.zLocalRoot);
424 }
425 unlink_local_database(1);
426 db_close(1);
427 unlink_local_database(0);
428 }
429