Fossil

Check-in [b3dadcc4]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Begin adding code to implement the "stash" command. The code compiles and runs but is currently incomplete. This is an incremental check-in to preserve state. (Ironically, if "stash" were working, I'd have probably just done a "stash snapshot" to capture this state, rather than an experimental branch.)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:b3dadcc4a6dbe5b89a7979f7f1b6ff5d93d2382d
User & Date: drh 2010-12-18 17:24:21
Context
2010-12-18
18:56
The "stash" functionality is now in place. Need to test more prior to merging into trunk. check-in: 22aa74dc user: drh tags: experimental
17:24
Begin adding code to implement the "stash" command. The code compiles and runs but is currently incomplete. This is an incremental check-in to preserve state. (Ironically, if "stash" were working, I'd have probably just done a "stash snapshot" to capture this state, rather than an experimental branch.) check-in: b3dadcc4 user: drh tags: experimental
14:18
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. check-in: 30981b64 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.mk.

66
67
68
69
70
71
72

73
74
75
76
77
78
79
...
144
145
146
147
148
149
150

151
152
153
154
155
156
157
...
222
223
224
225
226
227
228

229
230
231
232
233
234
235
...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
...
696
697
698
699
700
701
702







703
704
705
706
707
708
709
  $(SRCDIR)/schema.c \
  $(SRCDIR)/search.c \
  $(SRCDIR)/setup.c \
  $(SRCDIR)/sha1.c \
  $(SRCDIR)/shun.c \
  $(SRCDIR)/skins.c \
  $(SRCDIR)/sqlcmd.c \

  $(SRCDIR)/stat.c \
  $(SRCDIR)/style.c \
  $(SRCDIR)/sync.c \
  $(SRCDIR)/tag.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
................................................................................
  schema_.c \
  search_.c \
  setup_.c \
  sha1_.c \
  shun_.c \
  skins_.c \
  sqlcmd_.c \

  stat_.c \
  style_.c \
  sync_.c \
  tag_.c \
  th_main_.c \
  timeline_.c \
  tkt_.c \
................................................................................
 $(OBJDIR)/schema.o \
 $(OBJDIR)/search.o \
 $(OBJDIR)/setup.o \
 $(OBJDIR)/sha1.o \
 $(OBJDIR)/shun.o \
 $(OBJDIR)/skins.o \
 $(OBJDIR)/sqlcmd.o \

 $(OBJDIR)/stat.o \
 $(OBJDIR)/style.o \
 $(OBJDIR)/sync.o \
 $(OBJDIR)/tag.o \
 $(OBJDIR)/th_main.o \
 $(OBJDIR)/timeline.o \
 $(OBJDIR)/tkt.o \
................................................................................
#
$(SRCDIR)/../manifest:	
	# noop

clean:	
	rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
	rm -f translate makeheaders mkindex page_index.h headers
	rm -f add.h allrepo.h attach.h bag.h bisect.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h event.h export.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h import.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h sqlcmd.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h

page_index.h: $(TRANS_SRC) mkindex
	./mkindex $(TRANS_SRC) >$@
headers:	page_index.h makeheaders VERSION.h
	./makeheaders  add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
	touch headers
headers: Makefile
Makefile:
add_.c:	$(SRCDIR)/add.c translate
	./translate $(SRCDIR)/add.c >add_.c

$(OBJDIR)/add.o:	add_.c add.h  $(SRCDIR)/config.h
................................................................................
sqlcmd_.c:	$(SRCDIR)/sqlcmd.c translate
	./translate $(SRCDIR)/sqlcmd.c >sqlcmd_.c

$(OBJDIR)/sqlcmd.o:	sqlcmd_.c sqlcmd.h  $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/sqlcmd.o -c sqlcmd_.c

sqlcmd.h:	headers







stat_.c:	$(SRCDIR)/stat.c translate
	./translate $(SRCDIR)/stat.c >stat_.c

$(OBJDIR)/stat.o:	stat_.c stat.h  $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/stat.o -c stat_.c

stat.h:	headers







>







 







>







 







>







 







|




|







 







>
>
>
>
>
>
>







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
  $(SRCDIR)/schema.c \
  $(SRCDIR)/search.c \
  $(SRCDIR)/setup.c \
  $(SRCDIR)/sha1.c \
  $(SRCDIR)/shun.c \
  $(SRCDIR)/skins.c \
  $(SRCDIR)/sqlcmd.c \
  $(SRCDIR)/stash.c \
  $(SRCDIR)/stat.c \
  $(SRCDIR)/style.c \
  $(SRCDIR)/sync.c \
  $(SRCDIR)/tag.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
................................................................................
  schema_.c \
  search_.c \
  setup_.c \
  sha1_.c \
  shun_.c \
  skins_.c \
  sqlcmd_.c \
  stash_.c \
  stat_.c \
  style_.c \
  sync_.c \
  tag_.c \
  th_main_.c \
  timeline_.c \
  tkt_.c \
................................................................................
 $(OBJDIR)/schema.o \
 $(OBJDIR)/search.o \
 $(OBJDIR)/setup.o \
 $(OBJDIR)/sha1.o \
 $(OBJDIR)/shun.o \
 $(OBJDIR)/skins.o \
 $(OBJDIR)/sqlcmd.o \
 $(OBJDIR)/stash.o \
 $(OBJDIR)/stat.o \
 $(OBJDIR)/style.o \
 $(OBJDIR)/sync.o \
 $(OBJDIR)/tag.o \
 $(OBJDIR)/th_main.o \
 $(OBJDIR)/timeline.o \
 $(OBJDIR)/tkt.o \
................................................................................
#
$(SRCDIR)/../manifest:	
	# noop

clean:	
	rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h
	rm -f translate makeheaders mkindex page_index.h headers
	rm -f add.h allrepo.h attach.h bag.h bisect.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h event.h export.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h import.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h sqlcmd.h stash.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h

page_index.h: $(TRANS_SRC) mkindex
	./mkindex $(TRANS_SRC) >$@
headers:	page_index.h makeheaders VERSION.h
	./makeheaders  add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h
	touch headers
headers: Makefile
Makefile:
add_.c:	$(SRCDIR)/add.c translate
	./translate $(SRCDIR)/add.c >add_.c

$(OBJDIR)/add.o:	add_.c add.h  $(SRCDIR)/config.h
................................................................................
sqlcmd_.c:	$(SRCDIR)/sqlcmd.c translate
	./translate $(SRCDIR)/sqlcmd.c >sqlcmd_.c

$(OBJDIR)/sqlcmd.o:	sqlcmd_.c sqlcmd.h  $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/sqlcmd.o -c sqlcmd_.c

sqlcmd.h:	headers
stash_.c:	$(SRCDIR)/stash.c translate
	./translate $(SRCDIR)/stash.c >stash_.c

$(OBJDIR)/stash.o:	stash_.c stash.h  $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/stash.o -c stash_.c

stash.h:	headers
stat_.c:	$(SRCDIR)/stat.c translate
	./translate $(SRCDIR)/stat.c >stat_.c

$(OBJDIR)/stat.o:	stat_.c stat.h  $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/stat.o -c stat_.c

stat.h:	headers

Changes to src/makemake.tcl.

59
60
61
62
63
64
65

66
67
68
69
70
71
72
  schema
  search
  setup
  sha1
  shun
  skins
  sqlcmd

  stat
  style
  sync
  tag
  th_main
  timeline
  tkt







>







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  schema
  search
  setup
  sha1
  shun
  skins
  sqlcmd
  stash
  stat
  style
  sync
  tag
  th_main
  timeline
  tkt

Added src/stash.c.



















































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
** Copyright (c) 2010 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)

** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@sqlite.org
**
*******************************************************************************
**
** This file contains code used to implement the "stash" command.
*/
#include "config.h"
#include "stash.h"
#include <assert.h>


/*
** SQL code to implement the tables needed by the stash.
*/
static const char zStashInit[] = 
@ CREATE TABLE IF NOT EXISTS %s.stash(
@   stashid INTEGER PRIMARY KEY,     -- Unique stash identifier
@   vid INTEGER,                     -- The baseline check-out for this stash
@   comment TEXT,                    -- Comment for this stash.  Or NULL
@   ctime TIMESTAMP                  -- When the stash was created
@ );
@ CREATE TABLE IF NOT EXISTS %s.stashfile(
@   stashid INTEGER REFERENCES stash,  -- Stash that contains this file
@   rid INTEGER,                       -- Baseline content in BLOB table or 0.
@   isAdded BOOLEAN,                   -- True if this is an added file
@   isRemoved BOOLEAN,                 -- True if this file is deleted
@   isExec BOOLEAN,                    -- True if file is executable
@   origname TEXT,                     -- Original filename
@   newname TEXT,                      -- New name for file at next check-in
@   delta BLOB,                        -- Delta from baseline. Content if rid=0
@   PRIMARY KEY(origname, stashid)
@ );
@ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1);
;

