Fossil SCM

Simplifications to the implementation of undo/redo. Add the --explain option to undo/redo that shows what would be undone or redone without actually doing anything.

drh 2010-12-18 14:18 trunk
Commit 30981b64a4fcf74d2a0019d03c1e7479abbc8437
3 files changed +1 +84 -64 +3 -1
--- src/merge.c
+++ src/merge.c
@@ -73,10 +73,11 @@
7373
** V The current checkout
7474
** M The version being merged in
7575
** P The "pivot" - the most recent common ancestor of V and M.
7676
*/
7777
78
+ undo_capture_command_line();
7879
detailFlag = find_option("detail",0,0)!=0;
7980
pickFlag = find_option("cherrypick",0,0)!=0;
8081
backoutFlag = find_option("backout",0,0)!=0;
8182
debugFlag = find_option("debug",0,0)!=0;
8283
zBinGlob = find_option("binary",0,1);
8384
--- src/merge.c
+++ src/merge.c
@@ -73,10 +73,11 @@
73 ** V The current checkout
74 ** M The version being merged in
75 ** P The "pivot" - the most recent common ancestor of V and M.
76 */
77
 
78 detailFlag = find_option("detail",0,0)!=0;
79 pickFlag = find_option("cherrypick",0,0)!=0;
80 backoutFlag = find_option("backout",0,0)!=0;
81 debugFlag = find_option("debug",0,0)!=0;
82 zBinGlob = find_option("binary",0,1);
83
--- src/merge.c
+++ src/merge.c
@@ -73,10 +73,11 @@
73 ** V The current checkout
74 ** M The version being merged in
75 ** P The "pivot" - the most recent common ancestor of V and M.
76 */
77
78 undo_capture_command_line();
79 detailFlag = find_option("detail",0,0)!=0;
80 pickFlag = find_option("cherrypick",0,0)!=0;
81 backoutFlag = find_option("backout",0,0)!=0;
82 debugFlag = find_option("debug",0,0)!=0;
83 zBinGlob = find_option("binary",0,1);
84
+84 -64
--- src/undo.c
+++ src/undo.c
@@ -133,16 +133,38 @@
133133
void undo_reset(void){
134134
static const char zSql[] =
135135
@ DROP TABLE IF EXISTS undo;
136136
@ DROP TABLE IF EXISTS undo_vfile;
137137
@ DROP TABLE IF EXISTS undo_vmerge;
138
- @ DROP TABLE IF EXISTS undo_pending;
139138
;
140139
db_multi_exec(zSql);
141140
db_lset_int("undo_available", 0);
142141
db_lset_int("undo_checkout", 0);
143142
}
143
+
144
+/*
145
+** The following variable stores the original command-line of the
146
+** command that is a candidate to be undone.
147
+*/
148
+static char *undoCmd = 0;
149
+
150
+/*
151
+** Capture the current command-line and store it as part of the undo
152
+** state. This routine is called before options are extracted from the
153
+** command-line so that we can record the complete command-line.
154
+*/
155
+void undo_capture_command_line(void){
156
+ Blob cmdline;
157
+ int i;
158
+ assert( undoCmd==0 );
159
+ blob_zero(&cmdline);
160
+ for(i=1; i<g.argc; i++){
161
+ if( i>1 ) blob_append(&cmdline, " ", 1);
162
+ blob_append(&cmdline, g.argv[i], -1);
163
+ }
164
+ undoCmd = blob_str(&cmdline);
165
+}
144166
145167
/*
146168
** This flag is true if we are in the process of collecting file changes
147169
** for undo. When this flag is false, undo_save() is a no-op.
148170
*/
@@ -161,18 +183,18 @@
161183
@ existsflag BOOLEAN, -- True if the file exists
162184
@ content BLOB -- Saved content
163185
@ );
164186
@ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
165187
@ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
166
- @ CREATE TABLE %s.undo_pending(undoId INTEGER PRIMARY KEY);
167188
;
168189
undo_reset();
169190
if( strcmp(g.zMainDbType,zDb)==0 ) zDb = "main";
170191
db_multi_exec(zSql, zDb, zDb, zDb, zDb);
171192
cid = db_lget_int("checkout", 0);
172193
db_lset_int("undo_checkout", cid);
173194
db_lset_int("undo_available", 1);
195
+ db_lset("undo_cmdline", undoCmd);
174196
undoActive = 1;
175197
}
176198
177199
/*
178200
** This flag is true if one or more files have changed and have been
@@ -243,81 +265,79 @@
243265
undo_all_filesystem(0);
244266
}
245267
246268
/*
247269
** COMMAND: undo
270
+** COMMAND: redo
248271
**
249
-** Usage: %fossil undo ?FILENAME...?
272
+** Usage: %fossil undo ?--explain? ?FILENAME...?
273
+** or: %fossil redo ?--explain? ?FILENAME...?
250274
**
251275
** Undo the most recent update or merge or revert operation. If FILENAME is
252276
** specified then restore the content of the named file(s) but otherwise
253
-** leave the update or merge or revert in effect.
277
+** leave the update or merge or revert in effect. The redo command undoes
278
+** the effect of the most recent undo.
279
+**
280
+** If the --explain option is present, not changes are made and instead
281
+** the undo or redo command explains what actions the undo or redo would
282
+** have done had the --explain been omitted.
254283
**
255284
** A single level of undo/redo is supported. The undo/redo stack
256285
** is cleared by the commit and checkout commands.
257286
*/
258287
void undo_cmd(void){
259
- int undo_available;
260
- db_must_be_within_tree();
261
- db_begin_transaction();
262
- undo_available = db_lget_int("undo_available", 0);
263
- if( g.argc==2 ){
264
- if( undo_available!=1 ){
265
- fossil_fatal("no update or merge operation is available to undo");
266
- }
267
- undo_all(0);
268
- db_lset_int("undo_available", 2);
269
- }else if( g.argc>=3 ){
270
- int i;
271
- if( undo_available==0 ){
272
- fossil_fatal("no update or merge operation is available to undo");
273
- }
274
- for(i=2; i<g.argc; i++){
275
- const char *zFile = g.argv[i];
276
- Blob path;
277
- file_tree_name(zFile, &path, 1);
278
- undo_one(blob_str(&path), 0);
279
- blob_reset(&path);
280
- }
281
- }
282
- db_end_transaction(0);
283
-}
284
-
285
-/*
286
-** COMMAND: redo
287
-**
288
-** Usage: %fossil redo ?FILENAME...?
289
-**
290
-** Redo an update or merge or revert operation that has been undone
291
-** by the undo command. If FILENAME is specified then restore the changes
292
-** associated with the named file(s) but otherwise leave the update
293
-** or merge undone.
294
-**
295
-** A single level of undo/redo is supported. The undo/redo stack
296
-** is cleared by the commit and checkout commands.
297
-*/
298
-void redo_cmd(void){
299
- int undo_available;
300
- db_must_be_within_tree();
301
- db_begin_transaction();
302
- undo_available = db_lget_int("undo_available", 0);
303
- if( g.argc==2 ){
304
- if( undo_available!=2 ){
305
- fossil_fatal("no undone update or merge operation is available to redo");
306
- }
307
- undo_all(1);
308
- db_lset_int("undo_available", 1);
309
- }else if( g.argc>=3 ){
310
- int i;
311
- if( undo_available==0 ){
312
- fossil_fatal("no update or merge operation is available to redo");
313
- }
314
- for(i=2; i<g.argc; i++){
315
- const char *zFile = g.argv[i];
316
- Blob path;
317
- file_tree_name(zFile, &path, 1);
318
- undo_one(blob_str(&path), 0);
288
+ int isRedo = g.argv[1][0]=='r';
289
+ int undo_available;
290
+ int explainFlag = find_option("explain", 0, 0)!=0;
291
+ const char *zCmd = isRedo ? "redo" : "undo";
292
+ db_must_be_within_tree();
293
+ db_begin_transaction();
294
+ undo_available = db_lget_int("undo_available", 0);
295
+ if( explainFlag ){
296
+ if( undo_available==0 ){
297
+ printf("No undo or redo is available\n");
298
+ }else{
299
+ Stmt q;
300
+ int nChng = 0;
301
+ zCmd = undo_available==1 ? "undo" : "redo";
302
+ printf("A %s is available for the following command:\n\n %s %s\n\n",
303
+ zCmd, g.argv[0], db_lget("undo_cmdline", "???"));
304
+ db_prepare(&q,
305
+ "SELECT existsflag, pathname FROM undo ORDER BY pathname"
306
+ );
307
+ while( db_step(&q)==SQLITE_ROW ){
308
+ if( nChng==0 ){
309
+ printf("The following file changes would occur if the "
310
+ "command above is %sne:\n\n", zCmd);
311
+ }
312
+ nChng++;
313
+ printf("%s %s\n",
314
+ db_column_int(&q,0) ? "UPDATE" : "DELETE",
315
+ db_column_text(&q, 1)
316
+ );
317
+ }
318
+ db_finalize(&q);
319
+ if( nChng==0 ){
320
+ printf("No file changes would occur with this undo/redo.\n");
321
+ }
322
+ }
323
+ }else if( g.argc==2 ){
324
+ if( undo_available!=(1+isRedo) ){
325
+ fossil_fatal("nothing to %s", zCmd);
326
+ }
327
+ undo_all(isRedo);
328
+ db_lset_int("undo_available", 2-isRedo);
329
+ }else if( g.argc>=3 ){
330
+ int i;
331
+ if( undo_available==0 ){
332
+ fossil_fatal("nothing to %s", zCmd);
333
+ }
334
+ for(i=2; i<g.argc; i++){
335
+ const char *zFile = g.argv[i];
336
+ Blob path;
337
+ file_tree_name(zFile, &path, 1);
338
+ undo_one(blob_str(&path), isRedo);
319339
blob_reset(&path);
320340
}
321341
}
322342
db_end_transaction(0);
323343
}
324344
--- src/undo.c
+++ src/undo.c
@@ -133,16 +133,38 @@
133 void undo_reset(void){
134 static const char zSql[] =
135 @ DROP TABLE IF EXISTS undo;
136 @ DROP TABLE IF EXISTS undo_vfile;
137 @ DROP TABLE IF EXISTS undo_vmerge;
138 @ DROP TABLE IF EXISTS undo_pending;
139 ;
140 db_multi_exec(zSql);
141 db_lset_int("undo_available", 0);
142 db_lset_int("undo_checkout", 0);
143 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
145 /*
146 ** This flag is true if we are in the process of collecting file changes
147 ** for undo. When this flag is false, undo_save() is a no-op.
148 */
@@ -161,18 +183,18 @@
161 @ existsflag BOOLEAN, -- True if the file exists
162 @ content BLOB -- Saved content
163 @ );
164 @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
165 @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
166 @ CREATE TABLE %s.undo_pending(undoId INTEGER PRIMARY KEY);
167 ;
168 undo_reset();
169 if( strcmp(g.zMainDbType,zDb)==0 ) zDb = "main";
170 db_multi_exec(zSql, zDb, zDb, zDb, zDb);
171 cid = db_lget_int("checkout", 0);
172 db_lset_int("undo_checkout", cid);
173 db_lset_int("undo_available", 1);
 
174 undoActive = 1;
175 }
176
177 /*
178 ** This flag is true if one or more files have changed and have been
@@ -243,81 +265,79 @@
243 undo_all_filesystem(0);
244 }
245
246 /*
247 ** COMMAND: undo
 
248 **
249 ** Usage: %fossil undo ?FILENAME...?
 
250 **
251 ** Undo the most recent update or merge or revert operation. If FILENAME is
252 ** specified then restore the content of the named file(s) but otherwise
253 ** leave the update or merge or revert in effect.
 
 
 
 
 
254 **
255 ** A single level of undo/redo is supported. The undo/redo stack
256 ** is cleared by the commit and checkout commands.
257 */
258 void undo_cmd(void){
259 int undo_available;
260 db_must_be_within_tree();
261 db_begin_transaction();
262 undo_available = db_lget_int("undo_available", 0);
263 if( g.argc==2 ){
264 if( undo_available!=1 ){
265 fossil_fatal("no update or merge operation is available to undo");
266 }
267 undo_all(0);
268 db_lset_int("undo_available", 2);
269 }else if( g.argc>=3 ){
270 int i;
271 if( undo_available==0 ){
272 fossil_fatal("no update or merge operation is available to undo");
273 }
274 for(i=2; i<g.argc; i++){
275 const char *zFile = g.argv[i];
276 Blob path;
277 file_tree_name(zFile, &path, 1);
278 undo_one(blob_str(&path), 0);
279 blob_reset(&path);
280 }
281 }
282 db_end_transaction(0);
283 }
284
285 /*
286 ** COMMAND: redo
287 **
288 ** Usage: %fossil redo ?FILENAME...?
289 **
290 ** Redo an update or merge or revert operation that has been undone
291 ** by the undo command. If FILENAME is specified then restore the changes
292 ** associated with the named file(s) but otherwise leave the update
293 ** or merge undone.
294 **
295 ** A single level of undo/redo is supported. The undo/redo stack
296 ** is cleared by the commit and checkout commands.
297 */
298 void redo_cmd(void){
299 int undo_available;
300 db_must_be_within_tree();
301 db_begin_transaction();
302 undo_available = db_lget_int("undo_available", 0);
303 if( g.argc==2 ){
304 if( undo_available!=2 ){
305 fossil_fatal("no undone update or merge operation is available to redo");
306 }
307 undo_all(1);
308 db_lset_int("undo_available", 1);
309 }else if( g.argc>=3 ){
310 int i;
311 if( undo_available==0 ){
312 fossil_fatal("no update or merge operation is available to redo");
313 }
314 for(i=2; i<g.argc; i++){
315 const char *zFile = g.argv[i];
316 Blob path;
317 file_tree_name(zFile, &path, 1);
318 undo_one(blob_str(&path), 0);
319 blob_reset(&path);
320 }
321 }
322 db_end_transaction(0);
323 }
324
--- src/undo.c
+++ src/undo.c
@@ -133,16 +133,38 @@
133 void undo_reset(void){
134 static const char zSql[] =
135 @ DROP TABLE IF EXISTS undo;
136 @ DROP TABLE IF EXISTS undo_vfile;
137 @ DROP TABLE IF EXISTS undo_vmerge;
 
138 ;
139 db_multi_exec(zSql);
140 db_lset_int("undo_available", 0);
141 db_lset_int("undo_checkout", 0);
142 }
143
144 /*
145 ** The following variable stores the original command-line of the
146 ** command that is a candidate to be undone.
147 */
148 static char *undoCmd = 0;
149
150 /*
151 ** Capture the current command-line and store it as part of the undo
152 ** state. This routine is called before options are extracted from the
153 ** command-line so that we can record the complete command-line.
154 */
155 void undo_capture_command_line(void){
156 Blob cmdline;
157 int i;
158 assert( undoCmd==0 );
159 blob_zero(&cmdline);
160 for(i=1; i<g.argc; i++){
161 if( i>1 ) blob_append(&cmdline, " ", 1);
162 blob_append(&cmdline, g.argv[i], -1);
163 }
164 undoCmd = blob_str(&cmdline);
165 }
166
167 /*
168 ** This flag is true if we are in the process of collecting file changes
169 ** for undo. When this flag is false, undo_save() is a no-op.
170 */
@@ -161,18 +183,18 @@
183 @ existsflag BOOLEAN, -- True if the file exists
184 @ content BLOB -- Saved content
185 @ );
186 @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
187 @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
 
