Fossil

Changes On Branch andygoth-enhanced-symlink
Login

Changes On Branch andygoth-enhanced-symlink

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

Changes In Branch andygoth-enhanced-symlink Excluding Merge-Ins

This is equivalent to a diff from 50844e5c to 95b6fad9

2017-10-16
13:54
Improvements to automatic hyperlink enabling and robot exclusion. ... (check-in: f014e156 user: drh tags: trunk)
2017-10-14
23:20
Update /dir to support readme and readme.* files being a symlink to the actual document. Symlinks to symlinks are not supported in this instance. The document type is determined by the link target name. ... (Closed-Leaf check-in: eb4dda48 user: andygoth tags: andygoth-doc-symlink)
22:49
Remove unnecessary double NUL-termination from blob ... (Closed-Leaf check-in: 95b6fad9 user: andygoth tags: andygoth-enhanced-symlink)
22:22
Update change log to describe Windows behavior of "l" flag to "manifest" setting ... (check-in: 810370cd user: andygoth tags: andygoth-enhanced-symlink)
19:28
Merge trunk ... (check-in: 057645a9 user: andygoth tags: andygoth-enhanced-symlink)
2017-10-12
13:37
Rewrote the autoreconfigure explanation comment. It was confusing and contained errors. ... (check-in: 50844e5c user: wyoung tags: trunk)
2017-10-11
19:56
Removed the GNU make conditional around the new autoreconfigure dependency rule in the top-level Makefile.in. Apparently we have users using some other make, perhaps BSD make. Since the conditional only prevents autoreconfigure in the "make clean" case, where it is a minor annoyance, it's not important to work out how to retain this in a non-GNU make world. ... (check-in: 454e1193 user: wyoung tags: trunk)

Changes to src/add.c.

76
77
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
  /* Possible names of auxiliary files generated when the "manifest" property
  ** is used
  */
  static const struct {
    const char *fname;
    int flg;
  }aManifestflags[] = {
    { "manifest",      MFESTFLG_RAW },
    { "manifest.uuid", MFESTFLG_UUID },
    { "manifest.tags", MFESTFLG_TAGS }

  };
  static const char *azManifests[3];

  /*
  ** Names of repository files, if they exist in the checkout.
  */
  static const char *azRepo[4] = { 0, 0, 0, 0 };

  /* Cached setting "manifest" */







|
|
|
>

|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  /* Possible names of auxiliary files generated when the "manifest" property
  ** is used
  */
  static const struct {
    const char *fname;
    int flg;
  }aManifestflags[] = {
    { "manifest",          MFESTFLG_RAW },
    { "manifest.uuid",     MFESTFLG_UUID },
    { "manifest.tags",     MFESTFLG_TAGS },
    { "manifest.symlinks", MFESTFLG_SYMLINKS }
  };
  static const char *azManifests[4];

  /*
  ** Names of repository files, if they exist in the checkout.
  */
  static const char *azRepo[4] = { 0, 0, 0, 0 };

  /* Cached setting "manifest" */

Changes to src/checkin.c.

1497
1498
1499
1500
1501
1502
1503






