Fossil

Check-in [2b1a4797]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge latest changes from trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | symlinks
Files: files | file ages | folders
SHA1:2b1a4797ddb731960250fe81a9b9bc6556d67c3a
User & Date: dmitry 2011-09-01 23:18:53
Context
2011-09-01
23:44
Fix indentation in update.c. check-in: 1d54dce4 user: dmitry tags: symlinks
23:18
Merge latest changes from trunk. check-in: 2b1a4797 user: dmitry tags: symlinks
22:56
Refactor file_perm(), file_islink(), and file_isexe(). Now file_perm() calls stat only once. check-in: eac23495 user: dmitry tags: symlinks
22:22
Fix typos and other cleanup on the "build.wiki" doc page. check-in: 0f9fb203 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Deleted Makefile.in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/make
#
# This is the top-level makefile for Fossil when the build is occurring
# on a unix platform.  This works out-of-the-box on most unix platforms.
# But you are free to vary some of the definitions if desired.
#
#### The toplevel directory of the source tree.  Fossil can be built
#    in a directory that is separate from the source tree.  Just change
#    the following to point from the build directory to the src/ folder.
#
SRCDIR = @srcdir@/src

#### The directory into which object code files should be written.
#
#
OBJDIR = ./bld

#### C Compiler and options for use in building executables that
#    will run on the platform that is doing the build.  This is used
#    to compile code-generator programs as part of the build process.
#    See TCC below for the C compiler for building the finished binary.
#
BCC = @CC_FOR_BUILD@

#### The suffix to add to final executable file.  When cross-compiling
#    to windows, make this ".exe".  Otherwise leave it blank.
#
E = @EXEEXT@

TCC = @CC@

#### Tcl shell for use in running the fossil testsuite.  If you do not
#    care about testing the end result, this can be blank.
#
TCLSH = tclsh

LIB =	@LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
TCC +=	@EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
INSTALLDIR = $(DESTDIR)@prefix@/bin
USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@

include $(SRCDIR)/main.mk

distclean: clean
	rm -f autoconfig.h config.log Makefile
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


























































































Changes to src/db.c.

759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774

/*
** If zDbName is a valid local database file, open it and return
** true.  If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
  i64 lsize;
  int rc;
  sqlite3_stmt *pStmt;

  if( file_access(zDbName, F_OK) ) return 0;
  lsize = file_size(zDbName);
  if( lsize%1024!=0 || lsize<4096 ) return 0;
  db_open_or_attach(zDbName, "localdb");
  g.localOpen = 1;
  db_open_config(0);







<
<







759
760
761
762
763
764
765


766
767
768
769
770
771
772

/*
** If zDbName is a valid local database file, open it and return
** true.  If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
  i64 lsize;



  if( file_access(zDbName, F_OK) ) return 0;
  lsize = file_size(zDbName);
  if( lsize%1024!=0 || lsize<4096 ) return 0;
  db_open_or_attach(zDbName, "localdb");
  g.localOpen = 1;
  db_open_config(0);

Changes to src/http_ssl.c.

278
279
280
281
282
283
284











285
286
287
288
289
290
291
      return 1;
    }
    if( blob_str(&ans)[0]=='a' ) {
      ssl_save_certificate(cert);
    }
    blob_reset(&ans);
  }











  X509_free(cert);
  return 0;
}

/*
** Save certificate to global config.
*/







>
>
>
>
>
>
>
>
>
>
>







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
      return 1;
    }
    if( blob_str(&ans)[0]=='a' ) {
      ssl_save_certificate(cert);
    }
    blob_reset(&ans);
  }

  /* Set the Global.zIpAddr variable to the server we are talking to.
  ** This is used to populate the ipaddr column of the rcvfrom table,
  ** if any files are received from the server.
  */
  {
      /* IPv4 only code */
      const unsigned char *ip = (const unsigned char *) BIO_get_conn_ip(iBio);
      g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
  }

  X509_free(cert);
  return 0;
}

/*
** Save certificate to global config.
*/

Changes to src/tkt.c.

897
898
899
900
901
902
903




904
905
906
907
908
909
910
...
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
...
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982




983

984
985
986
987
988
989
990
...
994
995
996
997
998
999
1000
1001
1002
1003













































































1004
1005
1006
1007
1008
1009
1010
**         in "ticket show". So it's possible, to set multiline text or
**         text with special characters.
**
**     %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
**
**         like set, but create a new ticket with the given values.
**




