| | @@ -85,23 +85,36 @@ |
| 85 | 85 | } |
| 86 | 86 | db_finalize(&q); |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | /* |
| 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. |
| 91 | 93 | */ |
| 92 | | -static void undo_all(int redoFlag){ |
| 94 | +static void undo_all_filesystem(int redoFlag){ |
| 93 | 95 | 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 | + ); |
| 98 | 102 | while( db_step(&q)==SQLITE_ROW ){ |
| 99 | 103 | const char *zPathname = db_column_text(&q, 0); |
| 100 | 104 | undo_one(zPathname, redoFlag); |
| 101 | 105 | } |
| 102 | 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); |
| 103 | 116 | db_multi_exec( |
| 104 | 117 | "CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;" |
| 105 | 118 | "DELETE FROM vfile;" |
| 106 | 119 | "INSERT INTO vfile SELECT * FROM undo_vfile;" |
| 107 | 120 | "DELETE FROM undo_vfile;" |
| | @@ -126,16 +139,23 @@ |
| 126 | 139 | void undo_reset(void){ |
| 127 | 140 | static const char zSql[] = |
| 128 | 141 | @ DROP TABLE IF EXISTS undo; |
| 129 | 142 | @ DROP TABLE IF EXISTS undo_vfile; |
| 130 | 143 | @ DROP TABLE IF EXISTS undo_vmerge; |
| 144 | + @ DROP TABLE IF EXISTS undo_pending; |
| 131 | 145 | ; |
| 132 | 146 | db_multi_exec(zSql); |
| 133 | 147 | db_lset_int("undo_available", 0); |
| 134 | 148 | db_lset_int("undo_checkout", 0); |
| 135 | 149 | } |
| 136 | 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 | + |
| 137 | 157 | /* |
| 138 | 158 | ** Begin capturing a snapshot that can be undone. |
| 139 | 159 | */ |
| 140 | 160 | void undo_begin(void){ |
| 141 | 161 | int cid; |
| | @@ -146,18 +166,29 @@ |
| 146 | 166 | @ existsflag BOOLEAN, -- True if the file exists |
| 147 | 167 | @ content BLOB -- Saved content |
| 148 | 168 | @ ); |
| 149 | 169 | @ CREATE TABLE undo_vfile AS SELECT * FROM vfile; |
| 150 | 170 | @ CREATE TABLE undo_vmerge AS SELECT * FROM vmerge; |
| 171 | + @ CREATE TABLE undo_pending(undoId INTEGER PRIMARY KEY); |
| 151 | 172 | ; |
| 152 | 173 | undo_reset(); |
| 153 | 174 | db_multi_exec(zSql); |
| 154 | 175 | cid = db_lget_int("checkout", 0); |
| 155 | 176 | db_lset_int("undo_checkout", cid); |
| 156 | 177 | db_lset_int("undo_available", 1); |
| 178 | + undoActive = 1; |
| 157 | 179 | } |
| 158 | 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 | + |
| 159 | 190 | /* |
| 160 | 191 | ** Save the current content of the file zPathname so that it |
| 161 | 192 | ** will be undoable. The name is relative to the root of the |
| 162 | 193 | ** tree. |
| 163 | 194 | */ |
| | @@ -165,10 +196,11 @@ |
| 165 | 196 | char *zFullname; |
| 166 | 197 | Blob content; |
| 167 | 198 | int existsFlag; |
| 168 | 199 | Stmt q; |
| 169 | 200 | |
| 201 | + if( !undoActive ) return; |
| 170 | 202 | zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); |
| 171 | 203 | existsFlag = file_size(zFullname)>=0; |
| 172 | 204 | db_prepare(&q, |
| 173 | 205 | "REPLACE INTO undo(pathname,redoflag,existsflag,content)" |
| 174 | 206 | " VALUES(%Q,0,%d,:c)", |
| | @@ -182,10 +214,39 @@ |
| 182 | 214 | db_step(&q); |
| 183 | 215 | db_finalize(&q); |
| 184 | 216 | if( existsFlag ){ |
| 185 | 217 | blob_reset(&content); |
| 186 | 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); |
| 187 | 248 | } |
| 188 | 249 | |
| 189 | 250 | /* |
| 190 | 251 | ** COMMAND: undo |
| 191 | 252 | ** |
| 192 | 253 | |