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