1504
1505
1506
1507
1508
1509
1510
  int nBasename;              /* Size of base filename */
  Stmt q;                     /* Various queries */
  Blob mcksum;                /* Manifest checksum */
  ManifestFile *pFile;        /* File from the baseline */
  int nFBcard = 0;            /* Number of B-cards and F-cards */
  int i;                      /* Loop counter */
  const char *zColor;         /* Modified value of p->zColor */







  assert( pBaseline==0 || pBaseline->zBaseline==0 );
  assert( pBaseline==0 || zBaselineUuid!=0 );
  blob_zero(pOut);
  if( vid ){
    zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d AND "
      "EXISTS(SELECT 1 FROM event WHERE event.type='ci' and event.objid=%d)",







>
>
>
>
>
>







1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
  int nBasename;              /* Size of base filename */
  Stmt q;                     /* Various queries */
  Blob mcksum;                /* Manifest checksum */
  ManifestFile *pFile;        /* File from the baseline */
  int nFBcard = 0;            /* Number of B-cards and F-cards */
  int i;                      /* Loop counter */
  const char *zColor;         /* Modified value of p->zColor */

  /* On Windows, get symlink permission status from the "manifest.symlinks" file
  ** if it exists and if the "manifest" setting contains the "l" flag. */
#ifdef _WIN32
  int manifestSymlinks = get_checkout_symlink_table();
#endif

  assert( pBaseline==0 || pBaseline->zBaseline==0 );
  assert( pBaseline==0 || zBaselineUuid!=0 );
  blob_zero(pOut);
  if( vid ){
    zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d AND "
      "EXISTS(SELECT 1 FROM event WHERE event.type='ci' and event.objid=%d)",
1551
1552
1553
1554
1555
1556
1557

1558







1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
    int isSelected = db_column_int(&q, 6);
    const char *zPerm;
    int cmp;

    blob_resize(&filename, nBasename);
    blob_append(&filename, zName, -1);


#if !defined(_WIN32)







    /* For unix, extract the "executable" and "symlink" permissions
    ** directly from the filesystem.  On windows, permissions are
    ** unchanged from the original.  However, only do this if the file
    ** itself is actually selected to be part of this check-in.
    */
    if( isSelected ){
      int mPerm;

      mPerm = file_wd_perm(blob_str(&filename));
      isExe = ( mPerm==PERM_EXE );
      isLink = ( mPerm==PERM_LNK );
    }







>
|
>
>
>
>
>
>
>


|
<
<







1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575


1576
1577
1578
1579
1580
1581
1582
    int isSelected = db_column_int(&q, 6);
    const char *zPerm;
    int cmp;

    blob_resize(&filename, nBasename);
    blob_append(&filename, zName, -1);

    /* Potentially update the permissions of files selected to be checked in. */
#ifdef _WIN32
    /* For Windows, if the "manifest" setting contains the "l" flag and the
    ** "manifest.symlinks" file exists, use its contents to determine which
    ** files do and do not have the symlink permission. */
    if( isSelected && manifestSymlinks ){
      isLink = db_exists("SELECT 1 FROM symlink_perm WHERE filename=%Q", zName);
    }
#else
    /* For unix, extract the "executable" and "symlink" permissions
    ** directly from the filesystem.  On windows, permissions are
    ** unchanged from the original. */


    if( isSelected ){
      int mPerm;

      mPerm = file_wd_perm(blob_str(&filename));
      isExe = ( mPerm==PERM_EXE );
      isLink = ( mPerm==PERM_LNK );
    }
2611
2612
2613
2614
2615
2616
2617










2618
2619
2620
2621
2622
2623
2624
2625
    zManifestFile = mprintf("%smanifest.tags", g.zLocalRoot);
    blob_zero(&tagslist);
    get_checkin_taglist(nvid, &tagslist);
    blob_write_to_file(&tagslist, zManifestFile);
    blob_reset(&tagslist);
    free(zManifestFile);
  }











  if( !g.markPrivate ){
    autosync_loop(SYNC_PUSH|SYNC_PULL, db_get_int("autosync-tries", 1), 0);
  }
  if( count_nonbranch_children(vid)>1 ){
    fossil_print("**** warning: a fork has occurred *****\n");
  }
}







>
>
>
>
>
>
>
>
>
>








2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
    zManifestFile = mprintf("%smanifest.tags", g.zLocalRoot);
    blob_zero(&tagslist);
    get_checkin_taglist(nvid, &tagslist);
    blob_write_to_file(&tagslist, zManifestFile);
    blob_reset(&tagslist);
    free(zManifestFile);
  }

  if( outputManifest & MFESTFLG_SYMLINKS ){
    Blob symlinkslist;
    zManifestFile = mprintf("%smanifest.symlinks", g.zLocalRoot);
    blob_zero(&symlinkslist);
    get_checkin_symlinklist(nvid, &symlinkslist);
    blob_write_to_file(&symlinkslist, zManifestFile);
    blob_reset(&symlinkslist);
    free(zManifestFile);
  }

  if( !g.markPrivate ){
    autosync_loop(SYNC_PUSH|SYNC_PULL, db_get_int("autosync-tries", 1), 0);
  }
  if( count_nonbranch_children(vid)>1 ){
    fossil_print("**** warning: a fork has occurred *****\n");
  }
}

Changes to src/checkout.c.

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  db_bind_text(&s, ":path", zFilename);
  db_step(&s);
  db_reset(&s);
}

/*
** Set or clear the execute permission bit (as appropriate) for all
** files in the current check-out, and replace files that have
** symlink bit with actual symlinks.
*/
void checkout_set_all_exe(int vid){
  Blob filename;
  int baseLen;
  Manifest *pManifest;
  ManifestFile *pFile;








|
<







91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
  db_bind_text(&s, ":path", zFilename);
  db_step(&s);
  db_reset(&s);
}