/*
** Add zFName to the stash given by stashid.  zFName might be the name of a
** file or a directory.  If a directory, add all changed files contained
** within that directory.
*/
static void stash_add_file_or_dir(int stashid, int vid, const char *zFName){
  char *zFile;          /* Normalized filename */
  char *zTreename;      /* Name of the file in the tree */
  Blob fname;           /* Filename relative to root */
  Blob sql;             /* Query statement text */
  Stmt q;               /* Query against the vfile table */
  Stmt ins;             /* Insert statement */

  zFile = mprintf("%/", zFName);
  file_tree_name(zFile, &fname, 1);
  zTreename = blob_str(&fname);
  blob_zero(&sql);
  blob_appendf(&sql,
    "SELECT deleted, isexe, mrid, pathname, coalesce(origname,pathname)"
    "  FROM vfile"
    " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
    vid
  );
  if( strcmp(zTreename,".")!=0 ){
    blob_appendf(&sql,
      "   AND (pathname GLOB '%q/*' OR origname GLOB '%q/*'"
            "  OR pathname=%Q OR origname=%Q)",
      zTreename, zTreename, zTreename, zTreename
    );
  }
  db_prepare(&q, blob_str(&sql));
  blob_reset(&sql);
  db_prepare(&ins,
     "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec,"
                           "origname, newname, delta)"
     "VALUES(%d,:rid,:isadd,:isrm,:isexe,:orig,:new,:content)",
     stashid
  );
  while( db_step(&q)==SQLITE_ROW ){
    int deleted = db_column_int(&q, 0);
    int rid = db_column_int(&q, 2);
    const char *zName = db_column_text(&q, 3);
    const char *zOrig = db_column_text(&q, 4);
    char *zPath = mprintf("%s/%s", g.zLocalRoot, zName);
    Blob content;

    db_bind_int(&ins, ":rid", rid);
    db_bind_int(&ins, ":isadd", rid==0);
    db_bind_int(&ins, ":isrm", deleted);
#ifdef _WIN32
    db_bind_int(&ins, ":isexe", db_column_int(&q, 2));
#endif
    db_bind_text(&ins, ":orig", zOrig);
    db_bind_text(&ins, ":new", zName);
    if( rid==0 ){
      /* A new file */
      blob_read_from_file(&content, zPath);
      db_bind_blob(&ins, ":content", &content);
    }else if( deleted ){
      db_bind_null(&ins, ":content");
    }else{
      /* A modified file */
      Blob orig;
      Blob disk;
      blob_read_from_file(&disk, zPath);
      content_get(rid, &orig);
      blob_delta_create(&orig, &disk, &content);
      blob_reset(&orig);
      blob_reset(&disk);
      db_bind_blob(&ins, ":content", &content);
    }
    db_step(&ins);
    db_reset(&ins);
    fossil_free(zPath);
    blob_reset(&content);
  }
  db_finalize(&ins);
  db_finalize(&q);
  fossil_free(zFile);
  blob_reset(&fname);
}

