Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch branch-1.19 Excluding Merge-Ins
This is equivalent to a diff from 6517b5c857 to 285eeba64f
2012-11-17
| ||
19:22 | Prevent delta loops on sync operations that might otherwise occur if a sequence of file changes ends with a file back to its original state after a sequence of two or more intermediate states. The is a backport/cherrypick of check-in [141b990722ea81e10e5] Leaf check-in: 285eeba64f user: drh tags: branch-1.19 | |
2012-11-14
| ||
20:28 | Detect infinite loops in the DELTA table and abort out of content_get() when they are found. Fix an off-by-one error in the version-3 clone protocol. This error might cause an incomplete and corrupt clone if a transfer block fills up just before sending the very last blob. Backport of fixes from 2012-08-23 21:15:36 check-in: 99053ab141 user: drh tags: branch-1.19 | |
2012-10-11
| ||
19:45 | Cherrypick changes [0c37874941c8972], [9ba8a393fcc569b], and [ae092ec605eed11] in order to backport the --setmtime option of "fossil update" and the --age and -t options of "fossil ls" to version 1.19. check-in: 773c6c5f2c user: drh tags: branch-1.19 | |
2011-09-01
| ||
20:23 | Stop publishing x64 binaries for linux. x86 binaries are sufficient. check-in: bd04a48925 user: drh tags: trunk | |
18:25 | Version 1.19. check-in: 6517b5c857 user: drh tags: trunk, release, version-1.19 | |
17:45 | Merging the unwanted two trunk leaves. check-in: a22c381757 user: viriketo tags: trunk | |
Changes to src/checkin.c.
151 151 int vid; 152 152 int useSha1sum = find_option("sha1sum", 0, 0)!=0; 153 153 int cwdRelative = 0; 154 154 db_must_be_within_tree(); 155 155 cwdRelative = determine_cwd_relative_option(); 156 156 blob_zero(&report); 157 157 vid = db_lget_int("checkout", 0); 158 - vfile_check_signature(vid, 0, useSha1sum); 158 + vfile_check_signature(vid, useSha1sum ? CKSIG_SHA1 : 0); 159 159 status_report(&report, "", 0, cwdRelative); 160 160 blob_write_to_file(&report, "-"); 161 161 } 162 162 163 163 /* 164 164 ** COMMAND: status 165 165 ** ................................................................................ 189 189 fossil_print("server-code: %s\n", db_get("server-code", "")); 190 190 vid = db_lget_int("checkout", 0); 191 191 if( vid ){ 192 192 show_common_info(vid, "checkout:", 1, 1); 193 193 } 194 194 changes_cmd(); 195 195 } 196 + 197 +/* 198 +** Implementation of the checkin_mtime SQL function 199 +*/ 200 + 196 201 197 202 /* 198 203 ** COMMAND: ls 199 204 ** 200 -** Usage: %fossil ls [-l] 205 +** Usage: %fossil ls ?OPTIONS? ?VERSION? 201 206 ** 202 207 ** Show the names of all files in the current checkout. The -l provides 203 208 ** extra information about each file. 209 +** 210 +** Options: 211 +** -l Provide extra information about each file. 212 +** --age Show when each file was committed 213 +** 214 +** See also: changes, extra, status 204 215 */ 205 216 void ls_cmd(void){ 206 217 int vid; 207 218 Stmt q; 208 219 int isBrief; 220 + int showAge; 221 + char *zOrderBy = "pathname"; 209 222 210 223 isBrief = find_option("l","l", 0)==0; 224 + showAge = find_option("age",0,0)!=0; 211 225 db_must_be_within_tree(); 212 226 vid = db_lget_int("checkout", 0); 213 - vfile_check_signature(vid, 0, 0); 214 - db_prepare(&q, 215 - "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" 216 - " FROM vfile" 217 - " ORDER BY 1" 218 - ); 227 + if( find_option("t","t",0)!=0 ){ 228 + if( showAge ){ 229 + zOrderBy = mprintf("checkin_mtime(%d,rid) DESC", vid); 230 + }else{ 231 + zOrderBy = "mtime DESC"; 232 + } 233 + } 234 + verify_all_options(); 235 + vfile_check_signature(vid, 0); 236 + if( showAge ){ 237 + db_prepare(&q, 238 + "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)," 239 + " datetime(checkin_mtime(%d,rid),'unixepoch','localtime')" 240 + " FROM vfile" 241 + " ORDER BY %s", vid, zOrderBy 242 + ); 243 + }else{ 244 + db_prepare(&q, 245 + "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" 246 + " FROM vfile" 247 + " ORDER BY %s", zOrderBy 248 + ); 249 + } 219 250 while( db_step(&q)==SQLITE_ROW ){ 220 251 const char *zPathname = db_column_text(&q,0); 221 252 int isDeleted = db_column_int(&q, 1); 222 253 int isNew = db_column_int(&q,2)==0; 223 254 int chnged = db_column_int(&q,3); 224 255 int renamed = db_column_int(&q,4); 225 256 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 226 - if( isBrief ){ 257 + if( showAge ){ 258 + fossil_print("%s %s\n", db_column_text(&q, 5), zPathname); 259 + }else if( isBrief ){ 227 260 fossil_print("%s\n", zPathname); 228 261 }else if( isNew ){ 229 262 fossil_print("ADDED %s\n", zPathname); 230 263 }else if( isDeleted ){ 231 264 fossil_print("DELETED %s\n", zPathname); 232 265 }else if( !file_isfile(zFullName) ){ 233 266 if( file_access(zFullName, 0)==0 ){
Changes to src/checkout.c.
31 31 ** 2: There is no existing checkout 32 32 */ 33 33 int unsaved_changes(void){ 34 34 int vid; 35 35 db_must_be_within_tree(); 36 36 vid = db_lget_int("checkout",0); 37 37 if( vid==0 ) return 2; 38 - vfile_check_signature(vid, 1, 0); 38 + vfile_check_signature(vid, CKSIG_ENOTFILE); 39 39 return db_exists("SELECT 1 FROM vfile WHERE chnged" 40 40 " OR coalesce(origname!=pathname,0)"); 41 41 } 42 42 43 43 /* 44 44 ** Undo the current check-out. Unlink all files from the disk. 45 45 ** Clear the VFILE table.
Changes to src/content.c.
267 267 a[0] = rid; 268 268 a[1] = nextRid; 269 269 n = 1; 270 270 while( !bag_find(&contentCache.inCache, nextRid) 271 271 && (nextRid = findSrcid(nextRid))>0 ){ 272 272 n++; 273 273 if( n>=nAlloc ){ 274 + if( n>db_int(0, "SELECT max(rid) FROM blob") ){ 275 + fossil_panic("infinite loop in DELTA table"); 276 + } 274 277 nAlloc = nAlloc*2 + 10; 275 278 a = fossil_realloc(a, nAlloc*sizeof(a[0])); 276 279 } 277 280 a[n] = nextRid; 278 281 } 279 282 mx = n; 280 283 rc = content_get(a[n], pBlob);
Changes to src/db.c.
625 625 sqlite3_context *context, 626 626 int argc, 627 627 sqlite3_value **argv 628 628 ){ 629 629 sqlite3_result_int64(context, time(0)); 630 630 } 631 631 632 +/* 633 +** Function to return the check-in time for a file. 634 +*/ 635 +void db_checkin_mtime_function( 636 + sqlite3_context *context, 637 + int argc, 638 + sqlite3_value **argv 639 +){ 640 + i64 mtime; 641 + int rc = mtime_of_manifest_file(sqlite3_value_int(argv[0]), 642 + sqlite3_value_int(argv[1]), &mtime); 643 + if( rc==0 ){ 644 + sqlite3_result_int64(context, mtime); 645 + } 646 +} 647 + 632 648 633 649 /* 634 650 ** Open a database file. Return a pointer to the new database 635 651 ** connection. An error results in process abort. 636 652 */ 637 653 static sqlite3 *openDatabase(const char *zDbName){ 638 654 int rc; ................................................................................ 647 663 ); 648 664 if( rc!=SQLITE_OK ){ 649 665 db_err(sqlite3_errmsg(db)); 650 666 } 651 667 sqlite3_busy_timeout(db, 5000); 652 668 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ 653 669 sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); 670 + sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_ANY, 0, 671 + db_checkin_mtime_function, 0, 0); 654 672 return db; 655 673 } 656 674 657 675 658 676 /* 659 677 ** zDbName is the name of a database file. If no other database 660 678 ** file is open, then open this one. If another database file is 661 679 ** already open, then attach zDbName using the name zLabel. 662 680 */ 663 -static void db_open_or_attach(const char *zDbName, const char *zLabel){ 681 +void db_open_or_attach(const char *zDbName, const char *zLabel){ 664 682 if( !g.db ){ 665 683 g.db = openDatabase(zDbName); 666 684 g.zMainDbType = zLabel; 667 685 db_connection_init(); 668 686 }else{ 669 687 db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); 670 688 }
Changes to src/descendants.c.
154 154 } 155 155 } 156 156 157 157 /* 158 158 ** Load the record ID rid and up to N-1 closest ancestors into 159 159 ** the "ok" table. 160 160 */ 161 -void compute_ancestors(int rid, int N){ 161 +void compute_ancestors(int rid, int N, int directOnly){ 162 162 Bag seen; 163 163 PQueue queue; 164 164 Stmt ins; 165 165 Stmt q; 166 166 bag_init(&seen); 167 167 pqueue_init(&queue); 168 168 bag_insert(&seen, rid); 169 169 pqueue_insert(&queue, rid, 0.0, 0); 170 170 db_prepare(&ins, "INSERT OR IGNORE INTO ok VALUES(:rid)"); 171 171 db_prepare(&q, 172 172 "SELECT a.pid, b.mtime FROM plink a LEFT JOIN plink b ON b.cid=a.pid" 173 - " WHERE a.cid=:rid" 173 + " WHERE a.cid=:rid %s", 174 + directOnly ? " AND a.isprim" : "" 174 175 ); 175 176 while( (N--)>0 && (rid = pqueue_extract(&queue, 0))!=0 ){ 176 177 db_bind_int(&ins, ":rid", rid); 177 178 db_step(&ins); 178 179 db_reset(&ins); 179 180 db_bind_int(&q, ":rid", rid); 180 181 while( db_step(&q)==SQLITE_ROW ){ ................................................................................ 200 201 ** direct ancestor as the largest generation number. 201 202 */ 202 203 void compute_direct_ancestors(int rid, int N){ 203 204 Stmt ins; 204 205 Stmt q; 205 206 int gen = 0; 206 207 db_multi_exec( 207 - "CREATE TEMP TABLE ancestor(rid INTEGER, generation INTEGER PRIMARY KEY);" 208 + "CREATE TEMP TABLE IF NOT EXISTS ancestor(rid INTEGER," 209 + " generation INTEGER PRIMARY KEY);" 210 + "DELETE FROM ancestor;" 208 211 "INSERT INTO ancestor VALUES(%d, 0);", rid 209 212 ); 210 213 db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)"); 211 214 db_prepare(&q, 212 215 "SELECT pid FROM plink" 213 216 " WHERE cid=:rid AND isprim" 214 217 ); ................................................................................ 222 225 db_bind_int(&ins, ":gen", gen); 223 226 db_step(&ins); 224 227 db_reset(&ins); 225 228 } 226 229 db_finalize(&ins); 227 230 db_finalize(&q); 228 231 } 232 + 233 +/* 234 +** Compute the "mtime" of the file given whose blob.rid is "fid" that 235 +** is part of check-in "vid". The mtime will be the mtime on vid or 236 +** some ancestor of vid where fid first appears. 237 +*/ 238 +int mtime_of_manifest_file( 239 + int vid, /* The check-in that contains fid */ 240 + int fid, /* The id of the file whose check-in time is sought */ 241 + i64 *pMTime /* Write result here */ 242 +){ 243 + static int prevVid = -1; 244 + static Stmt q; 245 + 246 + if( prevVid!=vid ){ 247 + prevVid = vid; 248 + db_multi_exec("DROP TABLE IF EXISTS temp.ok;" 249 + "CREATE TEMP TABLE ok(x INTEGER PRIMARY KEY);"); 250 + compute_ancestors(vid, 100000000, 1); 251 + } 252 + db_static_prepare(&q, 253 + "SELECT (max(event.mtime)-2440587.5)*86400 FROM mlink, event" 254 + " WHERE mlink.mid=event.objid" 255 + " AND +mlink.mid IN ok" 256 + " AND mlink.fid=:fid"); 257 + db_bind_int(&q, ":fid", fid); 258 + if( db_step(&q)!=SQLITE_ROW ){ 259 + db_reset(&q); 260 + return 1; 261 + } 262 + *pMTime = db_column_int64(&q, 0); 263 + db_reset(&q); 264 + return 0; 265 +} 229 266 230 267 /* 231 268 ** Load the record ID rid and up to N-1 closest descendants into 232 269 ** the "ok" table. 233 270 */ 234 271 void compute_descendants(int rid, int N){ 235 272 Bag seen;
Changes to src/diffcmd.c.
210 210 Stmt q; 211 211 int ignoreEolWs; /* Ignore end-of-line whitespace */ 212 212 int asNewFile; /* Treat non-existant files as empty files */ 213 213 214 214 ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0; 215 215 asNewFile = (diffFlags & DIFF_NEWFILE)!=0; 216 216 vid = db_lget_int("checkout", 0); 217 - vfile_check_signature(vid, 1, 0); 217 + vfile_check_signature(vid, CKSIG_ENOTFILE); 218 218 blob_zero(&sql); 219 219 db_begin_transaction(); 220 220 if( zFrom ){ 221 221 int rid = name_to_typed_rid(zFrom, "ci"); 222 222 if( !is_a_version(rid) ){ 223 223 fossil_fatal("no such check-in: %s", zFrom); 224 224 }
Changes to src/file.c.
21 21 #include <sys/types.h> 22 22 #include <sys/stat.h> 23 23 #include <unistd.h> 24 24 #include <string.h> 25 25 #include <errno.h> 26 26 #include "file.h" 27 27 28 +#ifdef _WIN32 29 +# include <sys/utime.h> 30 +#else 31 +# include <sys/time.h> 32 +#endif 33 + 28 34 /* 29 35 ** The file status information from the most recent stat() call. 30 36 ** 31 37 ** Use _stati64 rather than stat on windows, in order to handle files 32 38 ** larger than 2GB. 33 39 */ 34 40 #if defined(_WIN32) && defined(__MSVCRT__) ................................................................................ 213 219 chmod(zFilename, buf.st_mode & ~0111); 214 220 rc = 1; 215 221 } 216 222 } 217 223 #endif /* _WIN32 */ 218 224 return rc; 219 225 } 226 + 227 +/* 228 +** Set the mtime for a file. 229 +*/ 230 +void file_set_mtime(const char *zFilename, i64 newMTime){ 231 +#if !defined(_WIN32) 232 + struct timeval tv[2]; 233 + memset(tv, 0, sizeof(tv[0])*2); 234 + tv[0].tv_sec = newMTime; 235 + tv[1].tv_sec = newMTime; 236 + utimes(zFilename, tv); 237 +#else 238 + struct utimbuf tb; 239 + char *zMbcs = fossil_utf8_to_mbcs(zFilename); 240 + tb.actime = newMTime; 241 + tb.modtime = newMTime; 242 + _utime(zMbcs, &tb); 243 + fossil_mbcs_free(zMbcs); 244 +#endif 245 +} 246 + 247 +/* 248 +** COMMAND: test-set-mtime 249 +** 250 +** Usage: %fossil test-set-mtime FILENAME DATE/TIME 251 +** 252 +** Sets the mtime of the named file to the date/time shown. 253 +*/ 254 +void test_set_mtime(void){ 255 + const char *zFile; 256 + char *zDate; 257 + i64 iMTime; 258 + if( g.argc!=4 ){ 259 + usage("test-set-mtime FILENAME DATE/TIME"); 260 + } 261 + db_open_or_attach(":memory:", "mem"); 262 + iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]); 263 + zFile = g.argv[2]; 264 + file_set_mtime(zFile, iMTime); 265 + iMTime = file_mtime(zFile); 266 + zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime); 267 + fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime); 268 +} 220 269 221 270 /* 222 271 ** Delete a file. 223 272 */ 224 273 void file_delete(const char *zFilename){ 225 274 char *z = fossil_utf8_to_mbcs(zFilename); 226 275 unlink(z);
Changes to src/finfo.c.
49 49 int vid; 50 50 51 51 if( g.argc!=3 ) usage("-s|--status FILENAME"); 52 52 vid = db_lget_int("checkout", 0); 53 53 if( vid==0 ){ 54 54 fossil_panic("no checkout to finfo files in"); 55 55 } 56 - vfile_check_signature(vid, 1, 0); 56 + vfile_check_signature(vid, CKSIG_ENOTFILE); 57 57 file_tree_name(g.argv[2], &fname, 1); 58 58 db_prepare(&q, 59 59 "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" 60 60 " FROM vfile WHERE vfile.pathname=%B", &fname); 61 61 blob_zero(&line); 62 62 if ( db_step(&q)==SQLITE_ROW ) { 63 63 Blob uuid;
Changes to src/merge.c.
141 141 int t = pid; 142 142 pid = mid; 143 143 mid = t; 144 144 } 145 145 if( !is_a_version(pid) ){ 146 146 fossil_fatal("not a version: record #%d", pid); 147 147 } 148 - vfile_check_signature(vid, 1, 0); 148 + vfile_check_signature(vid, CKSIG_ENOTFILE); 149 149 db_begin_transaction(); 150 150 if( !nochangeFlag ) undo_begin(); 151 151 load_vfile_from_rid(mid); 152 152 load_vfile_from_rid(pid); 153 153 154 154 /* 155 155 ** The vfile.pathname field is used to match files against each other. The
Changes to src/stash.c.
142 142 int vid; /* Current checkout */ 143 143 144 144 zComment = find_option("comment", "m", 1); 145 145 verify_all_options(); 146 146 stashid = db_lget_int("stash-next", 1); 147 147 db_lset_int("stash-next", stashid+1); 148 148 vid = db_lget_int("checkout", 0); 149 - vfile_check_signature(vid, 0, 0); 149 + vfile_check_signature(vid, 0); 150 150 db_multi_exec( 151 151 "INSERT INTO stash(stashid,vid,comment,ctime)" 152 152 "VALUES(%d,%d,%Q,julianday('now'))", 153 153 stashid, vid, zComment 154 154 ); 155 155 if( g.argc>3 ){ 156 156 int i;
Changes to src/timeline.c.
984 984 db_multi_exec("%s", blob_str(&sql)); 985 985 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); 986 986 } 987 987 if( useDividers ) timeline_add_dividers(0, d_rid); 988 988 db_multi_exec("DELETE FROM ok"); 989 989 } 990 990 if( p_rid ){ 991 - compute_ancestors(p_rid, nEntry+1); 991 + compute_ancestors(p_rid, nEntry+1, 0); 992 992 np = db_int(0, "SELECT count(*)-1 FROM ok"); 993 993 if( np>0 ){ 994 994 if( nd>0 ) blob_appendf(&desc, " and "); 995 995 blob_appendf(&desc, "%d ancestors", np); 996 996 db_multi_exec("%s", blob_str(&sql)); 997 997 } 998 998 if( d_rid==0 && useDividers ) timeline_add_dividers(0, p_rid); ................................................................................ 1505 1505 ); 1506 1506 1507 1507 if( mode==3 || mode==4 ){ 1508 1508 db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); 1509 1509 if( mode==3 ){ 1510 1510 compute_descendants(objid, n); 1511 1511 }else{ 1512 - compute_ancestors(objid, n); 1512 + compute_ancestors(objid, n, 0); 1513 1513 } 1514 1514 blob_appendf(&sql, " AND blob.rid IN ok"); 1515 1515 } 1516 1516 if( zType && (zType[0]!='a') ){ 1517 1517 blob_appendf(&sql, " AND event.type=%Q ", zType); 1518 1518 } 1519 1519
Changes to src/update.c.
90 90 int vid; /* Current version */ 91 91 int tid=0; /* Target version - version we are changing to */ 92 92 Stmt q; 93 93 int latestFlag; /* --latest. Pick the latest version if true */ 94 94 int nochangeFlag; /* -n or --nochange. Do a dry run */ 95 95 int verboseFlag; /* -v or --verbose. Output extra information */ 96 96 int debugFlag; /* --debug option */ 97 + int setmtimeFlag; /* --setmtime. Set mtimes on files */ 97 98 int nChng; /* Number of file renames */ 98 99 int *aChng; /* Array of file renames */ 99 100 int i; /* Loop counter */ 100 101 int nConflict = 0; /* Number of merge conflicts */ 101 102 Stmt mtimeXfer; /* Statment to transfer mtimes */ 102 103 103 104 if( !internalUpdate ){ ................................................................................ 104 105 undo_capture_command_line(); 105 106 url_proxy_options(); 106 107 } 107 108 latestFlag = find_option("latest",0, 0)!=0; 108 109 nochangeFlag = find_option("nochange","n",0)!=0; 109 110 verboseFlag = find_option("verbose","v",0)!=0; 110 111 debugFlag = find_option("debug",0,0)!=0; 112 + setmtimeFlag = find_option("setmtime",0,0)!=0; 111 113 db_must_be_within_tree(); 112 114 vid = db_lget_int("checkout", 0); 113 115 if( vid==0 ){ 114 116 fossil_fatal("cannot find current version"); 115 117 } 116 118 if( !nochangeFlag && db_exists("SELECT 1 FROM vmerge") ){ 117 119 fossil_fatal("cannot update an uncommitted merge"); ................................................................................ 184 186 } 185 187 186 188 if( tid==0 ){ 187 189 fossil_panic("Internal Error: unable to find a version to update to."); 188 190 } 189 191 190 192 db_begin_transaction(); 191 - vfile_check_signature(vid, 1, 0); 193 + vfile_check_signature(vid, CKSIG_ENOTFILE); 192 194 if( !nochangeFlag && !internalUpdate ) undo_begin(); 193 195 load_vfile_from_rid(tid); 194 196 195 197 /* 196 198 ** The record.fn field is used to match files against each other. The 197 199 ** FV table contains one row for each each unique filename in 198 200 ** in the current checkout, the pivot, and the version being merged. ................................................................................ 203 205 " fn TEXT PRIMARY KEY," /* The filename relative to root */ 204 206 " idv INTEGER," /* VFILE entry for current version */ 205 207 " idt INTEGER," /* VFILE entry for target version */ 206 208 " chnged BOOLEAN," /* True if current version has been edited */ 207 209 " ridv INTEGER," /* Record ID for current version */ 208 210 " ridt INTEGER," /* Record ID for target */ 209 211 " isexe BOOLEAN," /* Does target have execute permission? */ 212 + " deleted BOOLEAN DEFAULT 0,"/* File marke by "rm" to become unmanaged */ 210 213 " fnt TEXT" /* Filename of same file on target version */ 211 214 ");" 212 215 ); 213 216 214 217 /* Add files found in the current version 215 218 */ 216 219 db_multi_exec( 217 - "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)" 218 - " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged" 220 + "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged,deleted)" 221 + " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged, deleted" 219 222 " FROM vfile WHERE vid=%d", 220 223 vid 221 224 ); 222 225 223 226 /* Compute file name changes on V->T. Record name changes in files that 224 227 ** have changed locally. 225 228 */ ................................................................................ 307 310 } 308 311 309 312 /* 310 313 ** Alter the content of the checkout so that it conforms with the 311 314 ** target 312 315 */ 313 316 db_prepare(&q, 314 - "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe FROM fv ORDER BY 1" 317 + "SELECT fn, idv, ridv, idt, ridt, chnged, fnt," 318 + " isexe, deleted FROM fv ORDER BY 1" 315 319 ); 316 320 db_prepare(&mtimeXfer, 317 321 "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)" 318 322 " WHERE id=:idt" 319 323 ); 320 324 assert( g.zLocalRoot!=0 ); 321 325 assert( strlen(g.zLocalRoot)>1 ); ................................................................................ 325 329 int idv = db_column_int(&q, 1); /* VFILE entry for current */ 326 330 int ridv = db_column_int(&q, 2); /* RecordID for current */ 327 331 int idt = db_column_int(&q, 3); /* VFILE entry for target */ 328 332 int ridt = db_column_int(&q, 4); /* RecordID for target */ 329 333 int chnged = db_column_int(&q, 5); /* Current is edited */ 330 334 const char *zNewName = db_column_text(&q,6);/* New filename */ 331 335 int isexe = db_column_int(&q, 7); /* EXE perm for new file */ 336 + int deleted = db_column_int(&q, 8); /* Marked for deletion */ 332 337 char *zFullPath; /* Full pathname of the file */ 333 338 char *zFullNewPath; /* Full pathname of dest */ 334 339 char nameChng; /* True if the name changed */ 335 340 336 341 zFullPath = mprintf("%s%s", g.zLocalRoot, zName); 337 342 zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName); 338 343 nameChng = fossil_strcmp(zName, zNewName); 344 + if( deleted ){ 345 + db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt); 346 + } 339 347 if( idv>0 && ridv==0 && idt>0 && ridt>0 ){ 340 348 /* Conflict. This file has been added to the current checkout 341 349 ** but also exists in the target checkout. Use the current version. 342 350 */ 343 351 fossil_print("CONFLICT %s\n", zName); 344 352 nConflict++; 345 353 }else if( idt>0 && idv==0 ){ 346 354 /* File added in the target. */ 347 355 fossil_print("ADD %s\n", zName); 348 356 undo_save(zName); 349 357 if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0); 350 - }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){ 358 + }else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){ 351 359 /* The file is unedited. Change it to the target version */ 352 360 undo_save(zName); 353 - fossil_print("UPDATE %s\n", zName); 361 + if( deleted ){ 362 + fossil_print("UPDATE %s - change to unmanged file\n", zName); 363 + }else{ 364 + fossil_print("UPDATE %s\n", zName); 365 + } 354 366 if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0); 355 367 }else if( idt>0 && idv>0 && file_size(zFullPath)<0 ){ 356 368 /* The file missing from the local check-out. Restore it to the 357 369 ** version that appears in the target. */ 358 - fossil_print("UPDATE %s\n", zName); 370 + fossil_print("UPDATE %s%s\n", zName, 371 + deleted?" - change to unmanaged file":""); 359 372 undo_save(zName); 360 373 if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0); 361 374 }else if( idt==0 && idv>0 ){ 362 375 if( ridv==0 ){ 363 376 /* Added in current checkout. Continue to hold the file as 364 377 ** as an addition */ 365 378 db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); ................................................................................ 454 467 db_lset_int("checkout", tid); 455 468 }else{ 456 469 /* A subset of files have been checked out. Keep the current 457 470 ** checkout unchanged. */ 458 471 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); 459 472 } 460 473 if( !internalUpdate ) undo_finish(); 474 + if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME); 461 475 db_end_transaction(0); 462 476 } 463 477 } 464 478 465 479 /* 466 480 ** Make sure empty directories are created 467 481 */ ................................................................................ 608 622 file_tree_name(zFile, &fname, 1); 609 623 db_multi_exec("REPLACE INTO torevert VALUES(%B)", &fname); 610 624 blob_reset(&fname); 611 625 } 612 626 }else{ 613 627 int vid; 614 628 vid = db_lget_int("checkout", 0); 615 - vfile_check_signature(vid, 0, 0); 629 + vfile_check_signature(vid, 0); 616 630 db_multi_exec( 617 631 "DELETE FROM vmerge;" 618 632 "INSERT INTO torevert " 619 633 "SELECT pathname" 620 634 " FROM vfile " 621 635 " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" 622 636 );
Changes to src/vfile.c.
120 120 } 121 121 db_finalize(&ridq); 122 122 db_finalize(&ins); 123 123 manifest_destroy(p); 124 124 db_end_transaction(0); 125 125 } 126 126 127 -/* 128 -** Check the file signature of the disk image for every VFILE of vid. 129 -** 130 -** Set the VFILE.CHNGED field on every file that has changed. Also 131 -** set VFILE.CHNGED on every folder that contains a file or folder 132 -** that has changed. 133 -** 134 -** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume 135 -** the file has changed without having the check the on-disk image. 136 -** 137 -** If the size of the file has changed, then we assume that it has 138 -** changed. If the mtime of the file has not changed and useSha1sum is false 139 -** and the mtime-changes setting is true (the default) then we assume that 140 -** the file has not changed. If the mtime has changed, we go ahead and 141 -** double-check that the file has changed by looking at its SHA1 sum. 127 +#if INTERFACE 128 +/* 129 +** The cksigFlags parameter to vfile_check_signature() is an OR-ed 130 +** combination of the following bits: 131 +*/ 132 +#define CKSIG_ENOTFILE 0x001 /* non-file FS objects throw an error */ 133 +#define CKSIG_SHA1 0x002 /* Verify file content using sha1sum */ 134 +#define CKSIG_SETMTIME 0x004 /* Set mtime to last check-out time */ 135 + 136 +#endif /* INTERFACE */ 137 + 138 +/* 139 +** Look at every VFILE entry with the given vid and update 140 +** VFILE.CHNGED field according to whether or not 141 +** the file has changed. 0 means no change. 1 means edited. 2 means 142 +** the file has changed due to a merge. 3 means the file was added 143 +** by a merge. 144 +** 145 +** If VFILE.DELETED is true or if VFILE.RID is zero, then the file was either 146 +** removed from configuration management via "fossil rm" or added via 147 +** "fossil add", respectively, and in both cases we always know that 148 +** the file has changed without having the check the size, mtime, 149 +** or on-disk content. 150 +** 151 +** If the size of the file has changed, then we always know that the file 152 +** changed without having to look at the mtime or on-disk content. 153 +** 154 +** The mtime of the file is only a factor if the mtime-changes setting 155 +** is false and the useSha1sum flag is false. If the mtime-changes 156 +** setting is true (or undefined - it defaults to true) or if useSha1sum 157 +** is true, then we do not trust the mtime and will examine the on-disk 158 +** content to determine if a file really is the same. 159 +** 160 +** If the mtime is used, it is used only to determine if files are the same. 161 +** If the mtime of a file has changed, we still examine the on-disk content 162 +** to see whether or not the edit was a null-edit. 142 163 */ 143 -void vfile_check_signature(int vid, int notFileIsFatal, int useSha1sum){ 164 +void vfile_check_signature(int vid, unsigned int cksigFlags){ 144 165 int nErr = 0; 145 166 Stmt q; 146 167 Blob fileCksum, origCksum; 147 - int checkMtime = useSha1sum==0 && db_get_boolean("mtime-changes", 1); 168 + int useMtime = (cksigFlags & CKSIG_SHA1)==0 169 + && db_get_boolean("mtime-changes", 1); 148 170 149 171 db_begin_transaction(); 150 172 db_prepare(&q, "SELECT id, %Q || pathname," 151 173 " vfile.mrid, deleted, chnged, uuid, size, mtime" 152 174 " FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid" 153 175 " WHERE vid=%d ", g.zLocalRoot, vid); 154 176 while( db_step(&q)==SQLITE_ROW ){ 155 177 int id, rid, isDeleted; 156 178 const char *zName; 157 179 int chnged = 0; 158 180 int oldChnged; 159 181 i64 oldMtime; 160 182 i64 currentMtime; 183 + i64 origSize; 184 + i64 currentSize; 161 185 162 186 id = db_column_int(&q, 0); 163 187 zName = db_column_text(&q, 1); 164 188 rid = db_column_int(&q, 2); 165 189 isDeleted = db_column_int(&q, 3); 166 - oldChnged = db_column_int(&q, 4); 190 + oldChnged = chnged = db_column_int(&q, 4); 167 191 oldMtime = db_column_int64(&q, 7); 168 - if( isDeleted ){ 192 + currentSize = file_size(zName); 193 + origSize = db_column_int64(&q, 6); 194 + currentMtime = file_mtime(0); 195 + if( chnged==0 && (isDeleted || rid==0) ){ 196 + /* "fossil rm" or "fossil add" always change the file */ 169 197 chnged = 1; 170 - }else if( !file_isfile(zName) && file_size(0)>=0 ){ 171 - if( notFileIsFatal ){ 198 + }else if( !file_isfile(0) && currentSize>=0 ){ 199 + if( cksigFlags & CKSIG_ENOTFILE ){ 172 200 fossil_warning("not an ordinary file: %s", zName); 173 201 nErr++; 174 202 } 175 203 chnged = 1; 176 - }else if( oldChnged>=2 ){ 177 - chnged = oldChnged; 178 - }else if( rid==0 ){ 179 - chnged = 1; 180 204 } 181 - if( chnged!=1 ){ 182 - i64 origSize = db_column_int64(&q, 6); 183 - currentMtime = file_mtime(0); 184 - if( origSize!=file_size(0) ){ 205 + if( origSize!=currentSize ){ 206 + if( chnged!=1 ){ 185 207 /* A file size change is definitive - the file has changed. No 186 - ** need to check the sha1sum */ 208 + ** need to check the mtime or sha1sum */ 187 209 chnged = 1; 188 210 } 189 - } 190 - if( chnged!=1 && (checkMtime==0 || currentMtime!=oldMtime) ){ 211 + }else if( chnged==1 && rid!=0 && !isDeleted ){ 212 + /* File is believed to have changed but it is the same size. 213 + ** Double check that it really has changed by looking at content. */ 214 + assert( origSize==currentSize ); 215 + db_ephemeral_blob(&q, 5, &origCksum); 216 + if( sha1sum_file(zName, &fileCksum) ){ 217 + blob_zero(&fileCksum); 218 + } 219 + if( blob_compare(&fileCksum, &origCksum)==0 ) chnged = 0; 220 + blob_reset(&origCksum); 221 + blob_reset(&fileCksum); 222 + }else if( (chnged==0 || chnged==2) 223 + && (useMtime==0 || currentMtime!=oldMtime) ){ 224 + /* For files that were formerly believed to be unchanged or that were 225 + ** changed by merging, if their mtime changes, or unconditionally 226 + ** if --sha1sum is used, check to see if they have been edited by 227 + ** looking at their SHA1 sum */ 228 + assert( origSize==currentSize ); 191 229 db_ephemeral_blob(&q, 5, &origCksum); 192 230 if( sha1sum_file(zName, &fileCksum) ){ 193 231 blob_zero(&fileCksum); 194 232 } 195 233 if( blob_compare(&fileCksum, &origCksum) ){ 196 234 chnged = 1; 197 - }else if( currentMtime!=oldMtime ){ 198 - db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", 199 - currentMtime, id); 200 235 } 201 236 blob_reset(&origCksum); 202 237 blob_reset(&fileCksum); 203 238 } 204 - if( chnged!=oldChnged && (chnged || !checkMtime) ){ 205 - db_multi_exec("UPDATE vfile SET chnged=%d WHERE id=%d", chnged, id); 239 + if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2) ){ 240 + i64 desiredMtime; 241 + if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){ 242 + if( currentMtime!=desiredMtime ){ 243 + file_set_mtime(zName, desiredMtime); 244 + currentMtime = file_mtime(zName); 245 + } 246 + } 247 + } 248 + if( currentMtime!=oldMtime || chnged!=oldChnged ){ 249 + db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d", 250 + currentMtime, chnged, id); 206 251 } 207 252 } 208 253 db_finalize(&q); 209 254 if( nErr ) fossil_fatal("abort due to prior errors"); 210 255 db_end_transaction(0); 211 256 } 212 257
Changes to src/xfer.c.
266 266 int isPrivate, /* True if rid is a private artifact */ 267 267 Blob *pContent, /* The content of the file to send */ 268 268 Blob *pUuid /* The UUID of the file to send */ 269 269 ){ 270 270 static const char *azQuery[] = { 271 271 "SELECT pid FROM plink x" 272 272 " WHERE cid=%d" 273 - " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" 274 - " AND NOT EXISTS(SELECT 1 FROM plink y" 275 - " WHERE y.pid=x.cid AND y.cid=x.pid)", 276 - 277 - "SELECT pid FROM mlink x" 273 + " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", 274 + 275 + "SELECT pid, min(mtime) FROM mlink, event ON mlink.mid=event.objid" 278 276 " WHERE fid=%d" 279 277 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" 280 - " AND NOT EXISTS(SELECT 1 FROM mlink y" 281 - " WHERE y.pid=x.fid AND y.fid=x.pid)" 282 278 }; 283 279 int i; 284 280 Blob src, delta; 285 281 int size = 0; 286 282 int srcId = 0; 287 283 288 284 for(i=0; srcId==0 && i<count(azQuery); i++){ ................................................................................ 992 988 if( iVers>=3 ){ 993 989 send_compressed_file(&xfer, seqno); 994 990 }else{ 995 991 send_file(&xfer, seqno, 0, 1); 996 992 } 997 993 seqno++; 998 994 } 999 - if( seqno>=max ) seqno = 0; 995 + if( seqno>max ) seqno = 0; 1000 996 @ clone_seqno %d(seqno) 1001 997 }else{ 1002 998 isClone = 1; 1003 999 isPull = 1; 1004 1000 deltaFlag = 1; 1005 1001 } 1006 1002 @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
Added test/update-test-1.sh.
1 +#!/bin/sh 2 +# 3 +# Run this script in an empty directory. A single argument is the full 4 +# pathname of the fossil binary. Example: 5 +# 6 +# sh update-test-1.sh /home/drh/fossil/m1/fossil 7 +# 8 +export FOSSIL=$1 9 +rm -rf aaa bbb update-test-1.fossil 10 + 11 +# Create a test repository 12 +$FOSSIL new update-test-1.fossil 13 + 14 +# In checkout aaa, add file one.txt 15 +mkdir aaa 16 +cd aaa 17 +$FOSSIL open ../update-test-1.fossil 18 +echo one >one.txt 19 +$FOSSIL add one.txt 20 +$FOSSIL commit -m add-one --tag add-one 21 + 22 +# Open checkout bbb. 23 +mkdir ../bbb 24 +cd ../bbb 25 +$FOSSIL open ../update-test-1.fossil 26 + 27 +# Back in aaa, add file two.txt 28 +cd ../aaa 29 +echo two >two.txt 30 +$FOSSIL add two.txt 31 +$FOSSIL commit -m add-two --tag add-two 32 + 33 +# In bbb, delete file one.txt. Then update the change from aaa that 34 +# adds file two. Verify that one.txt says deleted. 35 +cd ../bbb 36 +$FOSSIL rm one.txt 37 +$FOSSIL changes 38 +echo '========================================================================' 39 +$FOSSIL update 40 +echo '======== The previous should show "ADD two.txt" ========================' 41 +$FOSSIL changes 42 +echo '======== The previous should show "DELETE one.txt" =====================' 43 +$FOSSIL commit --test -m check-in 44 +echo '======== Only file two.txt is checked in ==============================='
Added test/update-test-2.sh.
1 +#!/bin/sh 2 +# 3 +# Run this script in an empty directory. A single argument is the full 4 +# pathname of the fossil binary. Example: 5 +# 6 +# sh update-test-2.sh /home/drh/fossil/m1/fossil 7 +# 8 +export FOSSIL=$1 9 +rm -rf aaa bbb update-test-2.fossil 10 + 11 +# Create a test repository 12 +$FOSSIL new update-test-2.fossil 13 + 14 +# In checkout aaa, add file one.txt. 15 +mkdir aaa 16 +cd aaa 17 +$FOSSIL open ../update-test-2.fossil 18 +echo one >one.txt 19 +$FOSSIL add one.txt 20 +$FOSSIL commit -m add-one --tag add-one 21 + 22 +# Create checkout bbb. 23 +mkdir ../bbb 24 +cd ../bbb 25 +$FOSSIL open ../update-test-2.fossil 26 + 27 +# Back in aaa, make changes to one.txt. Add file two.txt. 28 +cd ../aaa 29 +echo change >>one.txt 30 +echo two >two.txt 31 +$FOSSIL add two.txt 32 +$FOSSIL commit -m 'chng one and add two' --tag add-two 33 + 34 +# In bbb, remove one.txt, then update. 35 +cd ../bbb 36 +$FOSSIL rm one.txt 37 +$FOSSIL changes 38 +echo '========================================================================' 39 +$FOSSIL update 40 +echo '======== Previous should show "ADD two.txt" and conflict on one.txt ====' 41 +$FOSSIL changes 42 +echo '======== The previous should show "DELETE one.txt" =====================' 43 +$FOSSIL commit --test -m 'check-in' 44 +echo '======== Only file two.txt is checked in ==============================='