/*
** Set or clear the execute permission bit (as appropriate) for all
** files in the current check-out.

*/
void checkout_set_all_exe(int vid){
  Blob filename;
  int baseLen;
  Manifest *pManifest;
  ManifestFile *pFile;

126
127
128
129
130
131
132
133
134
135
136
137
138
139

140
141
142
143
144
145
146


/*
** If the "manifest" setting is true, then automatically generate
** files named "manifest" and "manifest.uuid" containing, respectively,
** the text of the manifest and the artifact ID of the manifest.
** If the manifest setting is set, but is not a boolean value, then treat
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
  char *zManFile;
  Blob manifest;
  Blob taglist;

  int flg;

  flg = db_get_manifest_setting();

  if( flg & MFESTFLG_RAW ){
    blob_zero(&manifest);
    content_get(vid, &manifest);







|
|





>







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146


/*
** If the "manifest" setting is true, then automatically generate
** files named "manifest" and "manifest.uuid" containing, respectively,
** the text of the manifest and the artifact ID of the manifest.
** If the manifest setting is set, but is not a boolean value, then treat
** each character as a flag to enable writing "manifest", "manifest.uuid",
** "manifest.tags", or "manifest.symlinks".
*/
void manifest_to_disk(int vid){
  char *zManFile;
  Blob manifest;
  Blob taglist;
  Blob symlinklist;
  int flg;

  flg = db_get_manifest_setting();

  if( flg & MFESTFLG_RAW ){
    blob_zero(&manifest);
    content_get(vid, &manifest);
180
181
182
183
184
185
186














187
188
189
190
191
192
193
  }else{
    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
      zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
      file_delete(zManFile);
      free(zManFile);
    }
  }














}

/*
** Find the branch name and all symbolic tags for a particular check-in
** identified by "rid".
**
** The branch name is actually only extracted if this procedure is run







>
>
>
>
>
>
>
>
>
>
>
>
>
>







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  }else{
    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
      zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
      file_delete(zManFile);
      free(zManFile);
    }
  }
  if( flg & MFESTFLG_SYMLINKS ){
    blob_zero(&symlinklist);
    zManFile = mprintf("%smanifest.symlinks", g.zLocalRoot);
    get_checkin_symlinklist(vid, &symlinklist);
    blob_write_to_file(&symlinklist, zManFile);
    free(zManFile);
    blob_reset(&symlinklist);
  }else{
    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.symlinks'") ){
      zManFile = mprintf("%smanifest.symlinks", g.zLocalRoot);
      file_delete(zManFile);
      free(zManFile);
    }
  }
}

/*
** Find the branch name and all symbolic tags for a particular check-in
** identified by "rid".
**
** The branch name is actually only extracted if this procedure is run
214
215
216
217
218
219
220












































































221
222
223
224
225
226
227
    zName = db_column_text(&stmt, 0);
    blob_appendf(pOut, "tag %s\n", zName);
  }
  db_reset(&stmt);
  db_finalize(&stmt);
}














































































/*
** COMMAND: checkout*
** COMMAND: co*
**
** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
**    or: %fossil co ?VERSION | --latest? ?OPTIONS?







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
    zName = db_column_text(&stmt, 0);
    blob_appendf(pOut, "tag %s\n", zName);
  }
  db_reset(&stmt);
  db_finalize(&stmt);
}

/*
** Store into "pOut" a sorted list of all symlinks in a checkin "rid".  Each
** entry in the list is the full name of the symlink (not the name of its
** target) followed by a newline.
*/
void get_checkin_symlinklist(int rid, Blob *pOut){
  Manifest *pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
  ManifestFile *pFile;
  blob_reset(pOut);
  manifest_file_rewind(pManifest);
  while( (pFile = manifest_file_next(pManifest, 0)) ){
    if( pFile->zPerm && strstr(pFile->zPerm, "l") ){
      blob_appendf(pOut, "%s\n", pFile->zName);
    }
  }
  manifest_destroy(pManifest);
}

