Fossil SCM

Merge with trunk to get the latest web UI fixes.

florian 2022-08-14 08:54 timeline-keyboard-navigation merge
Commit e317872280cbbb9f36bbd8099a7cb0f5beb28716c95be8de579ac01039eec1aa
+44 -26
--- Dockerfile
+++ Dockerfile
@@ -1,26 +1,44 @@
1
-###
2
-# Dockerfile for Fossil
3
-###
4
-FROM fedora:29
5
-
6
-### Now install some additional parts we will need for the build
7
-RUN dnf update -y && dnf install -y gcc make tcl tcl-devel zlib-devel openssl-devel tar && dnf clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil
8
-
9
-### If you want to build "trunk", change the next line accordingly.
10
-ENV FOSSIL_INSTALL_VERSION release
11
-
12
-RUN curl "https://fossil-scm.org/home/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx
13
-RUN cd fossil-src && ./configure --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl=1 --with-tcl-stubs --with-tcl-private-stubs
14
-RUN cd fossil-src/src && mv main.c main.c.orig && sed s/\"now\"/0/ <main.c.orig >main.c
15
-RUN cd fossil-src && make && strip fossil && cp fossil /usr/bin && cd .. && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil
16
-
17
-### Build is done, remove modules no longer needed
18
-RUN dnf remove -y gcc make zlib-devel tcl-devel openssl-devel tar && dnf clean all
19
-
20
-USER fossil
21
-
22
-ENV HOME /opt/fossil
23
-
24
-EXPOSE 8080
25
-
26
-CMD ["/usr/bin/fossil", "server", "--create", "--user", "admin", "/opt/fossil/repository.fossil"]
1
+# STAGE 1: Build a static Fossil binary atop Alpine Linux
2
+
3
+# Avoid the temptation to swap the wget call below out for an ADD URL
4
+# directive. The URL is fixed for a given release tag, which triggers
5
+# Docker's caching behavior, causing it to reuse that version as long
6
+# as it remains in the cache. We prefer to rely on the caching of the
7
+# server instance on fossil-scm.org, which will keep these trunk
8
+# tarballs around until the next trunk commit.
9
+
10
+FROM alpine:latest AS builder
11
+WORKDIR /tmp
12
+RUN apk update \
13
+ && apk upgrade --no-cache \
14
+ && apk add --no-cache \
15
+ busybox-static gcc make \
16
+ musl-dev \
17
+ openssl-dev openssl-libs-static \
18
+ zlib-dev zlib-static \
19
+ && wget -O - https://fossil-scm.org/home/tarball/src | tar -xz \
20
+ && src/configure --static CFLAGS='-Os -s' \
21
+ && make -j
22
+
23
+# STAGE 2: Pare that back to the bare essentials.
24
+
25
+FROM scratch
26
+WORKDIR /jail
27
+COPY --from=builder /tmp/fossil /jail/bin/
28
+COPY --from=builder /bin/busybox.static /bin/busybox
29
+RUN [ "/bin/busybox", "--install", "/bin" ]
30
+RUN mkdir -m 700 dev museum \
31
+ && mknod -m 600 dev/null c 1 3 \
32
+ && mknod -m 600 dev/urandom c 1 9
33
+
34
+# Now we can run the stripped-down environment in a chroot jail, while
35
+# leaving open the option to debug it live via the Busybox shell.
36
+
37
+EXPOSE 8080/tcp
38
+CMD [ \
39
+ "bin/fossil", "server", \
40
+ "--chroot", "/jail", \
41
+ "--create", \
42
+ "--jsmode", "bundled", \
43
+ "--user", "admin", \
44
+ "museum/repo.fossil"]
2745
--- Dockerfile
+++ Dockerfile
@@ -1,26 +1,44 @@
1 ###
2 # Dockerfile for Fossil
3 ###
4 FROM fedora:29
5
6 ### Now install some additional parts we will need for the build
7 RUN dnf update -y && dnf install -y gcc make tcl tcl-devel zlib-devel openssl-devel tar && dnf clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil
8
9 ### If you want to build "trunk", change the next line accordingly.
10 ENV FOSSIL_INSTALL_VERSION release
11
12 RUN curl "https://fossil-scm.org/home/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx
13 RUN cd fossil-src && ./configure --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl=1 --with-tcl-stubs --with-tcl-private-stubs
14 RUN cd fossil-src/src && mv main.c main.c.orig && sed s/\"now\"/0/ <main.c.orig >main.c
15 RUN cd fossil-src && make && strip fossil && cp fossil /usr/bin && cd .. && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil
16
17 ### Build is done, remove modules no longer needed
18 RUN dnf remove -y gcc make zlib-devel tcl-devel openssl-devel tar && dnf clean all
19
20 USER fossil
21
22 ENV HOME /opt/fossil
23
24 EXPOSE 8080
25
26 CMD ["/usr/bin/fossil", "server", "--create", "--user", "admin", "/opt/fossil/repository.fossil"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
--- Dockerfile
+++ Dockerfile
@@ -1,26 +1,44 @@
1 # STAGE 1: Build a static Fossil binary atop Alpine Linux
2
3 # Avoid the temptation to swap the wget call below out for an ADD URL
4 # directive. The URL is fixed for a given release tag, which triggers
5 # Docker's caching behavior, causing it to reuse that version as long
6 # as it remains in the cache. We prefer to rely on the caching of the
7 # server instance on fossil-scm.org, which will keep these trunk
8 # tarballs around until the next trunk commit.
9
10 FROM alpine:latest AS builder
11 WORKDIR /tmp
12 RUN apk update \
13 && apk upgrade --no-cache \
14 && apk add --no-cache \
15 busybox-static gcc make \
16 musl-dev \
17 openssl-dev openssl-libs-static \
18 zlib-dev zlib-static \
19 && wget -O - https://fossil-scm.org/home/tarball/src | tar -xz \
20 && src/configure --static CFLAGS='-Os -s' \
21 && make -j
22
23 # STAGE 2: Pare that back to the bare essentials.
24
25 FROM scratch
26 WORKDIR /jail
27 COPY --from=builder /tmp/fossil /jail/bin/
28 COPY --from=builder /bin/busybox.static /bin/busybox
29 RUN [ "/bin/busybox", "--install", "/bin" ]
30 RUN mkdir -m 700 dev museum \
31 && mknod -m 600 dev/null c 1 3 \
32 && mknod -m 600 dev/urandom c 1 9
33
34 # Now we can run the stripped-down environment in a chroot jail, while
35 # leaving open the option to debug it live via the Busybox shell.
36
37 EXPOSE 8080/tcp
38 CMD [ \
39 "bin/fossil", "server", \
40 "--chroot", "/jail", \
41 "--create", \
42 "--jsmode", "bundled", \
43 "--user", "admin", \
44 "museum/repo.fossil"]
45
+34 -8
--- src/accordion.js
+++ src/accordion.js
@@ -1,12 +1,34 @@
1
-/* Attach appropriate javascript to each ".accordion" button so that
2
-** it expands and contracts when clicked.
3
-** The uncompressed source code for the SVG icons can be found on the
4
-** wiki page "branch/accordion-experiments" in the Fossil repository.
1
+/*
2
+** Attach appropriate javascript to each ".accordion" button so that it expands
3
+** and contracts when clicked.
4
+**
5
+** The uncompressed source code for the SVG icons can be found on the wiki page
6
+** "branch/accordion-experiments" in the Fossil repository.
7
+**
8
+** Implementation notes:
9
+**
10
+** The `maxHeight' CSS property is quite restrictive for vertical resizing of
11
+** elements, especially for dynamic-content areas like the diff panels. That's
12
+** why `maxHeight' is set only during animation, to prevent truncated elements.
13
+** (The diff panels may get truncated right after page loading, and other
14
+** elements may get truncated when resizing the browser window to a smaller
15
+** width, causing vertical growth.)
16
+**
17
+** Another problem is that `scrollHeight' used to calculate the expanded height
18
+** while still in the contracted state may return values with small errors on
19
+** some browsers, especially for large elements, presumably due to omitting the
20
+** space required by the vertical scrollbar that may become necessary, causing
21
+** additional horizontal shrinking and consequently more vertical growth than
22
+** calculated. That's why setting `maxHeight' to `scrollHeight' is considered
23
+** "good enough" only during animation, but cleared afterwards.
24
+**
25
+** https://fossil-scm.org/forum/forumpost/66d7075f40
26
+** https://fossil-scm.org/home/timeline?r=accordion-fix
527
*/
628
var acc_svgdata = ["data:image/svg+xml,"+
7
- "%3Csvg xmlns='http:"+"/"+"/www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+
29
+ "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+
830
"%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+
931
"%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+
1032
"%3Cpath style='fill:rgb(64,64,64)' d='M13,13H3V3h10v10z'/%3E"+
1133
"%3Cpath style='fill:rgb(248,248,248)' d='M12,12H4V4h8v8z'/%3E"+
1234
"%3Cpath style='fill:rgb(80,128,208)' d='", "'/%3E%3C/svg%3E",
@@ -19,17 +41,21 @@
1941
a[i].insertBefore(img,a[i].firstChild);
2042
img = document.createElement("img");
2143
img.src = acc_svgdata[0]+acc_svgdata[3]+acc_svgdata[1];
2244
img.className = "accordion_btn accordion_btn_minus";
2345
a[i].insertBefore(img,a[i].firstChild);
24
- var p = a[i].nextElementSibling;
25
- p.style.maxHeight = p.scrollHeight + "px";
2646
a[i].addEventListener("click",function(){
2747
var x = this.nextElementSibling;
2848
if( this.classList.contains("accordion_closed") ){
2949
x.style.maxHeight = x.scrollHeight + "px";
50
+ setTimeout(function(){
51
+ x.style.maxHeight = "";
52
+ },250); // default.css: .accordion_panel { transition-duration }
3053
}else{
31
- x.style.maxHeight = "0";
54
+ x.style.maxHeight = x.scrollHeight + "px";
55
+ setTimeout(function(){
56
+ x.style.maxHeight = "0";
57
+ },1);
3258
}
3359
this.classList.toggle("accordion_closed");
3460
});
3561
}
3662
--- src/accordion.js
+++ src/accordion.js
@@ -1,12 +1,34 @@
1 /* Attach appropriate javascript to each ".accordion" button so that
2 ** it expands and contracts when clicked.
3 ** The uncompressed source code for the SVG icons can be found on the
4 ** wiki page "branch/accordion-experiments" in the Fossil repository.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5 */
6 var acc_svgdata = ["data:image/svg+xml,"+
7 "%3Csvg xmlns='http:"+"/"+"/www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+
8 "%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+
9 "%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+
10 "%3Cpath style='fill:rgb(64,64,64)' d='M13,13H3V3h10v10z'/%3E"+
11 "%3Cpath style='fill:rgb(248,248,248)' d='M12,12H4V4h8v8z'/%3E"+
12 "%3Cpath style='fill:rgb(80,128,208)' d='", "'/%3E%3C/svg%3E",
@@ -19,17 +41,21 @@
19 a[i].insertBefore(img,a[i].firstChild);
20 img = document.createElement("img");
21 img.src = acc_svgdata[0]+acc_svgdata[3]+acc_svgdata[1];
22 img.className = "accordion_btn accordion_btn_minus";
23 a[i].insertBefore(img,a[i].firstChild);
24 var p = a[i].nextElementSibling;
25 p.style.maxHeight = p.scrollHeight + "px";
26 a[i].addEventListener("click",function(){
27 var x = this.nextElementSibling;
28 if( this.classList.contains("accordion_closed") ){
29 x.style.maxHeight = x.scrollHeight + "px";
 
 
 
30 }else{
31 x.style.maxHeight = "0";
 
 
 
32 }
33 this.classList.toggle("accordion_closed");
34 });
35 }
36
--- src/accordion.js
+++ src/accordion.js
@@ -1,12 +1,34 @@
1 /*
2 ** Attach appropriate javascript to each ".accordion" button so that it expands
3 ** and contracts when clicked.
4 **
5 ** The uncompressed source code for the SVG icons can be found on the wiki page
6 ** "branch/accordion-experiments" in the Fossil repository.
7 **
8 ** Implementation notes:
9 **
10 ** The `maxHeight' CSS property is quite restrictive for vertical resizing of
11 ** elements, especially for dynamic-content areas like the diff panels. That's
12 ** why `maxHeight' is set only during animation, to prevent truncated elements.
13 ** (The diff panels may get truncated right after page loading, and other
14 ** elements may get truncated when resizing the browser window to a smaller
15 ** width, causing vertical growth.)
16 **
17 ** Another problem is that `scrollHeight' used to calculate the expanded height
18 ** while still in the contracted state may return values with small errors on
19 ** some browsers, especially for large elements, presumably due to omitting the
20 ** space required by the vertical scrollbar that may become necessary, causing
21 ** additional horizontal shrinking and consequently more vertical growth than
22 ** calculated. That's why setting `maxHeight' to `scrollHeight' is considered
23 ** "good enough" only during animation, but cleared afterwards.
24 **
25 ** https://fossil-scm.org/forum/forumpost/66d7075f40
26 ** https://fossil-scm.org/home/timeline?r=accordion-fix
27 */
28 var acc_svgdata = ["data:image/svg+xml,"+
29 "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E"+
30 "%3Cpath style='fill:black;opacity:0' d='M16,16H0V0h16v16z'/%3E"+
31 "%3Cpath style='fill:rgb(240,240,240)' d='M14,14H2V2h12v12z'/%3E"+
32 "%3Cpath style='fill:rgb(64,64,64)' d='M13,13H3V3h10v10z'/%3E"+
33 "%3Cpath style='fill:rgb(248,248,248)' d='M12,12H4V4h8v8z'/%3E"+
34 "%3Cpath style='fill:rgb(80,128,208)' d='", "'/%3E%3C/svg%3E",
@@ -19,17 +41,21 @@
41 a[i].insertBefore(img,a[i].firstChild);
42 img = document.createElement("img");
43 img.src = acc_svgdata[0]+acc_svgdata[3]+acc_svgdata[1];
44 img.className = "accordion_btn accordion_btn_minus";
45 a[i].insertBefore(img,a[i].firstChild);
 
 
46 a[i].addEventListener("click",function(){
47 var x = this.nextElementSibling;
48 if( this.classList.contains("accordion_closed") ){
49 x.style.maxHeight = x.scrollHeight + "px";
50 setTimeout(function(){
51 x.style.maxHeight = "";
52 },250); // default.css: .accordion_panel { transition-duration }
53 }else{
54 x.style.maxHeight = x.scrollHeight + "px";
55 setTimeout(function(){
56 x.style.maxHeight = "0";
57 },1);
58 }
59 this.classList.toggle("accordion_closed");
60 });
61 }
62
+55 -10
--- src/branch.c
+++ src/branch.c
@@ -289,31 +289,51 @@
289289
** Prepare a query that will list branches.
290290
**
291291
** If (which<0) then the query pulls only closed branches. If
292292
** (which>0) then the query pulls all (closed and opened)
293293
** branches. Else the query pulls currently-opened branches.
294
+**
295
+** If the BRL_ORDERBY_MTIME flag is set and nLimitMRU ("Limit Most Recently Used
296
+** style") is a non-zero number, the result is limited to nLimitMRU entries, and
297
+** the BRL_REVERSE flag is applied in an outer query after processing the limit,
298
+** so that it's possible to generate short lists with the most recently modified
299
+** branches sorted chronologically in either direction, as does the "branch lsh"
300
+** command.
301
+** For other cases, the outer query is also generated, but works as a no-op. The
302
+** code to build the outer query is marked with *//* OUTER QUERY *//* comments.
294303
*/
295
-void branch_prepare_list_query(Stmt *pQuery, int brFlags, const char *zBrNameGlob){
304
+void branch_prepare_list_query(
305
+ Stmt *pQuery,
306
+ int brFlags,
307
+ const char *zBrNameGlob,
308
+ int nLimitMRU
309
+){
296310
Blob sql;
297311
blob_init(&sql, 0, 0);
298312
brlist_create_temp_table();
313
+ /* Ignore nLimitMRU if no chronological sort requested. */
314
+ if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0;
315
+ /* Undocumented: invert negative values for nLimitMRU, so that command-line
316
+ ** arguments similar to `head -5' with "option numbers" are possible. */
317
+ if( nLimitMRU<0 ) nLimitMRU = -nLimitMRU;
318
+ blob_append_sql(&sql,"SELECT name, isprivate FROM ("); /* OUTER QUERY */
299319
switch( brFlags & BRL_OPEN_CLOSED_MASK ){
300320
case BRL_CLOSED_ONLY: {
301321
blob_append_sql(&sql,
302
- "SELECT name, isprivate FROM tmp_brlist WHERE isclosed"
322
+ "SELECT name, isprivate, mtime FROM tmp_brlist WHERE isclosed"
303323
);
304324
break;
305325
}
306326
case BRL_BOTH: {
307327
blob_append_sql(&sql,
308
- "SELECT name, isprivate FROM tmp_brlist WHERE 1"
328
+ "SELECT name, isprivate, mtime FROM tmp_brlist WHERE 1"
309329
);
310330
break;
311331
}
312332
case BRL_OPEN_ONLY: {
313333
blob_append_sql(&sql,
314
- "SELECT name, isprivate FROM tmp_brlist WHERE NOT isclosed"
334
+ "SELECT name, isprivate, mtime FROM tmp_brlist WHERE NOT isclosed"
315335
);
316336
break;
317337
}
318338
}
319339
if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
@@ -321,13 +341,20 @@
321341
if( brFlags & BRL_ORDERBY_MTIME ){
322342
blob_append_sql(&sql, " ORDER BY -mtime");
323343
}else{
324344
blob_append_sql(&sql, " ORDER BY name COLLATE nocase");
325345
}
326
- if( brFlags & BRL_REVERSE ){
346
+ if( brFlags & BRL_REVERSE && !nLimitMRU ){
327347
blob_append_sql(&sql," DESC");
328348
}
349
+ if( nLimitMRU ){
350
+ blob_append_sql(&sql," LIMIT %d",nLimitMRU);
351
+ }
352
+ blob_append_sql(&sql,")"); /* OUTER QUERY */
353
+ if( brFlags & BRL_REVERSE && nLimitMRU ){
354
+ blob_append_sql(&sql," ORDER BY mtime"); /* OUTER QUERY */
355
+ }
329356
db_prepare_blob(pQuery, &sql);
330357
blob_reset(&sql);
331358
}
332359
333360
/*
@@ -585,10 +612,11 @@
585612
** > fossil branch info BRANCH-NAME
586613
**
587614
** Print information about a branch
588615
**
589616
** > fossil branch list|ls ?OPTIONS? ?GLOB?
617
+** > fossil branch lsh ?OPTIONS? ?LIMIT?
590618
**
591619
** List all branches. Options:
592620
** -a|--all List all branches. Default show only open branches
593621
** -c|--closed List closed branches.
594622
** -p List only private branches.
@@ -597,10 +625,15 @@
597625
**
598626
** The current branch is marked with an asterisk. Private branches are
599627
** marked with a hash sign.
600628
**
601629
** If GLOB is given, show only branches matching the pattern.
630
+**
631
+** The "lsh" variant of this subcommand shows recently changed branches,
632
+** and accepts an optional LIMIT argument (defaults to 5) to cap output,
633
+** but no GLOB argument. All other options are supported, with -t being
634
+** an implied no-op.
602635
**
603636
** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
604637
**
605638
** Create a new branch BRANCH-NAME off of check-in BASIS.
606639
** Supported options for this subcommand include:
@@ -651,29 +684,41 @@
651684
"SELECT datetime(mtime,toLocal()) FROM event"
652685
" WHERE objid=%d", rid);
653686
fossil_print("%s: open as of %s on %.16s\n", zBrName, zDate, zUuid);
654687
}
655688
}
656
- }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){
689
+ }else if( strncmp(zCmd,"list",n)==0 ||
690
+ strncmp(zCmd, "ls", n)==0 ||
691
+ strcmp(zCmd, "lsh")==0 ){
657692
Stmt q;
658693
int vid;
659694
char *zCurrent = 0;
660695
const char *zBrNameGlob = 0;
696
+ int nLimit = 0;
661697
int brFlags = BRL_OPEN_ONLY;
662698
if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
663699
if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;
664700
if( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME;
665701
if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE;
666702
if( find_option("p",0,0)!=0 ) brFlags |= BRL_PRIVATE;
667
- if( g.argc >= 4 ) zBrNameGlob = g.argv[3];
703
+
704
+ if( strcmp(zCmd, "lsh")==0 ){
705
+ nLimit = 5;
706
+ if( g.argc>4 || (g.argc==4 && (nLimit = atoi(g.argv[3]))==0) ){
707
+ fossil_fatal("the lsh subcommand allows one optional numeric argument");
708
+ }
709
+ brFlags |= BRL_ORDERBY_MTIME;
710
+ }else{
711
+ if( g.argc >= 4 ) zBrNameGlob = g.argv[3];
712
+ }
668713
669714
if( g.localOpen ){
670715
vid = db_lget_int("checkout", 0);
671716
zCurrent = db_text(0, "SELECT value FROM tagxref"
672717
" WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
673718
}
674
- branch_prepare_list_query(&q, brFlags, zBrNameGlob);
719
+ branch_prepare_list_query(&q, brFlags, zBrNameGlob, nLimit);
675720
while( db_step(&q)==SQLITE_ROW ){
676721
const char *zBr = db_column_text(&q, 0);
677722
int isPriv = zCurrent!=0 && db_column_int(&q, 1)==1;
678723
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
679724
fossil_print("%s%s%s\n",
@@ -703,11 +748,11 @@
703748
usage("branch unhide branch-name(s)...");
704749
}
705750
branch_cmd_hide(3,0);
706751
}else{
707752
fossil_fatal("branch subcommand should be one of: "
708
- "close current hide info list ls new reopen unhide");
753
+ "close current hide info list ls lsh new reopen unhide");
709754
}
710755
}
711756
712757
/*
713758
** This is the new-style branch-list page that shows the branch names
@@ -862,11 +907,11 @@
862907
@ reopened).</li>
863908
@ </ol>
864909
style_sidebox_end();
865910
#endif
866911
867
- branch_prepare_list_query(&q, brFlags, 0);
912
+ branch_prepare_list_query(&q, brFlags, 0, 0);
868913
cnt = 0;
869914
while( db_step(&q)==SQLITE_ROW ){
870915
const char *zBr = db_column_text(&q, 0);
871916
if( cnt==0 ){
872917
if( colorTest ){
873918
--- src/branch.c
+++ src/branch.c
@@ -289,31 +289,51 @@
289 ** Prepare a query that will list branches.
290 **
291 ** If (which<0) then the query pulls only closed branches. If
292 ** (which>0) then the query pulls all (closed and opened)
293 ** branches. Else the query pulls currently-opened branches.
 
 
 
 
 
 
 
 
 
294 */
295 void branch_prepare_list_query(Stmt *pQuery, int brFlags, const char *zBrNameGlob){
 
 
 
 
 
296 Blob sql;
297 blob_init(&sql, 0, 0);
298 brlist_create_temp_table();
 
 
 
 
 
 
299 switch( brFlags & BRL_OPEN_CLOSED_MASK ){
300 case BRL_CLOSED_ONLY: {
301 blob_append_sql(&sql,
302 "SELECT name, isprivate FROM tmp_brlist WHERE isclosed"
303 );
304 break;
305 }
306 case BRL_BOTH: {
307 blob_append_sql(&sql,
308 "SELECT name, isprivate FROM tmp_brlist WHERE 1"
309 );
310 break;
311 }
312 case BRL_OPEN_ONLY: {
313 blob_append_sql(&sql,
314 "SELECT name, isprivate FROM tmp_brlist WHERE NOT isclosed"
315 );
316 break;
317 }
318 }
319 if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
@@ -321,13 +341,20 @@
321 if( brFlags & BRL_ORDERBY_MTIME ){
322 blob_append_sql(&sql, " ORDER BY -mtime");
323 }else{
324 blob_append_sql(&sql, " ORDER BY name COLLATE nocase");
325 }
326 if( brFlags & BRL_REVERSE ){
327 blob_append_sql(&sql," DESC");
328 }
 
 
 
 
 
 
 
329 db_prepare_blob(pQuery, &sql);
330 blob_reset(&sql);
331 }
332
333 /*
@@ -585,10 +612,11 @@
585 ** > fossil branch info BRANCH-NAME
586 **
587 ** Print information about a branch
588 **
589 ** > fossil branch list|ls ?OPTIONS? ?GLOB?
 
590 **
591 ** List all branches. Options:
592 ** -a|--all List all branches. Default show only open branches
593 ** -c|--closed List closed branches.
594 ** -p List only private branches.
@@ -597,10 +625,15 @@
597 **
598 ** The current branch is marked with an asterisk. Private branches are
599 ** marked with a hash sign.
600 **
601 ** If GLOB is given, show only branches matching the pattern.
 
 
 
 
 
602 **
603 ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
604 **
605 ** Create a new branch BRANCH-NAME off of check-in BASIS.
606 ** Supported options for this subcommand include:
@@ -651,29 +684,41 @@
651 "SELECT datetime(mtime,toLocal()) FROM event"
652 " WHERE objid=%d", rid);
653 fossil_print("%s: open as of %s on %.16s\n", zBrName, zDate, zUuid);
654 }
655 }
656 }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){
 
 
657 Stmt q;
658 int vid;
659 char *zCurrent = 0;
660 const char *zBrNameGlob = 0;
 
661 int brFlags = BRL_OPEN_ONLY;
662 if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
663 if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;
664 if( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME;
665 if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE;
666 if( find_option("p",0,0)!=0 ) brFlags |= BRL_PRIVATE;
667 if( g.argc >= 4 ) zBrNameGlob = g.argv[3];
 
 
 
 
 
 
 
 
 
668
669 if( g.localOpen ){
670 vid = db_lget_int("checkout", 0);
671 zCurrent = db_text(0, "SELECT value FROM tagxref"
672 " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
673 }
674 branch_prepare_list_query(&q, brFlags, zBrNameGlob);
675 while( db_step(&q)==SQLITE_ROW ){
676 const char *zBr = db_column_text(&q, 0);
677 int isPriv = zCurrent!=0 && db_column_int(&q, 1)==1;
678 int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
679 fossil_print("%s%s%s\n",
@@ -703,11 +748,11 @@
703 usage("branch unhide branch-name(s)...");
704 }
705 branch_cmd_hide(3,0);
706 }else{
707 fossil_fatal("branch subcommand should be one of: "
708 "close current hide info list ls new reopen unhide");
709 }
710 }
711
712 /*
713 ** This is the new-style branch-list page that shows the branch names
@@ -862,11 +907,11 @@
862 @ reopened).</li>
863 @ </ol>
864 style_sidebox_end();
865 #endif
866
867 branch_prepare_list_query(&q, brFlags, 0);
868 cnt = 0;
869 while( db_step(&q)==SQLITE_ROW ){
870 const char *zBr = db_column_text(&q, 0);
871 if( cnt==0 ){
872 if( colorTest ){
873
--- src/branch.c
+++ src/branch.c
@@ -289,31 +289,51 @@
289 ** Prepare a query that will list branches.
290 **
291 ** If (which<0) then the query pulls only closed branches. If
292 ** (which>0) then the query pulls all (closed and opened)
293 ** branches. Else the query pulls currently-opened branches.
294 **
295 ** If the BRL_ORDERBY_MTIME flag is set and nLimitMRU ("Limit Most Recently Used
296 ** style") is a non-zero number, the result is limited to nLimitMRU entries, and
297 ** the BRL_REVERSE flag is applied in an outer query after processing the limit,
298 ** so that it's possible to generate short lists with the most recently modified
299 ** branches sorted chronologically in either direction, as does the "branch lsh"
300 ** command.
301 ** For other cases, the outer query is also generated, but works as a no-op. The
302 ** code to build the outer query is marked with *//* OUTER QUERY *//* comments.
303 */
304 void branch_prepare_list_query(
305 Stmt *pQuery,
306 int brFlags,
307 const char *zBrNameGlob,
308 int nLimitMRU
309 ){
310 Blob sql;
311 blob_init(&sql, 0, 0);
312 brlist_create_temp_table();
313 /* Ignore nLimitMRU if no chronological sort requested. */
314 if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0;
315 /* Undocumented: invert negative values for nLimitMRU, so that command-line
316 ** arguments similar to `head -5' with "option numbers" are possible. */
317 if( nLimitMRU<0 ) nLimitMRU = -nLimitMRU;
318 blob_append_sql(&sql,"SELECT name, isprivate FROM ("); /* OUTER QUERY */
319 switch( brFlags & BRL_OPEN_CLOSED_MASK ){
320 case BRL_CLOSED_ONLY: {
321 blob_append_sql(&sql,
322 "SELECT name, isprivate, mtime FROM tmp_brlist WHERE isclosed"
323 );
324 break;
325 }
326 case BRL_BOTH: {
327 blob_append_sql(&sql,
328 "SELECT name, isprivate, mtime FROM tmp_brlist WHERE 1"
329 );
330 break;
331 }
332 case BRL_OPEN_ONLY: {
333 blob_append_sql(&sql,
334 "SELECT name, isprivate, mtime FROM tmp_brlist WHERE NOT isclosed"
335 );
336 break;
337 }
338 }
339 if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
@@ -321,13 +341,20 @@
341 if( brFlags & BRL_ORDERBY_MTIME ){
342 blob_append_sql(&sql, " ORDER BY -mtime");
343 }else{
344 blob_append_sql(&sql, " ORDER BY name COLLATE nocase");
345 }
346 if( brFlags & BRL_REVERSE && !nLimitMRU ){
347 blob_append_sql(&sql," DESC");
348 }
349 if( nLimitMRU ){
350 blob_append_sql(&sql," LIMIT %d",nLimitMRU);
351 }
352 blob_append_sql(&sql,")"); /* OUTER QUERY */
353 if( brFlags & BRL_REVERSE && nLimitMRU ){
354 blob_append_sql(&sql," ORDER BY mtime"); /* OUTER QUERY */
355 }
356 db_prepare_blob(pQuery, &sql);
357 blob_reset(&sql);
358 }
359
360 /*
@@ -585,10 +612,11 @@
612 ** > fossil branch info BRANCH-NAME
613 **
614 ** Print information about a branch
615 **
616 ** > fossil branch list|ls ?OPTIONS? ?GLOB?
617 ** > fossil branch lsh ?OPTIONS? ?LIMIT?
618 **
619 ** List all branches. Options:
620 ** -a|--all List all branches. Default show only open branches
621 ** -c|--closed List closed branches.
622 ** -p List only private branches.
@@ -597,10 +625,15 @@
625 **
626 ** The current branch is marked with an asterisk. Private branches are
627 ** marked with a hash sign.
628 **
629 ** If GLOB is given, show only branches matching the pattern.
630 **
631 ** The "lsh" variant of this subcommand shows recently changed branches,
632 ** and accepts an optional LIMIT argument (defaults to 5) to cap output,
633 ** but no GLOB argument. All other options are supported, with -t being
634 ** an implied no-op.
635 **
636 ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
637 **
638 ** Create a new branch BRANCH-NAME off of check-in BASIS.
639 ** Supported options for this subcommand include:
@@ -651,29 +684,41 @@
684 "SELECT datetime(mtime,toLocal()) FROM event"
685 " WHERE objid=%d", rid);
686 fossil_print("%s: open as of %s on %.16s\n", zBrName, zDate, zUuid);
687 }
688 }
689 }else if( strncmp(zCmd,"list",n)==0 ||
690 strncmp(zCmd, "ls", n)==0 ||
691 strcmp(zCmd, "lsh")==0 ){
692 Stmt q;
693 int vid;
694 char *zCurrent = 0;
695 const char *zBrNameGlob = 0;
696 int nLimit = 0;
697 int brFlags = BRL_OPEN_ONLY;
698 if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH;
699 if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY;
700 if( find_option("t",0,0)!=0 ) brFlags |= BRL_ORDERBY_MTIME;
701 if( find_option("r",0,0)!=0 ) brFlags |= BRL_REVERSE;
702 if( find_option("p",0,0)!=0 ) brFlags |= BRL_PRIVATE;
703
704 if( strcmp(zCmd, "lsh")==0 ){
705 nLimit = 5;
706 if( g.argc>4 || (g.argc==4 && (nLimit = atoi(g.argv[3]))==0) ){
707 fossil_fatal("the lsh subcommand allows one optional numeric argument");
708 }
709 brFlags |= BRL_ORDERBY_MTIME;
710 }else{
711 if( g.argc >= 4 ) zBrNameGlob = g.argv[3];
712 }
713
714 if( g.localOpen ){
715 vid = db_lget_int("checkout", 0);
716 zCurrent = db_text(0, "SELECT value FROM tagxref"
717 " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
718 }
719 branch_prepare_list_query(&q, brFlags, zBrNameGlob, nLimit);
720 while( db_step(&q)==SQLITE_ROW ){
721 const char *zBr = db_column_text(&q, 0);
722 int isPriv = zCurrent!=0 && db_column_int(&q, 1)==1;
723 int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
724 fossil_print("%s%s%s\n",
@@ -703,11 +748,11 @@
748 usage("branch unhide branch-name(s)...");
749 }
750 branch_cmd_hide(3,0);
751 }else{
752 fossil_fatal("branch subcommand should be one of: "
753 "close current hide info list ls lsh new reopen unhide");
754 }
755 }
756
757 /*
758 ** This is the new-style branch-list page that shows the branch names
@@ -862,11 +907,11 @@
907 @ reopened).</li>
908 @ </ol>
909 style_sidebox_end();
910 #endif
911
912 branch_prepare_list_query(&q, brFlags, 0, 0);
913 cnt = 0;
914 while( db_step(&q)==SQLITE_ROW ){
915 const char *zBr = db_column_text(&q, 0);
916 if( cnt==0 ){
917 if( colorTest ){
918
--- src/json_branch.c
+++ src/json_branch.c
@@ -128,11 +128,11 @@
128128
cson_object_set(pay,"current",json_new_string(zCurrent));
129129
}
130130
}
131131
132132
133
- branch_prepare_list_query(&q, branchListFlags, 0);
133
+ branch_prepare_list_query(&q, branchListFlags, 0, 0);
134134
cson_object_set(pay,"branches",listV);
135135
while((SQLITE_ROW==db_step(&q))){
136136
cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
137137
if(v){
138138
cson_array_append(list,v);
139139
--- src/json_branch.c
+++ src/json_branch.c
@@ -128,11 +128,11 @@
128 cson_object_set(pay,"current",json_new_string(zCurrent));
129 }
130 }
131
132
133 branch_prepare_list_query(&q, branchListFlags, 0);
134 cson_object_set(pay,"branches",listV);
135 while((SQLITE_ROW==db_step(&q))){
136 cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
137 if(v){
138 cson_array_append(list,v);
139
--- src/json_branch.c
+++ src/json_branch.c
@@ -128,11 +128,11 @@
128 cson_object_set(pay,"current",json_new_string(zCurrent));
129 }
130 }
131
132
133 branch_prepare_list_query(&q, branchListFlags, 0, 0);
134 cson_object_set(pay,"branches",listV);
135 while((SQLITE_ROW==db_step(&q))){
136 cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0);
137 if(v){
138 cson_array_append(list,v);
139
+25 -16
--- src/main.c
+++ src/main.c
@@ -1471,21 +1471,24 @@
14711471
/*
14721472
** If running as root, chroot to the directory containing the
14731473
** repository zRepo and then drop root privileges. Return the
14741474
** new repository name.
14751475
**
1476
-** zRepo might be a directory itself. In that case chroot into
1477
-** the directory zRepo.
1476
+** zRepo can be a directory. If so and if the repo name was saved
1477
+** to g.zRepositoryName before we were called, we canonicalize the
1478
+** two paths and check that one is the prefix of the other, else you
1479
+** won't be able to open the repo inside the jail. If it all works
1480
+** out, we return the "jailed" version of the repo name.
14781481
**
14791482
** Assume the user-id and group-id of the repository, or if zRepo
14801483
** is a directory, of that directory.
14811484
**
14821485
** The noJail flag means that the chroot jail is not entered. But
14831486
** privileges are still lowered to that of the user-id and group-id
14841487
** of the repository file.
14851488
*/
1486
-char *enter_chroot_jail(char *zRepo, int noJail){
1489
+static char *enter_chroot_jail(const char *zRepo, int noJail){
14871490
#if !defined(_WIN32)
14881491
if( getuid()==0 ){
14891492
int i;
14901493
struct stat sStat;
14911494
Blob dir;
@@ -1496,15 +1499,27 @@
14961499
14971500
file_canonical_name(zRepo, &dir, 0);
14981501
zDir = blob_str(&dir);
14991502
if( !noJail ){
15001503
if( file_isdir(zDir, ExtFILE)==1 ){
1504
+ if( g.zRepositoryName ){
1505
+ size_t n = strlen(zDir);
1506
+ Blob repo;
1507
+ file_canonical_name(g.zRepositoryName, &repo, 0);
1508
+ zRepo = blob_str(&repo);
1509
+ if( strncmp(zRepo, zDir, n)!=0 ){
1510
+ fossil_fatal("repo %s not under chroot dir %s", zRepo, zDir);
1511
+ }
1512
+ zRepo += n;
1513
+ if( *zRepo == '\0' ) zRepo = "/";
1514
+ }else {
1515
+ zRepo = "/";
1516
+ g.fJail = 1;
1517
+ }
15011518
if( file_chdir(zDir, 1) ){
15021519
fossil_panic("unable to chroot into %s", zDir);
15031520
}
1504
- g.fJail = 1;
1505
- zRepo = "/";
15061521
}else{
15071522
for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
15081523
if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);
15091524
if( i>0 ){
15101525
zDir[i] = 0;
@@ -1527,11 +1542,11 @@
15271542
if( g.db==0 && file_isfile(zRepo, ExtFILE) ){
15281543
db_open_repository(zRepo);
15291544
}
15301545
}
15311546
#endif
1532
- return zRepo;
1547
+ return (char*)zRepo; /* no longer const: always reassigned from blob_str() */
15331548
}
15341549
15351550
/*
15361551
** Called whenever a crash is encountered while processing a webpage.
15371552
*/
@@ -2814,15 +2829,12 @@
28142829
zIpAddr = cgi_ssh_remote_addr(0);
28152830
if( zIpAddr && zIpAddr[0] ){
28162831
g.fSshClient |= CGI_SSH_CLIENT;
28172832
}
28182833
}
2819
- if( zChRoot ){
2820
- enter_chroot_jail((char*)zChRoot, noJail);
2821
- }else{
2822
- g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
2823
- }
2834
+ g.zRepositoryName = enter_chroot_jail(
2835
+ zChRoot ? zChRoot : g.zRepositoryName, noJail);
28242836
if( useSCGI ){
28252837
cgi_handle_scgi_request();
28262838
}else if( g.fSshClient & CGI_SSH_CLIENT ){
28272839
ssh_request_loop(zIpAddr, glob_create(zFileGlob));
28282840
}else{
@@ -3319,15 +3331,12 @@
33193331
g.cgiOutput = 1;
33203332
find_server_repository(2, 0);
33213333
if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
33223334
allowRepoList = 1;
33233335
}else{
3324
- if( zChRoot ){
3325
- enter_chroot_jail((char*)zChRoot, noJail);
3326
- }else{
3327
- g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
3328
- }
3336
+ g.zRepositoryName = enter_chroot_jail(
3337
+ zChRoot ? zChRoot : g.zRepositoryName, noJail);
33293338
}
33303339
if( flags & HTTP_SERVER_SCGI ){
33313340
cgi_handle_scgi_request();
33323341
}else if( g.httpUseSSL ){
33333342
#if FOSSIL_ENABLE_SSL
33343343
--- src/main.c
+++ src/main.c
@@ -1471,21 +1471,24 @@
1471 /*
1472 ** If running as root, chroot to the directory containing the
1473 ** repository zRepo and then drop root privileges. Return the
1474 ** new repository name.
1475 **
1476 ** zRepo might be a directory itself. In that case chroot into
1477 ** the directory zRepo.
 
 
 
1478 **
1479 ** Assume the user-id and group-id of the repository, or if zRepo
1480 ** is a directory, of that directory.
1481 **
1482 ** The noJail flag means that the chroot jail is not entered. But
1483 ** privileges are still lowered to that of the user-id and group-id
1484 ** of the repository file.
1485 */
1486 char *enter_chroot_jail(char *zRepo, int noJail){
1487 #if !defined(_WIN32)
1488 if( getuid()==0 ){
1489 int i;
1490 struct stat sStat;
1491 Blob dir;
@@ -1496,15 +1499,27 @@
1496
1497 file_canonical_name(zRepo, &dir, 0);
1498 zDir = blob_str(&dir);
1499 if( !noJail ){
1500 if( file_isdir(zDir, ExtFILE)==1 ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1501 if( file_chdir(zDir, 1) ){
1502 fossil_panic("unable to chroot into %s", zDir);
1503 }
1504 g.fJail = 1;
1505 zRepo = "/";
1506 }else{
1507 for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
1508 if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);
1509 if( i>0 ){
1510 zDir[i] = 0;
@@ -1527,11 +1542,11 @@
1527 if( g.db==0 && file_isfile(zRepo, ExtFILE) ){
1528 db_open_repository(zRepo);
1529 }
1530 }
1531 #endif
1532 return zRepo;
1533 }
1534
1535 /*
1536 ** Called whenever a crash is encountered while processing a webpage.
1537 */
@@ -2814,15 +2829,12 @@
2814 zIpAddr = cgi_ssh_remote_addr(0);
2815 if( zIpAddr && zIpAddr[0] ){
2816 g.fSshClient |= CGI_SSH_CLIENT;
2817 }
2818 }
2819 if( zChRoot ){
2820 enter_chroot_jail((char*)zChRoot, noJail);
2821 }else{
2822 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
2823 }
2824 if( useSCGI ){
2825 cgi_handle_scgi_request();
2826 }else if( g.fSshClient & CGI_SSH_CLIENT ){
2827 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
2828 }else{
@@ -3319,15 +3331,12 @@
3319 g.cgiOutput = 1;
3320 find_server_repository(2, 0);
3321 if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
3322 allowRepoList = 1;
3323 }else{
3324 if( zChRoot ){
3325 enter_chroot_jail((char*)zChRoot, noJail);
3326 }else{
3327 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
3328 }
3329 }
3330 if( flags & HTTP_SERVER_SCGI ){
3331 cgi_handle_scgi_request();
3332 }else if( g.httpUseSSL ){
3333 #if FOSSIL_ENABLE_SSL
3334
--- src/main.c
+++ src/main.c
@@ -1471,21 +1471,24 @@
1471 /*
1472 ** If running as root, chroot to the directory containing the
1473 ** repository zRepo and then drop root privileges. Return the
1474 ** new repository name.
1475 **
1476 ** zRepo can be a directory. If so and if the repo name was saved
1477 ** to g.zRepositoryName before we were called, we canonicalize the
1478 ** two paths and check that one is the prefix of the other, else you
1479 ** won't be able to open the repo inside the jail. If it all works
1480 ** out, we return the "jailed" version of the repo name.
1481 **
1482 ** Assume the user-id and group-id of the repository, or if zRepo
1483 ** is a directory, of that directory.
1484 **
1485 ** The noJail flag means that the chroot jail is not entered. But
1486 ** privileges are still lowered to that of the user-id and group-id
1487 ** of the repository file.
1488 */
1489 static char *enter_chroot_jail(const char *zRepo, int noJail){
1490 #if !defined(_WIN32)
1491 if( getuid()==0 ){
1492 int i;
1493 struct stat sStat;
1494 Blob dir;
@@ -1496,15 +1499,27 @@
1499
1500 file_canonical_name(zRepo, &dir, 0);
1501 zDir = blob_str(&dir);
1502 if( !noJail ){
1503 if( file_isdir(zDir, ExtFILE)==1 ){
1504 if( g.zRepositoryName ){
1505 size_t n = strlen(zDir);
1506 Blob repo;
1507 file_canonical_name(g.zRepositoryName, &repo, 0);
1508 zRepo = blob_str(&repo);
1509 if( strncmp(zRepo, zDir, n)!=0 ){
1510 fossil_fatal("repo %s not under chroot dir %s", zRepo, zDir);
1511 }
1512 zRepo += n;
1513 if( *zRepo == '\0' ) zRepo = "/";
1514 }else {
1515 zRepo = "/";
1516 g.fJail = 1;
1517 }
1518 if( file_chdir(zDir, 1) ){
1519 fossil_panic("unable to chroot into %s", zDir);
1520 }
 
 
1521 }else{
1522 for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
1523 if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);
1524 if( i>0 ){
1525 zDir[i] = 0;
@@ -1527,11 +1542,11 @@
1542 if( g.db==0 && file_isfile(zRepo, ExtFILE) ){
1543 db_open_repository(zRepo);
1544 }
1545 }
1546 #endif
1547 return (char*)zRepo; /* no longer const: always reassigned from blob_str() */
1548 }
1549
1550 /*
1551 ** Called whenever a crash is encountered while processing a webpage.
1552 */
@@ -2814,15 +2829,12 @@
2829 zIpAddr = cgi_ssh_remote_addr(0);
2830 if( zIpAddr && zIpAddr[0] ){
2831 g.fSshClient |= CGI_SSH_CLIENT;
2832 }
2833 }
2834 g.zRepositoryName = enter_chroot_jail(
2835 zChRoot ? zChRoot : g.zRepositoryName, noJail);
 
 
 
2836 if( useSCGI ){
2837 cgi_handle_scgi_request();
2838 }else if( g.fSshClient & CGI_SSH_CLIENT ){
2839 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
2840 }else{
@@ -3319,15 +3331,12 @@
3331 g.cgiOutput = 1;
3332 find_server_repository(2, 0);
3333 if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
3334 allowRepoList = 1;
3335 }else{
3336 g.zRepositoryName = enter_chroot_jail(
3337 zChRoot ? zChRoot : g.zRepositoryName, noJail);
 
 
 
3338 }
3339 if( flags & HTTP_SERVER_SCGI ){
3340 cgi_handle_scgi_request();
3341 }else if( g.httpUseSSL ){
3342 #if FOSSIL_ENABLE_SSL
3343
+1 -1
--- src/setup.c
+++ src/setup.c
@@ -1102,11 +1102,11 @@
11021102
@ engines as well as a short RSS description.
11031103
@ (Property: "project-description")</p>
11041104
@ <hr />
11051105
entry_attribute("Canonical Server URL", 40, "email-url",
11061106
"eurl", "", 0);
1107
- @ <p>This is the URL used access this repository as a server.
1107
+ @ <p>This is the URL used to access this repository as a server.
11081108
@ Other repositories use this URL to clone or sync against this repository.
11091109
@ This is also the basename for hyperlinks included in email alert text.
11101110
@ Omit the trailing "/".
11111111
@ If this repo will not be set up as a persistent server and will not
11121112
@ be sending email alerts, then leave this entry blank.
11131113
--- src/setup.c
+++ src/setup.c
@@ -1102,11 +1102,11 @@
1102 @ engines as well as a short RSS description.
1103 @ (Property: "project-description")</p>
1104 @ <hr />
1105 entry_attribute("Canonical Server URL", 40, "email-url",
1106 "eurl", "", 0);
1107 @ <p>This is the URL used access this repository as a server.
1108 @ Other repositories use this URL to clone or sync against this repository.
1109 @ This is also the basename for hyperlinks included in email alert text.
1110 @ Omit the trailing "/".
1111 @ If this repo will not be set up as a persistent server and will not
1112 @ be sending email alerts, then leave this entry blank.
1113
--- src/setup.c
+++ src/setup.c
@@ -1102,11 +1102,11 @@
1102 @ engines as well as a short RSS description.
1103 @ (Property: "project-description")</p>
1104 @ <hr />
1105 entry_attribute("Canonical Server URL", 40, "email-url",
1106 "eurl", "", 0);
1107 @ <p>This is the URL used to access this repository as a server.
1108 @ Other repositories use this URL to clone or sync against this repository.
1109 @ This is also the basename for hyperlinks included in email alert text.
1110 @ Omit the trailing "/".
1111 @ If this repo will not be set up as a persistent server and will not
1112 @ be sending email alerts, then leave this entry blank.
1113
+1 -1
--- src/stat.c
+++ src/stat.c
@@ -231,11 +231,11 @@
231231
@ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
232232
n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
233233
" WHERE +tagname GLOB 'wiki-*'");
234234
@ %,d(n)
235235
@ </td></tr>
236
- if( db_table_exists("repository","chat") ){
236
+ if( g.perm.Chat && db_table_exists("repository","chat") ){
237237
sqlite3_int64 sz = 0;
238238
char zSz[100];
239239
n = db_int(0, "SELECT max(msgid) FROM chat");
240240
m = db_int(0, "SELECT count(*) FROM chat WHERE mdel IS NOT TRUE");
241241
sz = db_int64(0, "SELECT sum(coalesce(length(xmsg),0)+"
242242
--- src/stat.c
+++ src/stat.c
@@ -231,11 +231,11 @@
231 @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
232 n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
233 " WHERE +tagname GLOB 'wiki-*'");
234 @ %,d(n)
235 @ </td></tr>
236 if( db_table_exists("repository","chat") ){
237 sqlite3_int64 sz = 0;
238 char zSz[100];
239 n = db_int(0, "SELECT max(msgid) FROM chat");
240 m = db_int(0, "SELECT count(*) FROM chat WHERE mdel IS NOT TRUE");
241 sz = db_int64(0, "SELECT sum(coalesce(length(xmsg),0)+"
242
--- src/stat.c
+++ src/stat.c
@@ -231,11 +231,11 @@
231 @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
232 n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
233 " WHERE +tagname GLOB 'wiki-*'");
234 @ %,d(n)
235 @ </td></tr>
236 if( g.perm.Chat && db_table_exists("repository","chat") ){
237 sqlite3_int64 sz = 0;
238 char zSz[100];
239 n = db_int(0, "SELECT max(msgid) FROM chat");
240 m = db_int(0, "SELECT count(*) FROM chat WHERE mdel IS NOT TRUE");
241 sz = db_int64(0, "SELECT sum(coalesce(length(xmsg),0)+"
242
+130 -76
--- www/build.wiki
+++ www/build.wiki
@@ -112,11 +112,12 @@
112112
<p>For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL
113113
discussion in the "TLS and Fossil" document].</p>
114114
115115
<li><p>
116116
To build a statically linked binary (suitable for use inside a chroot
117
-jail) add the <b>--static</b> option.
117
+jail) add the <b>--static</b> option. (See the [#docker | Docker section
118
+below].)
118119
119120
<li><p>
120121
To enable the native [./th1.md#tclEval | Tcl integration feature] feature,
121122
add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options.
122123
@@ -248,85 +249,138 @@
248249
TCC += -Dsocketlen_t=int
249250
TCC += -DSQLITE_MAX_MMAP_SIZE=0
250251
</pre></blockquote>
251252
</ul>
252253
253
-<h2>5.0 Building a Static Binary on Linux using Docker</h2>
254
-
255
-Building a static binary on Linux is not as straightforward as it
256
-could be because the GNU C library requires that certain components be
257
-dynamically loadable. That can be worked around by building against a
258
-different C library, which is simplest to do by way of a container
259
-environment like [https://www.docker.com/ | Docker].
260
-
261
-The following instructions for building fossil using Docker
262
-were adapted from [https://fossil-scm.org/forum/forumpost/5dd2d61e5f | forumpost/5dd2d61e5f].
263
-These instructions assume that docker is installed and that the user running
264
-these instructions has permission to do so (i.e., they are <tt>root</tt> or
265
-are a member of the <tt>docker</tt> group).
266
-
267
-First, create a file named <tt>Dockerfile</tt> with the following contents:
268
-
269
-<pre><code>
270
-FROM alpine:edge
271
-RUN apk update \
272
- && apk upgrade \
273
- && apk add --no-cache \
274
- curl gcc make tcl \
275
- musl-dev \
276
- openssl-dev zlib-dev \
277
- openssl-libs-static zlib-static \
278
- && curl \
279
- "https://fossil-scm.org/home/tarball/fossil-src.tar.gz?name=fossil-src&uuid=trunk" \
280
- -o fossil-src.tar.gz \
281
- && tar xf fossil-src.tar.gz \
282
- && cd fossil-src \
283
- && ./configure \
284
- --static \
285
- --disable-fusefs \
286
- --with-th1-docs \
287
- --with-th1-hooks \
288
- && make
289
-</code></pre>
290
-
291
-Be sure to modify the <tt>configure</tt> flags, if desired. e.g., add <tt>--json</tt>
292
-for JSON support.
293
-
294
-From the directory containing that file, build it with docker:
295
-
296
-<pre><code># docker build -t fossil_static .</code></pre>
297
-
298
-If you get permissions errors when running that as a non-root user,
299
-be sure to add the user to the <tt>docker</tt> group before trying
300
-again.
301
-
302
-That creates a docker image and builds a static fossil binary inside
303
-it. That step will take several minutes or more, depending on the
304
-speed of the build environment.
305
-
306
-Next, create a docker container to host the image we just created:
307
-
308
-<pre><code># docker create --name fossil fossil_static</code></pre>
309
-
310
-Then copy the fossil binary from that container:
311
-
312
-<pre><code># docker cp fossil:/fossil-src/fossil fossil</code></pre>
313
-
314
-The resulting binary will be <em>huge</em> because it is built with
315
-debug info. To strip that information, reducing the size greatly:
316
-
317
-<pre><code># strip fossil</code></pre>
318
-
319
-To delete the Docker container and image (if desired), run:
320
-
321
-<pre><code># docker container rm fossil
322
-# docker image ls
323
-</code></pre>
324
-
325
-Note the IDs of the images named <tt>fossil_static</tt> and <tt>alpine</tt>, then:
326
-
327
-<pre><code>docker image rm THE_FOSSIL_ID THE_ALPINE_ID</code></pre>
254
+
255
+<h2 id="docker">5.0 Building a Docker Container</h2>
256
+
257
+Fossil ships a <tt>Dockerfile</tt> at the top of its source tree which
258
+you can build like so:
259
+
260
+<pre><code> $ docker build -t fossil --no-cache .</code></pre>
261
+
262
+If the image built successfully, you can create a container from it and
263
+test that it runs:
264
+
265
+<pre><code> $ docker run --name fossil -p 9999:8080/tcp fossil</code></pre>
266
+
267
+This shows us remapping the internal TCP listening port as 9999 on the
268
+host. As a rule, there's little point to using the "<tt>fossil server
269
+--port</tt>" feature inside a Docker container. Let it default to 8080
270
+internally, then remap it to wherever you want it on the host instead.
271
+
272
+Our stock <tt>Dockerfile</tt> configures Fossil with the default feature
273
+set, so you may wish to modify the <tt>Dockerfile</tt> to add
274
+configuration options, add APK packages to support those options, and so
275
+forth.
276
+
277
+It builds tip-of-trunk. To get a release version instead, append
278
+"<tt>?r=release</tt>" to the URL in the <tt>Dockerfile</tt>, then
279
+(re)build it.
280
+
281
+
282
+<h3>5.1 Running It in Production</h3>
283
+
284
+If you want the container to serve an existing repository, there are at
285
+least two right ways to do it.
286
+
287
+The wrong way is to use the <tt>Dockerfile COPY</tt> command to bake it
288
+into the container at build time. It will become one of the container's
289
+base layers, so that each time you restart the container, it gets reset
290
+to its prior state. This almost certainly is not what you want.
291
+
292
+The simplest correct method is to stop the container if it was running,
293
+then say:
294
+
295
+<pre><code> $ docker cp /path/to/repo.fossil fossil:/jail/museum/repo.fossil
296
+ $ docker start fossil
297
+ $ docker exec fossil chown 0 /jail/museum/repo.fossil</code></pre>
298
+
299
+That copies the local Fossil repo into the container where the server
300
+expects to find it, so that the "start" command causes it to serve from
301
+that copied-in file instead. Since it lives atop the base layers, it
302
+persists as part of the container proper, surviving restarts.
303
+
304
+(The same is true of the default mode of operation: the <tt>fossil
305
+server --create</tt> flag initializes a fresh Fossil repo atop the base
306
+layer.)
307
+
308
+If you skip the "chown" command and put "http://localhost:9999/" into
309
+your browser, expecting to see the copied-in repo's home page, you will
310
+get an opaque "Not Found" error instead. This is because the user and
311
+group ID of the file will be that of your local user on the container's
312
+host machine, which won't map to anything in the container's
313
+<tt>/etc/passwd</tt> and <tt>/etc/group</tt> files, effectively
314
+preventing the server from reading the copied-in repository file. You
315
+don't have to restart the server after fixing this: simply reload the
316
+browser, and Fossil will try again.
317
+
318
+This simple method has a problem: Docker containers are designed to be
319
+killed off at the slightest cause, rebuilt, and redeployed. If you do
320
+that with the repo inside the container, it gets destroyed, too. The
321
+solution is to replace the "run" command above with the following:
322
+
323
+<pre><code> $ docker create \
324
+ --name fossil -p 9999:8080/tcp \
325
+ -v museum:/jail/museum fossil
326
+</code></pre>
327
+
328
+Now when you "docker cp" the local repo into the container, it lands on
329
+a separate [https://docs.docker.com/storage/volumes/ | Docker volume]
330
+mounted inside it, which has an independent lifetime. When you need to
331
+rebuild the container — such as to upgrade to a newer version of Fossil
332
+— the volume remains behind and gets remapped into the new contanier
333
+when you recreate it by giving the above command again.
334
+
335
+
336
+<h3>5.2 Why Chroot?</h3>
337
+
338
+A potentially surprising feature of this container is that it runs
339
+Fossil as root, which causes [./chroot.md | Fossil's chroot jail
340
+feature] to kick in. Since a Docker container is a type of über-jail
341
+already, you may be wondering why we don't either:
342
+
343
+ # run <tt>fossil server --nojail</tt> to skip the internal chroot; or
344
+ # create a non-root user and force Docker to use that instead
345
+
346
+The reason is, although this container is quite stripped-down by today's
347
+standards, it's based on the [https://www.busybox.net/BusyBox.html |
348
+surprisingly powerful Busybox project]. (This author made a living for
349
+years in the early 1990s using Unix systems that were less powerful than
350
+this container.) If someone ever figured out how to make a Fossil binary
351
+execute arbitrary commands on the host or to open up a remote shell,
352
+they'd likely be able to island-hop from there into the rest of your
353
+network. We need this cute double-jail dance to keep the Fossil instance
354
+from accessing the Busybox features.
355
+
356
+We deem this risk low since a) it's never happened, that we know of;
357
+and b) we've turned off all of the risky features like TH1 docs.
358
+Nevertheless, we believe in defense-in-depth.
359
+
360
+
361
+<h3>5.3 Extracting a Static Binary</h3>
362
+
363
+Our 2-stage build process uses Alpine Linux only as a build host. Once
364
+we've got everything reduced to the two key static binaries — Fossil and
365
+Busybox — we throw all the rest of it away.
366
+
367
+A secondary benefit falls out of this process for free: it's arguably
368
+the easiest way to build a purely static Fossil binary for Linux. Most
369
+modern Linux distros make this surprisingly difficult, but Alpine's
370
+back-to-basics nature makes static builds work the way they used to,
371
+back in the day. If that's what you're after, you can skip the "run"
372
+command above and create a temporary container from the image, then
373
+extract the executable from it instead:
374
+
375
+<pre><code> $ docker create --name fossil-static-tmp fossil
376
+ $ docker cp fossil-static-tmp:/jail/bin/fossil .
377
+ $ docker container rm fossil-static-tmp
378
+</code></pre>
379
+
380
+The resulting binary is the single largest file inside that container,
381
+at about 6 MiB. (It's built stripped.)
328382
329383
330384
<h2>6.0 Building on/for Android</h2>
331385
332386
<h3>6.1 Cross-compiling from Linux</h3>
333387
--- www/build.wiki
+++ www/build.wiki
@@ -112,11 +112,12 @@
112 <p>For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL
113 discussion in the "TLS and Fossil" document].</p>
114
115 <li><p>
116 To build a statically linked binary (suitable for use inside a chroot
117 jail) add the <b>--static</b> option.
 
118
119 <li><p>
120 To enable the native [./th1.md#tclEval | Tcl integration feature] feature,
121 add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options.
122
@@ -248,85 +249,138 @@
248 TCC += -Dsocketlen_t=int
249 TCC += -DSQLITE_MAX_MMAP_SIZE=0
250 </pre></blockquote>
251 </ul>
252
253 <h2>5.0 Building a Static Binary on Linux using Docker</h2>
254
255 Building a static binary on Linux is not as straightforward as it
256 could be because the GNU C library requires that certain components be
257 dynamically loadable. That can be worked around by building against a
258 different C library, which is simplest to do by way of a container
259 environment like [https://www.docker.com/ | Docker].
260
261 The following instructions for building fossil using Docker
262 were adapted from [https://fossil-scm.org/forum/forumpost/5dd2d61e5f | forumpost/5dd2d61e5f].
263 These instructions assume that docker is installed and that the user running
264 these instructions has permission to do so (i.e., they are <tt>root</tt> or
265 are a member of the <tt>docker</tt> group).
266
267 First, create a file named <tt>Dockerfile</tt> with the following contents:
268
269 <pre><code>
270 FROM alpine:edge
271 RUN apk update \
272 && apk upgrade \
273 && apk add --no-cache \
274 curl gcc make tcl \
275 musl-dev \
276 openssl-dev zlib-dev \
277 openssl-libs-static zlib-static \
278 && curl \
279 "https://fossil-scm.org/home/tarball/fossil-src.tar.gz?name=fossil-src&uuid=trunk" \
280 -o fossil-src.tar.gz \
281 && tar xf fossil-src.tar.gz \
282 && cd fossil-src \
283 && ./configure \
284 --static \
285 --disable-fusefs \
286 --with-th1-docs \
287 --with-th1-hooks \
288 && make
289 </code></pre>
290
291 Be sure to modify the <tt>configure</tt> flags, if desired. e.g., add <tt>--json</tt>
292 for JSON support.
293
294 From the directory containing that file, build it with docker:
295
296 <pre><code># docker build -t fossil_static .</code></pre>
297
298 If you get permissions errors when running that as a non-root user,
299 be sure to add the user to the <tt>docker</tt> group before trying
300 again.
301
302 That creates a docker image and builds a static fossil binary inside
303 it. That step will take several minutes or more, depending on the
304 speed of the build environment.
305
306 Next, create a docker container to host the image we just created:
307
308 <pre><code># docker create --name fossil fossil_static</code></pre>
309
310 Then copy the fossil binary from that container:
311
312 <pre><code># docker cp fossil:/fossil-src/fossil fossil</code></pre>
313
314 The resulting binary will be <em>huge</em> because it is built with
315 debug info. To strip that information, reducing the size greatly:
316
317 <pre><code># strip fossil</code></pre>
318
319 To delete the Docker container and image (if desired), run:
320
321 <pre><code># docker container rm fossil
322 # docker image ls
323 </code></pre>
324
325 Note the IDs of the images named <tt>fossil_static</tt> and <tt>alpine</tt>, then:
326
327 <pre><code>docker image rm THE_FOSSIL_ID THE_ALPINE_ID</code></pre>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
329
330 <h2>6.0 Building on/for Android</h2>
331
332 <h3>6.1 Cross-compiling from Linux</h3>
333
--- www/build.wiki
+++ www/build.wiki
@@ -112,11 +112,12 @@
112 <p>For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL
113 discussion in the "TLS and Fossil" document].</p>
114
115 <li><p>
116 To build a statically linked binary (suitable for use inside a chroot
117 jail) add the <b>--static</b> option. (See the [#docker | Docker section
118 below].)
119
120 <li><p>
121 To enable the native [./th1.md#tclEval | Tcl integration feature] feature,
122 add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options.
123
@@ -248,85 +249,138 @@
249 TCC += -Dsocketlen_t=int
250 TCC += -DSQLITE_MAX_MMAP_SIZE=0
251 </pre></blockquote>
252 </ul>
253
254
255 <h2 id="docker">5.0 Building a Docker Container</h2>
256
257 Fossil ships a <tt>Dockerfile</tt> at the top of its source tree which
258 you can build like so:
259
260 <pre><code> $ docker build -t fossil --no-cache .</code></pre>
261
262 If the image built successfully, you can create a container from it and
263 test that it runs:
264
265 <pre><code> $ docker run --name fossil -p 9999:8080/tcp fossil</code></pre>
266
267 This shows us remapping the internal TCP listening port as 9999 on the
268 host. As a rule, there's little point to using the "<tt>fossil server
269 --port</tt>" feature inside a Docker container. Let it default to 8080
270 internally, then remap it to wherever you want it on the host instead.
271
272 Our stock <tt>Dockerfile</tt> configures Fossil with the default feature
273 set, so you may wish to modify the <tt>Dockerfile</tt> to add
274 configuration options, add APK packages to support those options, and so
275 forth.
276
277 It builds tip-of-trunk. To get a release version instead, append
278 "<tt>?r=release</tt>" to the URL in the <tt>Dockerfile</tt>, then
279 (re)build it.
280
281
282 <h3>5.1 Running It in Production</h3>
283
284 If you want the container to serve an existing repository, there are at
285 least two right ways to do it.
286
287 The wrong way is to use the <tt>Dockerfile COPY</tt> command to bake it
288 into the container at build time. It will become one of the container's
289 base layers, so that each time you restart the container, it gets reset
290 to its prior state. This almost certainly is not what you want.
291
292 The simplest correct method is to stop the container if it was running,
293 then say:
294
295 <pre><code> $ docker cp /path/to/repo.fossil fossil:/jail/museum/repo.fossil
296 $ docker start fossil
297 $ docker exec fossil chown 0 /jail/museum/repo.fossil</code></pre>
298
299 That copies the local Fossil repo into the container where the server
300 expects to find it, so that the "start" command causes it to serve from
301 that copied-in file instead. Since it lives atop the base layers, it
302 persists as part of the container proper, surviving restarts.
303
304 (The same is true of the default mode of operation: the <tt>fossil
305 server --create</tt> flag initializes a fresh Fossil repo atop the base
306 layer.)
307
308 If you skip the "chown" command and put "http://localhost:9999/" into
309 your browser, expecting to see the copied-in repo's home page, you will
310 get an opaque "Not Found" error instead. This is because the user and
311 group ID of the file will be that of your local user on the container's
312 host machine, which won't map to anything in the container's
313 <tt>/etc/passwd</tt> and <tt>/etc/group</tt> files, effectively
314 preventing the server from reading the copied-in repository file. You
315 don't have to restart the server after fixing this: simply reload the
316 browser, and Fossil will try again.
317
318 This simple method has a problem: Docker containers are designed to be
319 killed off at the slightest cause, rebuilt, and redeployed. If you do
320 that with the repo inside the container, it gets destroyed, too. The
321 solution is to replace the "run" command above with the following:
322
323 <pre><code> $ docker create \
324 --name fossil -p 9999:8080/tcp \
325 -v museum:/jail/museum fossil
326 </code></pre>
327
328 Now when you "docker cp" the local repo into the container, it lands on
329 a separate [https://docs.docker.com/storage/volumes/ | Docker volume]
330 mounted inside it, which has an independent lifetime. When you need to
331 rebuild the container — such as to upgrade to a newer version of Fossil
332 — the volume remains behind and gets remapped into the new contanier
333 when you recreate it by giving the above command again.
334
335
336 <h3>5.2 Why Chroot?</h3>
337
338 A potentially surprising feature of this container is that it runs
339 Fossil as root, which causes [./chroot.md | Fossil's chroot jail
340 feature] to kick in. Since a Docker container is a type of über-jail
341 already, you may be wondering why we don't either:
342
343 # run <tt>fossil server --nojail</tt> to skip the internal chroot; or
344 # create a non-root user and force Docker to use that instead
345
346 The reason is, although this container is quite stripped-down by today's
347 standards, it's based on the [https://www.busybox.net/BusyBox.html |
348 surprisingly powerful Busybox project]. (This author made a living for
349 years in the early 1990s using Unix systems that were less powerful than
350 this container.) If someone ever figured out how to make a Fossil binary
351 execute arbitrary commands on the host or to open up a remote shell,
352 they'd likely be able to island-hop from there into the rest of your
353 network. We need this cute double-jail dance to keep the Fossil instance
354 from accessing the Busybox features.
355
356 We deem this risk low since a) it's never happened, that we know of;
357 and b) we've turned off all of the risky features like TH1 docs.
358 Nevertheless, we believe in defense-in-depth.
359
360
361 <h3>5.3 Extracting a Static Binary</h3>
362
363 Our 2-stage build process uses Alpine Linux only as a build host. Once
364 we've got everything reduced to the two key static binaries — Fossil and
365 Busybox — we throw all the rest of it away.
366
367 A secondary benefit falls out of this process for free: it's arguably
368 the easiest way to build a purely static Fossil binary for Linux. Most
369 modern Linux distros make this surprisingly difficult, but Alpine's
370 back-to-basics nature makes static builds work the way they used to,
371 back in the day. If that's what you're after, you can skip the "run"
372 command above and create a temporary container from the image, then
373 extract the executable from it instead:
374
375 <pre><code> $ docker create --name fossil-static-tmp fossil
376 $ docker cp fossil-static-tmp:/jail/bin/fossil .
377 $ docker container rm fossil-static-tmp
378 </code></pre>
379
380 The resulting binary is the single largest file inside that container,
381 at about 6 MiB. (It's built stripped.)
382
383
384 <h2>6.0 Building on/for Android</h2>
385
386 <h3>6.1 Cross-compiling from Linux</h3>
387
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -113,36 +113,41 @@
113113
[/help?cmd=ui|UI],
114114
protected by [./caps/ | a fine-grained role-based
115115
access control system].
116116
These additional capabilities are available for Git as 3rd-party
117117
add-ons, but with Fossil they are integrated into
118
-the design. One way to describe Fossil is that it is
118
+the design, to the point that it approximates
119119
"[https://github.com/ | GitHub]-in-a-box."
120120
121
-Fossil can do operations over all local repo clones and check-out
122
-directories with a single command. For example, Fossil lets you say
123
-"<tt>fossil all sync</tt>" on a laptop prior to taking it off the network
124
-hosting those repos. You can sync up to all of the private repos on your
125
-company network plus those public Internet-hosted repos you use. Whether
126
-going out for a working lunch or on a transoceanic airplane trip, one
127
-command gets you in sync. This works with several other Fossil
128
-sub-commands, such as "<tt>fossil all changes</tt>" to get a list of files
121
+Even if you only want straight version control, Fossil has affordances
122
+not available in Git.
123
+
124
+For instance, Fossil can do operations over all local repo clones and
125
+check-out directories with a single command. You can say "<tt>fossil
126
+all sync</tt>" on a laptop prior to taking it off the network hosting
127
+those repos, as before going on a trip. It doesn't matter if those
128
+repos are private and restricted to your company network or public
129
+Internet-hosted repos, you get synced up with everything you need while
130
+off-network.
131
+
132
+You get the same capability with several other Fossil
133
+sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files
129134
that you forgot to commit prior to the end of your working day, across
130135
all repos.
131136
132137
Whenever Fossil is told to modify the local checkout in some destructive
133138
way ([/help?cmd=rm|fossil rm], [/help?cmd=update|fossil update],
134139
[/help?cmd=revert|fossil revert], etc.) Fossil remembers the prior state
135140
and is able to return the check-out directory to that state with a
136
-<tt>fossil undo</tt> command. You cannot undo a commit in Fossil
137
-([#history | on purpose!]) but as long as the change remains confined to
141
+<tt>fossil undo</tt> command. While you cannot undo a commit in Fossil
142
+— [#history | on purpose!] — as long as the change remains confined to
138143
the local check-out directory only, Fossil makes undo
139144
[https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in
140145
Git].
141146
142
-For developers who choose to self-host projects (rather than using a
143
-3rd-party service such as GitHub) Fossil is much easier to set up, since
147
+For developers who choose to self-host projects rather than rely on a
148
+3rd-party service such as GitHub, Fossil is much easier to set up:
144149
the stand-alone Fossil executable together with a [./server/any/cgi.md|2-line CGI script]
145150
suffice to instantiate a full-featured developer website. To accomplish
146151
the same using Git requires locating, installing, configuring, integrating,
147152
and managing a wide assortment of separate tools. Standing up a developer
148153
website using Fossil can be done in minutes, whereas doing the same using
@@ -165,27 +170,38 @@
165170
so that most people end up installing it via some kind of package
166171
manager, simply because the creation of complicated binary packages is
167172
best delegated to people skilled in their creation. Normal Git users are
168173
not expected to build Git from source and install it themselves.
169174
170
-Fossil is a single self-contained stand-alone executable which by default
171
-depends only on common platform libraries. You can statically link
172
-to get an executable with no external dependencies at all &mdash; a useful
173
-feature for running inside a restrictive
174
-[https://en.wikipedia.org/wiki/Chroot|chroot jail].
175
-
176
-The precompiled Fossil binaries are delivered as just a single
177
-executable. The precompiled Windows deliveries are just a ZIP archive
175
+Fossil is a single self-contained stand-alone executable which
176
+depends only on common platform libraries in its default configuration.
177
+To install one of [https://fossil-scm.org/home/uv/download.html | our
178
+precompiled binaries], unpack the executable from the archive and put it
179
+somewhere in your <tt>PATH</tt>. To uninstall it, delete the executable.
180
+
181
+This policy is particularly useful when running Fossil inside a
182
+restrictive container, anything from [./chroot.md | classic chroot
183
+jails] to modern [https://en.wikipedia.org/wiki/OS-level_virtualization
184
+| OS-level virtualization mechanisms] such as
185
+[https://en.wikipedia.org/wiki/Docker_(software) | Docker]. Our
186
+[/file?name=Dockerfile&ci=trunk | stock <tt>Dockerfile</tt>]
187
+creates a container that's under 9 MiB on 64-bit Linux, including
188
+a capable [https://www.busybox.net/ | Busybox] environment for live
189
+debugging of the container's innards.
190
+
191
+Modern Linux systems tend to make full static linking
192
+[https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
193
+| difficult], but our official executables do statically link to OpenSSL
194
+to remove a version dependency, resulting in an executable that's around
195
+6 MiB, depending on the platform. ([Release Build How-To | Details].)
196
+The result is dependent only upon widespread platform libraries with
197
+stable ABIs such as glibc, zlib, etc.
198
+
199
+Full static linking is easier on Windows, so our precompiled Windows
200
+binaries are just a ZIP archive
178201
containing only "<tt>fossil.exe</tt>". There is no "<tt>setup.exe</tt>"
179
-to run. Linux and Mac precompiled binaries are a tarball containing
180
-just the "<tt>fossil</tt>" executable. To install, just put the
181
-executable on your PATH. To uninstall, just delete the executable.
182
-To upgrade (or downgrade) simply replace the executable.
183
-
184
-A typical Fossil executable is between 5 and 7 megabytes uncompressed
185
-(as of 2020-12-12),
186
-assuming that the executable is statically linked against OpenSSL.
202
+to run.
187203
188204
Fossil is easy to build from sources. Just run
189205
"<tt>./configure && make</tt>" on POSIX systems and
190206
"<tt>nmake /f Makefile.msc</tt>" on Windows.
191207
@@ -208,18 +224,20 @@
208224
matters is effectiveness and efficiency. We believe Fossil achieves
209225
this.
210226
211227
The above size comparisons aren't apples-to-apples anyway. We've
212228
compared the size of Fossil with all of its [#features | many built-in
213
-features] to a fairly minimal Git installation. You must add a lot
214
-of third-party
215
-software to Git to give it a Fossil-equivalent feature set. Consider
216
-[https://about.gitlab.com/|GitLab], a third-party extension to Git
217
-wrapping it in many features, making it roughly Fossil-equivalent,
229
+features] to a fairly minimal Git installation. You must add a lot of
230
+third-party software to Git to give it a Fossil-equivalent feature set.
231
+Consider [https://about.gitlab.com/|GitLab], a third-party extension to
232
+Git wrapping it in many features, making it roughly Fossil-equivalent,
218233
though [https://docs.gitlab.com/ee/install/requirements.html|much more
219
-resource hungry] and hence more costly to run than the equivalent
220
-Fossil setup. GitLab's basic requirements are easy to accept when you're dedicating
234
+resource hungry] and hence more costly to run than the equivalent Fossil
235
+setup. [https://hub.docker.com/r/gitlab/gitlab-ce/ | The official GitLab
236
+Community Edition container] currently clocks in at 2.66 GiB!
237
+
238
+GitLab's requirements are easy to accept when you're dedicating
221239
a local rack server or blade to it, since its minimum requirements are
222240
more or less a description of the smallest
223241
thing you could call a "server" these days, but when you go to host that
224242
in the cloud, you can expect to pay about 8 times as much to comfortably host
225243
GitLab as for Fossil.³ This difference is largely due to basic
226244
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -113,36 +113,41 @@
113 [/help?cmd=ui|UI],
114 protected by [./caps/ | a fine-grained role-based
115 access control system].
116 These additional capabilities are available for Git as 3rd-party
117 add-ons, but with Fossil they are integrated into
118 the design. One way to describe Fossil is that it is
119 "[https://github.com/ | GitHub]-in-a-box."
120
121 Fossil can do operations over all local repo clones and check-out
122 directories with a single command. For example, Fossil lets you say
123 "<tt>fossil all sync</tt>" on a laptop prior to taking it off the network
124 hosting those repos. You can sync up to all of the private repos on your
125 company network plus those public Internet-hosted repos you use. Whether
126 going out for a working lunch or on a transoceanic airplane trip, one
127 command gets you in sync. This works with several other Fossil
128 sub-commands, such as "<tt>fossil all changes</tt>" to get a list of files
 
 
 
 
 
129 that you forgot to commit prior to the end of your working day, across
130 all repos.
131
132 Whenever Fossil is told to modify the local checkout in some destructive
133 way ([/help?cmd=rm|fossil rm], [/help?cmd=update|fossil update],
134 [/help?cmd=revert|fossil revert], etc.) Fossil remembers the prior state
135 and is able to return the check-out directory to that state with a
136 <tt>fossil undo</tt> command. You cannot undo a commit in Fossil
137 ([#history | on purpose!]) but as long as the change remains confined to
138 the local check-out directory only, Fossil makes undo
139 [https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in
140 Git].
141
142 For developers who choose to self-host projects (rather than using a
143 3rd-party service such as GitHub) Fossil is much easier to set up, since
144 the stand-alone Fossil executable together with a [./server/any/cgi.md|2-line CGI script]
145 suffice to instantiate a full-featured developer website. To accomplish
146 the same using Git requires locating, installing, configuring, integrating,
147 and managing a wide assortment of separate tools. Standing up a developer
148 website using Fossil can be done in minutes, whereas doing the same using
@@ -165,27 +170,38 @@
165 so that most people end up installing it via some kind of package
166 manager, simply because the creation of complicated binary packages is
167 best delegated to people skilled in their creation. Normal Git users are
168 not expected to build Git from source and install it themselves.
169
170 Fossil is a single self-contained stand-alone executable which by default
171 depends only on common platform libraries. You can statically link
172 to get an executable with no external dependencies at all &mdash; a useful
173 feature for running inside a restrictive
174 [https://en.wikipedia.org/wiki/Chroot|chroot jail].
175
176 The precompiled Fossil binaries are delivered as just a single
177 executable. The precompiled Windows deliveries are just a ZIP archive
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178 containing only "<tt>fossil.exe</tt>". There is no "<tt>setup.exe</tt>"
179 to run. Linux and Mac precompiled binaries are a tarball containing
180 just the "<tt>fossil</tt>" executable. To install, just put the
181 executable on your PATH. To uninstall, just delete the executable.
182 To upgrade (or downgrade) simply replace the executable.
183
184 A typical Fossil executable is between 5 and 7 megabytes uncompressed
185 (as of 2020-12-12),
186 assuming that the executable is statically linked against OpenSSL.
187
188 Fossil is easy to build from sources. Just run
189 "<tt>./configure && make</tt>" on POSIX systems and
190 "<tt>nmake /f Makefile.msc</tt>" on Windows.
191
@@ -208,18 +224,20 @@
208 matters is effectiveness and efficiency. We believe Fossil achieves
209 this.
210
211 The above size comparisons aren't apples-to-apples anyway. We've
212 compared the size of Fossil with all of its [#features | many built-in
213 features] to a fairly minimal Git installation. You must add a lot
214 of third-party
215 software to Git to give it a Fossil-equivalent feature set. Consider
216 [https://about.gitlab.com/|GitLab], a third-party extension to Git
217 wrapping it in many features, making it roughly Fossil-equivalent,
218 though [https://docs.gitlab.com/ee/install/requirements.html|much more
219 resource hungry] and hence more costly to run than the equivalent
220 Fossil setup. GitLab's basic requirements are easy to accept when you're dedicating
 
 
 
221 a local rack server or blade to it, since its minimum requirements are
222 more or less a description of the smallest
223 thing you could call a "server" these days, but when you go to host that
224 in the cloud, you can expect to pay about 8 times as much to comfortably host
225 GitLab as for Fossil.³ This difference is largely due to basic
226
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -113,36 +113,41 @@
113 [/help?cmd=ui|UI],
114 protected by [./caps/ | a fine-grained role-based
115 access control system].
116 These additional capabilities are available for Git as 3rd-party
117 add-ons, but with Fossil they are integrated into
118 the design, to the point that it approximates
119 "[https://github.com/ | GitHub]-in-a-box."
120
121 Even if you only want straight version control, Fossil has affordances
122 not available in Git.
123
124 For instance, Fossil can do operations over all local repo clones and
125 check-out directories with a single command. You can say "<tt>fossil
126 all sync</tt>" on a laptop prior to taking it off the network hosting
127 those repos, as before going on a trip. It doesn't matter if those
128 repos are private and restricted to your company network or public
129 Internet-hosted repos, you get synced up with everything you need while
130 off-network.
131
132 You get the same capability with several other Fossil
133 sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files
134 that you forgot to commit prior to the end of your working day, across
135 all repos.
136
137 Whenever Fossil is told to modify the local checkout in some destructive
138 way ([/help?cmd=rm|fossil rm], [/help?cmd=update|fossil update],
139 [/help?cmd=revert|fossil revert], etc.) Fossil remembers the prior state
140 and is able to return the check-out directory to that state with a
141 <tt>fossil undo</tt> command. While you cannot undo a commit in Fossil
142 — [#history | on purpose!] — as long as the change remains confined to
143 the local check-out directory only, Fossil makes undo
144 [https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in
145 Git].
146
147 For developers who choose to self-host projects rather than rely on a
148 3rd-party service such as GitHub, Fossil is much easier to set up:
149 the stand-alone Fossil executable together with a [./server/any/cgi.md|2-line CGI script]
150 suffice to instantiate a full-featured developer website. To accomplish
151 the same using Git requires locating, installing, configuring, integrating,
152 and managing a wide assortment of separate tools. Standing up a developer
153 website using Fossil can be done in minutes, whereas doing the same using
@@ -165,27 +170,38 @@
170 so that most people end up installing it via some kind of package
171 manager, simply because the creation of complicated binary packages is
172 best delegated to people skilled in their creation. Normal Git users are
173 not expected to build Git from source and install it themselves.
174
175 Fossil is a single self-contained stand-alone executable which
176 depends only on common platform libraries in its default configuration.
177 To install one of [https://fossil-scm.org/home/uv/download.html | our
178 precompiled binaries], unpack the executable from the archive and put it
179 somewhere in your <tt>PATH</tt>. To uninstall it, delete the executable.
180
181 This policy is particularly useful when running Fossil inside a
182 restrictive container, anything from [./chroot.md | classic chroot
183 jails] to modern [https://en.wikipedia.org/wiki/OS-level_virtualization
184 | OS-level virtualization mechanisms] such as
185 [https://en.wikipedia.org/wiki/Docker_(software) | Docker]. Our
186 [/file?name=Dockerfile&ci=trunk | stock <tt>Dockerfile</tt>]
187 creates a container that's under 9 MiB on 64-bit Linux, including
188 a capable [https://www.busybox.net/ | Busybox] environment for live
189 debugging of the container's innards.
190
191 Modern Linux systems tend to make full static linking
192 [https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
193 | difficult], but our official executables do statically link to OpenSSL
194 to remove a version dependency, resulting in an executable that's around
195 6 MiB, depending on the platform. ([Release Build How-To | Details].)
196 The result is dependent only upon widespread platform libraries with
197 stable ABIs such as glibc, zlib, etc.
198
199 Full static linking is easier on Windows, so our precompiled Windows
200 binaries are just a ZIP archive
201 containing only "<tt>fossil.exe</tt>". There is no "<tt>setup.exe</tt>"
202 to run.
 
 
 
 
 
 
 
203
204 Fossil is easy to build from sources. Just run
205 "<tt>./configure && make</tt>" on POSIX systems and
206 "<tt>nmake /f Makefile.msc</tt>" on Windows.
207
@@ -208,18 +224,20 @@
224 matters is effectiveness and efficiency. We believe Fossil achieves
225 this.
226
227 The above size comparisons aren't apples-to-apples anyway. We've
228 compared the size of Fossil with all of its [#features | many built-in
229 features] to a fairly minimal Git installation. You must add a lot of
230 third-party software to Git to give it a Fossil-equivalent feature set.
231 Consider [https://about.gitlab.com/|GitLab], a third-party extension to
232 Git wrapping it in many features, making it roughly Fossil-equivalent,
 
233 though [https://docs.gitlab.com/ee/install/requirements.html|much more
234 resource hungry] and hence more costly to run than the equivalent Fossil
235 setup. [https://hub.docker.com/r/gitlab/gitlab-ce/ | The official GitLab
236 Community Edition container] currently clocks in at 2.66 GiB!
237
238 GitLab's requirements are easy to accept when you're dedicating
239 a local rack server or blade to it, since its minimum requirements are
240 more or less a description of the smallest
241 thing you could call a "server" these days, but when you go to host that
242 in the cloud, you can expect to pay about 8 times as much to comfortably host
243 GitLab as for Fossil.³ This difference is largely due to basic
244

Keyboard Shortcuts

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