Fossil SCM

If a "merge" or "update" or "revert" fails, then automatically rollback any partial changes to the filesystem. Ticket [a9722a15d2].

drh 2010-01-14 15:34 trunk
Commit 7c3cb28d0f76c038ae4cc17a65311564b4d90408
+5 -3
--- src/blob.c
+++ src/blob.c
@@ -675,30 +675,32 @@
675675
** C: in this example.
676676
*/
677677
if( !(i==2 && zName[1]==':') ){
678678
#endif
679679
if( file_mkdir(zName, 1) ){
680
- fossil_panic("unable to create directory %s", zName);
680
+ fossil_fatal_recursive("unable to create directory %s", zName);
681
+ return 0;
681682
}
682683
#ifdef __MINGW32__
683684
}
684685
#endif
685686
zName[i] = '/';
686687
}
687688
}
688689
out = fopen(zName, "wb");
689690
if( out==0 ){
690
- fossil_panic("unable to open file \"%s\" for writing", zName);
691
+ fossil_fatal_recursive("unable to open file \"%s\" for writing", zName);
692
+ return 0;
691693
}
692694
needToClose = 1;
693695
if( zName!=zBuf ) free(zName);
694696
}
695697
blob_is_init(pBlob);
696698
wrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
697699
if( needToClose ) fclose(out);
698700
if( wrote!=blob_size(pBlob) ){
699
- fossil_panic("short write: %d of %d bytes to %s", wrote,
701
+ fossil_fatal_recursive("short write: %d of %d bytes to %s", wrote,
700702
blob_size(pBlob), zFilename);
701703
}
702704
return wrote;
703705
}
704706
705707
--- src/blob.c
+++ src/blob.c
@@ -675,30 +675,32 @@
675 ** C: in this example.
676 */
677 if( !(i==2 && zName[1]==':') ){
678 #endif
679 if( file_mkdir(zName, 1) ){
680 fossil_panic("unable to create directory %s", zName);
 
681 }
682 #ifdef __MINGW32__
683 }
684 #endif
685 zName[i] = '/';
686 }
687 }
688 out = fopen(zName, "wb");
689 if( out==0 ){
690 fossil_panic("unable to open file \"%s\" for writing", zName);
 
691 }
692 needToClose = 1;
693 if( zName!=zBuf ) free(zName);
694 }
695 blob_is_init(pBlob);
696 wrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
697 if( needToClose ) fclose(out);
698 if( wrote!=blob_size(pBlob) ){
699 fossil_panic("short write: %d of %d bytes to %s", wrote,
700 blob_size(pBlob), zFilename);
701 }
702 return wrote;
703 }
704
705
--- src/blob.c
+++ src/blob.c
@@ -675,30 +675,32 @@
675 ** C: in this example.
676 */
677 if( !(i==2 && zName[1]==':') ){
678 #endif
679 if( file_mkdir(zName, 1) ){
680 fossil_fatal_recursive("unable to create directory %s", zName);
681 return 0;
682 }
683 #ifdef __MINGW32__
684 }
685 #endif
686 zName[i] = '/';
687 }
688 }
689 out = fopen(zName, "wb");
690 if( out==0 ){
691 fossil_fatal_recursive("unable to open file \"%s\" for writing", zName);
692 return 0;
693 }
694 needToClose = 1;
695 if( zName!=zBuf ) free(zName);
696 }
697 blob_is_init(pBlob);
698 wrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
699 if( needToClose ) fclose(out);
700 if( wrote!=blob_size(pBlob) ){
701 fossil_fatal_recursive("short write: %d of %d bytes to %s", wrote,
702 blob_size(pBlob), zFilename);
703 }
704 return wrote;
705 }
706
707
+6 -1
--- src/db.c
+++ src/db.c
@@ -99,11 +99,11 @@
9999
} aHook[5];
100100
static Stmt *pAllStmt = 0; /* List of all unfinalized statements */
101101
102102
/*
103103
** This routine is called by the SQLite commit-hook mechanism
104
-** just prior to each omit. All this routine does is verify
104
+** just prior to each commit. All this routine does is verify
105105
** that nBegin really is zero. That insures that transactions
106106
** cannot commit by any means other than by calling db_end_transaction()
107107
** below.
108108
**
109109
** This is just a safety and sanity check.
@@ -138,18 +138,23 @@
138138
db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT");
139139
doRollback = 0;
140140
}
141141
}
142142
void db_force_rollback(void){
143
+ static int busy = 0;
144
+ if( busy ) return;
145
+ busy = 1;
146
+ undo_rollback();
143147
if( nBegin ){
144148
sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
145149
if( isNewRepo ){
146150
db_close();
147151
unlink(g.zRepositoryName);
148152
}
149153
}
150154
nBegin = 0;
155
+ busy = 0;
151156
}
152157
153158
/*
154159
** Install a commit hook. Hooks are installed in sequence order.
155160
** It is an error to install the same commit hook more than once.
156161
--- src/db.c
+++ src/db.c
@@ -99,11 +99,11 @@
99 } aHook[5];
100 static Stmt *pAllStmt = 0; /* List of all unfinalized statements */
101
102 /*
103 ** This routine is called by the SQLite commit-hook mechanism
104 ** just prior to each omit. All this routine does is verify
105 ** that nBegin really is zero. That insures that transactions
106 ** cannot commit by any means other than by calling db_end_transaction()
107 ** below.
108 **
109 ** This is just a safety and sanity check.
@@ -138,18 +138,23 @@
138 db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT");
139 doRollback = 0;
140 }
141 }
142 void db_force_rollback(void){
 
 
 
 
143 if( nBegin ){
144 sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
145 if( isNewRepo ){
146 db_close();
147 unlink(g.zRepositoryName);
148 }
149 }
150 nBegin = 0;
 
151 }
152
153 /*
154 ** Install a commit hook. Hooks are installed in sequence order.
155 ** It is an error to install the same commit hook more than once.
156
--- src/db.c
+++ src/db.c
@@ -99,11 +99,11 @@
99 } aHook[5];
100 static Stmt *pAllStmt = 0; /* List of all unfinalized statements */
101
102 /*
103 ** This routine is called by the SQLite commit-hook mechanism
104 ** just prior to each commit. All this routine does is verify
105 ** that nBegin really is zero. That insures that transactions
106 ** cannot commit by any means other than by calling db_end_transaction()
107 ** below.
108 **
109 ** This is just a safety and sanity check.
@@ -138,18 +138,23 @@
138 db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT");
139 doRollback = 0;
140 }
141 }
142 void db_force_rollback(void){
143 static int busy = 0;
144 if( busy ) return;
145 busy = 1;
146 undo_rollback();
147 if( nBegin ){
148 sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0);
149 if( isNewRepo ){
150 db_close();
151 unlink(g.zRepositoryName);
152 }
153 }
154 nBegin = 0;
155 busy = 0;
156 }
157
158 /*
159 ** Install a commit hook. Hooks are installed in sequence order.
160 ** It is an error to install the same commit hook more than once.
161
+42 -1
--- src/main.c
+++ src/main.c
@@ -257,16 +257,25 @@
257257
aCommand[idx].xFunc();
258258
return 0;
259259
}
260260
261261
/*
262
-** Print an error message, rollback all databases, and quit.
262
+** The following variable becomes true while processing a fatal error
263
+** or a panic. If additional "recursive-fatal" errors occur while
264
+** shutting down, the recursive errors are silently ignored.
265
+*/
266
+static int mainInFatalError = 0;
267
+
268
+/*
269
+** Print an error message, rollback all databases, and quit. These
270
+** routines never return.
263271
*/
264272
void fossil_panic(const char *zFormat, ...){
265273
char *z;
266274
va_list ap;
267275
static int once = 1;
276
+ mainInFatalError = 1;
268277
va_start(ap, zFormat);
269278
z = vmprintf(zFormat, ap);
270279
va_end(ap);
271280
if( g.cgiPanic && once ){
272281
once = 0;
@@ -279,10 +288,39 @@
279288
exit(1);
280289
}
281290
void fossil_fatal(const char *zFormat, ...){
282291
char *z;
283292
va_list ap;
293
+ mainInFatalError = 1;
294
+ va_start(ap, zFormat);
295
+ z = vmprintf(zFormat, ap);
296
+ va_end(ap);
297
+ if( g.cgiPanic ){
298
+ g.cgiPanic = 0;
299
+ cgi_printf("<p><font color=\"red\">%h</font></p>", z);
300
+ cgi_reply();
301
+ }else{
302
+ fprintf(stderr, "%s: %s\n", g.argv[0], z);
303
+ }
304
+ db_force_rollback();
305
+ exit(1);
306
+}
307
+
308
+/* This routine works like fossil_fatal() except that if called
309
+** recursively, the recursive call is a no-op.
310
+**
311
+** Use this in places where an error might occur while doing
312
+** fatal error shutdown processing. Unlike fossil_panic() and
313
+** fossil_fatal() which never return, this routine might return if
314
+** the fatal error handing is already in process. The caller must
315
+** be prepared for this routine to return.
316
+*/
317
+void fossil_fatal_recursive(const char *zFormat, ...){
318
+ char *z;
319
+ va_list ap;
320
+ if( mainInFatalError ) return;
321
+ mainInFatalError = 1;
284322
va_start(ap, zFormat);
285323
z = vmprintf(zFormat, ap);
286324
va_end(ap);
287325
if( g.cgiPanic ){
288326
g.cgiPanic = 0;
@@ -292,10 +330,13 @@
292330
fprintf(stderr, "%s: %s\n", g.argv[0], z);
293331
}
294332
db_force_rollback();
295333
exit(1);
296334
}
335
+
336
+
337
+/* Print a warning message */
297338
void fossil_warning(const char *zFormat, ...){
298339
char *z;
299340
va_list ap;
300341
va_start(ap, zFormat);
301342
z = vmprintf(zFormat, ap);
302343
--- src/main.c
+++ src/main.c
@@ -257,16 +257,25 @@
257 aCommand[idx].xFunc();
258 return 0;
259 }
260
261 /*
262 ** Print an error message, rollback all databases, and quit.
 
 
 
 
 
 
 
 
263 */
264 void fossil_panic(const char *zFormat, ...){
265 char *z;
266 va_list ap;
267 static int once = 1;
 
268 va_start(ap, zFormat);
269 z = vmprintf(zFormat, ap);
270 va_end(ap);
271 if( g.cgiPanic && once ){
272 once = 0;
@@ -279,10 +288,39 @@
279 exit(1);
280 }
281 void fossil_fatal(const char *zFormat, ...){
282 char *z;
283 va_list ap;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284 va_start(ap, zFormat);
285 z = vmprintf(zFormat, ap);
286 va_end(ap);
287 if( g.cgiPanic ){
288 g.cgiPanic = 0;
@@ -292,10 +330,13 @@
292 fprintf(stderr, "%s: %s\n", g.argv[0], z);
293 }
294 db_force_rollback();
295 exit(1);
296 }
 
 
 
297 void fossil_warning(const char *zFormat, ...){
298 char *z;
299 va_list ap;
300 va_start(ap, zFormat);
301 z = vmprintf(zFormat, ap);
302
--- src/main.c
+++ src/main.c
@@ -257,16 +257,25 @@
257 aCommand[idx].xFunc();
258 return 0;
259 }
260
261 /*
262 ** The following variable becomes true while processing a fatal error
263 ** or a panic. If additional "recursive-fatal" errors occur while
264 ** shutting down, the recursive errors are silently ignored.
265 */
266 static int mainInFatalError = 0;
267
268 /*
269 ** Print an error message, rollback all databases, and quit. These
270 ** routines never return.
271 */
272 void fossil_panic(const char *zFormat, ...){
273 char *z;
274 va_list ap;
275 static int once = 1;
276 mainInFatalError = 1;
277 va_start(ap, zFormat);
278 z = vmprintf(zFormat, ap);
279 va_end(ap);
280 if( g.cgiPanic && once ){
281 once = 0;
@@ -279,10 +288,39 @@
288 exit(1);
289 }
290 void fossil_fatal(const char *zFormat, ...){
291 char *z;
292 va_list ap;
293 mainInFatalError = 1;
294 va_start(ap, zFormat);
295 z = vmprintf(zFormat, ap);
296 va_end(ap);
297 if( g.cgiPanic ){
298 g.cgiPanic = 0;
299 cgi_printf("<p><font color=\"red\">%h</font></p>", z);
300 cgi_reply();
301 }else{
302 fprintf(stderr, "%s: %s\n", g.argv[0], z);
303 }
304 db_force_rollback();
305 exit(1);
306 }
307
308 /* This routine works like fossil_fatal() except that if called
309 ** recursively, the recursive call is a no-op.
310 **
311 ** Use this in places where an error might occur while doing
312 ** fatal error shutdown processing. Unlike fossil_panic() and
313 ** fossil_fatal() which never return, this routine might return if
314 ** the fatal error handing is already in process. The caller must
315 ** be prepared for this routine to return.
316 */
317 void fossil_fatal_recursive(const char *zFormat, ...){
318 char *z;
319 va_list ap;
320 if( mainInFatalError ) return;
321 mainInFatalError = 1;
322 va_start(ap, zFormat);
323 z = vmprintf(zFormat, ap);
324 va_end(ap);
325 if( g.cgiPanic ){
326 g.cgiPanic = 0;
@@ -292,10 +330,13 @@
330 fprintf(stderr, "%s: %s\n", g.argv[0], z);
331 }
332 db_force_rollback();
333 exit(1);
334 }
335
336
337 /* Print a warning message */
338 void fossil_warning(const char *zFormat, ...){
339 char *z;
340 va_list ap;
341 va_start(ap, zFormat);
342 z = vmprintf(zFormat, ap);
343
--- src/merge.c
+++ src/merge.c
@@ -286,7 +286,8 @@
286286
/*
287287
** Clean up the mid and pid VFILE entries. Then commit the changes.
288288
*/
289289
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
290290
db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
291
+ undo_finish();
291292
db_end_transaction(0);
292293
}
293294
--- src/merge.c
+++ src/merge.c
@@ -286,7 +286,8 @@
286 /*
287 ** Clean up the mid and pid VFILE entries. Then commit the changes.
288 */
289 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
290 db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
 
291 db_end_transaction(0);
292 }
293
--- src/merge.c
+++ src/merge.c
@@ -286,7 +286,8 @@
286 /*
287 ** Clean up the mid and pid VFILE entries. Then commit the changes.
288 */
289 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
290 db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
291 undo_finish();
292 db_end_transaction(0);
293 }
294
+67 -6
--- src/undo.c
+++ src/undo.c
@@ -85,23 +85,36 @@
8585
}
8686
db_finalize(&q);
8787
}
8888
8989
/*
90
-** Undo or redo all undoable or redoable changes.
90
+** Undo or redo changes to the filesystem. Undo the changes in the
91
+** same order that they were originally carried out - undo the oldest
92
+** change first and undo the most recent change last.
9193
*/
92
-static void undo_all(int redoFlag){
94
+static void undo_all_filesystem(int redoFlag){
9395
Stmt q;
94
- int ucid;
95
- int ncid;
96
- db_prepare(&q, "SELECT pathname FROM undo WHERE redoflag=%d"
97
- " ORDER BY +pathname", redoFlag);
96
+ db_prepare(&q,
97
+ "SELECT pathname FROM undo"
98
+ " WHERE redoflag=%d"
99
+ " ORDER BY rowid",
100
+ redoFlag
101
+ );
98102
while( db_step(&q)==SQLITE_ROW ){
99103
const char *zPathname = db_column_text(&q, 0);
100104
undo_one(zPathname, redoFlag);
101105
}
102106
db_finalize(&q);
107
+}
108
+
109
+/*
110
+** Undo or redo all undoable or redoable changes.
111
+*/
112
+static void undo_all(int redoFlag){
113
+ int ucid;
114
+ int ncid;
115
+ undo_all_filesystem(redoFlag);
103116
db_multi_exec(
104117
"CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;"
105118
"DELETE FROM vfile;"
106119
"INSERT INTO vfile SELECT * FROM undo_vfile;"
107120
"DELETE FROM undo_vfile;"
@@ -126,16 +139,23 @@
126139
void undo_reset(void){
127140
static const char zSql[] =
128141
@ DROP TABLE IF EXISTS undo;
129142
@ DROP TABLE IF EXISTS undo_vfile;
130143
@ DROP TABLE IF EXISTS undo_vmerge;
144
+ @ DROP TABLE IF EXISTS undo_pending;
131145
;
132146
db_multi_exec(zSql);
133147
db_lset_int("undo_available", 0);
134148
db_lset_int("undo_checkout", 0);
135149
}
136150
151
+/*
152
+** This flag is true if we are in the process of collecting file changes
153
+** for undo. When this flag is false, undo_save() is a no-op.
154
+*/
155
+static int undoActive = 0;
156
+
137157
/*
138158
** Begin capturing a snapshot that can be undone.
139159
*/
140160
void undo_begin(void){
141161
int cid;
@@ -146,18 +166,29 @@
146166
@ existsflag BOOLEAN, -- True if the file exists
147167
@ content BLOB -- Saved content
148168
@ );
149169
@ CREATE TABLE undo_vfile AS SELECT * FROM vfile;
150170
@ CREATE TABLE undo_vmerge AS SELECT * FROM vmerge;
171
+ @ CREATE TABLE undo_pending(undoId INTEGER PRIMARY KEY);
151172
;
152173
undo_reset();
153174
db_multi_exec(zSql);
154175
cid = db_lget_int("checkout", 0);
155176
db_lset_int("undo_checkout", cid);
156177
db_lset_int("undo_available", 1);
178
+ undoActive = 1;
157179
}
158180
181
+/*
182
+** This flag is true if one or more files have changed and have been
183
+** recorded in the undo log but the undo log has not yet been committed.
184
+**
185
+** If a fatal error occurs and this flag is set, that means we should
186
+** rollback all the filesystem changes.
187
+*/
188
+static int undoNeedRollback = 0;
189
+
159190
/*
160191
** Save the current content of the file zPathname so that it
161192
** will be undoable. The name is relative to the root of the
162193
** tree.
163194
*/
@@ -165,10 +196,11 @@
165196
char *zFullname;
166197
Blob content;
167198
int existsFlag;
168199
Stmt q;
169200
201
+ if( !undoActive ) return;
170202
zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
171203
existsFlag = file_size(zFullname)>=0;
172204
db_prepare(&q,
173205
"REPLACE INTO undo(pathname,redoflag,existsflag,content)"
174206
" VALUES(%Q,0,%d,:c)",
@@ -182,10 +214,39 @@
182214
db_step(&q);
183215
db_finalize(&q);
184216
if( existsFlag ){
185217
blob_reset(&content);
186218
}
219
+ undoNeedRollback = 1;
220
+}
221
+
222
+/*
223
+** Complete the undo process is one is currently in process.
224
+*/
225
+void undo_finish(void){
226
+ if( undoActive ){
227
+ undoActive = 0;
228
+ undoNeedRollback = 0;
229
+ }
230
+}
231
+
232
+/*
233
+** This routine is called when the process aborts due to an error.
234
+** If an undo was being accumulated but was not finished, attempt
235
+** to rollback all of the filesystem changes.
236
+**
237
+** This rollback occurs, for example, if an "update" or "merge" operation
238
+** could not run to completion because a file that needed to be written
239
+** was locked or had permissions turned off.
240
+*/
241
+void undo_rollback(void){
242
+ if( !undoNeedRollback ) return;
243
+ assert( undoActive );
244
+ undoNeedRollback = 0;
245
+ undoActive = 0;
246
+ printf("Rolling back prior filesystem changes...\n");
247
+ undo_all_filesystem(0);
187248
}
188249
189250
/*
190251
** COMMAND: undo
191252
**
192253
--- src/undo.c
+++ src/undo.c
@@ -85,23 +85,36 @@
85 }
86 db_finalize(&q);
87 }
88
89 /*
90 ** Undo or redo all undoable or redoable changes.
 
 
91 */
92 static void undo_all(int redoFlag){
93 Stmt q;
94 int ucid;
95 int ncid;
96 db_prepare(&q, "SELECT pathname FROM undo WHERE redoflag=%d"
97 " ORDER BY +pathname", redoFlag);
 
 
98 while( db_step(&q)==SQLITE_ROW ){
99 const char *zPathname = db_column_text(&q, 0);
100 undo_one(zPathname, redoFlag);
101 }
102 db_finalize(&q);
 
 
 
 
 
 
 
 
 
103 db_multi_exec(
104 "CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;"
105 "DELETE FROM vfile;"
106 "INSERT INTO vfile SELECT * FROM undo_vfile;"
107 "DELETE FROM undo_vfile;"
@@ -126,16 +139,23 @@
126 void undo_reset(void){
127 static const char zSql[] =
128 @ DROP TABLE IF EXISTS undo;
129 @ DROP TABLE IF EXISTS undo_vfile;
130 @ DROP TABLE IF EXISTS undo_vmerge;
 
131 ;
132 db_multi_exec(zSql);
133 db_lset_int("undo_available", 0);
134 db_lset_int("undo_checkout", 0);
135 }
136
 
 
 
 
 
 
137 /*
138 ** Begin capturing a snapshot that can be undone.
139 */
140 void undo_begin(void){
141 int cid;
@@ -146,18 +166,29 @@
146 @ existsflag BOOLEAN, -- True if the file exists
147 @ content BLOB -- Saved content
148 @ );
149 @ CREATE TABLE undo_vfile AS SELECT * FROM vfile;
150 @ CREATE TABLE undo_vmerge AS SELECT * FROM vmerge;
 
151 ;
152 undo_reset();
153 db_multi_exec(zSql);
154 cid = db_lget_int("checkout", 0);
155 db_lset_int("undo_checkout", cid);
156 db_lset_int("undo_available", 1);
 
157 }
158
 
 
 
 
 
 
 
 
 
159 /*
160 ** Save the current content of the file zPathname so that it
161 ** will be undoable. The name is relative to the root of the
162 ** tree.
163 */
@@ -165,10 +196,11 @@
165 char *zFullname;
166 Blob content;
167 int existsFlag;
168 Stmt q;
169
 
170 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
171 existsFlag = file_size(zFullname)>=0;
172 db_prepare(&q,
173 "REPLACE INTO undo(pathname,redoflag,existsflag,content)"
174 " VALUES(%Q,0,%d,:c)",
@@ -182,10 +214,39 @@
182 db_step(&q);
183 db_finalize(&q);
184 if( existsFlag ){
185 blob_reset(&content);
186 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187 }
188
189 /*
190 ** COMMAND: undo
191 **
192
--- src/undo.c
+++ src/undo.c
@@ -85,23 +85,36 @@
85 }
86 db_finalize(&q);
87 }
88
89 /*
90 ** Undo or redo changes to the filesystem. Undo the changes in the
91 ** same order that they were originally carried out - undo the oldest
92 ** change first and undo the most recent change last.
93 */
94 static void undo_all_filesystem(int redoFlag){
95 Stmt q;
96 db_prepare(&q,
97 "SELECT pathname FROM undo"
98 " WHERE redoflag=%d"
99 " ORDER BY rowid",
100 redoFlag
101 );
102 while( db_step(&q)==SQLITE_ROW ){
103 const char *zPathname = db_column_text(&q, 0);
104 undo_one(zPathname, redoFlag);
105 }
106 db_finalize(&q);
107 }
108
109 /*
110 ** Undo or redo all undoable or redoable changes.
111 */
112 static void undo_all(int redoFlag){
113 int ucid;
114 int ncid;
115 undo_all_filesystem(redoFlag);
116 db_multi_exec(
117 "CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;"
118 "DELETE FROM vfile;"
119 "INSERT INTO vfile SELECT * FROM undo_vfile;"
120 "DELETE FROM undo_vfile;"
@@ -126,16 +139,23 @@
139 void undo_reset(void){
140 static const char zSql[] =
141 @ DROP TABLE IF EXISTS undo;
142 @ DROP TABLE IF EXISTS undo_vfile;
143 @ DROP TABLE IF EXISTS undo_vmerge;
144 @ DROP TABLE IF EXISTS undo_pending;
145 ;
146 db_multi_exec(zSql);
147 db_lset_int("undo_available", 0);
148 db_lset_int("undo_checkout", 0);
149 }
150
151 /*
152 ** This flag is true if we are in the process of collecting file changes
153 ** for undo. When this flag is false, undo_save() is a no-op.
154 */
155 static int undoActive = 0;
156
157 /*
158 ** Begin capturing a snapshot that can be undone.
159 */
160 void undo_begin(void){
161 int cid;
@@ -146,18 +166,29 @@
166 @ existsflag BOOLEAN, -- True if the file exists
167 @ content BLOB -- Saved content
168 @ );
169 @ CREATE TABLE undo_vfile AS SELECT * FROM vfile;
170 @ CREATE TABLE undo_vmerge AS SELECT * FROM vmerge;
171 @ CREATE TABLE undo_pending(undoId INTEGER PRIMARY KEY);
172 ;
173 undo_reset();
174 db_multi_exec(zSql);
175 cid = db_lget_int("checkout", 0);
176 db_lset_int("undo_checkout", cid);
177 db_lset_int("undo_available", 1);
178 undoActive = 1;
179 }
180
181 /*
182 ** This flag is true if one or more files have changed and have been
183 ** recorded in the undo log but the undo log has not yet been committed.
184 **
185 ** If a fatal error occurs and this flag is set, that means we should
186 ** rollback all the filesystem changes.
187 */
188 static int undoNeedRollback = 0;
189
190 /*
191 ** Save the current content of the file zPathname so that it
192 ** will be undoable. The name is relative to the root of the
193 ** tree.
194 */
@@ -165,10 +196,11 @@
196 char *zFullname;
197 Blob content;
198 int existsFlag;
199 Stmt q;
200
201 if( !undoActive ) return;
202 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
203 existsFlag = file_size(zFullname)>=0;
204 db_prepare(&q,
205 "REPLACE INTO undo(pathname,redoflag,existsflag,content)"
206 " VALUES(%Q,0,%d,:c)",
@@ -182,10 +214,39 @@
214 db_step(&q);
215 db_finalize(&q);
216 if( existsFlag ){
217 blob_reset(&content);
218 }
219 undoNeedRollback = 1;
220 }
221
222 /*
223 ** Complete the undo process is one is currently in process.
224 */
225 void undo_finish(void){
226 if( undoActive ){
227 undoActive = 0;
228 undoNeedRollback = 0;
229 }
230 }
231
232 /*
233 ** This routine is called when the process aborts due to an error.
234 ** If an undo was being accumulated but was not finished, attempt
235 ** to rollback all of the filesystem changes.
236 **
237 ** This rollback occurs, for example, if an "update" or "merge" operation
238 ** could not run to completion because a file that needed to be written
239 ** was locked or had permissions turned off.
240 */
241 void undo_rollback(void){
242 if( !undoNeedRollback ) return;
243 assert( undoActive );
244 undoNeedRollback = 0;
245 undoActive = 0;
246 printf("Rolling back prior filesystem changes...\n");
247 undo_all_filesystem(0);
248 }
249
250 /*
251 ** COMMAND: undo
252 **
253
+3 -1
--- src/update.c
+++ src/update.c
@@ -119,11 +119,11 @@
119119
" ORDER BY event.mtime DESC");
120120
}
121121
122122
db_begin_transaction();
123123
vfile_check_signature(vid, 1);
124
- undo_begin();
124
+ if( !nochangeFlag ) undo_begin();
125125
load_vfile_from_rid(tid);
126126
127127
/*
128128
** The record.fn field is used to match files against each other. The
129129
** FV table contains one row for each each unique filename in
@@ -288,10 +288,11 @@
288288
}else{
289289
/* A subset of files have been checked out. Keep the current
290290
** checkout unchanged. */
291291
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
292292
}
293
+ undo_finish();
293294
db_end_transaction(0);
294295
}
295296
}
296297
297298
@@ -394,7 +395,8 @@
394395
}
395396
blob_reset(&record);
396397
blob_reset(&fname);
397398
free(zFile);
398399
}
400
+ undo_finish();
399401
db_end_transaction(0);
400402
}
401403
--- src/update.c
+++ src/update.c
@@ -119,11 +119,11 @@
119 " ORDER BY event.mtime DESC");
120 }
121
122 db_begin_transaction();
123 vfile_check_signature(vid, 1);
124 undo_begin();
125 load_vfile_from_rid(tid);
126
127 /*
128 ** The record.fn field is used to match files against each other. The
129 ** FV table contains one row for each each unique filename in
@@ -288,10 +288,11 @@
288 }else{
289 /* A subset of files have been checked out. Keep the current
290 ** checkout unchanged. */
291 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
292 }
 
293 db_end_transaction(0);
294 }
295 }
296
297
@@ -394,7 +395,8 @@
394 }
395 blob_reset(&record);
396 blob_reset(&fname);
397 free(zFile);
398 }
 
399 db_end_transaction(0);
400 }
401
--- src/update.c
+++ src/update.c
@@ -119,11 +119,11 @@
119 " ORDER BY event.mtime DESC");
120 }
121
122 db_begin_transaction();
123 vfile_check_signature(vid, 1);
124 if( !nochangeFlag ) undo_begin();
125 load_vfile_from_rid(tid);
126
127 /*
128 ** The record.fn field is used to match files against each other. The
129 ** FV table contains one row for each each unique filename in
@@ -288,10 +288,11 @@
288 }else{
289 /* A subset of files have been checked out. Keep the current
290 ** checkout unchanged. */
291 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
292 }
293 undo_finish();
294 db_end_transaction(0);
295 }
296 }
297
298
@@ -394,7 +395,8 @@
395 }
396 blob_reset(&record);
397 blob_reset(&fname);
398 free(zFile);
399 }
400 undo_finish();
401 db_end_transaction(0);
402 }
403

Keyboard Shortcuts

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