#ifdef _WIN32
/*
** Create a temporary table called "symlink_perm" containing the names of all
** files considered to be symlinks.  This function only exists in Windows
** because Unix symlink status comes directly from the filesystem.  The return
** value is 1 if the table was created or 0 if symlink status is to be inherited
** from the baseline check-in manifest.  The latter case occurs when the file
** does not exist or when the "manifest" setting does not contain the "l" flag.
*/
int get_checkout_symlink_table(void){
  Blob content = BLOB_INITIALIZER;
  char *zFile, *zLine, *zEnd;

  /* If the "manifest" setting lacks the "l" flag, do no further processing.
   * Symlink status will be inherited from the previous check-in. */
  if( !(db_get_manifest_setting() & MFESTFLG_SYMLINKS) ){
    return 0;
  }

  /* If the "manifest.symlinks" file does not exist, act as if the "manifest"
   * setting didn't have "l".  The file will be regenerated with the next commit
   * or update, but for now, temporarily disable symlink status updating. */
  zFile = mprintf("%smanifest.symlinks", g.zLocalRoot);
  if( file_wd_size(zFile)<0 ){
    return 0;
  }

  /* Read "manifest.symlinks" into a blob to be analyzed.  Simplify processing
   * by forcing it to end with newline.  (Blobs are always NUL-terminated.) */
  blob_read_from_file(&content, zFile);
  blob_append(&content, "\n", 1);
  zLine = blob_buffer(&content);

  /* Insert each non-empty line of "manifest.symlinks" into the "symlink_perm"
   * temporary table. */
  db_begin_transaction();
  db_multi_exec("CREATE TEMP TABLE IF NOT EXISTS symlink_perm("
                "filename TEXT PRIMARY KEY %s)", filename_collation());
  while( *zLine ){
    /* Find end of line and replace with NUL. */
    for( zEnd = zLine; *zEnd!='\r' && *zEnd!='\n'; ++zEnd );
    *zEnd = 0;

    /* If not a blank line, insert filename into symlink table. */
    if( *zLine ){
      db_multi_exec("INSERT OR IGNORE INTO symlink_perm VALUES(%Q)", zLine);
    }

    /* Find start of next line, or find terminating NUL at end of file. */
    for( zLine = zEnd+1; *zLine=='\r' || *zLine=='\n'; ++zLine );
  }
  db_end_transaction(0);
  blob_reset(&content);

  /* Let the caller know the "symlink_perm" table was created and is valid. */
  return 1;
}
#endif

/*
** COMMAND: checkout*
** COMMAND: co*
**
** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
**    or: %fossil co ?VERSION | --latest? ?OPTIONS?

Changes to src/db.c.

2473
2474
2475
2476
2477
2478
2479
2480
2481
2482

2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505

2506
2507
2508
2509
2510
2511
2512
}
void db_lset_int(const char *zName, int value){
  db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
}

#if INTERFACE
/* Manifest generation flags */
#define MFESTFLG_RAW  0x01
#define MFESTFLG_UUID 0x02
#define MFESTFLG_TAGS 0x04

#endif /* INTERFACE */

/*
** Get the manifest setting.  For backwards compatibility first check if the
** value is a boolean.  If it's not a boolean, treat each character as a flag
** to enable a manifest type.  This system puts certain boundary conditions on
** which letters can be used to represent flags (any permutation of flags must
** not be able to fully form one of the boolean values).
*/
int db_get_manifest_setting(void){
  int flg;
  char *zVal = db_get("manifest", 0);
  if( zVal==0 || is_false(zVal) ){
    return 0;
  }else if( is_truth(zVal) ){
    return MFESTFLG_RAW|MFESTFLG_UUID;
  }
  flg = 0;
  while( *zVal ){
    switch( *zVal ){
      case 'r': flg |= MFESTFLG_RAW;  break;
      case 'u': flg |= MFESTFLG_UUID; break;
      case 't': flg |= MFESTFLG_TAGS; break;

    }
    zVal++;
  }
  return flg;
}









|
|
|
>




















|
|
|
>







2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
}
void db_lset_int(const char *zName, int value){
  db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
}

#if INTERFACE
/* Manifest generation flags */
#define MFESTFLG_RAW      0x01
#define MFESTFLG_UUID     0x02
#define MFESTFLG_TAGS     0x04
#define MFESTFLG_SYMLINKS 0x08
#endif /* INTERFACE */