/*
** Create a new stash based on the uncommitted changes currently in
** the working directory.
**
** If the "-m" or "--comment" command-line option is present, gather
** its argument as the stash comment.
**
** If files are named on the command-line, then only stash the named
** files.
*/
static void stash_create(void){
  const char *zComment;              /* Comment to add to the stash */
  int stashid;                       /* ID of the new stash */
  int vid;                           /* Current checkout */

  zComment = find_option("comment", "m", 1);
  verify_all_options();
  stashid = db_lget_int("stash-next", 1);
  db_lset_int("stash-next", stashid+1);
  vid = db_lget_int("checkout", 0);
  db_multi_exec(
    "INSERT INTO stash(stashid,vid,comment,ctime)"
    "VALUES(%d,%d,%Q,julianday('now'))",
    stashid, vid, zComment
  );
  if( g.argc>3 ){
    int i;
    for(i=3; i<g.argc; i++){
      stash_add_file_or_dir(stashid, vid, g.argv[i]);
    }
  }else{
    stash_add_file_or_dir(stashid, vid, ".");
  } 
}

/*
** COMMAND: stash
**
** Usage: %fossil COMMAND ARGS...
**
**    fossil stash
**    fossil stash save [-m COMMENT] [FILES...]
**
**         Save the current changes in the working tree as a new stash.
**         Then revert the changes back to the last check-in.  If FILES
**         are listed, then only stash and revert the named files.  The
**         "save" verb can be omitted if and only if there are no other
**         arguments.
**
**    fossil stash list
**
**         List all changes sets currently stashed.
**
**    fossil stash pop
**
**         Apply the most recently create stash to the current working
**         check-out.  Then delete that stash.  This is equivalent to
**         doing an "apply" and a "drop" against the most recent stash.
**
**    fossil stash apply STASHID
**
**         Apply the identified stash to the current working check-out.
**         But unlike "pop", keep the stash so that it can be used again.
**
**    fossil stash goto STASHID
**
**         Update to the baseline checkout for STASHID then apply the
**         changes of STASHID.  Keep STASHID so that it can be reused
**
**    fossil drop STASHID
**
**         Forget everything about STASHID.
**
**    fossil stash snapshot [-m COMMENT] [FILES...]
**
**         Save the current changes in the working tress as a new stash
**         but, unlike "save", do not revert those changes.
*/
void stash_cmd(void){
  const char *zDb = "localdb";
  const char *zCmd;
  int nCmd;
  db_must_be_within_tree();
  db_begin_transaction();
  if( strcmp(g.zMainDbType, zDb)==0 ) zDb = "main";
  db_multi_exec(zStashInit, zDb, zDb);
  if( g.argc<=2 ){
    zCmd = "save";
  }else{
    zCmd = g.argv[2];
  }
  nCmd = strlen(zCmd);
  if( memcmp(zCmd, "save", nCmd)==0 ){
    stash_create();
    undo_disable();
    revert_cmd();
  }else
  if( memcmp(zCmd, "snapshot", nCmd)==0 ){
    stash_create();
  }else
  if( memcmp(zCmd, "list", nCmd)==0 ){
    Stmt q;
    verify_all_options();
    db_prepare(&q,
       "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
       "       comment, datetime(ctime) FROM stash"
       " ORDER BY ctime DESC"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zCom;
      printf("%5d: [%.14s] on %s\n",
        db_column_int(&q, 0),
        db_column_text(&q, 1),
        db_column_text(&q, 3)
      );
      zCom = db_column_text(&q, 2);
      if( zCom && zCom[0] ){
        printf("      ");
        comment_print(zCom, 7, 79);
      }
    }
    db_finalize(&q);
  }else
  if( memcmp(zCmd, "drop", nCmd)==0 ){
  }else
  if( memcmp(zCmd, "pop", nCmd)==0 ){
  }else
  if( memcmp(zCmd, "apply", nCmd)==0 ){
  }else
  if( memcmp(zCmd, "goto", nCmd)==0 ){
  }else
  {
    usage("apply|drop|goto|list|pop|save|snapshot ARGS...");
  }
  db_end_transaction(0);
}