** The values in set|add are not validated against the definitions
** given in "Ticket Common Script".
*/
void ticket_cmd(void){
  int n;

  /* do some ints, we want to be inside a checkout */
................................................................................
  ** Check that the user exists.
  */
  if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
    fossil_fatal("no such user: %s", g.zLogin);
  }

  if( g.argc<3 ){
    usage("add|fieldlist|set|show");
  }else{
    n = strlen(g.argv[2]);
    if( n==1 && g.argv[2][0]=='s' ){
      /* set/show cannot be distinguished, so show the usage */
      usage("add|fieldlist|set|show");
    }else if( strncmp(g.argv[2],"list",n)==0 ){
      if( g.argc==3 ){
        usage("list fields|reports");
      }else{
        n = strlen(g.argv[3]);
        if( !strncmp(g.argv[3],"fields",n) ){
          /* simply show all field names */
................................................................................
          }

          rptshow( zRep, zSep, zFilterUuid, tktEncoding );

        }
      }else{
        /* add a new ticket or update an existing ticket */
        enum { set,add,err } eCmd = err;
        int i = 0;
        int rid;
        const char *zTktUuid = 0;
        Blob tktchng, cksum;

        /* get command type (set/add) and get uuid, if needed for set */
        if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ){




          eCmd = set;

          if( g.argc==3 ){
            usage("set TICKETUUID");
          }
          zTktUuid = db_text(0, 
            "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
          );
          if( !zTktUuid ){
................................................................................
        }else if( strncmp(g.argv[2],"add",n)==0 ){
          eCmd = add;
          i = 3;
          zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
        }
        /* none of set/add, so show the usage! */
        if( eCmd==err ){
          usage("add|fieldlist|set|show");
        }
        













































































        /* read all given ticket field/value pairs from command line */
        if( i==g.argc ){
          fossil_fatal("empty %s command aborted!",g.argv[2]);
        }
        getAllTicketFields();
        /* read commandline and assign fields in the azValue array */
        while( i<g.argc ){







>
>
>
>







 







|




|







 







|






|
>
>
>
>
|
>







 







|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
...
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
...
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
....
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
**         in "ticket show". So it's possible, to set multiline text or
**         text with special characters.
**
**     %fossil ticket add FIELD VALUE ?FIELD VALUE .. ? ?-q|--quote?
**
**         like set, but create a new ticket with the given values.
**
**     %fossil ticket history TICKETUUID
**
**         Show the complete change history for the ticket
**
** The values in set|add are not validated against the definitions
** given in "Ticket Common Script".
*/
void ticket_cmd(void){
  int n;

  /* do some ints, we want to be inside a checkout */
................................................................................
  ** Check that the user exists.
  */
  if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
    fossil_fatal("no such user: %s", g.zLogin);
  }

  if( g.argc<3 ){
    usage("add|fieldlist|set|show|history");
  }else{
    n = strlen(g.argv[2]);
    if( n==1 && g.argv[2][0]=='s' ){
      /* set/show cannot be distinguished, so show the usage */
      usage("add|fieldlist|set|show|history");
    }else if( strncmp(g.argv[2],"list",n)==0 ){
      if( g.argc==3 ){
        usage("list fields|reports");
      }else{
        n = strlen(g.argv[3]);
        if( !strncmp(g.argv[3],"fields",n) ){
          /* simply show all field names */
................................................................................
          }

          rptshow( zRep, zSep, zFilterUuid, tktEncoding );

        }
      }else{
        /* add a new ticket or update an existing ticket */
        enum { set,add,history,err } eCmd = err;
        int i = 0;
        int rid;
        const char *zTktUuid = 0;
        Blob tktchng, cksum;

        /* get command type (set/add) and get uuid, if needed for set */
        if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
           strncmp(g.argv[2],"history",n)==0 ){
          if( strncmp(g.argv[2],"history",n)==0 ){
            eCmd = history;
          }else{
            eCmd = set;
          }
          if( g.argc==3 ){
            usage("set TICKETUUID");
          }
          zTktUuid = db_text(0, 
            "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
          );
          if( !zTktUuid ){
................................................................................
        }else if( strncmp(g.argv[2],"add",n)==0 ){
          eCmd = add;
          i = 3;
          zTktUuid = db_text(0, "SELECT lower(hex(randomblob(20)))");
        }
        /* none of set/add, so show the usage! */
        if( eCmd==err ){
          usage("add|fieldlist|set|show|history");
        }

        /* we just handle history separately here, does not get out */
        if( eCmd==history ){
          Stmt q;
          char *zTitle;
          int tagid;

          if ( i != g.argc ){
            fossil_fatal("no other parameters expected to %s!",g.argv[2]);
          }
          tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zTktUuid);
          if( tagid==0 ){
            fossil_fatal("no such ticket %h", zTktUuid);
          }  
          db_prepare(&q,
            "SELECT datetime(mtime,'localtime'), objid, uuid, NULL, NULL, NULL"
            "  FROM event, blob"
            " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
            "   AND blob.rid=event.objid"
            " UNION "
            "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
            "  FROM attachment, blob"
            " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
            "   AND blob.rid=attachid"
            " ORDER BY 1 DESC",
            tagid, tagid
          );
          while( db_step(&q)==SQLITE_ROW ){
            Manifest *pTicket;
            char zShort[12];
            const char *zDate = db_column_text(&q, 0);
            int rid = db_column_int(&q, 1);
            const char *zChngUuid = db_column_text(&q, 2);
            const char *zFile = db_column_text(&q, 4);
            memcpy(zShort, zChngUuid, 10);
            zShort[10] = 0;
            if( zFile!=0 ){
              const char *zSrc = db_column_text(&q, 3);
              const char *zUser = db_column_text(&q, 5);
              if( zSrc==0 || zSrc[0]==0 ){
                fossil_print("Delete attachment %h\n", zFile);
              }else{
                fossil_print("Add attachment %h\n", zFile);
              }
              fossil_print(" by %h on %h\n", zUser, zDate);
            }else{
              pTicket = manifest_get(rid, CFTYPE_TICKET);
              if( pTicket ){
                int i;

                fossil_print("Ticket Change by %h on %h:\n", pTicket->zUser, zDate);
                for(i=0; i<pTicket->nField; i++){
                  Blob val;
                  const char *z;
                  z = pTicket->aField[i].zName;
                  blob_set(&val, pTicket->aField[i].zValue);
                  if( z[0]=='+' ){
                    fossil_print("  Append to ");
		    z++;
		  }else{
		    fossil_print("  Change ");
                  }
		  fossil_print("%h: ",z);
		  if( blob_size(&val)>50 || contains_newline(&val)) {
                    fossil_print("\n    ",blob_str(&val));
                    comment_print(blob_str(&val),4,79);
                  }else{
                    fossil_print("%s\n",blob_str(&val));
                  }
                  blob_reset(&val);
                }
              }
              manifest_destroy(pTicket);
            }
          }
          db_finalize(&q);
          return;
        }
        /* read all given ticket field/value pairs from command line */
        if( i==g.argc ){
          fossil_fatal("empty %s command aborted!",g.argv[2]);
        }
        getAllTicketFields();
        /* read commandline and assign fields in the azValue array */
        while( i<g.argc ){

Added test/merge_renames.test.































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#
# Tests for merging with renames
# 
#

catch {exec $::fossilexe info} res
puts res=$res
if {![regexp {not within an open checkout} $res]} {
  puts stderr "Cannot run this test within an open checkout"
  return
}

######################################
#  Test 1                            #
#  Reported: Ticket [554f44ee74e3d]  #
######################################

fossil new rep.fossil
fossil open rep.fossil

write_file f1 "line"
fossil add f1
fossil commit -m "c1"
fossil tag add pivot current

write_file f1 "line2"
fossil commit -m "c2"

write_file f1 "line3"
fossil commit -m "c3"

write_file f1 "line4"
fossil commit -m "c4"

write_file f1 "line5"
fossil commit -m "c4"

write_file f1 "line6"
fossil commit -m "c4"

fossil update pivot
fossil mv f1 f2
exec mv f1 f2
fossil commit -b rename -m "c5"

fossil merge trunk
fossil commit -m "trunk merged"

fossil update pivot
write_file f3 "someline"
fossil add f3
fossil commit -b branch2 -m "newbranch"

fossil merge trunk
puts $RESULT

set deletes 0
foreach {status filename} $RESULT {
    if {$status=="DELETE"} {
        set deletes [expr $deletes + 1]
    }
}

if {$deletes!=0} {
    # failed
    protOut "Error, the merge should not delete any file"
    test merge_renames-1 0
} else {
    test merge_renames-1 1
}

fossil close -f
exec rm rep.fossil

######################################
#  Test 2                            #
#  Reported: Ticket [74413366fe5067] #
######################################

fossil new rep.fossil
fossil open rep.fossil

write_file f1 "line"
fossil add f1
fossil commit -m "base file"
fossil tag add pivot current

write_file f2 "line2"
fossil add f2
fossil commit -m "newfile"

fossil mv f2 f2new
exec mv f2 f2new
fossil commit -m "rename"

fossil update pivot
write_file f1 "line3"
fossil commit -b branch -m "change"

fossil merge trunk
fossil commit -m "trunk merged"

fossil update trunk

fossil merge branch
puts $RESULT

# Not a nice way to check, but I don't know more tcl now
set deletes 0
foreach {status filename} $RESULT {
    if {$status=="DELETE"} {
        set deletes [expr $deletes + 1]
    }
}

if {$deletes!=0} {
    # failed
    protOut "Error, the merge should not delete any file"
    test merge_renames-2 0
} else {
    test merge_renames-2 1
}

fossil close -f
exec rm rep.fossil

######################################
#  Test 3                            #
#  Reported: Ticket [30b28cf351]     #
######################################

fossil new rep.fossil
fossil open rep.fossil

write_file f1 "line"
fossil add f1
fossil commit -m "base file"
fossil tag add pivot current

write_file f2 "line2"
fossil add f2
fossil commit -m "newfile"

fossil mv f2 f2new
exec mv f2 f2new
fossil commit -m "rename"

fossil update pivot
write_file f1 "line3"
fossil commit -b branch -m "change"

fossil merge trunk
fossil commit -m "trunk merged"

fossil update trunk

fossil merge branch
puts $RESULT

# Not a nice way to check, but I don't know more tcl now
set deletes 0
foreach {status filename} $RESULT {
    if {$status=="DELETE"} {
        set deletes [expr $deletes + 1]
    }
}

if {$deletes!=0} {
    # failed
    protOut "Error, the merge should not delete any file"
    test merge_renames-2 0
} else {
    test merge_renames-2 1
}

fossil close -f
exec rm rep.fossil

######################################
#  Test 4                            #
#  Reported: Ticket [67176c3aa4]     #
######################################

# TO BE WRITTEN.


# Tests for troubles not specifically linked with renames but that I'd like to
# write:
#  [c26c63eb1b] - 'merge --backout' does not handle conflicts properly
#  [953031915f] - Lack of warning when overwriting extra files 
#  [4df5f38f1e] - Troubles merging a file delete with a file change 

Changes to www/build.wiki.

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
..
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<h2>1.0 Obtaining The Source Code</h2>

<p>Fossil is self-hosting, so you can obtain a ZIP archive containing
a snapshot of the latest version directly from fossil's own fossil
repository.  Follow these steps:</p>

<ol>
<li><p>Pointer your web browser at
<a href="http://www.fossil-scm.org/">
http://www.fossil-scm.org/</a>.  Click on the "Login" menu button.</p></li>

<li><p>Log in as anonymous.  The password is shown on screen.
The reason for requiring this login is to prevent spiders from
walking the entire website, downloading ZIP archives
of every historical version, and thereby soaking up all our bandwidth.</p></li>

<li><p>Click on the 
<a href="http://www.fossil-scm.org/fossil/timeline">Timeline</a> or
<a href="http://www.fossil-scm.org/fossil/leaves">Leaves</a> link at
the top of the page.</p></li>

<li><p>Select a version of of fossil you want to download.  Click on its
link.  Note that you must successfully log in as "anonymous" in step 1
above in order to see the link to the detailed version information.</p></li>

<li><p>Finally, click on one of the
"Zip Archive" or "Tarball" links, according to your preference.
................................................................................
complete source code and download it to your browser.
</ol>

<h2>2.0 Compiling</h2>

<ol>
<li value="6">
<p>Create a directory to hold the source code.  Then unarchive the
ZIP or tarball you downloaded into that directory.  You should be
in the top-level folder of that directory</p></li>

<li><i>(Optional, unix only)</i>
Run <b>./configure</b> to construct a makefile.

<ol type="a">
<li><p>
If you do not have the OpenSSL library installed on your system, then







|









|
<
|







 







|
|
<







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
..
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
<h2>1.0 Obtaining The Source Code</h2>

<p>Fossil is self-hosting, so you can obtain a ZIP archive containing
a snapshot of the latest version directly from fossil's own fossil
repository.  Follow these steps:</p>

<ol>
<li><p>Point your web browser at
<a href="http://www.fossil-scm.org/">
http://www.fossil-scm.org/</a>.  Click on the "Login" menu button.</p></li>

<li><p>Log in as anonymous.  The password is shown on screen.
The reason for requiring this login is to prevent spiders from
walking the entire website, downloading ZIP archives
of every historical version, and thereby soaking up all our bandwidth.</p></li>

<li><p>Click on the 
<a href="http://www.fossil-scm.org/fossil/timeline">Timeline</a> 

link at the top of the page.</p></li>

<li><p>Select a version of of fossil you want to download.  Click on its
link.  Note that you must successfully log in as "anonymous" in step 1
above in order to see the link to the detailed version information.</p></li>

<li><p>Finally, click on one of the
"Zip Archive" or "Tarball" links, according to your preference.
................................................................................
complete source code and download it to your browser.
</ol>

<h2>2.0 Compiling</h2>

<ol>
<li value="6">
<p>Unpack the ZIP or tarball you downloaded into that directory then
<b>cd</b> into the directory created.</p></li>


<li><i>(Optional, unix only)</i>
Run <b>./configure</b> to construct a makefile.

<ol type="a">
<li><p>
If you do not have the OpenSSL library installed on your system, then