/*
** Get the manifest setting.  For backwards compatibility first check if the
** value is a boolean.  If it's not a boolean, treat each character as a flag
** to enable a manifest type.  This system puts certain boundary conditions on
** which letters can be used to represent flags (any permutation of flags must
** not be able to fully form one of the boolean values).
*/
int db_get_manifest_setting(void){
  int flg;
  char *zVal = db_get("manifest", 0);
  if( zVal==0 || is_false(zVal) ){
    return 0;
  }else if( is_truth(zVal) ){
    return MFESTFLG_RAW|MFESTFLG_UUID;
  }
  flg = 0;
  while( *zVal ){
    switch( *zVal ){
      case 'r': flg |= MFESTFLG_RAW;      break;
      case 'u': flg |= MFESTFLG_UUID;     break;
      case 't': flg |= MFESTFLG_TAGS;     break;
      case 'l': flg |= MFESTFLG_SYMLINKS; break;
    }
    zVal++;
  }
  return flg;
}


2583
2584
2585
2586
2587
2588
2589
2590

2591
2592
2593
2594
2595
2596
2597
2598
** COMMAND: open
**
** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS?
**
** Open a connection to the local repository in FILENAME.  A checkout
** for the repository is created with its root at the working directory.
** If VERSION is specified then that version is checked out.  Otherwise
** the latest version is checked out.  No files other than "manifest"

** and "manifest.uuid" are modified if the --keep option is present.
**
** Options:
**   --empty           Initialize checkout as being empty, but still connected
**                     with the local repository. If you commit this checkout,
**                     it will become a new "initial" commit in the repository.
**   --keep            Only modify the manifest and manifest.uuid files
**   --nested          Allow opening a repository inside an opened checkout







|
>
|







