Fossil SCM

Enhance the stash so that it stores hashes and no long depends on RID value. Do this is a way that is backwards compatible and transparent to the user. After running any "stash" command using this version of Fossil or later, the schema will automatically update and the stash should survive a subsequent RID renumbering event in the repository without damage.

drh 2019-01-19 18:29 trunk
Commit ed06585f41ee2acef54b03da90445278b7a88f2241f29932855d071718d3b5cd
2 files changed +5 +131 -31
+5
--- src/db.c
+++ src/db.c
@@ -1662,10 +1662,14 @@
16621662
** sure that the repository database that was just opened has not
16631663
** be replaced by a clone of the same project, with different RID
16641664
** values.
16651665
*/
16661666
if( g.localOpen && !db_fingerprint_ok() ){
1667
+ /* Uncomment the following when we are ready for automatic recovery: */
1668
+#if 0
1669
+ stash_rid_renumbering_event();
1670
+#else
16671671
fossil_print(
16681672
"Oops. It looks like the repository database file located at\n"
16691673
" \"%s\"\n", zDbName
16701674
);
16711675
fossil_print(
@@ -1682,10 +1686,11 @@
16821686
"WILL BE IRREVOCABLY LOST.\n\n",
16831687
g.argv[0],
16841688
g.argv[0], zDbName
16851689
);
16861690
fossil_fatal("bad fingerprint");
1691
+#endif
16871692
}
16881693
}
16891694
16901695
/*
16911696
** Return true if there have been any changes to the repository
16921697
--- src/db.c
+++ src/db.c
@@ -1662,10 +1662,14 @@
1662 ** sure that the repository database that was just opened has not
1663 ** be replaced by a clone of the same project, with different RID
1664 ** values.
1665 */
1666 if( g.localOpen && !db_fingerprint_ok() ){
 
 
 
 
1667 fossil_print(
1668 "Oops. It looks like the repository database file located at\n"
1669 " \"%s\"\n", zDbName
1670 );
1671 fossil_print(
@@ -1682,10 +1686,11 @@
1682 "WILL BE IRREVOCABLY LOST.\n\n",
1683 g.argv[0],
1684 g.argv[0], zDbName
1685 );
1686 fossil_fatal("bad fingerprint");
 
1687 }
1688 }
1689
1690 /*
1691 ** Return true if there have been any changes to the repository
1692
--- src/db.c
+++ src/db.c
@@ -1662,10 +1662,14 @@
1662 ** sure that the repository database that was just opened has not
1663 ** be replaced by a clone of the same project, with different RID
1664 ** values.
1665 */
1666 if( g.localOpen && !db_fingerprint_ok() ){
1667 /* Uncomment the following when we are ready for automatic recovery: */
1668 #if 0
1669 stash_rid_renumbering_event();
1670 #else
1671 fossil_print(
1672 "Oops. It looks like the repository database file located at\n"
1673 " \"%s\"\n", zDbName
1674 );
1675 fossil_print(
@@ -1682,10 +1686,11 @@
1686 "WILL BE IRREVOCABLY LOST.\n\n",
1687 g.argv[0],
1688 g.argv[0], zDbName
1689 );
1690 fossil_fatal("bad fingerprint");
1691 #endif
1692 }
1693 }
1694
1695 /*
1696 ** Return true if there have been any changes to the repository
1697
+131 -31
--- src/stash.c
+++ src/stash.c
@@ -21,32 +21,145 @@
2121
#include <assert.h>
2222
2323
2424
/*
2525
** SQL code to implement the tables needed by the stash.
26
+**
27
+** Historical schema changes:
28
+**
29
+** 2019-01-19: stash.hash and stashfile.hash columns added. The
30
+** corresponding stash.vid and stashfile.rid columns are
31
+** retained for compatibility with older versions of
32
+** fossil but are no longer used.
33
+**
34
+** 2016-10-16: Change the PRIMARY KEY on stashfile from (origname,stashid)
35
+** to (newname,stashid).
36
+**
37
+** 2011-09-01: stashfile.isLink column added
38
+**
2639
*/
2740
static const char zStashInit[] =
2841
@ CREATE TABLE IF NOT EXISTS localdb.stash(
2942
@ stashid INTEGER PRIMARY KEY, -- Unique stash identifier
30
-@ vid INTEGER, -- The baseline checkout for this stash
43
+@ vid INTEGER, -- Legacy baseline RID value. Do not use.
44
+@ hash TEXT, -- The SHA hash for the baseline
3145
@ comment TEXT, -- Comment for this stash. Or NULL
3246
@ ctime TIMESTAMP -- When the stash was created
3347
@ );
3448
@ CREATE TABLE IF NOT EXISTS localdb.stashfile(
3549
@ stashid INTEGER REFERENCES stash, -- Stash that contains this file
36
-@ rid INTEGER, -- Baseline content in BLOB table or 0.
3750
@ isAdded BOOLEAN, -- True if this is an added file
3851
@ isRemoved BOOLEAN, -- True if this file is deleted
3952
@ isExec BOOLEAN, -- True if file is executable
4053
@ isLink BOOLEAN, -- True if file is a symlink
54
+@ rid INTEGER, -- Legacy baseline RID value. Do not use
55
+@ hash TEXT, -- Hash for baseline or NULL
4156
@ origname TEXT, -- Original filename
4257
@ newname TEXT, -- New name for file at next check-in
43
-@ delta BLOB, -- Delta from baseline. Content if rid=0
58
+@ delta BLOB, -- Delta from baseline or raw content
4459
@ PRIMARY KEY(newname, stashid)
4560
@ );
4661
@ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1);
4762
;
63
+
64
+/*
65
+** Make sure the stash and stashfile tables exist and have been
66
+** upgraded to their latest format. Create and upgrade the tables
67
+** as necessary.
68
+*/
69
+static void stash_tables_exist_and_current(void){
70
+ if( db_table_has_column("localdb","stashfile","hash") ){
71
+ /* The schema is up-to-date. But it could be that an older version
72
+ ** of Fossil that does no know about the stash.hash and stashfile.hash
73
+ ** columns has run since the schema was updated, and added entries that
74
+ ** have NULL hash columns. Check for this case, and fill in any missing
75
+ ** hash values.
76
+ */
77
+ if( db_int(0, "SELECT hash IS NULL FROM stash"
78
+ " ORDER BY stashid DESC LIMIT 1")
79
+ ){
80
+ db_multi_exec(
81
+ "UPDATE stash"
82
+ " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stash.vid)"
83
+ " WHERE hash IS NULL;"
84
+ "UPDATE stashfile"
85
+ " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stashfile.rid)"
86
+ " WHERE hash IS NULL AND rid>0;"
87
+ );
88
+ }
89
+ return;
90
+ }
91
+
92
+ if( !db_table_exists("localdb","stashfile")
93
+ || !db_table_exists("localdb","stash")
94
+ ){
95
+ /* Tables do not exist. Create them from scratch. */
96
+ db_multi_exec("DROP TABLE IF EXISTS localdb.stash;");
97
+ db_multi_exec("DROP TABLE IF EXISTS localdb.stashfile;");
98
+ db_multi_exec(zStashInit /*works-like:""*/);
99
+ return;
100
+ }
101
+
102
+ /* The tables exists but are not necessarily current. Upgrade them
103
+ ** to the latest format.
104
+ **
105
+ ** We can assume the 2011-09-01 format that includes the stashfile.isLink
106
+ ** column. The only upgrades we need to worry about the PRIMARY KEY
107
+ ** change on 2016-10-16 and the addition of the "hash" columns on
108
+ ** 2019-01-19.
109
+ */
110
+ db_multi_exec(
111
+ "ALTER TABLE localdb.stash RENAME TO old_stash;"
112
+ "ALTER TABLE localdb.stashfile RENAME TO old_stashfile;"
113
+ );
114
+ db_multi_exec(zStashInit /*works-like:""*/);
115
+ db_multi_exec(
116
+ "INSERT INTO localdb.stash(stashid,vid,hash,comment,ctime)"
117
+ " SELECT stashid, vid,"
118
+ " (SELECT uuid FROM blob WHERE blob.rid=old_stash.vid),"
119
+ " comment, ctime FROM old_stash;"
120
+ "DROP TABLE old_stash;"
121
+ );
122
+ db_multi_exec(
123
+ "INSERT INTO localdb.stashfile(stashid,isAdded,isRemoved,isExec,"
124
+ "isLink,rid,hash,origname,newname,delta)"
125
+ " SELECT stashid, isAdded, isRemoved, isExec, isLink, rid,"
126
+ " (SELECT uuid FROM blob WHERE blob.rid=old_stashfile.rid),"
127
+ " origname, newname, delta FROM old_stashfile;"
128
+ "DROP TABLE old_stashfile;"
129
+ );
130
+}
131
+
132
+/*
133
+** Update the stash.vid and stashfile.rid values after a RID renumbering
134
+** event.
135
+*/
136
+void stash_rid_renumbering_event(void){
137
+ if( !db_table_has_column("localdb","stash","hash") ){
138
+ /* If the stash schema was the older style that lacked hash value, then
139
+ ** recovery is not possible. Save off the old data, then reset the stash
140
+ ** to empty. */
141
+ if( db_table_exists("localdb","stash") ){
142
+ db_multi_exec("ALTER TABLE stash RENAME TO broken_stash;");
143
+ fossil_print("Unrecoverable stash content stored in \"broken_stash\"\n");
144
+ }
145
+ if( db_table_exists("localdb","stashfile") ){
146
+ db_multi_exec("ALTER TABLE stashfile RENAME TO broken_stashfile;");
147
+ fossil_print("Unrecoverable stashfile content stored"
148
+ " in \"broken_stashfile\"\n");
149
+ }
150
+ }else{
151
+ /* Reset stash.vid and stash.rid values based on hashes */
152
+ db_multi_exec(
153
+ "UPDATE stash"
154
+ " SET vid=(SELECT rid FROM blob WHERE blob.uuid=stash.hash);"
155
+ "UPDATE stashfile"
156
+ " SET rid=(SELECT rid FROM blob WHERE blob.uuid=stashfile.hash)"
157
+ " WHERE hash IS NOT NULL;"
158
+ );
159
+ }
160
+}
48161
49162
/*
50163
** Add zFName to the stash given by stashid. zFName might be the name of a
51164
** file or a directory. If a directory, add all changed files contained
52165
** within that directory.
@@ -77,13 +190,14 @@
77190
);
78191
}
79192
db_prepare(&q, "%s", blob_sql_text(&sql));
80193
blob_reset(&sql);
81194
db_prepare(&ins,
82
- "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
83
- "origname, newname, delta)"
84
- "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
195
+ "INSERT INTO stashfile(stashid, isAdded, isRemoved, isExec, isLink, rid, "
196
+ "hash, origname, newname, delta)"
197
+ "VALUES(%d,:isadd,:isrm,:isexe,:islink,:rid,"
198
+ "(SELECT uuid FROM blob WHERE rid=:rid),:orig,:new,:content)",
85199
stashid
86200
);
87201
while( db_step(&q)==SQLITE_ROW ){
88202
int deleted = db_column_int(&q, 0);
89203
int rid = db_column_int(&q, 3);
@@ -171,13 +285,13 @@
171285
stashid = db_lget_int("stash-next", 1);
172286
db_lset_int("stash-next", stashid+1);
173287
vid = db_lget_int("checkout", 0);
174288
vfile_check_signature(vid, 0);
175289
db_multi_exec(
176
- "INSERT INTO stash(stashid,vid,comment,ctime)"
177
- "VALUES(%d,%d,%Q,julianday('now'))",
178
- stashid, vid, zComment
290
+ "INSERT INTO stash(stashid,vid,hash,comment,ctime)"
291
+ "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d),%Q,julianday('now'))",
292
+ stashid, vid, vid, zComment
179293
);
180294
if( g.argc>3 ){
181295
int i;
182296
for(i=3; i<g.argc; i++){
183297
stash_add_file_or_dir(stashid, vid, g.argv[i]);
@@ -193,12 +307,12 @@
193307
*/
194308
static void stash_apply(int stashid, int nConflict){
195309
int vid;
196310
Stmt q;
197311
db_prepare(&q,
198
- "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
199
- " FROM stashfile WHERE stashid=%d",
312
+ "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
313
+ " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
200314
stashid
201315
);
202316
vid = db_lget_int("checkout",0);
203317
db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
204318
filename_collation());
@@ -296,12 +410,12 @@
296410
){
297411
Stmt q;
298412
Blob empty;
299413
blob_zero(&empty);
300414
db_prepare(&q,
301
- "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
302
- " FROM stashfile WHERE stashid=%d",
415
+ "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
416
+ " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
303417
stashid
304418
);
305419
while( db_step(&q)==SQLITE_ROW ){
306420
int rid = db_column_int(&q, 0);
307421
int isRemoved = db_column_int(&q, 1);
@@ -468,25 +582,11 @@
468582
int rc;
469583
undo_capture_command_line();
470584
db_must_be_within_tree();
471585
db_open_config(0, 0);
472586
db_begin_transaction();
473
- db_multi_exec(zStashInit /*works-like:""*/);
474
- rc = db_exists("SELECT 1 FROM sqlite_master"
475
- " WHERE name='stashfile'"
476
- " AND sql GLOB '* PRIMARY KEY(origname, stashid)*'");
477
- if( rc!=0 ){
478
- db_multi_exec(
479
- "CREATE TABLE localdb.stashfile_tmp AS SELECT * FROM stashfile;"
480
- "DROP TABLE stashfile;"
481
- );
482
- db_multi_exec(zStashInit /*works-like:""*/);
483
- db_multi_exec(
484
- "INSERT INTO stashfile SELECT * FROM stashfile_tmp;"
485
- "DROP TABLE stashfile_tmp;"
486
- );
487
- }
587
+ stash_tables_exist_and_current();
488588
if( g.argc<=2 ){
489589
zCmd = "save";
490590
}else{
491591
zCmd = g.argv[2];
492592
}
@@ -534,12 +634,11 @@
534634
if( !verboseFlag ){
535635
verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
536636
}
537637
verify_all_options();
538638
db_prepare(&q,
539
- "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
540
- " comment, datetime(ctime) FROM stash"
639
+ "SELECT stashid, hash, comment, datetime(ctime) FROM stash"
541640
" ORDER BY ctime"
542641
);
543642
if( verboseFlag ){
544643
db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
545644
" FROM stashfile WHERE stashid=$id");
@@ -628,11 +727,12 @@
628727
int nConflict;
629728
int vid;
630729
if( g.argc>4 ) usage("apply STASHID");
631730
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
632731
undo_begin();
633
- vid = db_int(0, "SELECT vid FROM stash WHERE stashid=%d", stashid);
732
+ vid = db_int(0, "SELECT blob.rid FROM stash,blob"
733
+ " WHERE stashid=%d AND blob.uuid=stash.hash", stashid);
634734
nConflict = update_to(vid);
635735
stash_apply(stashid, nConflict);
636736
db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
637737
"(SELECT origname FROM stashfile WHERE stashid=%d)",
638738
stashid);
639739
--- src/stash.c
+++ src/stash.c
@@ -21,32 +21,145 @@
21 #include <assert.h>
22
23
24 /*
25 ** SQL code to implement the tables needed by the stash.
 
 
 
 
 
 
 
 
 
 
 
 
 
26 */
27 static const char zStashInit[] =
28 @ CREATE TABLE IF NOT EXISTS localdb.stash(
29 @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier
30 @ vid INTEGER, -- The baseline checkout for this stash
 
31 @ comment TEXT, -- Comment for this stash. Or NULL
32 @ ctime TIMESTAMP -- When the stash was created
33 @ );
34 @ CREATE TABLE IF NOT EXISTS localdb.stashfile(
35 @ stashid INTEGER REFERENCES stash, -- Stash that contains this file
36 @ rid INTEGER, -- Baseline content in BLOB table or 0.
37 @ isAdded BOOLEAN, -- True if this is an added file
38 @ isRemoved BOOLEAN, -- True if this file is deleted
39 @ isExec BOOLEAN, -- True if file is executable
40 @ isLink BOOLEAN, -- True if file is a symlink
 
 
41 @ origname TEXT, -- Original filename
42 @ newname TEXT, -- New name for file at next check-in
43 @ delta BLOB, -- Delta from baseline. Content if rid=0
44 @ PRIMARY KEY(newname, stashid)
45 @ );
46 @ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1);
47 ;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
49 /*
50 ** Add zFName to the stash given by stashid. zFName might be the name of a
51 ** file or a directory. If a directory, add all changed files contained
52 ** within that directory.
@@ -77,13 +190,14 @@
77 );
78 }
79 db_prepare(&q, "%s", blob_sql_text(&sql));
80 blob_reset(&sql);
81 db_prepare(&ins,
82 "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
83 "origname, newname, delta)"
84 "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
 
85 stashid
86 );
87 while( db_step(&q)==SQLITE_ROW ){
88 int deleted = db_column_int(&q, 0);
89 int rid = db_column_int(&q, 3);
@@ -171,13 +285,13 @@
171 stashid = db_lget_int("stash-next", 1);
172 db_lset_int("stash-next", stashid+1);
173 vid = db_lget_int("checkout", 0);
174 vfile_check_signature(vid, 0);
175 db_multi_exec(
176 "INSERT INTO stash(stashid,vid,comment,ctime)"
177 "VALUES(%d,%d,%Q,julianday('now'))",
178 stashid, vid, zComment
179 );
180 if( g.argc>3 ){
181 int i;
182 for(i=3; i<g.argc; i++){
183 stash_add_file_or_dir(stashid, vid, g.argv[i]);
@@ -193,12 +307,12 @@
193 */
194 static void stash_apply(int stashid, int nConflict){
195 int vid;
196 Stmt q;
197 db_prepare(&q,
198 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
199 " FROM stashfile WHERE stashid=%d",
200 stashid
201 );
202 vid = db_lget_int("checkout",0);
203 db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
204 filename_collation());
@@ -296,12 +410,12 @@
296 ){
297 Stmt q;
298 Blob empty;
299 blob_zero(&empty);
300 db_prepare(&q,
301 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
302 " FROM stashfile WHERE stashid=%d",
303 stashid
304 );
305 while( db_step(&q)==SQLITE_ROW ){
306 int rid = db_column_int(&q, 0);
307 int isRemoved = db_column_int(&q, 1);
@@ -468,25 +582,11 @@
468 int rc;
469 undo_capture_command_line();
470 db_must_be_within_tree();
471 db_open_config(0, 0);
472 db_begin_transaction();
473 db_multi_exec(zStashInit /*works-like:""*/);
474 rc = db_exists("SELECT 1 FROM sqlite_master"
475 " WHERE name='stashfile'"
476 " AND sql GLOB '* PRIMARY KEY(origname, stashid)*'");
477 if( rc!=0 ){
478 db_multi_exec(
479 "CREATE TABLE localdb.stashfile_tmp AS SELECT * FROM stashfile;"
480 "DROP TABLE stashfile;"
481 );
482 db_multi_exec(zStashInit /*works-like:""*/);
483 db_multi_exec(
484 "INSERT INTO stashfile SELECT * FROM stashfile_tmp;"
485 "DROP TABLE stashfile_tmp;"
486 );
487 }
488 if( g.argc<=2 ){
489 zCmd = "save";
490 }else{
491 zCmd = g.argv[2];
492 }
@@ -534,12 +634,11 @@
534 if( !verboseFlag ){
535 verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
536 }
537 verify_all_options();
538 db_prepare(&q,
539 "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
540 " comment, datetime(ctime) FROM stash"
541 " ORDER BY ctime"
542 );
543 if( verboseFlag ){
544 db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
545 " FROM stashfile WHERE stashid=$id");
@@ -628,11 +727,12 @@
628 int nConflict;
629 int vid;
630 if( g.argc>4 ) usage("apply STASHID");
631 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
632 undo_begin();
633 vid = db_int(0, "SELECT vid FROM stash WHERE stashid=%d", stashid);
 
634 nConflict = update_to(vid);
635 stash_apply(stashid, nConflict);
636 db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
637 "(SELECT origname FROM stashfile WHERE stashid=%d)",
638 stashid);
639
--- src/stash.c
+++ src/stash.c
@@ -21,32 +21,145 @@
21 #include <assert.h>
22
23
24 /*
25 ** SQL code to implement the tables needed by the stash.
26 **
27 ** Historical schema changes:
28 **
29 ** 2019-01-19: stash.hash and stashfile.hash columns added. The
30 ** corresponding stash.vid and stashfile.rid columns are
31 ** retained for compatibility with older versions of
32 ** fossil but are no longer used.
33 **
34 ** 2016-10-16: Change the PRIMARY KEY on stashfile from (origname,stashid)
35 ** to (newname,stashid).
36 **
37 ** 2011-09-01: stashfile.isLink column added
38 **
39 */
40 static const char zStashInit[] =
41 @ CREATE TABLE IF NOT EXISTS localdb.stash(
42 @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier
43 @ vid INTEGER, -- Legacy baseline RID value. Do not use.
44 @ hash TEXT, -- The SHA hash for the baseline
45 @ comment TEXT, -- Comment for this stash. Or NULL
46 @ ctime TIMESTAMP -- When the stash was created
47 @ );
48 @ CREATE TABLE IF NOT EXISTS localdb.stashfile(
49 @ stashid INTEGER REFERENCES stash, -- Stash that contains this file
 
50 @ isAdded BOOLEAN, -- True if this is an added file
51 @ isRemoved BOOLEAN, -- True if this file is deleted
52 @ isExec BOOLEAN, -- True if file is executable
53 @ isLink BOOLEAN, -- True if file is a symlink
54 @ rid INTEGER, -- Legacy baseline RID value. Do not use
55 @ hash TEXT, -- Hash for baseline or NULL
56 @ origname TEXT, -- Original filename
57 @ newname TEXT, -- New name for file at next check-in
58 @ delta BLOB, -- Delta from baseline or raw content
59 @ PRIMARY KEY(newname, stashid)
60 @ );
61 @ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1);
62 ;
63
64 /*
65 ** Make sure the stash and stashfile tables exist and have been
66 ** upgraded to their latest format. Create and upgrade the tables
67 ** as necessary.
68 */
69 static void stash_tables_exist_and_current(void){
70 if( db_table_has_column("localdb","stashfile","hash") ){
71 /* The schema is up-to-date. But it could be that an older version
72 ** of Fossil that does no know about the stash.hash and stashfile.hash
73 ** columns has run since the schema was updated, and added entries that
74 ** have NULL hash columns. Check for this case, and fill in any missing
75 ** hash values.
76 */
77 if( db_int(0, "SELECT hash IS NULL FROM stash"
78 " ORDER BY stashid DESC LIMIT 1")
79 ){
80 db_multi_exec(
81 "UPDATE stash"
82 " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stash.vid)"
83 " WHERE hash IS NULL;"
84 "UPDATE stashfile"
85 " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stashfile.rid)"
86 " WHERE hash IS NULL AND rid>0;"
87 );
88 }
89 return;
90 }
91
92 if( !db_table_exists("localdb","stashfile")
93 || !db_table_exists("localdb","stash")
94 ){
95 /* Tables do not exist. Create them from scratch. */
96 db_multi_exec("DROP TABLE IF EXISTS localdb.stash;");
97 db_multi_exec("DROP TABLE IF EXISTS localdb.stashfile;");
98 db_multi_exec(zStashInit /*works-like:""*/);
99 return;
100 }
101
102 /* The tables exists but are not necessarily current. Upgrade them
103 ** to the latest format.
104 **
105 ** We can assume the 2011-09-01 format that includes the stashfile.isLink
106 ** column. The only upgrades we need to worry about the PRIMARY KEY
107 ** change on 2016-10-16 and the addition of the "hash" columns on
108 ** 2019-01-19.
109 */
110 db_multi_exec(
111 "ALTER TABLE localdb.stash RENAME TO old_stash;"
112 "ALTER TABLE localdb.stashfile RENAME TO old_stashfile;"
113 );
114 db_multi_exec(zStashInit /*works-like:""*/);
115 db_multi_exec(
116 "INSERT INTO localdb.stash(stashid,vid,hash,comment,ctime)"
117 " SELECT stashid, vid,"
118 " (SELECT uuid FROM blob WHERE blob.rid=old_stash.vid),"
119 " comment, ctime FROM old_stash;"
120 "DROP TABLE old_stash;"
121 );
122 db_multi_exec(
123 "INSERT INTO localdb.stashfile(stashid,isAdded,isRemoved,isExec,"
124 "isLink,rid,hash,origname,newname,delta)"
125 " SELECT stashid, isAdded, isRemoved, isExec, isLink, rid,"
126 " (SELECT uuid FROM blob WHERE blob.rid=old_stashfile.rid),"
127 " origname, newname, delta FROM old_stashfile;"
128 "DROP TABLE old_stashfile;"
129 );
130 }
131
132 /*
133 ** Update the stash.vid and stashfile.rid values after a RID renumbering
134 ** event.
135 */
136 void stash_rid_renumbering_event(void){
137 if( !db_table_has_column("localdb","stash","hash") ){
138 /* If the stash schema was the older style that lacked hash value, then
139 ** recovery is not possible. Save off the old data, then reset the stash
140 ** to empty. */
141 if( db_table_exists("localdb","stash") ){
142 db_multi_exec("ALTER TABLE stash RENAME TO broken_stash;");
143 fossil_print("Unrecoverable stash content stored in \"broken_stash\"\n");
144 }
145 if( db_table_exists("localdb","stashfile") ){
146 db_multi_exec("ALTER TABLE stashfile RENAME TO broken_stashfile;");
147 fossil_print("Unrecoverable stashfile content stored"
148 " in \"broken_stashfile\"\n");
149 }
150 }else{
151 /* Reset stash.vid and stash.rid values based on hashes */
152 db_multi_exec(
153 "UPDATE stash"
154 " SET vid=(SELECT rid FROM blob WHERE blob.uuid=stash.hash);"
155 "UPDATE stashfile"
156 " SET rid=(SELECT rid FROM blob WHERE blob.uuid=stashfile.hash)"
157 " WHERE hash IS NOT NULL;"
158 );
159 }
160 }
161
162 /*
163 ** Add zFName to the stash given by stashid. zFName might be the name of a
164 ** file or a directory. If a directory, add all changed files contained
165 ** within that directory.
@@ -77,13 +190,14 @@
190 );
191 }
192 db_prepare(&q, "%s", blob_sql_text(&sql));
193 blob_reset(&sql);
194 db_prepare(&ins,
195 "INSERT INTO stashfile(stashid, isAdded, isRemoved, isExec, isLink, rid, "
196 "hash, origname, newname, delta)"
197 "VALUES(%d,:isadd,:isrm,:isexe,:islink,:rid,"
198 "(SELECT uuid FROM blob WHERE rid=:rid),:orig,:new,:content)",
199 stashid
200 );
201 while( db_step(&q)==SQLITE_ROW ){
202 int deleted = db_column_int(&q, 0);
203 int rid = db_column_int(&q, 3);
@@ -171,13 +285,13 @@
285 stashid = db_lget_int("stash-next", 1);
286 db_lset_int("stash-next", stashid+1);
287 vid = db_lget_int("checkout", 0);
288 vfile_check_signature(vid, 0);
289 db_multi_exec(
290 "INSERT INTO stash(stashid,vid,hash,comment,ctime)"
291 "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d),%Q,julianday('now'))",
292 stashid, vid, vid, zComment
293 );
294 if( g.argc>3 ){
295 int i;
296 for(i=3; i<g.argc; i++){
297 stash_add_file_or_dir(stashid, vid, g.argv[i]);
@@ -193,12 +307,12 @@
307 */
308 static void stash_apply(int stashid, int nConflict){
309 int vid;
310 Stmt q;
311 db_prepare(&q,
312 "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
313 " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
314 stashid
315 );
316 vid = db_lget_int("checkout",0);
317 db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
318 filename_collation());
@@ -296,12 +410,12 @@
410 ){
411 Stmt q;
412 Blob empty;
413 blob_zero(&empty);
414 db_prepare(&q,
415 "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
416 " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
417 stashid
418 );
419 while( db_step(&q)==SQLITE_ROW ){
420 int rid = db_column_int(&q, 0);
421 int isRemoved = db_column_int(&q, 1);
@@ -468,25 +582,11 @@
582 int rc;
583 undo_capture_command_line();
584 db_must_be_within_tree();
585 db_open_config(0, 0);
586 db_begin_transaction();
587 stash_tables_exist_and_current();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588 if( g.argc<=2 ){
589 zCmd = "save";
590 }else{
591 zCmd = g.argv[2];
592 }
@@ -534,12 +634,11 @@
634 if( !verboseFlag ){
635 verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
636 }
637 verify_all_options();
638 db_prepare(&q,
639 "SELECT stashid, hash, comment, datetime(ctime) FROM stash"
 
640 " ORDER BY ctime"
641 );
642 if( verboseFlag ){
643 db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
644 " FROM stashfile WHERE stashid=$id");
@@ -628,11 +727,12 @@
727 int nConflict;
728 int vid;
729 if( g.argc>4 ) usage("apply STASHID");
730 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
731 undo_begin();
732 vid = db_int(0, "SELECT blob.rid FROM stash,blob"
733 " WHERE stashid=%d AND blob.uuid=stash.hash", stashid);
734 nConflict = update_to(vid);
735 stash_apply(stashid, nConflict);
736 db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
737 "(SELECT origname FROM stashfile WHERE stashid=%d)",
738 stashid);
739

Keyboard Shortcuts

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