a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** Copyright (c) 2007 D. Richard Hipp a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** This program is free software; you can redistribute it and/or a36177bcce 2007-09-11 drh: ** modify it under the terms of the GNU General Public a36177bcce 2007-09-11 drh: ** License version 2 as published by the Free Software Foundation. a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** This program is distributed in the hope that it will be useful, a36177bcce 2007-09-11 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of a36177bcce 2007-09-11 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU a36177bcce 2007-09-11 drh: ** General Public License for more details. a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** You should have received a copy of the GNU General Public a36177bcce 2007-09-11 drh: ** License along with this library; if not, write to the a36177bcce 2007-09-11 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, a36177bcce 2007-09-11 drh: ** Boston, MA 02111-1307, USA. a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** Author contact information: a36177bcce 2007-09-11 drh: ** drh@hwaci.com a36177bcce 2007-09-11 drh: ** http://www.hwaci.com/drh/ a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ******************************************************************************* a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** This file implements the undo/redo functionality. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: #include "config.h" a36177bcce 2007-09-11 drh: #include "undo.h" a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** Undo the change to the file zPathname. zPathname is the pathname a36177bcce 2007-09-11 drh: ** of the file relative to the root of the repository. If redoFlag is a36177bcce 2007-09-11 drh: ** true the redo a change. If there is nothing to undo (or redo) then a36177bcce 2007-09-11 drh: ** this routine is a noop. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: static void undo_one(const char *zPathname, int redoFlag){ a36177bcce 2007-09-11 drh: Stmt q; a36177bcce 2007-09-11 drh: char *zFullname; a36177bcce 2007-09-11 drh: db_prepare(&q, 6f5654c7ab 2007-09-12 drh: "SELECT content, existsflag FROM undo WHERE pathname=%Q AND redoflag=%d", a36177bcce 2007-09-11 drh: zPathname, redoFlag a36177bcce 2007-09-11 drh: ); a36177bcce 2007-09-11 drh: if( db_step(&q)==SQLITE_ROW ){ a36177bcce 2007-09-11 drh: int old_exists; a36177bcce 2007-09-11 drh: int new_exists; a36177bcce 2007-09-11 drh: Blob current; a36177bcce 2007-09-11 drh: Blob new; a36177bcce 2007-09-11 drh: zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); a36177bcce 2007-09-11 drh: new_exists = file_size(zFullname)>=0; a36177bcce 2007-09-11 drh: if( new_exists ){ a36177bcce 2007-09-11 drh: blob_read_from_file(¤t, zFullname); a36177bcce 2007-09-11 drh: }else{ a36177bcce 2007-09-11 drh: blob_zero(¤t); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: blob_zero(&new); a36177bcce 2007-09-11 drh: old_exists = db_column_int(&q, 1); a36177bcce 2007-09-11 drh: if( old_exists ){ a36177bcce 2007-09-11 drh: db_ephemeral_blob(&q, 0, &new); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: if( old_exists ){ a36177bcce 2007-09-11 drh: if( new_exists ){ a36177bcce 2007-09-11 drh: printf("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); a36177bcce 2007-09-11 drh: }else{ a36177bcce 2007-09-11 drh: printf("NEW %s\n", zPathname); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: blob_write_to_file(&new, zFullname); a36177bcce 2007-09-11 drh: }else{ a36177bcce 2007-09-11 drh: printf("DELETE %s\n", zPathname); a36177bcce 2007-09-11 drh: unlink(zFullname); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: blob_reset(&new); a36177bcce 2007-09-11 drh: free(zFullname); a36177bcce 2007-09-11 drh: db_finalize(&q); a36177bcce 2007-09-11 drh: db_prepare(&q, a36177bcce 2007-09-11 drh: "UPDATE undo SET content=:c, existsflag=%d, redoflag=NOT redoflag" a36177bcce 2007-09-11 drh: " WHERE pathname=%Q", a36177bcce 2007-09-11 drh: new_exists, zPathname a36177bcce 2007-09-11 drh: ); a36177bcce 2007-09-11 drh: if( new_exists ){ a36177bcce 2007-09-11 drh: db_bind_blob(&q, ":c", ¤t); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: db_step(&q); a36177bcce 2007-09-11 drh: blob_reset(¤t); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: db_finalize(&q); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** Undo or redo all undoable or redoable changes. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: static void undo_all(int redoFlag){ a36177bcce 2007-09-11 drh: Stmt q; 6f5654c7ab 2007-09-12 drh: int ucid; 6f5654c7ab 2007-09-12 drh: int ncid; a36177bcce 2007-09-11 drh: db_prepare(&q, "SELECT pathname FROM undo WHERE redoflag=%d" a36177bcce 2007-09-11 drh: " ORDER BY +pathname", redoFlag); 6f5654c7ab 2007-09-12 drh: while( db_step(&q)==SQLITE_ROW ){ a36177bcce 2007-09-11 drh: const char *zPathname = db_column_text(&q, 0); a36177bcce 2007-09-11 drh: undo_one(zPathname, redoFlag); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: db_finalize(&q); a36177bcce 2007-09-11 drh: db_multi_exec( a36177bcce 2007-09-11 drh: "CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;" a36177bcce 2007-09-11 drh: "DELETE FROM vfile;" a36177bcce 2007-09-11 drh: "INSERT INTO vfile SELECT * FROM undo_vfile;" a36177bcce 2007-09-11 drh: "DELETE FROM undo_vfile;" a36177bcce 2007-09-11 drh: "INSERT INTO undo_vfile SELECT * FROM undo_vfile_2;" a36177bcce 2007-09-11 drh: "DROP TABLE undo_vfile_2;" a36177bcce 2007-09-11 drh: "CREATE TEMP TABLE undo_vmerge_2 AS SELECT * FROM vmerge;" a36177bcce 2007-09-11 drh: "DELETE FROM vmerge;" a36177bcce 2007-09-11 drh: "INSERT INTO vmerge SELECT * FROM undo_vmerge;" a36177bcce 2007-09-11 drh: "DELETE FROM undo_vmerge;" a36177bcce 2007-09-11 drh: "INSERT INTO undo_vmerge SELECT * FROM undo_vmerge_2;" a36177bcce 2007-09-11 drh: "DROP TABLE undo_vmerge_2;" a36177bcce 2007-09-11 drh: ); 6f5654c7ab 2007-09-12 drh: ncid = db_lget_int("undo_checkout", 0); 6f5654c7ab 2007-09-12 drh: ucid = db_lget_int("checkout", 0); 6f5654c7ab 2007-09-12 drh: db_lset_int("undo_checkout", ucid); 6f5654c7ab 2007-09-12 drh: db_lset_int("checkout", ncid); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** Reset the the undo memory. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: void undo_reset(void){ a36177bcce 2007-09-11 drh: static const char zSql[] = a36177bcce 2007-09-11 drh: @ DROP TABLE IF EXISTS undo; a36177bcce 2007-09-11 drh: @ DROP TABLE IF EXISTS undo_vfile; a36177bcce 2007-09-11 drh: @ DROP TABLE IF EXISTS undo_vmerge; a36177bcce 2007-09-11 drh: ; a36177bcce 2007-09-11 drh: db_multi_exec(zSql); a36177bcce 2007-09-11 drh: db_lset_int("undo_available", 0); 6f5654c7ab 2007-09-12 drh: db_lset_int("undo_checkout", 0); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** Begin capturing a snapshot that can be undone. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: void undo_begin(void){ 6f5654c7ab 2007-09-12 drh: int cid; a36177bcce 2007-09-11 drh: static const char zSql[] = a36177bcce 2007-09-11 drh: @ CREATE TABLE undo( a36177bcce 2007-09-11 drh: @ pathname TEXT UNIQUE, -- Name of the file a36177bcce 2007-09-11 drh: @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable a36177bcce 2007-09-11 drh: @ existsflag BOOLEAN, -- True if the file exists a36177bcce 2007-09-11 drh: @ content BLOB -- Saved content a36177bcce 2007-09-11 drh: @ ); a36177bcce 2007-09-11 drh: @ CREATE TABLE undo_vfile AS SELECT * FROM vfile; 6f5654c7ab 2007-09-12 drh: @ CREATE TABLE undo_vmerge AS SELECT * FROM vmerge; a36177bcce 2007-09-11 drh: ; a36177bcce 2007-09-11 drh: undo_reset(); a36177bcce 2007-09-11 drh: db_multi_exec(zSql); 6f5654c7ab 2007-09-12 drh: cid = db_lget_int("checkout", 0); 6f5654c7ab 2007-09-12 drh: db_lset_int("undo_checkout", cid); a36177bcce 2007-09-11 drh: db_lset_int("undo_available", 1); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** Save the current content of the file zPathname so that it a36177bcce 2007-09-11 drh: ** will be undoable. The name is relative to the root of the a36177bcce 2007-09-11 drh: ** tree. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: void undo_save(const char *zPathname){ a36177bcce 2007-09-11 drh: char *zFullname; a36177bcce 2007-09-11 drh: Blob content; a36177bcce 2007-09-11 drh: int existsFlag; a36177bcce 2007-09-11 drh: Stmt q; a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); a36177bcce 2007-09-11 drh: existsFlag = file_size(zFullname)>=0; a36177bcce 2007-09-11 drh: db_prepare(&q, a36177bcce 2007-09-11 drh: "REPLACE INTO undo(pathname,redoflag,existsflag,content)" a36177bcce 2007-09-11 drh: " VALUES(%Q,0,%d,:c)", a36177bcce 2007-09-11 drh: zPathname, existsFlag a36177bcce 2007-09-11 drh: ); a36177bcce 2007-09-11 drh: if( existsFlag ){ a36177bcce 2007-09-11 drh: blob_read_from_file(&content, zFullname); a36177bcce 2007-09-11 drh: db_bind_blob(&q, ":c", &content); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: free(zFullname); a36177bcce 2007-09-11 drh: db_step(&q); a36177bcce 2007-09-11 drh: db_finalize(&q); 6f5654c7ab 2007-09-12 drh: if( existsFlag ){ 6f5654c7ab 2007-09-12 drh: blob_reset(&content); 6f5654c7ab 2007-09-12 drh: } a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** COMMAND: undo a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** Usage: %fossil undo ?FILENAME...? a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** Undo the most recent update or merge operation. If FILENAME is a36177bcce 2007-09-11 drh: ** specified then restore the content of the named file(s) but otherwise a36177bcce 2007-09-11 drh: ** leave the update or merge in effect. a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** A single level of undo/redo is supported. The undo/redo stack a36177bcce 2007-09-11 drh: ** is cleared by the commit and checkout commands. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: void undo_cmd(void){ a36177bcce 2007-09-11 drh: int undo_available; a36177bcce 2007-09-11 drh: db_must_be_within_tree(); a36177bcce 2007-09-11 drh: db_begin_transaction(); a36177bcce 2007-09-11 drh: undo_available = db_lget_int("undo_available", 0); a36177bcce 2007-09-11 drh: if( g.argc==2 ){ a36177bcce 2007-09-11 drh: if( undo_available!=1 ){ a36177bcce 2007-09-11 drh: fossil_fatal("no update or merge operation is available to undo"); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: undo_all(0); a36177bcce 2007-09-11 drh: db_lset_int("undo_available", 2); a36177bcce 2007-09-11 drh: }else if( g.argc>=3 ){ a36177bcce 2007-09-11 drh: int i; a36177bcce 2007-09-11 drh: if( undo_available==0 ){ a36177bcce 2007-09-11 drh: fossil_fatal("no update or merge operation is available to undo"); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: for(i=2; i<g.argc; i++){ a36177bcce 2007-09-11 drh: const char *zFile = g.argv[i]; a36177bcce 2007-09-11 drh: Blob path; a36177bcce 2007-09-11 drh: file_tree_name(zFile, &path); a36177bcce 2007-09-11 drh: undo_one(blob_str(&path), 0); a36177bcce 2007-09-11 drh: blob_reset(&path); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: db_end_transaction(0); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: a36177bcce 2007-09-11 drh: /* a36177bcce 2007-09-11 drh: ** COMMAND: redo a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** Usage: %fossil redo ?FILENAME...? a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** Redo the an update or merge operation that has been undone by the a36177bcce 2007-09-11 drh: ** undo command. If FILENAME is specified then restore the changes a36177bcce 2007-09-11 drh: ** associated with the named file(s) but otherwise leave the update a36177bcce 2007-09-11 drh: ** or merge undone. a36177bcce 2007-09-11 drh: ** a36177bcce 2007-09-11 drh: ** A single level of undo/redo is supported. The undo/redo stack a36177bcce 2007-09-11 drh: ** is cleared by the commit and checkout commands. a36177bcce 2007-09-11 drh: */ a36177bcce 2007-09-11 drh: void redo_cmd(void){ a36177bcce 2007-09-11 drh: int undo_available; a36177bcce 2007-09-11 drh: db_must_be_within_tree(); a36177bcce 2007-09-11 drh: db_begin_transaction(); a36177bcce 2007-09-11 drh: undo_available = db_lget_int("undo_available", 0); a36177bcce 2007-09-11 drh: if( g.argc==2 ){ a36177bcce 2007-09-11 drh: if( undo_available!=2 ){ a36177bcce 2007-09-11 drh: fossil_fatal("no undone update or merge operation is available to redo"); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: undo_all(1); a36177bcce 2007-09-11 drh: db_lset_int("undo_available", 1); a36177bcce 2007-09-11 drh: }else if( g.argc>=3 ){ a36177bcce 2007-09-11 drh: int i; a36177bcce 2007-09-11 drh: if( undo_available==0 ){ a36177bcce 2007-09-11 drh: fossil_fatal("no update or merge operation is available to redo"); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: for(i=2; i<g.argc; i++){ a36177bcce 2007-09-11 drh: const char *zFile = g.argv[i]; a36177bcce 2007-09-11 drh: Blob path; a36177bcce 2007-09-11 drh: file_tree_name(zFile, &path); a36177bcce 2007-09-11 drh: undo_one(blob_str(&path), 0); a36177bcce 2007-09-11 drh: blob_reset(&path); a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: } a36177bcce 2007-09-11 drh: db_end_transaction(0); a36177bcce 2007-09-11 drh: }