Fossil SCM

The "fossil undo" and "fossil redo" commands remember file mtimes and restore them appropriately. When upgrading through this version, you may need to run "fossil undo --reset" to clear your old undo stack.

drh 2012-10-10 16:06 trunk
Commit 0c37874941c89728f6cc0063a2a44b8bc2a38c6c
+1 -1
--- src/db.c
+++ src/db.c
@@ -722,11 +722,11 @@
722722
/*
723723
** zDbName is the name of a database file. If no other database
724724
** file is open, then open this one. If another database file is
725725
** already open, then attach zDbName using the name zLabel.
726726
*/
727
-static void db_open_or_attach(const char *zDbName, const char *zLabel){
727
+void db_open_or_attach(const char *zDbName, const char *zLabel){
728728
if( !g.db ){
729729
g.db = openDatabase(zDbName);
730730
g.zMainDbType = zLabel;
731731
db_connection_init();
732732
}else{
733733
--- src/db.c
+++ src/db.c
@@ -722,11 +722,11 @@
722 /*
723 ** zDbName is the name of a database file. If no other database
724 ** file is open, then open this one. If another database file is
725 ** already open, then attach zDbName using the name zLabel.
726 */
727 static void db_open_or_attach(const char *zDbName, const char *zLabel){
728 if( !g.db ){
729 g.db = openDatabase(zDbName);
730 g.zMainDbType = zLabel;
731 db_connection_init();
732 }else{
733
--- src/db.c
+++ src/db.c
@@ -722,11 +722,11 @@
722 /*
723 ** zDbName is the name of a database file. If no other database
724 ** file is open, then open this one. If another database file is
725 ** already open, then attach zDbName using the name zLabel.
726 */
727 void db_open_or_attach(const char *zDbName, const char *zLabel){
728 if( !g.db ){
729 g.db = openDatabase(zDbName);
730 g.zMainDbType = zLabel;
731 db_connection_init();
732 }else{
733
+44
--- src/file.c
+++ src/file.c
@@ -34,10 +34,11 @@
3434
** On Windows, include the Platform SDK header file.
3535
*/
3636
#ifdef _WIN32
3737
# include <direct.h>
3838
# include <windows.h>
39
+# include <sys/utime.h>
3940
#endif
4041
4142
/*
4243
** The file status information from the most recent stat() call.
4344
**
@@ -389,10 +390,53 @@
389390
}
390391
}
391392
#endif /* _WIN32 */
392393
return rc;
393394
}
395
+
396
+/*
397
+** Set the mtime for a file.
398
+*/
399
+void file_set_mtime(const char *zFilename, i64 newMTime){
400
+#if !defined(_WIN32)
401
+ struct timeval tv[2];
402
+ memset(tv, 0, sizeof(tv[0])*2);
403
+ tv[0].tv_sec = newMTime;
404
+ tv[1].tv_sec = newMTime;
405
+ utimes(zFilename, tv);
406
+#else
407
+ struct utimbuf tb;
408
+ wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
409
+ tb.actime = newMTime;
410
+ tb.modtime = newMTime;
411
+ _wutime(zMbcs, &tb);
412
+ fossil_mbcs_free(zMbcs);
413
+#endif
414
+}
415
+
416
+/*
417
+** COMMAND: test-set-mtime
418
+**
419
+** Usage: %fossil test-set-mtime FILENAME DATE/TIME
420
+**
421
+** Sets the mtime of the named file to the date/time shown.
422
+*/
423
+void test_set_mtime(void){
424
+ const char *zFile;
425
+ char *zDate;
426
+ i64 iMTime;
427
+ if( g.argc!=4 ){
428
+ usage("test-set-mtime FILENAME DATE/TIME");
429
+ }
430
+ db_open_or_attach(":memory:", "mem");
431
+ iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
432
+ zFile = g.argv[2];
433
+ file_set_mtime(zFile, iMTime);
434
+ iMTime = file_wd_mtime(zFile);
435
+ zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
436
+ fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
437
+}
394438
395439
/*
396440
** Delete a file.
397441
*/
398442
void file_delete(const char *zFilename){
399443
--- src/file.c
+++ src/file.c
@@ -34,10 +34,11 @@
34 ** On Windows, include the Platform SDK header file.
35 */
36 #ifdef _WIN32
37 # include <direct.h>
38 # include <windows.h>
 
39 #endif
40
41 /*
42 ** The file status information from the most recent stat() call.
43 **
@@ -389,10 +390,53 @@
389 }
390 }
391 #endif /* _WIN32 */
392 return rc;
393 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
395 /*
396 ** Delete a file.
397 */
398 void file_delete(const char *zFilename){
399
--- src/file.c
+++ src/file.c
@@ -34,10 +34,11 @@
34 ** On Windows, include the Platform SDK header file.
35 */
36 #ifdef _WIN32
37 # include <direct.h>
38 # include <windows.h>
39 # include <sys/utime.h>
40 #endif
41
42 /*
43 ** The file status information from the most recent stat() call.
44 **
@@ -389,10 +390,53 @@
390 }
391 }
392 #endif /* _WIN32 */
393 return rc;
394 }
395
396 /*
397 ** Set the mtime for a file.
398 */
399 void file_set_mtime(const char *zFilename, i64 newMTime){
400 #if !defined(_WIN32)
401 struct timeval tv[2];
402 memset(tv, 0, sizeof(tv[0])*2);
403 tv[0].tv_sec = newMTime;
404 tv[1].tv_sec = newMTime;
405 utimes(zFilename, tv);
406 #else
407 struct utimbuf tb;
408 wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
409 tb.actime = newMTime;
410 tb.modtime = newMTime;
411 _wutime(zMbcs, &tb);
412 fossil_mbcs_free(zMbcs);
413 #endif
414 }
415
416 /*
417 ** COMMAND: test-set-mtime
418 **
419 ** Usage: %fossil test-set-mtime FILENAME DATE/TIME
420 **
421 ** Sets the mtime of the named file to the date/time shown.
422 */
423 void test_set_mtime(void){
424 const char *zFile;
425 char *zDate;
426 i64 iMTime;
427 if( g.argc!=4 ){
428 usage("test-set-mtime FILENAME DATE/TIME");
429 }
430 db_open_or_attach(":memory:", "mem");
431 iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
432 zFile = g.argv[2];
433 file_set_mtime(zFile, iMTime);
434 iMTime = file_wd_mtime(zFile);
435 zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
436 fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
437 }
438
439 /*
440 ** Delete a file.
441 */
442 void file_delete(const char *zFilename){
443
+2
--- src/main.c
+++ src/main.c
@@ -555,10 +555,11 @@
555555
newArgv[j] = 0;
556556
g.argc = j;
557557
g.argv = newArgv;
558558
}
559559
560
+#ifdef FOSSIL_ENABLE_TCL
560561
/*
561562
** Make a deep copy of the provided argument array and return it.
562563
*/
563564
static char **copy_args(int argc, char **argv){
564565
char **zNewArgv;
@@ -568,10 +569,11 @@
568569
for(i=0; i<argc; i++){
569570
zNewArgv[i] = fossil_strdup(argv[i]);
570571
}
571572
return zNewArgv;
572573
}
574
+#endif
573575
574576
/*
575577
** This procedure runs first.
576578
*/
577579
int main(int argc, char **argv)
578580
--- src/main.c
+++ src/main.c
@@ -555,10 +555,11 @@
555 newArgv[j] = 0;
556 g.argc = j;
557 g.argv = newArgv;
558 }
559
 
560 /*
561 ** Make a deep copy of the provided argument array and return it.
562 */
563 static char **copy_args(int argc, char **argv){
564 char **zNewArgv;
@@ -568,10 +569,11 @@
568 for(i=0; i<argc; i++){
569 zNewArgv[i] = fossil_strdup(argv[i]);
570 }
571 return zNewArgv;
572 }
 
573
574 /*
575 ** This procedure runs first.
576 */
577 int main(int argc, char **argv)
578
--- src/main.c
+++ src/main.c
@@ -555,10 +555,11 @@
555 newArgv[j] = 0;
556 g.argc = j;
557 g.argv = newArgv;
558 }
559
560 #ifdef FOSSIL_ENABLE_TCL
561 /*
562 ** Make a deep copy of the provided argument array and return it.
563 */
564 static char **copy_args(int argc, char **argv){
565 char **zNewArgv;
@@ -568,10 +569,11 @@
569 for(i=0; i<argc; i++){
570 zNewArgv[i] = fossil_strdup(argv[i]);
571 }
572 return zNewArgv;
573 }
574 #endif
575
576 /*
577 ** This procedure runs first.
578 */
579 int main(int argc, char **argv)
580
+14 -6
--- src/undo.c
+++ src/undo.c
@@ -30,11 +30,11 @@
3030
*/
3131
static void undo_one(const char *zPathname, int redoFlag){
3232
Stmt q;
3333
char *zFullname;
3434
db_prepare(&q,
35
- "SELECT content, existsflag, isExe, isLink FROM undo"
35
+ "SELECT content, existsflag, isExe, isLink, mtime FROM undo"
3636
" WHERE pathname=%Q AND redoflag=%d",
3737
zPathname, redoFlag
3838
);
3939
if( db_step(&q)==SQLITE_ROW ){
4040
int old_exists;
@@ -41,10 +41,11 @@
4141
int new_exists;
4242
int old_exe;
4343
int new_exe;
4444
int new_link;
4545
int old_link;
46
+ i64 old_mtime, new_mtime;
4647
Blob current;
4748
Blob new;
4849
zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
4950
old_link = db_column_int(&q, 3);
5051
new_link = file_wd_islink(zFullname);
@@ -54,17 +55,20 @@
5455
blob_read_link(&current, zFullname);
5556
}else{
5657
blob_read_from_file(&current, zFullname);
5758
}
5859
new_exe = file_wd_isexe(zFullname);
60
+ new_mtime = file_wd_mtime(zFullname);
5961
}else{
6062
blob_zero(&current);
6163
new_exe = 0;
64
+ new_mtime = 0;
6265
}
6366
blob_zero(&new);
6467
old_exists = db_column_int(&q, 1);
6568
old_exe = db_column_int(&q, 2);
69
+ old_mtime = db_column_int64(&q, 4);
6670
if( old_exists ){
6771
db_ephemeral_blob(&q, 0, &new);
6872
}
6973
if( old_exists ){
7074
if( new_exists ){
@@ -79,22 +83,23 @@
7983
symlink_create(blob_str(&new), zFullname);
8084
}else{
8185
blob_write_to_file(&new, zFullname);
8286
}
8387
file_wd_setexe(zFullname, old_exe);
88
+ file_set_mtime(zFullname, old_mtime);
8489
}else{
8590
fossil_print("DELETE %s\n", zPathname);
8691
file_delete(zFullname);
8792
}
8893
blob_reset(&new);
8994
free(zFullname);
9095
db_finalize(&q);
9196
db_prepare(&q,
9297
"UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d,"
93
- " redoflag=NOT redoflag"
98
+ " mtime=%lld, redoflag=NOT redoflag"
9499
" WHERE pathname=%Q",
95
- new_exists, new_exe, new_link, zPathname
100
+ new_exists, new_exe, new_link, new_mtime, zPathname
96101
);
97102
if( new_exists ){
98103
db_bind_blob(&q, ":c", &current);
99104
}
100105
db_step(&q);
@@ -225,10 +230,11 @@
225230
@ pathname TEXT UNIQUE, -- Name of the file
226231
@ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable
227232
@ existsflag BOOLEAN, -- True if the file exists
228233
@ isExe BOOLEAN, -- True if the file is executable
229234
@ isLink BOOLEAN, -- True if the file is symlink
235
+ @ mtime DATETIME, -- File modification time
230236
@ content BLOB -- Saved content
231237
@ );
232238
@ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
233239
@ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
234240
;
@@ -274,13 +280,14 @@
274280
zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
275281
existsFlag = file_wd_size(zFullname)>=0;
276282
isLink = file_wd_islink(zFullname);
277283
db_prepare(&q,
278284
"INSERT OR IGNORE INTO"
279
- " undo(pathname,redoflag,existsflag,isExe,isLink,content)"
280
- " VALUES(%Q,0,%d,%d,%d,:c)",
281
- zPathname, existsFlag, file_wd_isexe(zFullname), isLink
285
+ " undo(pathname,redoflag,existsflag,isExe,isLink,mtime,content)"
286
+ " VALUES(%Q,0,%d,%d,%d,%lld,:c)",
287
+ zPathname, existsFlag, file_wd_isexe(zFullname), isLink,
288
+ file_wd_mtime(zFullname)
282289
);
283290
if( existsFlag ){
284291
if( isLink ){
285292
blob_read_link(&content, zFullname);
286293
}else{
@@ -383,10 +390,11 @@
383390
int isRedo = g.argv[1][0]=='r';
384391
int undo_available;
385392
int explainFlag = find_option("explain", 0, 0)!=0;
386393
const char *zCmd = isRedo ? "redo" : "undo";
387394
db_must_be_within_tree();
395
+ if( find_option("reset", 0, 0)!=0 ){ undo_reset(); return; }
388396
verify_all_options();
389397
db_begin_transaction();
390398
undo_available = db_lget_int("undo_available", 0);
391399
if( explainFlag ){
392400
if( undo_available==0 ){
393401
--- src/undo.c
+++ src/undo.c
@@ -30,11 +30,11 @@
30 */
31 static void undo_one(const char *zPathname, int redoFlag){
32 Stmt q;
33 char *zFullname;
34 db_prepare(&q,
35 "SELECT content, existsflag, isExe, isLink FROM undo"
36 " WHERE pathname=%Q AND redoflag=%d",
37 zPathname, redoFlag
38 );
39 if( db_step(&q)==SQLITE_ROW ){
40 int old_exists;
@@ -41,10 +41,11 @@
41 int new_exists;
42 int old_exe;
43 int new_exe;
44 int new_link;
45 int old_link;
 
46 Blob current;
47 Blob new;
48 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
49 old_link = db_column_int(&q, 3);
50 new_link = file_wd_islink(zFullname);
@@ -54,17 +55,20 @@
54 blob_read_link(&current, zFullname);
55 }else{
56 blob_read_from_file(&current, zFullname);
57 }
58 new_exe = file_wd_isexe(zFullname);
 
59 }else{
60 blob_zero(&current);
61 new_exe = 0;
 
62 }
63 blob_zero(&new);
64 old_exists = db_column_int(&q, 1);
65 old_exe = db_column_int(&q, 2);
 
66 if( old_exists ){
67 db_ephemeral_blob(&q, 0, &new);
68 }
69 if( old_exists ){
70 if( new_exists ){
@@ -79,22 +83,23 @@
79 symlink_create(blob_str(&new), zFullname);
80 }else{
81 blob_write_to_file(&new, zFullname);
82 }
83 file_wd_setexe(zFullname, old_exe);
 
84 }else{
85 fossil_print("DELETE %s\n", zPathname);
86 file_delete(zFullname);
87 }
88 blob_reset(&new);
89 free(zFullname);
90 db_finalize(&q);
91 db_prepare(&q,
92 "UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d,"
93 " redoflag=NOT redoflag"
94 " WHERE pathname=%Q",
95 new_exists, new_exe, new_link, zPathname
96 );
97 if( new_exists ){
98 db_bind_blob(&q, ":c", &current);
99 }
100 db_step(&q);
@@ -225,10 +230,11 @@
225 @ pathname TEXT UNIQUE, -- Name of the file
226 @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable
227 @ existsflag BOOLEAN, -- True if the file exists
228 @ isExe BOOLEAN, -- True if the file is executable
229 @ isLink BOOLEAN, -- True if the file is symlink
 
230 @ content BLOB -- Saved content
231 @ );
232 @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
233 @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
234 ;
@@ -274,13 +280,14 @@
274 zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
275 existsFlag = file_wd_size(zFullname)>=0;
276 isLink = file_wd_islink(zFullname);
277 db_prepare(&q,
278 "INSERT OR IGNORE INTO"
279 " undo(pathname,redoflag,existsflag,isExe,isLink,content)"
280 " VALUES(%Q,0,%d,%d,%d,:c)",
281 zPathname, existsFlag, file_wd_isexe(zFullname), isLink
 
282 );
283 if( existsFlag ){
284 if( isLink ){
285 blob_read_link(&content, zFullname);
286 }else{
@@ -383,10 +390,11 @@
383 int isRedo = g.argv[1][0]=='r';
384 int undo_available;
385 int explainFlag = find_option("explain", 0, 0)!=0;
386 const char *zCmd = isRedo ? "redo" : "undo";
387 db_must_be_within_tree();
 
388 verify_all_options();
389 db_begin_transaction();
390 undo_available = db_lget_int("undo_available", 0);
391 if( explainFlag ){
392 if( undo_available==0 ){
393
--- src/undo.c
+++ src/undo.c
@@ -30,11 +30,11 @@
30 */
31 static void undo_one(const char *zPathname, int redoFlag){
32 Stmt q;
33 char *zFullname;
34 db_prepare(&q,
35 "SELECT content, existsflag, isExe, isLink, mtime FROM undo"
36 " WHERE pathname=%Q AND redoflag=%d",
37 zPathname, redoFlag
38 );
39 if( db_step(&q)==SQLITE_ROW ){
40 int old_exists;
@@ -41,10 +41,11 @@
41 int new_exists;
42 int old_exe;
43 int new_exe;
44 int new_link;
45 int old_link;
46 i64 old_mtime, new_mtime;
47 Blob current;
48 Blob new;
49 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
50 old_link = db_column_int(&q, 3);
51 new_link = file_wd_islink(zFullname);
@@ -54,17 +55,20 @@
55 blob_read_link(&current, zFullname);
56 }else{
57 blob_read_from_file(&current, zFullname);
58 }
59 new_exe = file_wd_isexe(zFullname);
60 new_mtime = file_wd_mtime(zFullname);
61 }else{
62 blob_zero(&current);
63 new_exe = 0;
64 new_mtime = 0;
65 }
66 blob_zero(&new);
67 old_exists = db_column_int(&q, 1);
68 old_exe = db_column_int(&q, 2);
69 old_mtime = db_column_int64(&q, 4);
70 if( old_exists ){
71 db_ephemeral_blob(&q, 0, &new);
72 }
73 if( old_exists ){
74 if( new_exists ){
@@ -79,22 +83,23 @@
83 symlink_create(blob_str(&new), zFullname);
84 }else{
85 blob_write_to_file(&new, zFullname);
86 }
87 file_wd_setexe(zFullname, old_exe);
88 file_set_mtime(zFullname, old_mtime);
89 }else{
90 fossil_print("DELETE %s\n", zPathname);
91 file_delete(zFullname);
92 }
93 blob_reset(&new);
94 free(zFullname);
95 db_finalize(&q);
96 db_prepare(&q,
97 "UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d,"
98 " mtime=%lld, redoflag=NOT redoflag"
99 " WHERE pathname=%Q",
100 new_exists, new_exe, new_link, new_mtime, zPathname
101 );
102 if( new_exists ){
103 db_bind_blob(&q, ":c", &current);
104 }
105 db_step(&q);
@@ -225,10 +230,11 @@
230 @ pathname TEXT UNIQUE, -- Name of the file
231 @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable
232 @ existsflag BOOLEAN, -- True if the file exists
233 @ isExe BOOLEAN, -- True if the file is executable
234 @ isLink BOOLEAN, -- True if the file is symlink
235 @ mtime DATETIME, -- File modification time
236 @ content BLOB -- Saved content
237 @ );
238 @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
239 @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
240 ;
@@ -274,13 +280,14 @@
280 zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
281 existsFlag = file_wd_size(zFullname)>=0;
282 isLink = file_wd_islink(zFullname);
283 db_prepare(&q,
284 "INSERT OR IGNORE INTO"
285 " undo(pathname,redoflag,existsflag,isExe,isLink,mtime,content)"
286 " VALUES(%Q,0,%d,%d,%d,%lld,:c)",
287 zPathname, existsFlag, file_wd_isexe(zFullname), isLink,
288 file_wd_mtime(zFullname)
289 );
290 if( existsFlag ){
291 if( isLink ){
292 blob_read_link(&content, zFullname);
293 }else{
@@ -383,10 +390,11 @@
390 int isRedo = g.argv[1][0]=='r';
391 int undo_available;
392 int explainFlag = find_option("explain", 0, 0)!=0;
393 const char *zCmd = isRedo ? "redo" : "undo";
394 db_must_be_within_tree();
395 if( find_option("reset", 0, 0)!=0 ){ undo_reset(); return; }
396 verify_all_options();
397 db_begin_transaction();
398 undo_available = db_lget_int("undo_available", 0);
399 if( explainFlag ){
400 if( undo_available==0 ){
401

Keyboard Shortcuts

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