2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
** COMMAND: open
**
** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS?
**
** Open a connection to the local repository in FILENAME.  A checkout
** for the repository is created with its root at the working directory.
** If VERSION is specified then that version is checked out.  Otherwise
** the latest version is checked out.  If the --keep option is present,
** no files other than "manifest", "manifest.uuid", "manifest.tags", and
** "manifest.symlinks" are modified.
**
** Options:
**   --empty           Initialize checkout as being empty, but still connected
**                     with the local repository. If you commit this checkout,
**                     it will become a new "initial" commit in the repository.
**   --keep            Only modify the manifest and manifest.uuid files
**   --nested          Allow opening a repository inside an opened checkout
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
** The value is the primary branch for the project.
*/
/*
** SETTING: manifest         width=5 versionable
** If enabled, automatically create files "manifest" and "manifest.uuid"
** in every checkout.
**
** Optionally use combinations of characters 'r' for "manifest",
** 'u' for "manifest.uuid" and 't' for "manifest.tags".  The SQLite
** and Fossil repositories both require manifests.
*/
/*
** SETTING: max-loadavg      width=25 default=0.0
** Some CPU-intensive web pages (ex: /zip, /tarball, /blame)
** are disallowed if the system load average goes above this
** value.  "0.0" means no limit.  This only works on unix.
** Only local settings of this value make a difference since







|
|
|







2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
** The value is the primary branch for the project.
*/
/*
** SETTING: manifest         width=5 versionable
** If enabled, automatically create files "manifest" and "manifest.uuid"
** in every checkout.
**
** Optionally use combinations of characters 'r' for "manifest", 'u' for
** "manifest.uuid", 't' for "manifest.tags", and 'l' for "manifest.symlinks".
** The SQLite and Fossil repositories both require manifests.
*/
/*
** SETTING: max-loadavg      width=25 default=0.0
** Some CPU-intensive web pages (ex: /zip, /tarball, /blame)
** are disallowed if the system load average goes above this
** value.  "0.0" means no limit.  This only works on unix.
** Only local settings of this value make a difference since

Changes to src/tar.c.

512
513
514
515
516
517
518





519
520
521
522
523
524
525
        eflg |= MFESTFLG_UUID;
      }
      if( (pInclude==0 || glob_match(pInclude, "manifest.tags"))
       && !glob_match(pExclude, "manifest.tags")
       && (flg & MFESTFLG_TAGS) ){
        eflg |= MFESTFLG_TAGS;
      }






      if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){
        if( eflg & MFESTFLG_RAW ){
          blob_append(&filename, "manifest", -1);
          zName = blob_str(&filename);
        }
        if( eflg & MFESTFLG_RAW ) {







>
>
>
>
>







512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
        eflg |= MFESTFLG_UUID;
      }
      if( (pInclude==0 || glob_match(pInclude, "manifest.tags"))
       && !glob_match(pExclude, "manifest.tags")
       && (flg & MFESTFLG_TAGS) ){
        eflg |= MFESTFLG_TAGS;
      }
      if( (pInclude==0 || glob_match(pInclude, "manifest.symlinks"))
       && !glob_match(pExclude, "manifest.symlinks")
       && (flg & MFESTFLG_SYMLINKS) ){
        eflg |= MFESTFLG_SYMLINKS;
      }

      if( eflg & (MFESTFLG_RAW|MFESTFLG_UUID) ){
        if( eflg & MFESTFLG_RAW ){
          blob_append(&filename, "manifest", -1);
          zName = blob_str(&filename);
        }
        if( eflg & MFESTFLG_RAW ) {
540
541
542
543
544
545
546










547
548
549
550
551
552
553
        blob_zero(&tagslist);
        get_checkin_taglist(rid, &tagslist);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, "manifest.tags", -1);
        zName = blob_str(&filename);
        tar_add_file(zName, &tagslist, 0, mTime);
        blob_reset(&tagslist);










      }
    }
    manifest_file_rewind(pManifest);
    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
      int fid;
      if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue;
      if( glob_match(pExclude, pFile->zName) ) continue;







>
>
>
>
>
>
>
>
>
>







545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
        blob_zero(&tagslist);
        get_checkin_taglist(rid, &tagslist);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, "manifest.tags", -1);
        zName = blob_str(&filename);
        tar_add_file(zName, &tagslist, 0, mTime);
        blob_reset(&tagslist);
      }
      if( eflg & MFESTFLG_SYMLINKS ){
        Blob symlinklist;
        blob_zero(&symlinklist);
        get_checkin_symlinklist(rid, &symlinklist);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, "manifest.symlinks", -1);
        zName = blob_str(&filename);
        tar_add_file(zName, &symlinklist, 0, mTime);
        blob_reset(&symlinklist);
      }
    }
    manifest_file_rewind(pManifest);
    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
      int fid;
      if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue;
      if( glob_match(pExclude, pFile->zName) ) continue;

Changes to src/vfile.c.

171
172
173
174
175
176
177







178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

209




210







211
212
213
214
215
216
217
** to see whether or not the edit was a null-edit.
*/
void vfile_check_signature(int vid, unsigned int cksigFlags){
  int nErr = 0;
  Stmt q;
  int useMtime = (cksigFlags & CKSIG_HASH)==0
                    && db_get_boolean("mtime-changes", 1);








  db_begin_transaction();
  db_prepare(&q, "SELECT id, %Q || pathname,"
                 "       vfile.mrid, deleted, chnged, uuid, size, mtime,"
                 "      CASE WHEN isexe THEN %d WHEN islink THEN %d ELSE %d END"
                 "  FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid"
                 " WHERE vid=%d ", g.zLocalRoot, PERM_EXE, PERM_LNK, PERM_REG,
                 vid);
  while( db_step(&q)==SQLITE_ROW ){
    int id, rid, isDeleted;
    const char *zName;
    int chnged = 0;
    int oldChnged;
#ifndef _WIN32
    int origPerm;
    int currentPerm;
#endif
    i64 oldMtime;
    i64 currentMtime;
    i64 origSize;
    i64 currentSize;

    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isDeleted = db_column_int(&q, 3);
    oldChnged = chnged = db_column_int(&q, 4);
    oldMtime = db_column_int64(&q, 7);
    origSize = db_column_int64(&q, 6);
    currentSize = file_wd_size(zName);
    currentMtime = file_wd_mtime(0);

#ifndef _WIN32




    origPerm = db_column_int(&q, 8);







    currentPerm = file_wd_perm(zName);
#endif
    if( chnged==0 && (isDeleted || rid==0) ){
      /* "fossil rm" or "fossil add" always change the file */
      chnged = 1;
    }else if( !file_wd_isfile_or_link(0) && currentSize>=0 ){
      if( cksigFlags & CKSIG_ENOTFILE ){







>
>
>
>
>
>
>













<


<














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







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

198
199

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
** to see whether or not the edit was a null-edit.
*/
void vfile_check_signature(int vid, unsigned int cksigFlags){
  int nErr = 0;
  Stmt q;
  int useMtime = (cksigFlags & CKSIG_HASH)==0
                    && db_get_boolean("mtime-changes", 1);

  /* On Windows, get symlink permission status from the "manifest.symlinks" file
  ** if it exists and if the "manifest" setting contains the "l" flag. */
#ifdef _WIN32
  int manifestSymlinks = get_checkout_symlink_table();
  int nRoot = strlen(g.zLocalRoot);
#endif

  db_begin_transaction();
  db_prepare(&q, "SELECT id, %Q || pathname,"
                 "       vfile.mrid, deleted, chnged, uuid, size, mtime,"
                 "      CASE WHEN isexe THEN %d WHEN islink THEN %d ELSE %d END"
                 "  FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid"
                 " WHERE vid=%d ", g.zLocalRoot, PERM_EXE, PERM_LNK, PERM_REG,
                 vid);
  while( db_step(&q)==SQLITE_ROW ){
    int id, rid, isDeleted;
    const char *zName;
    int chnged = 0;
    int oldChnged;

    int origPerm;
    int currentPerm;

    i64 oldMtime;
    i64 currentMtime;
    i64 origSize;
    i64 currentSize;

    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isDeleted = db_column_int(&q, 3);
    oldChnged = chnged = db_column_int(&q, 4);
    oldMtime = db_column_int64(&q, 7);
    origSize = db_column_int64(&q, 6);
    currentSize = file_wd_size(zName);
    currentMtime = file_wd_mtime(0);
    origPerm = db_column_int(&q, 8);
#ifdef _WIN32
    /* For Windows, if the "manifest" setting contains the "l" flag and the
    ** "manifest.symlinks" file exists, use its contents to determine which
    ** files do and do not have the symlink permission. */
    if( !manifestSymlinks ){
      currentPerm = origPerm;
    }else if( db_exists("SELECT 1 FROM symlink_perm "
                        "WHERE filename=%Q", zName+nRoot) ){
      currentPerm = PERM_LNK;
    }else{
      currentPerm = 0;
    }
#else
    currentPerm = file_wd_perm(zName);
#endif
    if( chnged==0 && (isDeleted || rid==0) ){
      /* "fossil rm" or "fossil add" always change the file */
      chnged = 1;
    }else if( !file_wd_isfile_or_link(0) && currentSize>=0 ){
      if( cksigFlags & CKSIG_ENOTFILE ){
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263

264
265
266
267

268
269
270
271
272
273
274
275
276
277
278
279
      if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){
        if( currentMtime!=desiredMtime ){
          file_set_mtime(zName, desiredMtime);
          currentMtime = file_wd_mtime(zName);
        }
      }
    }
#ifndef _WIN32
    if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){
       /* Changing to a symlink takes priority over all other change types. */
       chnged = 7;
    }else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){
       /* Confirm metadata change types. */
      if( origPerm==currentPerm ){
        chnged = 0;

      }else if( currentPerm==PERM_EXE ){
        chnged = 6;
      }else if( origPerm==PERM_EXE ){
        chnged = 8;

      }else if( origPerm==PERM_LNK ){
        chnged = 9;
      }
    }
#endif
    if( currentMtime!=oldMtime || chnged!=oldChnged ){
      db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d",
                    currentMtime, chnged, id);
    }
  }
  db_finalize(&q);
  if( nErr ) fossil_fatal("abort due to prior errors");







<







>




>




<







266
267
268
269
270
271
272

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296
      if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){
        if( currentMtime!=desiredMtime ){
          file_set_mtime(zName, desiredMtime);
          currentMtime = file_wd_mtime(zName);
        }
      }
    }

    if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){
       /* Changing to a symlink takes priority over all other change types. */
       chnged = 7;
    }else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){
       /* Confirm metadata change types. */
      if( origPerm==currentPerm ){
        chnged = 0;
#ifndef _WIN32
      }else if( currentPerm==PERM_EXE ){
        chnged = 6;
      }else if( origPerm==PERM_EXE ){
        chnged = 8;
#endif
      }else if( origPerm==PERM_LNK ){
        chnged = 9;
      }
    }

    if( currentMtime!=oldMtime || chnged!=oldChnged ){
      db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d",
                    currentMtime, chnged, id);
    }
  }
  db_finalize(&q);
  if( nErr ) fossil_fatal("abort due to prior errors");