Changes to src/undo.c.

142
143
144
145
146
147
148










149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
...
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197







198
199
200
201
202
203
204
...
238
239
240
241
242
243
244




245
246
247
248
249
250
251
...
286
287
288
289
290
291
292

293
294
295
296
297
298
299
...
316
317
318
319
320
321
322



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339






340
341
342
343
}

/*
** The following variable stores the original command-line of the
** command that is a candidate to be undone.
*/
static char *undoCmd = 0;











/*
** Capture the current command-line and store it as part of the undo
** state.  This routine is called before options are extracted from the
** command-line so that we can record the complete command-line.
*/
void undo_capture_command_line(void){
  Blob cmdline;
  int i;
  assert( undoCmd==0 );

  blob_zero(&cmdline);
  for(i=1; i<g.argc; i++){
    if( i>1 ) blob_append(&cmdline, " ", 1);
    blob_append(&cmdline, g.argv[i], -1);
  }
  undoCmd = blob_str(&cmdline);
}

/*
** This flag is true if we are in the process of collecting file changes
** for undo.  When this flag is false, undo_save() is a no-op.
*/
static int undoActive = 0;

/*
** Begin capturing a snapshot that can be undone.
*/
void undo_begin(void){
  int cid;
  const char *zDb = "localdb";
  static const char zSql[] = 
................................................................................
    @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
    @   existsflag BOOLEAN,               -- True if the file exists
    @   content BLOB                      -- Saved content
    @ );
    @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
    @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
  ;

  undo_reset();
  if( strcmp(g.zMainDbType,zDb)==0 ) zDb = "main";
  db_multi_exec(zSql, zDb, zDb, zDb, zDb);
  cid = db_lget_int("checkout", 0);
  db_lset_int("undo_checkout", cid);
  db_lset_int("undo_available", 1);
  db_lset("undo_cmdline", undoCmd);
  undoActive = 1;
}








