Fossil SCM
Merge from trunk.
Commit
8add3c418ac8cdeb1310e3b533e5d11c416ea09b3320db17e159ba55c317ff73
Parent
5a2e00424e3ef4a…
29 files changed
+6
-6
+191
-178
+1
-1
+3
-4
+1
-1
+8
-1
+18
-2
+10
-1
+149
-12
+2
-1
+12
-8
+119
-15
+50
-17
+14
-1
+1
-1
+2
+31
-4
+11
-4
+55
-2
+25
+28
-25
+6
-4
+1
+1
-1
+16
-5
+8
+197
-88
+8
~
Dockerfile
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
src/checkout.c
~
src/db.c
~
src/doc.c
~
src/encode.c
~
src/forum.c
~
src/fossil.diff.js
~
src/info.c
~
src/main.c
~
src/merge.c
~
src/name.c
~
src/patch.c
~
src/printf.c
~
src/sitemap.c
~
src/style.c
~
src/timeline.c
~
src/update.c
~
src/xfer.c
~
test/merge1.test
~
test/merge3.test
~
test/merge4.test
~
test/tester.tcl
~
test/update.test
~
tools/translate.c
~
www/changes.wiki
~
www/ssl-server.md
~
www/sync.wiki
+6
-6
| --- Dockerfile | ||
| +++ Dockerfile | ||
| @@ -3,19 +3,19 @@ | ||
| 3 | 3 | |
| 4 | 4 | ## --------------------------------------------------------------------- |
| 5 | 5 | ## STAGE 1: Build static Fossil binary |
| 6 | 6 | ## --------------------------------------------------------------------- |
| 7 | 7 | |
| 8 | -### We aren't pinning to a more stable version of Alpine because we want | |
| 8 | +### We don't pin a more stable version of our base layer because we want | |
| 9 | 9 | ### to build with the latest tools and libraries available in case they |
| 10 | 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | 13 | FROM alpine:latest AS bld |
| 14 | 14 | WORKDIR /fsl |
| 15 | 15 | |
| 16 | -### Bake the basic Alpine Linux into a base layer so it only changes | |
| 16 | +### Bake the build-time userland into a base layer so it only changes | |
| 17 | 17 | ### when the upstream image is updated or we change the package set. |
| 18 | 18 | RUN set -x \ |
| 19 | 19 | && apk update \ |
| 20 | 20 | && apk upgrade --no-cache \ |
| 21 | 21 | && apk add --no-cache \ |
| @@ -23,19 +23,19 @@ | ||
| 23 | 23 | linux-headers musl-dev \ |
| 24 | 24 | openssl-dev openssl-libs-static \ |
| 25 | 25 | zlib-dev zlib-static |
| 26 | 26 | |
| 27 | 27 | ### Build Fossil as a separate layer so we don't have to rebuild the |
| 28 | -### Alpine environment for each iteration of Fossil's dev cycle. | |
| 28 | +### userland for each iteration of Fossil's dev cycle. | |
| 29 | 29 | ### |
| 30 | 30 | ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs |
| 31 | 31 | ### automatically when you give it a local file name but not if you give |
| 32 | 32 | ### it a /tarball URL! It matters because we default to a URL in case |
| 33 | 33 | ### you're building outside a Fossil checkout, but when building via the |
| 34 | -### container-image target, we avoid a costly hit on fossil-scm.org | |
| 35 | -### by leveraging its DVCS nature via the "tarball" command and passing | |
| 36 | -### the resulting file's name in. | |
| 34 | +### container-image target, we avoid a costly hit on fossil-scm.org by | |
| 35 | +### leveraging its DVCS nature via the "tarball" command and passing the | |
| 36 | +### resulting file's name in. | |
| 37 | 37 | ARG FSLCFG="" |
| 38 | 38 | ARG FSLVER="trunk" |
| 39 | 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | 40 | ENV FSLSTB=/fsl/src.tar.gz |
| 41 | 41 | ADD $FSLURL $FSLSTB |
| 42 | 42 |
| --- Dockerfile | |
| +++ Dockerfile | |
| @@ -3,19 +3,19 @@ | |
| 3 | |
| 4 | ## --------------------------------------------------------------------- |
| 5 | ## STAGE 1: Build static Fossil binary |
| 6 | ## --------------------------------------------------------------------- |
| 7 | |
| 8 | ### We aren't pinning to a more stable version of Alpine because we want |
| 9 | ### to build with the latest tools and libraries available in case they |
| 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | FROM alpine:latest AS bld |
| 14 | WORKDIR /fsl |
| 15 | |
| 16 | ### Bake the basic Alpine Linux into a base layer so it only changes |
| 17 | ### when the upstream image is updated or we change the package set. |
| 18 | RUN set -x \ |
| 19 | && apk update \ |
| 20 | && apk upgrade --no-cache \ |
| 21 | && apk add --no-cache \ |
| @@ -23,19 +23,19 @@ | |
| 23 | linux-headers musl-dev \ |
| 24 | openssl-dev openssl-libs-static \ |
| 25 | zlib-dev zlib-static |
| 26 | |
| 27 | ### Build Fossil as a separate layer so we don't have to rebuild the |
| 28 | ### Alpine environment for each iteration of Fossil's dev cycle. |
| 29 | ### |
| 30 | ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs |
| 31 | ### automatically when you give it a local file name but not if you give |
| 32 | ### it a /tarball URL! It matters because we default to a URL in case |
| 33 | ### you're building outside a Fossil checkout, but when building via the |
| 34 | ### container-image target, we avoid a costly hit on fossil-scm.org |
| 35 | ### by leveraging its DVCS nature via the "tarball" command and passing |
| 36 | ### the resulting file's name in. |
| 37 | ARG FSLCFG="" |
| 38 | ARG FSLVER="trunk" |
| 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | ENV FSLSTB=/fsl/src.tar.gz |
| 41 | ADD $FSLURL $FSLSTB |
| 42 |
| --- Dockerfile | |
| +++ Dockerfile | |
| @@ -3,19 +3,19 @@ | |
| 3 | |
| 4 | ## --------------------------------------------------------------------- |
| 5 | ## STAGE 1: Build static Fossil binary |
| 6 | ## --------------------------------------------------------------------- |
| 7 | |
| 8 | ### We don't pin a more stable version of our base layer because we want |
| 9 | ### to build with the latest tools and libraries available in case they |
| 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | FROM alpine:latest AS bld |
| 14 | WORKDIR /fsl |
| 15 | |
| 16 | ### Bake the build-time userland into a base layer so it only changes |
| 17 | ### when the upstream image is updated or we change the package set. |
| 18 | RUN set -x \ |
| 19 | && apk update \ |
| 20 | && apk upgrade --no-cache \ |
| 21 | && apk add --no-cache \ |
| @@ -23,19 +23,19 @@ | |
| 23 | linux-headers musl-dev \ |
| 24 | openssl-dev openssl-libs-static \ |
| 25 | zlib-dev zlib-static |
| 26 | |
| 27 | ### Build Fossil as a separate layer so we don't have to rebuild the |
| 28 | ### userland for each iteration of Fossil's dev cycle. |
| 29 | ### |
| 30 | ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs |
| 31 | ### automatically when you give it a local file name but not if you give |
| 32 | ### it a /tarball URL! It matters because we default to a URL in case |
| 33 | ### you're building outside a Fossil checkout, but when building via the |
| 34 | ### container-image target, we avoid a costly hit on fossil-scm.org by |
| 35 | ### leveraging its DVCS nature via the "tarball" command and passing the |
| 36 | ### resulting file's name in. |
| 37 | ARG FSLCFG="" |
| 38 | ARG FSLVER="trunk" |
| 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | ENV FSLSTB=/fsl/src.tar.gz |
| 41 | ADD $FSLURL $FSLSTB |
| 42 |
+191
-178
| --- extsrc/sqlite3.c | ||
| +++ extsrc/sqlite3.c | ||
| @@ -16,11 +16,11 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: | |
| 21 | +** e6c30ee52c5cdc193804cec63374d558b45e with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | ||
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.48.0" |
| 469 | 469 | #define SQLITE_VERSION_NUMBER 3048000 |
| 470 | -#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" | |
| 470 | +#define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf" | |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| @@ -14046,13 +14046,17 @@ | ||
| 14046 | 14046 | # define SQLITE_MAX_VDBE_OP 250000000 |
| 14047 | 14047 | #endif |
| 14048 | 14048 | |
| 14049 | 14049 | /* |
| 14050 | 14050 | ** The maximum number of arguments to an SQL function. |
| 14051 | +** | |
| 14052 | +** This value has a hard upper limit of 32767 due to storage | |
| 14053 | +** constraints (it needs to fit inside a i16). We keep it | |
| 14054 | +** lower than that to prevent abuse. | |
| 14051 | 14055 | */ |
| 14052 | 14056 | #ifndef SQLITE_MAX_FUNCTION_ARG |
| 14053 | -# define SQLITE_MAX_FUNCTION_ARG 127 | |
| 14057 | +# define SQLITE_MAX_FUNCTION_ARG 1000 | |
| 14054 | 14058 | #endif |
| 14055 | 14059 | |
| 14056 | 14060 | /* |
| 14057 | 14061 | ** The suggested maximum number of in-memory pages to use for |
| 14058 | 14062 | ** the main database table and for temporary tables. |
| @@ -16049,10 +16053,26 @@ | ||
| 16049 | 16053 | #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ |
| 16050 | 16054 | #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ |
| 16051 | 16055 | #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ |
| 16052 | 16056 | #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ |
| 16053 | 16057 | #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ |
| 16058 | + | |
| 16059 | +#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL) | |
| 16060 | + | |
| 16061 | +/* | |
| 16062 | +** The argument to this macro is a file descriptor (type sqlite3_file*). | |
| 16063 | +** Return 0 if it is not open, or non-zero (but not 1) if it is. | |
| 16064 | +** | |
| 16065 | +** This is so that expressions can be written as: | |
| 16066 | +** | |
| 16067 | +** if( isOpen(pPager->jfd) ){ ... | |
| 16068 | +** | |
| 16069 | +** instead of | |
| 16070 | +** | |
| 16071 | +** if( pPager->jfd->pMethods ){ ... | |
| 16072 | +*/ | |
| 16073 | +#define isOpen(pFd) ((pFd)->pMethods!=0) | |
| 16054 | 16074 | |
| 16055 | 16075 | /* |
| 16056 | 16076 | ** Flags that make up the mask passed to sqlite3PagerGet(). |
| 16057 | 16077 | */ |
| 16058 | 16078 | #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ |
| @@ -18113,11 +18133,11 @@ | ||
| 18113 | 18133 | ** |
| 18114 | 18134 | ** The u.pHash field is used by the global built-ins. The u.pDestructor |
| 18115 | 18135 | ** field is used by per-connection app-def functions. |
| 18116 | 18136 | */ |
| 18117 | 18137 | struct FuncDef { |
| 18118 | - i8 nArg; /* Number of arguments. -1 means unlimited */ | |
| 18138 | + i16 nArg; /* Number of arguments. -1 means unlimited */ | |
| 18119 | 18139 | u32 funcFlags; /* Some combination of SQLITE_FUNC_* */ |
| 18120 | 18140 | void *pUserData; /* User data parameter */ |
| 18121 | 18141 | FuncDef *pNext; /* Next function with same name */ |
| 18122 | 18142 | void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */ |
| 18123 | 18143 | void (*xFinalize)(sqlite3_context*); /* Agg finalizer */ |
| @@ -23708,11 +23728,11 @@ | ||
| 23708 | 23728 | Vdbe *pVdbe; /* The VM that owns this context */ |
| 23709 | 23729 | int iOp; /* Instruction number of OP_Function */ |
| 23710 | 23730 | int isError; /* Error code returned by the function. */ |
| 23711 | 23731 | u8 enc; /* Encoding to use for results */ |
| 23712 | 23732 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23713 | - u8 argc; /* Number of arguments */ | |
| 23733 | + u16 argc; /* Number of arguments */ | |
| 23714 | 23734 | sqlite3_value *argv[1]; /* Argument set */ |
| 23715 | 23735 | }; |
| 23716 | 23736 | |
| 23717 | 23737 | /* A bitfield type for use inside of structures. Always follow with :N where |
| 23718 | 23738 | ** N is the number of bits. |
| @@ -58014,24 +58034,10 @@ | ||
| 58014 | 58034 | # define USEFETCH(x) ((x)->bUseFetch) |
| 58015 | 58035 | #else |
| 58016 | 58036 | # define USEFETCH(x) 0 |
| 58017 | 58037 | #endif |
| 58018 | 58038 | |
| 58019 | -/* | |
| 58020 | -** The argument to this macro is a file descriptor (type sqlite3_file*). | |
| 58021 | -** Return 0 if it is not open, or non-zero (but not 1) if it is. | |
| 58022 | -** | |
| 58023 | -** This is so that expressions can be written as: | |
| 58024 | -** | |
| 58025 | -** if( isOpen(pPager->jfd) ){ ... | |
| 58026 | -** | |
| 58027 | -** instead of | |
| 58028 | -** | |
| 58029 | -** if( pPager->jfd->pMethods ){ ... | |
| 58030 | -*/ | |
| 58031 | -#define isOpen(pFd) ((pFd)->pMethods!=0) | |
| 58032 | - | |
| 58033 | 58039 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58034 | 58040 | /* |
| 58035 | 58041 | ** Return true if page pgno can be read directly from the database file |
| 58036 | 58042 | ** by the b-tree layer. This is the case if: |
| 58037 | 58043 | ** |
| @@ -59313,11 +59319,11 @@ | ||
| 59313 | 59319 | rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); |
| 59314 | 59320 | } |
| 59315 | 59321 | } |
| 59316 | 59322 | pPager->journalOff = 0; |
| 59317 | 59323 | }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST |
| 59318 | - || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) | |
| 59324 | + || (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL) | |
| 59319 | 59325 | ){ |
| 59320 | 59326 | rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); |
| 59321 | 59327 | pPager->journalOff = 0; |
| 59322 | 59328 | }else{ |
| 59323 | 59329 | /* This branch may be executed with Pager.journalMode==MEMORY if |
| @@ -68023,15 +68029,11 @@ | ||
| 68023 | 68029 | ** so it takes care to hold an exclusive lock on the corresponding |
| 68024 | 68030 | ** WAL_READ_LOCK() while changing values. |
| 68025 | 68031 | */ |
| 68026 | 68032 | static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ |
| 68027 | 68033 | volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ |
| 68028 | - u32 mxReadMark; /* Largest aReadMark[] value */ | |
| 68029 | - int mxI; /* Index of largest aReadMark[] value */ | |
| 68030 | - int i; /* Loop counter */ | |
| 68031 | 68034 | int rc = SQLITE_OK; /* Return code */ |
| 68032 | - u32 mxFrame; /* Wal frame to lock to */ | |
| 68033 | 68035 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68034 | 68036 | int nBlockTmout = 0; |
| 68035 | 68037 | #endif |
| 68036 | 68038 | |
| 68037 | 68039 | assert( pWal->readLock<0 ); /* Not currently locked */ |
| @@ -68133,145 +68135,151 @@ | ||
| 68133 | 68135 | |
| 68134 | 68136 | assert( pWal->nWiData>0 ); |
| 68135 | 68137 | assert( pWal->apWiData[0]!=0 ); |
| 68136 | 68138 | pInfo = walCkptInfo(pWal); |
| 68137 | 68139 | SEH_INJECT_FAULT; |
| 68138 | - if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame | |
| 68139 | -#ifdef SQLITE_ENABLE_SNAPSHOT | |
| 68140 | - && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) | |
| 68141 | -#endif | |
| 68142 | - ){ | |
| 68143 | - /* The WAL has been completely backfilled (or it is empty). | |
| 68144 | - ** and can be safely ignored. | |
| 68145 | - */ | |
| 68146 | - rc = walLockShared(pWal, WAL_READ_LOCK(0)); | |
| 68147 | - walShmBarrier(pWal); | |
| 68148 | - if( rc==SQLITE_OK ){ | |
| 68149 | - if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ | |
| 68150 | - /* It is not safe to allow the reader to continue here if frames | |
| 68151 | - ** may have been appended to the log before READ_LOCK(0) was obtained. | |
| 68152 | - ** When holding READ_LOCK(0), the reader ignores the entire log file, | |
| 68153 | - ** which implies that the database file contains a trustworthy | |
| 68154 | - ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from | |
| 68155 | - ** happening, this is usually correct. | |
| 68156 | - ** | |
| 68157 | - ** However, if frames have been appended to the log (or if the log | |
| 68158 | - ** is wrapped and written for that matter) before the READ_LOCK(0) | |
| 68159 | - ** is obtained, that is not necessarily true. A checkpointer may | |
| 68160 | - ** have started to backfill the appended frames but crashed before | |
| 68161 | - ** it finished. Leaving a corrupt image in the database file. | |
| 68162 | - */ | |
| 68163 | - walUnlockShared(pWal, WAL_READ_LOCK(0)); | |
| 68164 | - return WAL_RETRY; | |
| 68165 | - } | |
| 68166 | - pWal->readLock = 0; | |
| 68167 | - return SQLITE_OK; | |
| 68168 | - }else if( rc!=SQLITE_BUSY ){ | |
| 68169 | - return rc; | |
| 68170 | - } | |
| 68171 | - } | |
| 68172 | - | |
| 68173 | - /* If we get this far, it means that the reader will want to use | |
| 68174 | - ** the WAL to get at content from recent commits. The job now is | |
| 68175 | - ** to select one of the aReadMark[] entries that is closest to | |
| 68176 | - ** but not exceeding pWal->hdr.mxFrame and lock that entry. | |
| 68177 | - */ | |
| 68178 | - mxReadMark = 0; | |
| 68179 | - mxI = 0; | |
| 68180 | - mxFrame = pWal->hdr.mxFrame; | |
| 68181 | -#ifdef SQLITE_ENABLE_SNAPSHOT | |
| 68182 | - if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){ | |
| 68183 | - mxFrame = pWal->pSnapshot->mxFrame; | |
| 68184 | - } | |
| 68185 | -#endif | |
| 68186 | - for(i=1; i<WAL_NREADER; i++){ | |
| 68187 | - u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; | |
| 68188 | - if( mxReadMark<=thisMark && thisMark<=mxFrame ){ | |
| 68189 | - assert( thisMark!=READMARK_NOT_USED ); | |
| 68190 | - mxReadMark = thisMark; | |
| 68191 | - mxI = i; | |
| 68192 | - } | |
| 68193 | - } | |
| 68194 | - if( (pWal->readOnly & WAL_SHM_RDONLY)==0 | |
| 68195 | - && (mxReadMark<mxFrame || mxI==0) | |
| 68196 | - ){ | |
| 68197 | - for(i=1; i<WAL_NREADER; i++){ | |
| 68198 | - rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 68199 | - if( rc==SQLITE_OK ){ | |
| 68200 | - AtomicStore(pInfo->aReadMark+i,mxFrame); | |
| 68201 | - mxReadMark = mxFrame; | |
| 68202 | - mxI = i; | |
| 68203 | - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 68204 | - break; | |
| 68205 | - }else if( rc!=SQLITE_BUSY ){ | |
| 68206 | - return rc; | |
| 68207 | - } | |
| 68208 | - } | |
| 68209 | - } | |
| 68210 | - if( mxI==0 ){ | |
| 68211 | - assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); | |
| 68212 | - return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; | |
| 68213 | - } | |
| 68214 | - | |
| 68215 | - (void)walEnableBlockingMs(pWal, nBlockTmout); | |
| 68216 | - rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); | |
| 68217 | - walDisableBlocking(pWal); | |
| 68218 | - if( rc ){ | |
| 68219 | -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 68220 | - if( rc==SQLITE_BUSY_TIMEOUT ){ | |
| 68221 | - *pCnt |= WAL_RETRY_BLOCKED_MASK; | |
| 68222 | - } | |
| 68223 | -#else | |
| 68224 | - assert( rc!=SQLITE_BUSY_TIMEOUT ); | |
| 68225 | -#endif | |
| 68226 | - assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT ); | |
| 68227 | - return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; | |
| 68228 | - } | |
| 68229 | - /* Now that the read-lock has been obtained, check that neither the | |
| 68230 | - ** value in the aReadMark[] array or the contents of the wal-index | |
| 68231 | - ** header have changed. | |
| 68232 | - ** | |
| 68233 | - ** It is necessary to check that the wal-index header did not change | |
| 68234 | - ** between the time it was read and when the shared-lock was obtained | |
| 68235 | - ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility | |
| 68236 | - ** that the log file may have been wrapped by a writer, or that frames | |
| 68237 | - ** that occur later in the log than pWal->hdr.mxFrame may have been | |
| 68238 | - ** copied into the database by a checkpointer. If either of these things | |
| 68239 | - ** happened, then reading the database with the current value of | |
| 68240 | - ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry | |
| 68241 | - ** instead. | |
| 68242 | - ** | |
| 68243 | - ** Before checking that the live wal-index header has not changed | |
| 68244 | - ** since it was read, set Wal.minFrame to the first frame in the wal | |
| 68245 | - ** file that has not yet been checkpointed. This client will not need | |
| 68246 | - ** to read any frames earlier than minFrame from the wal file - they | |
| 68247 | - ** can be safely read directly from the database file. | |
| 68248 | - ** | |
| 68249 | - ** Because a ShmBarrier() call is made between taking the copy of | |
| 68250 | - ** nBackfill and checking that the wal-header in shared-memory still | |
| 68251 | - ** matches the one cached in pWal->hdr, it is guaranteed that the | |
| 68252 | - ** checkpointer that set nBackfill was not working with a wal-index | |
| 68253 | - ** header newer than that cached in pWal->hdr. If it were, that could | |
| 68254 | - ** cause a problem. The checkpointer could omit to checkpoint | |
| 68255 | - ** a version of page X that lies before pWal->minFrame (call that version | |
| 68256 | - ** A) on the basis that there is a newer version (version B) of the same | |
| 68257 | - ** page later in the wal file. But if version B happens to like past | |
| 68258 | - ** frame pWal->hdr.mxFrame - then the client would incorrectly assume | |
| 68259 | - ** that it can read version A from the database file. However, since | |
| 68260 | - ** we can guarantee that the checkpointer that set nBackfill could not | |
| 68261 | - ** see any pages past pWal->hdr.mxFrame, this problem does not come up. | |
| 68262 | - */ | |
| 68263 | - pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; | |
| 68264 | - walShmBarrier(pWal); | |
| 68265 | - if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark | |
| 68266 | - || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) | |
| 68267 | - ){ | |
| 68268 | - walUnlockShared(pWal, WAL_READ_LOCK(mxI)); | |
| 68269 | - return WAL_RETRY; | |
| 68270 | - }else{ | |
| 68271 | - assert( mxReadMark<=pWal->hdr.mxFrame ); | |
| 68272 | - pWal->readLock = (i16)mxI; | |
| 68140 | + { | |
| 68141 | + u32 mxReadMark; /* Largest aReadMark[] value */ | |
| 68142 | + int mxI; /* Index of largest aReadMark[] value */ | |
| 68143 | + int i; /* Loop counter */ | |
| 68144 | + u32 mxFrame; /* Wal frame to lock to */ | |
| 68145 | + if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame | |
| 68146 | +#ifdef SQLITE_ENABLE_SNAPSHOT | |
| 68147 | + && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) | |
| 68148 | +#endif | |
| 68149 | + ){ | |
| 68150 | + /* The WAL has been completely backfilled (or it is empty). | |
| 68151 | + ** and can be safely ignored. | |
| 68152 | + */ | |
| 68153 | + rc = walLockShared(pWal, WAL_READ_LOCK(0)); | |
| 68154 | + walShmBarrier(pWal); | |
| 68155 | + if( rc==SQLITE_OK ){ | |
| 68156 | + if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){ | |
| 68157 | + /* It is not safe to allow the reader to continue here if frames | |
| 68158 | + ** may have been appended to the log before READ_LOCK(0) was obtained. | |
| 68159 | + ** When holding READ_LOCK(0), the reader ignores the entire log file, | |
| 68160 | + ** which implies that the database file contains a trustworthy | |
| 68161 | + ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from | |
| 68162 | + ** happening, this is usually correct. | |
| 68163 | + ** | |
| 68164 | + ** However, if frames have been appended to the log (or if the log | |
| 68165 | + ** is wrapped and written for that matter) before the READ_LOCK(0) | |
| 68166 | + ** is obtained, that is not necessarily true. A checkpointer may | |
| 68167 | + ** have started to backfill the appended frames but crashed before | |
| 68168 | + ** it finished. Leaving a corrupt image in the database file. | |
| 68169 | + */ | |
| 68170 | + walUnlockShared(pWal, WAL_READ_LOCK(0)); | |
| 68171 | + return WAL_RETRY; | |
| 68172 | + } | |
| 68173 | + pWal->readLock = 0; | |
| 68174 | + return SQLITE_OK; | |
| 68175 | + }else if( rc!=SQLITE_BUSY ){ | |
| 68176 | + return rc; | |
| 68177 | + } | |
| 68178 | + } | |
| 68179 | + | |
| 68180 | + /* If we get this far, it means that the reader will want to use | |
| 68181 | + ** the WAL to get at content from recent commits. The job now is | |
| 68182 | + ** to select one of the aReadMark[] entries that is closest to | |
| 68183 | + ** but not exceeding pWal->hdr.mxFrame and lock that entry. | |
| 68184 | + */ | |
| 68185 | + mxReadMark = 0; | |
| 68186 | + mxI = 0; | |
| 68187 | + mxFrame = pWal->hdr.mxFrame; | |
| 68188 | +#ifdef SQLITE_ENABLE_SNAPSHOT | |
| 68189 | + if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){ | |
| 68190 | + mxFrame = pWal->pSnapshot->mxFrame; | |
| 68191 | + } | |
| 68192 | +#endif | |
| 68193 | + for(i=1; i<WAL_NREADER; i++){ | |
| 68194 | + u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; | |
| 68195 | + if( mxReadMark<=thisMark && thisMark<=mxFrame ){ | |
| 68196 | + assert( thisMark!=READMARK_NOT_USED ); | |
| 68197 | + mxReadMark = thisMark; | |
| 68198 | + mxI = i; | |
| 68199 | + } | |
| 68200 | + } | |
| 68201 | + if( (pWal->readOnly & WAL_SHM_RDONLY)==0 | |
| 68202 | + && (mxReadMark<mxFrame || mxI==0) | |
| 68203 | + ){ | |
| 68204 | + for(i=1; i<WAL_NREADER; i++){ | |
| 68205 | + rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 68206 | + if( rc==SQLITE_OK ){ | |
| 68207 | + AtomicStore(pInfo->aReadMark+i,mxFrame); | |
| 68208 | + mxReadMark = mxFrame; | |
| 68209 | + mxI = i; | |
| 68210 | + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 68211 | + break; | |
| 68212 | + }else if( rc!=SQLITE_BUSY ){ | |
| 68213 | + return rc; | |
| 68214 | + } | |
| 68215 | + } | |
| 68216 | + } | |
| 68217 | + if( mxI==0 ){ | |
| 68218 | + assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); | |
| 68219 | + return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; | |
| 68220 | + } | |
| 68221 | + | |
| 68222 | + (void)walEnableBlockingMs(pWal, nBlockTmout); | |
| 68223 | + rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); | |
| 68224 | + walDisableBlocking(pWal); | |
| 68225 | + if( rc ){ | |
| 68226 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 68227 | + if( rc==SQLITE_BUSY_TIMEOUT ){ | |
| 68228 | + *pCnt |= WAL_RETRY_BLOCKED_MASK; | |
| 68229 | + } | |
| 68230 | +#else | |
| 68231 | + assert( rc!=SQLITE_BUSY_TIMEOUT ); | |
| 68232 | +#endif | |
| 68233 | + assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT); | |
| 68234 | + return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; | |
| 68235 | + } | |
| 68236 | + /* Now that the read-lock has been obtained, check that neither the | |
| 68237 | + ** value in the aReadMark[] array or the contents of the wal-index | |
| 68238 | + ** header have changed. | |
| 68239 | + ** | |
| 68240 | + ** It is necessary to check that the wal-index header did not change | |
| 68241 | + ** between the time it was read and when the shared-lock was obtained | |
| 68242 | + ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility | |
| 68243 | + ** that the log file may have been wrapped by a writer, or that frames | |
| 68244 | + ** that occur later in the log than pWal->hdr.mxFrame may have been | |
| 68245 | + ** copied into the database by a checkpointer. If either of these things | |
| 68246 | + ** happened, then reading the database with the current value of | |
| 68247 | + ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry | |
| 68248 | + ** instead. | |
| 68249 | + ** | |
| 68250 | + ** Before checking that the live wal-index header has not changed | |
| 68251 | + ** since it was read, set Wal.minFrame to the first frame in the wal | |
| 68252 | + ** file that has not yet been checkpointed. This client will not need | |
| 68253 | + ** to read any frames earlier than minFrame from the wal file - they | |
| 68254 | + ** can be safely read directly from the database file. | |
| 68255 | + ** | |
| 68256 | + ** Because a ShmBarrier() call is made between taking the copy of | |
| 68257 | + ** nBackfill and checking that the wal-header in shared-memory still | |
| 68258 | + ** matches the one cached in pWal->hdr, it is guaranteed that the | |
| 68259 | + ** checkpointer that set nBackfill was not working with a wal-index | |
| 68260 | + ** header newer than that cached in pWal->hdr. If it were, that could | |
| 68261 | + ** cause a problem. The checkpointer could omit to checkpoint | |
| 68262 | + ** a version of page X that lies before pWal->minFrame (call that version | |
| 68263 | + ** A) on the basis that there is a newer version (version B) of the same | |
| 68264 | + ** page later in the wal file. But if version B happens to like past | |
| 68265 | + ** frame pWal->hdr.mxFrame - then the client would incorrectly assume | |
| 68266 | + ** that it can read version A from the database file. However, since | |
| 68267 | + ** we can guarantee that the checkpointer that set nBackfill could not | |
| 68268 | + ** see any pages past pWal->hdr.mxFrame, this problem does not come up. | |
| 68269 | + */ | |
| 68270 | + pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; | |
| 68271 | + walShmBarrier(pWal); | |
| 68272 | + if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark | |
| 68273 | + || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) | |
| 68274 | + ){ | |
| 68275 | + walUnlockShared(pWal, WAL_READ_LOCK(mxI)); | |
| 68276 | + return WAL_RETRY; | |
| 68277 | + }else{ | |
| 68278 | + assert( mxReadMark<=pWal->hdr.mxFrame ); | |
| 68279 | + pWal->readLock = (i16)mxI; | |
| 68280 | + } | |
| 68273 | 68281 | } |
| 68274 | 68282 | return rc; |
| 68275 | 68283 | } |
| 68276 | 68284 | |
| 68277 | 68285 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| @@ -91889,11 +91897,11 @@ | ||
| 91889 | 91897 | ** |
| 91890 | 91898 | ** sqlite3_column_int() |
| 91891 | 91899 | ** sqlite3_column_int64() |
| 91892 | 91900 | ** sqlite3_column_text() |
| 91893 | 91901 | ** sqlite3_column_text16() |
| 91894 | -** sqlite3_column_real() | |
| 91902 | +** sqlite3_column_double() | |
| 91895 | 91903 | ** sqlite3_column_bytes() |
| 91896 | 91904 | ** sqlite3_column_bytes16() |
| 91897 | 91905 | ** sqlite3_column_blob() |
| 91898 | 91906 | */ |
| 91899 | 91907 | static void columnMallocFailure(sqlite3_stmt *pStmt) |
| @@ -109896,11 +109904,11 @@ | ||
| 109896 | 109904 | p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight); |
| 109897 | 109905 | } |
| 109898 | 109906 | p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); |
| 109899 | 109907 | addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, |
| 109900 | 109908 | (void*)p4, P4_COLLSEQ); |
| 109901 | - sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5); | |
| 109909 | + sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5); | |
| 109902 | 109910 | return addr; |
| 109903 | 109911 | } |
| 109904 | 109912 | |
| 109905 | 109913 | /* |
| 109906 | 109914 | ** Return true if expression pExpr is a vector, or false otherwise. |
| @@ -129732,11 +129740,11 @@ | ||
| 129732 | 129740 | || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) |
| 129733 | 129741 | ){ |
| 129734 | 129742 | return; |
| 129735 | 129743 | } |
| 129736 | 129744 | p0type = sqlite3_value_type(argv[0]); |
| 129737 | - p1 = sqlite3_value_int(argv[1]); | |
| 129745 | + p1 = sqlite3_value_int64(argv[1]); | |
| 129738 | 129746 | if( p0type==SQLITE_BLOB ){ |
| 129739 | 129747 | len = sqlite3_value_bytes(argv[0]); |
| 129740 | 129748 | z = sqlite3_value_blob(argv[0]); |
| 129741 | 129749 | if( z==0 ) return; |
| 129742 | 129750 | assert( len==sqlite3_value_bytes(argv[0]) ); |
| @@ -129757,11 +129765,11 @@ | ||
| 129757 | 129765 | ** from 2009-02-02 for compatibility of applications that exploited the |
| 129758 | 129766 | ** old buggy behavior. */ |
| 129759 | 129767 | if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */ |
| 129760 | 129768 | #endif |
| 129761 | 129769 | if( argc==3 ){ |
| 129762 | - p2 = sqlite3_value_int(argv[2]); | |
| 129770 | + p2 = sqlite3_value_int64(argv[2]); | |
| 129763 | 129771 | if( p2<0 ){ |
| 129764 | 129772 | p2 = -p2; |
| 129765 | 129773 | negP2 = 1; |
| 129766 | 129774 | } |
| 129767 | 129775 | }else{ |
| @@ -129796,13 +129804,15 @@ | ||
| 129796 | 129804 | SQLITE_SKIP_UTF8(z2); |
| 129797 | 129805 | } |
| 129798 | 129806 | sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT, |
| 129799 | 129807 | SQLITE_UTF8); |
| 129800 | 129808 | }else{ |
| 129801 | - if( p1+p2>len ){ | |
| 129809 | + if( p1>=len ){ | |
| 129810 | + p1 = p2 = 0; | |
| 129811 | + }else if( p2>len-p1 ){ | |
| 129802 | 129812 | p2 = len-p1; |
| 129803 | - if( p2<0 ) p2 = 0; | |
| 129813 | + assert( p2>0 ); | |
| 129804 | 129814 | } |
| 129805 | 129815 | sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT); |
| 129806 | 129816 | } |
| 129807 | 129817 | } |
| 129808 | 129818 | |
| @@ -129809,17 +129819,17 @@ | ||
| 129809 | 129819 | /* |
| 129810 | 129820 | ** Implementation of the round() function |
| 129811 | 129821 | */ |
| 129812 | 129822 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 129813 | 129823 | static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 129814 | - int n = 0; | |
| 129824 | + i64 n = 0; | |
| 129815 | 129825 | double r; |
| 129816 | 129826 | char *zBuf; |
| 129817 | 129827 | assert( argc==1 || argc==2 ); |
| 129818 | 129828 | if( argc==2 ){ |
| 129819 | 129829 | if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; |
| 129820 | - n = sqlite3_value_int(argv[1]); | |
| 129830 | + n = sqlite3_value_int64(argv[1]); | |
| 129821 | 129831 | if( n>30 ) n = 30; |
| 129822 | 129832 | if( n<0 ) n = 0; |
| 129823 | 129833 | } |
| 129824 | 129834 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 129825 | 129835 | r = sqlite3_value_double(argv[0]); |
| @@ -141316,11 +141326,11 @@ | ||
| 141316 | 141326 | sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); |
| 141317 | 141327 | sqlite3ClearTempRegCache(pParse); |
| 141318 | 141328 | |
| 141319 | 141329 | /* Do the b-tree integrity checks */ |
| 141320 | 141330 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141321 | - sqlite3VdbeChangeP5(v, (u8)i); | |
| 141331 | + sqlite3VdbeChangeP5(v, (u16)i); | |
| 141322 | 141332 | addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); |
| 141323 | 141333 | sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, |
| 141324 | 141334 | sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), |
| 141325 | 141335 | P4_DYNAMIC); |
| 141326 | 141336 | sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3); |
| @@ -150549,11 +150559,11 @@ | ||
| 150549 | 150559 | } |
| 150550 | 150560 | sqlite3ReleaseTempReg(pParse, regSubtype); |
| 150551 | 150561 | } |
| 150552 | 150562 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150553 | 150563 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150554 | - sqlite3VdbeChangeP5(v, (u8)nArg); | |
| 150564 | + sqlite3VdbeChangeP5(v, (u16)nArg); | |
| 150555 | 150565 | sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); |
| 150556 | 150566 | sqlite3VdbeJumpHere(v, iTop); |
| 150557 | 150567 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150558 | 150568 | } |
| 150559 | 150569 | sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), |
| @@ -150712,11 +150722,11 @@ | ||
| 150712 | 150722 | sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, |
| 150713 | 150723 | (char *)pColl, P4_COLLSEQ); |
| 150714 | 150724 | } |
| 150715 | 150725 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150716 | 150726 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150717 | - sqlite3VdbeChangeP5(v, (u8)nArg); | |
| 150727 | + sqlite3VdbeChangeP5(v, (u16)nArg); | |
| 150718 | 150728 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150719 | 150729 | } |
| 150720 | 150730 | if( addrNext ){ |
| 150721 | 150731 | sqlite3VdbeResolveLabel(v, addrNext); |
| 150722 | 150732 | } |
| @@ -154106,11 +154116,11 @@ | ||
| 154106 | 154116 | /* Set the P5 operand of the OP_Program instruction to non-zero if |
| 154107 | 154117 | ** recursive invocation of this trigger program is disallowed. Recursive |
| 154108 | 154118 | ** invocation is disallowed if (a) the sub-program is really a trigger, |
| 154109 | 154119 | ** not a foreign key action, and (b) the flag to enable recursive triggers |
| 154110 | 154120 | ** is clear. */ |
| 154111 | - sqlite3VdbeChangeP5(v, (u8)bRecursive); | |
| 154121 | + sqlite3VdbeChangeP5(v, (u16)bRecursive); | |
| 154112 | 154122 | } |
| 154113 | 154123 | } |
| 154114 | 154124 | |
| 154115 | 154125 | /* |
| 154116 | 154126 | ** This is called to code the required FOR EACH ROW triggers for an operation |
| @@ -170414,10 +170424,11 @@ | ||
| 170414 | 170424 | int pc, |
| 170415 | 170425 | VdbeOp *pOp |
| 170416 | 170426 | ){ |
| 170417 | 170427 | if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; |
| 170418 | 170428 | sqlite3VdbePrintOp(0, pc, pOp); |
| 170429 | + sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */ | |
| 170419 | 170430 | } |
| 170420 | 170431 | #endif |
| 170421 | 170432 | |
| 170422 | 170433 | /* |
| 170423 | 170434 | ** Generate the end of the WHERE loop. See comments on |
| @@ -172523,11 +172534,11 @@ | ||
| 172523 | 172534 | sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); |
| 172524 | 172535 | } |
| 172525 | 172536 | sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, |
| 172526 | 172537 | bInverse, regArg, pWin->regAccum); |
| 172527 | 172538 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172528 | - sqlite3VdbeChangeP5(v, (u8)nArg); | |
| 172539 | + sqlite3VdbeChangeP5(v, (u16)nArg); | |
| 172529 | 172540 | if( pWin->bExprArgs ){ |
| 172530 | 172541 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172531 | 172542 | } |
| 172532 | 172543 | } |
| 172533 | 172544 | |
| @@ -184078,12 +184089,12 @@ | ||
| 184078 | 184089 | # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 |
| 184079 | 184090 | #endif |
| 184080 | 184091 | #if SQLITE_MAX_VDBE_OP<40 |
| 184081 | 184092 | # error SQLITE_MAX_VDBE_OP must be at least 40 |
| 184082 | 184093 | #endif |
| 184083 | -#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127 | |
| 184084 | -# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127 | |
| 184094 | +#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767 | |
| 184095 | +# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767 | |
| 184085 | 184096 | #endif |
| 184086 | 184097 | #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 |
| 184087 | 184098 | # error SQLITE_MAX_ATTACHED must be between 0 and 125 |
| 184088 | 184099 | #endif |
| 184089 | 184100 | #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 |
| @@ -185492,11 +185503,10 @@ | ||
| 185492 | 185503 | #if defined(SQLITE_DEBUG) |
| 185493 | 185504 | /* Invoke these debugging routines so that the compiler does not |
| 185494 | 185505 | ** issue "defined but not used" warnings. */ |
| 185495 | 185506 | if( x==9999 ){ |
| 185496 | 185507 | sqlite3ShowExpr(0); |
| 185497 | - sqlite3ShowExpr(0); | |
| 185498 | 185508 | sqlite3ShowExprList(0); |
| 185499 | 185509 | sqlite3ShowIdList(0); |
| 185500 | 185510 | sqlite3ShowSrcList(0); |
| 185501 | 185511 | sqlite3ShowWith(0); |
| 185502 | 185512 | sqlite3ShowUpsert(0); |
| @@ -185509,11 +185519,10 @@ | ||
| 185509 | 185519 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185510 | 185520 | sqlite3ShowWindow(0); |
| 185511 | 185521 | sqlite3ShowWinFunc(0); |
| 185512 | 185522 | #endif |
| 185513 | 185523 | sqlite3ShowSelect(0); |
| 185514 | - sqlite3ShowWhereTerm(0); | |
| 185515 | 185524 | } |
| 185516 | 185525 | #endif |
| 185517 | 185526 | break; |
| 185518 | 185527 | } |
| 185519 | 185528 | |
| @@ -226344,11 +226353,15 @@ | ||
| 226344 | 226353 | static int dbpageSync(sqlite3_vtab *pVtab){ |
| 226345 | 226354 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226346 | 226355 | if( pTab->pgnoTrunc>0 ){ |
| 226347 | 226356 | Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; |
| 226348 | 226357 | Pager *pPager = sqlite3BtreePager(pBt); |
| 226349 | - sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); | |
| 226358 | + sqlite3BtreeEnter(pBt); | |
| 226359 | + if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){ | |
| 226360 | + sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); | |
| 226361 | + } | |
| 226362 | + sqlite3BtreeLeave(pBt); | |
| 226350 | 226363 | } |
| 226351 | 226364 | pTab->pgnoTrunc = 0; |
| 226352 | 226365 | return SQLITE_OK; |
| 226353 | 226366 | } |
| 226354 | 226367 | |
| @@ -255419,11 +255432,11 @@ | ||
| 255419 | 255432 | int nArg, /* Number of args */ |
| 255420 | 255433 | sqlite3_value **apUnused /* Function arguments */ |
| 255421 | 255434 | ){ |
| 255422 | 255435 | assert( nArg==0 ); |
| 255423 | 255436 | UNUSED_PARAM2(nArg, apUnused); |
| 255424 | - sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT); | |
| 255437 | + sqlite3_result_text(pCtx, "fts5: 2024-12-19 14:20:47 5b96dcf5f6bf41dcb89ced64efd4585e36dce718c428c2324d94e4942905c3bb", -1, SQLITE_TRANSIENT); | |
| 255425 | 255438 | } |
| 255426 | 255439 | |
| 255427 | 255440 | /* |
| 255428 | 255441 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255429 | 255442 | ** |
| 255430 | 255443 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.48.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3048000 |
| 470 | #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -14046,13 +14046,17 @@ | |
| 14046 | # define SQLITE_MAX_VDBE_OP 250000000 |
| 14047 | #endif |
| 14048 | |
| 14049 | /* |
| 14050 | ** The maximum number of arguments to an SQL function. |
| 14051 | */ |
| 14052 | #ifndef SQLITE_MAX_FUNCTION_ARG |
| 14053 | # define SQLITE_MAX_FUNCTION_ARG 127 |
| 14054 | #endif |
| 14055 | |
| 14056 | /* |
| 14057 | ** The suggested maximum number of in-memory pages to use for |
| 14058 | ** the main database table and for temporary tables. |
| @@ -16049,10 +16053,26 @@ | |
| 16049 | #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ |
| 16050 | #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ |
| 16051 | #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ |
| 16052 | #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ |
| 16053 | #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ |
| 16054 | |
| 16055 | /* |
| 16056 | ** Flags that make up the mask passed to sqlite3PagerGet(). |
| 16057 | */ |
| 16058 | #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ |
| @@ -18113,11 +18133,11 @@ | |
| 18113 | ** |
| 18114 | ** The u.pHash field is used by the global built-ins. The u.pDestructor |
| 18115 | ** field is used by per-connection app-def functions. |
| 18116 | */ |
| 18117 | struct FuncDef { |
| 18118 | i8 nArg; /* Number of arguments. -1 means unlimited */ |
| 18119 | u32 funcFlags; /* Some combination of SQLITE_FUNC_* */ |
| 18120 | void *pUserData; /* User data parameter */ |
| 18121 | FuncDef *pNext; /* Next function with same name */ |
| 18122 | void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */ |
| 18123 | void (*xFinalize)(sqlite3_context*); /* Agg finalizer */ |
| @@ -23708,11 +23728,11 @@ | |
| 23708 | Vdbe *pVdbe; /* The VM that owns this context */ |
| 23709 | int iOp; /* Instruction number of OP_Function */ |
| 23710 | int isError; /* Error code returned by the function. */ |
| 23711 | u8 enc; /* Encoding to use for results */ |
| 23712 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23713 | u8 argc; /* Number of arguments */ |
| 23714 | sqlite3_value *argv[1]; /* Argument set */ |
| 23715 | }; |
| 23716 | |
| 23717 | /* A bitfield type for use inside of structures. Always follow with :N where |
| 23718 | ** N is the number of bits. |
| @@ -58014,24 +58034,10 @@ | |
| 58014 | # define USEFETCH(x) ((x)->bUseFetch) |
| 58015 | #else |
| 58016 | # define USEFETCH(x) 0 |
| 58017 | #endif |
| 58018 | |
| 58019 | /* |
| 58020 | ** The argument to this macro is a file descriptor (type sqlite3_file*). |
| 58021 | ** Return 0 if it is not open, or non-zero (but not 1) if it is. |
| 58022 | ** |
| 58023 | ** This is so that expressions can be written as: |
| 58024 | ** |
| 58025 | ** if( isOpen(pPager->jfd) ){ ... |
| 58026 | ** |
| 58027 | ** instead of |
| 58028 | ** |
| 58029 | ** if( pPager->jfd->pMethods ){ ... |
| 58030 | */ |
| 58031 | #define isOpen(pFd) ((pFd)->pMethods!=0) |
| 58032 | |
| 58033 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58034 | /* |
| 58035 | ** Return true if page pgno can be read directly from the database file |
| 58036 | ** by the b-tree layer. This is the case if: |
| 58037 | ** |
| @@ -59313,11 +59319,11 @@ | |
| 59313 | rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); |
| 59314 | } |
| 59315 | } |
| 59316 | pPager->journalOff = 0; |
| 59317 | }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST |
| 59318 | || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) |
| 59319 | ){ |
| 59320 | rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); |
| 59321 | pPager->journalOff = 0; |
| 59322 | }else{ |
| 59323 | /* This branch may be executed with Pager.journalMode==MEMORY if |
| @@ -68023,15 +68029,11 @@ | |
| 68023 | ** so it takes care to hold an exclusive lock on the corresponding |
| 68024 | ** WAL_READ_LOCK() while changing values. |
| 68025 | */ |
| 68026 | static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ |
| 68027 | volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ |
| 68028 | u32 mxReadMark; /* Largest aReadMark[] value */ |
| 68029 | int mxI; /* Index of largest aReadMark[] value */ |
| 68030 | int i; /* Loop counter */ |
| 68031 | int rc = SQLITE_OK; /* Return code */ |
| 68032 | u32 mxFrame; /* Wal frame to lock to */ |
| 68033 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68034 | int nBlockTmout = 0; |
| 68035 | #endif |
| 68036 | |
| 68037 | assert( pWal->readLock<0 ); /* Not currently locked */ |
| @@ -68133,145 +68135,151 @@ | |
| 68133 | |
| 68134 | assert( pWal->nWiData>0 ); |
| 68135 | assert( pWal->apWiData[0]!=0 ); |
| 68136 | pInfo = walCkptInfo(pWal); |
| 68137 | SEH_INJECT_FAULT; |
| 68138 | if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame |
| 68139 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68140 | && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) |
| 68141 | #endif |
| 68142 | ){ |
| 68143 | /* The WAL has been completely backfilled (or it is empty). |
| 68144 | ** and can be safely ignored. |
| 68145 | */ |
| 68146 | rc = walLockShared(pWal, WAL_READ_LOCK(0)); |
| 68147 | walShmBarrier(pWal); |
| 68148 | if( rc==SQLITE_OK ){ |
| 68149 | if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ |
| 68150 | /* It is not safe to allow the reader to continue here if frames |
| 68151 | ** may have been appended to the log before READ_LOCK(0) was obtained. |
| 68152 | ** When holding READ_LOCK(0), the reader ignores the entire log file, |
| 68153 | ** which implies that the database file contains a trustworthy |
| 68154 | ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from |
| 68155 | ** happening, this is usually correct. |
| 68156 | ** |
| 68157 | ** However, if frames have been appended to the log (or if the log |
| 68158 | ** is wrapped and written for that matter) before the READ_LOCK(0) |
| 68159 | ** is obtained, that is not necessarily true. A checkpointer may |
| 68160 | ** have started to backfill the appended frames but crashed before |
| 68161 | ** it finished. Leaving a corrupt image in the database file. |
| 68162 | */ |
| 68163 | walUnlockShared(pWal, WAL_READ_LOCK(0)); |
| 68164 | return WAL_RETRY; |
| 68165 | } |
| 68166 | pWal->readLock = 0; |
| 68167 | return SQLITE_OK; |
| 68168 | }else if( rc!=SQLITE_BUSY ){ |
| 68169 | return rc; |
| 68170 | } |
| 68171 | } |
| 68172 | |
| 68173 | /* If we get this far, it means that the reader will want to use |
| 68174 | ** the WAL to get at content from recent commits. The job now is |
| 68175 | ** to select one of the aReadMark[] entries that is closest to |
| 68176 | ** but not exceeding pWal->hdr.mxFrame and lock that entry. |
| 68177 | */ |
| 68178 | mxReadMark = 0; |
| 68179 | mxI = 0; |
| 68180 | mxFrame = pWal->hdr.mxFrame; |
| 68181 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68182 | if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){ |
| 68183 | mxFrame = pWal->pSnapshot->mxFrame; |
| 68184 | } |
| 68185 | #endif |
| 68186 | for(i=1; i<WAL_NREADER; i++){ |
| 68187 | u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; |
| 68188 | if( mxReadMark<=thisMark && thisMark<=mxFrame ){ |
| 68189 | assert( thisMark!=READMARK_NOT_USED ); |
| 68190 | mxReadMark = thisMark; |
| 68191 | mxI = i; |
| 68192 | } |
| 68193 | } |
| 68194 | if( (pWal->readOnly & WAL_SHM_RDONLY)==0 |
| 68195 | && (mxReadMark<mxFrame || mxI==0) |
| 68196 | ){ |
| 68197 | for(i=1; i<WAL_NREADER; i++){ |
| 68198 | rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 68199 | if( rc==SQLITE_OK ){ |
| 68200 | AtomicStore(pInfo->aReadMark+i,mxFrame); |
| 68201 | mxReadMark = mxFrame; |
| 68202 | mxI = i; |
| 68203 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 68204 | break; |
| 68205 | }else if( rc!=SQLITE_BUSY ){ |
| 68206 | return rc; |
| 68207 | } |
| 68208 | } |
| 68209 | } |
| 68210 | if( mxI==0 ){ |
| 68211 | assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); |
| 68212 | return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; |
| 68213 | } |
| 68214 | |
| 68215 | (void)walEnableBlockingMs(pWal, nBlockTmout); |
| 68216 | rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); |
| 68217 | walDisableBlocking(pWal); |
| 68218 | if( rc ){ |
| 68219 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68220 | if( rc==SQLITE_BUSY_TIMEOUT ){ |
| 68221 | *pCnt |= WAL_RETRY_BLOCKED_MASK; |
| 68222 | } |
| 68223 | #else |
| 68224 | assert( rc!=SQLITE_BUSY_TIMEOUT ); |
| 68225 | #endif |
| 68226 | assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT ); |
| 68227 | return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; |
| 68228 | } |
| 68229 | /* Now that the read-lock has been obtained, check that neither the |
| 68230 | ** value in the aReadMark[] array or the contents of the wal-index |
| 68231 | ** header have changed. |
| 68232 | ** |
| 68233 | ** It is necessary to check that the wal-index header did not change |
| 68234 | ** between the time it was read and when the shared-lock was obtained |
| 68235 | ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility |
| 68236 | ** that the log file may have been wrapped by a writer, or that frames |
| 68237 | ** that occur later in the log than pWal->hdr.mxFrame may have been |
| 68238 | ** copied into the database by a checkpointer. If either of these things |
| 68239 | ** happened, then reading the database with the current value of |
| 68240 | ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry |
| 68241 | ** instead. |
| 68242 | ** |
| 68243 | ** Before checking that the live wal-index header has not changed |
| 68244 | ** since it was read, set Wal.minFrame to the first frame in the wal |
| 68245 | ** file that has not yet been checkpointed. This client will not need |
| 68246 | ** to read any frames earlier than minFrame from the wal file - they |
| 68247 | ** can be safely read directly from the database file. |
| 68248 | ** |
| 68249 | ** Because a ShmBarrier() call is made between taking the copy of |
| 68250 | ** nBackfill and checking that the wal-header in shared-memory still |
| 68251 | ** matches the one cached in pWal->hdr, it is guaranteed that the |
| 68252 | ** checkpointer that set nBackfill was not working with a wal-index |
| 68253 | ** header newer than that cached in pWal->hdr. If it were, that could |
| 68254 | ** cause a problem. The checkpointer could omit to checkpoint |
| 68255 | ** a version of page X that lies before pWal->minFrame (call that version |
| 68256 | ** A) on the basis that there is a newer version (version B) of the same |
| 68257 | ** page later in the wal file. But if version B happens to like past |
| 68258 | ** frame pWal->hdr.mxFrame - then the client would incorrectly assume |
| 68259 | ** that it can read version A from the database file. However, since |
| 68260 | ** we can guarantee that the checkpointer that set nBackfill could not |
| 68261 | ** see any pages past pWal->hdr.mxFrame, this problem does not come up. |
| 68262 | */ |
| 68263 | pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; |
| 68264 | walShmBarrier(pWal); |
| 68265 | if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark |
| 68266 | || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) |
| 68267 | ){ |
| 68268 | walUnlockShared(pWal, WAL_READ_LOCK(mxI)); |
| 68269 | return WAL_RETRY; |
| 68270 | }else{ |
| 68271 | assert( mxReadMark<=pWal->hdr.mxFrame ); |
| 68272 | pWal->readLock = (i16)mxI; |
| 68273 | } |
| 68274 | return rc; |
| 68275 | } |
| 68276 | |
| 68277 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| @@ -91889,11 +91897,11 @@ | |
| 91889 | ** |
| 91890 | ** sqlite3_column_int() |
| 91891 | ** sqlite3_column_int64() |
| 91892 | ** sqlite3_column_text() |
| 91893 | ** sqlite3_column_text16() |
| 91894 | ** sqlite3_column_real() |
| 91895 | ** sqlite3_column_bytes() |
| 91896 | ** sqlite3_column_bytes16() |
| 91897 | ** sqlite3_column_blob() |
| 91898 | */ |
| 91899 | static void columnMallocFailure(sqlite3_stmt *pStmt) |
| @@ -109896,11 +109904,11 @@ | |
| 109896 | p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight); |
| 109897 | } |
| 109898 | p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); |
| 109899 | addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, |
| 109900 | (void*)p4, P4_COLLSEQ); |
| 109901 | sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5); |
| 109902 | return addr; |
| 109903 | } |
| 109904 | |
| 109905 | /* |
| 109906 | ** Return true if expression pExpr is a vector, or false otherwise. |
| @@ -129732,11 +129740,11 @@ | |
| 129732 | || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) |
| 129733 | ){ |
| 129734 | return; |
| 129735 | } |
| 129736 | p0type = sqlite3_value_type(argv[0]); |
| 129737 | p1 = sqlite3_value_int(argv[1]); |
| 129738 | if( p0type==SQLITE_BLOB ){ |
| 129739 | len = sqlite3_value_bytes(argv[0]); |
| 129740 | z = sqlite3_value_blob(argv[0]); |
| 129741 | if( z==0 ) return; |
| 129742 | assert( len==sqlite3_value_bytes(argv[0]) ); |
| @@ -129757,11 +129765,11 @@ | |
| 129757 | ** from 2009-02-02 for compatibility of applications that exploited the |
| 129758 | ** old buggy behavior. */ |
| 129759 | if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */ |
| 129760 | #endif |
| 129761 | if( argc==3 ){ |
| 129762 | p2 = sqlite3_value_int(argv[2]); |
| 129763 | if( p2<0 ){ |
| 129764 | p2 = -p2; |
| 129765 | negP2 = 1; |
| 129766 | } |
| 129767 | }else{ |
| @@ -129796,13 +129804,15 @@ | |
| 129796 | SQLITE_SKIP_UTF8(z2); |
| 129797 | } |
| 129798 | sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT, |
| 129799 | SQLITE_UTF8); |
| 129800 | }else{ |
| 129801 | if( p1+p2>len ){ |
| 129802 | p2 = len-p1; |
| 129803 | if( p2<0 ) p2 = 0; |
| 129804 | } |
| 129805 | sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT); |
| 129806 | } |
| 129807 | } |
| 129808 | |
| @@ -129809,17 +129819,17 @@ | |
| 129809 | /* |
| 129810 | ** Implementation of the round() function |
| 129811 | */ |
| 129812 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 129813 | static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 129814 | int n = 0; |
| 129815 | double r; |
| 129816 | char *zBuf; |
| 129817 | assert( argc==1 || argc==2 ); |
| 129818 | if( argc==2 ){ |
| 129819 | if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; |
| 129820 | n = sqlite3_value_int(argv[1]); |
| 129821 | if( n>30 ) n = 30; |
| 129822 | if( n<0 ) n = 0; |
| 129823 | } |
| 129824 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 129825 | r = sqlite3_value_double(argv[0]); |
| @@ -141316,11 +141326,11 @@ | |
| 141316 | sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); |
| 141317 | sqlite3ClearTempRegCache(pParse); |
| 141318 | |
| 141319 | /* Do the b-tree integrity checks */ |
| 141320 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141321 | sqlite3VdbeChangeP5(v, (u8)i); |
| 141322 | addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); |
| 141323 | sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, |
| 141324 | sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), |
| 141325 | P4_DYNAMIC); |
| 141326 | sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3); |
| @@ -150549,11 +150559,11 @@ | |
| 150549 | } |
| 150550 | sqlite3ReleaseTempReg(pParse, regSubtype); |
| 150551 | } |
| 150552 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150553 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150554 | sqlite3VdbeChangeP5(v, (u8)nArg); |
| 150555 | sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); |
| 150556 | sqlite3VdbeJumpHere(v, iTop); |
| 150557 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150558 | } |
| 150559 | sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), |
| @@ -150712,11 +150722,11 @@ | |
| 150712 | sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, |
| 150713 | (char *)pColl, P4_COLLSEQ); |
| 150714 | } |
| 150715 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150716 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150717 | sqlite3VdbeChangeP5(v, (u8)nArg); |
| 150718 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150719 | } |
| 150720 | if( addrNext ){ |
| 150721 | sqlite3VdbeResolveLabel(v, addrNext); |
| 150722 | } |
| @@ -154106,11 +154116,11 @@ | |
| 154106 | /* Set the P5 operand of the OP_Program instruction to non-zero if |
| 154107 | ** recursive invocation of this trigger program is disallowed. Recursive |
| 154108 | ** invocation is disallowed if (a) the sub-program is really a trigger, |
| 154109 | ** not a foreign key action, and (b) the flag to enable recursive triggers |
| 154110 | ** is clear. */ |
| 154111 | sqlite3VdbeChangeP5(v, (u8)bRecursive); |
| 154112 | } |
| 154113 | } |
| 154114 | |
| 154115 | /* |
| 154116 | ** This is called to code the required FOR EACH ROW triggers for an operation |
| @@ -170414,10 +170424,11 @@ | |
| 170414 | int pc, |
| 170415 | VdbeOp *pOp |
| 170416 | ){ |
| 170417 | if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; |
| 170418 | sqlite3VdbePrintOp(0, pc, pOp); |
| 170419 | } |
| 170420 | #endif |
| 170421 | |
| 170422 | /* |
| 170423 | ** Generate the end of the WHERE loop. See comments on |
| @@ -172523,11 +172534,11 @@ | |
| 172523 | sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); |
| 172524 | } |
| 172525 | sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, |
| 172526 | bInverse, regArg, pWin->regAccum); |
| 172527 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172528 | sqlite3VdbeChangeP5(v, (u8)nArg); |
| 172529 | if( pWin->bExprArgs ){ |
| 172530 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172531 | } |
| 172532 | } |
| 172533 | |
| @@ -184078,12 +184089,12 @@ | |
| 184078 | # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 |
| 184079 | #endif |
| 184080 | #if SQLITE_MAX_VDBE_OP<40 |
| 184081 | # error SQLITE_MAX_VDBE_OP must be at least 40 |
| 184082 | #endif |
| 184083 | #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127 |
| 184084 | # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127 |
| 184085 | #endif |
| 184086 | #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 |
| 184087 | # error SQLITE_MAX_ATTACHED must be between 0 and 125 |
| 184088 | #endif |
| 184089 | #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 |
| @@ -185492,11 +185503,10 @@ | |
| 185492 | #if defined(SQLITE_DEBUG) |
| 185493 | /* Invoke these debugging routines so that the compiler does not |
| 185494 | ** issue "defined but not used" warnings. */ |
| 185495 | if( x==9999 ){ |
| 185496 | sqlite3ShowExpr(0); |
| 185497 | sqlite3ShowExpr(0); |
| 185498 | sqlite3ShowExprList(0); |
| 185499 | sqlite3ShowIdList(0); |
| 185500 | sqlite3ShowSrcList(0); |
| 185501 | sqlite3ShowWith(0); |
| 185502 | sqlite3ShowUpsert(0); |
| @@ -185509,11 +185519,10 @@ | |
| 185509 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185510 | sqlite3ShowWindow(0); |
| 185511 | sqlite3ShowWinFunc(0); |
| 185512 | #endif |
| 185513 | sqlite3ShowSelect(0); |
| 185514 | sqlite3ShowWhereTerm(0); |
| 185515 | } |
| 185516 | #endif |
| 185517 | break; |
| 185518 | } |
| 185519 | |
| @@ -226344,11 +226353,15 @@ | |
| 226344 | static int dbpageSync(sqlite3_vtab *pVtab){ |
| 226345 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226346 | if( pTab->pgnoTrunc>0 ){ |
| 226347 | Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; |
| 226348 | Pager *pPager = sqlite3BtreePager(pBt); |
| 226349 | sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); |
| 226350 | } |
| 226351 | pTab->pgnoTrunc = 0; |
| 226352 | return SQLITE_OK; |
| 226353 | } |
| 226354 | |
| @@ -255419,11 +255432,11 @@ | |
| 255419 | int nArg, /* Number of args */ |
| 255420 | sqlite3_value **apUnused /* Function arguments */ |
| 255421 | ){ |
| 255422 | assert( nArg==0 ); |
| 255423 | UNUSED_PARAM2(nArg, apUnused); |
| 255424 | sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT); |
| 255425 | } |
| 255426 | |
| 255427 | /* |
| 255428 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255429 | ** |
| 255430 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** e6c30ee52c5cdc193804cec63374d558b45e with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.48.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3048000 |
| 470 | #define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -14046,13 +14046,17 @@ | |
| 14046 | # define SQLITE_MAX_VDBE_OP 250000000 |
| 14047 | #endif |
| 14048 | |
| 14049 | /* |
| 14050 | ** The maximum number of arguments to an SQL function. |
| 14051 | ** |
| 14052 | ** This value has a hard upper limit of 32767 due to storage |
| 14053 | ** constraints (it needs to fit inside a i16). We keep it |
| 14054 | ** lower than that to prevent abuse. |
| 14055 | */ |
| 14056 | #ifndef SQLITE_MAX_FUNCTION_ARG |
| 14057 | # define SQLITE_MAX_FUNCTION_ARG 1000 |
| 14058 | #endif |
| 14059 | |
| 14060 | /* |
| 14061 | ** The suggested maximum number of in-memory pages to use for |
| 14062 | ** the main database table and for temporary tables. |
| @@ -16049,10 +16053,26 @@ | |
| 16053 | #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ |
| 16054 | #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ |
| 16055 | #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ |
| 16056 | #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ |
| 16057 | #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ |
| 16058 | |
| 16059 | #define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL) |
| 16060 | |
| 16061 | /* |
| 16062 | ** The argument to this macro is a file descriptor (type sqlite3_file*). |
| 16063 | ** Return 0 if it is not open, or non-zero (but not 1) if it is. |
| 16064 | ** |
| 16065 | ** This is so that expressions can be written as: |
| 16066 | ** |
| 16067 | ** if( isOpen(pPager->jfd) ){ ... |
| 16068 | ** |
| 16069 | ** instead of |
| 16070 | ** |
| 16071 | ** if( pPager->jfd->pMethods ){ ... |
| 16072 | */ |
| 16073 | #define isOpen(pFd) ((pFd)->pMethods!=0) |
| 16074 | |
| 16075 | /* |
| 16076 | ** Flags that make up the mask passed to sqlite3PagerGet(). |
| 16077 | */ |
| 16078 | #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ |
| @@ -18113,11 +18133,11 @@ | |
| 18133 | ** |
| 18134 | ** The u.pHash field is used by the global built-ins. The u.pDestructor |
| 18135 | ** field is used by per-connection app-def functions. |
| 18136 | */ |
| 18137 | struct FuncDef { |
| 18138 | i16 nArg; /* Number of arguments. -1 means unlimited */ |
| 18139 | u32 funcFlags; /* Some combination of SQLITE_FUNC_* */ |
| 18140 | void *pUserData; /* User data parameter */ |
| 18141 | FuncDef *pNext; /* Next function with same name */ |
| 18142 | void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */ |
| 18143 | void (*xFinalize)(sqlite3_context*); /* Agg finalizer */ |
| @@ -23708,11 +23728,11 @@ | |
| 23728 | Vdbe *pVdbe; /* The VM that owns this context */ |
| 23729 | int iOp; /* Instruction number of OP_Function */ |
| 23730 | int isError; /* Error code returned by the function. */ |
| 23731 | u8 enc; /* Encoding to use for results */ |
| 23732 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23733 | u16 argc; /* Number of arguments */ |
| 23734 | sqlite3_value *argv[1]; /* Argument set */ |
| 23735 | }; |
| 23736 | |
| 23737 | /* A bitfield type for use inside of structures. Always follow with :N where |
| 23738 | ** N is the number of bits. |
| @@ -58014,24 +58034,10 @@ | |
| 58034 | # define USEFETCH(x) ((x)->bUseFetch) |
| 58035 | #else |
| 58036 | # define USEFETCH(x) 0 |
| 58037 | #endif |
| 58038 | |
| 58039 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58040 | /* |
| 58041 | ** Return true if page pgno can be read directly from the database file |
| 58042 | ** by the b-tree layer. This is the case if: |
| 58043 | ** |
| @@ -59313,11 +59319,11 @@ | |
| 59319 | rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); |
| 59320 | } |
| 59321 | } |
| 59322 | pPager->journalOff = 0; |
| 59323 | }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST |
| 59324 | || (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL) |
| 59325 | ){ |
| 59326 | rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); |
| 59327 | pPager->journalOff = 0; |
| 59328 | }else{ |
| 59329 | /* This branch may be executed with Pager.journalMode==MEMORY if |
| @@ -68023,15 +68029,11 @@ | |
| 68029 | ** so it takes care to hold an exclusive lock on the corresponding |
| 68030 | ** WAL_READ_LOCK() while changing values. |
| 68031 | */ |
| 68032 | static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ |
| 68033 | volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ |
| 68034 | int rc = SQLITE_OK; /* Return code */ |
| 68035 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68036 | int nBlockTmout = 0; |
| 68037 | #endif |
| 68038 | |
| 68039 | assert( pWal->readLock<0 ); /* Not currently locked */ |
| @@ -68133,145 +68135,151 @@ | |
| 68135 | |
| 68136 | assert( pWal->nWiData>0 ); |
| 68137 | assert( pWal->apWiData[0]!=0 ); |
| 68138 | pInfo = walCkptInfo(pWal); |
| 68139 | SEH_INJECT_FAULT; |
| 68140 | { |
| 68141 | u32 mxReadMark; /* Largest aReadMark[] value */ |
| 68142 | int mxI; /* Index of largest aReadMark[] value */ |
| 68143 | int i; /* Loop counter */ |
| 68144 | u32 mxFrame; /* Wal frame to lock to */ |
| 68145 | if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame |
| 68146 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68147 | && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0) |
| 68148 | #endif |
| 68149 | ){ |
| 68150 | /* The WAL has been completely backfilled (or it is empty). |
| 68151 | ** and can be safely ignored. |
| 68152 | */ |
| 68153 | rc = walLockShared(pWal, WAL_READ_LOCK(0)); |
| 68154 | walShmBarrier(pWal); |
| 68155 | if( rc==SQLITE_OK ){ |
| 68156 | if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){ |
| 68157 | /* It is not safe to allow the reader to continue here if frames |
| 68158 | ** may have been appended to the log before READ_LOCK(0) was obtained. |
| 68159 | ** When holding READ_LOCK(0), the reader ignores the entire log file, |
| 68160 | ** which implies that the database file contains a trustworthy |
| 68161 | ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from |
| 68162 | ** happening, this is usually correct. |
| 68163 | ** |
| 68164 | ** However, if frames have been appended to the log (or if the log |
| 68165 | ** is wrapped and written for that matter) before the READ_LOCK(0) |
| 68166 | ** is obtained, that is not necessarily true. A checkpointer may |
| 68167 | ** have started to backfill the appended frames but crashed before |
| 68168 | ** it finished. Leaving a corrupt image in the database file. |
| 68169 | */ |
| 68170 | walUnlockShared(pWal, WAL_READ_LOCK(0)); |
| 68171 | return WAL_RETRY; |
| 68172 | } |
| 68173 | pWal->readLock = 0; |
| 68174 | return SQLITE_OK; |
| 68175 | }else if( rc!=SQLITE_BUSY ){ |
| 68176 | return rc; |
| 68177 | } |
| 68178 | } |
| 68179 | |
| 68180 | /* If we get this far, it means that the reader will want to use |
| 68181 | ** the WAL to get at content from recent commits. The job now is |
| 68182 | ** to select one of the aReadMark[] entries that is closest to |
| 68183 | ** but not exceeding pWal->hdr.mxFrame and lock that entry. |
| 68184 | */ |
| 68185 | mxReadMark = 0; |
| 68186 | mxI = 0; |
| 68187 | mxFrame = pWal->hdr.mxFrame; |
| 68188 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 68189 | if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){ |
| 68190 | mxFrame = pWal->pSnapshot->mxFrame; |
| 68191 | } |
| 68192 | #endif |
| 68193 | for(i=1; i<WAL_NREADER; i++){ |
| 68194 | u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT; |
| 68195 | if( mxReadMark<=thisMark && thisMark<=mxFrame ){ |
| 68196 | assert( thisMark!=READMARK_NOT_USED ); |
| 68197 | mxReadMark = thisMark; |
| 68198 | mxI = i; |
| 68199 | } |
| 68200 | } |
| 68201 | if( (pWal->readOnly & WAL_SHM_RDONLY)==0 |
| 68202 | && (mxReadMark<mxFrame || mxI==0) |
| 68203 | ){ |
| 68204 | for(i=1; i<WAL_NREADER; i++){ |
| 68205 | rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 68206 | if( rc==SQLITE_OK ){ |
| 68207 | AtomicStore(pInfo->aReadMark+i,mxFrame); |
| 68208 | mxReadMark = mxFrame; |
| 68209 | mxI = i; |
| 68210 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 68211 | break; |
| 68212 | }else if( rc!=SQLITE_BUSY ){ |
| 68213 | return rc; |
| 68214 | } |
| 68215 | } |
| 68216 | } |
| 68217 | if( mxI==0 ){ |
| 68218 | assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); |
| 68219 | return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; |
| 68220 | } |
| 68221 | |
| 68222 | (void)walEnableBlockingMs(pWal, nBlockTmout); |
| 68223 | rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); |
| 68224 | walDisableBlocking(pWal); |
| 68225 | if( rc ){ |
| 68226 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68227 | if( rc==SQLITE_BUSY_TIMEOUT ){ |
| 68228 | *pCnt |= WAL_RETRY_BLOCKED_MASK; |
| 68229 | } |
| 68230 | #else |
| 68231 | assert( rc!=SQLITE_BUSY_TIMEOUT ); |
| 68232 | #endif |
| 68233 | assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT); |
| 68234 | return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; |
| 68235 | } |
| 68236 | /* Now that the read-lock has been obtained, check that neither the |
| 68237 | ** value in the aReadMark[] array or the contents of the wal-index |
| 68238 | ** header have changed. |
| 68239 | ** |
| 68240 | ** It is necessary to check that the wal-index header did not change |
| 68241 | ** between the time it was read and when the shared-lock was obtained |
| 68242 | ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility |
| 68243 | ** that the log file may have been wrapped by a writer, or that frames |
| 68244 | ** that occur later in the log than pWal->hdr.mxFrame may have been |
| 68245 | ** copied into the database by a checkpointer. If either of these things |
| 68246 | ** happened, then reading the database with the current value of |
| 68247 | ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry |
| 68248 | ** instead. |
| 68249 | ** |
| 68250 | ** Before checking that the live wal-index header has not changed |
| 68251 | ** since it was read, set Wal.minFrame to the first frame in the wal |
| 68252 | ** file that has not yet been checkpointed. This client will not need |
| 68253 | ** to read any frames earlier than minFrame from the wal file - they |
| 68254 | ** can be safely read directly from the database file. |
| 68255 | ** |
| 68256 | ** Because a ShmBarrier() call is made between taking the copy of |
| 68257 | ** nBackfill and checking that the wal-header in shared-memory still |
| 68258 | ** matches the one cached in pWal->hdr, it is guaranteed that the |
| 68259 | ** checkpointer that set nBackfill was not working with a wal-index |
| 68260 | ** header newer than that cached in pWal->hdr. If it were, that could |
| 68261 | ** cause a problem. The checkpointer could omit to checkpoint |
| 68262 | ** a version of page X that lies before pWal->minFrame (call that version |
| 68263 | ** A) on the basis that there is a newer version (version B) of the same |
| 68264 | ** page later in the wal file. But if version B happens to like past |
| 68265 | ** frame pWal->hdr.mxFrame - then the client would incorrectly assume |
| 68266 | ** that it can read version A from the database file. However, since |
| 68267 | ** we can guarantee that the checkpointer that set nBackfill could not |
| 68268 | ** see any pages past pWal->hdr.mxFrame, this problem does not come up. |
| 68269 | */ |
| 68270 | pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT; |
| 68271 | walShmBarrier(pWal); |
| 68272 | if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark |
| 68273 | || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) |
| 68274 | ){ |
| 68275 | walUnlockShared(pWal, WAL_READ_LOCK(mxI)); |
| 68276 | return WAL_RETRY; |
| 68277 | }else{ |
| 68278 | assert( mxReadMark<=pWal->hdr.mxFrame ); |
| 68279 | pWal->readLock = (i16)mxI; |
| 68280 | } |
| 68281 | } |
| 68282 | return rc; |
| 68283 | } |
| 68284 | |
| 68285 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| @@ -91889,11 +91897,11 @@ | |
| 91897 | ** |
| 91898 | ** sqlite3_column_int() |
| 91899 | ** sqlite3_column_int64() |
| 91900 | ** sqlite3_column_text() |
| 91901 | ** sqlite3_column_text16() |
| 91902 | ** sqlite3_column_double() |
| 91903 | ** sqlite3_column_bytes() |
| 91904 | ** sqlite3_column_bytes16() |
| 91905 | ** sqlite3_column_blob() |
| 91906 | */ |
| 91907 | static void columnMallocFailure(sqlite3_stmt *pStmt) |
| @@ -109896,11 +109904,11 @@ | |
| 109904 | p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight); |
| 109905 | } |
| 109906 | p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); |
| 109907 | addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, |
| 109908 | (void*)p4, P4_COLLSEQ); |
| 109909 | sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5); |
| 109910 | return addr; |
| 109911 | } |
| 109912 | |
| 109913 | /* |
| 109914 | ** Return true if expression pExpr is a vector, or false otherwise. |
| @@ -129732,11 +129740,11 @@ | |
| 129740 | || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) |
| 129741 | ){ |
| 129742 | return; |
| 129743 | } |
| 129744 | p0type = sqlite3_value_type(argv[0]); |
| 129745 | p1 = sqlite3_value_int64(argv[1]); |
| 129746 | if( p0type==SQLITE_BLOB ){ |
| 129747 | len = sqlite3_value_bytes(argv[0]); |
| 129748 | z = sqlite3_value_blob(argv[0]); |
| 129749 | if( z==0 ) return; |
| 129750 | assert( len==sqlite3_value_bytes(argv[0]) ); |
| @@ -129757,11 +129765,11 @@ | |
| 129765 | ** from 2009-02-02 for compatibility of applications that exploited the |
| 129766 | ** old buggy behavior. */ |
| 129767 | if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */ |
| 129768 | #endif |
| 129769 | if( argc==3 ){ |
| 129770 | p2 = sqlite3_value_int64(argv[2]); |
| 129771 | if( p2<0 ){ |
| 129772 | p2 = -p2; |
| 129773 | negP2 = 1; |
| 129774 | } |
| 129775 | }else{ |
| @@ -129796,13 +129804,15 @@ | |
| 129804 | SQLITE_SKIP_UTF8(z2); |
| 129805 | } |
| 129806 | sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT, |
| 129807 | SQLITE_UTF8); |
| 129808 | }else{ |
| 129809 | if( p1>=len ){ |
| 129810 | p1 = p2 = 0; |
| 129811 | }else if( p2>len-p1 ){ |
| 129812 | p2 = len-p1; |
| 129813 | assert( p2>0 ); |
| 129814 | } |
| 129815 | sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT); |
| 129816 | } |
| 129817 | } |
| 129818 | |
| @@ -129809,17 +129819,17 @@ | |
| 129819 | /* |
| 129820 | ** Implementation of the round() function |
| 129821 | */ |
| 129822 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 129823 | static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 129824 | i64 n = 0; |
| 129825 | double r; |
| 129826 | char *zBuf; |
| 129827 | assert( argc==1 || argc==2 ); |
| 129828 | if( argc==2 ){ |
| 129829 | if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; |
| 129830 | n = sqlite3_value_int64(argv[1]); |
| 129831 | if( n>30 ) n = 30; |
| 129832 | if( n<0 ) n = 0; |
| 129833 | } |
| 129834 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 129835 | r = sqlite3_value_double(argv[0]); |
| @@ -141316,11 +141326,11 @@ | |
| 141326 | sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); |
| 141327 | sqlite3ClearTempRegCache(pParse); |
| 141328 | |
| 141329 | /* Do the b-tree integrity checks */ |
| 141330 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141331 | sqlite3VdbeChangeP5(v, (u16)i); |
| 141332 | addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); |
| 141333 | sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, |
| 141334 | sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), |
| 141335 | P4_DYNAMIC); |
| 141336 | sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3); |
| @@ -150549,11 +150559,11 @@ | |
| 150559 | } |
| 150560 | sqlite3ReleaseTempReg(pParse, regSubtype); |
| 150561 | } |
| 150562 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150563 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150564 | sqlite3VdbeChangeP5(v, (u16)nArg); |
| 150565 | sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); |
| 150566 | sqlite3VdbeJumpHere(v, iTop); |
| 150567 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150568 | } |
| 150569 | sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), |
| @@ -150712,11 +150722,11 @@ | |
| 150722 | sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, |
| 150723 | (char *)pColl, P4_COLLSEQ); |
| 150724 | } |
| 150725 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150726 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150727 | sqlite3VdbeChangeP5(v, (u16)nArg); |
| 150728 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150729 | } |
| 150730 | if( addrNext ){ |
| 150731 | sqlite3VdbeResolveLabel(v, addrNext); |
| 150732 | } |
| @@ -154106,11 +154116,11 @@ | |
| 154116 | /* Set the P5 operand of the OP_Program instruction to non-zero if |
| 154117 | ** recursive invocation of this trigger program is disallowed. Recursive |
| 154118 | ** invocation is disallowed if (a) the sub-program is really a trigger, |
| 154119 | ** not a foreign key action, and (b) the flag to enable recursive triggers |
| 154120 | ** is clear. */ |
| 154121 | sqlite3VdbeChangeP5(v, (u16)bRecursive); |
| 154122 | } |
| 154123 | } |
| 154124 | |
| 154125 | /* |
| 154126 | ** This is called to code the required FOR EACH ROW triggers for an operation |
| @@ -170414,10 +170424,11 @@ | |
| 170424 | int pc, |
| 170425 | VdbeOp *pOp |
| 170426 | ){ |
| 170427 | if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; |
| 170428 | sqlite3VdbePrintOp(0, pc, pOp); |
| 170429 | sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */ |
| 170430 | } |
| 170431 | #endif |
| 170432 | |
| 170433 | /* |
| 170434 | ** Generate the end of the WHERE loop. See comments on |
| @@ -172523,11 +172534,11 @@ | |
| 172534 | sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); |
| 172535 | } |
| 172536 | sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, |
| 172537 | bInverse, regArg, pWin->regAccum); |
| 172538 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172539 | sqlite3VdbeChangeP5(v, (u16)nArg); |
| 172540 | if( pWin->bExprArgs ){ |
| 172541 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172542 | } |
| 172543 | } |
| 172544 | |
| @@ -184078,12 +184089,12 @@ | |
| 184089 | # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 |
| 184090 | #endif |
| 184091 | #if SQLITE_MAX_VDBE_OP<40 |
| 184092 | # error SQLITE_MAX_VDBE_OP must be at least 40 |
| 184093 | #endif |
| 184094 | #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767 |
| 184095 | # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767 |
| 184096 | #endif |
| 184097 | #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 |
| 184098 | # error SQLITE_MAX_ATTACHED must be between 0 and 125 |
| 184099 | #endif |
| 184100 | #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 |
| @@ -185492,11 +185503,10 @@ | |
| 185503 | #if defined(SQLITE_DEBUG) |
| 185504 | /* Invoke these debugging routines so that the compiler does not |
| 185505 | ** issue "defined but not used" warnings. */ |
| 185506 | if( x==9999 ){ |
| 185507 | sqlite3ShowExpr(0); |
| 185508 | sqlite3ShowExprList(0); |
| 185509 | sqlite3ShowIdList(0); |
| 185510 | sqlite3ShowSrcList(0); |
| 185511 | sqlite3ShowWith(0); |
| 185512 | sqlite3ShowUpsert(0); |
| @@ -185509,11 +185519,10 @@ | |
| 185519 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185520 | sqlite3ShowWindow(0); |
| 185521 | sqlite3ShowWinFunc(0); |
| 185522 | #endif |
| 185523 | sqlite3ShowSelect(0); |
| 185524 | } |
| 185525 | #endif |
| 185526 | break; |
| 185527 | } |
| 185528 | |
| @@ -226344,11 +226353,15 @@ | |
| 226353 | static int dbpageSync(sqlite3_vtab *pVtab){ |
| 226354 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226355 | if( pTab->pgnoTrunc>0 ){ |
| 226356 | Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; |
| 226357 | Pager *pPager = sqlite3BtreePager(pBt); |
| 226358 | sqlite3BtreeEnter(pBt); |
| 226359 | if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){ |
| 226360 | sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc); |
| 226361 | } |
| 226362 | sqlite3BtreeLeave(pBt); |
| 226363 | } |
| 226364 | pTab->pgnoTrunc = 0; |
| 226365 | return SQLITE_OK; |
| 226366 | } |
| 226367 | |
| @@ -255419,11 +255432,11 @@ | |
| 255432 | int nArg, /* Number of args */ |
| 255433 | sqlite3_value **apUnused /* Function arguments */ |
| 255434 | ){ |
| 255435 | assert( nArg==0 ); |
| 255436 | UNUSED_PARAM2(nArg, apUnused); |
| 255437 | sqlite3_result_text(pCtx, "fts5: 2024-12-19 14:20:47 5b96dcf5f6bf41dcb89ced64efd4585e36dce718c428c2324d94e4942905c3bb", -1, SQLITE_TRANSIENT); |
| 255438 | } |
| 255439 | |
| 255440 | /* |
| 255441 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255442 | ** |
| 255443 |
+1
-1
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.48.0" |
| 150 | 150 | #define SQLITE_VERSION_NUMBER 3048000 |
| 151 | -#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" | |
| 151 | +#define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| 157 | 157 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.48.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3048000 |
| 151 | #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| 157 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.48.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3048000 |
| 151 | #define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| 157 |
+3
-4
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -171,23 +171,22 @@ | ||
| 171 | 171 | ** each character as a flag to enable writing "manifest", "manifest.uuid" or |
| 172 | 172 | ** "manifest.tags". |
| 173 | 173 | */ |
| 174 | 174 | void manifest_to_disk(int vid){ |
| 175 | 175 | char *zManFile; |
| 176 | - Blob manifest; | |
| 177 | - Blob taglist; | |
| 178 | 176 | int flg; |
| 179 | 177 | |
| 180 | 178 | flg = db_get_manifest_setting(); |
| 181 | 179 | |
| 182 | 180 | if( flg & MFESTFLG_RAW ){ |
| 183 | - blob_zero(&manifest); | |
| 181 | + Blob manifest = BLOB_INITIALIZER; | |
| 184 | 182 | content_get(vid, &manifest); |
| 185 | 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 186 | 184 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 187 | 185 | blob_write_to_file(&manifest, zManFile); |
| 188 | 186 | free(zManFile); |
| 187 | + blob_reset(&manifest); | |
| 189 | 188 | }else{ |
| 190 | 189 | if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ |
| 191 | 190 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 192 | 191 | file_delete(zManFile); |
| 193 | 192 | free(zManFile); |
| @@ -207,11 +206,11 @@ | ||
| 207 | 206 | file_delete(zManFile); |
| 208 | 207 | free(zManFile); |
| 209 | 208 | } |
| 210 | 209 | } |
| 211 | 210 | if( flg & MFESTFLG_TAGS ){ |
| 212 | - blob_zero(&taglist); | |
| 211 | + Blob taglist = BLOB_INITIALIZER; | |
| 213 | 212 | zManFile = mprintf("%smanifest.tags", g.zLocalRoot); |
| 214 | 213 | get_checkin_taglist(vid, &taglist); |
| 215 | 214 | blob_write_to_file(&taglist, zManFile); |
| 216 | 215 | free(zManFile); |
| 217 | 216 | blob_reset(&taglist); |
| 218 | 217 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -171,23 +171,22 @@ | |
| 171 | ** each character as a flag to enable writing "manifest", "manifest.uuid" or |
| 172 | ** "manifest.tags". |
| 173 | */ |
| 174 | void manifest_to_disk(int vid){ |
| 175 | char *zManFile; |
| 176 | Blob manifest; |
| 177 | Blob taglist; |
| 178 | int flg; |
| 179 | |
| 180 | flg = db_get_manifest_setting(); |
| 181 | |
| 182 | if( flg & MFESTFLG_RAW ){ |
| 183 | blob_zero(&manifest); |
| 184 | content_get(vid, &manifest); |
| 185 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 186 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 187 | blob_write_to_file(&manifest, zManFile); |
| 188 | free(zManFile); |
| 189 | }else{ |
| 190 | if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ |
| 191 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 192 | file_delete(zManFile); |
| 193 | free(zManFile); |
| @@ -207,11 +206,11 @@ | |
| 207 | file_delete(zManFile); |
| 208 | free(zManFile); |
| 209 | } |
| 210 | } |
| 211 | if( flg & MFESTFLG_TAGS ){ |
| 212 | blob_zero(&taglist); |
| 213 | zManFile = mprintf("%smanifest.tags", g.zLocalRoot); |
| 214 | get_checkin_taglist(vid, &taglist); |
| 215 | blob_write_to_file(&taglist, zManFile); |
| 216 | free(zManFile); |
| 217 | blob_reset(&taglist); |
| 218 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -171,23 +171,22 @@ | |
| 171 | ** each character as a flag to enable writing "manifest", "manifest.uuid" or |
| 172 | ** "manifest.tags". |
| 173 | */ |
| 174 | void manifest_to_disk(int vid){ |
| 175 | char *zManFile; |
| 176 | int flg; |
| 177 | |
| 178 | flg = db_get_manifest_setting(); |
| 179 | |
| 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | content_get(vid, &manifest); |
| 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 184 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 185 | blob_write_to_file(&manifest, zManFile); |
| 186 | free(zManFile); |
| 187 | blob_reset(&manifest); |
| 188 | }else{ |
| 189 | if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ |
| 190 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 191 | file_delete(zManFile); |
| 192 | free(zManFile); |
| @@ -207,11 +206,11 @@ | |
| 206 | file_delete(zManFile); |
| 207 | free(zManFile); |
| 208 | } |
| 209 | } |
| 210 | if( flg & MFESTFLG_TAGS ){ |
| 211 | Blob taglist = BLOB_INITIALIZER; |
| 212 | zManFile = mprintf("%smanifest.tags", g.zLocalRoot); |
| 213 | get_checkin_taglist(vid, &taglist); |
| 214 | blob_write_to_file(&taglist, zManFile); |
| 215 | free(zManFile); |
| 216 | blob_reset(&taglist); |
| 217 |
M
src/db.c
+1
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -2105,11 +2105,11 @@ | ||
| 2105 | 2105 | sqlite3 *, char **, const sqlite3_api_routines * |
| 2106 | 2106 | ); |
| 2107 | 2107 | sqlite3_appendvfs_init(0,0,0); |
| 2108 | 2108 | g.zVfsName = "apndvfs"; |
| 2109 | 2109 | } |
| 2110 | - blob_zero(&bNameCheck); | |
| 2110 | + blob_reset(&bNameCheck); | |
| 2111 | 2111 | rc = sqlite3_open_v2( |
| 2112 | 2112 | zDbName, &db, |
| 2113 | 2113 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 2114 | 2114 | g.zVfsName |
| 2115 | 2115 | ); |
| 2116 | 2116 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -2105,11 +2105,11 @@ | |
| 2105 | sqlite3 *, char **, const sqlite3_api_routines * |
| 2106 | ); |
| 2107 | sqlite3_appendvfs_init(0,0,0); |
| 2108 | g.zVfsName = "apndvfs"; |
| 2109 | } |
| 2110 | blob_zero(&bNameCheck); |
| 2111 | rc = sqlite3_open_v2( |
| 2112 | zDbName, &db, |
| 2113 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 2114 | g.zVfsName |
| 2115 | ); |
| 2116 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -2105,11 +2105,11 @@ | |
| 2105 | sqlite3 *, char **, const sqlite3_api_routines * |
| 2106 | ); |
| 2107 | sqlite3_appendvfs_init(0,0,0); |
| 2108 | g.zVfsName = "apndvfs"; |
| 2109 | } |
| 2110 | blob_reset(&bNameCheck); |
| 2111 | rc = sqlite3_open_v2( |
| 2112 | zDbName, &db, |
| 2113 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 2114 | g.zVfsName |
| 2115 | ); |
| 2116 |
+8
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -788,11 +788,11 @@ | ||
| 788 | 788 | ){ |
| 789 | 789 | Blob title; |
| 790 | 790 | int isPopup = P("popup")!=0; |
| 791 | 791 | blob_init(&title,0,0); |
| 792 | 792 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 793 | - Blob tail; | |
| 793 | + Blob tail = BLOB_INITIALIZER; | |
| 794 | 794 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 795 | 795 | if( wiki_find_title(pBody, &title, &tail) ){ |
| 796 | 796 | if( !isPopup ) style_header("%s", blob_str(&title)); |
| 797 | 797 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 798 | 798 | }else{ |
| @@ -801,10 +801,11 @@ | ||
| 801 | 801 | } |
| 802 | 802 | if( !isPopup ){ |
| 803 | 803 | document_emit_js(); |
| 804 | 804 | style_finish_page(); |
| 805 | 805 | } |
| 806 | + blob_reset(&tail); | |
| 806 | 807 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 807 | 808 | Blob tail = BLOB_INITIALIZER; |
| 808 | 809 | markdown_to_html(pBody, &title, &tail); |
| 809 | 810 | if( !isPopup ){ |
| 810 | 811 | if( blob_size(&title)>0 ){ |
| @@ -816,10 +817,11 @@ | ||
| 816 | 817 | convert_href_and_output(&tail); |
| 817 | 818 | if( !isPopup ){ |
| 818 | 819 | document_emit_js(); |
| 819 | 820 | style_finish_page(); |
| 820 | 821 | } |
| 822 | + blob_reset(&tail); | |
| 821 | 823 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 822 | 824 | style_header("%s", zDefaultTitle); |
| 823 | 825 | @ <blockquote><pre> |
| 824 | 826 | @ %h(blob_str(pBody)) |
| 825 | 827 | @ </pre></blockquote> |
| @@ -949,10 +951,11 @@ | ||
| 949 | 951 | |
| 950 | 952 | login_check_credentials(); |
| 951 | 953 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 952 | 954 | style_set_current_feature("doc"); |
| 953 | 955 | blob_init(&title, 0, 0); |
| 956 | + blob_init(&filebody, 0, 0); | |
| 954 | 957 | zDfltTitle = isUV ? "" : "Documentation"; |
| 955 | 958 | db_begin_transaction(); |
| 956 | 959 | while( rid==0 && (++nMiss)<=count(azSuffix) ){ |
| 957 | 960 | zName = P("name"); |
| 958 | 961 | if( isUV ){ |
| @@ -1059,10 +1062,12 @@ | ||
| 1059 | 1062 | } |
| 1060 | 1063 | cgi_check_for_malice(); |
| 1061 | 1064 | document_render(&filebody, zMime, zDfltTitle, zName); |
| 1062 | 1065 | if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found"); |
| 1063 | 1066 | db_end_transaction(0); |
| 1067 | + blob_reset(&title); | |
| 1068 | + blob_reset(&filebody); | |
| 1064 | 1069 | return; |
| 1065 | 1070 | |
| 1066 | 1071 | /* Jump here when unable to locate the document */ |
| 1067 | 1072 | doc_not_found: |
| 1068 | 1073 | db_end_transaction(0); |
| @@ -1075,10 +1080,12 @@ | ||
| 1075 | 1080 | @ <p>Document %h(zOrigName) not found |
| 1076 | 1081 | if( fossil_strcmp(zCheckin,"ckout")!=0 ){ |
| 1077 | 1082 | @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> |
| 1078 | 1083 | } |
| 1079 | 1084 | style_finish_page(); |
| 1085 | + blob_reset(&title); | |
| 1086 | + blob_reset(&filebody); | |
| 1080 | 1087 | return; |
| 1081 | 1088 | } |
| 1082 | 1089 | |
| 1083 | 1090 | /* |
| 1084 | 1091 | ** The default logo. |
| 1085 | 1092 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -788,11 +788,11 @@ | |
| 788 | ){ |
| 789 | Blob title; |
| 790 | int isPopup = P("popup")!=0; |
| 791 | blob_init(&title,0,0); |
| 792 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 793 | Blob tail; |
| 794 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 795 | if( wiki_find_title(pBody, &title, &tail) ){ |
| 796 | if( !isPopup ) style_header("%s", blob_str(&title)); |
| 797 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 798 | }else{ |
| @@ -801,10 +801,11 @@ | |
| 801 | } |
| 802 | if( !isPopup ){ |
| 803 | document_emit_js(); |
| 804 | style_finish_page(); |
| 805 | } |
| 806 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 807 | Blob tail = BLOB_INITIALIZER; |
| 808 | markdown_to_html(pBody, &title, &tail); |
| 809 | if( !isPopup ){ |
| 810 | if( blob_size(&title)>0 ){ |
| @@ -816,10 +817,11 @@ | |
| 816 | convert_href_and_output(&tail); |
| 817 | if( !isPopup ){ |
| 818 | document_emit_js(); |
| 819 | style_finish_page(); |
| 820 | } |
| 821 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 822 | style_header("%s", zDefaultTitle); |
| 823 | @ <blockquote><pre> |
| 824 | @ %h(blob_str(pBody)) |
| 825 | @ </pre></blockquote> |
| @@ -949,10 +951,11 @@ | |
| 949 | |
| 950 | login_check_credentials(); |
| 951 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 952 | style_set_current_feature("doc"); |
| 953 | blob_init(&title, 0, 0); |
| 954 | zDfltTitle = isUV ? "" : "Documentation"; |
| 955 | db_begin_transaction(); |
| 956 | while( rid==0 && (++nMiss)<=count(azSuffix) ){ |
| 957 | zName = P("name"); |
| 958 | if( isUV ){ |
| @@ -1059,10 +1062,12 @@ | |
| 1059 | } |
| 1060 | cgi_check_for_malice(); |
| 1061 | document_render(&filebody, zMime, zDfltTitle, zName); |
| 1062 | if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found"); |
| 1063 | db_end_transaction(0); |
| 1064 | return; |
| 1065 | |
| 1066 | /* Jump here when unable to locate the document */ |
| 1067 | doc_not_found: |
| 1068 | db_end_transaction(0); |
| @@ -1075,10 +1080,12 @@ | |
| 1075 | @ <p>Document %h(zOrigName) not found |
| 1076 | if( fossil_strcmp(zCheckin,"ckout")!=0 ){ |
| 1077 | @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> |
| 1078 | } |
| 1079 | style_finish_page(); |
| 1080 | return; |
| 1081 | } |
| 1082 | |
| 1083 | /* |
| 1084 | ** The default logo. |
| 1085 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -788,11 +788,11 @@ | |
| 788 | ){ |
| 789 | Blob title; |
| 790 | int isPopup = P("popup")!=0; |
| 791 | blob_init(&title,0,0); |
| 792 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 793 | Blob tail = BLOB_INITIALIZER; |
| 794 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 795 | if( wiki_find_title(pBody, &title, &tail) ){ |
| 796 | if( !isPopup ) style_header("%s", blob_str(&title)); |
| 797 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 798 | }else{ |
| @@ -801,10 +801,11 @@ | |
| 801 | } |
| 802 | if( !isPopup ){ |
| 803 | document_emit_js(); |
| 804 | style_finish_page(); |
| 805 | } |
| 806 | blob_reset(&tail); |
| 807 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 808 | Blob tail = BLOB_INITIALIZER; |
| 809 | markdown_to_html(pBody, &title, &tail); |
| 810 | if( !isPopup ){ |
| 811 | if( blob_size(&title)>0 ){ |
| @@ -816,10 +817,11 @@ | |
| 817 | convert_href_and_output(&tail); |
| 818 | if( !isPopup ){ |
| 819 | document_emit_js(); |
| 820 | style_finish_page(); |
| 821 | } |
| 822 | blob_reset(&tail); |
| 823 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 824 | style_header("%s", zDefaultTitle); |
| 825 | @ <blockquote><pre> |
| 826 | @ %h(blob_str(pBody)) |
| 827 | @ </pre></blockquote> |
| @@ -949,10 +951,11 @@ | |
| 951 | |
| 952 | login_check_credentials(); |
| 953 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 954 | style_set_current_feature("doc"); |
| 955 | blob_init(&title, 0, 0); |
| 956 | blob_init(&filebody, 0, 0); |
| 957 | zDfltTitle = isUV ? "" : "Documentation"; |
| 958 | db_begin_transaction(); |
| 959 | while( rid==0 && (++nMiss)<=count(azSuffix) ){ |
| 960 | zName = P("name"); |
| 961 | if( isUV ){ |
| @@ -1059,10 +1062,12 @@ | |
| 1062 | } |
| 1063 | cgi_check_for_malice(); |
| 1064 | document_render(&filebody, zMime, zDfltTitle, zName); |
| 1065 | if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found"); |
| 1066 | db_end_transaction(0); |
| 1067 | blob_reset(&title); |
| 1068 | blob_reset(&filebody); |
| 1069 | return; |
| 1070 | |
| 1071 | /* Jump here when unable to locate the document */ |
| 1072 | doc_not_found: |
| 1073 | db_end_transaction(0); |
| @@ -1075,10 +1080,12 @@ | |
| 1080 | @ <p>Document %h(zOrigName) not found |
| 1081 | if( fossil_strcmp(zCheckin,"ckout")!=0 ){ |
| 1082 | @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> |
| 1083 | } |
| 1084 | style_finish_page(); |
| 1085 | blob_reset(&title); |
| 1086 | blob_reset(&filebody); |
| 1087 | return; |
| 1088 | } |
| 1089 | |
| 1090 | /* |
| 1091 | ** The default logo. |
| 1092 |
+18
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -729,10 +729,28 @@ | ||
| 729 | 729 | while( *z && n-- ){ |
| 730 | 730 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 731 | 731 | z++; |
| 732 | 732 | } |
| 733 | 733 | } |
| 734 | + | |
| 735 | +/* | |
| 736 | +** Decode hexadecimal into a string and return the new string. Space to | |
| 737 | +** hold the string is obtained from fossil_malloc() and should be released | |
| 738 | +** by the caller. | |
| 739 | +** | |
| 740 | +** If the input is not hex, return NULL. | |
| 741 | +*/ | |
| 742 | +char *decode16_dup(const char *zIn){ | |
| 743 | + int nIn = (int)strlen(zIn); | |
| 744 | + char *zOut; | |
| 745 | + if( !validate16(zIn, nIn) ) return 0; | |
| 746 | + zOut = fossil_malloc(nIn/2+1); | |
| 747 | + decode16((const u8*)zIn, (u8*)zOut, nIn); | |
| 748 | + zOut[nIn/2] = 0; | |
| 749 | + return zOut; | |
| 750 | +} | |
| 751 | + | |
| 734 | 752 | |
| 735 | 753 | /* |
| 736 | 754 | ** Decode a string encoded using "quoted-printable". |
| 737 | 755 | ** |
| 738 | 756 | ** (1) "=" followed by two hex digits becomes a single |
| 739 | 757 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -729,10 +729,28 @@ | |
| 729 | while( *z && n-- ){ |
| 730 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 731 | z++; |
| 732 | } |
| 733 | } |
| 734 | |
| 735 | /* |
| 736 | ** Decode a string encoded using "quoted-printable". |
| 737 | ** |
| 738 | ** (1) "=" followed by two hex digits becomes a single |
| 739 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -729,10 +729,28 @@ | |
| 729 | while( *z && n-- ){ |
| 730 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 731 | z++; |
| 732 | } |
| 733 | } |
| 734 | |
| 735 | /* |
| 736 | ** Decode hexadecimal into a string and return the new string. Space to |
| 737 | ** hold the string is obtained from fossil_malloc() and should be released |
| 738 | ** by the caller. |
| 739 | ** |
| 740 | ** If the input is not hex, return NULL. |
| 741 | */ |
| 742 | char *decode16_dup(const char *zIn){ |
| 743 | int nIn = (int)strlen(zIn); |
| 744 | char *zOut; |
| 745 | if( !validate16(zIn, nIn) ) return 0; |
| 746 | zOut = fossil_malloc(nIn/2+1); |
| 747 | decode16((const u8*)zIn, (u8*)zOut, nIn); |
| 748 | zOut[nIn/2] = 0; |
| 749 | return zOut; |
| 750 | } |
| 751 | |
| 752 | |
| 753 | /* |
| 754 | ** Decode a string encoded using "quoted-printable". |
| 755 | ** |
| 756 | ** (1) "=" followed by two hex digits becomes a single |
| 757 |
-2
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -1830,11 +1830,10 @@ | ||
| 1830 | 1830 | if( db_int(0, "SELECT count(*) FROM user " |
| 1831 | 1831 | " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){ |
| 1832 | 1832 | @ <p>No non-supervisor moderators |
| 1833 | 1833 | }else{ |
| 1834 | 1834 | Stmt q = empty_Stmt; |
| 1835 | - int nRows = 0; | |
| 1836 | 1835 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1837 | 1836 | "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'" |
| 1838 | 1837 | " ORDER BY login"); |
| 1839 | 1838 | @ <table class='bordered'> |
| 1840 | 1839 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| @@ -1841,11 +1840,10 @@ | ||
| 1841 | 1840 | @ <tbody> |
| 1842 | 1841 | while( SQLITE_ROW==db_step(&q) ){ |
| 1843 | 1842 | const int iUid = db_column_int(&q, 0); |
| 1844 | 1843 | const char *zUser = db_column_text(&q, 1); |
| 1845 | 1844 | const char *zCap = db_column_text(&q, 2); |
| 1846 | - ++nRows; | |
| 1847 | 1845 | @ <tr> |
| 1848 | 1846 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1849 | 1847 | @ <td>(%h(zCap))</td> |
| 1850 | 1848 | @ </tr> |
| 1851 | 1849 | } |
| 1852 | 1850 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1830,11 +1830,10 @@ | |
| 1830 | if( db_int(0, "SELECT count(*) FROM user " |
| 1831 | " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){ |
| 1832 | @ <p>No non-supervisor moderators |
| 1833 | }else{ |
| 1834 | Stmt q = empty_Stmt; |
| 1835 | int nRows = 0; |
| 1836 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1837 | "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'" |
| 1838 | " ORDER BY login"); |
| 1839 | @ <table class='bordered'> |
| 1840 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| @@ -1841,11 +1840,10 @@ | |
| 1841 | @ <tbody> |
| 1842 | while( SQLITE_ROW==db_step(&q) ){ |
| 1843 | const int iUid = db_column_int(&q, 0); |
| 1844 | const char *zUser = db_column_text(&q, 1); |
| 1845 | const char *zCap = db_column_text(&q, 2); |
| 1846 | ++nRows; |
| 1847 | @ <tr> |
| 1848 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1849 | @ <td>(%h(zCap))</td> |
| 1850 | @ </tr> |
| 1851 | } |
| 1852 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -1830,11 +1830,10 @@ | |
| 1830 | if( db_int(0, "SELECT count(*) FROM user " |
| 1831 | " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){ |
| 1832 | @ <p>No non-supervisor moderators |
| 1833 | }else{ |
| 1834 | Stmt q = empty_Stmt; |
| 1835 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1836 | "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'" |
| 1837 | " ORDER BY login"); |
| 1838 | @ <table class='bordered'> |
| 1839 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| @@ -1841,11 +1840,10 @@ | |
| 1840 | @ <tbody> |
| 1841 | while( SQLITE_ROW==db_step(&q) ){ |
| 1842 | const int iUid = db_column_int(&q, 0); |
| 1843 | const char *zUser = db_column_text(&q, 1); |
| 1844 | const char *zCap = db_column_text(&q, 2); |
| 1845 | @ <tr> |
| 1846 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1847 | @ <td>(%h(zCap))</td> |
| 1848 | @ </tr> |
| 1849 | } |
| 1850 |
+10
-1
| --- src/fossil.diff.js | ||
| +++ src/fossil.diff.js | ||
| @@ -28,17 +28,21 @@ | ||
| 28 | 28 | window.fossil.onPageLoad(function(){ |
| 29 | 29 | /** |
| 30 | 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | 31 | /info and similar pages. |
| 32 | 32 | */ |
| 33 | + if( !window.fossil.page.diffControlContainer ){ | |
| 34 | + return; | |
| 35 | + } | |
| 33 | 36 | const D = window.fossil.dom; |
| 34 | 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 35 | 38 | let checkedCount = |
| 36 | 39 | 0 /* When showing more than one diff, keep track of how many |
| 37 | 40 | "show/hide" checkboxes are are checked so we can update the |
| 38 | 41 | "show/hide all" label dynamically. */; |
| 39 | - let btnAll /* show/hide all diffs UI control */; | |
| 42 | + let btnAll /* UI control to show/hide all diffs */; | |
| 43 | + /* Install a diff-toggle button for the given diff table element. */ | |
| 40 | 44 | const addToggle = function(diffElem){ |
| 41 | 45 | const sib = diffElem.previousElementSibling, |
| 42 | 46 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 43 | 47 | if(!sib) return; |
| 44 | 48 | const lblToggle = D.label(); |
| @@ -59,10 +63,15 @@ | ||
| 59 | 63 | if( !document.querySelector('body.fdiff') ){ |
| 60 | 64 | /* Don't show the diff toggle button for /fdiff because it only |
| 61 | 65 | has a single file to show (and also a different DOM layout). */ |
| 62 | 66 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 63 | 67 | } |
| 68 | + /** | |
| 69 | + Set up a "toggle all diffs" button which toggles all of the | |
| 70 | + above-installed checkboxes, but only if more than one diff is | |
| 71 | + rendered. | |
| 72 | + */ | |
| 64 | 73 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 65 | 74 | if(icm) { |
| 66 | 75 | btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); |
| 67 | 76 | D.append( icm, btnAll ); |
| 68 | 77 | btnAll.addEventListener('click', function(ev){ |
| 69 | 78 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -28,17 +28,21 @@ | |
| 28 | window.fossil.onPageLoad(function(){ |
| 29 | /** |
| 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | /info and similar pages. |
| 32 | */ |
| 33 | const D = window.fossil.dom; |
| 34 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 35 | let checkedCount = |
| 36 | 0 /* When showing more than one diff, keep track of how many |
| 37 | "show/hide" checkboxes are are checked so we can update the |
| 38 | "show/hide all" label dynamically. */; |
| 39 | let btnAll /* show/hide all diffs UI control */; |
| 40 | const addToggle = function(diffElem){ |
| 41 | const sib = diffElem.previousElementSibling, |
| 42 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 43 | if(!sib) return; |
| 44 | const lblToggle = D.label(); |
| @@ -59,10 +63,15 @@ | |
| 59 | if( !document.querySelector('body.fdiff') ){ |
| 60 | /* Don't show the diff toggle button for /fdiff because it only |
| 61 | has a single file to show (and also a different DOM layout). */ |
| 62 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 63 | } |
| 64 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 65 | if(icm) { |
| 66 | btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); |
| 67 | D.append( icm, btnAll ); |
| 68 | btnAll.addEventListener('click', function(ev){ |
| 69 |
| --- src/fossil.diff.js | |
| +++ src/fossil.diff.js | |
| @@ -28,17 +28,21 @@ | |
| 28 | window.fossil.onPageLoad(function(){ |
| 29 | /** |
| 30 | Adds toggle checkboxes to each file entry in the diff views for |
| 31 | /info and similar pages. |
| 32 | */ |
| 33 | if( !window.fossil.page.diffControlContainer ){ |
| 34 | return; |
| 35 | } |
| 36 | const D = window.fossil.dom; |
| 37 | const allToggles = [/*collection of all diff-toggle checkboxes*/]; |
| 38 | let checkedCount = |
| 39 | 0 /* When showing more than one diff, keep track of how many |
| 40 | "show/hide" checkboxes are are checked so we can update the |
| 41 | "show/hide all" label dynamically. */; |
| 42 | let btnAll /* UI control to show/hide all diffs */; |
| 43 | /* Install a diff-toggle button for the given diff table element. */ |
| 44 | const addToggle = function(diffElem){ |
| 45 | const sib = diffElem.previousElementSibling, |
| 46 | ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0; |
| 47 | if(!sib) return; |
| 48 | const lblToggle = D.label(); |
| @@ -59,10 +63,15 @@ | |
| 63 | if( !document.querySelector('body.fdiff') ){ |
| 64 | /* Don't show the diff toggle button for /fdiff because it only |
| 65 | has a single file to show (and also a different DOM layout). */ |
| 66 | document.querySelectorAll('table.diff').forEach(addToggle); |
| 67 | } |
| 68 | /** |
| 69 | Set up a "toggle all diffs" button which toggles all of the |
| 70 | above-installed checkboxes, but only if more than one diff is |
| 71 | rendered. |
| 72 | */ |
| 73 | const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0; |
| 74 | if(icm) { |
| 75 | btnAll = D.addClass(D.a("#", "Hide diffs"), "button"); |
| 76 | D.append( icm, btnAll ); |
| 77 | btnAll.addEventListener('click', function(ev){ |
| 78 |
+149
-12
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -320,10 +320,11 @@ | ||
| 320 | 320 | |TIMELINE_NOSCROLL |
| 321 | 321 | |TIMELINE_XMERGE |
| 322 | 322 | |TIMELINE_CHPICK, |
| 323 | 323 | 0, 0, 0, rid, rid2, 0); |
| 324 | 324 | db_finalize(&q); |
| 325 | + blob_reset(&sql); | |
| 325 | 326 | } |
| 326 | 327 | |
| 327 | 328 | |
| 328 | 329 | /* |
| 329 | 330 | ** Append the difference between artifacts to the output |
| @@ -624,11 +625,10 @@ | ||
| 624 | 625 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 625 | 626 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 626 | 627 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 627 | 628 | if( nChng==0 ){ |
| 628 | 629 | @ <p>No uncommitted changes</p> |
| 629 | - style_finish_page(); | |
| 630 | 630 | return; |
| 631 | 631 | } |
| 632 | 632 | db_prepare(&q, |
| 633 | 633 | /* 0 1 2 3 4 5 6 */ |
| 634 | 634 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -785,17 +785,12 @@ | ||
| 785 | 785 | @ Changes to %h(zFile) |
| 786 | 786 | @ </span></div> |
| 787 | 787 | if( pCfg ){ |
| 788 | 788 | char *zFullFN; |
| 789 | 789 | char *zHexFN; |
| 790 | - int nFullFN; | |
| 791 | 790 | zFullFN = file_canonical_name_dup(zLhs); |
| 792 | - nFullFN = (int)strlen(zFullFN); | |
| 793 | - zHexFN = fossil_malloc( nFullFN*2 + 5 ); | |
| 794 | - zHexFN[0] = 'x'; | |
| 795 | - encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN); | |
| 796 | - zHexFN[1+nFullFN*2] = 0; | |
| 791 | + zHexFN = mprintf("x%H", zFullFN); | |
| 797 | 792 | fossil_free(zFullFN); |
| 798 | 793 | pCfg->zLeftHash = zHexFN; |
| 799 | 794 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 800 | 795 | pCfg->zLeftHash = 0; |
| 801 | 796 | fossil_free(zHexFN); |
| @@ -820,11 +815,14 @@ | ||
| 820 | 815 | ** |
| 821 | 816 | ** If the "exbase=PATH" query parameter is provided, then the diff shown |
| 822 | 817 | ** uses the files in PATH as the baseline. This is the same as using |
| 823 | 818 | ** the "--from PATH" argument to the "fossil diff" command-line. In fact, |
| 824 | 819 | ** when using "fossil ui --from PATH", the --from argument becomes the value |
| 825 | -** of the exbase query parameter for the start page. | |
| 820 | +** of the exbase query parameter for the start page. Note that if PATH | |
| 821 | +** is a pure hexadecimal string, it is decoded first before being used as | |
| 822 | +** the pathname. Real pathnames should contain at least one directory | |
| 823 | +** separator character. | |
| 826 | 824 | ** |
| 827 | 825 | ** Other query parameters related to diffs are also accepted. |
| 828 | 826 | */ |
| 829 | 827 | void ckout_page(void){ |
| 830 | 828 | int vid; |
| @@ -832,11 +830,11 @@ | ||
| 832 | 830 | int nHome; |
| 833 | 831 | const char *zExBase; |
| 834 | 832 | char *zHostname; |
| 835 | 833 | char *zCwd; |
| 836 | 834 | |
| 837 | - if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ | |
| 835 | + if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){ | |
| 838 | 836 | cgi_redirectf("%R/home"); |
| 839 | 837 | return; |
| 840 | 838 | } |
| 841 | 839 | file_chdir(g.zLocalRoot, 0); |
| 842 | 840 | vid = db_lget_int("checkout", 0); |
| @@ -862,18 +860,20 @@ | ||
| 862 | 860 | } |
| 863 | 861 | render_checkin_context(vid, 0, 0, 0); |
| 864 | 862 | @ <hr> |
| 865 | 863 | zExBase = P("exbase"); |
| 866 | 864 | if( zExBase && zExBase[0] ){ |
| 867 | - char *zCBase = file_canonical_name_dup(zExBase); | |
| 865 | + char *zPath = decode16_dup(zExBase); | |
| 866 | + char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase); | |
| 868 | 867 | if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){ |
| 869 | 868 | @ <p>Using external baseline: ~%h(zCBase+nHome)</p> |
| 870 | 869 | }else{ |
| 871 | 870 | @ <p>Using external baseline: %h(zCBase)</p> |
| 872 | 871 | } |
| 873 | 872 | ckout_external_base_diff(vid, zCBase); |
| 874 | 873 | fossil_free(zCBase); |
| 874 | + fossil_free(zPath); | |
| 875 | 875 | }else{ |
| 876 | 876 | ckout_normal_diff(vid); |
| 877 | 877 | } |
| 878 | 878 | style_finish_page(); |
| 879 | 879 | } |
| @@ -1933,11 +1933,11 @@ | ||
| 1933 | 1933 | tag_private_status(rid); |
| 1934 | 1934 | } |
| 1935 | 1935 | db_finalize(&q); |
| 1936 | 1936 | if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", |
| 1937 | 1937 | rid, TAG_CLUSTER) ){ |
| 1938 | - @ Cluster | |
| 1938 | + @ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>. | |
| 1939 | 1939 | cnt++; |
| 1940 | 1940 | } |
| 1941 | 1941 | if( cnt==0 ){ |
| 1942 | 1942 | @ Unrecognized artifact |
| 1943 | 1943 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| @@ -2249,12 +2249,12 @@ | ||
| 2249 | 2249 | } |
| 2250 | 2250 | if( zName[0]=='x' |
| 2251 | 2251 | && ((nName-1)&1)==0 |
| 2252 | 2252 | && validate16(&zName[1],nName-1) |
| 2253 | 2253 | && g.perm.Admin |
| 2254 | - && db_open_local(0) | |
| 2255 | 2254 | && cgi_is_loopback(g.zIpAddr) |
| 2255 | + && db_open_local(0) | |
| 2256 | 2256 | ){ |
| 2257 | 2257 | /* Treat the HASH as a hex-encoded filename */ |
| 2258 | 2258 | int n = (nName-1)/2; |
| 2259 | 2259 | char *zFN = fossil_malloc(n+1); |
| 2260 | 2260 | decode16((const u8*)&zName[1], (u8*)zFN, nName-1); |
| @@ -3168,10 +3168,143 @@ | ||
| 3168 | 3168 | ticket_output_change_artifact(pTktChng, 0, 1, 0); |
| 3169 | 3169 | manifest_destroy(pTktChng); |
| 3170 | 3170 | style_finish_page(); |
| 3171 | 3171 | } |
| 3172 | 3172 | |
| 3173 | +/* | |
| 3174 | +** rid is a cluster. Paint a page that contains detailed information | |
| 3175 | +** about that cluster. | |
| 3176 | +*/ | |
| 3177 | +static void cluster_info(int rid, const char *zName){ | |
| 3178 | + Manifest *pCluster; | |
| 3179 | + int i; | |
| 3180 | + Blob where = BLOB_INITIALIZER; | |
| 3181 | + Blob unks = BLOB_INITIALIZER; | |
| 3182 | + Stmt q; | |
| 3183 | + char *zSha1Bg; | |
| 3184 | + char *zSha3Bg; | |
| 3185 | + int badRid = 0; | |
| 3186 | + int rcvid; | |
| 3187 | + int hashClr = PB("hclr"); | |
| 3188 | + const char *zDate; | |
| 3189 | + | |
| 3190 | + pCluster = manifest_get(rid, CFTYPE_CLUSTER, 0); | |
| 3191 | + if( pCluster==0 ){ | |
| 3192 | + artifact_page(); | |
| 3193 | + return; | |
| 3194 | + } | |
| 3195 | + style_header("Cluster %S", zName); | |
| 3196 | + rcvid = db_int(0, "SELECT rcvid FROM blob WHERE rid=%d", rid); | |
| 3197 | + if( rcvid==0 ){ | |
| 3198 | + zDate = 0; | |
| 3199 | + }else{ | |
| 3200 | + zDate = db_text(0, "SELECT datetime(mtime) FROM rcvfrom WHERE rcvid=%d", | |
| 3201 | + rcvid); | |
| 3202 | + } | |
| 3203 | + @ <p>Artifact %z(href("%R/artifact/%h",zName))%S(zName)</a> is a cluster | |
| 3204 | + @ with %d(pCluster->nCChild) entries | |
| 3205 | + if( g.perm.Admin ){ | |
| 3206 | + @ received <a href="%R/rcvfrom?rcvid=%d(rcvid)">%h(zDate)</a>: | |
| 3207 | + }else{ | |
| 3208 | + @ received %h(zDate): | |
| 3209 | + } | |
| 3210 | + blob_appendf(&where,"IN(0"); | |
| 3211 | + for(i=0; i<pCluster->nCChild; i++){ | |
| 3212 | + int rid = fast_uuid_to_rid(pCluster->azCChild[i]); | |
| 3213 | + if( rid ){ | |
| 3214 | + blob_appendf(&where,",%d", rid); | |
| 3215 | + }else{ | |
| 3216 | + if( blob_size(&unks)>0 ) blob_append_char(&unks, ','); | |
| 3217 | + badRid++; | |
| 3218 | + blob_append_sql(&unks,"(%d,%Q)",-badRid,pCluster->azCChild[i]); | |
| 3219 | + } | |
| 3220 | + } | |
| 3221 | + blob_append_char(&where,')'); | |
| 3222 | + describe_artifacts(blob_str(&where)); | |
| 3223 | + blob_reset(&where); | |
| 3224 | + if( badRid>0 ){ | |
| 3225 | + db_multi_exec( | |
| 3226 | + "WITH unks(rx,hx) AS (VALUES %s)\n" | |
| 3227 | + "INSERT INTO description(rid,uuid,type,summary) " | |
| 3228 | + " SELECT rx, hx, 'phantom', '' FROM unks;", | |
| 3229 | + blob_sql_text(&unks) | |
| 3230 | + ); | |
| 3231 | + } | |
| 3232 | + blob_reset(&unks); | |
| 3233 | + db_prepare(&q, | |
| 3234 | + "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" | |
| 3235 | + " FROM description ORDER BY uuid" | |
| 3236 | + ); | |
| 3237 | + if( skin_detail_boolean("white-foreground") ){ | |
| 3238 | + zSha1Bg = "#714417"; | |
| 3239 | + zSha3Bg = "#177117"; | |
| 3240 | + }else{ | |
| 3241 | + zSha1Bg = "#ebffb0"; | |
| 3242 | + zSha3Bg = "#b0ffb0"; | |
| 3243 | + } | |
| 3244 | + @ <table cellpadding="2" cellspacing="0" border="1"> | |
| 3245 | + if( g.perm.Admin ){ | |
| 3246 | + @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks | |
| 3247 | + }else{ | |
| 3248 | + @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks | |
| 3249 | + } | |
| 3250 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 3251 | + int rid = db_column_int(&q,0); | |
| 3252 | + const char *zUuid = db_column_text(&q, 1); | |
| 3253 | + const char *zDesc = db_column_text(&q, 2); | |
| 3254 | + int isPriv = db_column_int(&q,3); | |
| 3255 | + int isPhantom = db_column_int(&q,4); | |
| 3256 | + const char *zRef = db_column_text(&q,6); | |
| 3257 | + if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ | |
| 3258 | + /* Don't show private artifacts to users without Private (x) permission */ | |
| 3259 | + continue; | |
| 3260 | + } | |
| 3261 | + if( rid<=0 ){ | |
| 3262 | + @ <tr><td> </td> | |
| 3263 | + }else if( hashClr ){ | |
| 3264 | + const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg; | |
| 3265 | + @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td> | |
| 3266 | + }else{ | |
| 3267 | + @ <tr><td align="right">%d(rid)</td> | |
| 3268 | + } | |
| 3269 | + if( rid<=0 ){ | |
| 3270 | + @ <td> %S(zUuid) </td> | |
| 3271 | + }else{ | |
| 3272 | + @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> | |
| 3273 | + } | |
| 3274 | + if( g.perm.Admin ){ | |
| 3275 | + int rcvid = db_column_int(&q,5); | |
| 3276 | + if( rcvid<=0 ){ | |
| 3277 | + @ <td> | |
| 3278 | + }else{ | |
| 3279 | + @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> | |
| 3280 | + } | |
| 3281 | + } | |
| 3282 | + @ <td align="left">%h(zDesc)</td> | |
| 3283 | + if( zRef && zRef[0] ){ | |
| 3284 | + @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> | |
| 3285 | + }else{ | |
| 3286 | + @ <td> | |
| 3287 | + } | |
| 3288 | + if( isPriv || isPhantom ){ | |
| 3289 | + if( isPriv==0 ){ | |
| 3290 | + @ <td>phantom</td> | |
| 3291 | + }else if( isPhantom==0 ){ | |
| 3292 | + @ <td>private</td> | |
| 3293 | + }else{ | |
| 3294 | + @ <td>private,phantom</td> | |
| 3295 | + } | |
| 3296 | + }else{ | |
| 3297 | + @ <td> | |
| 3298 | + } | |
| 3299 | + @ </tr> | |
| 3300 | + } | |
| 3301 | + @ </table> | |
| 3302 | + db_finalize(&q); | |
| 3303 | + style_finish_page(); | |
| 3304 | +} | |
| 3305 | + | |
| 3173 | 3306 | |
| 3174 | 3307 | /* |
| 3175 | 3308 | ** WEBPAGE: info |
| 3176 | 3309 | ** URL: info/NAME |
| 3177 | 3310 | ** |
| @@ -3256,10 +3389,14 @@ | ||
| 3256 | 3389 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| 3257 | 3390 | ci_page(); |
| 3258 | 3391 | }else |
| 3259 | 3392 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 3260 | 3393 | ainfo_page(); |
| 3394 | + }else | |
| 3395 | + if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", | |
| 3396 | + rid, TAG_CLUSTER) ){ | |
| 3397 | + cluster_info(rid, zName); | |
| 3261 | 3398 | }else |
| 3262 | 3399 | { |
| 3263 | 3400 | artifact_page(); |
| 3264 | 3401 | } |
| 3265 | 3402 | } |
| 3266 | 3403 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -320,10 +320,11 @@ | |
| 320 | |TIMELINE_NOSCROLL |
| 321 | |TIMELINE_XMERGE |
| 322 | |TIMELINE_CHPICK, |
| 323 | 0, 0, 0, rid, rid2, 0); |
| 324 | db_finalize(&q); |
| 325 | } |
| 326 | |
| 327 | |
| 328 | /* |
| 329 | ** Append the difference between artifacts to the output |
| @@ -624,11 +625,10 @@ | |
| 624 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 625 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 626 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 627 | if( nChng==0 ){ |
| 628 | @ <p>No uncommitted changes</p> |
| 629 | style_finish_page(); |
| 630 | return; |
| 631 | } |
| 632 | db_prepare(&q, |
| 633 | /* 0 1 2 3 4 5 6 */ |
| 634 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -785,17 +785,12 @@ | |
| 785 | @ Changes to %h(zFile) |
| 786 | @ </span></div> |
| 787 | if( pCfg ){ |
| 788 | char *zFullFN; |
| 789 | char *zHexFN; |
| 790 | int nFullFN; |
| 791 | zFullFN = file_canonical_name_dup(zLhs); |
| 792 | nFullFN = (int)strlen(zFullFN); |
| 793 | zHexFN = fossil_malloc( nFullFN*2 + 5 ); |
| 794 | zHexFN[0] = 'x'; |
| 795 | encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN); |
| 796 | zHexFN[1+nFullFN*2] = 0; |
| 797 | fossil_free(zFullFN); |
| 798 | pCfg->zLeftHash = zHexFN; |
| 799 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 800 | pCfg->zLeftHash = 0; |
| 801 | fossil_free(zHexFN); |
| @@ -820,11 +815,14 @@ | |
| 820 | ** |
| 821 | ** If the "exbase=PATH" query parameter is provided, then the diff shown |
| 822 | ** uses the files in PATH as the baseline. This is the same as using |
| 823 | ** the "--from PATH" argument to the "fossil diff" command-line. In fact, |
| 824 | ** when using "fossil ui --from PATH", the --from argument becomes the value |
| 825 | ** of the exbase query parameter for the start page. |
| 826 | ** |
| 827 | ** Other query parameters related to diffs are also accepted. |
| 828 | */ |
| 829 | void ckout_page(void){ |
| 830 | int vid; |
| @@ -832,11 +830,11 @@ | |
| 832 | int nHome; |
| 833 | const char *zExBase; |
| 834 | char *zHostname; |
| 835 | char *zCwd; |
| 836 | |
| 837 | if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ |
| 838 | cgi_redirectf("%R/home"); |
| 839 | return; |
| 840 | } |
| 841 | file_chdir(g.zLocalRoot, 0); |
| 842 | vid = db_lget_int("checkout", 0); |
| @@ -862,18 +860,20 @@ | |
| 862 | } |
| 863 | render_checkin_context(vid, 0, 0, 0); |
| 864 | @ <hr> |
| 865 | zExBase = P("exbase"); |
| 866 | if( zExBase && zExBase[0] ){ |
| 867 | char *zCBase = file_canonical_name_dup(zExBase); |
| 868 | if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){ |
| 869 | @ <p>Using external baseline: ~%h(zCBase+nHome)</p> |
| 870 | }else{ |
| 871 | @ <p>Using external baseline: %h(zCBase)</p> |
| 872 | } |
| 873 | ckout_external_base_diff(vid, zCBase); |
| 874 | fossil_free(zCBase); |
| 875 | }else{ |
| 876 | ckout_normal_diff(vid); |
| 877 | } |
| 878 | style_finish_page(); |
| 879 | } |
| @@ -1933,11 +1933,11 @@ | |
| 1933 | tag_private_status(rid); |
| 1934 | } |
| 1935 | db_finalize(&q); |
| 1936 | if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", |
| 1937 | rid, TAG_CLUSTER) ){ |
| 1938 | @ Cluster |
| 1939 | cnt++; |
| 1940 | } |
| 1941 | if( cnt==0 ){ |
| 1942 | @ Unrecognized artifact |
| 1943 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| @@ -2249,12 +2249,12 @@ | |
| 2249 | } |
| 2250 | if( zName[0]=='x' |
| 2251 | && ((nName-1)&1)==0 |
| 2252 | && validate16(&zName[1],nName-1) |
| 2253 | && g.perm.Admin |
| 2254 | && db_open_local(0) |
| 2255 | && cgi_is_loopback(g.zIpAddr) |
| 2256 | ){ |
| 2257 | /* Treat the HASH as a hex-encoded filename */ |
| 2258 | int n = (nName-1)/2; |
| 2259 | char *zFN = fossil_malloc(n+1); |
| 2260 | decode16((const u8*)&zName[1], (u8*)zFN, nName-1); |
| @@ -3168,10 +3168,143 @@ | |
| 3168 | ticket_output_change_artifact(pTktChng, 0, 1, 0); |
| 3169 | manifest_destroy(pTktChng); |
| 3170 | style_finish_page(); |
| 3171 | } |
| 3172 | |
| 3173 | |
| 3174 | /* |
| 3175 | ** WEBPAGE: info |
| 3176 | ** URL: info/NAME |
| 3177 | ** |
| @@ -3256,10 +3389,14 @@ | |
| 3256 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| 3257 | ci_page(); |
| 3258 | }else |
| 3259 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 3260 | ainfo_page(); |
| 3261 | }else |
| 3262 | { |
| 3263 | artifact_page(); |
| 3264 | } |
| 3265 | } |
| 3266 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -320,10 +320,11 @@ | |
| 320 | |TIMELINE_NOSCROLL |
| 321 | |TIMELINE_XMERGE |
| 322 | |TIMELINE_CHPICK, |
| 323 | 0, 0, 0, rid, rid2, 0); |
| 324 | db_finalize(&q); |
| 325 | blob_reset(&sql); |
| 326 | } |
| 327 | |
| 328 | |
| 329 | /* |
| 330 | ** Append the difference between artifacts to the output |
| @@ -624,11 +625,10 @@ | |
| 625 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 626 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 627 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 628 | if( nChng==0 ){ |
| 629 | @ <p>No uncommitted changes</p> |
| 630 | return; |
| 631 | } |
| 632 | db_prepare(&q, |
| 633 | /* 0 1 2 3 4 5 6 */ |
| 634 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -785,17 +785,12 @@ | |
| 785 | @ Changes to %h(zFile) |
| 786 | @ </span></div> |
| 787 | if( pCfg ){ |
| 788 | char *zFullFN; |
| 789 | char *zHexFN; |
| 790 | zFullFN = file_canonical_name_dup(zLhs); |
| 791 | zHexFN = mprintf("x%H", zFullFN); |
| 792 | fossil_free(zFullFN); |
| 793 | pCfg->zLeftHash = zHexFN; |
| 794 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 795 | pCfg->zLeftHash = 0; |
| 796 | fossil_free(zHexFN); |
| @@ -820,11 +815,14 @@ | |
| 815 | ** |
| 816 | ** If the "exbase=PATH" query parameter is provided, then the diff shown |
| 817 | ** uses the files in PATH as the baseline. This is the same as using |
| 818 | ** the "--from PATH" argument to the "fossil diff" command-line. In fact, |
| 819 | ** when using "fossil ui --from PATH", the --from argument becomes the value |
| 820 | ** of the exbase query parameter for the start page. Note that if PATH |
| 821 | ** is a pure hexadecimal string, it is decoded first before being used as |
| 822 | ** the pathname. Real pathnames should contain at least one directory |
| 823 | ** separator character. |
| 824 | ** |
| 825 | ** Other query parameters related to diffs are also accepted. |
| 826 | */ |
| 827 | void ckout_page(void){ |
| 828 | int vid; |
| @@ -832,11 +830,11 @@ | |
| 830 | int nHome; |
| 831 | const char *zExBase; |
| 832 | char *zHostname; |
| 833 | char *zCwd; |
| 834 | |
| 835 | if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){ |
| 836 | cgi_redirectf("%R/home"); |
| 837 | return; |
| 838 | } |
| 839 | file_chdir(g.zLocalRoot, 0); |
| 840 | vid = db_lget_int("checkout", 0); |
| @@ -862,18 +860,20 @@ | |
| 860 | } |
| 861 | render_checkin_context(vid, 0, 0, 0); |
| 862 | @ <hr> |
| 863 | zExBase = P("exbase"); |
| 864 | if( zExBase && zExBase[0] ){ |
| 865 | char *zPath = decode16_dup(zExBase); |
| 866 | char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase); |
| 867 | if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){ |
| 868 | @ <p>Using external baseline: ~%h(zCBase+nHome)</p> |
| 869 | }else{ |
| 870 | @ <p>Using external baseline: %h(zCBase)</p> |
| 871 | } |
| 872 | ckout_external_base_diff(vid, zCBase); |
| 873 | fossil_free(zCBase); |
| 874 | fossil_free(zPath); |
| 875 | }else{ |
| 876 | ckout_normal_diff(vid); |
| 877 | } |
| 878 | style_finish_page(); |
| 879 | } |
| @@ -1933,11 +1933,11 @@ | |
| 1933 | tag_private_status(rid); |
| 1934 | } |
| 1935 | db_finalize(&q); |
| 1936 | if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", |
| 1937 | rid, TAG_CLUSTER) ){ |
| 1938 | @ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>. |
| 1939 | cnt++; |
| 1940 | } |
| 1941 | if( cnt==0 ){ |
| 1942 | @ Unrecognized artifact |
| 1943 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| @@ -2249,12 +2249,12 @@ | |
| 2249 | } |
| 2250 | if( zName[0]=='x' |
| 2251 | && ((nName-1)&1)==0 |
| 2252 | && validate16(&zName[1],nName-1) |
| 2253 | && g.perm.Admin |
| 2254 | && cgi_is_loopback(g.zIpAddr) |
| 2255 | && db_open_local(0) |
| 2256 | ){ |
| 2257 | /* Treat the HASH as a hex-encoded filename */ |
| 2258 | int n = (nName-1)/2; |
| 2259 | char *zFN = fossil_malloc(n+1); |
| 2260 | decode16((const u8*)&zName[1], (u8*)zFN, nName-1); |
| @@ -3168,10 +3168,143 @@ | |
| 3168 | ticket_output_change_artifact(pTktChng, 0, 1, 0); |
| 3169 | manifest_destroy(pTktChng); |
| 3170 | style_finish_page(); |
| 3171 | } |
| 3172 | |
| 3173 | /* |
| 3174 | ** rid is a cluster. Paint a page that contains detailed information |
| 3175 | ** about that cluster. |
| 3176 | */ |
| 3177 | static void cluster_info(int rid, const char *zName){ |
| 3178 | Manifest *pCluster; |
| 3179 | int i; |
| 3180 | Blob where = BLOB_INITIALIZER; |
| 3181 | Blob unks = BLOB_INITIALIZER; |
| 3182 | Stmt q; |
| 3183 | char *zSha1Bg; |
| 3184 | char *zSha3Bg; |
| 3185 | int badRid = 0; |
| 3186 | int rcvid; |
| 3187 | int hashClr = PB("hclr"); |
| 3188 | const char *zDate; |
| 3189 | |
| 3190 | pCluster = manifest_get(rid, CFTYPE_CLUSTER, 0); |
| 3191 | if( pCluster==0 ){ |
| 3192 | artifact_page(); |
| 3193 | return; |
| 3194 | } |
| 3195 | style_header("Cluster %S", zName); |
| 3196 | rcvid = db_int(0, "SELECT rcvid FROM blob WHERE rid=%d", rid); |
| 3197 | if( rcvid==0 ){ |
| 3198 | zDate = 0; |
| 3199 | }else{ |
| 3200 | zDate = db_text(0, "SELECT datetime(mtime) FROM rcvfrom WHERE rcvid=%d", |
| 3201 | rcvid); |
| 3202 | } |
| 3203 | @ <p>Artifact %z(href("%R/artifact/%h",zName))%S(zName)</a> is a cluster |
| 3204 | @ with %d(pCluster->nCChild) entries |
| 3205 | if( g.perm.Admin ){ |
| 3206 | @ received <a href="%R/rcvfrom?rcvid=%d(rcvid)">%h(zDate)</a>: |
| 3207 | }else{ |
| 3208 | @ received %h(zDate): |
| 3209 | } |
| 3210 | blob_appendf(&where,"IN(0"); |
| 3211 | for(i=0; i<pCluster->nCChild; i++){ |
| 3212 | int rid = fast_uuid_to_rid(pCluster->azCChild[i]); |
| 3213 | if( rid ){ |
| 3214 | blob_appendf(&where,",%d", rid); |
| 3215 | }else{ |
| 3216 | if( blob_size(&unks)>0 ) blob_append_char(&unks, ','); |
| 3217 | badRid++; |
| 3218 | blob_append_sql(&unks,"(%d,%Q)",-badRid,pCluster->azCChild[i]); |
| 3219 | } |
| 3220 | } |
| 3221 | blob_append_char(&where,')'); |
| 3222 | describe_artifacts(blob_str(&where)); |
| 3223 | blob_reset(&where); |
| 3224 | if( badRid>0 ){ |
| 3225 | db_multi_exec( |
| 3226 | "WITH unks(rx,hx) AS (VALUES %s)\n" |
| 3227 | "INSERT INTO description(rid,uuid,type,summary) " |
| 3228 | " SELECT rx, hx, 'phantom', '' FROM unks;", |
| 3229 | blob_sql_text(&unks) |
| 3230 | ); |
| 3231 | } |
| 3232 | blob_reset(&unks); |
| 3233 | db_prepare(&q, |
| 3234 | "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" |
| 3235 | " FROM description ORDER BY uuid" |
| 3236 | ); |
| 3237 | if( skin_detail_boolean("white-foreground") ){ |
| 3238 | zSha1Bg = "#714417"; |
| 3239 | zSha3Bg = "#177117"; |
| 3240 | }else{ |
| 3241 | zSha1Bg = "#ebffb0"; |
| 3242 | zSha3Bg = "#b0ffb0"; |
| 3243 | } |
| 3244 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 3245 | if( g.perm.Admin ){ |
| 3246 | @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks |
| 3247 | }else{ |
| 3248 | @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks |
| 3249 | } |
| 3250 | while( db_step(&q)==SQLITE_ROW ){ |
| 3251 | int rid = db_column_int(&q,0); |
| 3252 | const char *zUuid = db_column_text(&q, 1); |
| 3253 | const char *zDesc = db_column_text(&q, 2); |
| 3254 | int isPriv = db_column_int(&q,3); |
| 3255 | int isPhantom = db_column_int(&q,4); |
| 3256 | const char *zRef = db_column_text(&q,6); |
| 3257 | if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ |
| 3258 | /* Don't show private artifacts to users without Private (x) permission */ |
| 3259 | continue; |
| 3260 | } |
| 3261 | if( rid<=0 ){ |
| 3262 | @ <tr><td> </td> |
| 3263 | }else if( hashClr ){ |
| 3264 | const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg; |
| 3265 | @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td> |
| 3266 | }else{ |
| 3267 | @ <tr><td align="right">%d(rid)</td> |
| 3268 | } |
| 3269 | if( rid<=0 ){ |
| 3270 | @ <td> %S(zUuid) </td> |
| 3271 | }else{ |
| 3272 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 3273 | } |
| 3274 | if( g.perm.Admin ){ |
| 3275 | int rcvid = db_column_int(&q,5); |
| 3276 | if( rcvid<=0 ){ |
| 3277 | @ <td> |
| 3278 | }else{ |
| 3279 | @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> |
| 3280 | } |
| 3281 | } |
| 3282 | @ <td align="left">%h(zDesc)</td> |
| 3283 | if( zRef && zRef[0] ){ |
| 3284 | @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> |
| 3285 | }else{ |
| 3286 | @ <td> |
| 3287 | } |
| 3288 | if( isPriv || isPhantom ){ |
| 3289 | if( isPriv==0 ){ |
| 3290 | @ <td>phantom</td> |
| 3291 | }else if( isPhantom==0 ){ |
| 3292 | @ <td>private</td> |
| 3293 | }else{ |
| 3294 | @ <td>private,phantom</td> |
| 3295 | } |
| 3296 | }else{ |
| 3297 | @ <td> |
| 3298 | } |
| 3299 | @ </tr> |
| 3300 | } |
| 3301 | @ </table> |
| 3302 | db_finalize(&q); |
| 3303 | style_finish_page(); |
| 3304 | } |
| 3305 | |
| 3306 | |
| 3307 | /* |
| 3308 | ** WEBPAGE: info |
| 3309 | ** URL: info/NAME |
| 3310 | ** |
| @@ -3256,10 +3389,14 @@ | |
| 3389 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| 3390 | ci_page(); |
| 3391 | }else |
| 3392 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 3393 | ainfo_page(); |
| 3394 | }else |
| 3395 | if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", |
| 3396 | rid, TAG_CLUSTER) ){ |
| 3397 | cluster_info(rid, zName); |
| 3398 | }else |
| 3399 | { |
| 3400 | artifact_page(); |
| 3401 | } |
| 3402 | } |
| 3403 |
+2
-1
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -1330,10 +1330,11 @@ | ||
| 1330 | 1330 | |
| 1331 | 1331 | /* We should be done with options.. */ |
| 1332 | 1332 | verify_all_options(); |
| 1333 | 1333 | fossil_version_blob(&versionInfo, verboseFlag); |
| 1334 | 1334 | fossil_print("%s", blob_str(&versionInfo)); |
| 1335 | + blob_reset(&versionInfo); | |
| 1335 | 1336 | } |
| 1336 | 1337 | |
| 1337 | 1338 | |
| 1338 | 1339 | /* |
| 1339 | 1340 | ** WEBPAGE: version |
| @@ -3297,11 +3298,11 @@ | ||
| 3297 | 3298 | } |
| 3298 | 3299 | zInitPage = find_option("page", "p", 1); |
| 3299 | 3300 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3300 | 3301 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3301 | 3302 | if( zFrom && zInitPage==0 ){ |
| 3302 | - zInitPage = mprintf("ckout?exbase=%T", zFrom); | |
| 3303 | + zInitPage = mprintf("ckout?exbase=%H", zFrom); | |
| 3303 | 3304 | } |
| 3304 | 3305 | } |
| 3305 | 3306 | zNotFound = find_option("notfound", 0, 1); |
| 3306 | 3307 | allowRepoList = find_option("repolist",0,0)!=0; |
| 3307 | 3308 | if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; |
| 3308 | 3309 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1330,10 +1330,11 @@ | |
| 1330 | |
| 1331 | /* We should be done with options.. */ |
| 1332 | verify_all_options(); |
| 1333 | fossil_version_blob(&versionInfo, verboseFlag); |
| 1334 | fossil_print("%s", blob_str(&versionInfo)); |
| 1335 | } |
| 1336 | |
| 1337 | |
| 1338 | /* |
| 1339 | ** WEBPAGE: version |
| @@ -3297,11 +3298,11 @@ | |
| 3297 | } |
| 3298 | zInitPage = find_option("page", "p", 1); |
| 3299 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3300 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3301 | if( zFrom && zInitPage==0 ){ |
| 3302 | zInitPage = mprintf("ckout?exbase=%T", zFrom); |
| 3303 | } |
| 3304 | } |
| 3305 | zNotFound = find_option("notfound", 0, 1); |
| 3306 | allowRepoList = find_option("repolist",0,0)!=0; |
| 3307 | if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; |
| 3308 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1330,10 +1330,11 @@ | |
| 1330 | |
| 1331 | /* We should be done with options.. */ |
| 1332 | verify_all_options(); |
| 1333 | fossil_version_blob(&versionInfo, verboseFlag); |
| 1334 | fossil_print("%s", blob_str(&versionInfo)); |
| 1335 | blob_reset(&versionInfo); |
| 1336 | } |
| 1337 | |
| 1338 | |
| 1339 | /* |
| 1340 | ** WEBPAGE: version |
| @@ -3297,11 +3298,11 @@ | |
| 3298 | } |
| 3299 | zInitPage = find_option("page", "p", 1); |
| 3300 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3301 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3302 | if( zFrom && zInitPage==0 ){ |
| 3303 | zInitPage = mprintf("ckout?exbase=%H", zFrom); |
| 3304 | } |
| 3305 | } |
| 3306 | zNotFound = find_option("notfound", 0, 1); |
| 3307 | allowRepoList = find_option("repolist",0,0)!=0; |
| 3308 | if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; |
| 3309 |
+12
-8
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -687,10 +687,11 @@ | ||
| 687 | 687 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 688 | 688 | ** used for merging, named *-baseline, *-original, |
| 689 | 689 | ** and *-merge. |
| 690 | 690 | ** -n|--dry-run Do not actually change files on disk |
| 691 | 691 | ** --nosync Do not auto-sync prior to merging |
| 692 | +** --noundo Do not record changes in the undo log | |
| 692 | 693 | ** -v|--verbose Show additional details of the merge |
| 693 | 694 | */ |
| 694 | 695 | void merge_cmd(void){ |
| 695 | 696 | int vid; /* Current version "V" */ |
| 696 | 697 | int mid; /* Version we are merging from "M" */ |
| @@ -712,10 +713,11 @@ | ||
| 712 | 713 | int nOverwrite = 0; /* Number of unmanaged files overwritten */ |
| 713 | 714 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 714 | 715 | const char *zVersion; /* The VERSION argument */ |
| 715 | 716 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 716 | 717 | int nMerge = 0; /* Number of prior merges processed */ |
| 718 | + int useUndo = 1; /* True to record changes in the undo log */ | |
| 717 | 719 | Stmt q; /* SQL statment used for merge processing */ |
| 718 | 720 | |
| 719 | 721 | |
| 720 | 722 | /* Notation: |
| 721 | 723 | ** |
| @@ -760,10 +762,12 @@ | ||
| 760 | 762 | ** * The --dry-run option is also useful in combination with --debug. |
| 761 | 763 | */ |
| 762 | 764 | debugFlag = find_option("debug",0,0)!=0; |
| 763 | 765 | if( debugFlag && verboseFlag ) debugFlag = 2; |
| 764 | 766 | showVfileFlag = find_option("show-vfile",0,0)!=0; |
| 767 | + useUndo = find_option("noundo",0,0)==0; | |
| 768 | + if( dryRunFlag ) useUndo = 0; | |
| 765 | 769 | |
| 766 | 770 | verify_all_options(); |
| 767 | 771 | db_must_be_within_tree(); |
| 768 | 772 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 769 | 773 | vid = db_lget_int("checkout", 0); |
| @@ -926,11 +930,11 @@ | ||
| 926 | 930 | integrateFlag ? "integrate:" : "merge-from:"); |
| 927 | 931 | print_checkin_description(pid, 12, "baseline:"); |
| 928 | 932 | } |
| 929 | 933 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 930 | 934 | if( nMerge==0 ) db_begin_transaction(); |
| 931 | - if( !dryRunFlag ) undo_begin(); | |
| 935 | + if( useUndo ) undo_begin(); | |
| 932 | 936 | if( load_vfile_from_rid(mid) && !forceMissingFlag ){ |
| 933 | 937 | fossil_fatal("missing content, unable to merge"); |
| 934 | 938 | } |
| 935 | 939 | if( load_vfile_from_rid(pid) && !forceMissingFlag ){ |
| 936 | 940 | fossil_fatal("missing content, unable to merge"); |
| @@ -1183,12 +1187,12 @@ | ||
| 1183 | 1187 | int ridm = db_column_int(&q, 1); |
| 1184 | 1188 | const char *zName = db_column_text(&q, 2); |
| 1185 | 1189 | int islinkm = db_column_int(&q, 3); |
| 1186 | 1190 | /* Copy content from idm over into idv. Overwrite idv. */ |
| 1187 | 1191 | fossil_print("UPDATE %s\n", zName); |
| 1192 | + if( useUndo ) undo_save(zName); | |
| 1188 | 1193 | if( !dryRunFlag ){ |
| 1189 | - undo_save(zName); | |
| 1190 | 1194 | db_multi_exec( |
| 1191 | 1195 | "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d," |
| 1192 | 1196 | " mhash=CASE WHEN rid<>%d" |
| 1193 | 1197 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 1194 | 1198 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| @@ -1265,11 +1269,11 @@ | ||
| 1265 | 1269 | }else{ |
| 1266 | 1270 | i64 sz; |
| 1267 | 1271 | const char *zErrMsg = 0; |
| 1268 | 1272 | int nc = 0; |
| 1269 | 1273 | |
| 1270 | - if( !dryRunFlag ) undo_save(zName); | |
| 1274 | + if( useUndo ) undo_save(zName); | |
| 1271 | 1275 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1272 | 1276 | sz = file_size(zFullPath, ExtFILE); |
| 1273 | 1277 | content_get(ridp, &p); |
| 1274 | 1278 | content_get(ridm, &m); |
| 1275 | 1279 | if( isBinary ){ |
| @@ -1352,11 +1356,11 @@ | ||
| 1352 | 1356 | zErrMsg = "local edits lost"; |
| 1353 | 1357 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1354 | 1358 | sz = file_size(zFullPath, ExtFILE); |
| 1355 | 1359 | fossil_free(zFullPath); |
| 1356 | 1360 | } |
| 1357 | - if( !dryRunFlag ) undo_save(zName); | |
| 1361 | + if( useUndo ) undo_save(zName); | |
| 1358 | 1362 | db_multi_exec( |
| 1359 | 1363 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 1360 | 1364 | ); |
| 1361 | 1365 | if( !dryRunFlag ){ |
| 1362 | 1366 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -1399,12 +1403,12 @@ | ||
| 1399 | 1403 | int idv = db_column_int(&q, 0); |
| 1400 | 1404 | const char *zOldName = db_column_text(&q, 1); |
| 1401 | 1405 | const char *zNewName = db_column_text(&q, 2); |
| 1402 | 1406 | int isExe = db_column_int(&q, 3); |
| 1403 | 1407 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 1404 | - if( !dryRunFlag ) undo_save(zOldName); | |
| 1405 | - if( !dryRunFlag ) undo_save(zNewName); | |
| 1408 | + if( useUndo ) undo_save(zOldName); | |
| 1409 | + if( useUndo ) undo_save(zNewName); | |
| 1406 | 1410 | db_multi_exec( |
| 1407 | 1411 | "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q", |
| 1408 | 1412 | zOldName |
| 1409 | 1413 | ); |
| 1410 | 1414 | db_multi_exec( |
| @@ -1498,12 +1502,12 @@ | ||
| 1498 | 1502 | "VALUES('ADDED',%Q,%d,%Q)", |
| 1499 | 1503 | /* fnm */ zName, |
| 1500 | 1504 | /* ridm */ db_column_int(&q,2), |
| 1501 | 1505 | /* fnr */ zName |
| 1502 | 1506 | ); |
| 1507 | + if( useUndo ) undo_save(zName); | |
| 1503 | 1508 | if( !dryRunFlag ){ |
| 1504 | - undo_save(zName); | |
| 1505 | 1509 | vfile_to_disk(0, idm, 0, 0); |
| 1506 | 1510 | } |
| 1507 | 1511 | } |
| 1508 | 1512 | db_finalize(&q); |
| 1509 | 1513 | |
| @@ -1558,9 +1562,9 @@ | ||
| 1558 | 1562 | } |
| 1559 | 1563 | if( bMultiMerge && nConflict==0 ){ |
| 1560 | 1564 | nMerge++; |
| 1561 | 1565 | goto merge_next_child; |
| 1562 | 1566 | } |
| 1563 | - if( !dryRunFlag ) undo_finish(); | |
| 1567 | + if( useUndo ) undo_finish(); | |
| 1564 | 1568 | |
| 1565 | 1569 | db_end_transaction(dryRunFlag); |
| 1566 | 1570 | } |
| 1567 | 1571 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -687,10 +687,11 @@ | |
| 687 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 688 | ** used for merging, named *-baseline, *-original, |
| 689 | ** and *-merge. |
| 690 | ** -n|--dry-run Do not actually change files on disk |
| 691 | ** --nosync Do not auto-sync prior to merging |
| 692 | ** -v|--verbose Show additional details of the merge |
| 693 | */ |
| 694 | void merge_cmd(void){ |
| 695 | int vid; /* Current version "V" */ |
| 696 | int mid; /* Version we are merging from "M" */ |
| @@ -712,10 +713,11 @@ | |
| 712 | int nOverwrite = 0; /* Number of unmanaged files overwritten */ |
| 713 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 714 | const char *zVersion; /* The VERSION argument */ |
| 715 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 716 | int nMerge = 0; /* Number of prior merges processed */ |
| 717 | Stmt q; /* SQL statment used for merge processing */ |
| 718 | |
| 719 | |
| 720 | /* Notation: |
| 721 | ** |
| @@ -760,10 +762,12 @@ | |
| 760 | ** * The --dry-run option is also useful in combination with --debug. |
| 761 | */ |
| 762 | debugFlag = find_option("debug",0,0)!=0; |
| 763 | if( debugFlag && verboseFlag ) debugFlag = 2; |
| 764 | showVfileFlag = find_option("show-vfile",0,0)!=0; |
| 765 | |
| 766 | verify_all_options(); |
| 767 | db_must_be_within_tree(); |
| 768 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 769 | vid = db_lget_int("checkout", 0); |
| @@ -926,11 +930,11 @@ | |
| 926 | integrateFlag ? "integrate:" : "merge-from:"); |
| 927 | print_checkin_description(pid, 12, "baseline:"); |
| 928 | } |
| 929 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 930 | if( nMerge==0 ) db_begin_transaction(); |
| 931 | if( !dryRunFlag ) undo_begin(); |
| 932 | if( load_vfile_from_rid(mid) && !forceMissingFlag ){ |
| 933 | fossil_fatal("missing content, unable to merge"); |
| 934 | } |
| 935 | if( load_vfile_from_rid(pid) && !forceMissingFlag ){ |
| 936 | fossil_fatal("missing content, unable to merge"); |
| @@ -1183,12 +1187,12 @@ | |
| 1183 | int ridm = db_column_int(&q, 1); |
| 1184 | const char *zName = db_column_text(&q, 2); |
| 1185 | int islinkm = db_column_int(&q, 3); |
| 1186 | /* Copy content from idm over into idv. Overwrite idv. */ |
| 1187 | fossil_print("UPDATE %s\n", zName); |
| 1188 | if( !dryRunFlag ){ |
| 1189 | undo_save(zName); |
| 1190 | db_multi_exec( |
| 1191 | "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d," |
| 1192 | " mhash=CASE WHEN rid<>%d" |
| 1193 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 1194 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| @@ -1265,11 +1269,11 @@ | |
| 1265 | }else{ |
| 1266 | i64 sz; |
| 1267 | const char *zErrMsg = 0; |
| 1268 | int nc = 0; |
| 1269 | |
| 1270 | if( !dryRunFlag ) undo_save(zName); |
| 1271 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1272 | sz = file_size(zFullPath, ExtFILE); |
| 1273 | content_get(ridp, &p); |
| 1274 | content_get(ridm, &m); |
| 1275 | if( isBinary ){ |
| @@ -1352,11 +1356,11 @@ | |
| 1352 | zErrMsg = "local edits lost"; |
| 1353 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1354 | sz = file_size(zFullPath, ExtFILE); |
| 1355 | fossil_free(zFullPath); |
| 1356 | } |
| 1357 | if( !dryRunFlag ) undo_save(zName); |
| 1358 | db_multi_exec( |
| 1359 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 1360 | ); |
| 1361 | if( !dryRunFlag ){ |
| 1362 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -1399,12 +1403,12 @@ | |
| 1399 | int idv = db_column_int(&q, 0); |
| 1400 | const char *zOldName = db_column_text(&q, 1); |
| 1401 | const char *zNewName = db_column_text(&q, 2); |
| 1402 | int isExe = db_column_int(&q, 3); |
| 1403 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 1404 | if( !dryRunFlag ) undo_save(zOldName); |
| 1405 | if( !dryRunFlag ) undo_save(zNewName); |
| 1406 | db_multi_exec( |
| 1407 | "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q", |
| 1408 | zOldName |
| 1409 | ); |
| 1410 | db_multi_exec( |
| @@ -1498,12 +1502,12 @@ | |
| 1498 | "VALUES('ADDED',%Q,%d,%Q)", |
| 1499 | /* fnm */ zName, |
| 1500 | /* ridm */ db_column_int(&q,2), |
| 1501 | /* fnr */ zName |
| 1502 | ); |
| 1503 | if( !dryRunFlag ){ |
| 1504 | undo_save(zName); |
| 1505 | vfile_to_disk(0, idm, 0, 0); |
| 1506 | } |
| 1507 | } |
| 1508 | db_finalize(&q); |
| 1509 | |
| @@ -1558,9 +1562,9 @@ | |
| 1558 | } |
| 1559 | if( bMultiMerge && nConflict==0 ){ |
| 1560 | nMerge++; |
| 1561 | goto merge_next_child; |
| 1562 | } |
| 1563 | if( !dryRunFlag ) undo_finish(); |
| 1564 | |
| 1565 | db_end_transaction(dryRunFlag); |
| 1566 | } |
| 1567 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -687,10 +687,11 @@ | |
| 687 | ** -K|--keep-merge-files On merge conflict, retain the temporary files |
| 688 | ** used for merging, named *-baseline, *-original, |
| 689 | ** and *-merge. |
| 690 | ** -n|--dry-run Do not actually change files on disk |
| 691 | ** --nosync Do not auto-sync prior to merging |
| 692 | ** --noundo Do not record changes in the undo log |
| 693 | ** -v|--verbose Show additional details of the merge |
| 694 | */ |
| 695 | void merge_cmd(void){ |
| 696 | int vid; /* Current version "V" */ |
| 697 | int mid; /* Version we are merging from "M" */ |
| @@ -712,10 +713,11 @@ | |
| 713 | int nOverwrite = 0; /* Number of unmanaged files overwritten */ |
| 714 | char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ |
| 715 | const char *zVersion; /* The VERSION argument */ |
| 716 | int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ |
| 717 | int nMerge = 0; /* Number of prior merges processed */ |
| 718 | int useUndo = 1; /* True to record changes in the undo log */ |
| 719 | Stmt q; /* SQL statment used for merge processing */ |
| 720 | |
| 721 | |
| 722 | /* Notation: |
| 723 | ** |
| @@ -760,10 +762,12 @@ | |
| 762 | ** * The --dry-run option is also useful in combination with --debug. |
| 763 | */ |
| 764 | debugFlag = find_option("debug",0,0)!=0; |
| 765 | if( debugFlag && verboseFlag ) debugFlag = 2; |
| 766 | showVfileFlag = find_option("show-vfile",0,0)!=0; |
| 767 | useUndo = find_option("noundo",0,0)==0; |
| 768 | if( dryRunFlag ) useUndo = 0; |
| 769 | |
| 770 | verify_all_options(); |
| 771 | db_must_be_within_tree(); |
| 772 | if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); |
| 773 | vid = db_lget_int("checkout", 0); |
| @@ -926,11 +930,11 @@ | |
| 930 | integrateFlag ? "integrate:" : "merge-from:"); |
| 931 | print_checkin_description(pid, 12, "baseline:"); |
| 932 | } |
| 933 | vfile_check_signature(vid, CKSIG_ENOTFILE); |
| 934 | if( nMerge==0 ) db_begin_transaction(); |
| 935 | if( useUndo ) undo_begin(); |
| 936 | if( load_vfile_from_rid(mid) && !forceMissingFlag ){ |
| 937 | fossil_fatal("missing content, unable to merge"); |
| 938 | } |
| 939 | if( load_vfile_from_rid(pid) && !forceMissingFlag ){ |
| 940 | fossil_fatal("missing content, unable to merge"); |
| @@ -1183,12 +1187,12 @@ | |
| 1187 | int ridm = db_column_int(&q, 1); |
| 1188 | const char *zName = db_column_text(&q, 2); |
| 1189 | int islinkm = db_column_int(&q, 3); |
| 1190 | /* Copy content from idm over into idv. Overwrite idv. */ |
| 1191 | fossil_print("UPDATE %s\n", zName); |
| 1192 | if( useUndo ) undo_save(zName); |
| 1193 | if( !dryRunFlag ){ |
| 1194 | db_multi_exec( |
| 1195 | "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d," |
| 1196 | " mhash=CASE WHEN rid<>%d" |
| 1197 | " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END" |
| 1198 | " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv |
| @@ -1265,11 +1269,11 @@ | |
| 1269 | }else{ |
| 1270 | i64 sz; |
| 1271 | const char *zErrMsg = 0; |
| 1272 | int nc = 0; |
| 1273 | |
| 1274 | if( useUndo ) undo_save(zName); |
| 1275 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1276 | sz = file_size(zFullPath, ExtFILE); |
| 1277 | content_get(ridp, &p); |
| 1278 | content_get(ridm, &m); |
| 1279 | if( isBinary ){ |
| @@ -1352,11 +1356,11 @@ | |
| 1356 | zErrMsg = "local edits lost"; |
| 1357 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 1358 | sz = file_size(zFullPath, ExtFILE); |
| 1359 | fossil_free(zFullPath); |
| 1360 | } |
| 1361 | if( useUndo ) undo_save(zName); |
| 1362 | db_multi_exec( |
| 1363 | "UPDATE vfile SET deleted=1 WHERE id=%d", idv |
| 1364 | ); |
| 1365 | if( !dryRunFlag ){ |
| 1366 | char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -1399,12 +1403,12 @@ | |
| 1403 | int idv = db_column_int(&q, 0); |
| 1404 | const char *zOldName = db_column_text(&q, 1); |
| 1405 | const char *zNewName = db_column_text(&q, 2); |
| 1406 | int isExe = db_column_int(&q, 3); |
| 1407 | fossil_print("RENAME %s -> %s\n", zOldName, zNewName); |
| 1408 | if( useUndo ) undo_save(zOldName); |
| 1409 | if( useUndo ) undo_save(zNewName); |
| 1410 | db_multi_exec( |
| 1411 | "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q", |
| 1412 | zOldName |
| 1413 | ); |
| 1414 | db_multi_exec( |
| @@ -1498,12 +1502,12 @@ | |
| 1502 | "VALUES('ADDED',%Q,%d,%Q)", |
| 1503 | /* fnm */ zName, |
| 1504 | /* ridm */ db_column_int(&q,2), |
| 1505 | /* fnr */ zName |
| 1506 | ); |
| 1507 | if( useUndo ) undo_save(zName); |
| 1508 | if( !dryRunFlag ){ |
| 1509 | vfile_to_disk(0, idm, 0, 0); |
| 1510 | } |
| 1511 | } |
| 1512 | db_finalize(&q); |
| 1513 | |
| @@ -1558,9 +1562,9 @@ | |
| 1562 | } |
| 1563 | if( bMultiMerge && nConflict==0 ){ |
| 1564 | nMerge++; |
| 1565 | goto merge_next_child; |
| 1566 | } |
| 1567 | if( useUndo ) undo_finish(); |
| 1568 | |
| 1569 | db_end_transaction(dryRunFlag); |
| 1570 | } |
| 1571 |
+119
-15
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -1676,34 +1676,44 @@ | ||
| 1676 | 1676 | ** n=N Show N artifacts |
| 1677 | 1677 | ** s=S Start with artifact number S |
| 1678 | 1678 | ** priv Show only unpublished or private artifacts |
| 1679 | 1679 | ** phan Show only phantom artifacts |
| 1680 | 1680 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1681 | +** recent Show the most recent N artifacts | |
| 1681 | 1682 | */ |
| 1682 | 1683 | void bloblist_page(void){ |
| 1683 | 1684 | Stmt q; |
| 1684 | 1685 | int s = atoi(PD("s","0")); |
| 1685 | 1686 | int n = atoi(PD("n","5000")); |
| 1686 | 1687 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1687 | 1688 | int privOnly = PB("priv"); |
| 1688 | 1689 | int phantomOnly = PB("phan"); |
| 1689 | 1690 | int hashClr = PB("hclr"); |
| 1691 | + int bRecent = PB("recent"); | |
| 1692 | + int bUnclst = PB("unclustered"); | |
| 1690 | 1693 | char *zRange; |
| 1691 | 1694 | char *zSha1Bg; |
| 1692 | 1695 | char *zSha3Bg; |
| 1693 | 1696 | |
| 1694 | 1697 | login_check_credentials(); |
| 1695 | 1698 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1696 | 1699 | cgi_check_for_malice(); |
| 1697 | 1700 | style_header("List Of Artifacts"); |
| 1698 | 1701 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1702 | + if( bRecent==0 || n!=250 ){ | |
| 1703 | + style_submenu_element("Recent","bloblist?n=250&recent"); | |
| 1704 | + } | |
| 1705 | + if( bUnclst==0 ){ | |
| 1706 | + style_submenu_element("Unclustered","bloblist?unclustered"); | |
| 1707 | + } | |
| 1699 | 1708 | if( g.perm.Admin ){ |
| 1700 | 1709 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1701 | 1710 | } |
| 1702 | 1711 | if( !phantomOnly ){ |
| 1703 | 1712 | style_submenu_element("Phantoms", "bloblist?phan"); |
| 1704 | 1713 | } |
| 1714 | + style_submenu_element("Clusters","clusterlist"); | |
| 1705 | 1715 | if( g.perm.Private || g.perm.Admin ){ |
| 1706 | 1716 | if( !privOnly ){ |
| 1707 | 1717 | style_submenu_element("Private", "bloblist?priv"); |
| 1708 | 1718 | } |
| 1709 | 1719 | }else{ |
| @@ -1710,58 +1720,70 @@ | ||
| 1710 | 1720 | privOnly = 0; |
| 1711 | 1721 | } |
| 1712 | 1722 | if( g.perm.Write ){ |
| 1713 | 1723 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1714 | 1724 | } |
| 1715 | - if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){ | |
| 1725 | + if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){ | |
| 1716 | 1726 | int i; |
| 1717 | 1727 | @ <p>Select a range of artifacts to view:</p> |
| 1718 | 1728 | @ <ul> |
| 1719 | 1729 | for(i=1; i<=mx; i+=n){ |
| 1720 | 1730 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| 1721 | 1731 | @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a> |
| 1722 | 1732 | } |
| 1733 | + @ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a> | |
| 1734 | + @ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a> | |
| 1723 | 1735 | @ </ul> |
| 1724 | 1736 | style_finish_page(); |
| 1725 | 1737 | return; |
| 1726 | 1738 | } |
| 1727 | 1739 | if( phantomOnly || privOnly || mx>n ){ |
| 1728 | 1740 | style_submenu_element("Index", "bloblist"); |
| 1729 | 1741 | } |
| 1730 | 1742 | if( privOnly ){ |
| 1743 | + @ <h2>Private Artifacts</h2> | |
| 1731 | 1744 | zRange = mprintf("IN private"); |
| 1732 | 1745 | }else if( phantomOnly ){ |
| 1746 | + @ <h2>Phantom Artifacts</h2> | |
| 1733 | 1747 | zRange = mprintf("IN phantom"); |
| 1748 | + }else if( bUnclst ){ | |
| 1749 | + @ <h2>Unclustered Artifacts</h2> | |
| 1750 | + zRange = mprintf("IN unclustered"); | |
| 1751 | + }else if( bRecent ){ | |
| 1752 | + @ <h2>%d(n) Most Recent Artifacts</h2> | |
| 1753 | + zRange = mprintf(">=(SELECT rid FROM blob" | |
| 1754 | + " ORDER BY rid DESC LIMIT 1 OFFSET %d)",n); | |
| 1734 | 1755 | }else{ |
| 1735 | 1756 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1736 | 1757 | } |
| 1737 | 1758 | describe_artifacts(zRange); |
| 1738 | 1759 | fossil_free(zRange); |
| 1739 | 1760 | db_prepare(&q, |
| 1740 | - "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" | |
| 1741 | - " FROM description ORDER BY rid" | |
| 1761 | + /* 0 1 2 3 4 5 6 */ | |
| 1762 | + "SELECT rid, uuid, summary, isPrivate, type='phantom', ref, rcvid, " | |
| 1763 | + " datetime(rcvfrom.mtime)" | |
| 1764 | + " FROM description LEFT JOIN rcvfrom USING(rcvid)" | |
| 1765 | + " ORDER BY rid %s", | |
| 1766 | + ((bRecent||bUnclst)?"DESC":"ASC")/*safe-for-%s*/ | |
| 1742 | 1767 | ); |
| 1743 | 1768 | if( skin_detail_boolean("white-foreground") ){ |
| 1744 | 1769 | zSha1Bg = "#714417"; |
| 1745 | 1770 | zSha3Bg = "#177117"; |
| 1746 | 1771 | }else{ |
| 1747 | 1772 | zSha1Bg = "#ebffb0"; |
| 1748 | 1773 | zSha3Bg = "#b0ffb0"; |
| 1749 | 1774 | } |
| 1750 | 1775 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 1751 | - if( g.perm.Admin ){ | |
| 1752 | - @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks | |
| 1753 | - }else{ | |
| 1754 | - @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks | |
| 1755 | - } | |
| 1776 | + @ <tr><th>RID<th>Hash<th>Received<th>Description<th>Ref<th>Remarks | |
| 1756 | 1777 | while( db_step(&q)==SQLITE_ROW ){ |
| 1757 | 1778 | int rid = db_column_int(&q,0); |
| 1758 | 1779 | const char *zUuid = db_column_text(&q, 1); |
| 1759 | 1780 | const char *zDesc = db_column_text(&q, 2); |
| 1760 | 1781 | int isPriv = db_column_int(&q,3); |
| 1761 | 1782 | int isPhantom = db_column_int(&q,4); |
| 1762 | - const char *zRef = db_column_text(&q,6); | |
| 1783 | + const char *zRef = db_column_text(&q,5); | |
| 1784 | + const char *zDate = db_column_text(&q,7); | |
| 1763 | 1785 | if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ |
| 1764 | 1786 | /* Don't show private artifacts to users without Private (x) permission */ |
| 1765 | 1787 | continue; |
| 1766 | 1788 | } |
| 1767 | 1789 | if( hashClr ){ |
| @@ -1770,16 +1792,14 @@ | ||
| 1770 | 1792 | }else{ |
| 1771 | 1793 | @ <tr><td align="right">%d(rid)</td> |
| 1772 | 1794 | } |
| 1773 | 1795 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1774 | 1796 | if( g.perm.Admin ){ |
| 1775 | - int rcvid = db_column_int(&q,5); | |
| 1776 | - if( rcvid<=0 ){ | |
| 1777 | - @ <td> | |
| 1778 | - }else{ | |
| 1779 | - @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> | |
| 1780 | - } | |
| 1797 | + int rcvid = db_column_int(&q, 6); | |
| 1798 | + @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%h(zDate)</a> | |
| 1799 | + }else{ | |
| 1800 | + @ <td>%h(zDate) | |
| 1781 | 1801 | } |
| 1782 | 1802 | @ <td align="left">%h(zDesc)</td> |
| 1783 | 1803 | if( zRef && zRef[0] ){ |
| 1784 | 1804 | @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> |
| 1785 | 1805 | }else{ |
| @@ -2163,5 +2183,89 @@ | ||
| 2163 | 2183 | " ORDER BY 1"); |
| 2164 | 2184 | @ <h1>Hash Prefix Collisions on All Artifacts</h1> |
| 2165 | 2185 | collision_report("SELECT uuid FROM blob ORDER BY 1"); |
| 2166 | 2186 | style_finish_page(); |
| 2167 | 2187 | } |
| 2188 | + | |
| 2189 | +/* | |
| 2190 | +** WEBPAGE: clusterlist | |
| 2191 | +** | |
| 2192 | +** Show information about all cluster artifacts in the database. | |
| 2193 | +*/ | |
| 2194 | +void clusterlist_page(void){ | |
| 2195 | + Stmt q; | |
| 2196 | + int cnt = 1; | |
| 2197 | + sqlite3_int64 szTotal = 0; | |
| 2198 | + sqlite3_int64 szCTotal = 0; | |
| 2199 | + login_check_credentials(); | |
| 2200 | + if( !g.perm.Read ){ login_needed(g.anon.Read); return; } | |
| 2201 | + style_header("All Cluster Artifacts"); | |
| 2202 | + style_submenu_element("All Artifactst", "bloblist"); | |
| 2203 | + if( g.perm.Admin ){ | |
| 2204 | + style_submenu_element("Artifact Log", "rcvfromlist"); | |
| 2205 | + } | |
| 2206 | + style_submenu_element("Phantoms", "bloblist?phan"); | |
| 2207 | + if( g.perm.Write ){ | |
| 2208 | + style_submenu_element("Artifact Stats", "artifact_stats"); | |
| 2209 | + } | |
| 2210 | + | |
| 2211 | + db_prepare(&q, | |
| 2212 | + "SELECT blob.uuid, " | |
| 2213 | + " blob.size, " | |
| 2214 | + " octet_length(blob.content), " | |
| 2215 | + " datetime(rcvfrom.mtime)," | |
| 2216 | + " user.login," | |
| 2217 | + " rcvfrom.ipaddr" | |
| 2218 | + " FROM tagxref JOIN blob ON tagxref.rid=blob.rid" | |
| 2219 | + " LEFT JOIN rcvfrom ON blob.rcvid=rcvfrom.rcvid" | |
| 2220 | + " LEFT JOIN user ON user.uid=rcvfrom.uid" | |
| 2221 | + " WHERE tagxref.tagid=%d" | |
| 2222 | + " ORDER BY rcvfrom.mtime, blob.uuid", | |
| 2223 | + TAG_CLUSTER | |
| 2224 | + ); | |
| 2225 | + @ <table cellpadding="2" cellspacing="0" border="1"> | |
| 2226 | + @ <tr><th> | |
| 2227 | + @ <th>Hash | |
| 2228 | + @ <th>Date Received | |
| 2229 | + @ <th>Size | |
| 2230 | + @ <th>Compressed Size | |
| 2231 | + if( g.perm.Admin ){ | |
| 2232 | + @ <th>User<th>IP-Address | |
| 2233 | + } | |
| 2234 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 2235 | + const char *zUuid = db_column_text(&q, 0); | |
| 2236 | + sqlite3_int64 sz = db_column_int64(&q, 1); | |
| 2237 | + sqlite3_int64 szC = db_column_int64(&q, 2); | |
| 2238 | + const char *zDate = db_column_text(&q, 3); | |
| 2239 | + const char *zUser = db_column_text(&q, 4); | |
| 2240 | + const char *zIp = db_column_text(&q, 5); | |
| 2241 | + szTotal += sz; | |
| 2242 | + szCTotal += szC; | |
| 2243 | + @ <tr><td align="right">%d(cnt++) | |
| 2244 | + @ <td><a href="%R/info/%S(zUuid)">%S(zUuid)</a> | |
| 2245 | + if( zDate ){ | |
| 2246 | + @ <td>%h(zDate) | |
| 2247 | + }else{ | |
| 2248 | + @ <td> | |
| 2249 | + } | |
| 2250 | + @ <td align="right">%,lld(sz) | |
| 2251 | + @ <td align="right">%,lld(szC) | |
| 2252 | + if( g.perm.Admin ){ | |
| 2253 | + if( zUser ){ | |
| 2254 | + @ <td>%h(zUser) | |
| 2255 | + }else{ | |
| 2256 | + @ <td> | |
| 2257 | + } | |
| 2258 | + if( zIp ){ | |
| 2259 | + @ <td>%h(zIp) | |
| 2260 | + }else{ | |
| 2261 | + @ <td> | |
| 2262 | + } | |
| 2263 | + } | |
| 2264 | + @ </tr> | |
| 2265 | + } | |
| 2266 | + @ </table> | |
| 2267 | + db_finalize(&q); | |
| 2268 | + @ <p>Total size of all clusters: %,lld(szTotal) bytes, | |
| 2269 | + @ %,lld(szCTotal) bytes compressed</p> | |
| 2270 | + style_finish_page(); | |
| 2271 | +} | |
| 2168 | 2272 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -1676,34 +1676,44 @@ | |
| 1676 | ** n=N Show N artifacts |
| 1677 | ** s=S Start with artifact number S |
| 1678 | ** priv Show only unpublished or private artifacts |
| 1679 | ** phan Show only phantom artifacts |
| 1680 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1681 | */ |
| 1682 | void bloblist_page(void){ |
| 1683 | Stmt q; |
| 1684 | int s = atoi(PD("s","0")); |
| 1685 | int n = atoi(PD("n","5000")); |
| 1686 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1687 | int privOnly = PB("priv"); |
| 1688 | int phantomOnly = PB("phan"); |
| 1689 | int hashClr = PB("hclr"); |
| 1690 | char *zRange; |
| 1691 | char *zSha1Bg; |
| 1692 | char *zSha3Bg; |
| 1693 | |
| 1694 | login_check_credentials(); |
| 1695 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1696 | cgi_check_for_malice(); |
| 1697 | style_header("List Of Artifacts"); |
| 1698 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1699 | if( g.perm.Admin ){ |
| 1700 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1701 | } |
| 1702 | if( !phantomOnly ){ |
| 1703 | style_submenu_element("Phantoms", "bloblist?phan"); |
| 1704 | } |
| 1705 | if( g.perm.Private || g.perm.Admin ){ |
| 1706 | if( !privOnly ){ |
| 1707 | style_submenu_element("Private", "bloblist?priv"); |
| 1708 | } |
| 1709 | }else{ |
| @@ -1710,58 +1720,70 @@ | |
| 1710 | privOnly = 0; |
| 1711 | } |
| 1712 | if( g.perm.Write ){ |
| 1713 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1714 | } |
| 1715 | if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){ |
| 1716 | int i; |
| 1717 | @ <p>Select a range of artifacts to view:</p> |
| 1718 | @ <ul> |
| 1719 | for(i=1; i<=mx; i+=n){ |
| 1720 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| 1721 | @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a> |
| 1722 | } |
| 1723 | @ </ul> |
| 1724 | style_finish_page(); |
| 1725 | return; |
| 1726 | } |
| 1727 | if( phantomOnly || privOnly || mx>n ){ |
| 1728 | style_submenu_element("Index", "bloblist"); |
| 1729 | } |
| 1730 | if( privOnly ){ |
| 1731 | zRange = mprintf("IN private"); |
| 1732 | }else if( phantomOnly ){ |
| 1733 | zRange = mprintf("IN phantom"); |
| 1734 | }else{ |
| 1735 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1736 | } |
| 1737 | describe_artifacts(zRange); |
| 1738 | fossil_free(zRange); |
| 1739 | db_prepare(&q, |
| 1740 | "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref" |
| 1741 | " FROM description ORDER BY rid" |
| 1742 | ); |
| 1743 | if( skin_detail_boolean("white-foreground") ){ |
| 1744 | zSha1Bg = "#714417"; |
| 1745 | zSha3Bg = "#177117"; |
| 1746 | }else{ |
| 1747 | zSha1Bg = "#ebffb0"; |
| 1748 | zSha3Bg = "#b0ffb0"; |
| 1749 | } |
| 1750 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 1751 | if( g.perm.Admin ){ |
| 1752 | @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks |
| 1753 | }else{ |
| 1754 | @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks |
| 1755 | } |
| 1756 | while( db_step(&q)==SQLITE_ROW ){ |
| 1757 | int rid = db_column_int(&q,0); |
| 1758 | const char *zUuid = db_column_text(&q, 1); |
| 1759 | const char *zDesc = db_column_text(&q, 2); |
| 1760 | int isPriv = db_column_int(&q,3); |
| 1761 | int isPhantom = db_column_int(&q,4); |
| 1762 | const char *zRef = db_column_text(&q,6); |
| 1763 | if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ |
| 1764 | /* Don't show private artifacts to users without Private (x) permission */ |
| 1765 | continue; |
| 1766 | } |
| 1767 | if( hashClr ){ |
| @@ -1770,16 +1792,14 @@ | |
| 1770 | }else{ |
| 1771 | @ <tr><td align="right">%d(rid)</td> |
| 1772 | } |
| 1773 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1774 | if( g.perm.Admin ){ |
| 1775 | int rcvid = db_column_int(&q,5); |
| 1776 | if( rcvid<=0 ){ |
| 1777 | @ <td> |
| 1778 | }else{ |
| 1779 | @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a> |
| 1780 | } |
| 1781 | } |
| 1782 | @ <td align="left">%h(zDesc)</td> |
| 1783 | if( zRef && zRef[0] ){ |
| 1784 | @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> |
| 1785 | }else{ |
| @@ -2163,5 +2183,89 @@ | |
| 2163 | " ORDER BY 1"); |
| 2164 | @ <h1>Hash Prefix Collisions on All Artifacts</h1> |
| 2165 | collision_report("SELECT uuid FROM blob ORDER BY 1"); |
| 2166 | style_finish_page(); |
| 2167 | } |
| 2168 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -1676,34 +1676,44 @@ | |
| 1676 | ** n=N Show N artifacts |
| 1677 | ** s=S Start with artifact number S |
| 1678 | ** priv Show only unpublished or private artifacts |
| 1679 | ** phan Show only phantom artifacts |
| 1680 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1681 | ** recent Show the most recent N artifacts |
| 1682 | */ |
| 1683 | void bloblist_page(void){ |
| 1684 | Stmt q; |
| 1685 | int s = atoi(PD("s","0")); |
| 1686 | int n = atoi(PD("n","5000")); |
| 1687 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1688 | int privOnly = PB("priv"); |
| 1689 | int phantomOnly = PB("phan"); |
| 1690 | int hashClr = PB("hclr"); |
| 1691 | int bRecent = PB("recent"); |
| 1692 | int bUnclst = PB("unclustered"); |
| 1693 | char *zRange; |
| 1694 | char *zSha1Bg; |
| 1695 | char *zSha3Bg; |
| 1696 | |
| 1697 | login_check_credentials(); |
| 1698 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1699 | cgi_check_for_malice(); |
| 1700 | style_header("List Of Artifacts"); |
| 1701 | style_submenu_element("250 Largest", "bigbloblist"); |
| 1702 | if( bRecent==0 || n!=250 ){ |
| 1703 | style_submenu_element("Recent","bloblist?n=250&recent"); |
| 1704 | } |
| 1705 | if( bUnclst==0 ){ |
| 1706 | style_submenu_element("Unclustered","bloblist?unclustered"); |
| 1707 | } |
| 1708 | if( g.perm.Admin ){ |
| 1709 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1710 | } |
| 1711 | if( !phantomOnly ){ |
| 1712 | style_submenu_element("Phantoms", "bloblist?phan"); |
| 1713 | } |
| 1714 | style_submenu_element("Clusters","clusterlist"); |
| 1715 | if( g.perm.Private || g.perm.Admin ){ |
| 1716 | if( !privOnly ){ |
| 1717 | style_submenu_element("Private", "bloblist?priv"); |
| 1718 | } |
| 1719 | }else{ |
| @@ -1710,58 +1720,70 @@ | |
| 1720 | privOnly = 0; |
| 1721 | } |
| 1722 | if( g.perm.Write ){ |
| 1723 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1724 | } |
| 1725 | if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){ |
| 1726 | int i; |
| 1727 | @ <p>Select a range of artifacts to view:</p> |
| 1728 | @ <ul> |
| 1729 | for(i=1; i<=mx; i+=n){ |
| 1730 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| 1731 | @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a> |
| 1732 | } |
| 1733 | @ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a> |
| 1734 | @ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a> |
| 1735 | @ </ul> |
| 1736 | style_finish_page(); |
| 1737 | return; |
| 1738 | } |
| 1739 | if( phantomOnly || privOnly || mx>n ){ |
| 1740 | style_submenu_element("Index", "bloblist"); |
| 1741 | } |
| 1742 | if( privOnly ){ |
| 1743 | @ <h2>Private Artifacts</h2> |
| 1744 | zRange = mprintf("IN private"); |
| 1745 | }else if( phantomOnly ){ |
| 1746 | @ <h2>Phantom Artifacts</h2> |
| 1747 | zRange = mprintf("IN phantom"); |
| 1748 | }else if( bUnclst ){ |
| 1749 | @ <h2>Unclustered Artifacts</h2> |
| 1750 | zRange = mprintf("IN unclustered"); |
| 1751 | }else if( bRecent ){ |
| 1752 | @ <h2>%d(n) Most Recent Artifacts</h2> |
| 1753 | zRange = mprintf(">=(SELECT rid FROM blob" |
| 1754 | " ORDER BY rid DESC LIMIT 1 OFFSET %d)",n); |
| 1755 | }else{ |
| 1756 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1757 | } |
| 1758 | describe_artifacts(zRange); |
| 1759 | fossil_free(zRange); |
| 1760 | db_prepare(&q, |
| 1761 | /* 0 1 2 3 4 5 6 */ |
| 1762 | "SELECT rid, uuid, summary, isPrivate, type='phantom', ref, rcvid, " |
| 1763 | " datetime(rcvfrom.mtime)" |
| 1764 | " FROM description LEFT JOIN rcvfrom USING(rcvid)" |
| 1765 | " ORDER BY rid %s", |
| 1766 | ((bRecent||bUnclst)?"DESC":"ASC")/*safe-for-%s*/ |
| 1767 | ); |
| 1768 | if( skin_detail_boolean("white-foreground") ){ |
| 1769 | zSha1Bg = "#714417"; |
| 1770 | zSha3Bg = "#177117"; |
| 1771 | }else{ |
| 1772 | zSha1Bg = "#ebffb0"; |
| 1773 | zSha3Bg = "#b0ffb0"; |
| 1774 | } |
| 1775 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 1776 | @ <tr><th>RID<th>Hash<th>Received<th>Description<th>Ref<th>Remarks |
| 1777 | while( db_step(&q)==SQLITE_ROW ){ |
| 1778 | int rid = db_column_int(&q,0); |
| 1779 | const char *zUuid = db_column_text(&q, 1); |
| 1780 | const char *zDesc = db_column_text(&q, 2); |
| 1781 | int isPriv = db_column_int(&q,3); |
| 1782 | int isPhantom = db_column_int(&q,4); |
| 1783 | const char *zRef = db_column_text(&q,5); |
| 1784 | const char *zDate = db_column_text(&q,7); |
| 1785 | if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ |
| 1786 | /* Don't show private artifacts to users without Private (x) permission */ |
| 1787 | continue; |
| 1788 | } |
| 1789 | if( hashClr ){ |
| @@ -1770,16 +1792,14 @@ | |
| 1792 | }else{ |
| 1793 | @ <tr><td align="right">%d(rid)</td> |
| 1794 | } |
| 1795 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1796 | if( g.perm.Admin ){ |
| 1797 | int rcvid = db_column_int(&q, 6); |
| 1798 | @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%h(zDate)</a> |
| 1799 | }else{ |
| 1800 | @ <td>%h(zDate) |
| 1801 | } |
| 1802 | @ <td align="left">%h(zDesc)</td> |
| 1803 | if( zRef && zRef[0] ){ |
| 1804 | @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> |
| 1805 | }else{ |
| @@ -2163,5 +2183,89 @@ | |
| 2183 | " ORDER BY 1"); |
| 2184 | @ <h1>Hash Prefix Collisions on All Artifacts</h1> |
| 2185 | collision_report("SELECT uuid FROM blob ORDER BY 1"); |
| 2186 | style_finish_page(); |
| 2187 | } |
| 2188 | |
| 2189 | /* |
| 2190 | ** WEBPAGE: clusterlist |
| 2191 | ** |
| 2192 | ** Show information about all cluster artifacts in the database. |
| 2193 | */ |
| 2194 | void clusterlist_page(void){ |
| 2195 | Stmt q; |
| 2196 | int cnt = 1; |
| 2197 | sqlite3_int64 szTotal = 0; |
| 2198 | sqlite3_int64 szCTotal = 0; |
| 2199 | login_check_credentials(); |
| 2200 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2201 | style_header("All Cluster Artifacts"); |
| 2202 | style_submenu_element("All Artifactst", "bloblist"); |
| 2203 | if( g.perm.Admin ){ |
| 2204 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 2205 | } |
| 2206 | style_submenu_element("Phantoms", "bloblist?phan"); |
| 2207 | if( g.perm.Write ){ |
| 2208 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 2209 | } |
| 2210 | |
| 2211 | db_prepare(&q, |
| 2212 | "SELECT blob.uuid, " |
| 2213 | " blob.size, " |
| 2214 | " octet_length(blob.content), " |
| 2215 | " datetime(rcvfrom.mtime)," |
| 2216 | " user.login," |
| 2217 | " rcvfrom.ipaddr" |
| 2218 | " FROM tagxref JOIN blob ON tagxref.rid=blob.rid" |
| 2219 | " LEFT JOIN rcvfrom ON blob.rcvid=rcvfrom.rcvid" |
| 2220 | " LEFT JOIN user ON user.uid=rcvfrom.uid" |
| 2221 | " WHERE tagxref.tagid=%d" |
| 2222 | " ORDER BY rcvfrom.mtime, blob.uuid", |
| 2223 | TAG_CLUSTER |
| 2224 | ); |
| 2225 | @ <table cellpadding="2" cellspacing="0" border="1"> |
| 2226 | @ <tr><th> |
| 2227 | @ <th>Hash |
| 2228 | @ <th>Date Received |
| 2229 | @ <th>Size |
| 2230 | @ <th>Compressed Size |
| 2231 | if( g.perm.Admin ){ |
| 2232 | @ <th>User<th>IP-Address |
| 2233 | } |
| 2234 | while( db_step(&q)==SQLITE_ROW ){ |
| 2235 | const char *zUuid = db_column_text(&q, 0); |
| 2236 | sqlite3_int64 sz = db_column_int64(&q, 1); |
| 2237 | sqlite3_int64 szC = db_column_int64(&q, 2); |
| 2238 | const char *zDate = db_column_text(&q, 3); |
| 2239 | const char *zUser = db_column_text(&q, 4); |
| 2240 | const char *zIp = db_column_text(&q, 5); |
| 2241 | szTotal += sz; |
| 2242 | szCTotal += szC; |
| 2243 | @ <tr><td align="right">%d(cnt++) |
| 2244 | @ <td><a href="%R/info/%S(zUuid)">%S(zUuid)</a> |
| 2245 | if( zDate ){ |
| 2246 | @ <td>%h(zDate) |
| 2247 | }else{ |
| 2248 | @ <td> |
| 2249 | } |
| 2250 | @ <td align="right">%,lld(sz) |
| 2251 | @ <td align="right">%,lld(szC) |
| 2252 | if( g.perm.Admin ){ |
| 2253 | if( zUser ){ |
| 2254 | @ <td>%h(zUser) |
| 2255 | }else{ |
| 2256 | @ <td> |
| 2257 | } |
| 2258 | if( zIp ){ |
| 2259 | @ <td>%h(zIp) |
| 2260 | }else{ |
| 2261 | @ <td> |
| 2262 | } |
| 2263 | } |
| 2264 | @ </tr> |
| 2265 | } |
| 2266 | @ </table> |
| 2267 | db_finalize(&q); |
| 2268 | @ <p>Total size of all clusters: %,lld(szTotal) bytes, |
| 2269 | @ %,lld(szCTotal) bytes compressed</p> |
| 2270 | style_finish_page(); |
| 2271 | } |
| 2272 |
+50
-17
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -81,13 +81,12 @@ | ||
| 81 | 81 | sqlite3_context *context, |
| 82 | 82 | int argc, |
| 83 | 83 | sqlite3_value **argv |
| 84 | 84 | ){ |
| 85 | 85 | const char *zFile; |
| 86 | - Blob x, y; | |
| 86 | + Blob x, y, out; | |
| 87 | 87 | int rid; |
| 88 | - char *aOut; | |
| 89 | 88 | int nOut; |
| 90 | 89 | sqlite3_int64 sz; |
| 91 | 90 | |
| 92 | 91 | rid = sqlite3_value_int(argv[0]); |
| 93 | 92 | if( !content_get(rid, &x) ){ |
| @@ -104,34 +103,29 @@ | ||
| 104 | 103 | if( sz<0 ){ |
| 105 | 104 | sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1); |
| 106 | 105 | blob_reset(&x); |
| 107 | 106 | return; |
| 108 | 107 | } |
| 109 | - aOut = sqlite3_malloc64(sz+70); | |
| 110 | - if( aOut==0 ){ | |
| 111 | - sqlite3_result_error_nomem(context); | |
| 112 | - blob_reset(&y); | |
| 113 | - blob_reset(&x); | |
| 114 | - return; | |
| 115 | - } | |
| 108 | + blob_init(&out, 0, 0); | |
| 109 | + blob_resize(&out, sz+70); | |
| 116 | 110 | if( blob_size(&x)==blob_size(&y) |
| 117 | 111 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 118 | 112 | ){ |
| 119 | 113 | blob_reset(&y); |
| 120 | 114 | blob_reset(&x); |
| 121 | 115 | sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); |
| 122 | 116 | return; |
| 123 | 117 | } |
| 124 | 118 | nOut = delta_create(blob_buffer(&x),blob_size(&x), |
| 125 | - blob_buffer(&y),blob_size(&y), aOut); | |
| 119 | + blob_buffer(&y),blob_size(&y), blob_buffer(&out)); | |
| 120 | + blob_resize(&out, nOut); | |
| 126 | 121 | blob_reset(&x); |
| 127 | 122 | blob_reset(&y); |
| 128 | - blob_init(&x, aOut, nOut); | |
| 129 | - blob_compress(&x, &x); | |
| 130 | - sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x), | |
| 123 | + blob_compress(&out, &out); | |
| 124 | + sqlite3_result_blob64(context, blob_buffer(&out), blob_size(&out), | |
| 131 | 125 | SQLITE_TRANSIENT); |
| 132 | - blob_reset(&x); | |
| 126 | + blob_reset(&out); | |
| 133 | 127 | } |
| 134 | 128 | |
| 135 | 129 | |
| 136 | 130 | /* |
| 137 | 131 | ** Generate a binary patch file and store it into the file |
| @@ -387,11 +381,11 @@ | ||
| 387 | 381 | if( unsaved_changes(0) ){ |
| 388 | 382 | if( (mFlags & PATCH_FORCE)==0 ){ |
| 389 | 383 | fossil_fatal("Cannot apply patch: there are unsaved changes " |
| 390 | 384 | "in the current check-out"); |
| 391 | 385 | }else{ |
| 392 | - blob_appendf(&cmd, "%$ revert", g.nameOfExe); | |
| 386 | + blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe); | |
| 393 | 387 | if( mFlags & PATCH_DRYRUN ){ |
| 394 | 388 | fossil_print("%s\n", blob_str(&cmd)); |
| 395 | 389 | }else{ |
| 396 | 390 | int rc = fossil_system(blob_str(&cmd)); |
| 397 | 391 | if( rc ){ |
| @@ -429,23 +423,27 @@ | ||
| 429 | 423 | } |
| 430 | 424 | } |
| 431 | 425 | } |
| 432 | 426 | blob_reset(&cmd); |
| 433 | 427 | if( db_table_exists("patch","patchmerge") ){ |
| 428 | + int nMerge = 0; | |
| 434 | 429 | db_prepare(&q, |
| 435 | 430 | "SELECT type, mhash, upper(type) FROM patch.patchmerge" |
| 436 | 431 | " WHERE type IN ('merge','cherrypick','backout','integrate')" |
| 437 | 432 | " AND mhash NOT GLOB '*[^a-fA-F0-9]*';" |
| 438 | 433 | ); |
| 439 | 434 | while( db_step(&q)==SQLITE_ROW ){ |
| 440 | 435 | const char *zType = db_column_text(&q,0); |
| 441 | 436 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 442 | 437 | if( strcmp(zType,"merge")==0 ){ |
| 443 | - blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1)); | |
| 438 | + blob_appendf(&cmd, " merge --noundo --nosync %s\n", | |
| 439 | + db_column_text(&q,1)); | |
| 444 | 440 | }else{ |
| 445 | - blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1)); | |
| 441 | + blob_appendf(&cmd, " merge --%s --noundo --nosync %s\n", | |
| 442 | + zType, db_column_text(&q,1)); | |
| 446 | 443 | } |
| 444 | + nMerge++; | |
| 447 | 445 | if( mFlags & PATCH_VERBOSE ){ |
| 448 | 446 | fossil_print("%-10s %s\n", db_column_text(&q,2), |
| 449 | 447 | db_column_text(&q,0)); |
| 450 | 448 | } |
| 451 | 449 | } |
| @@ -458,10 +456,45 @@ | ||
| 458 | 456 | fossil_fatal("unable to do merges:\n%s", |
| 459 | 457 | blob_str(&cmd)); |
| 460 | 458 | } |
| 461 | 459 | } |
| 462 | 460 | blob_reset(&cmd); |
| 461 | + | |
| 462 | + /* 2024-12-16 https://fossil-scm.org/home/forumpost/51a37054 | |
| 463 | + ** If one or more merge operations occurred in the patch and there are | |
| 464 | + ** files that are marked as "chnged' in the local VFILE but which | |
| 465 | + ** are not mentioned as having been modified in the patch, then | |
| 466 | + ** revert those files. | |
| 467 | + */ | |
| 468 | + if( nMerge ){ | |
| 469 | + int vid = db_lget_int("checkout", 0); | |
| 470 | + int nRevert = 0; | |
| 471 | + blob_append_escaped_arg(&cmd, g.nameOfExe, 1); | |
| 472 | + blob_appendf(&cmd, " revert --noundo "); | |
| 473 | + db_prepare(&q, | |
| 474 | + "SELECT pathname FROM vfile WHERE vid=%d AND chnged " | |
| 475 | + "EXCEPT SELECT pathname FROM chng", | |
| 476 | + vid | |
| 477 | + ); | |
| 478 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 479 | + blob_append_escaped_arg(&cmd, db_column_text(&q,0), 1); | |
| 480 | + nRevert++; | |
| 481 | + } | |
| 482 | + db_finalize(&q); | |
| 483 | + if( nRevert ){ | |
| 484 | + if( mFlags & PATCH_DRYRUN ){ | |
| 485 | + fossil_print("%s", blob_str(&cmd)); | |
| 486 | + }else{ | |
| 487 | + int rc = fossil_unsafe_system(blob_str(&cmd)); | |
| 488 | + if( rc ){ | |
| 489 | + fossil_fatal("unable to do reverts:\n%s", | |
| 490 | + blob_str(&cmd)); | |
| 491 | + } | |
| 492 | + } | |
| 493 | + } | |
| 494 | + blob_reset(&cmd); | |
| 495 | + } | |
| 463 | 496 | } |
| 464 | 497 | |
| 465 | 498 | /* Deletions */ |
| 466 | 499 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 467 | 500 | " WHERE origname IS NULL AND delta IS NULL"); |
| 468 | 501 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -81,13 +81,12 @@ | |
| 81 | sqlite3_context *context, |
| 82 | int argc, |
| 83 | sqlite3_value **argv |
| 84 | ){ |
| 85 | const char *zFile; |
| 86 | Blob x, y; |
| 87 | int rid; |
| 88 | char *aOut; |
| 89 | int nOut; |
| 90 | sqlite3_int64 sz; |
| 91 | |
| 92 | rid = sqlite3_value_int(argv[0]); |
| 93 | if( !content_get(rid, &x) ){ |
| @@ -104,34 +103,29 @@ | |
| 104 | if( sz<0 ){ |
| 105 | sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1); |
| 106 | blob_reset(&x); |
| 107 | return; |
| 108 | } |
| 109 | aOut = sqlite3_malloc64(sz+70); |
| 110 | if( aOut==0 ){ |
| 111 | sqlite3_result_error_nomem(context); |
| 112 | blob_reset(&y); |
| 113 | blob_reset(&x); |
| 114 | return; |
| 115 | } |
| 116 | if( blob_size(&x)==blob_size(&y) |
| 117 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 118 | ){ |
| 119 | blob_reset(&y); |
| 120 | blob_reset(&x); |
| 121 | sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); |
| 122 | return; |
| 123 | } |
| 124 | nOut = delta_create(blob_buffer(&x),blob_size(&x), |
| 125 | blob_buffer(&y),blob_size(&y), aOut); |
| 126 | blob_reset(&x); |
| 127 | blob_reset(&y); |
| 128 | blob_init(&x, aOut, nOut); |
| 129 | blob_compress(&x, &x); |
| 130 | sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x), |
| 131 | SQLITE_TRANSIENT); |
| 132 | blob_reset(&x); |
| 133 | } |
| 134 | |
| 135 | |
| 136 | /* |
| 137 | ** Generate a binary patch file and store it into the file |
| @@ -387,11 +381,11 @@ | |
| 387 | if( unsaved_changes(0) ){ |
| 388 | if( (mFlags & PATCH_FORCE)==0 ){ |
| 389 | fossil_fatal("Cannot apply patch: there are unsaved changes " |
| 390 | "in the current check-out"); |
| 391 | }else{ |
| 392 | blob_appendf(&cmd, "%$ revert", g.nameOfExe); |
| 393 | if( mFlags & PATCH_DRYRUN ){ |
| 394 | fossil_print("%s\n", blob_str(&cmd)); |
| 395 | }else{ |
| 396 | int rc = fossil_system(blob_str(&cmd)); |
| 397 | if( rc ){ |
| @@ -429,23 +423,27 @@ | |
| 429 | } |
| 430 | } |
| 431 | } |
| 432 | blob_reset(&cmd); |
| 433 | if( db_table_exists("patch","patchmerge") ){ |
| 434 | db_prepare(&q, |
| 435 | "SELECT type, mhash, upper(type) FROM patch.patchmerge" |
| 436 | " WHERE type IN ('merge','cherrypick','backout','integrate')" |
| 437 | " AND mhash NOT GLOB '*[^a-fA-F0-9]*';" |
| 438 | ); |
| 439 | while( db_step(&q)==SQLITE_ROW ){ |
| 440 | const char *zType = db_column_text(&q,0); |
| 441 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 442 | if( strcmp(zType,"merge")==0 ){ |
| 443 | blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1)); |
| 444 | }else{ |
| 445 | blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1)); |
| 446 | } |
| 447 | if( mFlags & PATCH_VERBOSE ){ |
| 448 | fossil_print("%-10s %s\n", db_column_text(&q,2), |
| 449 | db_column_text(&q,0)); |
| 450 | } |
| 451 | } |
| @@ -458,10 +456,45 @@ | |
| 458 | fossil_fatal("unable to do merges:\n%s", |
| 459 | blob_str(&cmd)); |
| 460 | } |
| 461 | } |
| 462 | blob_reset(&cmd); |
| 463 | } |
| 464 | |
| 465 | /* Deletions */ |
| 466 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 467 | " WHERE origname IS NULL AND delta IS NULL"); |
| 468 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -81,13 +81,12 @@ | |
| 81 | sqlite3_context *context, |
| 82 | int argc, |
| 83 | sqlite3_value **argv |
| 84 | ){ |
| 85 | const char *zFile; |
| 86 | Blob x, y, out; |
| 87 | int rid; |
| 88 | int nOut; |
| 89 | sqlite3_int64 sz; |
| 90 | |
| 91 | rid = sqlite3_value_int(argv[0]); |
| 92 | if( !content_get(rid, &x) ){ |
| @@ -104,34 +103,29 @@ | |
| 103 | if( sz<0 ){ |
| 104 | sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1); |
| 105 | blob_reset(&x); |
| 106 | return; |
| 107 | } |
| 108 | blob_init(&out, 0, 0); |
| 109 | blob_resize(&out, sz+70); |
| 110 | if( blob_size(&x)==blob_size(&y) |
| 111 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 112 | ){ |
| 113 | blob_reset(&y); |
| 114 | blob_reset(&x); |
| 115 | sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); |
| 116 | return; |
| 117 | } |
| 118 | nOut = delta_create(blob_buffer(&x),blob_size(&x), |
| 119 | blob_buffer(&y),blob_size(&y), blob_buffer(&out)); |
| 120 | blob_resize(&out, nOut); |
| 121 | blob_reset(&x); |
| 122 | blob_reset(&y); |
| 123 | blob_compress(&out, &out); |
| 124 | sqlite3_result_blob64(context, blob_buffer(&out), blob_size(&out), |
| 125 | SQLITE_TRANSIENT); |
| 126 | blob_reset(&out); |
| 127 | } |
| 128 | |
| 129 | |
| 130 | /* |
| 131 | ** Generate a binary patch file and store it into the file |
| @@ -387,11 +381,11 @@ | |
| 381 | if( unsaved_changes(0) ){ |
| 382 | if( (mFlags & PATCH_FORCE)==0 ){ |
| 383 | fossil_fatal("Cannot apply patch: there are unsaved changes " |
| 384 | "in the current check-out"); |
| 385 | }else{ |
| 386 | blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe); |
| 387 | if( mFlags & PATCH_DRYRUN ){ |
| 388 | fossil_print("%s\n", blob_str(&cmd)); |
| 389 | }else{ |
| 390 | int rc = fossil_system(blob_str(&cmd)); |
| 391 | if( rc ){ |
| @@ -429,23 +423,27 @@ | |
| 423 | } |
| 424 | } |
| 425 | } |
| 426 | blob_reset(&cmd); |
| 427 | if( db_table_exists("patch","patchmerge") ){ |
| 428 | int nMerge = 0; |
| 429 | db_prepare(&q, |
| 430 | "SELECT type, mhash, upper(type) FROM patch.patchmerge" |
| 431 | " WHERE type IN ('merge','cherrypick','backout','integrate')" |
| 432 | " AND mhash NOT GLOB '*[^a-fA-F0-9]*';" |
| 433 | ); |
| 434 | while( db_step(&q)==SQLITE_ROW ){ |
| 435 | const char *zType = db_column_text(&q,0); |
| 436 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 437 | if( strcmp(zType,"merge")==0 ){ |
| 438 | blob_appendf(&cmd, " merge --noundo --nosync %s\n", |
| 439 | db_column_text(&q,1)); |
| 440 | }else{ |
| 441 | blob_appendf(&cmd, " merge --%s --noundo --nosync %s\n", |
| 442 | zType, db_column_text(&q,1)); |
| 443 | } |
| 444 | nMerge++; |
| 445 | if( mFlags & PATCH_VERBOSE ){ |
| 446 | fossil_print("%-10s %s\n", db_column_text(&q,2), |
| 447 | db_column_text(&q,0)); |
| 448 | } |
| 449 | } |
| @@ -458,10 +456,45 @@ | |
| 456 | fossil_fatal("unable to do merges:\n%s", |
| 457 | blob_str(&cmd)); |
| 458 | } |
| 459 | } |
| 460 | blob_reset(&cmd); |
| 461 | |
| 462 | /* 2024-12-16 https://fossil-scm.org/home/forumpost/51a37054 |
| 463 | ** If one or more merge operations occurred in the patch and there are |
| 464 | ** files that are marked as "chnged' in the local VFILE but which |
| 465 | ** are not mentioned as having been modified in the patch, then |
| 466 | ** revert those files. |
| 467 | */ |
| 468 | if( nMerge ){ |
| 469 | int vid = db_lget_int("checkout", 0); |
| 470 | int nRevert = 0; |
| 471 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| 472 | blob_appendf(&cmd, " revert --noundo "); |
| 473 | db_prepare(&q, |
| 474 | "SELECT pathname FROM vfile WHERE vid=%d AND chnged " |
| 475 | "EXCEPT SELECT pathname FROM chng", |
| 476 | vid |
| 477 | ); |
| 478 | while( db_step(&q)==SQLITE_ROW ){ |
| 479 | blob_append_escaped_arg(&cmd, db_column_text(&q,0), 1); |
| 480 | nRevert++; |
| 481 | } |
| 482 | db_finalize(&q); |
| 483 | if( nRevert ){ |
| 484 | if( mFlags & PATCH_DRYRUN ){ |
| 485 | fossil_print("%s", blob_str(&cmd)); |
| 486 | }else{ |
| 487 | int rc = fossil_unsafe_system(blob_str(&cmd)); |
| 488 | if( rc ){ |
| 489 | fossil_fatal("unable to do reverts:\n%s", |
| 490 | blob_str(&cmd)); |
| 491 | } |
| 492 | } |
| 493 | } |
| 494 | blob_reset(&cmd); |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | /* Deletions */ |
| 499 | db_prepare(&q, "SELECT pathname FROM patch.chng" |
| 500 | " WHERE origname IS NULL AND delta IS NULL"); |
| 501 |
+14
-1
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -105,10 +105,11 @@ | ||
| 105 | 105 | Use %!j to include double-quotes around it. */ |
| 106 | 106 | #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$ |
| 107 | 107 | See blob_append_escaped_arg() for details |
| 108 | 108 | "%$" -> adds "./" prefix if necessary. |
| 109 | 109 | "%!$" -> omits the "./" prefix. */ |
| 110 | +#define etHEX 27 /* Encode a string as hexadecimal */ | |
| 110 | 111 | |
| 111 | 112 | |
| 112 | 113 | /* |
| 113 | 114 | ** An "etByte" is an 8-bit unsigned value. |
| 114 | 115 | */ |
| @@ -142,11 +143,11 @@ | ||
| 142 | 143 | ** NB: When modifying this table is it vital that you also update the fmtchr[] |
| 143 | 144 | ** variable to match!!! |
| 144 | 145 | */ |
| 145 | 146 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 146 | 147 | static const char aPrefix[] = "-x0\000X0"; |
| 147 | -static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$"; | |
| 148 | +static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H"; | |
| 148 | 149 | static const et_info fmtinfo[] = { |
| 149 | 150 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 150 | 151 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 151 | 152 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 152 | 153 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -176,10 +177,11 @@ | ||
| 176 | 177 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 177 | 178 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 178 | 179 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 179 | 180 | { '/', 0, 0, etPATH, 0, 0 }, |
| 180 | 181 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 182 | + { 'H', 0, 0, etHEX, 0, 0 }, | |
| 181 | 183 | { etERROR, 0,0,0,0,0} /* Must be last */ |
| 182 | 184 | }; |
| 183 | 185 | #define etNINFO count(fmtinfo) |
| 184 | 186 | |
| 185 | 187 | /* |
| @@ -843,10 +845,21 @@ | ||
| 843 | 845 | case etSHELLESC: { |
| 844 | 846 | char *zArg = va_arg(ap, char*); |
| 845 | 847 | blob_append_escaped_arg(pBlob, zArg, !flag_altform2); |
| 846 | 848 | length = width = 0; |
| 847 | 849 | break; |
| 850 | + } | |
| 851 | + case etHEX: { | |
| 852 | + char *zArg = va_arg(ap, char*); | |
| 853 | + int szArg = (int)strlen(zArg); | |
| 854 | + int szBlob = blob_size(pBlob); | |
| 855 | + u8 *aBuf; | |
| 856 | + blob_resize(pBlob, szBlob+szArg*2+1); | |
| 857 | + aBuf = (u8*)&blob_buffer(pBlob)[szBlob]; | |
| 858 | + encode16((const u8*)zArg, aBuf, szArg); | |
| 859 | + length = width = 0; | |
| 860 | + break; | |
| 848 | 861 | } |
| 849 | 862 | case etERROR: |
| 850 | 863 | buf[0] = '%'; |
| 851 | 864 | buf[1] = c; |
| 852 | 865 | errorflag = 0; |
| 853 | 866 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -105,10 +105,11 @@ | |
| 105 | Use %!j to include double-quotes around it. */ |
| 106 | #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$ |
| 107 | See blob_append_escaped_arg() for details |
| 108 | "%$" -> adds "./" prefix if necessary. |
| 109 | "%!$" -> omits the "./" prefix. */ |
| 110 | |
| 111 | |
| 112 | /* |
| 113 | ** An "etByte" is an 8-bit unsigned value. |
| 114 | */ |
| @@ -142,11 +143,11 @@ | |
| 142 | ** NB: When modifying this table is it vital that you also update the fmtchr[] |
| 143 | ** variable to match!!! |
| 144 | */ |
| 145 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 146 | static const char aPrefix[] = "-x0\000X0"; |
| 147 | static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$"; |
| 148 | static const et_info fmtinfo[] = { |
| 149 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 150 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 151 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 152 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -176,10 +177,11 @@ | |
| 176 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 177 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 178 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 179 | { '/', 0, 0, etPATH, 0, 0 }, |
| 180 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 181 | { etERROR, 0,0,0,0,0} /* Must be last */ |
| 182 | }; |
| 183 | #define etNINFO count(fmtinfo) |
| 184 | |
| 185 | /* |
| @@ -843,10 +845,21 @@ | |
| 843 | case etSHELLESC: { |
| 844 | char *zArg = va_arg(ap, char*); |
| 845 | blob_append_escaped_arg(pBlob, zArg, !flag_altform2); |
| 846 | length = width = 0; |
| 847 | break; |
| 848 | } |
| 849 | case etERROR: |
| 850 | buf[0] = '%'; |
| 851 | buf[1] = c; |
| 852 | errorflag = 0; |
| 853 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -105,10 +105,11 @@ | |
| 105 | Use %!j to include double-quotes around it. */ |
| 106 | #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$ |
| 107 | See blob_append_escaped_arg() for details |
| 108 | "%$" -> adds "./" prefix if necessary. |
| 109 | "%!$" -> omits the "./" prefix. */ |
| 110 | #define etHEX 27 /* Encode a string as hexadecimal */ |
| 111 | |
| 112 | |
| 113 | /* |
| 114 | ** An "etByte" is an 8-bit unsigned value. |
| 115 | */ |
| @@ -142,11 +143,11 @@ | |
| 143 | ** NB: When modifying this table is it vital that you also update the fmtchr[] |
| 144 | ** variable to match!!! |
| 145 | */ |
| 146 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 147 | static const char aPrefix[] = "-x0\000X0"; |
| 148 | static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H"; |
| 149 | static const et_info fmtinfo[] = { |
| 150 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 151 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 152 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 153 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -176,10 +177,11 @@ | |
| 177 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 178 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 179 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 180 | { '/', 0, 0, etPATH, 0, 0 }, |
| 181 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 182 | { 'H', 0, 0, etHEX, 0, 0 }, |
| 183 | { etERROR, 0,0,0,0,0} /* Must be last */ |
| 184 | }; |
| 185 | #define etNINFO count(fmtinfo) |
| 186 | |
| 187 | /* |
| @@ -843,10 +845,21 @@ | |
| 845 | case etSHELLESC: { |
| 846 | char *zArg = va_arg(ap, char*); |
| 847 | blob_append_escaped_arg(pBlob, zArg, !flag_altform2); |
| 848 | length = width = 0; |
| 849 | break; |
| 850 | } |
| 851 | case etHEX: { |
| 852 | char *zArg = va_arg(ap, char*); |
| 853 | int szArg = (int)strlen(zArg); |
| 854 | int szBlob = blob_size(pBlob); |
| 855 | u8 *aBuf; |
| 856 | blob_resize(pBlob, szBlob+szArg*2+1); |
| 857 | aBuf = (u8*)&blob_buffer(pBlob)[szBlob]; |
| 858 | encode16((const u8*)zArg, aBuf, szArg); |
| 859 | length = width = 0; |
| 860 | break; |
| 861 | } |
| 862 | case etERROR: |
| 863 | buf[0] = '%'; |
| 864 | buf[1] = c; |
| 865 | errorflag = 0; |
| 866 |
+1
-1
| --- src/sitemap.c | ||
| +++ src/sitemap.c | ||
| @@ -116,11 +116,11 @@ | ||
| 116 | 116 | if( inSublist ){ |
| 117 | 117 | @ </ul> |
| 118 | 118 | inSublist = 0; |
| 119 | 119 | } |
| 120 | 120 | @ </li> |
| 121 | - if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){ | |
| 121 | + if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){ | |
| 122 | 122 | @ <li>%z(href("%R/ckout"))Checkout Status</a></li> |
| 123 | 123 | } |
| 124 | 124 | if( g.perm.Read ){ |
| 125 | 125 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 126 | 126 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 127 | 127 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -116,11 +116,11 @@ | |
| 116 | if( inSublist ){ |
| 117 | @ </ul> |
| 118 | inSublist = 0; |
| 119 | } |
| 120 | @ </li> |
| 121 | if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){ |
| 122 | @ <li>%z(href("%R/ckout"))Checkout Status</a></li> |
| 123 | } |
| 124 | if( g.perm.Read ){ |
| 125 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 126 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 127 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -116,11 +116,11 @@ | |
| 116 | if( inSublist ){ |
| 117 | @ </ul> |
| 118 | inSublist = 0; |
| 119 | } |
| 120 | @ </li> |
| 121 | if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){ |
| 122 | @ <li>%z(href("%R/ckout"))Checkout Status</a></li> |
| 123 | } |
| 124 | if( g.perm.Read ){ |
| 125 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 126 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 127 |
+2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -821,10 +821,11 @@ | ||
| 821 | 821 | if( g.perm.Debug && P("showqp") ){ |
| 822 | 822 | @ <div class="debug"> |
| 823 | 823 | cgi_print_all(0, 0, 0); |
| 824 | 824 | @ </div> |
| 825 | 825 | } |
| 826 | + fossil_free(zTitle); | |
| 826 | 827 | } |
| 827 | 828 | |
| 828 | 829 | #if INTERFACE |
| 829 | 830 | /* Allowed parameters for style_adunit() */ |
| 830 | 831 | #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ |
| @@ -1300,10 +1301,11 @@ | ||
| 1300 | 1301 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 1301 | 1302 | Th_Store("home", g.zTop); |
| 1302 | 1303 | image_url_var("logo"); |
| 1303 | 1304 | image_url_var("background"); |
| 1304 | 1305 | Th_Render(blob_str(&css)); |
| 1306 | + blob_reset(&css); | |
| 1305 | 1307 | |
| 1306 | 1308 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1307 | 1309 | g.isConst = 1; |
| 1308 | 1310 | } |
| 1309 | 1311 | |
| 1310 | 1312 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -821,10 +821,11 @@ | |
| 821 | if( g.perm.Debug && P("showqp") ){ |
| 822 | @ <div class="debug"> |
| 823 | cgi_print_all(0, 0, 0); |
| 824 | @ </div> |
| 825 | } |
| 826 | } |
| 827 | |
| 828 | #if INTERFACE |
| 829 | /* Allowed parameters for style_adunit() */ |
| 830 | #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ |
| @@ -1300,10 +1301,11 @@ | |
| 1300 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 1301 | Th_Store("home", g.zTop); |
| 1302 | image_url_var("logo"); |
| 1303 | image_url_var("background"); |
| 1304 | Th_Render(blob_str(&css)); |
| 1305 | |
| 1306 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1307 | g.isConst = 1; |
| 1308 | } |
| 1309 | |
| 1310 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -821,10 +821,11 @@ | |
| 821 | if( g.perm.Debug && P("showqp") ){ |
| 822 | @ <div class="debug"> |
| 823 | cgi_print_all(0, 0, 0); |
| 824 | @ </div> |
| 825 | } |
| 826 | fossil_free(zTitle); |
| 827 | } |
| 828 | |
| 829 | #if INTERFACE |
| 830 | /* Allowed parameters for style_adunit() */ |
| 831 | #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ |
| @@ -1300,10 +1301,11 @@ | |
| 1301 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 1302 | Th_Store("home", g.zTop); |
| 1303 | image_url_var("logo"); |
| 1304 | image_url_var("background"); |
| 1305 | Th_Render(blob_str(&css)); |
| 1306 | blob_reset(&css); |
| 1307 | |
| 1308 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1309 | g.isConst = 1; |
| 1310 | } |
| 1311 | |
| 1312 |
+31
-4
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1996,11 +1996,11 @@ | ||
| 1996 | 1996 | zMatchStyle = "brlist"; |
| 1997 | 1997 | } |
| 1998 | 1998 | } |
| 1999 | 1999 | |
| 2000 | 2000 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2001 | - if( zBrName && !related ){ | |
| 2001 | + if( zBrName ){ | |
| 2002 | 2002 | cgi_delete_query_parameter("r"); |
| 2003 | 2003 | cgi_set_query_parameter("t", zBrName); (void)P("t"); |
| 2004 | 2004 | cgi_set_query_parameter("rel", "1"); |
| 2005 | 2005 | zTagName = zBrName; |
| 2006 | 2006 | related = 1; |
| @@ -2207,10 +2207,12 @@ | ||
| 2207 | 2207 | PathNode *p = 0; |
| 2208 | 2208 | const char *zFrom = 0; |
| 2209 | 2209 | const char *zTo = 0; |
| 2210 | 2210 | Blob ins; |
| 2211 | 2211 | int nNodeOnPath = 0; |
| 2212 | + int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */ | |
| 2213 | + int earlierRid = 0, laterRid = 0; | |
| 2212 | 2214 | |
| 2213 | 2215 | if( from_rid && to_rid ){ |
| 2214 | 2216 | if( from_to_mode==0 ){ |
| 2215 | 2217 | p = path_shortest(from_rid, to_rid, noMerge, 0, 0); |
| 2216 | 2218 | }else if( from_to_mode==1 ){ |
| @@ -2219,15 +2221,25 @@ | ||
| 2219 | 2221 | p = path_shortest(to_rid, from_rid, 0, 1, 0); |
| 2220 | 2222 | } |
| 2221 | 2223 | zFrom = P("from"); |
| 2222 | 2224 | zTo = zTo2 ? zTo2 : P("to"); |
| 2223 | 2225 | }else{ |
| 2224 | - if( path_common_ancestor(me_rid, you_rid) ){ | |
| 2226 | + commonAncs = path_common_ancestor(me_rid, you_rid); | |
| 2227 | + if( commonAncs!=0 ){ | |
| 2225 | 2228 | p = path_first(); |
| 2226 | 2229 | } |
| 2227 | - zFrom = P("me"); | |
| 2228 | - zTo = P("you"); | |
| 2230 | + if( commonAncs==you_rid ){ | |
| 2231 | + zFrom = P("you"); | |
| 2232 | + zTo = P("me"); | |
| 2233 | + earlierRid = you_rid; | |
| 2234 | + laterRid = me_rid; | |
| 2235 | + }else{ | |
| 2236 | + zFrom = P("me"); | |
| 2237 | + zTo = P("you"); | |
| 2238 | + earlierRid = me_rid; | |
| 2239 | + laterRid = you_rid; | |
| 2240 | + } | |
| 2229 | 2241 | } |
| 2230 | 2242 | blob_init(&ins, 0, 0); |
| 2231 | 2243 | db_multi_exec( |
| 2232 | 2244 | "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);" |
| 2233 | 2245 | ); |
| @@ -2266,10 +2278,23 @@ | ||
| 2266 | 2278 | " SELECT childid FROM cherrypick WHERE parentid IN pathnode;" |
| 2267 | 2279 | ); |
| 2268 | 2280 | } |
| 2269 | 2281 | } |
| 2270 | 2282 | db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related"); |
| 2283 | + if( earlierRid && laterRid && commonAncs==earlierRid ){ | |
| 2284 | + /* On a query with me=XXX, you=YYY, and rel, omit all nodes that | |
| 2285 | + ** are not ancestors of either XXX or YYY, as those nodes tend to | |
| 2286 | + ** be extraneous */ | |
| 2287 | + db_multi_exec( | |
| 2288 | + "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" | |
| 2289 | + ); | |
| 2290 | + compute_ancestors(laterRid, 0, 0, earlierRid); | |
| 2291 | + db_multi_exec( | |
| 2292 | + "DELETE FROM related WHERE x NOT IN ok;" | |
| 2293 | + "DELETE FROM pathnode WHERE x NOT IN ok;" | |
| 2294 | + ); | |
| 2295 | + } | |
| 2271 | 2296 | } |
| 2272 | 2297 | blob_append_sql(&sql, " AND event.objid IN pathnode"); |
| 2273 | 2298 | if( zChng && zChng[0] ){ |
| 2274 | 2299 | db_multi_exec( |
| 2275 | 2300 | "DELETE FROM pathnode " |
| @@ -3077,10 +3102,12 @@ | ||
| 3077 | 3102 | if( zOlderButton ){ |
| 3078 | 3103 | @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ |
| 3079 | 3104 | @ ↓</a> |
| 3080 | 3105 | } |
| 3081 | 3106 | document_emit_js(/*handles pikchrs rendered above*/); |
| 3107 | + blob_reset(&sql); | |
| 3108 | + blob_reset(&desc); | |
| 3082 | 3109 | style_finish_page(); |
| 3083 | 3110 | } |
| 3084 | 3111 | |
| 3085 | 3112 | /* |
| 3086 | 3113 | ** Translate a timeline entry into the printable format by |
| 3087 | 3114 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1996,11 +1996,11 @@ | |
| 1996 | zMatchStyle = "brlist"; |
| 1997 | } |
| 1998 | } |
| 1999 | |
| 2000 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2001 | if( zBrName && !related ){ |
| 2002 | cgi_delete_query_parameter("r"); |
| 2003 | cgi_set_query_parameter("t", zBrName); (void)P("t"); |
| 2004 | cgi_set_query_parameter("rel", "1"); |
| 2005 | zTagName = zBrName; |
| 2006 | related = 1; |
| @@ -2207,10 +2207,12 @@ | |
| 2207 | PathNode *p = 0; |
| 2208 | const char *zFrom = 0; |
| 2209 | const char *zTo = 0; |
| 2210 | Blob ins; |
| 2211 | int nNodeOnPath = 0; |
| 2212 | |
| 2213 | if( from_rid && to_rid ){ |
| 2214 | if( from_to_mode==0 ){ |
| 2215 | p = path_shortest(from_rid, to_rid, noMerge, 0, 0); |
| 2216 | }else if( from_to_mode==1 ){ |
| @@ -2219,15 +2221,25 @@ | |
| 2219 | p = path_shortest(to_rid, from_rid, 0, 1, 0); |
| 2220 | } |
| 2221 | zFrom = P("from"); |
| 2222 | zTo = zTo2 ? zTo2 : P("to"); |
| 2223 | }else{ |
| 2224 | if( path_common_ancestor(me_rid, you_rid) ){ |
| 2225 | p = path_first(); |
| 2226 | } |
| 2227 | zFrom = P("me"); |
| 2228 | zTo = P("you"); |
| 2229 | } |
| 2230 | blob_init(&ins, 0, 0); |
| 2231 | db_multi_exec( |
| 2232 | "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);" |
| 2233 | ); |
| @@ -2266,10 +2278,23 @@ | |
| 2266 | " SELECT childid FROM cherrypick WHERE parentid IN pathnode;" |
| 2267 | ); |
| 2268 | } |
| 2269 | } |
| 2270 | db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related"); |
| 2271 | } |
| 2272 | blob_append_sql(&sql, " AND event.objid IN pathnode"); |
| 2273 | if( zChng && zChng[0] ){ |
| 2274 | db_multi_exec( |
| 2275 | "DELETE FROM pathnode " |
| @@ -3077,10 +3102,12 @@ | |
| 3077 | if( zOlderButton ){ |
| 3078 | @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ |
| 3079 | @ ↓</a> |
| 3080 | } |
| 3081 | document_emit_js(/*handles pikchrs rendered above*/); |
| 3082 | style_finish_page(); |
| 3083 | } |
| 3084 | |
| 3085 | /* |
| 3086 | ** Translate a timeline entry into the printable format by |
| 3087 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1996,11 +1996,11 @@ | |
| 1996 | zMatchStyle = "brlist"; |
| 1997 | } |
| 1998 | } |
| 1999 | |
| 2000 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2001 | if( zBrName ){ |
| 2002 | cgi_delete_query_parameter("r"); |
| 2003 | cgi_set_query_parameter("t", zBrName); (void)P("t"); |
| 2004 | cgi_set_query_parameter("rel", "1"); |
| 2005 | zTagName = zBrName; |
| 2006 | related = 1; |
| @@ -2207,10 +2207,12 @@ | |
| 2207 | PathNode *p = 0; |
| 2208 | const char *zFrom = 0; |
| 2209 | const char *zTo = 0; |
| 2210 | Blob ins; |
| 2211 | int nNodeOnPath = 0; |
| 2212 | int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */ |
| 2213 | int earlierRid = 0, laterRid = 0; |
| 2214 | |
| 2215 | if( from_rid && to_rid ){ |
| 2216 | if( from_to_mode==0 ){ |
| 2217 | p = path_shortest(from_rid, to_rid, noMerge, 0, 0); |
| 2218 | }else if( from_to_mode==1 ){ |
| @@ -2219,15 +2221,25 @@ | |
| 2221 | p = path_shortest(to_rid, from_rid, 0, 1, 0); |
| 2222 | } |
| 2223 | zFrom = P("from"); |
| 2224 | zTo = zTo2 ? zTo2 : P("to"); |
| 2225 | }else{ |
| 2226 | commonAncs = path_common_ancestor(me_rid, you_rid); |
| 2227 | if( commonAncs!=0 ){ |
| 2228 | p = path_first(); |
| 2229 | } |
| 2230 | if( commonAncs==you_rid ){ |
| 2231 | zFrom = P("you"); |
| 2232 | zTo = P("me"); |
| 2233 | earlierRid = you_rid; |
| 2234 | laterRid = me_rid; |
| 2235 | }else{ |
| 2236 | zFrom = P("me"); |
| 2237 | zTo = P("you"); |
| 2238 | earlierRid = me_rid; |
| 2239 | laterRid = you_rid; |
| 2240 | } |
| 2241 | } |
| 2242 | blob_init(&ins, 0, 0); |
| 2243 | db_multi_exec( |
| 2244 | "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);" |
| 2245 | ); |
| @@ -2266,10 +2278,23 @@ | |
| 2278 | " SELECT childid FROM cherrypick WHERE parentid IN pathnode;" |
| 2279 | ); |
| 2280 | } |
| 2281 | } |
| 2282 | db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related"); |
| 2283 | if( earlierRid && laterRid && commonAncs==earlierRid ){ |
| 2284 | /* On a query with me=XXX, you=YYY, and rel, omit all nodes that |
| 2285 | ** are not ancestors of either XXX or YYY, as those nodes tend to |
| 2286 | ** be extraneous */ |
| 2287 | db_multi_exec( |
| 2288 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" |
| 2289 | ); |
| 2290 | compute_ancestors(laterRid, 0, 0, earlierRid); |
| 2291 | db_multi_exec( |
| 2292 | "DELETE FROM related WHERE x NOT IN ok;" |
| 2293 | "DELETE FROM pathnode WHERE x NOT IN ok;" |
| 2294 | ); |
| 2295 | } |
| 2296 | } |
| 2297 | blob_append_sql(&sql, " AND event.objid IN pathnode"); |
| 2298 | if( zChng && zChng[0] ){ |
| 2299 | db_multi_exec( |
| 2300 | "DELETE FROM pathnode " |
| @@ -3077,10 +3102,12 @@ | |
| 3102 | if( zOlderButton ){ |
| 3103 | @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ |
| 3104 | @ ↓</a> |
| 3105 | } |
| 3106 | document_emit_js(/*handles pikchrs rendered above*/); |
| 3107 | blob_reset(&sql); |
| 3108 | blob_reset(&desc); |
| 3109 | style_finish_page(); |
| 3110 | } |
| 3111 | |
| 3112 | /* |
| 3113 | ** Translate a timeline entry into the printable format by |
| 3114 |
+11
-4
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -854,10 +854,11 @@ | ||
| 854 | 854 | ** |
| 855 | 855 | ** If a file is reverted accidentally, it can be restored using |
| 856 | 856 | ** the "fossil undo" command. |
| 857 | 857 | ** |
| 858 | 858 | ** Options: |
| 859 | +** --noundo Do not record changes in the undo/redo log. | |
| 859 | 860 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 860 | 861 | ** VERSION |
| 861 | 862 | ** |
| 862 | 863 | ** See also: [[redo]], [[undo]], [[checkout]], [[update]] |
| 863 | 864 | */ |
| @@ -867,17 +868,19 @@ | ||
| 867 | 868 | ManifestFile *pCoFile; /* File within current check-out manifest */ |
| 868 | 869 | ManifestFile *pRvFile; /* File within revert version manifest */ |
| 869 | 870 | const char *zFile; /* Filename relative to check-out root */ |
| 870 | 871 | const char *zRevision; /* Selected revert version, NULL if current */ |
| 871 | 872 | Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */ |
| 873 | + int useUndo = 1; /* True to record changes in UNDO */ | |
| 872 | 874 | int i; |
| 873 | 875 | Stmt q; |
| 874 | 876 | int revertAll = 0; |
| 875 | 877 | int revisionOptNotSupported = 0; |
| 876 | 878 | |
| 877 | 879 | undo_capture_command_line(); |
| 878 | 880 | zRevision = find_option("revision", "r", 1); |
| 881 | + useUndo = find_option("noundo", 0, 0)==0; | |
| 879 | 882 | verify_all_options(); |
| 880 | 883 | |
| 881 | 884 | if( g.argc<2 ){ |
| 882 | 885 | usage("?OPTIONS? [FILE] ..."); |
| 883 | 886 | } |
| @@ -890,11 +893,15 @@ | ||
| 890 | 893 | /* Get manifests of revert version and (if different) current check-out. */ |
| 891 | 894 | pRvManifest = historical_manifest(zRevision); |
| 892 | 895 | pCoManifest = zRevision ? historical_manifest(0) : 0; |
| 893 | 896 | |
| 894 | 897 | db_begin_transaction(); |
| 895 | - undo_begin(); | |
| 898 | + if( useUndo ){ | |
| 899 | + undo_begin(); | |
| 900 | + }else{ | |
| 901 | + undo_reset(); | |
| 902 | + } | |
| 896 | 903 | db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); |
| 897 | 904 | |
| 898 | 905 | if( g.argc>2 ){ |
| 899 | 906 | for(i=2; i<g.argc; i++){ |
| 900 | 907 | Blob fname; |
| @@ -987,11 +994,11 @@ | ||
| 987 | 994 | if( !pRvFile ){ |
| 988 | 995 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", |
| 989 | 996 | zFile, zFile)==0 ){ |
| 990 | 997 | fossil_print("UNMANAGE %s\n", zFile); |
| 991 | 998 | }else{ |
| 992 | - undo_save(zFile); | |
| 999 | + if( useUndo ) undo_save(zFile); | |
| 993 | 1000 | file_delete(zFull); |
| 994 | 1001 | fossil_print("DELETE %s\n", zFile); |
| 995 | 1002 | } |
| 996 | 1003 | db_multi_exec( |
| 997 | 1004 | "UPDATE OR REPLACE vfile" |
| @@ -1014,11 +1021,11 @@ | ||
| 1014 | 1021 | } |
| 1015 | 1022 | |
| 1016 | 1023 | /* Get contents of reverted-to file. */ |
| 1017 | 1024 | content_get(fast_uuid_to_rid(pRvFile->zUuid), &record); |
| 1018 | 1025 | |
| 1019 | - undo_save(zFile); | |
| 1026 | + if( useUndo ) undo_save(zFile); | |
| 1020 | 1027 | if( file_size(zFull, RepoFILE)>=0 |
| 1021 | 1028 | && (rvPerm==PERM_LNK || file_islink(0)) |
| 1022 | 1029 | ){ |
| 1023 | 1030 | file_delete(zFull); |
| 1024 | 1031 | } |
| @@ -1040,12 +1047,12 @@ | ||
| 1040 | 1047 | } |
| 1041 | 1048 | blob_reset(&record); |
| 1042 | 1049 | free(zFull); |
| 1043 | 1050 | } |
| 1044 | 1051 | db_finalize(&q); |
| 1045 | - undo_finish(); | |
| 1052 | + if( useUndo) undo_finish(); | |
| 1046 | 1053 | db_end_transaction(0); |
| 1047 | 1054 | |
| 1048 | 1055 | /* Deallocate parsed manifest structures. */ |
| 1049 | 1056 | manifest_destroy(pRvManifest); |
| 1050 | 1057 | manifest_destroy(pCoManifest); |
| 1051 | 1058 | } |
| 1052 | 1059 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -854,10 +854,11 @@ | |
| 854 | ** |
| 855 | ** If a file is reverted accidentally, it can be restored using |
| 856 | ** the "fossil undo" command. |
| 857 | ** |
| 858 | ** Options: |
| 859 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 860 | ** VERSION |
| 861 | ** |
| 862 | ** See also: [[redo]], [[undo]], [[checkout]], [[update]] |
| 863 | */ |
| @@ -867,17 +868,19 @@ | |
| 867 | ManifestFile *pCoFile; /* File within current check-out manifest */ |
| 868 | ManifestFile *pRvFile; /* File within revert version manifest */ |
| 869 | const char *zFile; /* Filename relative to check-out root */ |
| 870 | const char *zRevision; /* Selected revert version, NULL if current */ |
| 871 | Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */ |
| 872 | int i; |
| 873 | Stmt q; |
| 874 | int revertAll = 0; |
| 875 | int revisionOptNotSupported = 0; |
| 876 | |
| 877 | undo_capture_command_line(); |
| 878 | zRevision = find_option("revision", "r", 1); |
| 879 | verify_all_options(); |
| 880 | |
| 881 | if( g.argc<2 ){ |
| 882 | usage("?OPTIONS? [FILE] ..."); |
| 883 | } |
| @@ -890,11 +893,15 @@ | |
| 890 | /* Get manifests of revert version and (if different) current check-out. */ |
| 891 | pRvManifest = historical_manifest(zRevision); |
| 892 | pCoManifest = zRevision ? historical_manifest(0) : 0; |
| 893 | |
| 894 | db_begin_transaction(); |
| 895 | undo_begin(); |
| 896 | db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); |
| 897 | |
| 898 | if( g.argc>2 ){ |
| 899 | for(i=2; i<g.argc; i++){ |
| 900 | Blob fname; |
| @@ -987,11 +994,11 @@ | |
| 987 | if( !pRvFile ){ |
| 988 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", |
| 989 | zFile, zFile)==0 ){ |
| 990 | fossil_print("UNMANAGE %s\n", zFile); |
| 991 | }else{ |
| 992 | undo_save(zFile); |
| 993 | file_delete(zFull); |
| 994 | fossil_print("DELETE %s\n", zFile); |
| 995 | } |
| 996 | db_multi_exec( |
| 997 | "UPDATE OR REPLACE vfile" |
| @@ -1014,11 +1021,11 @@ | |
| 1014 | } |
| 1015 | |
| 1016 | /* Get contents of reverted-to file. */ |
| 1017 | content_get(fast_uuid_to_rid(pRvFile->zUuid), &record); |
| 1018 | |
| 1019 | undo_save(zFile); |
| 1020 | if( file_size(zFull, RepoFILE)>=0 |
| 1021 | && (rvPerm==PERM_LNK || file_islink(0)) |
| 1022 | ){ |
| 1023 | file_delete(zFull); |
| 1024 | } |
| @@ -1040,12 +1047,12 @@ | |
| 1040 | } |
| 1041 | blob_reset(&record); |
| 1042 | free(zFull); |
| 1043 | } |
| 1044 | db_finalize(&q); |
| 1045 | undo_finish(); |
| 1046 | db_end_transaction(0); |
| 1047 | |
| 1048 | /* Deallocate parsed manifest structures. */ |
| 1049 | manifest_destroy(pRvManifest); |
| 1050 | manifest_destroy(pCoManifest); |
| 1051 | } |
| 1052 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -854,10 +854,11 @@ | |
| 854 | ** |
| 855 | ** If a file is reverted accidentally, it can be restored using |
| 856 | ** the "fossil undo" command. |
| 857 | ** |
| 858 | ** Options: |
| 859 | ** --noundo Do not record changes in the undo/redo log. |
| 860 | ** -r|--revision VERSION Revert given FILE(s) back to given |
| 861 | ** VERSION |
| 862 | ** |
| 863 | ** See also: [[redo]], [[undo]], [[checkout]], [[update]] |
| 864 | */ |
| @@ -867,17 +868,19 @@ | |
| 868 | ManifestFile *pCoFile; /* File within current check-out manifest */ |
| 869 | ManifestFile *pRvFile; /* File within revert version manifest */ |
| 870 | const char *zFile; /* Filename relative to check-out root */ |
| 871 | const char *zRevision; /* Selected revert version, NULL if current */ |
| 872 | Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */ |
| 873 | int useUndo = 1; /* True to record changes in UNDO */ |
| 874 | int i; |
| 875 | Stmt q; |
| 876 | int revertAll = 0; |
| 877 | int revisionOptNotSupported = 0; |
| 878 | |
| 879 | undo_capture_command_line(); |
| 880 | zRevision = find_option("revision", "r", 1); |
| 881 | useUndo = find_option("noundo", 0, 0)==0; |
| 882 | verify_all_options(); |
| 883 | |
| 884 | if( g.argc<2 ){ |
| 885 | usage("?OPTIONS? [FILE] ..."); |
| 886 | } |
| @@ -890,11 +893,15 @@ | |
| 893 | /* Get manifests of revert version and (if different) current check-out. */ |
| 894 | pRvManifest = historical_manifest(zRevision); |
| 895 | pCoManifest = zRevision ? historical_manifest(0) : 0; |
| 896 | |
| 897 | db_begin_transaction(); |
| 898 | if( useUndo ){ |
| 899 | undo_begin(); |
| 900 | }else{ |
| 901 | undo_reset(); |
| 902 | } |
| 903 | db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);"); |
| 904 | |
| 905 | if( g.argc>2 ){ |
| 906 | for(i=2; i<g.argc; i++){ |
| 907 | Blob fname; |
| @@ -987,11 +994,11 @@ | |
| 994 | if( !pRvFile ){ |
| 995 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q", |
| 996 | zFile, zFile)==0 ){ |
| 997 | fossil_print("UNMANAGE %s\n", zFile); |
| 998 | }else{ |
| 999 | if( useUndo ) undo_save(zFile); |
| 1000 | file_delete(zFull); |
| 1001 | fossil_print("DELETE %s\n", zFile); |
| 1002 | } |
| 1003 | db_multi_exec( |
| 1004 | "UPDATE OR REPLACE vfile" |
| @@ -1014,11 +1021,11 @@ | |
| 1021 | } |
| 1022 | |
| 1023 | /* Get contents of reverted-to file. */ |
| 1024 | content_get(fast_uuid_to_rid(pRvFile->zUuid), &record); |
| 1025 | |
| 1026 | if( useUndo ) undo_save(zFile); |
| 1027 | if( file_size(zFull, RepoFILE)>=0 |
| 1028 | && (rvPerm==PERM_LNK || file_islink(0)) |
| 1029 | ){ |
| 1030 | file_delete(zFull); |
| 1031 | } |
| @@ -1040,12 +1047,12 @@ | |
| 1047 | } |
| 1048 | blob_reset(&record); |
| 1049 | free(zFull); |
| 1050 | } |
| 1051 | db_finalize(&q); |
| 1052 | if( useUndo) undo_finish(); |
| 1053 | db_end_transaction(0); |
| 1054 | |
| 1055 | /* Deallocate parsed manifest structures. */ |
| 1056 | manifest_destroy(pRvManifest); |
| 1057 | manifest_destroy(pCoManifest); |
| 1058 | } |
| 1059 |
+55
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -1041,10 +1041,43 @@ | ||
| 1041 | 1041 | } |
| 1042 | 1042 | db_finalize(&q); |
| 1043 | 1043 | if( cnt==0 ) pXfer->resync = 0; |
| 1044 | 1044 | return cnt; |
| 1045 | 1045 | } |
| 1046 | + | |
| 1047 | +/* | |
| 1048 | +** Send an igot message for every cluster artifact that is not a phantom, | |
| 1049 | +** is not shunned, is not private, and that is not in the UNCLUSTERED table. | |
| 1050 | +** Return the number of cards sent. | |
| 1051 | +*/ | |
| 1052 | +static int send_all_clusters(Xfer *pXfer){ | |
| 1053 | + Stmt q; | |
| 1054 | + int cnt = 0; | |
| 1055 | + const char *zExtra; | |
| 1056 | + if( db_table_exists("temp","onremote") ){ | |
| 1057 | + zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)"; | |
| 1058 | + }else{ | |
| 1059 | + zExtra = ""; | |
| 1060 | + } | |
| 1061 | + db_prepare(&q, | |
| 1062 | + "SELECT uuid" | |
| 1063 | + " FROM tagxref JOIN blob ON tagxref.rid=blob.rid AND tagxref.tagid=%d" | |
| 1064 | + " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" | |
| 1065 | + " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" | |
| 1066 | + " AND NOT EXISTS(SELECT 1 FROM unclustered WHERE rid=blob.rid)" | |
| 1067 | + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s", | |
| 1068 | + TAG_CLUSTER, zExtra /*safe-for-%s*/ | |
| 1069 | + ); | |
| 1070 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1071 | + if( cnt==0 ) blob_appendf(pXfer->pOut, "# sending-clusters\n"); | |
| 1072 | + blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); | |
| 1073 | + cnt++; | |
| 1074 | + } | |
| 1075 | + db_finalize(&q); | |
| 1076 | + if( cnt ) blob_appendf(pXfer->pOut, "# end-of-clusters\n"); | |
| 1077 | + return cnt; | |
| 1078 | +} | |
| 1046 | 1079 | |
| 1047 | 1080 | /* |
| 1048 | 1081 | ** Send an igot message for every artifact. |
| 1049 | 1082 | */ |
| 1050 | 1083 | static void send_all(Xfer *pXfer){ |
| @@ -1781,10 +1814,19 @@ | ||
| 1781 | 1814 | ** The client sends this message to the server to ask the server |
| 1782 | 1815 | ** to tell it about alternative repositories in the reply. |
| 1783 | 1816 | */ |
| 1784 | 1817 | if( blob_eq(&xfer.aToken[1], "req-links") ){ |
| 1785 | 1818 | bSendLinks = 1; |
| 1819 | + }else | |
| 1820 | + | |
| 1821 | + /* pragma req-clusters | |
| 1822 | + ** | |
| 1823 | + ** This pragma requests that the server send igot cards for every | |
| 1824 | + ** cluster artifact that it knows about. | |
| 1825 | + */ | |
| 1826 | + if( blob_eq(&xfer.aToken[1], "req-clusters") ){ | |
| 1827 | + send_all_clusters(&xfer); | |
| 1786 | 1828 | } |
| 1787 | 1829 | |
| 1788 | 1830 | }else |
| 1789 | 1831 | |
| 1790 | 1832 | /* Unknown message |
| @@ -1981,11 +2023,11 @@ | ||
| 1981 | 2023 | int nCardSent = 0; /* Number of cards sent */ |
| 1982 | 2024 | int nCardRcvd = 0; /* Number of cards received */ |
| 1983 | 2025 | int nCycle = 0; /* Number of round trips to the server */ |
| 1984 | 2026 | int size; /* Size of a config value or uvfile */ |
| 1985 | 2027 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 1986 | - int nFileRecv; /* Number of files received */ | |
| 2028 | + int nFileRecv = 0; /* Number of files received */ | |
| 1987 | 2029 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 1988 | 2030 | const char *zCookie; /* Server cookie */ |
| 1989 | 2031 | i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */ |
| 1990 | 2032 | i64 nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 1991 | 2033 | int cloneSeqno = 1; /* Sequence number for clones */ |
| @@ -2007,10 +2049,11 @@ | ||
| 2007 | 2049 | int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ |
| 2008 | 2050 | int uvDoPush = 0; /* Generate uvfile messages to send to server */ |
| 2009 | 2051 | int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ |
| 2010 | 2052 | int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ |
| 2011 | 2053 | int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ |
| 2054 | + int nGimmeRcvd = 0; /* Number of gimme cards recevied on the prev cycle */ | |
| 2012 | 2055 | sqlite3_int64 mtime; /* Modification time on a UV file */ |
| 2013 | 2056 | int autopushFailed = 0; /* Autopush following commit failed if true */ |
| 2014 | 2057 | const char *zCkinLock; /* Name of check-in to lock. NULL for none */ |
| 2015 | 2058 | const char *zClientId; /* A unique identifier for this check-out */ |
| 2016 | 2059 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| @@ -2181,15 +2224,21 @@ | ||
| 2181 | 2224 | */ |
| 2182 | 2225 | if( (syncFlags & SYNC_PULL)!=0 |
| 2183 | 2226 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 2184 | 2227 | ){ |
| 2185 | 2228 | request_phantoms(&xfer, mxPhantomReq); |
| 2229 | + if( xfer.nGimmeSent>0 && nCycle==2 && (syncFlags & SYNC_PULL)!=0 ){ | |
| 2230 | + blob_appendf(&send, "pragma req-clusters\n"); | |
| 2231 | + } | |
| 2186 | 2232 | } |
| 2187 | 2233 | if( syncFlags & SYNC_PUSH ){ |
| 2188 | 2234 | send_unsent(&xfer); |
| 2189 | 2235 | nCardSent += send_unclustered(&xfer); |
| 2190 | 2236 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 2237 | + if( nGimmeRcvd>0 && nCycle==2 ){ | |
| 2238 | + send_all_clusters(&xfer); | |
| 2239 | + } | |
| 2191 | 2240 | } |
| 2192 | 2241 | |
| 2193 | 2242 | /* Client sends configuration parameter requests. On a clone, delay sending |
| 2194 | 2243 | ** this until the second cycle since the login card might fail on |
| 2195 | 2244 | ** the first cycle. |
| @@ -2369,10 +2418,11 @@ | ||
| 2369 | 2418 | nCardSent++; |
| 2370 | 2419 | } |
| 2371 | 2420 | go = 0; |
| 2372 | 2421 | nUvGimmeSent = 0; |
| 2373 | 2422 | nUvFileRcvd = 0; |
| 2423 | + nGimmeRcvd = 0; | |
| 2374 | 2424 | nPriorArtifact = nArtifactRcvd; |
| 2375 | 2425 | |
| 2376 | 2426 | /* Process the reply that came back from the server */ |
| 2377 | 2427 | while( blob_line(&recv, &xfer.line) ){ |
| 2378 | 2428 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| @@ -2449,11 +2499,14 @@ | ||
| 2449 | 2499 | && blob_is_hname(&xfer.aToken[1]) |
| 2450 | 2500 | ){ |
| 2451 | 2501 | remote_unk(&xfer.aToken[1]); |
| 2452 | 2502 | if( syncFlags & SYNC_PUSH ){ |
| 2453 | 2503 | int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 2454 | - if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0); | |
| 2504 | + if( rid ){ | |
| 2505 | + send_file(&xfer, rid, &xfer.aToken[1], 0); | |
| 2506 | + nGimmeRcvd++; | |
| 2507 | + } | |
| 2455 | 2508 | } |
| 2456 | 2509 | }else |
| 2457 | 2510 | |
| 2458 | 2511 | /* igot HASH ?PRIVATEFLAG? |
| 2459 | 2512 | ** |
| 2460 | 2513 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1041,10 +1041,43 @@ | |
| 1041 | } |
| 1042 | db_finalize(&q); |
| 1043 | if( cnt==0 ) pXfer->resync = 0; |
| 1044 | return cnt; |
| 1045 | } |
| 1046 | |
| 1047 | /* |
| 1048 | ** Send an igot message for every artifact. |
| 1049 | */ |
| 1050 | static void send_all(Xfer *pXfer){ |
| @@ -1781,10 +1814,19 @@ | |
| 1781 | ** The client sends this message to the server to ask the server |
| 1782 | ** to tell it about alternative repositories in the reply. |
| 1783 | */ |
| 1784 | if( blob_eq(&xfer.aToken[1], "req-links") ){ |
| 1785 | bSendLinks = 1; |
| 1786 | } |
| 1787 | |
| 1788 | }else |
| 1789 | |
| 1790 | /* Unknown message |
| @@ -1981,11 +2023,11 @@ | |
| 1981 | int nCardSent = 0; /* Number of cards sent */ |
| 1982 | int nCardRcvd = 0; /* Number of cards received */ |
| 1983 | int nCycle = 0; /* Number of round trips to the server */ |
| 1984 | int size; /* Size of a config value or uvfile */ |
| 1985 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 1986 | int nFileRecv; /* Number of files received */ |
| 1987 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 1988 | const char *zCookie; /* Server cookie */ |
| 1989 | i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */ |
| 1990 | i64 nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 1991 | int cloneSeqno = 1; /* Sequence number for clones */ |
| @@ -2007,10 +2049,11 @@ | |
| 2007 | int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ |
| 2008 | int uvDoPush = 0; /* Generate uvfile messages to send to server */ |
| 2009 | int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ |
| 2010 | int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ |
| 2011 | int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ |
| 2012 | sqlite3_int64 mtime; /* Modification time on a UV file */ |
| 2013 | int autopushFailed = 0; /* Autopush following commit failed if true */ |
| 2014 | const char *zCkinLock; /* Name of check-in to lock. NULL for none */ |
| 2015 | const char *zClientId; /* A unique identifier for this check-out */ |
| 2016 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| @@ -2181,15 +2224,21 @@ | |
| 2181 | */ |
| 2182 | if( (syncFlags & SYNC_PULL)!=0 |
| 2183 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 2184 | ){ |
| 2185 | request_phantoms(&xfer, mxPhantomReq); |
| 2186 | } |
| 2187 | if( syncFlags & SYNC_PUSH ){ |
| 2188 | send_unsent(&xfer); |
| 2189 | nCardSent += send_unclustered(&xfer); |
| 2190 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 2191 | } |
| 2192 | |
| 2193 | /* Client sends configuration parameter requests. On a clone, delay sending |
| 2194 | ** this until the second cycle since the login card might fail on |
| 2195 | ** the first cycle. |
| @@ -2369,10 +2418,11 @@ | |
| 2369 | nCardSent++; |
| 2370 | } |
| 2371 | go = 0; |
| 2372 | nUvGimmeSent = 0; |
| 2373 | nUvFileRcvd = 0; |
| 2374 | nPriorArtifact = nArtifactRcvd; |
| 2375 | |
| 2376 | /* Process the reply that came back from the server */ |
| 2377 | while( blob_line(&recv, &xfer.line) ){ |
| 2378 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| @@ -2449,11 +2499,14 @@ | |
| 2449 | && blob_is_hname(&xfer.aToken[1]) |
| 2450 | ){ |
| 2451 | remote_unk(&xfer.aToken[1]); |
| 2452 | if( syncFlags & SYNC_PUSH ){ |
| 2453 | int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 2454 | if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 2455 | } |
| 2456 | }else |
| 2457 | |
| 2458 | /* igot HASH ?PRIVATEFLAG? |
| 2459 | ** |
| 2460 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1041,10 +1041,43 @@ | |
| 1041 | } |
| 1042 | db_finalize(&q); |
| 1043 | if( cnt==0 ) pXfer->resync = 0; |
| 1044 | return cnt; |
| 1045 | } |
| 1046 | |
| 1047 | /* |
| 1048 | ** Send an igot message for every cluster artifact that is not a phantom, |
| 1049 | ** is not shunned, is not private, and that is not in the UNCLUSTERED table. |
| 1050 | ** Return the number of cards sent. |
| 1051 | */ |
| 1052 | static int send_all_clusters(Xfer *pXfer){ |
| 1053 | Stmt q; |
| 1054 | int cnt = 0; |
| 1055 | const char *zExtra; |
| 1056 | if( db_table_exists("temp","onremote") ){ |
| 1057 | zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)"; |
| 1058 | }else{ |
| 1059 | zExtra = ""; |
| 1060 | } |
| 1061 | db_prepare(&q, |
| 1062 | "SELECT uuid" |
| 1063 | " FROM tagxref JOIN blob ON tagxref.rid=blob.rid AND tagxref.tagid=%d" |
| 1064 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 1065 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 1066 | " AND NOT EXISTS(SELECT 1 FROM unclustered WHERE rid=blob.rid)" |
| 1067 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s", |
| 1068 | TAG_CLUSTER, zExtra /*safe-for-%s*/ |
| 1069 | ); |
| 1070 | while( db_step(&q)==SQLITE_ROW ){ |
| 1071 | if( cnt==0 ) blob_appendf(pXfer->pOut, "# sending-clusters\n"); |
| 1072 | blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); |
| 1073 | cnt++; |
| 1074 | } |
| 1075 | db_finalize(&q); |
| 1076 | if( cnt ) blob_appendf(pXfer->pOut, "# end-of-clusters\n"); |
| 1077 | return cnt; |
| 1078 | } |
| 1079 | |
| 1080 | /* |
| 1081 | ** Send an igot message for every artifact. |
| 1082 | */ |
| 1083 | static void send_all(Xfer *pXfer){ |
| @@ -1781,10 +1814,19 @@ | |
| 1814 | ** The client sends this message to the server to ask the server |
| 1815 | ** to tell it about alternative repositories in the reply. |
| 1816 | */ |
| 1817 | if( blob_eq(&xfer.aToken[1], "req-links") ){ |
| 1818 | bSendLinks = 1; |
| 1819 | }else |
| 1820 | |
| 1821 | /* pragma req-clusters |
| 1822 | ** |
| 1823 | ** This pragma requests that the server send igot cards for every |
| 1824 | ** cluster artifact that it knows about. |
| 1825 | */ |
| 1826 | if( blob_eq(&xfer.aToken[1], "req-clusters") ){ |
| 1827 | send_all_clusters(&xfer); |
| 1828 | } |
| 1829 | |
| 1830 | }else |
| 1831 | |
| 1832 | /* Unknown message |
| @@ -1981,11 +2023,11 @@ | |
| 2023 | int nCardSent = 0; /* Number of cards sent */ |
| 2024 | int nCardRcvd = 0; /* Number of cards received */ |
| 2025 | int nCycle = 0; /* Number of round trips to the server */ |
| 2026 | int size; /* Size of a config value or uvfile */ |
| 2027 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 2028 | int nFileRecv = 0; /* Number of files received */ |
| 2029 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 2030 | const char *zCookie; /* Server cookie */ |
| 2031 | i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */ |
| 2032 | i64 nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 2033 | int cloneSeqno = 1; /* Sequence number for clones */ |
| @@ -2007,10 +2049,11 @@ | |
| 2049 | int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ |
| 2050 | int uvDoPush = 0; /* Generate uvfile messages to send to server */ |
| 2051 | int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ |
| 2052 | int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ |
| 2053 | int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ |
| 2054 | int nGimmeRcvd = 0; /* Number of gimme cards recevied on the prev cycle */ |
| 2055 | sqlite3_int64 mtime; /* Modification time on a UV file */ |
| 2056 | int autopushFailed = 0; /* Autopush following commit failed if true */ |
| 2057 | const char *zCkinLock; /* Name of check-in to lock. NULL for none */ |
| 2058 | const char *zClientId; /* A unique identifier for this check-out */ |
| 2059 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| @@ -2181,15 +2224,21 @@ | |
| 2224 | */ |
| 2225 | if( (syncFlags & SYNC_PULL)!=0 |
| 2226 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 2227 | ){ |
| 2228 | request_phantoms(&xfer, mxPhantomReq); |
| 2229 | if( xfer.nGimmeSent>0 && nCycle==2 && (syncFlags & SYNC_PULL)!=0 ){ |
| 2230 | blob_appendf(&send, "pragma req-clusters\n"); |
| 2231 | } |
| 2232 | } |
| 2233 | if( syncFlags & SYNC_PUSH ){ |
| 2234 | send_unsent(&xfer); |
| 2235 | nCardSent += send_unclustered(&xfer); |
| 2236 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 2237 | if( nGimmeRcvd>0 && nCycle==2 ){ |
| 2238 | send_all_clusters(&xfer); |
| 2239 | } |
| 2240 | } |
| 2241 | |
| 2242 | /* Client sends configuration parameter requests. On a clone, delay sending |
| 2243 | ** this until the second cycle since the login card might fail on |
| 2244 | ** the first cycle. |
| @@ -2369,10 +2418,11 @@ | |
| 2418 | nCardSent++; |
| 2419 | } |
| 2420 | go = 0; |
| 2421 | nUvGimmeSent = 0; |
| 2422 | nUvFileRcvd = 0; |
| 2423 | nGimmeRcvd = 0; |
| 2424 | nPriorArtifact = nArtifactRcvd; |
| 2425 | |
| 2426 | /* Process the reply that came back from the server */ |
| 2427 | while( blob_line(&recv, &xfer.line) ){ |
| 2428 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| @@ -2449,11 +2499,14 @@ | |
| 2499 | && blob_is_hname(&xfer.aToken[1]) |
| 2500 | ){ |
| 2501 | remote_unk(&xfer.aToken[1]); |
| 2502 | if( syncFlags & SYNC_PUSH ){ |
| 2503 | int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 2504 | if( rid ){ |
| 2505 | send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 2506 | nGimmeRcvd++; |
| 2507 | } |
| 2508 | } |
| 2509 | }else |
| 2510 | |
| 2511 | /* igot HASH ?PRIVATEFLAG? |
| 2512 | ** |
| 2513 |
+25
| --- test/merge1.test | ||
| +++ test/merge1.test | ||
| @@ -75,10 +75,12 @@ | ||
| 75 | 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | 76 | } |
| 77 | 77 | write_file_indented t23 { |
| 78 | 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 79 | 79 | 111 - This is line ONE of the demo program - 1111 |
| 80 | + ####### SUGGESTED CONFLICT RESOLUTION follows ################### | |
| 81 | + 111 - This is line ONE OF the demo program - 1111 | |
| 80 | 82 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 81 | 83 | 111 - This is line one of the demo program - 1111 |
| 82 | 84 | ======= MERGED IN content follows =============================== (line 1) |
| 83 | 85 | 111 - This is line one OF the demo program - 1111 |
| 84 | 86 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -88,10 +90,12 @@ | ||
| 88 | 90 | 555 - we think it well and other stuff too - 5555 |
| 89 | 91 | } |
| 90 | 92 | write_file_indented t32 { |
| 91 | 93 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 92 | 94 | 111 - This is line one OF the demo program - 1111 |
| 95 | + ####### SUGGESTED CONFLICT RESOLUTION follows ################### | |
| 96 | + 111 - This is line ONE OF the demo program - 1111 | |
| 93 | 97 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 94 | 98 | 111 - This is line one of the demo program - 1111 |
| 95 | 99 | ======= MERGED IN content follows =============================== (line 1) |
| 96 | 100 | 111 - This is line ONE of the demo program - 1111 |
| 97 | 101 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -159,10 +163,13 @@ | ||
| 159 | 163 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 164 | 555 - we think it well and other stuff too - 5555 |
| 161 | 165 | } |
| 162 | 166 | write_file_indented t32 { |
| 163 | 167 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 168 | + ####### SUGGESTED CONFLICT RESOLUTION follows ################### | |
| 169 | + 000 - Zero lines added to the beginning of - 0000 | |
| 170 | + 111 - This is line one of the demo program - 1111 | |
| 164 | 171 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 165 | 172 | 111 - This is line one of the demo program - 1111 |
| 166 | 173 | ======= MERGED IN content follows =============================== (line 1) |
| 167 | 174 | 000 - Zero lines added to the beginning of - 0000 |
| 168 | 175 | 111 - This is line one of the demo program - 1111 |
| @@ -305,10 +312,19 @@ | ||
| 305 | 312 | mnop 2 |
| 306 | 313 | qrst |
| 307 | 314 | uvwx |
| 308 | 315 | yzAB 2 |
| 309 | 316 | CDEF 2 |
| 317 | + GHIJ 2 | |
| 318 | + ####### SUGGESTED CONFLICT RESOLUTION follows ################### | |
| 319 | + efgh 2 | |
| 320 | + ijkl 2 | |
| 321 | + mnop 3 | |
| 322 | + qrst 3 | |
| 323 | + uvwx 3 | |
| 324 | + yzAB 3 | |
| 325 | + CDEF 2 | |
| 310 | 326 | GHIJ 2 |
| 311 | 327 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 312 | 328 | efgh |
| 313 | 329 | ijkl |
| 314 | 330 | mnop |
| @@ -372,10 +388,19 @@ | ||
| 372 | 388 | ijkl 2 |
| 373 | 389 | mnop |
| 374 | 390 | qrst |
| 375 | 391 | uvwx |
| 376 | 392 | yzAB 2 |
| 393 | + CDEF 2 | |
| 394 | + GHIJ 2 | |
| 395 | + ####### SUGGESTED CONFLICT RESOLUTION follows ################### | |
| 396 | + efgh 2 | |
| 397 | + ijkl 2 | |
| 398 | + mnop 3 | |
| 399 | + qrst 3 | |
| 400 | + uvwx 3 | |
| 401 | + yzAB 3 | |
| 377 | 402 | CDEF 2 |
| 378 | 403 | GHIJ 2 |
| 379 | 404 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 380 | 405 | efgh |
| 381 | 406 | ijkl |
| 382 | 407 |
| --- test/merge1.test | |
| +++ test/merge1.test | |
| @@ -75,10 +75,12 @@ | |
| 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | } |
| 77 | write_file_indented t23 { |
| 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 79 | 111 - This is line ONE of the demo program - 1111 |
| 80 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 81 | 111 - This is line one of the demo program - 1111 |
| 82 | ======= MERGED IN content follows =============================== (line 1) |
| 83 | 111 - This is line one OF the demo program - 1111 |
| 84 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -88,10 +90,12 @@ | |
| 88 | 555 - we think it well and other stuff too - 5555 |
| 89 | } |
| 90 | write_file_indented t32 { |
| 91 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 92 | 111 - This is line one OF the demo program - 1111 |
| 93 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 94 | 111 - This is line one of the demo program - 1111 |
| 95 | ======= MERGED IN content follows =============================== (line 1) |
| 96 | 111 - This is line ONE of the demo program - 1111 |
| 97 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -159,10 +163,13 @@ | |
| 159 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 555 - we think it well and other stuff too - 5555 |
| 161 | } |
| 162 | write_file_indented t32 { |
| 163 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 164 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 165 | 111 - This is line one of the demo program - 1111 |
| 166 | ======= MERGED IN content follows =============================== (line 1) |
| 167 | 000 - Zero lines added to the beginning of - 0000 |
| 168 | 111 - This is line one of the demo program - 1111 |
| @@ -305,10 +312,19 @@ | |
| 305 | mnop 2 |
| 306 | qrst |
| 307 | uvwx |
| 308 | yzAB 2 |
| 309 | CDEF 2 |
| 310 | GHIJ 2 |
| 311 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 312 | efgh |
| 313 | ijkl |
| 314 | mnop |
| @@ -372,10 +388,19 @@ | |
| 372 | ijkl 2 |
| 373 | mnop |
| 374 | qrst |
| 375 | uvwx |
| 376 | yzAB 2 |
| 377 | CDEF 2 |
| 378 | GHIJ 2 |
| 379 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 380 | efgh |
| 381 | ijkl |
| 382 |
| --- test/merge1.test | |
| +++ test/merge1.test | |
| @@ -75,10 +75,12 @@ | |
| 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | } |
| 77 | write_file_indented t23 { |
| 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 79 | 111 - This is line ONE of the demo program - 1111 |
| 80 | ####### SUGGESTED CONFLICT RESOLUTION follows ################### |
| 81 | 111 - This is line ONE OF the demo program - 1111 |
| 82 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 83 | 111 - This is line one of the demo program - 1111 |
| 84 | ======= MERGED IN content follows =============================== (line 1) |
| 85 | 111 - This is line one OF the demo program - 1111 |
| 86 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -88,10 +90,12 @@ | |
| 90 | 555 - we think it well and other stuff too - 5555 |
| 91 | } |
| 92 | write_file_indented t32 { |
| 93 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 94 | 111 - This is line one OF the demo program - 1111 |
| 95 | ####### SUGGESTED CONFLICT RESOLUTION follows ################### |
| 96 | 111 - This is line ONE OF the demo program - 1111 |
| 97 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 98 | 111 - This is line one of the demo program - 1111 |
| 99 | ======= MERGED IN content follows =============================== (line 1) |
| 100 | 111 - This is line ONE of the demo program - 1111 |
| 101 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -159,10 +163,13 @@ | |
| 163 | 444 - If all goes well, we will be pleased - 4444 |
| 164 | 555 - we think it well and other stuff too - 5555 |
| 165 | } |
| 166 | write_file_indented t32 { |
| 167 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 168 | ####### SUGGESTED CONFLICT RESOLUTION follows ################### |
| 169 | 000 - Zero lines added to the beginning of - 0000 |
| 170 | 111 - This is line one of the demo program - 1111 |
| 171 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 172 | 111 - This is line one of the demo program - 1111 |
| 173 | ======= MERGED IN content follows =============================== (line 1) |
| 174 | 000 - Zero lines added to the beginning of - 0000 |
| 175 | 111 - This is line one of the demo program - 1111 |
| @@ -305,10 +312,19 @@ | |
| 312 | mnop 2 |
| 313 | qrst |
| 314 | uvwx |
| 315 | yzAB 2 |
| 316 | CDEF 2 |
| 317 | GHIJ 2 |
| 318 | ####### SUGGESTED CONFLICT RESOLUTION follows ################### |
| 319 | efgh 2 |
| 320 | ijkl 2 |
| 321 | mnop 3 |
| 322 | qrst 3 |
| 323 | uvwx 3 |
| 324 | yzAB 3 |
| 325 | CDEF 2 |
| 326 | GHIJ 2 |
| 327 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 328 | efgh |
| 329 | ijkl |
| 330 | mnop |
| @@ -372,10 +388,19 @@ | |
| 388 | ijkl 2 |
| 389 | mnop |
| 390 | qrst |
| 391 | uvwx |
| 392 | yzAB 2 |
| 393 | CDEF 2 |
| 394 | GHIJ 2 |
| 395 | ####### SUGGESTED CONFLICT RESOLUTION follows ################### |
| 396 | efgh 2 |
| 397 | ijkl 2 |
| 398 | mnop 3 |
| 399 | qrst 3 |
| 400 | uvwx 3 |
| 401 | yzAB 3 |
| 402 | CDEF 2 |
| 403 | GHIJ 2 |
| 404 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 405 | efgh |
| 406 | ijkl |
| 407 |
+28
-25
| --- test/merge3.test | ||
| +++ test/merge3.test | ||
| @@ -27,10 +27,13 @@ | ||
| 27 | 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | 28 | set x [read_file t4] |
| 29 | 29 | regsub -all \ |
| 30 | 30 | {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \ |
| 31 | 31 | $x {MINE:} x |
| 32 | + regsub -all \ | |
| 33 | + {####### SUGGESTED CONFLICT RESOLUTION follows #+} \ | |
| 34 | + $x {BOT:} x | |
| 32 | 35 | regsub -all \ |
| 33 | 36 | {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \ |
| 34 | 37 | $x {COM:} x |
| 35 | 38 | regsub -all \ |
| 36 | 39 | {======= MERGED IN content follows =+ \(line \d+\)} \ |
| @@ -73,56 +76,56 @@ | ||
| 73 | 76 | } { |
| 74 | 77 | 1 2 3b 4b 5b 6 7 8 9 |
| 75 | 78 | } { |
| 76 | 79 | 1 2 3 4 5c 6 7 8 9 |
| 77 | 80 | } { |
| 78 | - 1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9 | |
| 81 | + 1 2 MINE: 3b 4b 5b BOT: 3b 4b 5c COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9 | |
| 79 | 82 | } -expectError |
| 80 | 83 | merge-test 4 { |
| 81 | 84 | 1 2 3 4 5 6 7 8 9 |
| 82 | 85 | } { |
| 83 | 86 | 1 2 3b 4b 5b 6b 7 8 9 |
| 84 | 87 | } { |
| 85 | 88 | 1 2 3 4 5c 6 7 8 9 |
| 86 | 89 | } { |
| 87 | - 1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9 | |
| 90 | + 1 2 MINE: 3b 4b 5b 6b BOT: 3b 4b 5b 5c 6 COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9 | |
| 88 | 91 | } -expectError |
| 89 | 92 | merge-test 5 { |
| 90 | 93 | 1 2 3 4 5 6 7 8 9 |
| 91 | 94 | } { |
| 92 | 95 | 1 2 3b 4b 5b 6b 7 8 9 |
| 93 | 96 | } { |
| 94 | 97 | 1 2 3 4 5c 6c 7c 8 9 |
| 95 | 98 | } { |
| 96 | - 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9 | |
| 99 | + 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9 | |
| 97 | 100 | } -expectError |
| 98 | 101 | merge-test 6 { |
| 99 | 102 | 1 2 3 4 5 6 7 8 9 |
| 100 | 103 | } { |
| 101 | 104 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 102 | 105 | } { |
| 103 | 106 | 1 2 3 4 5c 6c 7c 8 9 |
| 104 | 107 | } { |
| 105 | - 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9 | |
| 108 | + 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9 | |
| 106 | 109 | } -expectError |
| 107 | 110 | merge-test 7 { |
| 108 | 111 | 1 2 3 4 5 6 7 8 9 |
| 109 | 112 | } { |
| 110 | 113 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 111 | 114 | } { |
| 112 | 115 | 1 2 3 4 5c 6c 7c 8c 9 |
| 113 | 116 | } { |
| 114 | - 1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9 | |
| 117 | + 1 2 MINE: 3b 4b 5b 6b 7 8b BOT: 3b 4b 5b 5c 6c 7c 8c COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9 | |
| 115 | 118 | } -expectError |
| 116 | 119 | merge-test 8 { |
| 117 | 120 | 1 2 3 4 5 6 7 8 9 |
| 118 | 121 | } { |
| 119 | 122 | 1 2 3b 4b 5b 6b 7 8b 9b |
| 120 | 123 | } { |
| 121 | 124 | 1 2 3 4 5c 6c 7c 8c 9 |
| 122 | 125 | } { |
| 123 | - 1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END | |
| 126 | + 1 2 MINE: 3b 4b 5b 6b 7 8b 9b BOT: 3b 4b 5b 5c 6c 7c 8c 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END | |
| 124 | 127 | } -expectError |
| 125 | 128 | merge-test 9 { |
| 126 | 129 | 1 2 3 4 5 6 7 8 9 |
| 127 | 130 | } { |
| 128 | 131 | 1 2 3b 4b 5 6 7 8b 9b |
| @@ -146,11 +149,11 @@ | ||
| 146 | 149 | } { |
| 147 | 150 | 1 2 3b 4b 5 6 7 8b 9b |
| 148 | 151 | } { |
| 149 | 152 | 1 2 3b 4c 5 6c 7c 8 9 |
| 150 | 153 | } { |
| 151 | - 1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b | |
| 154 | + 1 2 MINE: 3b 4b BOT: 3b 4c COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b | |
| 152 | 155 | } -expectError |
| 153 | 156 | merge-test 12 { |
| 154 | 157 | 1 2 3 4 5 6 7 8 9 |
| 155 | 158 | } { |
| 156 | 159 | 1 2 3b4b 5 6 7 8b 9b |
| @@ -201,20 +204,20 @@ | ||
| 201 | 204 | } { |
| 202 | 205 | 1 6 7 8 9 |
| 203 | 206 | } { |
| 204 | 207 | 1 2 3 4 9 |
| 205 | 208 | } { |
| 206 | - 1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 | |
| 209 | + 1 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 | |
| 207 | 210 | } -expectError |
| 208 | 211 | merge-test 25 { |
| 209 | 212 | 1 2 3 4 5 6 7 8 9 |
| 210 | 213 | } { |
| 211 | 214 | 1 7 8 9 |
| 212 | 215 | } { |
| 213 | 216 | 1 2 3 9 |
| 214 | 217 | } { |
| 215 | - 1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 | |
| 218 | + 1 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 | |
| 216 | 219 | } -expectError |
| 217 | 220 | |
| 218 | 221 | merge-test 30 { |
| 219 | 222 | 1 2 3 4 5 6 7 8 9 |
| 220 | 223 | } { |
| @@ -256,20 +259,20 @@ | ||
| 256 | 259 | } { |
| 257 | 260 | 1 2 3 4 9 |
| 258 | 261 | } { |
| 259 | 262 | 1 6 7 8 9 |
| 260 | 263 | } { |
| 261 | - 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9 | |
| 264 | + 1 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9 | |
| 262 | 265 | } -expectError |
| 263 | 266 | merge-test 35 { |
| 264 | 267 | 1 2 3 4 5 6 7 8 9 |
| 265 | 268 | } { |
| 266 | 269 | 1 2 3 9 |
| 267 | 270 | } { |
| 268 | 271 | 1 7 8 9 |
| 269 | 272 | } { |
| 270 | - 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9 | |
| 273 | + 1 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9 | |
| 271 | 274 | } -expectError |
| 272 | 275 | |
| 273 | 276 | merge-test 40 { |
| 274 | 277 | 2 3 4 5 6 7 8 |
| 275 | 278 | } { |
| @@ -311,20 +314,20 @@ | ||
| 311 | 314 | } { |
| 312 | 315 | 6 7 8 |
| 313 | 316 | } { |
| 314 | 317 | 2 3 4 |
| 315 | 318 | } { |
| 316 | - MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END | |
| 319 | + MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END | |
| 317 | 320 | } -expectError |
| 318 | 321 | merge-test 45 { |
| 319 | 322 | 2 3 4 5 6 7 8 |
| 320 | 323 | } { |
| 321 | 324 | 7 8 |
| 322 | 325 | } { |
| 323 | 326 | 2 3 |
| 324 | 327 | } { |
| 325 | - MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END | |
| 328 | + MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END | |
| 326 | 329 | } -expectError |
| 327 | 330 | |
| 328 | 331 | merge-test 50 { |
| 329 | 332 | 2 3 4 5 6 7 8 |
| 330 | 333 | } { |
| @@ -365,20 +368,20 @@ | ||
| 365 | 368 | } { |
| 366 | 369 | 2 3 4 |
| 367 | 370 | } { |
| 368 | 371 | 6 7 8 |
| 369 | 372 | } { |
| 370 | - MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END | |
| 373 | + MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END | |
| 371 | 374 | } -expectError |
| 372 | 375 | merge-test 55 { |
| 373 | 376 | 2 3 4 5 6 7 8 |
| 374 | 377 | } { |
| 375 | 378 | 2 3 |
| 376 | 379 | } { |
| 377 | 380 | 7 8 |
| 378 | 381 | } { |
| 379 | - MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END | |
| 382 | + MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END | |
| 380 | 383 | } -expectError |
| 381 | 384 | |
| 382 | 385 | merge-test 60 { |
| 383 | 386 | 1 2 3 4 5 6 7 8 9 |
| 384 | 387 | } { |
| @@ -420,20 +423,20 @@ | ||
| 420 | 423 | } { |
| 421 | 424 | 1 2b 3b 4b 5b 6 7 8 9 |
| 422 | 425 | } { |
| 423 | 426 | 1 2 3 4 9 |
| 424 | 427 | } { |
| 425 | - 1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 | |
| 428 | + 1 MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 | |
| 426 | 429 | } -expectError |
| 427 | 430 | merge-test 65 { |
| 428 | 431 | 1 2 3 4 5 6 7 8 9 |
| 429 | 432 | } { |
| 430 | 433 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 431 | 434 | } { |
| 432 | 435 | 1 2 3 9 |
| 433 | 436 | } { |
| 434 | - 1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 | |
| 437 | + 1 MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 | |
| 435 | 438 | } -expectError |
| 436 | 439 | |
| 437 | 440 | merge-test 70 { |
| 438 | 441 | 1 2 3 4 5 6 7 8 9 |
| 439 | 442 | } { |
| @@ -475,20 +478,20 @@ | ||
| 475 | 478 | } { |
| 476 | 479 | 1 2 3 4 9 |
| 477 | 480 | } { |
| 478 | 481 | 1 2b 3b 4b 5b 6 7 8 9 |
| 479 | 482 | } { |
| 480 | - 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9 | |
| 483 | + 1 MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9 | |
| 481 | 484 | } -expectError |
| 482 | 485 | merge-test 75 { |
| 483 | 486 | 1 2 3 4 5 6 7 8 9 |
| 484 | 487 | } { |
| 485 | 488 | 1 2 3 9 |
| 486 | 489 | } { |
| 487 | 490 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 488 | 491 | } { |
| 489 | - 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9 | |
| 492 | + 1 MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9 | |
| 490 | 493 | } -expectError |
| 491 | 494 | |
| 492 | 495 | merge-test 80 { |
| 493 | 496 | 2 3 4 5 6 7 8 |
| 494 | 497 | } { |
| @@ -530,20 +533,20 @@ | ||
| 530 | 533 | } { |
| 531 | 534 | 2b 3b 4b 5b 6 7 8 |
| 532 | 535 | } { |
| 533 | 536 | 2 3 4 |
| 534 | 537 | } { |
| 535 | - MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END | |
| 538 | + MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END | |
| 536 | 539 | } -expectError |
| 537 | 540 | merge-test 85 { |
| 538 | 541 | 2 3 4 5 6 7 8 |
| 539 | 542 | } { |
| 540 | 543 | 2b 3b 4b 5b 6b 7 8 |
| 541 | 544 | } { |
| 542 | 545 | 2 3 |
| 543 | 546 | } { |
| 544 | - MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END | |
| 547 | + MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END | |
| 545 | 548 | } -expectError |
| 546 | 549 | |
| 547 | 550 | merge-test 90 { |
| 548 | 551 | 2 3 4 5 6 7 8 |
| 549 | 552 | } { |
| @@ -585,20 +588,20 @@ | ||
| 585 | 588 | } { |
| 586 | 589 | 2 3 4 |
| 587 | 590 | } { |
| 588 | 591 | 2b 3b 4b 5b 6 7 8 |
| 589 | 592 | } { |
| 590 | - MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END | |
| 593 | + MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END | |
| 591 | 594 | } -expectError |
| 592 | 595 | merge-test 95 { |
| 593 | 596 | 2 3 4 5 6 7 8 |
| 594 | 597 | } { |
| 595 | 598 | 2 3 |
| 596 | 599 | } { |
| 597 | 600 | 2b 3b 4b 5b 6b 7 8 |
| 598 | 601 | } { |
| 599 | - MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END | |
| 602 | + MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END | |
| 600 | 603 | } -expectError |
| 601 | 604 | |
| 602 | 605 | merge-test 100 { |
| 603 | 606 | 1 2 3 4 5 6 7 8 9 |
| 604 | 607 | } { |
| @@ -631,20 +634,20 @@ | ||
| 631 | 634 | } { |
| 632 | 635 | 1 2 3 4 5 7 8 9b |
| 633 | 636 | } { |
| 634 | 637 | 1 2 3 4 5 7 8 9b a b c d e |
| 635 | 638 | } { |
| 636 | - 1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END | |
| 639 | + 1 2 3 4 5 7 8 MINE: 9b BOT: 9b a b c d e COM: 9 YOURS: 9b a b c d e END | |
| 637 | 640 | } -expectError |
| 638 | 641 | merge-test 104 { |
| 639 | 642 | 1 2 3 4 5 6 7 8 9 |
| 640 | 643 | } { |
| 641 | 644 | 1 2 3 4 5 7 8 9b a b c d e |
| 642 | 645 | } { |
| 643 | 646 | 1 2 3 4 5 7 8 9b |
| 644 | 647 | } { |
| 645 | - 1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END | |
| 648 | + 1 2 3 4 5 7 8 MINE: 9b a b c d e BOT: 9b COM: 9 YOURS: 9b END | |
| 646 | 649 | } -expectError |
| 647 | 650 | |
| 648 | 651 | ############################################################################### |
| 649 | 652 | |
| 650 | 653 | test_cleanup |
| 651 | 654 |
| --- test/merge3.test | |
| +++ test/merge3.test | |
| @@ -27,10 +27,13 @@ | |
| 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | set x [read_file t4] |
| 29 | regsub -all \ |
| 30 | {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \ |
| 31 | $x {MINE:} x |
| 32 | regsub -all \ |
| 33 | {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \ |
| 34 | $x {COM:} x |
| 35 | regsub -all \ |
| 36 | {======= MERGED IN content follows =+ \(line \d+\)} \ |
| @@ -73,56 +76,56 @@ | |
| 73 | } { |
| 74 | 1 2 3b 4b 5b 6 7 8 9 |
| 75 | } { |
| 76 | 1 2 3 4 5c 6 7 8 9 |
| 77 | } { |
| 78 | 1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9 |
| 79 | } -expectError |
| 80 | merge-test 4 { |
| 81 | 1 2 3 4 5 6 7 8 9 |
| 82 | } { |
| 83 | 1 2 3b 4b 5b 6b 7 8 9 |
| 84 | } { |
| 85 | 1 2 3 4 5c 6 7 8 9 |
| 86 | } { |
| 87 | 1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9 |
| 88 | } -expectError |
| 89 | merge-test 5 { |
| 90 | 1 2 3 4 5 6 7 8 9 |
| 91 | } { |
| 92 | 1 2 3b 4b 5b 6b 7 8 9 |
| 93 | } { |
| 94 | 1 2 3 4 5c 6c 7c 8 9 |
| 95 | } { |
| 96 | 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9 |
| 97 | } -expectError |
| 98 | merge-test 6 { |
| 99 | 1 2 3 4 5 6 7 8 9 |
| 100 | } { |
| 101 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 102 | } { |
| 103 | 1 2 3 4 5c 6c 7c 8 9 |
| 104 | } { |
| 105 | 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9 |
| 106 | } -expectError |
| 107 | merge-test 7 { |
| 108 | 1 2 3 4 5 6 7 8 9 |
| 109 | } { |
| 110 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 111 | } { |
| 112 | 1 2 3 4 5c 6c 7c 8c 9 |
| 113 | } { |
| 114 | 1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9 |
| 115 | } -expectError |
| 116 | merge-test 8 { |
| 117 | 1 2 3 4 5 6 7 8 9 |
| 118 | } { |
| 119 | 1 2 3b 4b 5b 6b 7 8b 9b |
| 120 | } { |
| 121 | 1 2 3 4 5c 6c 7c 8c 9 |
| 122 | } { |
| 123 | 1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END |
| 124 | } -expectError |
| 125 | merge-test 9 { |
| 126 | 1 2 3 4 5 6 7 8 9 |
| 127 | } { |
| 128 | 1 2 3b 4b 5 6 7 8b 9b |
| @@ -146,11 +149,11 @@ | |
| 146 | } { |
| 147 | 1 2 3b 4b 5 6 7 8b 9b |
| 148 | } { |
| 149 | 1 2 3b 4c 5 6c 7c 8 9 |
| 150 | } { |
| 151 | 1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b |
| 152 | } -expectError |
| 153 | merge-test 12 { |
| 154 | 1 2 3 4 5 6 7 8 9 |
| 155 | } { |
| 156 | 1 2 3b4b 5 6 7 8b 9b |
| @@ -201,20 +204,20 @@ | |
| 201 | } { |
| 202 | 1 6 7 8 9 |
| 203 | } { |
| 204 | 1 2 3 4 9 |
| 205 | } { |
| 206 | 1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 |
| 207 | } -expectError |
| 208 | merge-test 25 { |
| 209 | 1 2 3 4 5 6 7 8 9 |
| 210 | } { |
| 211 | 1 7 8 9 |
| 212 | } { |
| 213 | 1 2 3 9 |
| 214 | } { |
| 215 | 1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 |
| 216 | } -expectError |
| 217 | |
| 218 | merge-test 30 { |
| 219 | 1 2 3 4 5 6 7 8 9 |
| 220 | } { |
| @@ -256,20 +259,20 @@ | |
| 256 | } { |
| 257 | 1 2 3 4 9 |
| 258 | } { |
| 259 | 1 6 7 8 9 |
| 260 | } { |
| 261 | 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9 |
| 262 | } -expectError |
| 263 | merge-test 35 { |
| 264 | 1 2 3 4 5 6 7 8 9 |
| 265 | } { |
| 266 | 1 2 3 9 |
| 267 | } { |
| 268 | 1 7 8 9 |
| 269 | } { |
| 270 | 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9 |
| 271 | } -expectError |
| 272 | |
| 273 | merge-test 40 { |
| 274 | 2 3 4 5 6 7 8 |
| 275 | } { |
| @@ -311,20 +314,20 @@ | |
| 311 | } { |
| 312 | 6 7 8 |
| 313 | } { |
| 314 | 2 3 4 |
| 315 | } { |
| 316 | MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END |
| 317 | } -expectError |
| 318 | merge-test 45 { |
| 319 | 2 3 4 5 6 7 8 |
| 320 | } { |
| 321 | 7 8 |
| 322 | } { |
| 323 | 2 3 |
| 324 | } { |
| 325 | MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END |
| 326 | } -expectError |
| 327 | |
| 328 | merge-test 50 { |
| 329 | 2 3 4 5 6 7 8 |
| 330 | } { |
| @@ -365,20 +368,20 @@ | |
| 365 | } { |
| 366 | 2 3 4 |
| 367 | } { |
| 368 | 6 7 8 |
| 369 | } { |
| 370 | MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END |
| 371 | } -expectError |
| 372 | merge-test 55 { |
| 373 | 2 3 4 5 6 7 8 |
| 374 | } { |
| 375 | 2 3 |
| 376 | } { |
| 377 | 7 8 |
| 378 | } { |
| 379 | MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END |
| 380 | } -expectError |
| 381 | |
| 382 | merge-test 60 { |
| 383 | 1 2 3 4 5 6 7 8 9 |
| 384 | } { |
| @@ -420,20 +423,20 @@ | |
| 420 | } { |
| 421 | 1 2b 3b 4b 5b 6 7 8 9 |
| 422 | } { |
| 423 | 1 2 3 4 9 |
| 424 | } { |
| 425 | 1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 |
| 426 | } -expectError |
| 427 | merge-test 65 { |
| 428 | 1 2 3 4 5 6 7 8 9 |
| 429 | } { |
| 430 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 431 | } { |
| 432 | 1 2 3 9 |
| 433 | } { |
| 434 | 1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 |
| 435 | } -expectError |
| 436 | |
| 437 | merge-test 70 { |
| 438 | 1 2 3 4 5 6 7 8 9 |
| 439 | } { |
| @@ -475,20 +478,20 @@ | |
| 475 | } { |
| 476 | 1 2 3 4 9 |
| 477 | } { |
| 478 | 1 2b 3b 4b 5b 6 7 8 9 |
| 479 | } { |
| 480 | 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9 |
| 481 | } -expectError |
| 482 | merge-test 75 { |
| 483 | 1 2 3 4 5 6 7 8 9 |
| 484 | } { |
| 485 | 1 2 3 9 |
| 486 | } { |
| 487 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 488 | } { |
| 489 | 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9 |
| 490 | } -expectError |
| 491 | |
| 492 | merge-test 80 { |
| 493 | 2 3 4 5 6 7 8 |
| 494 | } { |
| @@ -530,20 +533,20 @@ | |
| 530 | } { |
| 531 | 2b 3b 4b 5b 6 7 8 |
| 532 | } { |
| 533 | 2 3 4 |
| 534 | } { |
| 535 | MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END |
| 536 | } -expectError |
| 537 | merge-test 85 { |
| 538 | 2 3 4 5 6 7 8 |
| 539 | } { |
| 540 | 2b 3b 4b 5b 6b 7 8 |
| 541 | } { |
| 542 | 2 3 |
| 543 | } { |
| 544 | MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END |
| 545 | } -expectError |
| 546 | |
| 547 | merge-test 90 { |
| 548 | 2 3 4 5 6 7 8 |
| 549 | } { |
| @@ -585,20 +588,20 @@ | |
| 585 | } { |
| 586 | 2 3 4 |
| 587 | } { |
| 588 | 2b 3b 4b 5b 6 7 8 |
| 589 | } { |
| 590 | MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END |
| 591 | } -expectError |
| 592 | merge-test 95 { |
| 593 | 2 3 4 5 6 7 8 |
| 594 | } { |
| 595 | 2 3 |
| 596 | } { |
| 597 | 2b 3b 4b 5b 6b 7 8 |
| 598 | } { |
| 599 | MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END |
| 600 | } -expectError |
| 601 | |
| 602 | merge-test 100 { |
| 603 | 1 2 3 4 5 6 7 8 9 |
| 604 | } { |
| @@ -631,20 +634,20 @@ | |
| 631 | } { |
| 632 | 1 2 3 4 5 7 8 9b |
| 633 | } { |
| 634 | 1 2 3 4 5 7 8 9b a b c d e |
| 635 | } { |
| 636 | 1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END |
| 637 | } -expectError |
| 638 | merge-test 104 { |
| 639 | 1 2 3 4 5 6 7 8 9 |
| 640 | } { |
| 641 | 1 2 3 4 5 7 8 9b a b c d e |
| 642 | } { |
| 643 | 1 2 3 4 5 7 8 9b |
| 644 | } { |
| 645 | 1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END |
| 646 | } -expectError |
| 647 | |
| 648 | ############################################################################### |
| 649 | |
| 650 | test_cleanup |
| 651 |
| --- test/merge3.test | |
| +++ test/merge3.test | |
| @@ -27,10 +27,13 @@ | |
| 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | set x [read_file t4] |
| 29 | regsub -all \ |
| 30 | {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \ |
| 31 | $x {MINE:} x |
| 32 | regsub -all \ |
| 33 | {####### SUGGESTED CONFLICT RESOLUTION follows #+} \ |
| 34 | $x {BOT:} x |
| 35 | regsub -all \ |
| 36 | {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \ |
| 37 | $x {COM:} x |
| 38 | regsub -all \ |
| 39 | {======= MERGED IN content follows =+ \(line \d+\)} \ |
| @@ -73,56 +76,56 @@ | |
| 76 | } { |
| 77 | 1 2 3b 4b 5b 6 7 8 9 |
| 78 | } { |
| 79 | 1 2 3 4 5c 6 7 8 9 |
| 80 | } { |
| 81 | 1 2 MINE: 3b 4b 5b BOT: 3b 4b 5c COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9 |
| 82 | } -expectError |
| 83 | merge-test 4 { |
| 84 | 1 2 3 4 5 6 7 8 9 |
| 85 | } { |
| 86 | 1 2 3b 4b 5b 6b 7 8 9 |
| 87 | } { |
| 88 | 1 2 3 4 5c 6 7 8 9 |
| 89 | } { |
| 90 | 1 2 MINE: 3b 4b 5b 6b BOT: 3b 4b 5b 5c 6 COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9 |
| 91 | } -expectError |
| 92 | merge-test 5 { |
| 93 | 1 2 3 4 5 6 7 8 9 |
| 94 | } { |
| 95 | 1 2 3b 4b 5b 6b 7 8 9 |
| 96 | } { |
| 97 | 1 2 3 4 5c 6c 7c 8 9 |
| 98 | } { |
| 99 | 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9 |
| 100 | } -expectError |
| 101 | merge-test 6 { |
| 102 | 1 2 3 4 5 6 7 8 9 |
| 103 | } { |
| 104 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 105 | } { |
| 106 | 1 2 3 4 5c 6c 7c 8 9 |
| 107 | } { |
| 108 | 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9 |
| 109 | } -expectError |
| 110 | merge-test 7 { |
| 111 | 1 2 3 4 5 6 7 8 9 |
| 112 | } { |
| 113 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 114 | } { |
| 115 | 1 2 3 4 5c 6c 7c 8c 9 |
| 116 | } { |
| 117 | 1 2 MINE: 3b 4b 5b 6b 7 8b BOT: 3b 4b 5b 5c 6c 7c 8c COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9 |
| 118 | } -expectError |
| 119 | merge-test 8 { |
| 120 | 1 2 3 4 5 6 7 8 9 |
| 121 | } { |
| 122 | 1 2 3b 4b 5b 6b 7 8b 9b |
| 123 | } { |
| 124 | 1 2 3 4 5c 6c 7c 8c 9 |
| 125 | } { |
| 126 | 1 2 MINE: 3b 4b 5b 6b 7 8b 9b BOT: 3b 4b 5b 5c 6c 7c 8c 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END |
| 127 | } -expectError |
| 128 | merge-test 9 { |
| 129 | 1 2 3 4 5 6 7 8 9 |
| 130 | } { |
| 131 | 1 2 3b 4b 5 6 7 8b 9b |
| @@ -146,11 +149,11 @@ | |
| 149 | } { |
| 150 | 1 2 3b 4b 5 6 7 8b 9b |
| 151 | } { |
| 152 | 1 2 3b 4c 5 6c 7c 8 9 |
| 153 | } { |
| 154 | 1 2 MINE: 3b 4b BOT: 3b 4c COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b |
| 155 | } -expectError |
| 156 | merge-test 12 { |
| 157 | 1 2 3 4 5 6 7 8 9 |
| 158 | } { |
| 159 | 1 2 3b4b 5 6 7 8b 9b |
| @@ -201,20 +204,20 @@ | |
| 204 | } { |
| 205 | 1 6 7 8 9 |
| 206 | } { |
| 207 | 1 2 3 4 9 |
| 208 | } { |
| 209 | 1 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 |
| 210 | } -expectError |
| 211 | merge-test 25 { |
| 212 | 1 2 3 4 5 6 7 8 9 |
| 213 | } { |
| 214 | 1 7 8 9 |
| 215 | } { |
| 216 | 1 2 3 9 |
| 217 | } { |
| 218 | 1 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 |
| 219 | } -expectError |
| 220 | |
| 221 | merge-test 30 { |
| 222 | 1 2 3 4 5 6 7 8 9 |
| 223 | } { |
| @@ -256,20 +259,20 @@ | |
| 259 | } { |
| 260 | 1 2 3 4 9 |
| 261 | } { |
| 262 | 1 6 7 8 9 |
| 263 | } { |
| 264 | 1 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9 |
| 265 | } -expectError |
| 266 | merge-test 35 { |
| 267 | 1 2 3 4 5 6 7 8 9 |
| 268 | } { |
| 269 | 1 2 3 9 |
| 270 | } { |
| 271 | 1 7 8 9 |
| 272 | } { |
| 273 | 1 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9 |
| 274 | } -expectError |
| 275 | |
| 276 | merge-test 40 { |
| 277 | 2 3 4 5 6 7 8 |
| 278 | } { |
| @@ -311,20 +314,20 @@ | |
| 314 | } { |
| 315 | 6 7 8 |
| 316 | } { |
| 317 | 2 3 4 |
| 318 | } { |
| 319 | MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END |
| 320 | } -expectError |
| 321 | merge-test 45 { |
| 322 | 2 3 4 5 6 7 8 |
| 323 | } { |
| 324 | 7 8 |
| 325 | } { |
| 326 | 2 3 |
| 327 | } { |
| 328 | MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END |
| 329 | } -expectError |
| 330 | |
| 331 | merge-test 50 { |
| 332 | 2 3 4 5 6 7 8 |
| 333 | } { |
| @@ -365,20 +368,20 @@ | |
| 368 | } { |
| 369 | 2 3 4 |
| 370 | } { |
| 371 | 6 7 8 |
| 372 | } { |
| 373 | MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END |
| 374 | } -expectError |
| 375 | merge-test 55 { |
| 376 | 2 3 4 5 6 7 8 |
| 377 | } { |
| 378 | 2 3 |
| 379 | } { |
| 380 | 7 8 |
| 381 | } { |
| 382 | MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END |
| 383 | } -expectError |
| 384 | |
| 385 | merge-test 60 { |
| 386 | 1 2 3 4 5 6 7 8 9 |
| 387 | } { |
| @@ -420,20 +423,20 @@ | |
| 423 | } { |
| 424 | 1 2b 3b 4b 5b 6 7 8 9 |
| 425 | } { |
| 426 | 1 2 3 4 9 |
| 427 | } { |
| 428 | 1 MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9 |
| 429 | } -expectError |
| 430 | merge-test 65 { |
| 431 | 1 2 3 4 5 6 7 8 9 |
| 432 | } { |
| 433 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 434 | } { |
| 435 | 1 2 3 9 |
| 436 | } { |
| 437 | 1 MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9 |
| 438 | } -expectError |
| 439 | |
| 440 | merge-test 70 { |
| 441 | 1 2 3 4 5 6 7 8 9 |
| 442 | } { |
| @@ -475,20 +478,20 @@ | |
| 478 | } { |
| 479 | 1 2 3 4 9 |
| 480 | } { |
| 481 | 1 2b 3b 4b 5b 6 7 8 9 |
| 482 | } { |
| 483 | 1 MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9 |
| 484 | } -expectError |
| 485 | merge-test 75 { |
| 486 | 1 2 3 4 5 6 7 8 9 |
| 487 | } { |
| 488 | 1 2 3 9 |
| 489 | } { |
| 490 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 491 | } { |
| 492 | 1 MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9 |
| 493 | } -expectError |
| 494 | |
| 495 | merge-test 80 { |
| 496 | 2 3 4 5 6 7 8 |
| 497 | } { |
| @@ -530,20 +533,20 @@ | |
| 533 | } { |
| 534 | 2b 3b 4b 5b 6 7 8 |
| 535 | } { |
| 536 | 2 3 4 |
| 537 | } { |
| 538 | MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END |
| 539 | } -expectError |
| 540 | merge-test 85 { |
| 541 | 2 3 4 5 6 7 8 |
| 542 | } { |
| 543 | 2b 3b 4b 5b 6b 7 8 |
| 544 | } { |
| 545 | 2 3 |
| 546 | } { |
| 547 | MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END |
| 548 | } -expectError |
| 549 | |
| 550 | merge-test 90 { |
| 551 | 2 3 4 5 6 7 8 |
| 552 | } { |
| @@ -585,20 +588,20 @@ | |
| 588 | } { |
| 589 | 2 3 4 |
| 590 | } { |
| 591 | 2b 3b 4b 5b 6 7 8 |
| 592 | } { |
| 593 | MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END |
| 594 | } -expectError |
| 595 | merge-test 95 { |
| 596 | 2 3 4 5 6 7 8 |
| 597 | } { |
| 598 | 2 3 |
| 599 | } { |
| 600 | 2b 3b 4b 5b 6b 7 8 |
| 601 | } { |
| 602 | MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END |
| 603 | } -expectError |
| 604 | |
| 605 | merge-test 100 { |
| 606 | 1 2 3 4 5 6 7 8 9 |
| 607 | } { |
| @@ -631,20 +634,20 @@ | |
| 634 | } { |
| 635 | 1 2 3 4 5 7 8 9b |
| 636 | } { |
| 637 | 1 2 3 4 5 7 8 9b a b c d e |
| 638 | } { |
| 639 | 1 2 3 4 5 7 8 MINE: 9b BOT: 9b a b c d e COM: 9 YOURS: 9b a b c d e END |
| 640 | } -expectError |
| 641 | merge-test 104 { |
| 642 | 1 2 3 4 5 6 7 8 9 |
| 643 | } { |
| 644 | 1 2 3 4 5 7 8 9b a b c d e |
| 645 | } { |
| 646 | 1 2 3 4 5 7 8 9b |
| 647 | } { |
| 648 | 1 2 3 4 5 7 8 MINE: 9b a b c d e BOT: 9b COM: 9 YOURS: 9b END |
| 649 | } -expectError |
| 650 | |
| 651 | ############################################################################### |
| 652 | |
| 653 | test_cleanup |
| 654 |
+6
-4
| --- test/merge4.test | ||
| +++ test/merge4.test | ||
| @@ -26,15 +26,17 @@ | ||
| 26 | 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | 28 | fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args |
| 29 | 29 | set x [read_file t4] |
| 30 | 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x |
| 31 | + regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x | |
| 31 | 32 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x |
| 32 | 33 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 33 | 34 | set x [split [string trim $x] \n] |
| 34 | 35 | set y [read_file t5] |
| 35 | 36 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y |
| 37 | + regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y | |
| 36 | 38 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y |
| 37 | 39 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 38 | 40 | set y [split [string trim $y] \n] |
| 39 | 41 | set result1 [string trim $result1] |
| 40 | 42 | if {$x!=$result1} { |
| @@ -58,13 +60,13 @@ | ||
| 58 | 60 | } { |
| 59 | 61 | 1 2b 3b 4b 5 6b 7b 8b 9 |
| 60 | 62 | } { |
| 61 | 63 | 1 2 3 4c 5c 6c 7 8 9 |
| 62 | 64 | } { |
| 63 | - 1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9 | |
| 65 | + 1 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < 9 | |
| 64 | 66 | } { |
| 65 | - 1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9 | |
| 67 | + 1 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < 9 | |
| 66 | 68 | } -expectError |
| 67 | 69 | merge-test 1001 { |
| 68 | 70 | 1 2 3 4 5 6 7 8 9 |
| 69 | 71 | } { |
| 70 | 72 | 1 2b 3b 4 5 6 7b 8b 9 |
| @@ -80,13 +82,13 @@ | ||
| 80 | 82 | } { |
| 81 | 83 | 2b 3b 4b 5 6b 7b 8b |
| 82 | 84 | } { |
| 83 | 85 | 2 3 4c 5c 6c 7 8 |
| 84 | 86 | } { |
| 85 | - > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < | |
| 87 | + > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < | |
| 86 | 88 | } { |
| 87 | - > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < | |
| 89 | + > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < | |
| 88 | 90 | } -expectError |
| 89 | 91 | merge-test 1003 { |
| 90 | 92 | 2 3 4 5 6 7 8 |
| 91 | 93 | } { |
| 92 | 94 | 2b 3b 4 5 6 7b 8b |
| 93 | 95 |
| --- test/merge4.test | |
| +++ test/merge4.test | |
| @@ -26,15 +26,17 @@ | |
| 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args |
| 29 | set x [read_file t4] |
| 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x |
| 31 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x |
| 32 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 33 | set x [split [string trim $x] \n] |
| 34 | set y [read_file t5] |
| 35 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y |
| 36 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y |
| 37 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 38 | set y [split [string trim $y] \n] |
| 39 | set result1 [string trim $result1] |
| 40 | if {$x!=$result1} { |
| @@ -58,13 +60,13 @@ | |
| 58 | } { |
| 59 | 1 2b 3b 4b 5 6b 7b 8b 9 |
| 60 | } { |
| 61 | 1 2 3 4c 5c 6c 7 8 9 |
| 62 | } { |
| 63 | 1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9 |
| 64 | } { |
| 65 | 1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9 |
| 66 | } -expectError |
| 67 | merge-test 1001 { |
| 68 | 1 2 3 4 5 6 7 8 9 |
| 69 | } { |
| 70 | 1 2b 3b 4 5 6 7b 8b 9 |
| @@ -80,13 +82,13 @@ | |
| 80 | } { |
| 81 | 2b 3b 4b 5 6b 7b 8b |
| 82 | } { |
| 83 | 2 3 4c 5c 6c 7 8 |
| 84 | } { |
| 85 | > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < |
| 86 | } { |
| 87 | > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < |
| 88 | } -expectError |
| 89 | merge-test 1003 { |
| 90 | 2 3 4 5 6 7 8 |
| 91 | } { |
| 92 | 2b 3b 4 5 6 7b 8b |
| 93 |
| --- test/merge4.test | |
| +++ test/merge4.test | |
| @@ -26,15 +26,17 @@ | |
| 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args |
| 29 | set x [read_file t4] |
| 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x |
| 31 | regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x |
| 32 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x |
| 33 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 34 | set x [split [string trim $x] \n] |
| 35 | set y [read_file t5] |
| 36 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y |
| 37 | regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y |
| 38 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y |
| 39 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 40 | set y [split [string trim $y] \n] |
| 41 | set result1 [string trim $result1] |
| 42 | if {$x!=$result1} { |
| @@ -58,13 +60,13 @@ | |
| 60 | } { |
| 61 | 1 2b 3b 4b 5 6b 7b 8b 9 |
| 62 | } { |
| 63 | 1 2 3 4c 5c 6c 7 8 9 |
| 64 | } { |
| 65 | 1 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < 9 |
| 66 | } { |
| 67 | 1 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < 9 |
| 68 | } -expectError |
| 69 | merge-test 1001 { |
| 70 | 1 2 3 4 5 6 7 8 9 |
| 71 | } { |
| 72 | 1 2b 3b 4 5 6 7b 8b 9 |
| @@ -80,13 +82,13 @@ | |
| 82 | } { |
| 83 | 2b 3b 4b 5 6b 7b 8b |
| 84 | } { |
| 85 | 2 3 4c 5c 6c 7 8 |
| 86 | } { |
| 87 | > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < |
| 88 | } { |
| 89 | > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < |
| 90 | } -expectError |
| 91 | merge-test 1003 { |
| 92 | 2 3 4 5 6 7 8 |
| 93 | } { |
| 94 | 2b 3b 4 5 6 7b 8b |
| 95 |
+1
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -332,10 +332,11 @@ | ||
| 332 | 332 | encoding-glob \ |
| 333 | 333 | exec-rel-paths \ |
| 334 | 334 | fileedit-glob \ |
| 335 | 335 | forbid-delta-manifests \ |
| 336 | 336 | forum-close-policy \ |
| 337 | + forum-title \ | |
| 337 | 338 | gdiff-command \ |
| 338 | 339 | gmerge-command \ |
| 339 | 340 | hash-digits \ |
| 340 | 341 | hooks \ |
| 341 | 342 | http-port \ |
| 342 | 343 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -332,10 +332,11 @@ | |
| 332 | encoding-glob \ |
| 333 | exec-rel-paths \ |
| 334 | fileedit-glob \ |
| 335 | forbid-delta-manifests \ |
| 336 | forum-close-policy \ |
| 337 | gdiff-command \ |
| 338 | gmerge-command \ |
| 339 | hash-digits \ |
| 340 | hooks \ |
| 341 | http-port \ |
| 342 |
| --- test/tester.tcl | |
| +++ test/tester.tcl | |
| @@ -332,10 +332,11 @@ | |
| 332 | encoding-glob \ |
| 333 | exec-rel-paths \ |
| 334 | fileedit-glob \ |
| 335 | forbid-delta-manifests \ |
| 336 | forum-close-policy \ |
| 337 | forum-title \ |
| 338 | gdiff-command \ |
| 339 | gmerge-command \ |
| 340 | hash-digits \ |
| 341 | hooks \ |
| 342 | http-port \ |
| 343 |
+1
-1
| --- test/update.test | ||
| +++ test/update.test | ||
| @@ -57,11 +57,11 @@ | ||
| 57 | 57 | |
| 58 | 58 | ############################################################################### |
| 59 | 59 | |
| 60 | 60 | fossil update --verbose |
| 61 | 61 | test update-already-up-to-date { |
| 62 | - [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date$} $RESULT] | |
| 62 | + [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date.$} $RESULT] | |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | # Remaining tests are carried out in the order update_cmd() performs checks. |
| 66 | 66 | # |
| 67 | 67 | # Common approach for tests below: |
| 68 | 68 |
| --- test/update.test | |
| +++ test/update.test | |
| @@ -57,11 +57,11 @@ | |
| 57 | |
| 58 | ############################################################################### |
| 59 | |
| 60 | fossil update --verbose |
| 61 | test update-already-up-to-date { |
| 62 | [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date$} $RESULT] |
| 63 | } |
| 64 | |
| 65 | # Remaining tests are carried out in the order update_cmd() performs checks. |
| 66 | # |
| 67 | # Common approach for tests below: |
| 68 |
| --- test/update.test | |
| +++ test/update.test | |
| @@ -57,11 +57,11 @@ | |
| 57 | |
| 58 | ############################################################################### |
| 59 | |
| 60 | fossil update --verbose |
| 61 | test update-already-up-to-date { |
| 62 | [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date.$} $RESULT] |
| 63 | } |
| 64 | |
| 65 | # Remaining tests are carried out in the order update_cmd() performs checks. |
| 66 | # |
| 67 | # Common approach for tests below: |
| 68 |
+16
-5
| --- tools/translate.c | ||
| +++ tools/translate.c | ||
| @@ -78,10 +78,21 @@ | ||
| 78 | 78 | |
| 79 | 79 | /* |
| 80 | 80 | ** Name of files being processed |
| 81 | 81 | */ |
| 82 | 82 | static const char *zInFile = "(stdin)"; |
| 83 | + | |
| 84 | +/* | |
| 85 | +** The `fossil_isspace()' function copied from the Fossil source code. | |
| 86 | +** Some MSVC runtime library versions of `isspace()' fail with an assertion that | |
| 87 | +** the input is smaller than -1 or greater than 255 in debug builds, due to sign | |
| 88 | +** extension when promoting `signed char' to `int' for non-ASCII characters. Use | |
| 89 | +** an `isspace()' replacement instead of explicit type casts to `unsigned char'. | |
| 90 | +*/ | |
| 91 | +int fossil_isspace(char c){ | |
| 92 | + return c==' ' || (c<='\r' && c>='\t'); | |
| 93 | +} | |
| 83 | 94 | |
| 84 | 95 | /* |
| 85 | 96 | ** Terminate an active cgi_printf() or free string |
| 86 | 97 | */ |
| 87 | 98 | static void end_block(FILE *out){ |
| @@ -106,21 +117,21 @@ | ||
| 106 | 117 | char zOut[4000]; /* The input line translated into appropriate output */ |
| 107 | 118 | |
| 108 | 119 | c1 = c2 = '-'; |
| 109 | 120 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 110 | 121 | lineNo++; |
| 111 | - for(i=0; zLine[i] && isspace(zLine[i]); i++){} | |
| 122 | + for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){} | |
| 112 | 123 | if( zLine[i]!='@' ){ |
| 113 | 124 | if( inPrint || inStr ) end_block(out); |
| 114 | 125 | fprintf(out,"%s",zLine); |
| 115 | 126 | /* 0123456789 12345 */ |
| 116 | 127 | if( strncmp(zLine, "/* @-comment: ", 14)==0 ){ |
| 117 | 128 | c1 = zLine[14]; |
| 118 | 129 | c2 = zLine[15]; |
| 119 | 130 | } |
| 120 | 131 | i += strlen(&zLine[i]); |
| 121 | - while( i>0 && isspace(zLine[i-1]) ){ i--; } | |
| 132 | + while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; } | |
| 122 | 133 | lastWasEq = i>0 && zLine[i-1]=='='; |
| 123 | 134 | lastWasComma = i>0 && zLine[i-1]==','; |
| 124 | 135 | }else if( lastWasEq || lastWasComma){ |
| 125 | 136 | /* If the last non-whitespace character before the first @ was |
| 126 | 137 | ** an "="(var init/set) or a ","(const definition in list) then |
| @@ -129,11 +140,11 @@ | ||
| 129 | 140 | ** and end of line. |
| 130 | 141 | */ |
| 131 | 142 | int indent, omitline; |
| 132 | 143 | char *zNewline = "\\n"; |
| 133 | 144 | i++; |
| 134 | - if( isspace(zLine[i]) ){ i++; } | |
| 145 | + if( fossil_isspace(zLine[i]) ){ i++; } | |
| 135 | 146 | indent = i - 2; |
| 136 | 147 | if( indent<0 ) indent = 0; |
| 137 | 148 | omitline = 0; |
| 138 | 149 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 139 | 150 | if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){ |
| @@ -147,11 +158,11 @@ | ||
| 147 | 158 | break; |
| 148 | 159 | } |
| 149 | 160 | if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; } |
| 150 | 161 | zOut[j++] = zLine[i]; |
| 151 | 162 | } |
| 152 | - if( zNewline[0] ) while( j>0 && isspace(zOut[j-1]) ){ j--; } | |
| 163 | + if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; } | |
| 153 | 164 | zOut[j] = 0; |
| 154 | 165 | if( j<=0 && omitline ){ |
| 155 | 166 | fprintf(out,"\n"); |
| 156 | 167 | }else{ |
| 157 | 168 | fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline); |
| @@ -171,11 +182,11 @@ | ||
| 171 | 182 | int indent; |
| 172 | 183 | int nC; |
| 173 | 184 | int nParam; |
| 174 | 185 | char c; |
| 175 | 186 | i++; |
| 176 | - if( isspace(zLine[i]) ){ i++; } | |
| 187 | + if( fossil_isspace(zLine[i]) ){ i++; } | |
| 177 | 188 | indent = i; |
| 178 | 189 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 179 | 190 | if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r' |
| 180 | 191 | || zLine[i+1]=='\n') ){ |
| 181 | 192 | zNewline = ""; |
| 182 | 193 |
| --- tools/translate.c | |
| +++ tools/translate.c | |
| @@ -78,10 +78,21 @@ | |
| 78 | |
| 79 | /* |
| 80 | ** Name of files being processed |
| 81 | */ |
| 82 | static const char *zInFile = "(stdin)"; |
| 83 | |
| 84 | /* |
| 85 | ** Terminate an active cgi_printf() or free string |
| 86 | */ |
| 87 | static void end_block(FILE *out){ |
| @@ -106,21 +117,21 @@ | |
| 106 | char zOut[4000]; /* The input line translated into appropriate output */ |
| 107 | |
| 108 | c1 = c2 = '-'; |
| 109 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 110 | lineNo++; |
| 111 | for(i=0; zLine[i] && isspace(zLine[i]); i++){} |
| 112 | if( zLine[i]!='@' ){ |
| 113 | if( inPrint || inStr ) end_block(out); |
| 114 | fprintf(out,"%s",zLine); |
| 115 | /* 0123456789 12345 */ |
| 116 | if( strncmp(zLine, "/* @-comment: ", 14)==0 ){ |
| 117 | c1 = zLine[14]; |
| 118 | c2 = zLine[15]; |
| 119 | } |
| 120 | i += strlen(&zLine[i]); |
| 121 | while( i>0 && isspace(zLine[i-1]) ){ i--; } |
| 122 | lastWasEq = i>0 && zLine[i-1]=='='; |
| 123 | lastWasComma = i>0 && zLine[i-1]==','; |
| 124 | }else if( lastWasEq || lastWasComma){ |
| 125 | /* If the last non-whitespace character before the first @ was |
| 126 | ** an "="(var init/set) or a ","(const definition in list) then |
| @@ -129,11 +140,11 @@ | |
| 129 | ** and end of line. |
| 130 | */ |
| 131 | int indent, omitline; |
| 132 | char *zNewline = "\\n"; |
| 133 | i++; |
| 134 | if( isspace(zLine[i]) ){ i++; } |
| 135 | indent = i - 2; |
| 136 | if( indent<0 ) indent = 0; |
| 137 | omitline = 0; |
| 138 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 139 | if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){ |
| @@ -147,11 +158,11 @@ | |
| 147 | break; |
| 148 | } |
| 149 | if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; } |
| 150 | zOut[j++] = zLine[i]; |
| 151 | } |
| 152 | if( zNewline[0] ) while( j>0 && isspace(zOut[j-1]) ){ j--; } |
| 153 | zOut[j] = 0; |
| 154 | if( j<=0 && omitline ){ |
| 155 | fprintf(out,"\n"); |
| 156 | }else{ |
| 157 | fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline); |
| @@ -171,11 +182,11 @@ | |
| 171 | int indent; |
| 172 | int nC; |
| 173 | int nParam; |
| 174 | char c; |
| 175 | i++; |
| 176 | if( isspace(zLine[i]) ){ i++; } |
| 177 | indent = i; |
| 178 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 179 | if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r' |
| 180 | || zLine[i+1]=='\n') ){ |
| 181 | zNewline = ""; |
| 182 |
| --- tools/translate.c | |
| +++ tools/translate.c | |
| @@ -78,10 +78,21 @@ | |
| 78 | |
| 79 | /* |
| 80 | ** Name of files being processed |
| 81 | */ |
| 82 | static const char *zInFile = "(stdin)"; |
| 83 | |
| 84 | /* |
| 85 | ** The `fossil_isspace()' function copied from the Fossil source code. |
| 86 | ** Some MSVC runtime library versions of `isspace()' fail with an assertion that |
| 87 | ** the input is smaller than -1 or greater than 255 in debug builds, due to sign |
| 88 | ** extension when promoting `signed char' to `int' for non-ASCII characters. Use |
| 89 | ** an `isspace()' replacement instead of explicit type casts to `unsigned char'. |
| 90 | */ |
| 91 | int fossil_isspace(char c){ |
| 92 | return c==' ' || (c<='\r' && c>='\t'); |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | ** Terminate an active cgi_printf() or free string |
| 97 | */ |
| 98 | static void end_block(FILE *out){ |
| @@ -106,21 +117,21 @@ | |
| 117 | char zOut[4000]; /* The input line translated into appropriate output */ |
| 118 | |
| 119 | c1 = c2 = '-'; |
| 120 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 121 | lineNo++; |
| 122 | for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){} |
| 123 | if( zLine[i]!='@' ){ |
| 124 | if( inPrint || inStr ) end_block(out); |
| 125 | fprintf(out,"%s",zLine); |
| 126 | /* 0123456789 12345 */ |
| 127 | if( strncmp(zLine, "/* @-comment: ", 14)==0 ){ |
| 128 | c1 = zLine[14]; |
| 129 | c2 = zLine[15]; |
| 130 | } |
| 131 | i += strlen(&zLine[i]); |
| 132 | while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; } |
| 133 | lastWasEq = i>0 && zLine[i-1]=='='; |
| 134 | lastWasComma = i>0 && zLine[i-1]==','; |
| 135 | }else if( lastWasEq || lastWasComma){ |
| 136 | /* If the last non-whitespace character before the first @ was |
| 137 | ** an "="(var init/set) or a ","(const definition in list) then |
| @@ -129,11 +140,11 @@ | |
| 140 | ** and end of line. |
| 141 | */ |
| 142 | int indent, omitline; |
| 143 | char *zNewline = "\\n"; |
| 144 | i++; |
| 145 | if( fossil_isspace(zLine[i]) ){ i++; } |
| 146 | indent = i - 2; |
| 147 | if( indent<0 ) indent = 0; |
| 148 | omitline = 0; |
| 149 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 150 | if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){ |
| @@ -147,11 +158,11 @@ | |
| 158 | break; |
| 159 | } |
| 160 | if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; } |
| 161 | zOut[j++] = zLine[i]; |
| 162 | } |
| 163 | if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; } |
| 164 | zOut[j] = 0; |
| 165 | if( j<=0 && omitline ){ |
| 166 | fprintf(out,"\n"); |
| 167 | }else{ |
| 168 | fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline); |
| @@ -171,11 +182,11 @@ | |
| 182 | int indent; |
| 183 | int nC; |
| 184 | int nParam; |
| 185 | char c; |
| 186 | i++; |
| 187 | if( fossil_isspace(zLine[i]) ){ i++; } |
| 188 | indent = i; |
| 189 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 190 | if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r' |
| 191 | || zLine[i+1]=='\n') ){ |
| 192 | zNewline = ""; |
| 193 |
+8
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -16,10 +16,18 @@ | ||
| 16 | 16 | merge or update operation. |
| 17 | 17 | * Issue a warning if a user tries to commit on a check-in where the |
| 18 | 18 | branch has been changed. |
| 19 | 19 | * When a merge conflict occurs, a new section is added to the conflict |
| 20 | 20 | text that shows Fossil's suggested resolution to the conflict. |
| 21 | + * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show | |
| 22 | + diffs of multiple files. | |
| 23 | + * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis | |
| 24 | + and debugging | |
| 25 | + * Fix a bug in [/help?cmd=patch|fossil patch create] that causes | |
| 26 | + [/help?cmd=revert|fossil revert] operations that happened on individual | |
| 27 | + files after a [/help?cmd=merge|fossil merge] to be omitted from the | |
| 28 | + patch. | |
| 21 | 29 | |
| 22 | 30 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 23 | 31 | |
| 24 | 32 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 25 | 33 | that have non-ASCII filenames |
| 26 | 34 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -16,10 +16,18 @@ | |
| 16 | merge or update operation. |
| 17 | * Issue a warning if a user tries to commit on a check-in where the |
| 18 | branch has been changed. |
| 19 | * When a merge conflict occurs, a new section is added to the conflict |
| 20 | text that shows Fossil's suggested resolution to the conflict. |
| 21 | |
| 22 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 23 | |
| 24 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 25 | that have non-ASCII filenames |
| 26 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -16,10 +16,18 @@ | |
| 16 | merge or update operation. |
| 17 | * Issue a warning if a user tries to commit on a check-in where the |
| 18 | branch has been changed. |
| 19 | * When a merge conflict occurs, a new section is added to the conflict |
| 20 | text that shows Fossil's suggested resolution to the conflict. |
| 21 | * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show |
| 22 | diffs of multiple files. |
| 23 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 24 | and debugging |
| 25 | * Fix a bug in [/help?cmd=patch|fossil patch create] that causes |
| 26 | [/help?cmd=revert|fossil revert] operations that happened on individual |
| 27 | files after a [/help?cmd=merge|fossil merge] to be omitted from the |
| 28 | patch. |
| 29 | |
| 30 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 31 | |
| 32 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 33 | that have non-ASCII filenames |
| 34 |
+197
-88
| --- www/ssl-server.md | ||
| +++ www/ssl-server.md | ||
| @@ -12,76 +12,107 @@ | ||
| 12 | 12 | |
| 13 | 13 | [0]: ./ssl.wiki |
| 14 | 14 | [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 |
| 15 | 15 | |
| 16 | 16 | Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), |
| 17 | -this has been fixed. Commands like | |
| 17 | +Fossil servers are now able to converse directly over TLS. Commands like | |
| 18 | 18 | |
| 19 | 19 | * "[fossil server](/help?cmd=server)" |
| 20 | 20 | * "[fossil ui](/help?cmd=ui)", and |
| 21 | 21 | * "[fossil http](/help?cmd=http)" |
| 22 | 22 | |
| 23 | -now all handle server-mode SSL/TLS encryption natively. It is now possible | |
| 24 | -to run a secure Fossil server without having to put Fossil behind an encrypting | |
| 25 | -web server or reverse proxy. Hence, it is now possible to stand up a complete | |
| 26 | -Fossil project website on an inexpensive VPS with no added software other than | |
| 27 | -Fossil itself and something like [certbot](https://certbot.eff.org) for | |
| 28 | -obtaining a CA-signed certificate. | |
| 29 | - | |
| 30 | -## Usage | |
| 23 | +may now handle the encryption natively when suitably configured, without | |
| 24 | +requiring a third-party proxy layer. | |
| 25 | + | |
| 26 | +## <a id="usage"></a>Usage | |
| 31 | 27 | |
| 32 | 28 | To put any of the Fossil server commands into SSL/TLS mode, simply |
| 33 | -add the "--cert" command-line option. | |
| 29 | +add the "`--cert`" command-line option: | |
| 34 | 30 | |
| 35 | 31 | fossil ui --cert unsafe-builtin |
| 36 | 32 | |
| 37 | -The --cert option is what tells Fossil to use TLS encryption. | |
| 38 | -Normally, the argument to --cert is the name of a file containing | |
| 39 | -the certificate (the "fullchain.pem" file) for the website. In this | |
| 40 | -example, the magic name "unsafe-builtin" is used, which causes Fossil | |
| 41 | -to use a self-signed cert rather than a real cert obtained from a | |
| 42 | -[Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority) | |
| 43 | -or "CA". As the name implies, this self-signed cert is not secure and | |
| 44 | -should only be used for testing. Your web-browser will complain bitterly | |
| 45 | -and will refuse to display the pages using the "unsafe-builtin" cert. | |
| 46 | -Firefox will allow you to click an "I know the risks" button and continue. | |
| 47 | -Other web browsers will stubornly refuse to display the page, under the theory | |
| 48 | -that weak encryption is worse than no encryption at all. Continue reading | |
| 49 | -to see how to solve this. | |
| 50 | - | |
| 51 | -## About Certs | |
| 52 | - | |
| 53 | -Certs are based on public-key or asymmetric cryptography. To create a cert, | |
| 54 | -you first create a new "key pair" consisting of a public key and a private key. | |
| 55 | -The public key can be freely shared with the world, but you must keep the | |
| 56 | -private key secret. If anyone gains access to your private key then he will be | |
| 57 | -able to impersonate you and break into your system. | |
| 58 | - | |
| 59 | -To obtain a cert, you send your public key and the name of the domain you | |
| 60 | -want to protect to a certificate authority. The CA then digitally signs | |
| 61 | -the combination of those two things using their own private key and sends | |
| 62 | -the signed combination back to you. The CA's digital signature of your | |
| 63 | -public key and domain name is the cert. | |
| 64 | - | |
| 65 | -SSL/TLS servers need two things in order to prove their identity to clients: | |
| 66 | - | |
| 67 | - 1. The cert that was signed by a CA | |
| 68 | - 2. The private key | |
| 69 | - | |
| 70 | -The SSL/TLS servers send the cert to each client, so that the client | |
| 71 | -can verify it. But the private key is kept strictly private and is never | |
| 72 | -shared with anyone. | |
| 73 | - | |
| 74 | -## How To Tell Fossil About Your Cert And Private Key | |
| 75 | - | |
| 76 | -If you do not have your own cert and private key, you can ask Fossil | |
| 33 | +Here, we are passing the magic name "unsafe-builtin" to cause Fossil to | |
| 34 | +use a [hard-coded self-signed cert][hcssc] rather than one obtained from | |
| 35 | +a recognized [Certificate Authority][CA], or "CA". | |
| 36 | + | |
| 37 | +As the name implies, this self-signed cert is _not secure_ and should | |
| 38 | +only be used for testing. Your web browser is likely to complain | |
| 39 | +bitterly about it and will refuse to display the pages using the | |
| 40 | +"unsafe-builtin" cert until you placate it. The complexity of the | |
| 41 | +ceremony demanded depends on how paranoid your browser’s creators have | |
| 42 | +decided to be. It may require as little as clicking a single big "I know | |
| 43 | +the risks" type of button, or it may require a sequence be several | |
| 44 | +clicks designed to discourage the “yes, yes, just let me do the thing” | |
| 45 | +crowd lest they run themselves into trouble by disregarding well-meant | |
| 46 | +warnings. | |
| 47 | + | |
| 48 | +Our purpose here is to show you an alternate path that will avoid the | |
| 49 | +issue entirely, not weigh in on which browser handles self-signed | |
| 50 | +certificates best. | |
| 51 | + | |
| 52 | +[CA]: https://en.wikipedia.org/wiki/Certificate_authority | |
| 53 | +[hcssc]: /info/c2a7b14c3f541edb96?ln=89-116 | |
| 54 | + | |
| 55 | +## <a id="about"></a>About Certs | |
| 56 | + | |
| 57 | +The X.509 certificate system used by browsers to secure TLS connections | |
| 58 | +is based on asymmetric public-key cryptography. The methods for | |
| 59 | +obtaining one vary widely, with a resulting tradeoff we may summarize as | |
| 60 | +trustworthiness versus convenience, the latter characteristic falling as | |
| 61 | +the former rises.(^No strict correlation exists. CAs have invented | |
| 62 | +highly inconvenient certification schemes that offer little additional | |
| 63 | +real-world trustworthiness. Extreme cases along this axis may be fairly | |
| 64 | +characterized as [security theater][st]. We focus in this document on | |
| 65 | +well-balanced trade-offs between decreasing convenience and useful | |
| 66 | +levels of trustworthiness gained thereby.) | |
| 67 | + | |
| 68 | +The self-signed method demonstrated above offers approximately zero | |
| 69 | +trustworthiness, though not zero _value_ since it does still provide | |
| 70 | +connection encryption. | |
| 71 | + | |
| 72 | +More trustworthy methods are necessarily less convenient. One such is to | |
| 73 | +send your public key and the name of the domain you want to protect to a | |
| 74 | +recognized CA, which then performs one or more tests to convince itself | |
| 75 | +that the requester is in control of that domain. If the CA’s tests all | |
| 76 | +pass, it produces an X.509 certificate bound to that domain, which | |
| 77 | +includes assorted other information under the CA’s digital signature | |
| 78 | +attesting to the validity of the document’s contents. The result is sent | |
| 79 | +back to the requester, which may then use it to transitively attest to | |
| 80 | +these tests’ success: presuming one cannot fake the type of signature | |
| 81 | +used, the document must have been signed by the trusted, recognized CA. | |
| 82 | + | |
| 83 | +There is one element of the assorted information included with a | |
| 84 | +certificate that is neither supplied by the requester nor rubber-stamped | |
| 85 | +on it in passing by the CA. It also generates a one-time key pair and | |
| 86 | +stores the public half in the certificate. The cryptosystem this keypair | |
| 87 | +is intended to work with varies both by the CA and by time, as older | |
| 88 | +systems become obsolete. Details aside, the CA then puts this matching | |
| 89 | +private half of the key in a separate file, often encrypted under a | |
| 90 | +separate cryptosystem for security. | |
| 91 | + | |
| 92 | +SSL/TLS servers need both resulting halves to make these attestations, | |
| 93 | +but they send only the public half to the client when establishing the | |
| 94 | +connection. The client then makes its own checks to determine whether it | |
| 95 | +trusts the attestations being made. | |
| 96 | + | |
| 97 | +A properly written and administered server never releases the private | |
| 98 | +key to anyone. Ideally, it goes directly from the CA to the requesting | |
| 99 | +server and never moves from there; then when it expires, the server | |
| 100 | +deletes it permanently. | |
| 101 | + | |
| 102 | +[st]: https://en.wikipedia.org/wiki/Security_theater | |
| 103 | + | |
| 104 | +## <a id="startup"></a>How To Tell Fossil About Your Cert And Private Key | |
| 105 | + | |
| 106 | +As we saw [above](#usage), | |
| 107 | +if you do not have your own cert and private key, you can ask Fossil | |
| 77 | 108 | to use "unsafe-builtin", which is a self-signed cert that is built into |
| 78 | -Fossil. This is wildly insecure, since the private key is not really private - | |
| 79 | -it is [in plain sight](/info/c2a7b14c3f541edb96?ln=89-116) in the Fossil | |
| 109 | +Fossil. This is wildly insecure, since the private key is not really private; | |
| 110 | +it is [in plain sight][hcssc] in the Fossil | |
| 80 | 111 | source tree for anybody to read. <b>Never add the private key that is |
| 81 | 112 | built into Fossil to your OS's trust store</b> as doing so will severely |
| 82 | -compromise your computer. The built-in cert is only useful for testing. | |
| 113 | +compromise your computer.[^ssattack] This built-in cert is only useful for testing. | |
| 83 | 114 | If you want actual security, you will need to come up with your own private |
| 84 | 115 | key and cert. |
| 85 | 116 | |
| 86 | 117 | Fossil wants to read certs and public keys in the |
| 87 | 118 | [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail). |
| @@ -98,66 +129,142 @@ | ||
| 98 | 129 | *base-64 encoding of the certificate* |
| 99 | 130 | -----END CERTIFICATE----- |
| 100 | 131 | |
| 101 | 132 | In both formats, text outside of the delimiters is ignored. That means |
| 102 | 133 | that if you have a PEM-formatted private key and a separate PEM-formatted |
| 103 | -certificate, you can concatenate the two into a single file and the | |
| 134 | +certificate, you can concatenate the two into a single file, and the | |
| 104 | 135 | individual components will still be easily accessible. |
| 105 | 136 | |
| 106 | -If you have a single file that holds both your private key and your | |
| 137 | +### <a id="cat"></a>Separate or Concatenated? | |
| 138 | + | |
| 139 | +Given a single concatenated file that holds both your private key and your | |
| 107 | 140 | cert, you can hand it off to the "[fossil server](/help?cmd=server)" |
| 108 | -command using the --cert option. Like this: | |
| 141 | +command using the `--cert` option, like this: | |
| 109 | 142 | |
| 110 | 143 | fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil |
| 111 | 144 | |
| 112 | 145 | The command above is sufficient to run a fully-encrypted web site for |
| 113 | 146 | the "myproject.fossil" Fossil repository. This command must be run as |
| 114 | 147 | root, since it wants to listen on TCP port 443, and only root processes are |
| 115 | 148 | allowed to do that. This is safe, however, since before reading any |
| 116 | -information off of the wire, Fossil will put itself inside a chroot jail | |
| 117 | -at /home/www and drop all root privileges. | |
| 118 | - | |
| 119 | -### Keeping The Cert And Private Key In Separate Files | |
| 120 | - | |
| 121 | -If you do not want to combine your cert and private key into a single | |
| 122 | -big PEM file, you can keep them separate using the --pkey option to | |
| 123 | -Fossil. | |
| 149 | +information off of the wire, Fossil will [put itself inside a chroot | |
| 150 | +jail](./chroot.md) at `/home/www` and drop all root privileges. | |
| 151 | + | |
| 152 | +This method of combining your cert and private key into a single big PEM | |
| 153 | +file carries risks, one of which is that the system administrator must | |
| 154 | +make both halves readable by the user running the Fossil server. Given | |
| 155 | +the chroot jail feature, a more secure scheme separates the halves so | |
| 156 | +that only root can read the private half, which then means that when | |
| 157 | +Fossil drops its root privileges, it becomes unable to access the | |
| 158 | +private key on disk. Fossil’s `server` feature includes the `--pkey` | |
| 159 | +option to allow for that use case: | |
| 124 | 160 | |
| 125 | 161 | fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil |
| 126 | 162 | |
| 127 | -## The ACME Protocol | |
| 163 | +[^ssattack]: ^How, you ask? Because the keys are known, they can be used | |
| 164 | + to provide signed certificates for **any** other domain. One foolish | |
| 165 | + enough to tell their OS’s TLS mechanisms to trust the signing | |
| 166 | + certificate is implicitly handing over all TLS encryption controls | |
| 167 | + to any attacker that knows they did this. Don’t do it. | |
| 168 | + | |
| 169 | +### <a id="chain"></a>Chains and Links | |
| 170 | + | |
| 171 | +The file name “`fullchain.pem`” used above is a reference to a term of | |
| 172 | +art within this world of TLS protocols and their associated X.509 | |
| 173 | +certificates. Within the simplistic scheme originally envisioned by the | |
| 174 | +creators of SSL — the predecessor to TLS — we were all expected to agree | |
| 175 | +on a single set of CA root authorities, and we would all agree to get | |
| 176 | +our certificates from one of them. The real world is more complicated: | |
| 177 | + | |
| 178 | +* The closest we have to universal acceptance of CAs is via the | |
| 179 | + [CA/Browser Forum][CAB], and even within its select membership there | |
| 180 | + is continual argument over which roots are trustworthy. (Hashing | |
| 181 | + that out is arguably this group’s key purpose.) | |
| 182 | + | |
| 183 | +* CAB’s decision regarding trustworthiness may not match that of any | |
| 184 | + given system’s administrator. There are solid, defensible reasons to | |
| 185 | + prune back the stock CA root set included with your browser, then to | |
| 186 | + augment it with ones CAB _doesn’t_ trust. | |
| 187 | + | |
| 188 | +* TLS isn’t limited to use between web browsers and public Internet | |
| 189 | + sites. Several common use cases preclude use of the process CAB | |
| 190 | + envisions, with servers able to contact Internet-based CA roots as | |
| 191 | + part of proving their identity. Different use cases demand different | |
| 192 | + CA root authority stores. | |
| 193 | + | |
| 194 | + The most common of these divergent cases are servers behind strict | |
| 195 | + firewalls and edge devices that never interact with the public | |
| 196 | + Internet. This class ranges from cheap home IoT devices to the | |
| 197 | + internal equipment managed by IT for a massive global corporation. | |
| 198 | + | |
| 199 | +Your private Fossil server is liable to fall into that last category. | |
| 200 | +This may then require that you generate a more complicated “chain” of | |
| 201 | +certificates for Fossil to use here, without which the client may not be | |
| 202 | +able to get back to a CA root it trusts. This is true regardless of | |
| 203 | +whether that client is another copy of Fossil or a web browser | |
| 204 | +traversing Fossil’s web UI, though that fact complicates matters by | |
| 205 | +allowing for multiple classes of client, each of which may have their | |
| 206 | +own rules for modifying the stock certificate scheme. | |
| 207 | + | |
| 208 | +This is distressingly common, in fact: Fossil links to OpenSSL to | |
| 209 | +provide its TLS support, but there is a good chance that your browser | |
| 210 | +uses another TLS implementation entirely. They may or may not agree on a | |
| 211 | +single CA root store. | |
| 212 | + | |
| 213 | +How you accommodate all this complexity varies by the CA and other | |
| 214 | +details. As but one example, Firefox’s “View Certificate” feature offers | |
| 215 | +_two_ ways to download a given web site’s certificate: the cert alone or | |
| 216 | +the “chain” leading back to the root. Depending on the use case, the | |
| 217 | +standalone certificate might suffice, or you might need some type of | |
| 218 | +cert chain. Complicating this is that the last link in the chain may be | |
| 219 | +left off when it is for a mutually trusted CA root, implicitly | |
| 220 | +completing the chain. | |
| 221 | + | |
| 222 | +[CAB]: https://en.wikipedia.org/wiki/CA/Browser_Forum | |
| 223 | + | |
| 224 | +## <a id="acme"></a>The ACME Protocol | |
| 225 | + | |
| 226 | +The [ACME Protocol][2] simplifies all this by automating the process of | |
| 227 | +proving to a recognized public CA that you are in control of a given | |
| 228 | +website. Without this proof, no valid CA will issue a cert for that | |
| 229 | +domain, as that allows fraudulent impersonation. | |
| 128 | 230 | |
| 129 | -The [ACME Protocol][2] is used to prove to a CA that you control a | |
| 130 | -website. CAs require proof that you control a domain before they | |
| 131 | -will issue a cert for that domain. The usual means of dealing | |
| 132 | -with ACME is to run the separate [certbot](https://certbot.eff.org) tool. | |
| 231 | +The primary implementation of ACME is [certbot], a product of the Let’s | |
| 232 | +Encrypt organization. | |
| 133 | 233 | Here is, in a nutshell, what certbot will do to obtain your cert: |
| 134 | 234 | |
| 135 | - 1. Certbot sends your "signing request" (the document that contains | |
| 235 | + 1. It sends your "signing request" (the document that contains | |
| 136 | 236 | your public key and your domain name) to the CA. |
| 137 | 237 | |
| 138 | - 2. After receiving the signing request, the CA needs to verify that you | |
| 139 | - control the domain of the cert. To do this (or, one common | |
| 140 | - way of doing this, at least) the CA sends a secret token back to | |
| 141 | - certbot through a secure backchannel, and instructs certbot to make | |
| 142 | - that token accessible on the (unencrypted, ordinary "http:") web site | |
| 143 | - for the domain in a particular file under the ".well-known" subdirectory. | |
| 144 | - | |
| 145 | - 3. Certbot puts the token where the CA requested it, then notifies the | |
| 146 | - CA that it is there. | |
| 147 | - | |
| 148 | - 4. The CA accesses the token to confirm that you do indeed control the | |
| 149 | - website. It then creates the cert and sends it back to certbot. | |
| 150 | - | |
| 151 | - 5. Certbot stores your cert and deletes the ".well-known" token. | |
| 238 | + 2. After receiving the signing request, the CA needs to verify that | |
| 239 | + you control the domain of the cert. One of several methods certbot has | |
| 240 | + for accomplishing this is to create a secret token and place it at | |
| 241 | + a well-known location, then tell the CA about it over ACME. | |
| 242 | + | |
| 243 | + 3. The CA then tries pulling that token, which if successful proves | |
| 244 | + that the requester is able to create arbitrary data on the server, | |
| 245 | + implicitly proving control over that server. This must be done | |
| 246 | + over the unencrypted HTTP protocol since TLS isn’t working yet. | |
| 247 | + | |
| 248 | + 4. If satisfied by this proof of control, the CA then creates the | |
| 249 | + keypair described above and bakes the public half into the | |
| 250 | + certificate it signs. It then sends this and the private half of | |
| 251 | + the key back to certbot. | |
| 252 | + | |
| 253 | + 5. Certbot stores these halves separately for the reasons sketched | |
| 254 | + out above. | |
| 255 | + | |
| 256 | + 6. It then deletes the secret one-time-use token it used to prove | |
| 257 | + domain control. ACME’s design precludes replay attacks. | |
| 152 | 258 | |
| 153 | 259 | In order for all of this to happen, certbot needs to be able to create |
| 154 | -a subdirectory named ".well-known", within a directory you specify, and | |
| 260 | +a subdirectory named ".well-known", within a directory you specify, | |
| 155 | 261 | then populate that subdirectory with a token file of some kind. To support |
| 156 | 262 | this, the "[fossil server](/help?cmd=server)" and |
| 157 | 263 | "[fossil http](/help?cmd=http)" commands have the --acme option. |
| 158 | -When the --acme option is specified and Fossil sees a URL where the path | |
| 264 | + | |
| 265 | +When specified, Fossil sees a URL where the path | |
| 159 | 266 | begins with ".well-known", then instead of doing its normal processing, it |
| 160 | 267 | looks for a file with that pathname and returns it to the client. If |
| 161 | 268 | the "server" or "http" command is referencing a single Fossil repository, |
| 162 | 269 | then the ".well-known" sub-directory should be in the same directory as |
| 163 | 270 | the repository file. If the "server" or "http" command are run against |
| @@ -172,9 +279,11 @@ | ||
| 172 | 279 | Then you create your public/private key pair and run certbot, giving it |
| 173 | 280 | a --webroot of /home/www. Certbot will create the sub-directory |
| 174 | 281 | named "/home/www/.well-known" and put token files there, which the CA |
| 175 | 282 | will verify. Then certbot will store your new cert in a particular file. |
| 176 | 283 | |
| 177 | -Once certbot has obtained your cert, then you can concatenate that | |
| 178 | -cert with your private key and run Fossil in SSL/TLS mode as shown above. | |
| 284 | +Once certbot has obtained your cert, you may either pass the two halves | |
| 285 | +to Fossil separately using the `--pkey` and `--cert` options described | |
| 286 | +above, or you may concatenate them and pass that via `--cert` alone. | |
| 179 | 287 | |
| 180 | 288 | [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment |
| 289 | +[certbot]: https://certbot.eff.org | |
| 181 | 290 |
| --- www/ssl-server.md | |
| +++ www/ssl-server.md | |
| @@ -12,76 +12,107 @@ | |
| 12 | |
| 13 | [0]: ./ssl.wiki |
| 14 | [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 |
| 15 | |
| 16 | Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), |
| 17 | this has been fixed. Commands like |
| 18 | |
| 19 | * "[fossil server](/help?cmd=server)" |
| 20 | * "[fossil ui](/help?cmd=ui)", and |
| 21 | * "[fossil http](/help?cmd=http)" |
| 22 | |
| 23 | now all handle server-mode SSL/TLS encryption natively. It is now possible |
| 24 | to run a secure Fossil server without having to put Fossil behind an encrypting |
| 25 | web server or reverse proxy. Hence, it is now possible to stand up a complete |
| 26 | Fossil project website on an inexpensive VPS with no added software other than |
| 27 | Fossil itself and something like [certbot](https://certbot.eff.org) for |
| 28 | obtaining a CA-signed certificate. |
| 29 | |
| 30 | ## Usage |
| 31 | |
| 32 | To put any of the Fossil server commands into SSL/TLS mode, simply |
| 33 | add the "--cert" command-line option. |
| 34 | |
| 35 | fossil ui --cert unsafe-builtin |
| 36 | |
| 37 | The --cert option is what tells Fossil to use TLS encryption. |
| 38 | Normally, the argument to --cert is the name of a file containing |
| 39 | the certificate (the "fullchain.pem" file) for the website. In this |
| 40 | example, the magic name "unsafe-builtin" is used, which causes Fossil |
| 41 | to use a self-signed cert rather than a real cert obtained from a |
| 42 | [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority) |
| 43 | or "CA". As the name implies, this self-signed cert is not secure and |
| 44 | should only be used for testing. Your web-browser will complain bitterly |
| 45 | and will refuse to display the pages using the "unsafe-builtin" cert. |
| 46 | Firefox will allow you to click an "I know the risks" button and continue. |
| 47 | Other web browsers will stubornly refuse to display the page, under the theory |
| 48 | that weak encryption is worse than no encryption at all. Continue reading |
| 49 | to see how to solve this. |
| 50 | |
| 51 | ## About Certs |
| 52 | |
| 53 | Certs are based on public-key or asymmetric cryptography. To create a cert, |
| 54 | you first create a new "key pair" consisting of a public key and a private key. |
| 55 | The public key can be freely shared with the world, but you must keep the |
| 56 | private key secret. If anyone gains access to your private key then he will be |
| 57 | able to impersonate you and break into your system. |
| 58 | |
| 59 | To obtain a cert, you send your public key and the name of the domain you |
| 60 | want to protect to a certificate authority. The CA then digitally signs |
| 61 | the combination of those two things using their own private key and sends |
| 62 | the signed combination back to you. The CA's digital signature of your |
| 63 | public key and domain name is the cert. |
| 64 | |
| 65 | SSL/TLS servers need two things in order to prove their identity to clients: |
| 66 | |
| 67 | 1. The cert that was signed by a CA |
| 68 | 2. The private key |
| 69 | |
| 70 | The SSL/TLS servers send the cert to each client, so that the client |
| 71 | can verify it. But the private key is kept strictly private and is never |
| 72 | shared with anyone. |
| 73 | |
| 74 | ## How To Tell Fossil About Your Cert And Private Key |
| 75 | |
| 76 | If you do not have your own cert and private key, you can ask Fossil |
| 77 | to use "unsafe-builtin", which is a self-signed cert that is built into |
| 78 | Fossil. This is wildly insecure, since the private key is not really private - |
| 79 | it is [in plain sight](/info/c2a7b14c3f541edb96?ln=89-116) in the Fossil |
| 80 | source tree for anybody to read. <b>Never add the private key that is |
| 81 | built into Fossil to your OS's trust store</b> as doing so will severely |
| 82 | compromise your computer. The built-in cert is only useful for testing. |
| 83 | If you want actual security, you will need to come up with your own private |
| 84 | key and cert. |
| 85 | |
| 86 | Fossil wants to read certs and public keys in the |
| 87 | [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail). |
| @@ -98,66 +129,142 @@ | |
| 98 | *base-64 encoding of the certificate* |
| 99 | -----END CERTIFICATE----- |
| 100 | |
| 101 | In both formats, text outside of the delimiters is ignored. That means |
| 102 | that if you have a PEM-formatted private key and a separate PEM-formatted |
| 103 | certificate, you can concatenate the two into a single file and the |
| 104 | individual components will still be easily accessible. |
| 105 | |
| 106 | If you have a single file that holds both your private key and your |
| 107 | cert, you can hand it off to the "[fossil server](/help?cmd=server)" |
| 108 | command using the --cert option. Like this: |
| 109 | |
| 110 | fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil |
| 111 | |
| 112 | The command above is sufficient to run a fully-encrypted web site for |
| 113 | the "myproject.fossil" Fossil repository. This command must be run as |
| 114 | root, since it wants to listen on TCP port 443, and only root processes are |
| 115 | allowed to do that. This is safe, however, since before reading any |
| 116 | information off of the wire, Fossil will put itself inside a chroot jail |
| 117 | at /home/www and drop all root privileges. |
| 118 | |
| 119 | ### Keeping The Cert And Private Key In Separate Files |
| 120 | |
| 121 | If you do not want to combine your cert and private key into a single |
| 122 | big PEM file, you can keep them separate using the --pkey option to |
| 123 | Fossil. |
| 124 | |
| 125 | fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil |
| 126 | |
| 127 | ## The ACME Protocol |
| 128 | |
| 129 | The [ACME Protocol][2] is used to prove to a CA that you control a |
| 130 | website. CAs require proof that you control a domain before they |
| 131 | will issue a cert for that domain. The usual means of dealing |
| 132 | with ACME is to run the separate [certbot](https://certbot.eff.org) tool. |
| 133 | Here is, in a nutshell, what certbot will do to obtain your cert: |
| 134 | |
| 135 | 1. Certbot sends your "signing request" (the document that contains |
| 136 | your public key and your domain name) to the CA. |
| 137 | |
| 138 | 2. After receiving the signing request, the CA needs to verify that you |
| 139 | control the domain of the cert. To do this (or, one common |
| 140 | way of doing this, at least) the CA sends a secret token back to |
| 141 | certbot through a secure backchannel, and instructs certbot to make |
| 142 | that token accessible on the (unencrypted, ordinary "http:") web site |
| 143 | for the domain in a particular file under the ".well-known" subdirectory. |
| 144 | |
| 145 | 3. Certbot puts the token where the CA requested it, then notifies the |
| 146 | CA that it is there. |
| 147 | |
| 148 | 4. The CA accesses the token to confirm that you do indeed control the |
| 149 | website. It then creates the cert and sends it back to certbot. |
| 150 | |
| 151 | 5. Certbot stores your cert and deletes the ".well-known" token. |
| 152 | |
| 153 | In order for all of this to happen, certbot needs to be able to create |
| 154 | a subdirectory named ".well-known", within a directory you specify, and |
| 155 | then populate that subdirectory with a token file of some kind. To support |
| 156 | this, the "[fossil server](/help?cmd=server)" and |
| 157 | "[fossil http](/help?cmd=http)" commands have the --acme option. |
| 158 | When the --acme option is specified and Fossil sees a URL where the path |
| 159 | begins with ".well-known", then instead of doing its normal processing, it |
| 160 | looks for a file with that pathname and returns it to the client. If |
| 161 | the "server" or "http" command is referencing a single Fossil repository, |
| 162 | then the ".well-known" sub-directory should be in the same directory as |
| 163 | the repository file. If the "server" or "http" command are run against |
| @@ -172,9 +279,11 @@ | |
| 172 | Then you create your public/private key pair and run certbot, giving it |
| 173 | a --webroot of /home/www. Certbot will create the sub-directory |
| 174 | named "/home/www/.well-known" and put token files there, which the CA |
| 175 | will verify. Then certbot will store your new cert in a particular file. |
| 176 | |
| 177 | Once certbot has obtained your cert, then you can concatenate that |
| 178 | cert with your private key and run Fossil in SSL/TLS mode as shown above. |
| 179 | |
| 180 | [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment |
| 181 |
| --- www/ssl-server.md | |
| +++ www/ssl-server.md | |
| @@ -12,76 +12,107 @@ | |
| 12 | |
| 13 | [0]: ./ssl.wiki |
| 14 | [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 |
| 15 | |
| 16 | Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), |
| 17 | Fossil servers are now able to converse directly over TLS. Commands like |
| 18 | |
| 19 | * "[fossil server](/help?cmd=server)" |
| 20 | * "[fossil ui](/help?cmd=ui)", and |
| 21 | * "[fossil http](/help?cmd=http)" |
| 22 | |
| 23 | may now handle the encryption natively when suitably configured, without |
| 24 | requiring a third-party proxy layer. |
| 25 | |
| 26 | ## <a id="usage"></a>Usage |
| 27 | |
| 28 | To put any of the Fossil server commands into SSL/TLS mode, simply |
| 29 | add the "`--cert`" command-line option: |
| 30 | |
| 31 | fossil ui --cert unsafe-builtin |
| 32 | |
| 33 | Here, we are passing the magic name "unsafe-builtin" to cause Fossil to |
| 34 | use a [hard-coded self-signed cert][hcssc] rather than one obtained from |
| 35 | a recognized [Certificate Authority][CA], or "CA". |
| 36 | |
| 37 | As the name implies, this self-signed cert is _not secure_ and should |
| 38 | only be used for testing. Your web browser is likely to complain |
| 39 | bitterly about it and will refuse to display the pages using the |
| 40 | "unsafe-builtin" cert until you placate it. The complexity of the |
| 41 | ceremony demanded depends on how paranoid your browser’s creators have |
| 42 | decided to be. It may require as little as clicking a single big "I know |
| 43 | the risks" type of button, or it may require a sequence be several |
| 44 | clicks designed to discourage the “yes, yes, just let me do the thing” |
| 45 | crowd lest they run themselves into trouble by disregarding well-meant |
| 46 | warnings. |
| 47 | |
| 48 | Our purpose here is to show you an alternate path that will avoid the |
| 49 | issue entirely, not weigh in on which browser handles self-signed |
| 50 | certificates best. |
| 51 | |
| 52 | [CA]: https://en.wikipedia.org/wiki/Certificate_authority |
| 53 | [hcssc]: /info/c2a7b14c3f541edb96?ln=89-116 |
| 54 | |
| 55 | ## <a id="about"></a>About Certs |
| 56 | |
| 57 | The X.509 certificate system used by browsers to secure TLS connections |
| 58 | is based on asymmetric public-key cryptography. The methods for |
| 59 | obtaining one vary widely, with a resulting tradeoff we may summarize as |
| 60 | trustworthiness versus convenience, the latter characteristic falling as |
| 61 | the former rises.(^No strict correlation exists. CAs have invented |
| 62 | highly inconvenient certification schemes that offer little additional |
| 63 | real-world trustworthiness. Extreme cases along this axis may be fairly |
| 64 | characterized as [security theater][st]. We focus in this document on |
| 65 | well-balanced trade-offs between decreasing convenience and useful |
| 66 | levels of trustworthiness gained thereby.) |
| 67 | |
| 68 | The self-signed method demonstrated above offers approximately zero |
| 69 | trustworthiness, though not zero _value_ since it does still provide |
| 70 | connection encryption. |
| 71 | |
| 72 | More trustworthy methods are necessarily less convenient. One such is to |
| 73 | send your public key and the name of the domain you want to protect to a |
| 74 | recognized CA, which then performs one or more tests to convince itself |
| 75 | that the requester is in control of that domain. If the CA’s tests all |
| 76 | pass, it produces an X.509 certificate bound to that domain, which |
| 77 | includes assorted other information under the CA’s digital signature |
| 78 | attesting to the validity of the document’s contents. The result is sent |
| 79 | back to the requester, which may then use it to transitively attest to |
| 80 | these tests’ success: presuming one cannot fake the type of signature |
| 81 | used, the document must have been signed by the trusted, recognized CA. |
| 82 | |
| 83 | There is one element of the assorted information included with a |
| 84 | certificate that is neither supplied by the requester nor rubber-stamped |
| 85 | on it in passing by the CA. It also generates a one-time key pair and |
| 86 | stores the public half in the certificate. The cryptosystem this keypair |
| 87 | is intended to work with varies both by the CA and by time, as older |
| 88 | systems become obsolete. Details aside, the CA then puts this matching |
| 89 | private half of the key in a separate file, often encrypted under a |
| 90 | separate cryptosystem for security. |
| 91 | |
| 92 | SSL/TLS servers need both resulting halves to make these attestations, |
| 93 | but they send only the public half to the client when establishing the |
| 94 | connection. The client then makes its own checks to determine whether it |
| 95 | trusts the attestations being made. |
| 96 | |
| 97 | A properly written and administered server never releases the private |
| 98 | key to anyone. Ideally, it goes directly from the CA to the requesting |
| 99 | server and never moves from there; then when it expires, the server |
| 100 | deletes it permanently. |
| 101 | |
| 102 | [st]: https://en.wikipedia.org/wiki/Security_theater |
| 103 | |
| 104 | ## <a id="startup"></a>How To Tell Fossil About Your Cert And Private Key |
| 105 | |
| 106 | As we saw [above](#usage), |
| 107 | if you do not have your own cert and private key, you can ask Fossil |
| 108 | to use "unsafe-builtin", which is a self-signed cert that is built into |
| 109 | Fossil. This is wildly insecure, since the private key is not really private; |
| 110 | it is [in plain sight][hcssc] in the Fossil |
| 111 | source tree for anybody to read. <b>Never add the private key that is |
| 112 | built into Fossil to your OS's trust store</b> as doing so will severely |
| 113 | compromise your computer.[^ssattack] This built-in cert is only useful for testing. |
| 114 | If you want actual security, you will need to come up with your own private |
| 115 | key and cert. |
| 116 | |
| 117 | Fossil wants to read certs and public keys in the |
| 118 | [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail). |
| @@ -98,66 +129,142 @@ | |
| 129 | *base-64 encoding of the certificate* |
| 130 | -----END CERTIFICATE----- |
| 131 | |
| 132 | In both formats, text outside of the delimiters is ignored. That means |
| 133 | that if you have a PEM-formatted private key and a separate PEM-formatted |
| 134 | certificate, you can concatenate the two into a single file, and the |
| 135 | individual components will still be easily accessible. |
| 136 | |
| 137 | ### <a id="cat"></a>Separate or Concatenated? |
| 138 | |
| 139 | Given a single concatenated file that holds both your private key and your |
| 140 | cert, you can hand it off to the "[fossil server](/help?cmd=server)" |
| 141 | command using the `--cert` option, like this: |
| 142 | |
| 143 | fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil |
| 144 | |
| 145 | The command above is sufficient to run a fully-encrypted web site for |
| 146 | the "myproject.fossil" Fossil repository. This command must be run as |
| 147 | root, since it wants to listen on TCP port 443, and only root processes are |
| 148 | allowed to do that. This is safe, however, since before reading any |
| 149 | information off of the wire, Fossil will [put itself inside a chroot |
| 150 | jail](./chroot.md) at `/home/www` and drop all root privileges. |
| 151 | |
| 152 | This method of combining your cert and private key into a single big PEM |
| 153 | file carries risks, one of which is that the system administrator must |
| 154 | make both halves readable by the user running the Fossil server. Given |
| 155 | the chroot jail feature, a more secure scheme separates the halves so |
| 156 | that only root can read the private half, which then means that when |
| 157 | Fossil drops its root privileges, it becomes unable to access the |
| 158 | private key on disk. Fossil’s `server` feature includes the `--pkey` |
| 159 | option to allow for that use case: |
| 160 | |
| 161 | fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil |
| 162 | |
| 163 | [^ssattack]: ^How, you ask? Because the keys are known, they can be used |
| 164 | to provide signed certificates for **any** other domain. One foolish |
| 165 | enough to tell their OS’s TLS mechanisms to trust the signing |
| 166 | certificate is implicitly handing over all TLS encryption controls |
| 167 | to any attacker that knows they did this. Don’t do it. |
| 168 | |
| 169 | ### <a id="chain"></a>Chains and Links |
| 170 | |
| 171 | The file name “`fullchain.pem`” used above is a reference to a term of |
| 172 | art within this world of TLS protocols and their associated X.509 |
| 173 | certificates. Within the simplistic scheme originally envisioned by the |
| 174 | creators of SSL — the predecessor to TLS — we were all expected to agree |
| 175 | on a single set of CA root authorities, and we would all agree to get |
| 176 | our certificates from one of them. The real world is more complicated: |
| 177 | |
| 178 | * The closest we have to universal acceptance of CAs is via the |
| 179 | [CA/Browser Forum][CAB], and even within its select membership there |
| 180 | is continual argument over which roots are trustworthy. (Hashing |
| 181 | that out is arguably this group’s key purpose.) |
| 182 | |
| 183 | * CAB’s decision regarding trustworthiness may not match that of any |
| 184 | given system’s administrator. There are solid, defensible reasons to |
| 185 | prune back the stock CA root set included with your browser, then to |
| 186 | augment it with ones CAB _doesn’t_ trust. |
| 187 | |
| 188 | * TLS isn’t limited to use between web browsers and public Internet |
| 189 | sites. Several common use cases preclude use of the process CAB |
| 190 | envisions, with servers able to contact Internet-based CA roots as |
| 191 | part of proving their identity. Different use cases demand different |
| 192 | CA root authority stores. |
| 193 | |
| 194 | The most common of these divergent cases are servers behind strict |
| 195 | firewalls and edge devices that never interact with the public |
| 196 | Internet. This class ranges from cheap home IoT devices to the |
| 197 | internal equipment managed by IT for a massive global corporation. |
| 198 | |
| 199 | Your private Fossil server is liable to fall into that last category. |
| 200 | This may then require that you generate a more complicated “chain” of |
| 201 | certificates for Fossil to use here, without which the client may not be |
| 202 | able to get back to a CA root it trusts. This is true regardless of |
| 203 | whether that client is another copy of Fossil or a web browser |
| 204 | traversing Fossil’s web UI, though that fact complicates matters by |
| 205 | allowing for multiple classes of client, each of which may have their |
| 206 | own rules for modifying the stock certificate scheme. |
| 207 | |
| 208 | This is distressingly common, in fact: Fossil links to OpenSSL to |
| 209 | provide its TLS support, but there is a good chance that your browser |
| 210 | uses another TLS implementation entirely. They may or may not agree on a |
| 211 | single CA root store. |
| 212 | |
| 213 | How you accommodate all this complexity varies by the CA and other |
| 214 | details. As but one example, Firefox’s “View Certificate” feature offers |
| 215 | _two_ ways to download a given web site’s certificate: the cert alone or |
| 216 | the “chain” leading back to the root. Depending on the use case, the |
| 217 | standalone certificate might suffice, or you might need some type of |
| 218 | cert chain. Complicating this is that the last link in the chain may be |
| 219 | left off when it is for a mutually trusted CA root, implicitly |
| 220 | completing the chain. |
| 221 | |
| 222 | [CAB]: https://en.wikipedia.org/wiki/CA/Browser_Forum |
| 223 | |
| 224 | ## <a id="acme"></a>The ACME Protocol |
| 225 | |
| 226 | The [ACME Protocol][2] simplifies all this by automating the process of |
| 227 | proving to a recognized public CA that you are in control of a given |
| 228 | website. Without this proof, no valid CA will issue a cert for that |
| 229 | domain, as that allows fraudulent impersonation. |
| 230 | |
| 231 | The primary implementation of ACME is [certbot], a product of the Let’s |
| 232 | Encrypt organization. |
| 233 | Here is, in a nutshell, what certbot will do to obtain your cert: |
| 234 | |
| 235 | 1. It sends your "signing request" (the document that contains |
| 236 | your public key and your domain name) to the CA. |
| 237 | |
| 238 | 2. After receiving the signing request, the CA needs to verify that |
| 239 | you control the domain of the cert. One of several methods certbot has |
| 240 | for accomplishing this is to create a secret token and place it at |
| 241 | a well-known location, then tell the CA about it over ACME. |
| 242 | |
| 243 | 3. The CA then tries pulling that token, which if successful proves |
| 244 | that the requester is able to create arbitrary data on the server, |
| 245 | implicitly proving control over that server. This must be done |
| 246 | over the unencrypted HTTP protocol since TLS isn’t working yet. |
| 247 | |
| 248 | 4. If satisfied by this proof of control, the CA then creates the |
| 249 | keypair described above and bakes the public half into the |
| 250 | certificate it signs. It then sends this and the private half of |
| 251 | the key back to certbot. |
| 252 | |
| 253 | 5. Certbot stores these halves separately for the reasons sketched |
| 254 | out above. |
| 255 | |
| 256 | 6. It then deletes the secret one-time-use token it used to prove |
| 257 | domain control. ACME’s design precludes replay attacks. |
| 258 | |
| 259 | In order for all of this to happen, certbot needs to be able to create |
| 260 | a subdirectory named ".well-known", within a directory you specify, |
| 261 | then populate that subdirectory with a token file of some kind. To support |
| 262 | this, the "[fossil server](/help?cmd=server)" and |
| 263 | "[fossil http](/help?cmd=http)" commands have the --acme option. |
| 264 | |
| 265 | When specified, Fossil sees a URL where the path |
| 266 | begins with ".well-known", then instead of doing its normal processing, it |
| 267 | looks for a file with that pathname and returns it to the client. If |
| 268 | the "server" or "http" command is referencing a single Fossil repository, |
| 269 | then the ".well-known" sub-directory should be in the same directory as |
| 270 | the repository file. If the "server" or "http" command are run against |
| @@ -172,9 +279,11 @@ | |
| 279 | Then you create your public/private key pair and run certbot, giving it |
| 280 | a --webroot of /home/www. Certbot will create the sub-directory |
| 281 | named "/home/www/.well-known" and put token files there, which the CA |
| 282 | will verify. Then certbot will store your new cert in a particular file. |
| 283 | |
| 284 | Once certbot has obtained your cert, you may either pass the two halves |
| 285 | to Fossil separately using the `--pkey` and `--cert` options described |
| 286 | above, or you may concatenate them and pass that via `--cert` alone. |
| 287 | |
| 288 | [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment |
| 289 | [certbot]: https://certbot.eff.org |
| 290 |
+8
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -789,10 +789,18 @@ | ||
| 789 | 789 | a successful commit. This instructs the server to release |
| 790 | 790 | any lock on any check-in previously held by that client. |
| 791 | 791 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 792 | 792 | that might arise if a check-in is aborted and then restarted |
| 793 | 793 | on a branch. |
| 794 | + | |
| 795 | +<li><b>req-clusters</b> A client sends the "req-clusters" pragma | |
| 796 | +to the server to ask the server to reply with "igot" cards for | |
| 797 | +every [./fileformat.wiki#cluster|cluster artifact] that it holds. The | |
| 798 | +client typically does this when it thinks that it might be attempting | |
| 799 | +to pull a long chain of cluster artifacts. Sending the artifacts | |
| 800 | +all at once can dramatically reduce the number of round trip | |
| 801 | +messages needed to complete the synchronization. | |
| 794 | 802 | </ol> |
| 795 | 803 | |
| 796 | 804 | <h3 id="comment">3.12 Comment Cards</h3> |
| 797 | 805 | |
| 798 | 806 | Any card that begins with "#" (ASCII 0x23) is a comment card and |
| 799 | 807 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -789,10 +789,18 @@ | |
| 789 | a successful commit. This instructs the server to release |
| 790 | any lock on any check-in previously held by that client. |
| 791 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 792 | that might arise if a check-in is aborted and then restarted |
| 793 | on a branch. |
| 794 | </ol> |
| 795 | |
| 796 | <h3 id="comment">3.12 Comment Cards</h3> |
| 797 | |
| 798 | Any card that begins with "#" (ASCII 0x23) is a comment card and |
| 799 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -789,10 +789,18 @@ | |
| 789 | a successful commit. This instructs the server to release |
| 790 | any lock on any check-in previously held by that client. |
| 791 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 792 | that might arise if a check-in is aborted and then restarted |
| 793 | on a branch. |
| 794 | |
| 795 | <li><b>req-clusters</b> A client sends the "req-clusters" pragma |
| 796 | to the server to ask the server to reply with "igot" cards for |
| 797 | every [./fileformat.wiki#cluster|cluster artifact] that it holds. The |
| 798 | client typically does this when it thinks that it might be attempting |
| 799 | to pull a long chain of cluster artifacts. Sending the artifacts |
| 800 | all at once can dramatically reduce the number of round trip |
| 801 | messages needed to complete the synchronization. |
| 802 | </ol> |
| 803 | |
| 804 | <h3 id="comment">3.12 Comment Cards</h3> |
| 805 | |
| 806 | Any card that begins with "#" (ASCII 0x23) is a comment card and |
| 807 |