Fossil SCM
Sync w/trunk.
Commit
79be1156a9f4dbfcc1ba23572adfe5a16dee04ecda8943aa2607f61a00918cc1
Parent
6e1cee191de9f8b…
14 files changed
+5
+28
-11
-2
+1
-28
+19
+19
+25
-4
+4
-7
+4
-7
+62
+1
+5
+26
+2
-8
| --- skins/darkmode/css.txt | ||
| +++ skins/darkmode/css.txt | ||
| @@ -548,5 +548,10 @@ | ||
| 548 | 548 | } |
| 549 | 549 | |
| 550 | 550 | pre.udiff { |
| 551 | 551 | overflow-x: auto; |
| 552 | 552 | } |
| 553 | + | |
| 554 | +body.report table.report tr td { color: black } | |
| 555 | +body.report table.report a { color: blue } | |
| 556 | +body.tkt td.tktDspValue { color: black } | |
| 557 | +body.tkt td.tktDspValue a { color: blue } | |
| 553 | 558 |
| --- skins/darkmode/css.txt | |
| +++ skins/darkmode/css.txt | |
| @@ -548,5 +548,10 @@ | |
| 548 | } |
| 549 | |
| 550 | pre.udiff { |
| 551 | overflow-x: auto; |
| 552 | } |
| 553 |
| --- skins/darkmode/css.txt | |
| +++ skins/darkmode/css.txt | |
| @@ -548,5 +548,10 @@ | |
| 548 | } |
| 549 | |
| 550 | pre.udiff { |
| 551 | overflow-x: auto; |
| 552 | } |
| 553 | |
| 554 | body.report table.report tr td { color: black } |
| 555 | body.report table.report a { color: blue } |
| 556 | body.tkt td.tktDspValue { color: black } |
| 557 | body.tkt td.tktDspValue a { color: blue } |
| 558 |
+28
-11
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -439,21 +439,10 @@ | ||
| 439 | 439 | Blob fullName = empty_blob; |
| 440 | 440 | |
| 441 | 441 | /* file_tree_name() throws a fatal error if g.argv[i] is outside of the |
| 442 | 442 | ** checkout. */ |
| 443 | 443 | file_tree_name(g.argv[i], &fullName, 0, 1); |
| 444 | - if(0==allowReservedFlag | |
| 445 | - && 0!=file_is_win_reserved(blob_str(&fullName))){ | |
| 446 | - /* Note that the 'add' internal machinery already _silently_ | |
| 447 | - ** skips over any names for which file_is_reserved_name() | |
| 448 | - ** returns true or which is in the fossil_reserved_name() | |
| 449 | - ** list. We do not need to warn for those, as they're outright | |
| 450 | - ** verboten. */ | |
| 451 | - fossil_fatal("Filename is reserved: %b\n" | |
| 452 | - "Use --allow-reserved to permit " | |
| 453 | - "reserved filenames.", &fullName); | |
| 454 | - } | |
| 455 | 444 | blob_reset(&fullName); |
| 456 | 445 | file_canonical_name(g.argv[i], &fullName, 0); |
| 457 | 446 | zName = blob_str(&fullName); |
| 458 | 447 | isDir = file_isdir(zName, RepoFILE); |
| 459 | 448 | if( isDir==1 ){ |
| @@ -486,10 +475,38 @@ | ||
| 486 | 475 | blob_reset(&fullName); |
| 487 | 476 | } |
| 488 | 477 | glob_free(pIgnore); |
| 489 | 478 | glob_free(pClean); |
| 490 | 479 | |
| 480 | + /** Check for Windows-reserved names and warn or exit, as | |
| 481 | + ** appopriate. Note that the 'add' internal machinery already | |
| 482 | + ** _silently_ skips over any names for which | |
| 483 | + ** file_is_reserved_name() returns true or which is in the | |
| 484 | + ** fossil_reserved_name() list. We do not need to warn for those, | |
| 485 | + ** as they're outright verboten. */ | |
| 486 | + if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){ | |
| 487 | + Stmt q = empty_Stmt; | |
| 488 | + db_prepare(&q,"SELECT pathname FROM sfile " | |
| 489 | + "WHERE win_reserved(pathname)"); | |
| 490 | + int reservedCount = 0; | |
| 491 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 492 | + const char * zName = db_column_text(&q, 0); | |
| 493 | + ++reservedCount; | |
| 494 | + if(allowReservedFlag){ | |
| 495 | + fossil_warning("WARNING: Windows-reserved " | |
| 496 | + "filename: %s", zName); | |
| 497 | + }else{ | |
| 498 | + fossil_warning("ERROR: Windows-reserved filename: %s", zName); | |
| 499 | + } | |
| 500 | + } | |
| 501 | + db_finalize(&q); | |
| 502 | + if(allowReservedFlag==0){ | |
| 503 | + fossil_fatal("ERROR: %d Windows-reserved filename(s) added. " | |
| 504 | + "Use --allow-reserved to permit such names.", | |
| 505 | + reservedCount); | |
| 506 | + } | |
| 507 | + } | |
| 491 | 508 | add_files_in_sfile(vid); |
| 492 | 509 | db_end_transaction(0); |
| 493 | 510 | } |
| 494 | 511 | |
| 495 | 512 | /* |
| 496 | 513 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -439,21 +439,10 @@ | |
| 439 | Blob fullName = empty_blob; |
| 440 | |
| 441 | /* file_tree_name() throws a fatal error if g.argv[i] is outside of the |
| 442 | ** checkout. */ |
| 443 | file_tree_name(g.argv[i], &fullName, 0, 1); |
| 444 | if(0==allowReservedFlag |
| 445 | && 0!=file_is_win_reserved(blob_str(&fullName))){ |
| 446 | /* Note that the 'add' internal machinery already _silently_ |
| 447 | ** skips over any names for which file_is_reserved_name() |
| 448 | ** returns true or which is in the fossil_reserved_name() |
| 449 | ** list. We do not need to warn for those, as they're outright |
| 450 | ** verboten. */ |
| 451 | fossil_fatal("Filename is reserved: %b\n" |
| 452 | "Use --allow-reserved to permit " |
| 453 | "reserved filenames.", &fullName); |
| 454 | } |
| 455 | blob_reset(&fullName); |
| 456 | file_canonical_name(g.argv[i], &fullName, 0); |
| 457 | zName = blob_str(&fullName); |
| 458 | isDir = file_isdir(zName, RepoFILE); |
| 459 | if( isDir==1 ){ |
| @@ -486,10 +475,38 @@ | |
| 486 | blob_reset(&fullName); |
| 487 | } |
| 488 | glob_free(pIgnore); |
| 489 | glob_free(pClean); |
| 490 | |
| 491 | add_files_in_sfile(vid); |
| 492 | db_end_transaction(0); |
| 493 | } |
| 494 | |
| 495 | /* |
| 496 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -439,21 +439,10 @@ | |
| 439 | Blob fullName = empty_blob; |
| 440 | |
| 441 | /* file_tree_name() throws a fatal error if g.argv[i] is outside of the |
| 442 | ** checkout. */ |
| 443 | file_tree_name(g.argv[i], &fullName, 0, 1); |
| 444 | blob_reset(&fullName); |
| 445 | file_canonical_name(g.argv[i], &fullName, 0); |
| 446 | zName = blob_str(&fullName); |
| 447 | isDir = file_isdir(zName, RepoFILE); |
| 448 | if( isDir==1 ){ |
| @@ -486,10 +475,38 @@ | |
| 475 | blob_reset(&fullName); |
| 476 | } |
| 477 | glob_free(pIgnore); |
| 478 | glob_free(pClean); |
| 479 | |
| 480 | /** Check for Windows-reserved names and warn or exit, as |
| 481 | ** appopriate. Note that the 'add' internal machinery already |
| 482 | ** _silently_ skips over any names for which |
| 483 | ** file_is_reserved_name() returns true or which is in the |
| 484 | ** fossil_reserved_name() list. We do not need to warn for those, |
| 485 | ** as they're outright verboten. */ |
| 486 | if(db_exists("SELECT 1 FROM sfile WHERE win_reserved(pathname)")){ |
| 487 | Stmt q = empty_Stmt; |
| 488 | db_prepare(&q,"SELECT pathname FROM sfile " |
| 489 | "WHERE win_reserved(pathname)"); |
| 490 | int reservedCount = 0; |
| 491 | while( db_step(&q)==SQLITE_ROW ){ |
| 492 | const char * zName = db_column_text(&q, 0); |
| 493 | ++reservedCount; |
| 494 | if(allowReservedFlag){ |
| 495 | fossil_warning("WARNING: Windows-reserved " |
| 496 | "filename: %s", zName); |
| 497 | }else{ |
| 498 | fossil_warning("ERROR: Windows-reserved filename: %s", zName); |
| 499 | } |
| 500 | } |
| 501 | db_finalize(&q); |
| 502 | if(allowReservedFlag==0){ |
| 503 | fossil_fatal("ERROR: %d Windows-reserved filename(s) added. " |
| 504 | "Use --allow-reserved to permit such names.", |
| 505 | reservedCount); |
| 506 | } |
| 507 | } |
| 508 | add_files_in_sfile(vid); |
| 509 | db_end_transaction(0); |
| 510 | } |
| 511 | |
| 512 | /* |
| 513 |
-2
| --- src/chat.c | ||
| +++ src/chat.c | ||
| @@ -163,12 +163,10 @@ | ||
| 163 | 163 | @ placeholder="Type message here." autocomplete="off"> |
| 164 | 164 | @ <textarea rows="8" id="chat-input-multi" \ |
| 165 | 165 | @ placeholder="Type message here. Ctrl-Enter sends it." \ |
| 166 | 166 | @ class="hidden"></textarea> |
| 167 | 167 | @ <input type="submit" value="Send" id="chat-message-submit"> |
| 168 | - @ <button id="chat-scroll-top" class="hidden">↑</button> | |
| 169 | - @ <button id="chat-scroll-bottom" class="hidden">↓</button> | |
| 170 | 168 | @ <span id="chat-settings-button" class="settings-icon" \ |
| 171 | 169 | @ aria-label="Settings..." aria-haspopup="true" ></span> |
| 172 | 170 | @ </div> |
| 173 | 171 | @ <div id='chat-input-file-area'> |
| 174 | 172 | @ <div class='file-selection-wrapper'> |
| 175 | 173 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -163,12 +163,10 @@ | |
| 163 | @ placeholder="Type message here." autocomplete="off"> |
| 164 | @ <textarea rows="8" id="chat-input-multi" \ |
| 165 | @ placeholder="Type message here. Ctrl-Enter sends it." \ |
| 166 | @ class="hidden"></textarea> |
| 167 | @ <input type="submit" value="Send" id="chat-message-submit"> |
| 168 | @ <button id="chat-scroll-top" class="hidden">↑</button> |
| 169 | @ <button id="chat-scroll-bottom" class="hidden">↓</button> |
| 170 | @ <span id="chat-settings-button" class="settings-icon" \ |
| 171 | @ aria-label="Settings..." aria-haspopup="true" ></span> |
| 172 | @ </div> |
| 173 | @ <div id='chat-input-file-area'> |
| 174 | @ <div class='file-selection-wrapper'> |
| 175 |
| --- src/chat.c | |
| +++ src/chat.c | |
| @@ -163,12 +163,10 @@ | |
| 163 | @ placeholder="Type message here." autocomplete="off"> |
| 164 | @ <textarea rows="8" id="chat-input-multi" \ |
| 165 | @ placeholder="Type message here. Ctrl-Enter sends it." \ |
| 166 | @ class="hidden"></textarea> |
| 167 | @ <input type="submit" value="Send" id="chat-message-submit"> |
| 168 | @ <span id="chat-settings-button" class="settings-icon" \ |
| 169 | @ aria-label="Settings..." aria-haspopup="true" ></span> |
| 170 | @ </div> |
| 171 | @ <div id='chat-input-file-area'> |
| 172 | @ <div class='file-selection-wrapper'> |
| 173 |
+1
-28
| --- src/chat.js | ||
| +++ src/chat.js | ||
| @@ -107,13 +107,11 @@ | ||
| 107 | 107 | btnSubmit: E1('#chat-message-submit'), |
| 108 | 108 | inputSingle: E1('#chat-input-single'), |
| 109 | 109 | inputMulti: E1('#chat-input-multi'), |
| 110 | 110 | inputCurrent: undefined/*one of inputSingle or inputMulti*/, |
| 111 | 111 | inputFile: E1('#chat-input-file'), |
| 112 | - contentDiv: E1('div.content'), | |
| 113 | - btnMsgHome: E1('#chat-scroll-top'), | |
| 114 | - btnMsgEnd: E1('#chat-scroll-bottom') | |
| 112 | + contentDiv: E1('div.content') | |
| 115 | 113 | }, |
| 116 | 114 | me: F.user.name, |
| 117 | 115 | mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50, |
| 118 | 116 | mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/, |
| 119 | 117 | pageIsActive: 'visible'===document.visibilityState, |
| @@ -339,18 +337,10 @@ | ||
| 339 | 337 | } |
| 340 | 338 | }, |
| 341 | 339 | toggleChatOnlyMode: function(){ |
| 342 | 340 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 343 | 341 | }, |
| 344 | - /* Turn the message area top/bottom buttons on (yes===true), off | |
| 345 | - (yes==false), or toggle them (no arguments). Returns this. */ | |
| 346 | - toggleNavButtons: function(yes){ | |
| 347 | - const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden'; | |
| 348 | - if(0===arguments.length) D.toggleClass(e, c); | |
| 349 | - else if(!arguments[0]) D.addClass(e, c); | |
| 350 | - else D.removeClass(e, c); | |
| 351 | - }, | |
| 352 | 342 | messageIsInView: function(e){ |
| 353 | 343 | return e ? overlapsElemView(e, this.e.messagesWrapper) : false; |
| 354 | 344 | }, |
| 355 | 345 | settings:{ |
| 356 | 346 | get: (k,dflt)=>F.storage.get(k,dflt), |
| @@ -965,14 +955,10 @@ | ||
| 965 | 955 | boolValue: ()=>Chat.settings.getBool('images-inline'), |
| 966 | 956 | callback: function(){ |
| 967 | 957 | const v = Chat.settings.toggle('images-inline'); |
| 968 | 958 | F.toast.message("Image mode set to "+(v ? "inline" : "hyperlink")+"."); |
| 969 | 959 | } |
| 970 | - },{ | |
| 971 | - label: "Message home/end buttons", | |
| 972 | - boolValue: ()=>!Chat.e.btnMsgHome.classList.contains('hidden'), | |
| 973 | - callback: ()=>Chat.toggleNavButtons() | |
| 974 | 960 | }]; |
| 975 | 961 | |
| 976 | 962 | /** Set up selection list of notification sounds. */ |
| 977 | 963 | if(true/*flip this to false to enable selection of audio files*/){ |
| 978 | 964 | settingsOps.push({ |
| @@ -1083,23 +1069,10 @@ | ||
| 1083 | 1069 | settingsPopup.options.adjustY = function(y){ |
| 1084 | 1070 | const rect = settingsButton.getBoundingClientRect(); |
| 1085 | 1071 | return rect.top - popupSize.height -2; |
| 1086 | 1072 | }; |
| 1087 | 1073 | })()/*#chat-settings-button setup*/; |
| 1088 | - | |
| 1089 | - (function(){ /* buttons to scroll to the begin/end of the messages. */ | |
| 1090 | - Chat.e.btnMsgEnd.addEventListener('click',function(ev){ | |
| 1091 | - ev.preventDefault(); | |
| 1092 | - Chat.scrollMessagesTo(1); | |
| 1093 | - return false; | |
| 1094 | - }); | |
| 1095 | - Chat.e.btnMsgHome.addEventListener('click',function(ev){ | |
| 1096 | - ev.preventDefault(); | |
| 1097 | - Chat.scrollMessagesTo(-1); | |
| 1098 | - return false; | |
| 1099 | - }); | |
| 1100 | - })(); | |
| 1101 | 1074 | |
| 1102 | 1075 | /** Callback for poll() to inject new content into the page. jx == |
| 1103 | 1076 | the response from /chat-poll. If atEnd is true, the message is |
| 1104 | 1077 | appended to the end of the chat list (for loading older |
| 1105 | 1078 | messages), else the beginning (the default). */ |
| 1106 | 1079 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -107,13 +107,11 @@ | |
| 107 | btnSubmit: E1('#chat-message-submit'), |
| 108 | inputSingle: E1('#chat-input-single'), |
| 109 | inputMulti: E1('#chat-input-multi'), |
| 110 | inputCurrent: undefined/*one of inputSingle or inputMulti*/, |
| 111 | inputFile: E1('#chat-input-file'), |
| 112 | contentDiv: E1('div.content'), |
| 113 | btnMsgHome: E1('#chat-scroll-top'), |
| 114 | btnMsgEnd: E1('#chat-scroll-bottom') |
| 115 | }, |
| 116 | me: F.user.name, |
| 117 | mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50, |
| 118 | mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/, |
| 119 | pageIsActive: 'visible'===document.visibilityState, |
| @@ -339,18 +337,10 @@ | |
| 339 | } |
| 340 | }, |
| 341 | toggleChatOnlyMode: function(){ |
| 342 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 343 | }, |
| 344 | /* Turn the message area top/bottom buttons on (yes===true), off |
| 345 | (yes==false), or toggle them (no arguments). Returns this. */ |
| 346 | toggleNavButtons: function(yes){ |
| 347 | const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden'; |
| 348 | if(0===arguments.length) D.toggleClass(e, c); |
| 349 | else if(!arguments[0]) D.addClass(e, c); |
| 350 | else D.removeClass(e, c); |
| 351 | }, |
| 352 | messageIsInView: function(e){ |
| 353 | return e ? overlapsElemView(e, this.e.messagesWrapper) : false; |
| 354 | }, |
| 355 | settings:{ |
| 356 | get: (k,dflt)=>F.storage.get(k,dflt), |
| @@ -965,14 +955,10 @@ | |
| 965 | boolValue: ()=>Chat.settings.getBool('images-inline'), |
| 966 | callback: function(){ |
| 967 | const v = Chat.settings.toggle('images-inline'); |
| 968 | F.toast.message("Image mode set to "+(v ? "inline" : "hyperlink")+"."); |
| 969 | } |
| 970 | },{ |
| 971 | label: "Message home/end buttons", |
| 972 | boolValue: ()=>!Chat.e.btnMsgHome.classList.contains('hidden'), |
| 973 | callback: ()=>Chat.toggleNavButtons() |
| 974 | }]; |
| 975 | |
| 976 | /** Set up selection list of notification sounds. */ |
| 977 | if(true/*flip this to false to enable selection of audio files*/){ |
| 978 | settingsOps.push({ |
| @@ -1083,23 +1069,10 @@ | |
| 1083 | settingsPopup.options.adjustY = function(y){ |
| 1084 | const rect = settingsButton.getBoundingClientRect(); |
| 1085 | return rect.top - popupSize.height -2; |
| 1086 | }; |
| 1087 | })()/*#chat-settings-button setup*/; |
| 1088 | |
| 1089 | (function(){ /* buttons to scroll to the begin/end of the messages. */ |
| 1090 | Chat.e.btnMsgEnd.addEventListener('click',function(ev){ |
| 1091 | ev.preventDefault(); |
| 1092 | Chat.scrollMessagesTo(1); |
| 1093 | return false; |
| 1094 | }); |
| 1095 | Chat.e.btnMsgHome.addEventListener('click',function(ev){ |
| 1096 | ev.preventDefault(); |
| 1097 | Chat.scrollMessagesTo(-1); |
| 1098 | return false; |
| 1099 | }); |
| 1100 | })(); |
| 1101 | |
| 1102 | /** Callback for poll() to inject new content into the page. jx == |
| 1103 | the response from /chat-poll. If atEnd is true, the message is |
| 1104 | appended to the end of the chat list (for loading older |
| 1105 | messages), else the beginning (the default). */ |
| 1106 |
| --- src/chat.js | |
| +++ src/chat.js | |
| @@ -107,13 +107,11 @@ | |
| 107 | btnSubmit: E1('#chat-message-submit'), |
| 108 | inputSingle: E1('#chat-input-single'), |
| 109 | inputMulti: E1('#chat-input-multi'), |
| 110 | inputCurrent: undefined/*one of inputSingle or inputMulti*/, |
| 111 | inputFile: E1('#chat-input-file'), |
| 112 | contentDiv: E1('div.content') |
| 113 | }, |
| 114 | me: F.user.name, |
| 115 | mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50, |
| 116 | mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/, |
| 117 | pageIsActive: 'visible'===document.visibilityState, |
| @@ -339,18 +337,10 @@ | |
| 337 | } |
| 338 | }, |
| 339 | toggleChatOnlyMode: function(){ |
| 340 | return this.chatOnlyMode(!this.isChatOnlyMode()); |
| 341 | }, |
| 342 | messageIsInView: function(e){ |
| 343 | return e ? overlapsElemView(e, this.e.messagesWrapper) : false; |
| 344 | }, |
| 345 | settings:{ |
| 346 | get: (k,dflt)=>F.storage.get(k,dflt), |
| @@ -965,14 +955,10 @@ | |
| 955 | boolValue: ()=>Chat.settings.getBool('images-inline'), |
| 956 | callback: function(){ |
| 957 | const v = Chat.settings.toggle('images-inline'); |
| 958 | F.toast.message("Image mode set to "+(v ? "inline" : "hyperlink")+"."); |
| 959 | } |
| 960 | }]; |
| 961 | |
| 962 | /** Set up selection list of notification sounds. */ |
| 963 | if(true/*flip this to false to enable selection of audio files*/){ |
| 964 | settingsOps.push({ |
| @@ -1083,23 +1069,10 @@ | |
| 1069 | settingsPopup.options.adjustY = function(y){ |
| 1070 | const rect = settingsButton.getBoundingClientRect(); |
| 1071 | return rect.top - popupSize.height -2; |
| 1072 | }; |
| 1073 | })()/*#chat-settings-button setup*/; |
| 1074 | |
| 1075 | /** Callback for poll() to inject new content into the page. jx == |
| 1076 | the response from /chat-poll. If atEnd is true, the message is |
| 1077 | appended to the end of the chat list (for loading older |
| 1078 | messages), else the beginning (the default). */ |
| 1079 |
M
src/db.c
+19
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1376,10 +1376,13 @@ | ||
| 1376 | 1376 | alert_display_name_func, 0, 0); |
| 1377 | 1377 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1378 | 1378 | db_obscure, 0, 0); |
| 1379 | 1379 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1380 | 1380 | db_protected_setting_func, 0, 0); |
| 1381 | + sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0, | |
| 1382 | + db_win_reserved_func,0,0 | |
| 1383 | + ); | |
| 1381 | 1384 | } |
| 1382 | 1385 | |
| 1383 | 1386 | #if USE_SEE |
| 1384 | 1387 | /* |
| 1385 | 1388 | ** This is a pointer to the saved database encryption key string. |
| @@ -2876,10 +2879,26 @@ | ||
| 2876 | 2879 | assert( rc==0 || rc==1 ); |
| 2877 | 2880 | if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; |
| 2878 | 2881 | sqlite3_result_value(context, argv[2-rc]); |
| 2879 | 2882 | } |
| 2880 | 2883 | } |
| 2884 | + | |
| 2885 | +/* | |
| 2886 | +** Implementation of the "win_reserved(X)" SQL function, a wrapper | |
| 2887 | +** for file_is_win_reserved(X) which returns true if X is | |
| 2888 | +** a Windows-reserved filename. | |
| 2889 | +*/ | |
| 2890 | +LOCAL void db_win_reserved_func( | |
| 2891 | + sqlite3_context *context, | |
| 2892 | + int argc, | |
| 2893 | + sqlite3_value **argv | |
| 2894 | +){ | |
| 2895 | + const char * zName = (const char *)sqlite3_value_text(argv[0]); | |
| 2896 | + if( zName!=0 ){ | |
| 2897 | + sqlite3_result_int(context, file_is_win_reserved(zName)!=0); | |
| 2898 | + } | |
| 2899 | +} | |
| 2881 | 2900 | |
| 2882 | 2901 | /* |
| 2883 | 2902 | ** Convert the input string into a artifact hash. Make a notation in the |
| 2884 | 2903 | ** CONCEALED table so that the hash can be undo using the db_reveal() |
| 2885 | 2904 | ** function at some later time. |
| 2886 | 2905 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1376,10 +1376,13 @@ | |
| 1376 | alert_display_name_func, 0, 0); |
| 1377 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1378 | db_obscure, 0, 0); |
| 1379 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1380 | db_protected_setting_func, 0, 0); |
| 1381 | } |
| 1382 | |
| 1383 | #if USE_SEE |
| 1384 | /* |
| 1385 | ** This is a pointer to the saved database encryption key string. |
| @@ -2876,10 +2879,26 @@ | |
| 2876 | assert( rc==0 || rc==1 ); |
| 2877 | if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; |
| 2878 | sqlite3_result_value(context, argv[2-rc]); |
| 2879 | } |
| 2880 | } |
| 2881 | |
| 2882 | /* |
| 2883 | ** Convert the input string into a artifact hash. Make a notation in the |
| 2884 | ** CONCEALED table so that the hash can be undo using the db_reveal() |
| 2885 | ** function at some later time. |
| 2886 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1376,10 +1376,13 @@ | |
| 1376 | alert_display_name_func, 0, 0); |
| 1377 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1378 | db_obscure, 0, 0); |
| 1379 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1380 | db_protected_setting_func, 0, 0); |
| 1381 | sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0, |
| 1382 | db_win_reserved_func,0,0 |
| 1383 | ); |
| 1384 | } |
| 1385 | |
| 1386 | #if USE_SEE |
| 1387 | /* |
| 1388 | ** This is a pointer to the saved database encryption key string. |
| @@ -2876,10 +2879,26 @@ | |
| 2879 | assert( rc==0 || rc==1 ); |
| 2880 | if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; |
| 2881 | sqlite3_result_value(context, argv[2-rc]); |
| 2882 | } |
| 2883 | } |
| 2884 | |
| 2885 | /* |
| 2886 | ** Implementation of the "win_reserved(X)" SQL function, a wrapper |
| 2887 | ** for file_is_win_reserved(X) which returns true if X is |
| 2888 | ** a Windows-reserved filename. |
| 2889 | */ |
| 2890 | LOCAL void db_win_reserved_func( |
| 2891 | sqlite3_context *context, |
| 2892 | int argc, |
| 2893 | sqlite3_value **argv |
| 2894 | ){ |
| 2895 | const char * zName = (const char *)sqlite3_value_text(argv[0]); |
| 2896 | if( zName!=0 ){ |
| 2897 | sqlite3_result_int(context, file_is_win_reserved(zName)!=0); |
| 2898 | } |
| 2899 | } |
| 2900 | |
| 2901 | /* |
| 2902 | ** Convert the input string into a artifact hash. Make a notation in the |
| 2903 | ** CONCEALED table so that the hash can be undo using the db_reveal() |
| 2904 | ** function at some later time. |
| 2905 |
M
src/db.c
+19
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1376,10 +1376,13 @@ | ||
| 1376 | 1376 | alert_display_name_func, 0, 0); |
| 1377 | 1377 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1378 | 1378 | db_obscure, 0, 0); |
| 1379 | 1379 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1380 | 1380 | db_protected_setting_func, 0, 0); |
| 1381 | + sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0, | |
| 1382 | + db_win_reserved_func,0,0 | |
| 1383 | + ); | |
| 1381 | 1384 | } |
| 1382 | 1385 | |
| 1383 | 1386 | #if USE_SEE |
| 1384 | 1387 | /* |
| 1385 | 1388 | ** This is a pointer to the saved database encryption key string. |
| @@ -2876,10 +2879,26 @@ | ||
| 2876 | 2879 | assert( rc==0 || rc==1 ); |
| 2877 | 2880 | if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; |
| 2878 | 2881 | sqlite3_result_value(context, argv[2-rc]); |
| 2879 | 2882 | } |
| 2880 | 2883 | } |
| 2884 | + | |
| 2885 | +/* | |
| 2886 | +** Implementation of the "win_reserved(X)" SQL function, a wrapper | |
| 2887 | +** for file_is_win_reserved(X) which returns true if X is | |
| 2888 | +** a Windows-reserved filename. | |
| 2889 | +*/ | |
| 2890 | +LOCAL void db_win_reserved_func( | |
| 2891 | + sqlite3_context *context, | |
| 2892 | + int argc, | |
| 2893 | + sqlite3_value **argv | |
| 2894 | +){ | |
| 2895 | + const char * zName = (const char *)sqlite3_value_text(argv[0]); | |
| 2896 | + if( zName!=0 ){ | |
| 2897 | + sqlite3_result_int(context, file_is_win_reserved(zName)!=0); | |
| 2898 | + } | |
| 2899 | +} | |
| 2881 | 2900 | |
| 2882 | 2901 | /* |
| 2883 | 2902 | ** Convert the input string into a artifact hash. Make a notation in the |
| 2884 | 2903 | ** CONCEALED table so that the hash can be undo using the db_reveal() |
| 2885 | 2904 | ** function at some later time. |
| 2886 | 2905 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1376,10 +1376,13 @@ | |
| 1376 | alert_display_name_func, 0, 0); |
| 1377 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1378 | db_obscure, 0, 0); |
| 1379 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1380 | db_protected_setting_func, 0, 0); |
| 1381 | } |
| 1382 | |
| 1383 | #if USE_SEE |
| 1384 | /* |
| 1385 | ** This is a pointer to the saved database encryption key string. |
| @@ -2876,10 +2879,26 @@ | |
| 2876 | assert( rc==0 || rc==1 ); |
| 2877 | if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; |
| 2878 | sqlite3_result_value(context, argv[2-rc]); |
| 2879 | } |
| 2880 | } |
| 2881 | |
| 2882 | /* |
| 2883 | ** Convert the input string into a artifact hash. Make a notation in the |
| 2884 | ** CONCEALED table so that the hash can be undo using the db_reveal() |
| 2885 | ** function at some later time. |
| 2886 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1376,10 +1376,13 @@ | |
| 1376 | alert_display_name_func, 0, 0); |
| 1377 | sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0, |
| 1378 | db_obscure, 0, 0); |
| 1379 | sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0, |
| 1380 | db_protected_setting_func, 0, 0); |
| 1381 | sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0, |
| 1382 | db_win_reserved_func,0,0 |
| 1383 | ); |
| 1384 | } |
| 1385 | |
| 1386 | #if USE_SEE |
| 1387 | /* |
| 1388 | ** This is a pointer to the saved database encryption key string. |
| @@ -2876,10 +2879,26 @@ | |
| 2879 | assert( rc==0 || rc==1 ); |
| 2880 | if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc; |
| 2881 | sqlite3_result_value(context, argv[2-rc]); |
| 2882 | } |
| 2883 | } |
| 2884 | |
| 2885 | /* |
| 2886 | ** Implementation of the "win_reserved(X)" SQL function, a wrapper |
| 2887 | ** for file_is_win_reserved(X) which returns true if X is |
| 2888 | ** a Windows-reserved filename. |
| 2889 | */ |
| 2890 | LOCAL void db_win_reserved_func( |
| 2891 | sqlite3_context *context, |
| 2892 | int argc, |
| 2893 | sqlite3_value **argv |
| 2894 | ){ |
| 2895 | const char * zName = (const char *)sqlite3_value_text(argv[0]); |
| 2896 | if( zName!=0 ){ |
| 2897 | sqlite3_result_int(context, file_is_win_reserved(zName)!=0); |
| 2898 | } |
| 2899 | } |
| 2900 | |
| 2901 | /* |
| 2902 | ** Convert the input string into a artifact hash. Make a notation in the |
| 2903 | ** CONCEALED table so that the hash can be undo using the db_reveal() |
| 2904 | ** function at some later time. |
| 2905 |
+25
-4
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -968,14 +968,18 @@ | ||
| 968 | 968 | } |
| 969 | 969 | |
| 970 | 970 | /* |
| 971 | 971 | ** WEBPAGE: rptview |
| 972 | 972 | ** |
| 973 | -** Generate a report. The rn query parameter is the report number | |
| 974 | -** corresponding to REPORTFMT.RN. If the tablist query parameter exists, | |
| 973 | +** Generate a report. The "rn" query parameter is the report number | |
| 974 | +** corresponding to REPORTFMT.RN. If the "tablist" query parameter exists, | |
| 975 | 975 | ** then the output consists of lines of tab-separated fields instead of |
| 976 | -** an HTML table. | |
| 976 | +** an HTML table. If the "rvsmpl" query parameter is set then report's | |
| 977 | +** submenu will contain an extra hyperlink that have a value-driven | |
| 978 | +** label and target. | |
| 979 | +** | |
| 980 | +** "rvsmpl" stands for Report View SubMenu's Parametric Link. | |
| 977 | 981 | */ |
| 978 | 982 | void rptview_page(void){ |
| 979 | 983 | int count = 0; |
| 980 | 984 | int rn, rc; |
| 981 | 985 | char *zSql; |
| @@ -1029,14 +1033,31 @@ | ||
| 1029 | 1033 | } |
| 1030 | 1034 | |
| 1031 | 1035 | count = 0; |
| 1032 | 1036 | if( !tabs ){ |
| 1033 | 1037 | struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 1038 | + const char *zQS = PD("QUERY_STRING",""); | |
| 1034 | 1039 | |
| 1035 | 1040 | db_multi_exec("PRAGMA empty_result_callbacks=ON"); |
| 1036 | 1041 | style_set_current_feature("report"); |
| 1037 | - style_submenu_element("Raw", "rptview?tablist=1&rn=%d&%h", rn, PD("QUERY_STRING","") ); | |
| 1042 | + /* | |
| 1043 | + ** Lets use a funcy button for /reportlist since that page may be | |
| 1044 | + ** heavily customized by the user. Some variants: ⊚ ⦾ ❊ ⊛ ⚛ ⸎ 💠 | |
| 1045 | + ** Enclosing it inside of square brackets makes its position | |
| 1046 | + ** determenistic and clearly distincts regular submenu links from | |
| 1047 | + ** those that are induced by the query string parameters. | |
| 1048 | + */ | |
| 1049 | + if( zQS[0] ){ | |
| 1050 | + style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS); | |
| 1051 | + style_submenu_element("[⊚]","%R/reportlist?%s",zQS); | |
| 1052 | + } else { | |
| 1053 | + style_submenu_element("Raw","%R/%s?tablist=1",g.zPath); | |
| 1054 | + style_submenu_element("[⊚]","%R/reportlist"); | |
| 1055 | + } | |
| 1056 | + style_submenu_parametric("rptview_",5); | |
| 1057 | + style_submenu_parametric("rv",5); | |
| 1058 | + | |
| 1038 | 1059 | if( g.perm.Admin |
| 1039 | 1060 | || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){ |
| 1040 | 1061 | style_submenu_element("Edit", "rptedit?rn=%d", rn); |
| 1041 | 1062 | } |
| 1042 | 1063 | if( g.perm.TktFmt ){ |
| 1043 | 1064 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -968,14 +968,18 @@ | |
| 968 | } |
| 969 | |
| 970 | /* |
| 971 | ** WEBPAGE: rptview |
| 972 | ** |
| 973 | ** Generate a report. The rn query parameter is the report number |
| 974 | ** corresponding to REPORTFMT.RN. If the tablist query parameter exists, |
| 975 | ** then the output consists of lines of tab-separated fields instead of |
| 976 | ** an HTML table. |
| 977 | */ |
| 978 | void rptview_page(void){ |
| 979 | int count = 0; |
| 980 | int rn, rc; |
| 981 | char *zSql; |
| @@ -1029,14 +1033,31 @@ | |
| 1029 | } |
| 1030 | |
| 1031 | count = 0; |
| 1032 | if( !tabs ){ |
| 1033 | struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 1034 | |
| 1035 | db_multi_exec("PRAGMA empty_result_callbacks=ON"); |
| 1036 | style_set_current_feature("report"); |
| 1037 | style_submenu_element("Raw", "rptview?tablist=1&rn=%d&%h", rn, PD("QUERY_STRING","") ); |
| 1038 | if( g.perm.Admin |
| 1039 | || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){ |
| 1040 | style_submenu_element("Edit", "rptedit?rn=%d", rn); |
| 1041 | } |
| 1042 | if( g.perm.TktFmt ){ |
| 1043 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -968,14 +968,18 @@ | |
| 968 | } |
| 969 | |
| 970 | /* |
| 971 | ** WEBPAGE: rptview |
| 972 | ** |
| 973 | ** Generate a report. The "rn" query parameter is the report number |
| 974 | ** corresponding to REPORTFMT.RN. If the "tablist" query parameter exists, |
| 975 | ** then the output consists of lines of tab-separated fields instead of |
| 976 | ** an HTML table. If the "rvsmpl" query parameter is set then report's |
| 977 | ** submenu will contain an extra hyperlink that have a value-driven |
| 978 | ** label and target. |
| 979 | ** |
| 980 | ** "rvsmpl" stands for Report View SubMenu's Parametric Link. |
| 981 | */ |
| 982 | void rptview_page(void){ |
| 983 | int count = 0; |
| 984 | int rn, rc; |
| 985 | char *zSql; |
| @@ -1029,14 +1033,31 @@ | |
| 1033 | } |
| 1034 | |
| 1035 | count = 0; |
| 1036 | if( !tabs ){ |
| 1037 | struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 1038 | const char *zQS = PD("QUERY_STRING",""); |
| 1039 | |
| 1040 | db_multi_exec("PRAGMA empty_result_callbacks=ON"); |
| 1041 | style_set_current_feature("report"); |
| 1042 | /* |
| 1043 | ** Lets use a funcy button for /reportlist since that page may be |
| 1044 | ** heavily customized by the user. Some variants: ⊚ ⦾ ❊ ⊛ ⚛ ⸎ 💠 |
| 1045 | ** Enclosing it inside of square brackets makes its position |
| 1046 | ** determenistic and clearly distincts regular submenu links from |
| 1047 | ** those that are induced by the query string parameters. |
| 1048 | */ |
| 1049 | if( zQS[0] ){ |
| 1050 | style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS); |
| 1051 | style_submenu_element("[⊚]","%R/reportlist?%s",zQS); |
| 1052 | } else { |
| 1053 | style_submenu_element("Raw","%R/%s?tablist=1",g.zPath); |
| 1054 | style_submenu_element("[⊚]","%R/reportlist"); |
| 1055 | } |
| 1056 | style_submenu_parametric("rptview_",5); |
| 1057 | style_submenu_parametric("rv",5); |
| 1058 | |
| 1059 | if( g.perm.Admin |
| 1060 | || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){ |
| 1061 | style_submenu_element("Edit", "rptedit?rn=%d", rn); |
| 1062 | } |
| 1063 | if( g.perm.TktFmt ){ |
| 1064 |
+4
-7
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -146,12 +146,12 @@ | ||
| 146 | 146 | ){ |
| 147 | 147 | gather_artifact_stats(1); |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | /* |
| 151 | -** Add the content(), compress(), and decompress() SQL functions to | |
| 152 | -** database connection db. | |
| 151 | +** Add the content(), compress(), decompress(), and | |
| 152 | +** gather_artifact_stats() SQL functions to database connection db. | |
| 153 | 153 | */ |
| 154 | 154 | int add_content_sql_commands(sqlite3 *db){ |
| 155 | 155 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 156 | 156 | sqlcmd_content, 0, 0); |
| 157 | 157 | sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, |
| @@ -171,18 +171,18 @@ | ||
| 171 | 171 | ** |
| 172 | 172 | ** These invoke the corresponding C routines. |
| 173 | 173 | ** |
| 174 | 174 | ** WARNING: |
| 175 | 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | -** method of than the "fossil sql" command. If an attacker gains access | |
| 176 | +** method other than the "fossil sql" command. If an attacker gains access | |
| 177 | 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | 178 | ** |
| 179 | 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | 181 | ** in future releases. |
| 182 | 182 | ** |
| 183 | -** 2020-11-29: This functions are now only available if the "fossil sql" | |
| 183 | +** 2020-11-29: These functions are now only available if the "fossil sql" | |
| 184 | 184 | ** command is started with the --test option. |
| 185 | 185 | */ |
| 186 | 186 | static void sqlcmd_db_protect( |
| 187 | 187 | sqlite3_context *context, |
| 188 | 188 | int argc, |
| @@ -204,13 +204,10 @@ | ||
| 204 | 204 | int argc, |
| 205 | 205 | sqlite3_value **argv |
| 206 | 206 | ){ |
| 207 | 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | 208 | } |
| 209 | - | |
| 210 | - | |
| 211 | - | |
| 212 | 209 | |
| 213 | 210 | /* |
| 214 | 211 | ** This is the "automatic extension" initializer that runs right after |
| 215 | 212 | ** the connection to the repository database is opened. Set up the |
| 216 | 213 | ** database connection to be more useful to the human operator. |
| 217 | 214 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -146,12 +146,12 @@ | |
| 146 | ){ |
| 147 | gather_artifact_stats(1); |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** Add the content(), compress(), and decompress() SQL functions to |
| 152 | ** database connection db. |
| 153 | */ |
| 154 | int add_content_sql_commands(sqlite3 *db){ |
| 155 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 156 | sqlcmd_content, 0, 0); |
| 157 | sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, |
| @@ -171,18 +171,18 @@ | |
| 171 | ** |
| 172 | ** These invoke the corresponding C routines. |
| 173 | ** |
| 174 | ** WARNING: |
| 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | ** method of than the "fossil sql" command. If an attacker gains access |
| 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | ** |
| 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | ** in future releases. |
| 182 | ** |
| 183 | ** 2020-11-29: This functions are now only available if the "fossil sql" |
| 184 | ** command is started with the --test option. |
| 185 | */ |
| 186 | static void sqlcmd_db_protect( |
| 187 | sqlite3_context *context, |
| 188 | int argc, |
| @@ -204,13 +204,10 @@ | |
| 204 | int argc, |
| 205 | sqlite3_value **argv |
| 206 | ){ |
| 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | } |
| 209 | |
| 210 | |
| 211 | |
| 212 | |
| 213 | /* |
| 214 | ** This is the "automatic extension" initializer that runs right after |
| 215 | ** the connection to the repository database is opened. Set up the |
| 216 | ** database connection to be more useful to the human operator. |
| 217 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -146,12 +146,12 @@ | |
| 146 | ){ |
| 147 | gather_artifact_stats(1); |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** Add the content(), compress(), decompress(), and |
| 152 | ** gather_artifact_stats() SQL functions to database connection db. |
| 153 | */ |
| 154 | int add_content_sql_commands(sqlite3 *db){ |
| 155 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 156 | sqlcmd_content, 0, 0); |
| 157 | sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, |
| @@ -171,18 +171,18 @@ | |
| 171 | ** |
| 172 | ** These invoke the corresponding C routines. |
| 173 | ** |
| 174 | ** WARNING: |
| 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | ** method other than the "fossil sql" command. If an attacker gains access |
| 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | ** |
| 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | ** in future releases. |
| 182 | ** |
| 183 | ** 2020-11-29: These functions are now only available if the "fossil sql" |
| 184 | ** command is started with the --test option. |
| 185 | */ |
| 186 | static void sqlcmd_db_protect( |
| 187 | sqlite3_context *context, |
| 188 | int argc, |
| @@ -204,13 +204,10 @@ | |
| 204 | int argc, |
| 205 | sqlite3_value **argv |
| 206 | ){ |
| 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | ** This is the "automatic extension" initializer that runs right after |
| 212 | ** the connection to the repository database is opened. Set up the |
| 213 | ** database connection to be more useful to the human operator. |
| 214 |
+4
-7
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -146,12 +146,12 @@ | ||
| 146 | 146 | ){ |
| 147 | 147 | gather_artifact_stats(1); |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | /* |
| 151 | -** Add the content(), compress(), and decompress() SQL functions to | |
| 152 | -** database connection db. | |
| 151 | +** Add the content(), compress(), decompress(), and | |
| 152 | +** gather_artifact_stats() SQL functions to database connection db. | |
| 153 | 153 | */ |
| 154 | 154 | int add_content_sql_commands(sqlite3 *db){ |
| 155 | 155 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 156 | 156 | sqlcmd_content, 0, 0); |
| 157 | 157 | sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, |
| @@ -171,18 +171,18 @@ | ||
| 171 | 171 | ** |
| 172 | 172 | ** These invoke the corresponding C routines. |
| 173 | 173 | ** |
| 174 | 174 | ** WARNING: |
| 175 | 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | -** method of than the "fossil sql" command. If an attacker gains access | |
| 176 | +** method other than the "fossil sql" command. If an attacker gains access | |
| 177 | 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | 178 | ** |
| 179 | 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | 181 | ** in future releases. |
| 182 | 182 | ** |
| 183 | -** 2020-11-29: This functions are now only available if the "fossil sql" | |
| 183 | +** 2020-11-29: These functions are now only available if the "fossil sql" | |
| 184 | 184 | ** command is started with the --test option. |
| 185 | 185 | */ |
| 186 | 186 | static void sqlcmd_db_protect( |
| 187 | 187 | sqlite3_context *context, |
| 188 | 188 | int argc, |
| @@ -204,13 +204,10 @@ | ||
| 204 | 204 | int argc, |
| 205 | 205 | sqlite3_value **argv |
| 206 | 206 | ){ |
| 207 | 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | 208 | } |
| 209 | - | |
| 210 | - | |
| 211 | - | |
| 212 | 209 | |
| 213 | 210 | /* |
| 214 | 211 | ** This is the "automatic extension" initializer that runs right after |
| 215 | 212 | ** the connection to the repository database is opened. Set up the |
| 216 | 213 | ** database connection to be more useful to the human operator. |
| 217 | 214 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -146,12 +146,12 @@ | |
| 146 | ){ |
| 147 | gather_artifact_stats(1); |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** Add the content(), compress(), and decompress() SQL functions to |
| 152 | ** database connection db. |
| 153 | */ |
| 154 | int add_content_sql_commands(sqlite3 *db){ |
| 155 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 156 | sqlcmd_content, 0, 0); |
| 157 | sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, |
| @@ -171,18 +171,18 @@ | |
| 171 | ** |
| 172 | ** These invoke the corresponding C routines. |
| 173 | ** |
| 174 | ** WARNING: |
| 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | ** method of than the "fossil sql" command. If an attacker gains access |
| 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | ** |
| 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | ** in future releases. |
| 182 | ** |
| 183 | ** 2020-11-29: This functions are now only available if the "fossil sql" |
| 184 | ** command is started with the --test option. |
| 185 | */ |
| 186 | static void sqlcmd_db_protect( |
| 187 | sqlite3_context *context, |
| 188 | int argc, |
| @@ -204,13 +204,10 @@ | |
| 204 | int argc, |
| 205 | sqlite3_value **argv |
| 206 | ){ |
| 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | } |
| 209 | |
| 210 | |
| 211 | |
| 212 | |
| 213 | /* |
| 214 | ** This is the "automatic extension" initializer that runs right after |
| 215 | ** the connection to the repository database is opened. Set up the |
| 216 | ** database connection to be more useful to the human operator. |
| 217 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -146,12 +146,12 @@ | |
| 146 | ){ |
| 147 | gather_artifact_stats(1); |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** Add the content(), compress(), decompress(), and |
| 152 | ** gather_artifact_stats() SQL functions to database connection db. |
| 153 | */ |
| 154 | int add_content_sql_commands(sqlite3 *db){ |
| 155 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 156 | sqlcmd_content, 0, 0); |
| 157 | sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, |
| @@ -171,18 +171,18 @@ | |
| 171 | ** |
| 172 | ** These invoke the corresponding C routines. |
| 173 | ** |
| 174 | ** WARNING: |
| 175 | ** Do not instantiate these functions for any Fossil webpage or command |
| 176 | ** method other than the "fossil sql" command. If an attacker gains access |
| 177 | ** to these functions, he will be able to disable other defense mechanisms. |
| 178 | ** |
| 179 | ** This routines are for interactiving testing only. They are experimental |
| 180 | ** and undocumented (apart from this comments) and might go away or change |
| 181 | ** in future releases. |
| 182 | ** |
| 183 | ** 2020-11-29: These functions are now only available if the "fossil sql" |
| 184 | ** command is started with the --test option. |
| 185 | */ |
| 186 | static void sqlcmd_db_protect( |
| 187 | sqlite3_context *context, |
| 188 | int argc, |
| @@ -204,13 +204,10 @@ | |
| 204 | int argc, |
| 205 | sqlite3_value **argv |
| 206 | ){ |
| 207 | if( !local_bSqlCmdTest ) db_protect_pop(); |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | ** This is the "automatic extension" initializer that runs right after |
| 212 | ** the connection to the repository database is opened. Set up the |
| 213 | ** database connection to be more useful to the human operator. |
| 214 |
+62
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -329,10 +329,72 @@ | ||
| 329 | 329 | aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL; |
| 330 | 330 | aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; |
| 331 | 331 | nSubmenuCtrl++; |
| 332 | 332 | } |
| 333 | 333 | } |
| 334 | + | |
| 335 | +/* Add hyperlinks depending on the existence and values of special | |
| 336 | +** parameters in the request's query string. The names of these | |
| 337 | +** parameters that are investigated are obtainted by concatenation | |
| 338 | +** of zPrefix with suffix "smplX", where X is either nothing or | |
| 339 | +** a positive digit <= nMaxDigit. zPrefix must start with a lowercase | |
| 340 | +** letter, be short and have no strange characters. A value is | |
| 341 | +** well-formed if its first filepath segment (separated by '/') | |
| 342 | +** has no strange characters. The labels of the resulting submenu items | |
| 343 | +** are equal to the well-formed values that are prepended by "✧" | |
| 344 | +** unless a value starts with a lowercase letter. | |
| 345 | +** Malformed values are silently ignored. | |
| 346 | +*/ | |
| 347 | +void style_submenu_parametric( | |
| 348 | + const char *zPrefix, /* common prefix of the query parameters names */ | |
| 349 | + const int nMaxDigit /* maximal digit on the end of param names */ | |
| 350 | +){ | |
| 351 | + const char *zQS; /* QUERY_STRING */ | |
| 352 | + const char *suffix = "smpl"; /* common suffix for all parameters */ | |
| 353 | + const short sfxlen = 4; /* length of the above suffix */ | |
| 354 | + char zN[32]; /* short names => no dynamic allocations */ | |
| 355 | + short i,l; | |
| 356 | + | |
| 357 | + /* zPrefix must be tidy and short; also filter out ENV/CGI variables */ | |
| 358 | + assert( zPrefix != 0 && fossil_islower(zPrefix[0]) ); | |
| 359 | + l = strnlen( zPrefix, sizeof(zN) ); | |
| 360 | + assert( l+sfxlen+2 <= sizeof(zN) ); | |
| 361 | + assert( fossil_no_strange_characters(zPrefix) ); | |
| 362 | + /* concatenate zPrefix and suffix */ | |
| 363 | + strcpy( zN, zPrefix ); | |
| 364 | + strcpy( zN + l, suffix ); | |
| 365 | + l += sfxlen; | |
| 366 | + zN[l+1] = 0; /* nul-terminator after digit's placeholder (if any) */ | |
| 367 | + zQS = PD("QUERY_STRING",""); | |
| 368 | + for( i = 0; i <= 9 && i <= nMaxDigit; i++ ){ | |
| 369 | + const char *zV, *z; | |
| 370 | + zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */ | |
| 371 | + zV = PD(zN,""); | |
| 372 | + if( zV[0] == 0 || zV[0] == '/' ){ | |
| 373 | + continue; | |
| 374 | + } | |
| 375 | + /* require the first path segment to be unfancy ASCII string */ | |
| 376 | + for( z = zV; z[0] && z[0] != '/' ;){ | |
| 377 | + if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++; | |
| 378 | + else break; | |
| 379 | + } | |
| 380 | + if( z[0] != 0 && z[0] != '/' ) | |
| 381 | + continue; | |
| 382 | + assert( nSubmenu < count(aSubmenu) ); | |
| 383 | + if(fossil_islower(zV[0])){ | |
| 384 | + aSubmenu[nSubmenu].zLabel = mprintf( "%s",zV); /* memory leak? */ | |
| 385 | + }else{ | |
| 386 | + aSubmenu[nSubmenu].zLabel = mprintf("✧%s",zV); /* maybe: ◦✧⸰⸎ ✨ */ | |
| 387 | + } | |
| 388 | + if( zQS[0] ){ | |
| 389 | + aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS); | |
| 390 | + }else{ | |
| 391 | + aSubmenu[nSubmenu].zLink = mprintf("%R/%s",zV); | |
| 392 | + } | |
| 393 | + nSubmenu++; | |
| 394 | + } | |
| 395 | +} | |
| 334 | 396 | |
| 335 | 397 | /* |
| 336 | 398 | ** Disable or enable the submenu |
| 337 | 399 | */ |
| 338 | 400 | void style_submenu_enable(int onOff){ |
| 339 | 401 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -329,10 +329,72 @@ | |
| 329 | aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL; |
| 330 | aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; |
| 331 | nSubmenuCtrl++; |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | /* |
| 336 | ** Disable or enable the submenu |
| 337 | */ |
| 338 | void style_submenu_enable(int onOff){ |
| 339 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -329,10 +329,72 @@ | |
| 329 | aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL; |
| 330 | aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; |
| 331 | nSubmenuCtrl++; |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | /* Add hyperlinks depending on the existence and values of special |
| 336 | ** parameters in the request's query string. The names of these |
| 337 | ** parameters that are investigated are obtainted by concatenation |
| 338 | ** of zPrefix with suffix "smplX", where X is either nothing or |
| 339 | ** a positive digit <= nMaxDigit. zPrefix must start with a lowercase |
| 340 | ** letter, be short and have no strange characters. A value is |
| 341 | ** well-formed if its first filepath segment (separated by '/') |
| 342 | ** has no strange characters. The labels of the resulting submenu items |
| 343 | ** are equal to the well-formed values that are prepended by "✧" |
| 344 | ** unless a value starts with a lowercase letter. |
| 345 | ** Malformed values are silently ignored. |
| 346 | */ |
| 347 | void style_submenu_parametric( |
| 348 | const char *zPrefix, /* common prefix of the query parameters names */ |
| 349 | const int nMaxDigit /* maximal digit on the end of param names */ |
| 350 | ){ |
| 351 | const char *zQS; /* QUERY_STRING */ |
| 352 | const char *suffix = "smpl"; /* common suffix for all parameters */ |
| 353 | const short sfxlen = 4; /* length of the above suffix */ |
| 354 | char zN[32]; /* short names => no dynamic allocations */ |
| 355 | short i,l; |
| 356 | |
| 357 | /* zPrefix must be tidy and short; also filter out ENV/CGI variables */ |
| 358 | assert( zPrefix != 0 && fossil_islower(zPrefix[0]) ); |
| 359 | l = strnlen( zPrefix, sizeof(zN) ); |
| 360 | assert( l+sfxlen+2 <= sizeof(zN) ); |
| 361 | assert( fossil_no_strange_characters(zPrefix) ); |
| 362 | /* concatenate zPrefix and suffix */ |
| 363 | strcpy( zN, zPrefix ); |
| 364 | strcpy( zN + l, suffix ); |
| 365 | l += sfxlen; |
| 366 | zN[l+1] = 0; /* nul-terminator after digit's placeholder (if any) */ |
| 367 | zQS = PD("QUERY_STRING",""); |
| 368 | for( i = 0; i <= 9 && i <= nMaxDigit; i++ ){ |
| 369 | const char *zV, *z; |
| 370 | zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */ |
| 371 | zV = PD(zN,""); |
| 372 | if( zV[0] == 0 || zV[0] == '/' ){ |
| 373 | continue; |
| 374 | } |
| 375 | /* require the first path segment to be unfancy ASCII string */ |
| 376 | for( z = zV; z[0] && z[0] != '/' ;){ |
| 377 | if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++; |
| 378 | else break; |
| 379 | } |
| 380 | if( z[0] != 0 && z[0] != '/' ) |
| 381 | continue; |
| 382 | assert( nSubmenu < count(aSubmenu) ); |
| 383 | if(fossil_islower(zV[0])){ |
| 384 | aSubmenu[nSubmenu].zLabel = mprintf( "%s",zV); /* memory leak? */ |
| 385 | }else{ |
| 386 | aSubmenu[nSubmenu].zLabel = mprintf("✧%s",zV); /* maybe: ◦✧⸰⸎ ✨ */ |
| 387 | } |
| 388 | if( zQS[0] ){ |
| 389 | aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS); |
| 390 | }else{ |
| 391 | aSubmenu[nSubmenu].zLink = mprintf("%R/%s",zV); |
| 392 | } |
| 393 | nSubmenu++; |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | /* |
| 398 | ** Disable or enable the submenu |
| 399 | */ |
| 400 | void style_submenu_enable(int onOff){ |
| 401 |
+1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -593,10 +593,11 @@ | ||
| 593 | 593 | }else if( rid && g.perm.ApndWiki ){ |
| 594 | 594 | style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); |
| 595 | 595 | } |
| 596 | 596 | if( g.perm.Hyperlink ){ |
| 597 | 597 | style_submenu_element("History", "%R/whistory?name=%T", zPageName); |
| 598 | + style_submenu_parametric("wiki",7); | |
| 598 | 599 | } |
| 599 | 600 | } |
| 600 | 601 | if( !isPopup ){ |
| 601 | 602 | style_set_current_page("%T?name=%T", g.zPath, zPageName); |
| 602 | 603 | wiki_page_header(WIKITYPE_UNKNOWN, zPageName, ""); |
| 603 | 604 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -593,10 +593,11 @@ | |
| 593 | }else if( rid && g.perm.ApndWiki ){ |
| 594 | style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); |
| 595 | } |
| 596 | if( g.perm.Hyperlink ){ |
| 597 | style_submenu_element("History", "%R/whistory?name=%T", zPageName); |
| 598 | } |
| 599 | } |
| 600 | if( !isPopup ){ |
| 601 | style_set_current_page("%T?name=%T", g.zPath, zPageName); |
| 602 | wiki_page_header(WIKITYPE_UNKNOWN, zPageName, ""); |
| 603 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -593,10 +593,11 @@ | |
| 593 | }else if( rid && g.perm.ApndWiki ){ |
| 594 | style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName); |
| 595 | } |
| 596 | if( g.perm.Hyperlink ){ |
| 597 | style_submenu_element("History", "%R/whistory?name=%T", zPageName); |
| 598 | style_submenu_parametric("wiki",7); |
| 599 | } |
| 600 | } |
| 601 | if( !isPopup ){ |
| 602 | style_set_current_page("%T?name=%T", g.zPath, zPageName); |
| 603 | wiki_page_header(WIKITYPE_UNKNOWN, zPageName, ""); |
| 604 |
+5
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -71,10 +71,15 @@ | ||
| 71 | 71 | that prints the minimum SQLite version required by the current version |
| 72 | 72 | of Fossil. This might be used by integrators who insist on building |
| 73 | 73 | Fossil to link against the system SQLite library rather than the |
| 74 | 74 | built-in copy of SQLite, to verify that their system SQLite library |
| 75 | 75 | is recent enough. |
| 76 | + * Webpage that shows [/help?cmd=/whistory|history of a wiki page] | |
| 77 | + gained client-side UI to help with comparison between two arbitrary | |
| 78 | + versions of a wiki (by the means of anchoring a "baseline" version) | |
| 79 | + and the ability to squeeze several sequential edits made by the same | |
| 80 | + user into a single "recycled" row (the latest edit in that sequence). | |
| 76 | 81 | |
| 77 | 82 | <a name='v2_14'></a> |
| 78 | 83 | <h2>Changes for Version 2.14 (2021-01-20)</h2> |
| 79 | 84 | |
| 80 | 85 | * <b>Schema Update Notice #1:</b> |
| 81 | 86 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -71,10 +71,15 @@ | |
| 71 | that prints the minimum SQLite version required by the current version |
| 72 | of Fossil. This might be used by integrators who insist on building |
| 73 | Fossil to link against the system SQLite library rather than the |
| 74 | built-in copy of SQLite, to verify that their system SQLite library |
| 75 | is recent enough. |
| 76 | |
| 77 | <a name='v2_14'></a> |
| 78 | <h2>Changes for Version 2.14 (2021-01-20)</h2> |
| 79 | |
| 80 | * <b>Schema Update Notice #1:</b> |
| 81 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -71,10 +71,15 @@ | |
| 71 | that prints the minimum SQLite version required by the current version |
| 72 | of Fossil. This might be used by integrators who insist on building |
| 73 | Fossil to link against the system SQLite library rather than the |
| 74 | built-in copy of SQLite, to verify that their system SQLite library |
| 75 | is recent enough. |
| 76 | * Webpage that shows [/help?cmd=/whistory|history of a wiki page] |
| 77 | gained client-side UI to help with comparison between two arbitrary |
| 78 | versions of a wiki (by the means of anchoring a "baseline" version) |
| 79 | and the ability to squeeze several sequential edits made by the same |
| 80 | user into a single "recycled" row (the latest edit in that sequence). |
| 81 | |
| 82 | <a name='v2_14'></a> |
| 83 | <h2>Changes for Version 2.14 (2021-01-20)</h2> |
| 84 | |
| 85 | * <b>Schema Update Notice #1:</b> |
| 86 |
+26
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -242,10 +242,36 @@ | ||
| 242 | 242 | </blockquote> |
| 243 | 243 | |
| 244 | 244 | <p>"fossil diff" is the difference between your tree on disk now and as the tree was |
| 245 | 245 | when you did "fossil open". An open is the first checkout from a repository |
| 246 | 246 | into a new directory. </p> |
| 247 | + | |
| 248 | +<p>To see the most recent changes made to the repository by other users, use "fossil timeline" to | |
| 249 | +find out the most recent commit, and then "fossil diff" between that commit and the | |
| 250 | +current tree: </p> | |
| 251 | +<blockquote> | |
| 252 | +<b> | |
| 253 | + fossil timeline <br><tt> | |
| 254 | + === 2021-03-28 === <br> | |
| 255 | + 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br> | |
| 256 | + === 2021-03-27 === <br> | |
| 257 | + 23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk) <br> | |
| 258 | + ⋮ <br> | |
| 259 | + </tt><br> | |
| 260 | + fossil diff --from current --to ab975c6632 <br><tt> | |
| 261 | + Index: frobnicate.c<br> | |
| 262 | + ============================================================<br> | |
| 263 | + --- frobnicate.c<br> | |
| 264 | + +++ frobnicate.c<br> | |
| 265 | + @@ -1,10 +1,11 @@<br> | |
| 266 | + +/* made a change to the source file */<br> | |
| 267 | + # Original text<br> | |
| 268 | +</tt></b> | |
| 269 | +</blockquote> | |
| 270 | + | |
| 271 | +"current" is an alias for the most recent version, so the command | |
| 272 | +"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results. | |
| 247 | 273 | |
| 248 | 274 | <p>To commit your changes to a local-only repository:</p> |
| 249 | 275 | <blockquote> |
| 250 | 276 | <b> |
| 251 | 277 | fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt> |
| 252 | 278 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -242,10 +242,36 @@ | |
| 242 | </blockquote> |
| 243 | |
| 244 | <p>"fossil diff" is the difference between your tree on disk now and as the tree was |
| 245 | when you did "fossil open". An open is the first checkout from a repository |
| 246 | into a new directory. </p> |
| 247 | |
| 248 | <p>To commit your changes to a local-only repository:</p> |
| 249 | <blockquote> |
| 250 | <b> |
| 251 | fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt> |
| 252 |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -242,10 +242,36 @@ | |
| 242 | </blockquote> |
| 243 | |
| 244 | <p>"fossil diff" is the difference between your tree on disk now and as the tree was |
| 245 | when you did "fossil open". An open is the first checkout from a repository |
| 246 | into a new directory. </p> |
| 247 | |
| 248 | <p>To see the most recent changes made to the repository by other users, use "fossil timeline" to |
| 249 | find out the most recent commit, and then "fossil diff" between that commit and the |
| 250 | current tree: </p> |
| 251 | <blockquote> |
| 252 | <b> |
| 253 | fossil timeline <br><tt> |
| 254 | === 2021-03-28 === <br> |
| 255 | 03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br> |
| 256 | === 2021-03-27 === <br> |
| 257 | 23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk) <br> |
| 258 | ⋮ <br> |
| 259 | </tt><br> |
| 260 | fossil diff --from current --to ab975c6632 <br><tt> |
| 261 | Index: frobnicate.c<br> |
| 262 | ============================================================<br> |
| 263 | --- frobnicate.c<br> |
| 264 | +++ frobnicate.c<br> |
| 265 | @@ -1,10 +1,11 @@<br> |
| 266 | +/* made a change to the source file */<br> |
| 267 | # Original text<br> |
| 268 | </tt></b> |
| 269 | </blockquote> |
| 270 | |
| 271 | "current" is an alias for the most recent version, so the command |
| 272 | "fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results. |
| 273 | |
| 274 | <p>To commit your changes to a local-only repository:</p> |
| 275 | <blockquote> |
| 276 | <b> |
| 277 | fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt> |
| 278 |
+2
-8
| --- www/rebaseharm.md | ||
| +++ www/rebaseharm.md | ||
| @@ -293,22 +293,16 @@ | ||
| 293 | 293 | unique timestamps for C3' and C5' but then you lose the information |
| 294 | 294 | about when those check-ins were originally created, which can make |
| 295 | 295 | historical analysis of changes more difficult. It might also |
| 296 | 296 | complicate the legal defense of prior art claims. |
| 297 | 297 | |
| 298 | -## <a name="lying"></a>5.0 Rebasing is lying about the project history | |
| 298 | +## <a name="lying"></a>5.0 Rebase misrepresents the project history | |
| 299 | 299 | |
| 300 | 300 | By discarding parentage information, rebase attempts to deceive the |
| 301 | 301 | reader about how the code actually came together. |
| 302 | 302 | |
| 303 | -You may be tempted to dismiss this as an anti-Git opinion on a Fossil | |
| 304 | -web site, but it’s spelled out just like that [in the Git rebase | |
| 305 | -documentation][gitrebase]. It speaks of “lying,” “telling stories,” | |
| 306 | -and “blasphemy.” | |
| 307 | - | |
| 308 | -That section of the Git docs is contrasting rebase with merge, which we | |
| 309 | -cover [above](#cap-loss), but Git’s rebase feature is more than just an | |
| 303 | +Git’s rebase feature is more than just an | |
| 310 | 304 | alternative to merging: it also provides mechanisms for changing the |
| 311 | 305 | project history in order to make editorial changes. Fossil shows that |
| 312 | 306 | you can get similar effects without modifying historical records, |
| 313 | 307 | allowing users to: |
| 314 | 308 | |
| 315 | 309 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -293,22 +293,16 @@ | |
| 293 | unique timestamps for C3' and C5' but then you lose the information |
| 294 | about when those check-ins were originally created, which can make |
| 295 | historical analysis of changes more difficult. It might also |
| 296 | complicate the legal defense of prior art claims. |
| 297 | |
| 298 | ## <a name="lying"></a>5.0 Rebasing is lying about the project history |
| 299 | |
| 300 | By discarding parentage information, rebase attempts to deceive the |
| 301 | reader about how the code actually came together. |
| 302 | |
| 303 | You may be tempted to dismiss this as an anti-Git opinion on a Fossil |
| 304 | web site, but it’s spelled out just like that [in the Git rebase |
| 305 | documentation][gitrebase]. It speaks of “lying,” “telling stories,” |
| 306 | and “blasphemy.” |
| 307 | |
| 308 | That section of the Git docs is contrasting rebase with merge, which we |
| 309 | cover [above](#cap-loss), but Git’s rebase feature is more than just an |
| 310 | alternative to merging: it also provides mechanisms for changing the |
| 311 | project history in order to make editorial changes. Fossil shows that |
| 312 | you can get similar effects without modifying historical records, |
| 313 | allowing users to: |
| 314 | |
| 315 |
| --- www/rebaseharm.md | |
| +++ www/rebaseharm.md | |
| @@ -293,22 +293,16 @@ | |
| 293 | unique timestamps for C3' and C5' but then you lose the information |
| 294 | about when those check-ins were originally created, which can make |
| 295 | historical analysis of changes more difficult. It might also |
| 296 | complicate the legal defense of prior art claims. |
| 297 | |
| 298 | ## <a name="lying"></a>5.0 Rebase misrepresents the project history |
| 299 | |
| 300 | By discarding parentage information, rebase attempts to deceive the |
| 301 | reader about how the code actually came together. |
| 302 | |
| 303 | Git’s rebase feature is more than just an |
| 304 | alternative to merging: it also provides mechanisms for changing the |
| 305 | project history in order to make editorial changes. Fossil shows that |
| 306 | you can get similar effects without modifying historical records, |
| 307 | allowing users to: |
| 308 | |
| 309 |