188 ;
189 undo_reset();
190 if( strcmp(g.zMainDbType,zDb)==0 ) zDb = "main";
191 db_multi_exec(zSql, zDb, zDb, zDb, zDb);
192 cid = db_lget_int("checkout", 0);
193 db_lset_int("undo_checkout", cid);
194 db_lset_int("undo_available", 1);
195 db_lset("undo_cmdline", undoCmd);
196 undoActive = 1;
197 }
198
199 /*
200 ** This flag is true if one or more files have changed and have been
@@ -243,81 +265,79 @@
265 undo_all_filesystem(0);
266 }
267
268 /*
269 ** COMMAND: undo
270 ** COMMAND: redo
271 **
272 ** Usage: %fossil undo ?--explain? ?FILENAME...?
273 ** or: %fossil redo ?--explain? ?FILENAME...?
274 **
275 ** Undo the most recent update or merge or revert operation. If FILENAME is
276 ** specified then restore the content of the named file(s) but otherwise
277 ** leave the update or merge or revert in effect. The redo command undoes
278 ** the effect of the most recent undo.
279 **
280 ** If the --explain option is present, not changes are made and instead
281 ** the undo or redo command explains what actions the undo or redo would
282 ** have done had the --explain been omitted.
283 **
284 ** A single level of undo/redo is supported. The undo/redo stack
285 ** is cleared by the commit and checkout commands.
286 */
287 void undo_cmd(void){
288 int isRedo = g.argv[1][0]=='r';
289 int undo_available;
290 int explainFlag = find_option("explain", 0, 0)!=0;
291 const char *zCmd = isRedo ? "redo" : "undo";
292 db_must_be_within_tree();
293 db_begin_transaction();
294 undo_available = db_lget_int("undo_available", 0);
295 if( explainFlag ){
296 if( undo_available==0 ){
297 printf("No undo or redo is available\n");
298 }else{
299 Stmt q;
300 int nChng = 0;
301 zCmd = undo_available==1 ? "undo" : "redo";
302 printf("A %s is available for the following command:\n\n %s %s\n\n",
303 zCmd, g.argv[0], db_lget("undo_cmdline", "???"));
304 db_prepare(&q,
305 "SELECT existsflag, pathname FROM undo ORDER BY pathname"
306 );
307 while( db_step(&q)==SQLITE_ROW ){
308 if( nChng==0 ){
309 printf("The following file changes would occur if the "
310 "command above is %sne:\n\n", zCmd);
311 }
312 nChng++;
313 printf("%s %s\n",
314 db_column_int(&q,0) ? "UPDATE" : "DELETE",
315 db_column_text(&q, 1)
316 );
317 }
318 db_finalize(&q);
319 if( nChng==0 ){
320 printf("No file changes would occur with this undo/redo.\n");
321 }
322 }
323 }else if( g.argc==2 ){
324 if( undo_available!=(1+isRedo) ){
325 fossil_fatal("nothing to %s", zCmd);
326 }
327 undo_all(isRedo);
328 db_lset_int("undo_available", 2-isRedo);
329 }else if( g.argc>=3 ){
330 int i;
331 if( undo_available==0 ){
332 fossil_fatal("nothing to %s", zCmd);
333 }
334 for(i=2; i<g.argc; i++){
335 const char *zFile = g.argv[i];
336 Blob path;
337 file_tree_name(zFile, &path, 1);
338 undo_one(blob_str(&path), isRedo);
 
 
 
 
 
 
 
 
 
339 blob_reset(&path);
340 }
341 }
342 db_end_transaction(0);
343 }
344
+3 -1
--- src/update.c
+++ src/update.c
@@ -67,10 +67,11 @@
6767
int nChng; /* Number of file renames */
6868
int *aChng; /* Array of file renames */
6969
int i; /* Loop counter */
7070
int nConflict = 0; /* Number of merge conflicts */
7171
72
+ undo_capture_command_line();
7273
url_proxy_options();
7374
latestFlag = find_option("latest",0, 0)!=0;
7475
nochangeFlag = find_option("nochange","n",0)!=0;
7576
verboseFlag = find_option("verbose","v",0)!=0;
7677
debugFlag = find_option("debug",0,0)!=0;
@@ -464,11 +465,12 @@
464465
Blob record;
465466
int i;
466467
int errCode;
467468
int rid = 0;
468469
Stmt q;
469
-
470
+
471
+ undo_capture_command_line();
470472
zRevision = find_option("revision", "r", 1);
471473
verify_all_options();
472474
473475
if( g.argc<2 ){
474476
usage("?OPTIONS? [FILE] ...");
475477
--- src/update.c
+++ src/update.c
@@ -67,10 +67,11 @@
67 int nChng; /* Number of file renames */
68 int *aChng; /* Array of file renames */
69 int i; /* Loop counter */
70 int nConflict = 0; /* Number of merge conflicts */
71
 
72 url_proxy_options();
73 latestFlag = find_option("latest",0, 0)!=0;
74 nochangeFlag = find_option("nochange","n",0)!=0;
75 verboseFlag = find_option("verbose","v",0)!=0;
76 debugFlag = find_option("debug",0,0)!=0;
@@ -464,11 +465,12 @@
464 Blob record;
465 int i;
466 int errCode;
467 int rid = 0;
468 Stmt q;
469
 
470 zRevision = find_option("revision", "r", 1);
471 verify_all_options();
472
473 if( g.argc<2 ){
474 usage("?OPTIONS? [FILE] ...");
475
--- src/update.c
+++ src/update.c
@@ -67,10 +67,11 @@
67 int nChng; /* Number of file renames */
68 int *aChng; /* Array of file renames */
69 int i; /* Loop counter */
70 int nConflict = 0; /* Number of merge conflicts */
71
72 undo_capture_command_line();
73 url_proxy_options();
74 latestFlag = find_option("latest",0, 0)!=0;
75 nochangeFlag = find_option("nochange","n",0)!=0;
76 verboseFlag = find_option("verbose","v",0)!=0;
77 debugFlag = find_option("debug",0,0)!=0;
@@ -464,11 +465,12 @@
465 Blob record;
466 int i;
467 int errCode;
468 int rid = 0;
469 Stmt q;
470
471 undo_capture_command_line();
472 zRevision = find_option("revision", "r", 1);
473 verify_all_options();
474
475 if( g.argc<2 ){
476 usage("?OPTIONS? [FILE] ...");
477

Keyboard Shortcuts

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