Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | The revert command no longer resets the undo state if nothing is actually reverted. Additional tests and bug fixes as well. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | revert |
Files: | files | file ages | folders |
SHA1: |
418dd7ac51ba4e1547fb9b0ca55c2985 |
User & Date: | joel 2016-04-01 04:04:52.491 |
Context
2016-04-02
| ||
04:28 | Remove superfluous initialization of $passed. ... (Closed-Leaf check-in: e33ed0c5 user: joel tags: revert) | |
2016-04-01
| ||
04:04 | The revert command no longer resets the undo state if nothing is actually reverted. Additional tests and bug fixes as well. ... (check-in: 418dd7ac user: joel tags: revert) | |
2016-03-29
| ||
10:38 | Update the built-in SQLite to 3.12.0 final. ... (check-in: 996705f5 user: drh tags: trunk) | |
Changes
Changes to src/update.c.
1 2 3 4 5 6 7 | /* ** Copyright (c) 2007 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".) | > | 1 2 3 4 5 6 7 8 | /* ** Copyright (c) 2007 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".) |
︙ | ︙ | |||
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 | void revert_cmd(void){ const char *zFile; const char *zRevision; Blob record; int i; int errCode; Stmt q; undo_capture_command_line(); zRevision = find_option("revision", "r", 1); verify_all_options(); if( g.argc<2 ){ usage("?OPTIONS? [FILE] ..."); } if( zRevision && g.argc<3 ){ fossil_fatal("the --revision option does not work for the entire tree"); } db_must_be_within_tree(); db_begin_transaction(); undo_begin(); db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); if( g.argc>2 ){ for(i=2; i<g.argc; i++){ Blob fname; zFile = mprintf("%/", g.argv[i]); blob_zero(&fname); file_tree_name(zFile, &fname, 0, 1); db_multi_exec( | > > > > > > > > > < > > > > > > | > > | > > > > > > > > > > > > > > > > > > < | < > > > > > > > > > > > > > > > > > > > > > > | | | > > | < < < < < < < | | > | 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 | void revert_cmd(void){ const char *zFile; const char *zRevision; Blob record; int i; int errCode; Stmt q; int vid; int mergeReverted = 0; undo_capture_command_line(); zRevision = find_option("revision", "r", 1); verify_all_options(); if( g.argc<2 ){ usage("?OPTIONS? [FILE] ..."); } if( zRevision && g.argc<3 ){ fossil_fatal("the --revision option does not work for the entire tree"); } db_must_be_within_tree(); db_begin_transaction(); undo_begin(); db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); if( g.argc>2 ){ int rid = 0; if( zRevision ){ rid = name_to_typed_rid(zRevision, "ci"); load_vfile_from_rid(rid); } for(i=2; i<g.argc; i++){ Blob fname; zFile = mprintf("%/", g.argv[i]); blob_zero(&fname); file_tree_name(zFile, &fname, 0, 1); db_multi_exec( "INSERT OR IGNORE INTO torevert" " SELECT pathname" " FROM vfile v" " WHERE vid=%d AND (pathname %s=%B OR origname %s=%B)" " AND (chnged OR deleted OR rid=0 OR pathname!=origname OR rid!=(" " SELECT rid FROM vfile WHERE vid=%d AND pathname=v.pathname));" "INSERT OR IGNORE INTO torevert" " SELECT origname" " FROM vfile" " WHERE vid=%d AND origname %s=%B" " AND pathname IN (SELECT name FROM torevert);", vid, filename_collation(), &fname, filename_collation(), &fname, rid, vid, filename_collation(), &fname ); free(zFile); blob_reset(&fname); } if( rid ) db_multi_exec("DELETE FROM vfile WHERE vid=%d", rid); /* Refuse to revert a renamed file if another file exists in its ** original location that wasn't specified when calling revert. */ db_prepare(&q, " SELECT origname, pathname" " FROM vfile" " WHERE pathname IN (SELECT name FROM torevert)" " AND origname NOT IN (SELECT name FROM torevert)" " AND origname IN (SELECT pathname FROM vfile)" ); if( db_step(&q)==SQLITE_ROW ){ const char *zOrig = db_column_text(&q, 0); zFile = db_column_text(&q, 1); fossil_fatal("cannot revert '%s' without '%s'", zFile, zOrig); } db_finalize(&q); }else{ mergeReverted = db_exists("SELECT 1 FROM vmerge"); db_multi_exec( "DELETE FROM vmerge;" "INSERT OR IGNORE INTO torevert " " SELECT pathname" " FROM vfile " " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" ); } /* If there's nothing to revert, rollback and exit so the previous undo ** state isn't unnecessarily discarded. */ if( !mergeReverted && !db_exists("SELECT 1 FROM torevert") ){ db_end_transaction(1); return; } db_multi_exec( "INSERT OR IGNORE INTO torevert" " SELECT origname" " FROM vfile" " WHERE origname!=pathname AND pathname IN (SELECT name FROM torevert);" ); /* Revert pathnames. Added files (rid==0) that are to be reverted will ** have a NULL pathname until their VFILE entries are deleted. */ db_multi_exec( "UPDATE vfile" " SET pathname=NULL, origname=coalesce(origname,pathname)" " WHERE pathname IN (SELECT name FROM torevert);" "UPDATE vfile" " SET pathname=origname, origname=NULL" " WHERE pathname IS NULL AND rid>0;" ); blob_zero(&record); db_prepare(&q, "SELECT name FROM torevert ORDER BY name"); if( zRevision==0 ){ int vid = db_lget_int("checkout", 0); zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); } while( db_step(&q)==SQLITE_ROW ){ int isExe = 0; int isLink = 0; char *zFull; zFile = db_column_text(&q, 0); zFull = mprintf("%/%/", g.zLocalRoot, zFile); errCode = historical_version_of_file(zRevision, zFile, &record, &isLink, &isExe, 0, 2); if( errCode==2 || !db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zFile) ){ if( db_exists("SELECT 1 FROM vfile" " WHERE pathname IS NULL AND origname=%Q", zFile) ){ fossil_print("UNMANAGE %s\n", zFile); }else{ undo_save(zFile); file_delete(zFull); fossil_print("DELETE %s\n", zFile); } }else{ sqlite3_int64 mtime; undo_save(zFile); if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(0)) ){ file_delete(zFull); } if( isLink ){ symlink_create(blob_str(&record), zFull); }else{ blob_write_to_file(&record, zFull); } file_wd_setexe(zFull, isExe); fossil_print("REVERT %s\n", zFile); mtime = file_wd_mtime(zFull); db_multi_exec( "UPDATE vfile" " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d,mrid=rid" " WHERE pathname=%Q", mtime, isExe, isLink, zFile ); } blob_reset(&record); free(zFull); } db_finalize(&q); db_multi_exec("DELETE FROM vfile WHERE pathname IS NULL"); undo_finish(); db_end_transaction(0); } |
Changes to test/mv-rm.test.
︙ | ︙ | |||
61 62 63 64 65 66 67 | cd [file join $rootDir subdir1] fossil mv ../f1 . test mv-soft-relative-1 {$RESULT eq "RENAME f1 subdir1/f1"} fossil revert test mv-soft-relative-2 { | | | | | | | | | | 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 | cd [file join $rootDir subdir1] fossil mv ../f1 . test mv-soft-relative-1 {$RESULT eq "RENAME f1 subdir1/f1"} fossil revert test mv-soft-relative-2 { [normalize_result] eq "REVERT f1\nDELETE subdir1/f1${undoMsg}" } cd $rootDir ################################### # Test 2: Soft Move Relative File # ################################### file mkdir [file join $rootDir subdir2] cd [file join $rootDir subdir2] fossil mv ../f2 ./f2 test mv-soft-relative-3 {$RESULT eq "RENAME f2 subdir2/f2"} fossil revert test mv-soft-relative-4 { [normalize_result] eq "REVERT f2\nDELETE subdir2/f2${undoMsg}" } cd $rootDir ######################################## # Test 3: Hard Move Relative Directory # ######################################## file mkdir [file join $rootDir subdir3] cd [file join $rootDir subdir3] fossil mv --hard ../f3 . test mv-hard-relative-1 { [normalize_result] eq "RENAME f3 subdir3/f3\nMOVED_FILE ${rootDir}/f3" } fossil revert test mv-hard-relative-2 { [normalize_result] eq "REVERT f3\nDELETE subdir3/f3${undoMsg}" } cd $rootDir ################################### # Test 4: Hard Move Relative File # ################################### file mkdir [file join $rootDir subdir4] cd [file join $rootDir subdir4] fossil mv --hard ../f4 ./f4 test mv-hard-relative-3 { [normalize_result] eq "RENAME f4 subdir4/f4\nMOVED_FILE ${rootDir}/f4" } fossil revert test mv-hard-relative-4 { [normalize_result] eq "REVERT f4\nDELETE subdir4/f4${undoMsg}" } cd $rootDir ######################################## # Test 5: Soft Move Absolute Directory # ######################################## file mkdir [file join $rootDir subdir5] cd [file join $rootDir subdir5] fossil mv [file join $rootDir f5] [file join $rootDir subdir5] test mv-soft-absolute-1 {$RESULT eq "RENAME f5 subdir5/f5"} fossil revert test mv-soft-absolute-2 { [normalize_result] eq "REVERT f5\nDELETE subdir5/f5${undoMsg}" } cd $rootDir ################################### # Test 6: Soft Move Absolute File # ################################### file mkdir [file join $rootDir subdir6] cd [file join $rootDir subdir6] fossil mv [file join $rootDir f6] [file join $rootDir subdir6 f6] test mv-soft-absolute-3 {$RESULT eq "RENAME f6 subdir6/f6"} fossil revert test mv-soft-absolute-4 { [normalize_result] eq "REVERT f6\nDELETE subdir6/f6${undoMsg}" } cd $rootDir ######################################## # Test 7: Hard Move Absolute Directory # ######################################## file mkdir [file join $rootDir subdir7] cd [file join $rootDir subdir7] fossil mv --hard [file join $rootDir f7] [file join $rootDir subdir7] test mv-hard-absolute-1 { [normalize_result] eq "RENAME f7 subdir7/f7\nMOVED_FILE ${rootDir}/f7" } fossil revert test mv-hard-absolute-2 { [normalize_result] eq "REVERT f7\nDELETE subdir7/f7${undoMsg}" } cd $rootDir ################################### # Test 8: Hard Move Absolute File # ################################### file mkdir [file join $rootDir subdir8] cd [file join $rootDir subdir8] fossil mv --hard [file join $rootDir f8] [file join $rootDir subdir8 f8] test mv-hard-absolute-3 { [normalize_result] eq "RENAME f8 subdir8/f8\nMOVED_FILE ${rootDir}/f8" } fossil revert test mv-hard-absolute-4 { [normalize_result] eq "REVERT f8\nDELETE subdir8/f8${undoMsg}" } cd $rootDir ########################################## # Test 9: Soft Remove Relative Directory # ########################################## |
︙ | ︙ |
Changes to test/revert.test.
︙ | ︙ | |||
21 22 23 24 25 26 27 | # # Test 'fossil revert' against expected results from 'fossil changes' and # 'fossil addremove -n', as well as by verifying the existence of files # on the file system. 'fossil undo' is called after each test # proc revert-test {testid revertArgs expectedRevertOutput args} { | < > > > > > > > > > > > | | > > > | 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 | # # Test 'fossil revert' against expected results from 'fossil changes' and # 'fossil addremove -n', as well as by verifying the existence of files # on the file system. 'fossil undo' is called after each test # proc revert-test {testid revertArgs expectedRevertOutput args} { set passed 1 set args [dict merge { -changes {} -addremove {} -exists {} -notexists {} } $args] set result [fossil revert {*}$revertArgs] test_status_list revert-$testid $result $expectedRevertOutput set statusListTests [list -changes changes -addremove {addremove -n}] foreach {key fossilArgs} $statusListTests { set expected [dict get $args $key] set result [fossil {*}$fossilArgs] test_status_list revert-$testid$key $result $expected } set fileExistsTests [list -exists 1 does -notexists 0 should] foreach {key expected verb} $fileExistsTests { set passed 1 foreach path [dict get $args $key] { if {[file exists $path] != $expected} { set passed 0 protOut " Failure: File $verb not exist: $path" } } test revert-$testid$key $passed } fossil undo } # Test that a 'fossil revert' call is a no-op. proc noop-revert-test {testid revertArgs} { set changes [fossil changes] set undo [fossil undo -n] test revert-$testid {[fossil revert {*}$revertArgs] eq ""} test revert-$testid-changes {[fossil changes] eq $changes} test revert-$testid-undo {[fossil undo -n] eq $undo} } require_no_open_checkout test_setup # Prepare first commit # write_file f1 "f1" write_file f2 "f2" write_file f3 "f3" write_file f4 "f4" fossil add f1 f2 f3 f4 fossil commit -m "c1" --tag c1 write_file f4 "f4.1" fossil commit -m "c2" --tag c2 # Make changes to be reverted # # Add f0 write_file f0 "f0" fossil add f0 # Remove f1 |
︙ | ︙ | |||
87 88 89 90 91 92 93 | UNMANAGE f0 REVERT f1 REVERT f2 REVERT f3 DELETE f3n } -addremove { ADDED f0 | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | UNMANAGE f0 REVERT f1 REVERT f2 REVERT f3 DELETE f3n } -addremove { ADDED f0 } -exists {f0 f1 f2 f3 f4} -notexists f3n # Test with a single filename argument # revert-test 1-2 f0 { UNMANAGE f0 } -changes { DELETED f1 EDITED f2 RENAMED f3n } -addremove { ADDED f0 } -exists {f0 f2 f3n f4} -notexists {f1 f3} revert-test 1-3 f1 { REVERT f1 } -changes { ADDED f0 EDITED f2 RENAMED f3n } -exists {f0 f1 f2 f3n f4} -notexists f3 revert-test 1-4 f2 { REVERT f2 } -changes { ADDED f0 DELETED f1 RENAMED f3n } -exists {f0 f2 f3n f4} -notexists {f1 f3} # Both files involved in a rename are reverted regardless of which filename # is used as an argument to 'fossil revert' # revert-test 1-5 f3 { REVERT f3 DELETE f3n } -changes { ADDED f0 DELETED f1 EDITED f2 } -exists {f0 f2 f3 f4} -notexists {f1 f3n} revert-test 1-6 f3n { REVERT f3 DELETE f3n } -changes { ADDED f0 DELETED f1 EDITED f2 } -exists {f0 f2 f3 f4} -notexists {f1 f3n} # Test with multiple filename arguments # revert-test 1-7 {f0 f2 f3n} { UNMANAGE f0 REVERT f2 REVERT f3 DELETE f3n } -changes { DELETED f1 } -addremove { ADDED f0 } -exists {f0 f2 f3 f4} -notexists {f1 f3n} # Test with a revision specified # revert-test 1-8 {-r c1 f4} { REVERT f4 } -changes { ADDED f0 DELETED f1 EDITED f2 RENAMED f3n EDITED f4 } -exists {f0 f2 f3n f4} -notexists {f1 f3} # Revision specified, no-op revert # noop-revert-test 1-9 {-r c2 f4 z1} # Invalid revision # fossil revert -r turnk f4 test revert-1-10 {$RESULT eq "not found: turnk"} # Test case-sensitivity # fossil set case-sensitive 0 revert-test 1-11 F3 { REVERT f3 DELETE f3n } -changes { ADDED f0 DELETED f1 EDITED f2 } -exists {f0 f2 f3 f4} -notexists {f1 f3n} fossil set case-sensitive 1 noop-revert-test 1-12 {F1 f4} fossil unset case-sensitive # No-op revert, no files or revision specified # fossil revert fossil clean -f noop-revert-test 1-13 {} # Reverting a no-op merge is not a no-op revert # fossil merge -f c1 revert-test 1-14 {} {} # Test reverting the combination of a renamed file and an added file that # uses the renamed file's original filename. # test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" write_file f1n "f1n" fossil mv f1 f1n write_file f1 "f1b" fossil add f1 foreach {testnum args} {1 {} 2 f1 3 {f1 f1n}} { revert-test 2-$testnum $args { REVERT f1 DELETE f1n } -exists {f1} -notexists {f1n} } fossil revert f1n test revert-2-2 {$RESULT eq "cannot revert 'f1n' without 'f1'"} # Test reverting a rename in the repo but not completed in the file # system test_setup write_file f1 "f1" fossil add f1 fossil commit -m "add f1" fossil mv --soft f1 f1new test 3-mv-1 {[file exists f1]} test 3-mv-2 {![file exists f1new]} revert-test 3-1 {} { REVERT f1 DELETE f1new } -exists {f1} -notexists {f1n} # Test chained renames # test_setup write_file f1 "f1" write_file f2 "f2" write_file f3 "f3" fossil add f1 f2 f3 fossil commit -m "add files" fossil mv --hard f1 f0 fossil mv --hard f2 f1 fossil mv --hard f3 f2 revert-test 4-1 f2 { DELETE f1 REVERT f2 REVERT f3 } -changes { RENAMED f0 } -exists {f0 f2 f3} -notexists {f1} fossil revert f1 test revert-4-2 {$RESULT eq "cannot revert 'f1' without 'f2'"} ############################################################################### test_cleanup |