/*
** This flag is true if one or more files have changed and have been
** recorded in the undo log but the undo log has not yet been committed.
**
** If a fatal error occurs and this flag is set, that means we should
** rollback all the filesystem changes.
................................................................................
}

/*
** Complete the undo process is one is currently in process.
*/
void undo_finish(void){
  if( undoActive ){




    undoActive = 0;
    undoNeedRollback = 0;
  }
}

/*
** This routine is called when the process aborts due to an error.
................................................................................
*/
void undo_cmd(void){
  int isRedo = g.argv[1][0]=='r';
  int undo_available;
  int explainFlag = find_option("explain", 0, 0)!=0;
  const char *zCmd = isRedo ? "redo" : "undo";
  db_must_be_within_tree();

  db_begin_transaction();
  undo_available = db_lget_int("undo_available", 0);
  if( explainFlag ){
    if( undo_available==0 ){
      printf("No undo or redo is available\n");
    }else{
      Stmt q;
................................................................................
        );
      }
      db_finalize(&q);
      if( nChng==0 ){
        printf("No file changes would occur with this undo/redo.\n");
      }
    }



  }else if( g.argc==2 ){
    if( undo_available!=(1+isRedo) ){
      fossil_fatal("nothing to %s", zCmd);
    }
    undo_all(isRedo);
    db_lset_int("undo_available", 2-isRedo);
  }else if( g.argc>=3 ){
    int i;
    if( undo_available==0 ){
      fossil_fatal("nothing to %s", zCmd);
    }
    for(i=2; i<g.argc; i++){
      const char *zFile = g.argv[i];
      Blob path;
      file_tree_name(zFile, &path, 1);
      undo_one(blob_str(&path), isRedo);
      blob_reset(&path);






    }
  }
  db_end_transaction(0);
}







>
>
>
>
>
>
>
>
>
>










>








<
<
<
<
<
<







 







>









>
>
>
>
>
>
>







 







>
>
>
>







 







>







 







>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>




142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177






178
179
180
181
182
183
184
...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
}

/*
** The following variable stores the original command-line of the
** command that is a candidate to be undone.
*/
static char *undoCmd = 0;

/*
** This flag is true if we are in the process of collecting file changes
** for undo.  When this flag is false, undo_save() is a no-op.
**
** The undoDisable flag, if set, prevents undo from being activated.
*/
static int undoActive = 0;
static int undoDisable = 0;


