Fossil SCM

fossil-scm / src / checkout.c
Source Blame History 655 lines
dbda8d6… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2007 D. Richard Hipp
dbda8d6… drh 3 **
dbda8d6… drh 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
c06edd2… drh 7
dbda8d6… drh 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
dbda8d6… drh 11 **
dbda8d6… drh 12 ** Author contact information:
dbda8d6… drh 13 ** [email protected]
dbda8d6… drh 14 ** http://www.hwaci.com/drh/
dbda8d6… drh 15 **
dbda8d6… drh 16 *******************************************************************************
dbda8d6… drh 17 **
dbda8d6… drh 18 ** This file contains code used to check-out versions of the project
dbda8d6… drh 19 ** from the local repository.
dbda8d6… drh 20 */
dbda8d6… drh 21 #include "config.h"
dbda8d6… drh 22 #include "checkout.h"
dbda8d6… drh 23 #include <assert.h>
2f9920a… drh 24 #include <zlib.h>
dbda8d6… drh 25
dbda8d6… drh 26 /*
bc36fdc… danield 27 ** Check to see if there is an existing check-out that has been
dbda8d6… drh 28 ** modified. Return values:
dbda8d6… drh 29 **
bc36fdc… danield 30 ** 0: There is an existing check-out but it is unmodified
bc36fdc… danield 31 ** 1: There is a modified check-out - there are unsaved changes
dbda8d6… drh 32 */
a9ad53b… joel 33 int unsaved_changes(unsigned int cksigFlags){
dbda8d6… drh 34 int vid;
dbda8d6… drh 35 db_must_be_within_tree();
dbda8d6… drh 36 vid = db_lget_int("checkout",0);
a9ad53b… joel 37 vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE);
e146d80… drh 38 return db_exists("SELECT 1 FROM vfile WHERE chnged"
e146d80… drh 39 " OR coalesce(origname!=pathname,0)");
dbda8d6… drh 40 }
dbda8d6… drh 41
dbda8d6… drh 42 /*
dbda8d6… drh 43 ** Undo the current check-out. Unlink all files from the disk.
dbda8d6… drh 44 ** Clear the VFILE table.
f132f86… drh 45 **
f132f86… drh 46 ** Also delete any directory that becomes empty as a result of deleting
f132f86… drh 47 ** files due to this operation, as long as that directory is not the
f132f86… drh 48 ** current working directory and is not on the empty-dirs list.
dbda8d6… drh 49 */
dbda8d6… drh 50 void uncheckout(int vid){
f132f86… drh 51 char *zPwd;
f132f86… drh 52 if( vid<=0 ) return;
f132f86… drh 53 sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
f132f86… drh 54 file_dirname_sql_function, 0, 0);
994a7c7… drh 55 sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8|SQLITE_DIRECTONLY,0,
f132f86… drh 56 file_delete_sql_function, 0, 0);
994a7c7… drh 57 sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
f132f86… drh 58 file_rmdir_sql_function, 0, 0);
f132f86… drh 59 db_multi_exec(
f132f86… drh 60 "CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
f132f86… drh 61 filename_collation()
f132f86… drh 62 );
f132f86… drh 63 db_multi_exec(
f132f86… drh 64 "INSERT OR IGNORE INTO dir_to_delete(name)"
f132f86… drh 65 " SELECT dirname(pathname) FROM vfile"
f132f86… drh 66 " WHERE vid=%d AND mrid>0",
f132f86… drh 67 vid
f132f86… drh 68 );
f132f86… drh 69 do{
f132f86… drh 70 db_multi_exec(
f132f86… drh 71 "INSERT OR IGNORE INTO dir_to_delete(name)"
f132f86… drh 72 " SELECT dirname(name) FROM dir_to_delete;"
f132f86… drh 73 );
f132f86… drh 74 }while( db_changes() );
f132f86… drh 75 db_multi_exec(
f132f86… drh 76 "SELECT unlink(%Q||pathname) FROM vfile"
f132f86… drh 77 " WHERE vid=%d AND mrid>0;",
f132f86… drh 78 g.zLocalRoot, vid
f132f86… drh 79 );
f132f86… drh 80 ensure_empty_dirs_created(1);
f132f86… drh 81 zPwd = file_getcwd(0,0);
f132f86… drh 82 db_multi_exec(
f132f86… drh 83 "SELECT rmdir(%Q||name) FROM dir_to_delete"
f132f86… drh 84 " WHERE (%Q||name)<>%Q ORDER BY name DESC",
f132f86… drh 85 g.zLocalRoot, g.zLocalRoot, zPwd
f132f86… drh 86 );
f132f86… drh 87 fossil_free(zPwd);
dbda8d6… drh 88 db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
dbda8d6… drh 89 }
dbda8d6… drh 90
dbda8d6… drh 91
dbda8d6… drh 92 /*
8ad5e46… wyoung 93 ** Given the abbreviated hash of a version, load the content of that
dbda8d6… drh 94 ** version in the VFILE table. Return the VID for the version.
dbda8d6… drh 95 **
dbda8d6… drh 96 ** If anything goes wrong, panic.
dbda8d6… drh 97 */
941ead2… andybradford 98 int load_vfile(const char *zName, int forceMissingFlag){
dbda8d6… drh 99 Blob uuid;
dbda8d6… drh 100 int vid;
dbda8d6… drh 101
dbda8d6… drh 102 blob_init(&uuid, zName, -1);
2a013f0… drh 103 if( name_to_uuid(&uuid, 1, "ci") ){
49b0ff1… drh 104 fossil_fatal("%s", g.zErrMsg);
dbda8d6… drh 105 }
dbda8d6… drh 106 vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
dbda8d6… drh 107 if( vid==0 ){
c774e29… drh 108 fossil_fatal("no such check-in: %s", g.argv[2]);
c774e29… drh 109 }
999b177… drh 110 if( !is_a_version(vid) ){
fe6d393… jan.nijtmans 111 fossil_fatal("object [%S] is not a check-in", blob_str(&uuid));
1298918… drh 112 }
941ead2… andybradford 113 if( load_vfile_from_rid(vid) && !forceMissingFlag ){
bc36fdc… danield 114 fossil_fatal("missing content, unable to check out");
941ead2… andybradford 115 };
1298918… drh 116 return vid;
dbda8d6… drh 117 }
dbda8d6… drh 118
dbda8d6… drh 119 /*
7c2577b… drh 120 ** Set or clear the vfile.isexe flag for a file.
588bb7c… aku 121 */
7c2577b… drh 122 static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){
d13054c… drh 123 static Stmt s;
d13054c… drh 124 db_static_prepare(&s,
d13054c… drh 125 "UPDATE vfile SET isexe=:isexe"
d13054c… drh 126 " WHERE vid=:vid AND pathname=:path AND isexe!=:isexe"
d13054c… drh 127 );
d13054c… drh 128 db_bind_int(&s, ":isexe", onoff);
d13054c… drh 129 db_bind_int(&s, ":vid", vid);
d13054c… drh 130 db_bind_text(&s, ":path", zFilename);
d13054c… drh 131 db_step(&s);
d13054c… drh 132 db_reset(&s);
588bb7c… aku 133 }
588bb7c… aku 134
588bb7c… aku 135 /*
5a48a9b… drh 136 ** Set or clear the execute permission bit (as appropriate) for all
e4f1c1f… drh 137 ** files in the current check-out, and replace files that have
e4f1c1f… drh 138 ** symlink bit with actual symlinks.
588bb7c… aku 139 */
d13054c… drh 140 void checkout_set_all_exe(int vid){
588bb7c… aku 141 Blob filename;
588bb7c… aku 142 int baseLen;
d13054c… drh 143 Manifest *pManifest;
d13054c… drh 144 ManifestFile *pFile;
5a48a9b… drh 145
5a48a9b… drh 146 /* Check the EXE permission status of all files
5a48a9b… drh 147 */
ec81aee… jan.nijtmans 148 pManifest = manifest_get(vid, CFTYPE_MANIFEST, 0);
d13054c… drh 149 if( pManifest==0 ) return;
588bb7c… aku 150 blob_zero(&filename);
897dfa4… drh 151 blob_appendf(&filename, "%s", g.zLocalRoot);
588bb7c… aku 152 baseLen = blob_size(&filename);
d13054c… drh 153 manifest_file_rewind(pManifest);
d13054c… drh 154 while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
588bb7c… aku 155 int isExe;
d13054c… drh 156 blob_append(&filename, pFile->zName, -1);
d13054c… drh 157 isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
1772357… drh 158 file_setexe(blob_str(&filename), isExe);
d13054c… drh 159 set_or_clear_isexe(pFile->zName, vid, isExe);
588bb7c… aku 160 blob_resize(&filename, baseLen);
588bb7c… aku 161 }
588bb7c… aku 162 blob_reset(&filename);
d13054c… drh 163 manifest_destroy(pManifest);
d13054c… drh 164 }
d13054c… drh 165
d13054c… drh 166
d13054c… drh 167 /*
d13054c… drh 168 ** If the "manifest" setting is true, then automatically generate
d13054c… drh 169 ** files named "manifest" and "manifest.uuid" containing, respectively,
d13054c… drh 170 ** the text of the manifest and the artifact ID of the manifest.
189bfc2… jan 171 ** If the manifest setting is set, but is not a boolean value, then treat
189bfc2… jan 172 ** each character as a flag to enable writing "manifest", "manifest.uuid" or
189bfc2… jan 173 ** "manifest.tags".
d13054c… drh 174 */
d13054c… drh 175 void manifest_to_disk(int vid){
d13054c… drh 176 char *zManFile;
189bfc2… jan 177 int flg;
189bfc2… jan 178
dfc0f1b… drh 179 flg = db_get_manifest_setting(0);
5a48a9b… drh 180
41af304… drh 181 if( flg & MFESTFLG_RAW ){
d564056… drh 182 Blob manifest = BLOB_INITIALIZER;
d13054c… drh 183 content_get(vid, &manifest);
2777682… drh 184 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
5a48a9b… drh 185 zManFile = mprintf("%smanifest", g.zLocalRoot);
5a48a9b… drh 186 blob_write_to_file(&manifest, zManFile);
5a48a9b… drh 187 free(zManFile);
d564056… drh 188 blob_reset(&manifest);
d8ec765… drh 189 }else{
d8ec765… drh 190 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
d8ec765… drh 191 zManFile = mprintf("%smanifest", g.zLocalRoot);
d8ec765… drh 192 file_delete(zManFile);
d8ec765… drh 193 free(zManFile);
d8ec765… drh 194 }
189bfc2… jan 195 }
189bfc2… jan 196 if( flg & MFESTFLG_UUID ){
41af304… drh 197 Blob hash;
189bfc2… jan 198 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
41af304… drh 199 blob_set_dynamic(&hash, rid_to_uuid(vid));
189bfc2… jan 200 blob_append(&hash, "\n", 1);
189bfc2… jan 201 blob_write_to_file(&hash, zManFile);
189bfc2… jan 202 free(zManFile);
189bfc2… jan 203 blob_reset(&hash);
189bfc2… jan 204 }else{
d8ec765… drh 205 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
d8ec765… drh 206 zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
d8ec765… drh 207 file_delete(zManFile);
d8ec765… drh 208 free(zManFile);
d8ec765… drh 209 }
d8ec765… drh 210 }
189bfc2… jan 211 if( flg & MFESTFLG_TAGS ){
d564056… drh 212 Blob taglist = BLOB_INITIALIZER;
189bfc2… jan 213 zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
189bfc2… jan 214 get_checkin_taglist(vid, &taglist);
189bfc2… jan 215 blob_write_to_file(&taglist, zManFile);
189bfc2… jan 216 free(zManFile);
189bfc2… jan 217 blob_reset(&taglist);
189bfc2… jan 218 }else{
189bfc2… jan 219 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
189bfc2… jan 220 zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
189bfc2… jan 221 file_delete(zManFile);
189bfc2… jan 222 free(zManFile);
189bfc2… jan 223 }
189bfc2… jan 224 }
189bfc2… jan 225 }
3df526c… jan.nijtmans 226
189bfc2… jan 227 /*
189bfc2… jan 228 ** Find the branch name and all symbolic tags for a particular check-in
189bfc2… jan 229 ** identified by "rid".
189bfc2… jan 230 **
189bfc2… jan 231 ** The branch name is actually only extracted if this procedure is run
189bfc2… jan 232 ** from within a local check-out. And the branch name is not the branch
189bfc2… jan 233 ** name for "rid" but rather the branch name for the current check-out.
189bfc2… jan 234 ** It is unclear if the rid parameter is always the same as the current
189bfc2… jan 235 ** check-out.
189bfc2… jan 236 */
189bfc2… jan 237 void get_checkin_taglist(int rid, Blob *pOut){
189bfc2… jan 238 Stmt stmt;
189bfc2… jan 239 char *zCurrent;
189bfc2… jan 240 blob_reset(pOut);
189bfc2… jan 241 zCurrent = db_text(0, "SELECT value FROM tagxref"
189bfc2… jan 242 " WHERE rid=%d AND tagid=%d", rid, TAG_BRANCH);
189bfc2… jan 243 blob_appendf(pOut, "branch %s\n", zCurrent);
189bfc2… jan 244 db_prepare(&stmt, "SELECT substr(tagname, 5)"
189bfc2… jan 245 " FROM tagxref, tag"
189bfc2… jan 246 " WHERE tagxref.rid=%d"
189bfc2… jan 247 " AND tagxref.tagtype>0"
189bfc2… jan 248 " AND tag.tagid=tagxref.tagid"
189bfc2… jan 249 " AND tag.tagname GLOB 'sym-*'", rid);
189bfc2… jan 250 while( db_step(&stmt)==SQLITE_ROW ){
189bfc2… jan 251 const char *zName;
189bfc2… jan 252 zName = db_column_text(&stmt, 0);
189bfc2… jan 253 blob_appendf(pOut, "tag %s\n", zName);
189bfc2… jan 254 }
189bfc2… jan 255 db_reset(&stmt);
189bfc2… jan 256 db_finalize(&stmt);
d8ec765… drh 257 }
189bfc2… jan 258
841772c… drh 259
d8ec765… drh 260 /*
841772c… drh 261 ** COMMAND: checkout*
db70849… danield 262 ** COMMAND: co#
2210be1… drh 263 **
2210be1… drh 264 ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
2210be1… drh 265 ** or: %fossil co ?VERSION | --latest? ?OPTIONS?
2210be1… drh 266 **
1bdd485… drh 267 ** NOTE: Most people use "fossil update" instead of "fossil checkout" for
1bdd485… drh 268 ** day-to-day operations. If you are new to Fossil and trying to learn your
1bdd485… drh 269 ** way around, it is recommended that you become familiar with the
1bdd485… drh 270 ** "fossil update" command first.
1bdd485… drh 271 **
1bdd485… drh 272 ** This command changes the current check-out to the version specified
1bdd485… drh 273 ** as an argument. The command aborts if there are edited files in the
bc36fdc… danield 274 ** current check-out unless the --force option is used. The --keep option
d8ec765… drh 275 ** leaves files on disk unchanged, except the manifest and manifest.uuid
d8ec765… drh 276 ** files.
d8ec765… drh 277 **
bc36fdc… danield 278 ** The --latest flag can be used in place of VERSION to check-out the
d8ec765… drh 279 ** latest version in the repository.
3df526c… jan.nijtmans 280 **
2210be1… drh 281 ** Options:
5bc31db… mgagnon 282 ** -f|--force Ignore edited files in the current check-out
5bc31db… mgagnon 283 ** -k|--keep Only update the manifest file(s)
bc36fdc… danield 284 ** --force-missing Force check-out even if content is missing
e194398… danield 285 ** --prompt Prompt before overwriting when --force is used
a7e86f5… drh 286 ** --setmtime Set timestamps of all files to match their SCM-side
bc36fdc… danield 287 ** times (the timestamp of the last check-in which modified
4cb50c4… stephan 288 ** them)
2210be1… drh 289 **
c965636… drh 290 ** See also: [[update]]
d8ec765… drh 291 */
d8ec765… drh 292 void checkout_cmd(void){
bc36fdc… danield 293 int forceFlag; /* Force check-out even if edits exist */
bc36fdc… danield 294 int forceMissingFlag; /* Force check-out even if missing content */
d8ec765… drh 295 int keepFlag; /* Do not change any files on disk */
bc36fdc… danield 296 int latestFlag; /* Check out the latest version */
bc36fdc… danield 297 char *zVers; /* Version to check out */
d8ec765… drh 298 int promptFlag; /* True to prompt before overwriting */
d8ec765… drh 299 int vid, prior;
a7e86f5… drh 300 int setmtimeFlag; /* --setmtime. Set mtimes on files */
d8ec765… drh 301 Blob cksum1, cksum1b, cksum2;
3df526c… jan.nijtmans 302
d8ec765… drh 303 db_must_be_within_tree();
e1962ef… drh 304 db_begin_transaction();
941ead2… andybradford 305 forceMissingFlag = find_option("force-missing",0,0)!=0;
5bc31db… mgagnon 306 keepFlag = find_option("keep","k",0)!=0;
5bc31db… mgagnon 307 forceFlag = find_option("force","f",0)!=0;
d8ec765… drh 308 latestFlag = find_option("latest",0,0)!=0;
d8ec765… drh 309 promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
a7e86f5… drh 310 setmtimeFlag = find_option("setmtime",0,0)!=0;
5bc31db… mgagnon 311
5bc31db… mgagnon 312 if( keepFlag != 0 ){
e194398… danield 313 /* After flag collection, in order not to affect promptFlag */
5bc31db… mgagnon 314 forceFlag=1;
5bc31db… mgagnon 315 }
74ac0c9… drh 316
74ac0c9… drh 317 /* We should be done with options.. */
74ac0c9… drh 318 verify_all_options();
74ac0c9… drh 319
d8ec765… drh 320 if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
d8ec765… drh 321 usage("VERSION|--latest ?--force? ?--keep?");
d8ec765… drh 322 }
f7d9413… jan.nijtmans 323 if( !forceFlag && unsaved_changes(0) ){
bc36fdc… danield 324 fossil_fatal("there are unsaved changes in the current check-out");
dbda8d6… drh 325 }
dbda8d6… drh 326 if( forceFlag ){
dbda8d6… drh 327 db_multi_exec("DELETE FROM vfile");
dbda8d6… drh 328 prior = 0;
dbda8d6… drh 329 }else{
dbda8d6… drh 330 prior = db_lget_int("checkout",0);
dbda8d6… drh 331 }
915bfd9… drh 332 if( latestFlag ){
915bfd9… drh 333 compute_leaves(db_lget_int("checkout",0), 1);
915bfd9… drh 334 zVers = db_text(0, "SELECT uuid FROM leaves, event, blob"
915bfd9… drh 335 " WHERE event.objid=leaves.rid AND blob.rid=leaves.rid"
915bfd9… drh 336 " ORDER BY event.mtime DESC");
915bfd9… drh 337 if( zVers==0 ){
16e703b… drh 338 zVers = db_text(0, "SELECT uuid FROM event, blob"
16e703b… drh 339 " WHERE event.objid=blob.rid AND event.type='ci'"
16e703b… drh 340 " ORDER BY event.mtime DESC");
16e703b… drh 341 }
16e703b… drh 342 if( zVers==0 ){
b0db865… jan.nijtmans 343 db_end_transaction(0);
b725c1c… jan.nijtmans 344 return;
915bfd9… drh 345 }
915bfd9… drh 346 }else{
915bfd9… drh 347 zVers = g.argv[2];
915bfd9… drh 348 }
941ead2… andybradford 349 vid = load_vfile(zVers, forceMissingFlag);
dbda8d6… drh 350 if( prior==vid ){
a7e86f5… drh 351 if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME);
b31aa7d… drh 352 db_end_transaction(0);
dbda8d6… drh 353 return;
dbda8d6… drh 354 }
915bfd9… drh 355 if( !keepFlag ){
dbda8d6… drh 356 uncheckout(prior);
dbda8d6… drh 357 }
dbda8d6… drh 358 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
915bfd9… drh 359 if( !keepFlag ){
f3e8e3e… drh 360 vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
a040ae6… drh 361 }
d13054c… drh 362 checkout_set_all_exe(vid);
f132f86… drh 363 ensure_empty_dirs_created(0);
f72ef85… andybradford 364 db_set_checkout(vid, 1);
915bfd9… drh 365 undo_reset();
dbda8d6… drh 366 db_multi_exec("DELETE FROM vmerge");
46ef289… drh 367 if( !keepFlag && db_get_boolean("repo-cksum",1) ){
915bfd9… drh 368 vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
915bfd9… drh 369 vfile_aggregate_checksum_disk(vid, &cksum2);
915bfd9… drh 370 if( blob_compare(&cksum1, &cksum2) ){
d8ec765… drh 371 fossil_print("WARNING: manifest checksum does not agree with disk\n");
915bfd9… drh 372 }
aab38ef… drh 373 if( blob_size(&cksum1b) && blob_compare(&cksum1, &cksum1b) ){
d8ec765… drh 374 fossil_print("WARNING: manifest checksum does not agree with manifest\n");
915bfd9… drh 375 }
915bfd9… drh 376 }
a7e86f5… drh 377 if( setmtimeFlag ) vfile_check_signature(vid, CKSIG_SETMTIME);
dbda8d6… drh 378 db_end_transaction(0);
4452576… drh 379 }
4452576… drh 380
4452576… drh 381 /*
915bfd9… drh 382 ** Unlink the local database file
915bfd9… drh 383 */
56d69db… drh 384 static void unlink_local_database(int manifestOnly){
56d69db… drh 385 const char *zReserved;
915bfd9… drh 386 int i;
fcdeaa2… drh 387 for(i=0; (zReserved = fossil_reserved_name(i, 1))!=0; i++){
56d69db… drh 388 if( manifestOnly==0 || zReserved[0]=='m' ){
56d69db… drh 389 char *z;
56d69db… drh 390 z = mprintf("%s%s", g.zLocalRoot, zReserved);
d8ec765… drh 391 file_delete(z);
56d69db… drh 392 free(z);
56d69db… drh 393 }
915bfd9… drh 394 }
915bfd9… drh 395 }
915bfd9… drh 396
915bfd9… drh 397 /*
841772c… drh 398 ** COMMAND: close*
2210be1… drh 399 **
2210be1… drh 400 ** Usage: %fossil close ?OPTIONS?
2210be1… drh 401 **
c965636… drh 402 ** The opposite of "[[open]]". Close the current database connection.
b6b50b1… mistachkin 403 ** Require a -f or --force flag if there are unsaved changes in the
c6aedbd… drh 404 ** current check-out or if there is non-empty stash.
6b85fd1… drh 405 **
2210be1… drh 406 ** Options:
2512d2d… km 407 ** -f|--force Necessary to close a check-out with uncommitted changes
4452576… drh 408 **
c965636… drh 409 ** See also: [[open]]
4452576… drh 410 */
4452576… drh 411 void close_cmd(void){
4452576… drh 412 int forceFlag = find_option("force","f",0)!=0;
4452576… drh 413 db_must_be_within_tree();
4e18dba… jan.nijtmans 414
74ac0c9… drh 415 /* We should be done with options.. */
74ac0c9… drh 416 verify_all_options();
74ac0c9… drh 417
f7d9413… jan.nijtmans 418 if( !forceFlag && unsaved_changes(0) ){
bc36fdc… danield 419 fossil_fatal("there are unsaved changes in the current check-out");
c6aedbd… drh 420 }
c6aedbd… drh 421 if( !forceFlag
1aa8067… drh 422 && db_table_exists("localdb","stash")
06aec61… drh 423 && db_exists("SELECT 1 FROM localdb.stash")
c6aedbd… drh 424 ){
bc36fdc… danield 425 fossil_fatal("closing the check-out will delete your stash");
0205148… drh 426 }
0205148… drh 427 if( db_is_writeable("repository") ){
0a5d0e1… drh 428 db_unset_mprintf(1, "ckout:%q", g.zLocalRoot);
56d69db… drh 429 }
56d69db… drh 430 unlink_local_database(1);
c2f5dbe… drh 431 db_close(1);
56d69db… drh 432 unlink_local_database(0);
2f9920a… drh 433 }
2f9920a… drh 434
2f9920a… drh 435
2f9920a… drh 436 /*
2f9920a… drh 437 ** COMMAND: get
2f9920a… drh 438 **
2f9920a… drh 439 ** Usage: %fossil get URL ?VERSION? ?OPTIONS?
2f9920a… drh 440 **
2f9920a… drh 441 ** Download a single check-in from a remote repository named URL and
692140d… drh 442 ** unpack all of the files into a subdirectory. The specific check-in
692140d… drh 443 ** to download is identified by VERSION. If VERSION is omitted, the
692140d… drh 444 ** latest trunk check-in is used. The URL can be a traditional "https:",
692140d… drh 445 ** "ssh:", or "file:" URL similar to the examples shown below, or it can
692140d… drh 446 ** be the name of a local repository/
2f9920a… drh 447 **
2f9920a… drh 448 ** * https://domain.com/project
2f9920a… drh 449 ** * ssh://my-server/project.fossil
2f9920a… drh 450 ** * file:/home/user/Fossils/project.fossil
2f9920a… drh 451 **
2f9920a… drh 452 ** This command works by downloading an SQL archive of the requested
2f9920a… drh 453 ** check-in and then extracting all the files from the archive.
2f9920a… drh 454 **
2f9920a… drh 455 ** Options:
692140d… drh 456 ** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ." to
692140d… drh 457 ** extract into the local directory. If this option is
692140d… drh 458 ** omitted, Fossil invents a subdirectory name derived
692140d… drh 459 ** from base filename in the URL and from the VERSION.
692140d… drh 460 ** -f|--force Overwrite existing files
692140d… drh 461 ** --list List all the files that would have been checked
692140d… drh 462 ** out but do not actually write anything to the
692140d… drh 463 ** filesystem.
692140d… drh 464 ** --sqlar ARCHIVE Leave the check-out in an SQL-archive named ARCHIVE
692140d… drh 465 ** rather than unpacking into separate files.
692140d… drh 466 ** -v|--verbose Show all files as they are extracted
2f9920a… drh 467 */
2f9920a… drh 468 void get_cmd(void){
2f9920a… drh 469 int forceFlag = find_option("force","f",0)!=0;
2f9920a… drh 470 int bVerbose = find_option("verbose","v",0)!=0;
74d5ce3… florian 471 int bQuiet = g.fQuiet;
2f9920a… drh 472 int bDebug = find_option("debug",0,0)!=0;
2f9920a… drh 473 int bList = find_option("list",0,0)!=0;
2f9920a… drh 474 const char *zSqlArchive = find_option("sqlar",0,1);
2f9920a… drh 475 const char *z;
2f9920a… drh 476 char *zDest = 0; /* Where to store results */
2f9920a… drh 477 char *zSql; /* SQL used to query the results */
2f9920a… drh 478 const char *zUrl; /* Url to get */
2f9920a… drh 479 const char *zVers; /* Version name to get */
2f9920a… drh 480 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
2f9920a… drh 481 Blob in, out; /* I/O for the HTTP request */
2f9920a… drh 482 Blob file; /* A file to extract */
2f9920a… drh 483 sqlite3 *db; /* Database containing downloaded sqlar */
2f9920a… drh 484 sqlite3_stmt *pStmt; /* Statement for querying the database */
2f9920a… drh 485 int rc; /* Result of subroutine calls */
2f9920a… drh 486 int nFile = 0; /* Number of files written */
2f9920a… drh 487 int nDir = 0; /* Number of directories written */
2f9920a… drh 488 i64 nByte = 0; /* Number of bytes written */
2f9920a… drh 489
2f9920a… drh 490 z = find_option("dest",0,1);
2f9920a… drh 491 if( z ) zDest = fossil_strdup(z);
2f9920a… drh 492 verify_all_options();
2f9920a… drh 493 if( g.argc<3 || g.argc>4 ){
788e68b… andybradford 494 usage("URL ?VERSION? ?OPTIONS?");
2f9920a… drh 495 }
2f9920a… drh 496 zUrl = g.argv[2];
ca0d66b… danield 497 zVers = g.argc==4 ? g.argv[3] : db_main_branch();
2f9920a… drh 498
2f9920a… drh 499 /* Parse the URL of the repository */
2f9920a… drh 500 url_parse(zUrl, 0);
2f9920a… drh 501
2f9920a… drh 502 /* Construct an appropriate name for the destination directory */
2f9920a… drh 503 if( zDest==0 ){
2f9920a… drh 504 int i;
2f9920a… drh 505 const char *zTail;
2f9920a… drh 506 const char *zDot;
2f9920a… drh 507 int n;
2f9920a… drh 508 if( g.url.isFile ){
2f9920a… drh 509 zTail = file_tail(g.url.name);
2f9920a… drh 510 }else{
2f9920a… drh 511 zTail = file_tail(g.url.path);
2f9920a… drh 512 }
2f9920a… drh 513 zDot = strchr(zTail,'.');
2f9920a… drh 514 if( zDot==0 ) zDot = zTail+strlen(zTail);
2f9920a… drh 515 n = (int)(zDot - zTail);
2f9920a… drh 516 zDest = mprintf("%.*s-%s", n, zTail, zVers);
2f9920a… drh 517 for(i=0; zDest[i]; i++){
2f9920a… drh 518 char c = zDest[i];
2f9920a… drh 519 if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
2f9920a… drh 520 zDest[i] = '-';
2f9920a… drh 521 }
2f9920a… drh 522 }
2f9920a… drh 523 }
2f9920a… drh 524 if( bDebug ){
2f9920a… drh 525 fossil_print("dest = %s\n", zDest);
2f9920a… drh 526 }
2f9920a… drh 527
2f9920a… drh 528 /* Error checking */
2f9920a… drh 529 if( zDest!=file_tail(zDest) ){
2f9920a… drh 530 fossil_fatal("--dest must be a simple directory name, not a path");
2f9920a… drh 531 }
2f9920a… drh 532 if( zVers!=file_tail(zVers) ){
196cb77… andybradford 533 fossil_fatal("The \"fossil get\" command does not currently work with"
2f9920a… drh 534 " version names that contain \"/\". This will be fixed in"
2f9920a… drh 535 " a future release.");
2f9920a… drh 536 }
2f9920a… drh 537 /* To relax the restrictions above, change the subpath URL formula below
2f9920a… drh 538 ** to use query parameters. Ex: /sqlar?r=%t&name=%t */
2f9920a… drh 539
2f9920a… drh 540 if( !forceFlag ){
2f9920a… drh 541 if( zSqlArchive ){
2f9920a… drh 542 if( file_isdir(zSqlArchive, ExtFILE)>0 ){
2f9920a… drh 543 fossil_fatal("file already exists: \"%s\"", zSqlArchive);
2f9920a… drh 544 }
2f9920a… drh 545 }else if( file_isdir(zDest, ExtFILE)>0 ){
2f9920a… drh 546 if( fossil_strcmp(zDest,".")==0 ){
ff7fe39… drh 547 if( file_directory_list(zDest,0,1,1,0) ){
2f9920a… drh 548 fossil_fatal("current directory is not empty");
2f9920a… drh 549 }
2f9920a… drh 550 }else{
2f9920a… drh 551 fossil_fatal("\"%s\" already exists", zDest);
2f9920a… drh 552 }
2f9920a… drh 553 }
2f9920a… drh 554 }
2f9920a… drh 555
2f9920a… drh 556 /* Construct a subpath on the URL if necessary */
be76a57… drh 557 if( g.url.isFile ){
2f9920a… drh 558 g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
2f9920a… drh 559 }else{
2f9920a… drh 560 g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
2f9920a… drh 561 }
2f9920a… drh 562
2f9920a… drh 563 if( bDebug ){
2f9920a… drh 564 urlparse_print(0);
2f9920a… drh 565 }
2f9920a… drh 566
2f9920a… drh 567 /* Fetch the ZIP archive for the requested check-in */
2f9920a… drh 568 blob_init(&in, 0, 0);
2f9920a… drh 569 blob_init(&out, 0, 0);
2f9920a… drh 570 if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
2f9920a… drh 571 if( bQuiet ) mHttpFlags |= HTTP_QUIET;
2f9920a… drh 572 rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
2f9920a… drh 573 if( rc
2f9920a… drh 574 || out.nUsed<512
2f9920a… drh 575 || (out.nUsed%512)!=0
2f9920a… drh 576 || memcmp(out.aData,"SQLite format 3",16)!=0
2f9920a… drh 577 ){
2f9920a… drh 578 fossil_fatal("Server did not return the requested check-in.");
2f9920a… drh 579 }
2f9920a… drh 580
2f9920a… drh 581 if( zSqlArchive ){
2f9920a… drh 582 blob_write_to_file(&out, zSqlArchive);
2f9920a… drh 583 if( bVerbose ) fossil_print("%s\n", zSqlArchive);
2f9920a… drh 584 return;
2f9920a… drh 585 }
2f9920a… drh 586
2f9920a… drh 587 rc = sqlite3_open(":memory:", &db);
2f9920a… drh 588 if( rc==SQLITE_OK ){
2f9920a… drh 589 int sz = blob_size(&out);
2f9920a… drh 590 rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
2f9920a… drh 591 SQLITE_DESERIALIZE_READONLY);
2f9920a… drh 592 }
2f9920a… drh 593 if( rc!=SQLITE_OK ){
2f9920a… drh 594 fossil_fatal("Cannot create an in-memory database: %s",
2f9920a… drh 595 sqlite3_errmsg(db));
2f9920a… drh 596 }
2a387c9… drh 597 zSql = mprintf("SELECT name, mode, sz, data, mtime FROM sqlar"
2f9920a… drh 598 " WHERE name GLOB '%q*'", zDest);
2f9920a… drh 599 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
2f9920a… drh 600 fossil_free(zSql);
2f9920a… drh 601 if( rc!=0 ){
2f9920a… drh 602 fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
2f9920a… drh 603 }
2f9920a… drh 604 blob_init(&file, 0, 0);
2f9920a… drh 605 while( sqlite3_step(pStmt)==SQLITE_ROW ){
2f9920a… drh 606 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
2f9920a… drh 607 int mode = sqlite3_column_int(pStmt, 1);
2f9920a… drh 608 int sz = sqlite3_column_int(pStmt, 2);
2f9920a… drh 609 if( bList ){
2f9920a… drh 610 fossil_print("%s\n", zFilename);
2f9920a… drh 611 }else if( mode & 0x4000 ){
2f9920a… drh 612 /* A directory name */
2f9920a… drh 613 nDir++;
2f9920a… drh 614 file_mkdir(zFilename, ExtFILE, 1);
2f9920a… drh 615 }else{
2f9920a… drh 616 /* A file */
2f9920a… drh 617 unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
2f9920a… drh 618 unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
2f9920a… drh 619 unsigned long int nOut2 = (unsigned long int)sz;
2f9920a… drh 620 nFile++;
2f9920a… drh 621 nByte += sz;
2f9920a… drh 622 blob_resize(&file, sz);
2f9920a… drh 623 if( nIn<sz ){
2f9920a… drh 624 rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
2f9920a… drh 625 inBuf, nIn);
2f9920a… drh 626 if( rc!=Z_OK ){
2f9920a… drh 627 fossil_fatal("Failed to uncompress file %s", zFilename);
2f9920a… drh 628 }
2f9920a… drh 629 }else{
2f9920a… drh 630 memcpy(blob_buffer(&file), inBuf, sz);
2f9920a… drh 631 }
2f9920a… drh 632 blob_write_to_file(&file, zFilename);
2f9920a… drh 633 if( mode & 0x40 ){
2f9920a… drh 634 file_setexe(zFilename, 1);
2f9920a… drh 635 }
2f9920a… drh 636 blob_zero(&file);
2a387c9… drh 637 file_set_mtime(zFilename, sqlite3_column_int64(pStmt,4));
2f9920a… drh 638 if( bVerbose ){
2f9920a… drh 639 fossil_print("%s\n", zFilename);
2f9920a… drh 640 }
2f9920a… drh 641 }
2f9920a… drh 642 }
2f9920a… drh 643 sqlite3_finalize(pStmt);
2f9920a… drh 644 sqlite3_close(db);
2f9920a… drh 645 blob_zero(&out);
2f9920a… drh 646 if( !bVerbose && !bQuiet && nFile>0 && zDest ){
2f9920a… drh 647 fossil_print("%d files (%,lld bytes) written into %s",
2f9920a… drh 648 nFile, nByte, zDest);
2f9920a… drh 649 if( nDir>1 ){
2f9920a… drh 650 fossil_print(" and %d subdirectories\n", nDir-1);
2f9920a… drh 651 }else{
2f9920a… drh 652 fossil_print("\n");
2f9920a… drh 653 }
2f9920a… drh 654 }
dbda8d6… drh 655 }

Keyboard Shortcuts

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