Changes to src/zip.c.

369
370
371
372
373
374
375





376
377
378
379
380
381
382
        eflg |= MFESTFLG_UUID;
      }
      if( (pInclude==0 || glob_match(pInclude, "manifest.tags"))
       && !glob_match(pExclude, "manifest.tags")
       && (flg & MFESTFLG_TAGS) ){
        eflg |= MFESTFLG_TAGS;
      }






      if( eflg & MFESTFLG_RAW ){
        blob_append(&filename, "manifest", -1);
        zName = blob_str(&filename);
        zip_add_folders(zName);
        sterilize_manifest(&mfile);
        zip_add_file(zName, &mfile, 0);







>
>
>
>
>







369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
        eflg |= MFESTFLG_UUID;
      }
      if( (pInclude==0 || glob_match(pInclude, "manifest.tags"))
       && !glob_match(pExclude, "manifest.tags")
       && (flg & MFESTFLG_TAGS) ){
        eflg |= MFESTFLG_TAGS;
      }
      if( (pInclude==0 || glob_match(pInclude, "manifest.symlinks"))
       && !glob_match(pExclude, "manifest.symlinks")
       && (flg & MFESTFLG_SYMLINKS) ){
        eflg |= MFESTFLG_SYMLINKS;
      }

      if( eflg & MFESTFLG_RAW ){
        blob_append(&filename, "manifest", -1);
        zName = blob_str(&filename);
        zip_add_folders(zName);
        sterilize_manifest(&mfile);
        zip_add_file(zName, &mfile, 0);
395
396
397
398
399
400
401











402
403
404
405
406
407
408
        get_checkin_taglist(rid, &tagslist);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, "manifest.tags", -1);
        zName = blob_str(&filename);
        zip_add_folders(zName);
        zip_add_file(zName, &tagslist, 0);
        blob_reset(&tagslist);











      }
    }
    manifest_file_rewind(pManifest);
    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
      int fid;
      if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue;
      if( glob_match(pExclude, pFile->zName) ) continue;