/*
** Capture the current command-line and store it as part of the undo
** state.  This routine is called before options are extracted from the
** command-line so that we can record the complete command-line.
*/
void undo_capture_command_line(void){
  Blob cmdline;
  int i;
  assert( undoCmd==0 );
  if( undoDisable ) return;
  blob_zero(&cmdline);
  for(i=1; i<g.argc; i++){
    if( i>1 ) blob_append(&cmdline, " ", 1);
    blob_append(&cmdline, g.argv[i], -1);
  }
  undoCmd = blob_str(&cmdline);
}







/*
** Begin capturing a snapshot that can be undone.
*/
void undo_begin(void){
  int cid;
  const char *zDb = "localdb";
  static const char zSql[] = 
................................................................................
    @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
    @   existsflag BOOLEAN,               -- True if the file exists
    @   content BLOB                      -- Saved content
    @ );
    @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
    @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
  ;
  if( undoDisable ) return;
  undo_reset();
  if( strcmp(g.zMainDbType,zDb)==0 ) zDb = "main";
  db_multi_exec(zSql, zDb, zDb, zDb, zDb);
  cid = db_lget_int("checkout", 0);
  db_lset_int("undo_checkout", cid);
  db_lset_int("undo_available", 1);
  db_lset("undo_cmdline", undoCmd);
  undoActive = 1;
}

/*
** Permanently disable undo 
*/
void undo_disable(void){
  undoDisable = 1;
}

/*
** This flag is true if one or more files have changed and have been
** recorded in the undo log but the undo log has not yet been committed.
**
** If a fatal error occurs and this flag is set, that means we should
** rollback all the filesystem changes.
................................................................................
}

/*
** Complete the undo process is one is currently in process.
*/
void undo_finish(void){
  if( undoActive ){
    if( undoNeedRollback ){
      printf("\"fossil undo\" is available to undo changes"
             " to the working checkout.\n");
    }
    undoActive = 0;
    undoNeedRollback = 0;
  }
}

/*
** This routine is called when the process aborts due to an error.
................................................................................
*/
void undo_cmd(void){
  int isRedo = g.argv[1][0]=='r';
  int undo_available;
  int explainFlag = find_option("explain", 0, 0)!=0;
  const char *zCmd = isRedo ? "redo" : "undo";
  db_must_be_within_tree();
  verify_all_options();
  db_begin_transaction();
  undo_available = db_lget_int("undo_available", 0);
  if( explainFlag ){
    if( undo_available==0 ){
      printf("No undo or redo is available\n");
    }else{
      Stmt q;
................................................................................
        );
      }
      db_finalize(&q);
      if( nChng==0 ){
        printf("No file changes would occur with this undo/redo.\n");
      }
    }
  }else{
    int vid1 = db_lget_int("checkout", 0);
    int vid2;
    if( g.argc==2 ){
      if( undo_available!=(1+isRedo) ){
        fossil_fatal("nothing to %s", zCmd);
      }
      undo_all(isRedo);
      db_lset_int("undo_available", 2-isRedo);
    }else if( g.argc>=3 ){
      int i;
      if( undo_available==0 ){
        fossil_fatal("nothing to %s", zCmd);
      }
      for(i=2; i<g.argc; i++){
        const char *zFile = g.argv[i];
        Blob path;
        file_tree_name(zFile, &path, 1);
        undo_one(blob_str(&path), isRedo);
        blob_reset(&path);
      }
    }
    vid2 = db_lget_int("checkout", 0);
    if( vid1!=vid2 ){
      printf("--------------------\n");
      show_common_info(vid2, "updated-to:", 1, 0);
    }
  }
  db_end_transaction(0);
}

Changes to src/update.c.

529
530
531
532
533
534
535
536
537
      free(zFull);
    }
    blob_reset(&record);
  }
  db_finalize(&q);
  undo_finish();
  db_end_transaction(0);
  printf("\"fossil undo\" is available to undo the changes shown above.\n");
}







<

529
530
531
532
533
534
535

536
      free(zFull);
    }
    blob_reset(&record);
  }
  db_finalize(&q);
  undo_finish();
  db_end_transaction(0);

}