File Annotation
Not logged in
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,
a36177bcce 2007-09-11       drh:     "SELECT content, exists 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(&current, zFullname);
a36177bcce 2007-09-11       drh:     }else{
a36177bcce 2007-09-11       drh:       blob_zero(&current);
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:     printf("%sdo changes to %s\n", redoFlag ? "Re" : "Un", zPathname);
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", &current);
a36177bcce 2007-09-11       drh:     }
a36177bcce 2007-09-11       drh:     db_step(&q);
a36177bcce 2007-09-11       drh:     blob_reset(&current);
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;
a36177bcce 2007-09-11       drh:   db_prepare(&q, "SELECT pathname FROM undo WHERE redoflag=%d"
a36177bcce 2007-09-11       drh:                  " ORDER BY +pathname", redoFlag);
a36177bcce 2007-09-11       drh:   while( db_step(&q) ){
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:   );
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);
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){
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;
a36177bcce 2007-09-11       drh:     @ CREATE TABLE undo_vfile 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);
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);
a36177bcce 2007-09-11       drh:   blob_reset(&content);
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: }