>
>
>
>
>
>
>
>
>
>
>







400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
        get_checkin_taglist(rid, &tagslist);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, "manifest.tags", -1);
        zName = blob_str(&filename);
        zip_add_folders(zName);
        zip_add_file(zName, &tagslist, 0);
        blob_reset(&tagslist);
      }
      if( eflg & MFESTFLG_SYMLINKS ){
        Blob symlinklist;
        blob_zero(&symlinklist);
        get_checkin_symlinklist(rid, &symlinklist);
        blob_resize(&filename, nPrefix);
        blob_append(&filename, "manifest.symlinks", -1);
        zName = blob_str(&filename);
        zip_add_folders(zName);
        zip_add_file(zName, &symlinklist, 0);
        blob_reset(&symlinklist);
      }
    }
    manifest_file_rewind(pManifest);
    while( (pFile = manifest_file_next(pManifest,0))!=0 ){
      int fid;
      if( pInclude!=0 && !glob_match(pInclude, pFile->zName) ) continue;
      if( glob_match(pExclude, pFile->zName) ) continue;

Changes to www/changes.wiki.

28
29
30
31
32
33
34




35
36
37
38
39
40
41
  *  Correct the [/help?cmd=/doc|/doc] page to support read-only repositories.
  *  Correct [/help?cmd=/zip|/zip], [/help?cmd=/tarball|/tarball],
     [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to
     honor the versioned manifest setting when outside of an open checkout
     directory.
  *  The admin-log and access-log settings are now on by default for
     new repositories.




  *  Update the built-in SQLite to version 3.20.1.

<a name='v2_3'></a>
<h2>Changes for Version 2.3 (2017-07-21)</h2>

  *  Update the built-in SQLite to version 3.20.0 (beta).
  *  Update internal Unicode character tables, used in regular expression







>
>
>
>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  *  Correct the [/help?cmd=/doc|/doc] page to support read-only repositories.
  *  Correct [/help?cmd=/zip|/zip], [/help?cmd=/tarball|/tarball],
     [/help?cmd=zip|zip], and [/help?cmd=tarball|tarball] pages and commands to
     honor the versioned manifest setting when outside of an open checkout
     directory.
  *  The admin-log and access-log settings are now on by default for
     new repositories.
  *  Add support for the "l" flag to the "manifest" setting to enable creation
     of a file "manifest.symlinks" which lists the names of all symlinks.  On
     Windows, this new file can be edited to change which files are and are not
     considered to be symlinks.
  *  Update the built-in SQLite to version 3.20.1.

<a name='v2_3'></a>
<h2>Changes for Version 2.3 (2017-07-21)</h2>

  *  Update the built-in SQLite to version 3.20.0 (beta).
  *  Update internal Unicode character tables, used in regular expression