Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | 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. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
ed06585f41ee2acef54b03da90445278 |
User & Date: | drh 2019-01-19 18:29:47 |
References
2021-10-21
| ||
08:42 | stash patch from forum post 5e6c3e3b78112df3 which updates it to handle the NULL stashfile.hash case introduced in [ed06585f41]. This resolves the 11 failing stash tests. ... (check-in: 4531bcd4 user: stephan tags: trunk) | |
Context
2019-01-19
| ||
21:31 | Change the schema of the vmerge table so that it remembers the hash of merged artifacts, not the RID. Schema updates are automatic, but once the update occurs, you are committed to using the newer version of Fossil moving forward. ... (check-in: 8977dfb0 user: drh tags: rid-renumbering) | |
21:03 | Remove unused variable ... (check-in: 302ce439 user: andygoth tags: trunk) | |
18:29 | 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. ... (check-in: ed06585f user: drh tags: trunk) | |
2019-01-17
| ||
22:43 | Added a warning about loss of stash and undo when performing the 'interim workaround' for an RID mismatch. ... (check-in: cc44fa84 user: stephan tags: trunk) | |
Changes
Changes to src/db.c.
︙ | ︙ | |||
1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 | /* If the checkout database was opened first, then check to make ** sure that the repository database that was just opened has not ** be replaced by a clone of the same project, with different RID ** values. */ if( g.localOpen && !db_fingerprint_ok() ){ fossil_print( "Oops. It looks like the repository database file located at\n" " \"%s\"\n", zDbName ); fossil_print( "has been swapped with a clone that may have different\n" "integer keys for the various artifacts. As of 2019-01-11,\n" "we are working on enhancing Fossil to be able to deal with\n" "that automatically, but we are not there yet. Sorry.\n\n" ); fossil_print( "As an interim workaround, try:\n" " %s close --force\n" " %s open \"%s\" --keep\n" "Noting that any STASH and UNDO information " "WILL BE IRREVOCABLY LOST.\n\n", g.argv[0], g.argv[0], zDbName ); fossil_fatal("bad fingerprint"); } } /* ** Return true if there have been any changes to the repository ** database since it was opened. ** | > > > > > | 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 | /* If the checkout database was opened first, then check to make ** sure that the repository database that was just opened has not ** be replaced by a clone of the same project, with different RID ** values. */ if( g.localOpen && !db_fingerprint_ok() ){ /* Uncomment the following when we are ready for automatic recovery: */ #if 0 stash_rid_renumbering_event(); #else fossil_print( "Oops. It looks like the repository database file located at\n" " \"%s\"\n", zDbName ); fossil_print( "has been swapped with a clone that may have different\n" "integer keys for the various artifacts. As of 2019-01-11,\n" "we are working on enhancing Fossil to be able to deal with\n" "that automatically, but we are not there yet. Sorry.\n\n" ); fossil_print( "As an interim workaround, try:\n" " %s close --force\n" " %s open \"%s\" --keep\n" "Noting that any STASH and UNDO information " "WILL BE IRREVOCABLY LOST.\n\n", g.argv[0], g.argv[0], zDbName ); fossil_fatal("bad fingerprint"); #endif } } /* ** Return true if there have been any changes to the repository ** database since it was opened. ** |
︙ | ︙ |
Changes to src/stash.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 | #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 localdb.stash( @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier | > > > > > > > > > > > > > | > < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include "config.h" #include "stash.h" #include <assert.h> /* ** SQL code to implement the tables needed by the stash. ** ** Historical schema changes: ** ** 2019-01-19: stash.hash and stashfile.hash columns added. The ** corresponding stash.vid and stashfile.rid columns are ** retained for compatibility with older versions of ** fossil but are no longer used. ** ** 2016-10-16: Change the PRIMARY KEY on stashfile from (origname,stashid) ** to (newname,stashid). ** ** 2011-09-01: stashfile.isLink column added ** */ static const char zStashInit[] = @ CREATE TABLE IF NOT EXISTS localdb.stash( @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier @ vid INTEGER, -- Legacy baseline RID value. Do not use. @ hash TEXT, -- The SHA hash for the baseline @ comment TEXT, -- Comment for this stash. Or NULL @ ctime TIMESTAMP -- When the stash was created @ ); @ CREATE TABLE IF NOT EXISTS localdb.stashfile( @ stashid INTEGER REFERENCES stash, -- Stash that contains this file @ isAdded BOOLEAN, -- True if this is an added file @ isRemoved BOOLEAN, -- True if this file is deleted @ isExec BOOLEAN, -- True if file is executable @ isLink BOOLEAN, -- True if file is a symlink @ rid INTEGER, -- Legacy baseline RID value. Do not use @ hash TEXT, -- Hash for baseline or NULL @ origname TEXT, -- Original filename @ newname TEXT, -- New name for file at next check-in @ delta BLOB, -- Delta from baseline or raw content @ PRIMARY KEY(newname, stashid) @ ); @ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1); ; /* ** Make sure the stash and stashfile tables exist and have been ** upgraded to their latest format. Create and upgrade the tables ** as necessary. */ static void stash_tables_exist_and_current(void){ if( db_table_has_column("localdb","stashfile","hash") ){ /* The schema is up-to-date. But it could be that an older version ** of Fossil that does no know about the stash.hash and stashfile.hash ** columns has run since the schema was updated, and added entries that ** have NULL hash columns. Check for this case, and fill in any missing ** hash values. */ if( db_int(0, "SELECT hash IS NULL FROM stash" " ORDER BY stashid DESC LIMIT 1") ){ db_multi_exec( "UPDATE stash" " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stash.vid)" " WHERE hash IS NULL;" "UPDATE stashfile" " SET hash=(SELECT uuid FROM blob WHERE blob.rid=stashfile.rid)" " WHERE hash IS NULL AND rid>0;" ); } return; } if( !db_table_exists("localdb","stashfile") || !db_table_exists("localdb","stash") ){ /* Tables do not exist. Create them from scratch. */ db_multi_exec("DROP TABLE IF EXISTS localdb.stash;"); db_multi_exec("DROP TABLE IF EXISTS localdb.stashfile;"); db_multi_exec(zStashInit /*works-like:""*/); return; } /* The tables exists but are not necessarily current. Upgrade them ** to the latest format. ** ** We can assume the 2011-09-01 format that includes the stashfile.isLink ** column. The only upgrades we need to worry about the PRIMARY KEY ** change on 2016-10-16 and the addition of the "hash" columns on ** 2019-01-19. */ db_multi_exec( "ALTER TABLE localdb.stash RENAME TO old_stash;" "ALTER TABLE localdb.stashfile RENAME TO old_stashfile;" ); db_multi_exec(zStashInit /*works-like:""*/); db_multi_exec( "INSERT INTO localdb.stash(stashid,vid,hash,comment,ctime)" " SELECT stashid, vid," " (SELECT uuid FROM blob WHERE blob.rid=old_stash.vid)," " comment, ctime FROM old_stash;" "DROP TABLE old_stash;" ); db_multi_exec( "INSERT INTO localdb.stashfile(stashid,isAdded,isRemoved,isExec," "isLink,rid,hash,origname,newname,delta)" " SELECT stashid, isAdded, isRemoved, isExec, isLink, rid," " (SELECT uuid FROM blob WHERE blob.rid=old_stashfile.rid)," " origname, newname, delta FROM old_stashfile;" "DROP TABLE old_stashfile;" ); } /* ** Update the stash.vid and stashfile.rid values after a RID renumbering ** event. */ void stash_rid_renumbering_event(void){ if( !db_table_has_column("localdb","stash","hash") ){ /* If the stash schema was the older style that lacked hash value, then ** recovery is not possible. Save off the old data, then reset the stash ** to empty. */ if( db_table_exists("localdb","stash") ){ db_multi_exec("ALTER TABLE stash RENAME TO broken_stash;"); fossil_print("Unrecoverable stash content stored in \"broken_stash\"\n"); } if( db_table_exists("localdb","stashfile") ){ db_multi_exec("ALTER TABLE stashfile RENAME TO broken_stashfile;"); fossil_print("Unrecoverable stashfile content stored" " in \"broken_stashfile\"\n"); } }else{ /* Reset stash.vid and stash.rid values based on hashes */ db_multi_exec( "UPDATE stash" " SET vid=(SELECT rid FROM blob WHERE blob.uuid=stash.hash);" "UPDATE stashfile" " SET rid=(SELECT rid FROM blob WHERE blob.uuid=stashfile.hash)" " WHERE hash IS NOT NULL;" ); } } /* ** 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){ |
︙ | ︙ | |||
75 76 77 78 79 80 81 | " OR pathname=%Q OR origname=%Q)", zTreename, zTreename, zTreename, zTreename ); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); db_prepare(&ins, | | | | > | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | " OR pathname=%Q OR origname=%Q)", zTreename, zTreename, zTreename, zTreename ); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); db_prepare(&ins, "INSERT INTO stashfile(stashid, isAdded, isRemoved, isExec, isLink, rid, " "hash, origname, newname, delta)" "VALUES(%d,:isadd,:isrm,:isexe,:islink,:rid," "(SELECT uuid FROM blob WHERE rid=:rid),:orig,:new,:content)", stashid ); while( db_step(&q)==SQLITE_ROW ){ int deleted = db_column_int(&q, 0); int rid = db_column_int(&q, 3); const char *zName = db_column_text(&q, 4); const char *zOrig = db_column_text(&q, 5); |
︙ | ︙ | |||
169 170 171 172 173 174 175 | zComment = blob_str(&comment); } stashid = db_lget_int("stash-next", 1); db_lset_int("stash-next", stashid+1); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); db_multi_exec( | | | | | | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | zComment = blob_str(&comment); } stashid = db_lget_int("stash-next", 1); db_lset_int("stash-next", stashid+1); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); db_multi_exec( "INSERT INTO stash(stashid,vid,hash,comment,ctime)" "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d),%Q,julianday('now'))", stashid, vid, 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, g.zLocalRoot); } return stashid; } /* ** Apply a stash to the current checkout. */ static void stash_apply(int stashid, int nConflict){ int vid; Stmt q; db_prepare(&q, "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash", stashid ); vid = db_lget_int("checkout",0); db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)", filename_collation()); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); |
︙ | ︙ | |||
294 295 296 297 298 299 300 | int fIncludeBinary, /* Do diffs against binary files */ u64 diffFlags /* Other diff flags */ ){ Stmt q; Blob empty; blob_zero(&empty); db_prepare(&q, | | | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | int fIncludeBinary, /* Do diffs against binary files */ u64 diffFlags /* Other diff flags */ ){ Stmt q; Blob empty; blob_zero(&empty); db_prepare(&q, "SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash", stashid ); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); int isLink = db_column_int(&q, 3); int isBin1, isBin2; |
︙ | ︙ | |||
466 467 468 469 470 471 472 | int nCmd; int stashid = 0; int rc; undo_capture_command_line(); db_must_be_within_tree(); db_open_config(0, 0); db_begin_transaction(); | < < | < < < < < < < < < < < < | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | int nCmd; int stashid = 0; int rc; undo_capture_command_line(); db_must_be_within_tree(); db_open_config(0, 0); db_begin_transaction(); stash_tables_exist_and_current(); if( g.argc<=2 ){ zCmd = "save"; }else{ zCmd = g.argv[2]; } nCmd = strlen(zCmd); if( memcmp(zCmd, "save", nCmd)==0 ){ |
︙ | ︙ | |||
532 533 534 535 536 537 538 | width = -1; } if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } verify_all_options(); db_prepare(&q, | < | | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | width = -1; } if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } verify_all_options(); db_prepare(&q, "SELECT stashid, hash, comment, datetime(ctime) FROM stash" " ORDER BY ctime" ); if( verboseFlag ){ db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname" " FROM stashfile WHERE stashid=$id"); } while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
626 627 628 629 630 631 632 | }else if( memcmp(zCmd, "goto", nCmd)==0 ){ int nConflict; int vid; if( g.argc>4 ) usage("apply STASHID"); stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); undo_begin(); | | > | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 | }else if( memcmp(zCmd, "goto", nCmd)==0 ){ int nConflict; int vid; if( g.argc>4 ) usage("apply STASHID"); stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); undo_begin(); vid = db_int(0, "SELECT blob.rid FROM stash,blob" " WHERE stashid=%d AND blob.uuid=stash.hash", stashid); nConflict = update_to(vid); stash_apply(stashid, nConflict); db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN " "(SELECT origname FROM stashfile WHERE stashid=%d)", stashid); undo_finish(); }else |
︙ | ︙ |