Index: src/add.c
==================================================================
--- src/add.c
+++ src/add.c
@@ -108,11 +108,11 @@
   }else{
     char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
     db_multi_exec(
       "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
       "VALUES(%d,0,0,0,%Q,%d,%d)",
-      vid, zPath, file_isexe(zFullname), file_islink(zFullname));
+      vid, zPath, file_wd_isexe(zFullname), file_wd_islink(zFullname));
     fossil_free(zFullname);
   }
   if( db_changes() ){
     fossil_print("ADDED  %s\n", zPath);
     return 1;
@@ -426,11 +426,11 @@
     const char * zFile;
     const char * zPath;
 
     zFile = db_column_text(&q, 0);
     zPath = db_column_text(&q, 1);
-    if( !file_isfile_or_link(zPath) ){
+    if( !file_wd_isfile_or_link(zPath) ){
       if( !isTest ){
         db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
       }
       fossil_print("DELETED  %s\n", zFile);
       nDelete++;

Index: src/blob.c
==================================================================
--- src/blob.c
+++ src/blob.c
@@ -681,11 +681,11 @@
   FILE *in;
   if( zFilename==0 || zFilename[0]==0
         || (zFilename[0]=='-' && zFilename[1]==0) ){
     return blob_read_from_channel(pBlob, stdin, -1);
   }
-  size = file_size(zFilename);
+  size = file_wd_size(zFilename);
   blob_zero(pBlob);
   if( size<0 ){
     fossil_fatal("no such file: %s", zFilename);
   }
   if( size==0 ){

Index: src/checkin.c
==================================================================
--- src/checkin.c
+++ src/checkin.c
@@ -64,11 +64,11 @@
       }
     }
     blob_append(report, zPrefix, nPrefix);
     if( isDeleted ){
       blob_appendf(report, "DELETED    %s\n", zDisplayName);
-    }else if( !file_isfile_or_link(zFullName) ){
+    }else if( !file_wd_isfile_or_link(zFullName) ){
       if( file_access(zFullName, 0)==0 ){
         blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName);
         if( missingIsFatal ){
           fossil_warning("not a file: %s", zDisplayName);
           nErr++;
@@ -227,11 +227,11 @@
       fossil_print("%s\n", zPathname);
     }else if( isNew ){
       fossil_print("ADDED      %s\n", zPathname);
     }else if( isDeleted ){
       fossil_print("DELETED    %s\n", zPathname);
-    }else if( !file_isfile_or_link(zFullName) ){
+    }else if( !file_wd_isfile_or_link(zFullName) ){
       if( file_access(zFullName, 0)==0 ){
         fossil_print("NOT_A_FILE %s\n", zPathname);
       }else{
         fossil_print("MISSING    %s\n", zPathname);
       }
@@ -661,16 +661,16 @@
     ** the filesystem.  On windows, the "executable" bit is retained
     ** unchanged from the original. 
     */
     blob_resize(&filename, nBasename);
     blob_append(&filename, zName, -1);
-    isexe = file_isexe(blob_str(&filename));
+    isexe = file_wd_isexe(blob_str(&filename));
     
     /* For unix, check if the file on the filesystem is symlink.
     ** On windows, the bit is retained unchanged from original. 
     */
-    isLink = file_islink(blob_str(&filename));
+    isLink = file_wd_islink(blob_str(&filename));
 #endif
     if( isexe ){
       zPerm = " x";
     }else if( isLink ){
       zPerm = " l"; /* note: symlinks don't have executable bit on unix */
@@ -1071,11 +1071,11 @@
     zFullname = db_column_text(&q, 1);
     rid = db_column_int(&q, 2);
     crnlOk = db_column_int(&q, 3);
 
     blob_zero(&content);
-    if( file_islink(zFullname) ){
+    if( file_wd_islink(zFullname) ){
       /* Instead of file content, put link destination path */
       blob_read_link(&content, zFullname);
     }else{
       blob_read_from_file(&content, zFullname);        
     }

Index: src/checkout.c
==================================================================
--- src/checkout.c
+++ src/checkout.c
@@ -113,11 +113,11 @@
   manifest_file_rewind(pManifest);
   while( (pFile = manifest_file_next(pManifest, 0))!=0 ){
     int isExe;
     blob_append(&filename, pFile->zName, -1);
     isExe = pFile->zPerm && strstr(pFile->zPerm, "x");
-    file_setexe(blob_str(&filename), isExe);
+    file_wd_setexe(blob_str(&filename), isExe);
     set_or_clear_isexe(pFile->zName, vid, isExe);
     blob_resize(&filename, baseLen);
   }
   blob_reset(&filename);
   manifest_destroy(pManifest);

Index: src/diffcmd.c
==================================================================
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -71,14 +71,14 @@
     Blob file2;               /* Content of zFile2 */
     const char *zName2;       /* Name of zFile2 for display */
 
     /* Read content of zFile2 into memory */
     blob_zero(&file2);
-    if( file_size(zFile2)<0 ){
+    if( file_wd_size(zFile2)<0 ){
       zName2 = "/dev/null";
     }else{
-      if( file_islink(zFile2) ){
+      if( file_wd_islink(zFile2) ){
         blob_read_link(&file2, zFile2);
       }else{
         blob_read_from_file(&file2, zFile2);
       }
       zName2 = zName;
@@ -193,11 +193,11 @@
   Blob fname;
   Blob content;
   int isLink;
   file_tree_name(zFileTreeName, &fname, 1);
   historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
-  if( !isLink != !file_islink(zFrom) ){
+  if( !isLink != !file_wd_islink(zFrom) ){
     diff_printf("cannot compute difference between symlink and regular file\n");
   }else{
     diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
   }
   blob_reset(&content);
@@ -288,11 +288,11 @@
       srcid = 0;
       if( !asNewFile ){ showDiff = 0; }
     }
     if( showDiff ){
       Blob content;
-      if( !isLink != !file_islink(zFullName) ){
+      if( !isLink != !file_wd_islink(zFullName) ){
         diff_print_index(zPathname);
         diff_printf("--- %s\n+++ %s\n", zPathname, zPathname);
         diff_printf("cannot compute difference between symlink and regular file\n");
         continue;
       }

Index: src/file.c
==================================================================
--- src/file.c
+++ src/file.c
@@ -13,11 +13,16 @@
 **   drh@hwaci.com
 **   http://www.hwaci.com/drh/
 **
 *******************************************************************************
 **
-** File utilities
+** File utilities.
+**
+** Functions named file_* are generic functions that always follow symlinks.
+**
+** Functions named file_wd_* are to be used for files inside working
+** directories. They follow symlinks depending on 'allow-symlinks' setting.
 */
 #include "config.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -32,98 +37,126 @@
 ** larger than 2GB.
 */
 #if defined(_WIN32) && defined(__MSVCRT__)
 # define stat _stati64
 #endif
+/*
+** On Windows S_ISLNK always returns FALSE.
+*/
+#if defined(_WIN32)
+# define S_ISLNK(x) (0)
+#endif
 static int fileStatValid = 0;
 static struct stat fileStat;
 
-static int fossil_stat(const char *zFilename, struct stat *buf){
+/*
+** Fill stat buf with information received from stat() or lstat().
+** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
+**
+*/
+static int fossil_stat(const char *zFilename, struct stat *buf, int isWd){
 #if !defined(_WIN32)
-  if( g.allowSymlinks ){
+  if( isWd && g.allowSymlinks ){
     return lstat(zFilename, buf);
   }else{
     return stat(zFilename, buf);
   }
 #else
-  return stat(zFilename, buf);
+  int rc = 0;
+  char *zMbcs = fossil_utf8_to_mbcs(zFilename);
+  rc = stat(zMbcs, buf);
+  fossil_mbcs_free(zMbcs);
+  return rc;
 #endif
 }
 
 /*
 ** Fill in the fileStat variable for the file named zFilename.
 ** If zFilename==0, then use the previous value of fileStat if
 ** there is a previous value.
 **
+** If isWd is TRUE, do lstat() instead of stat() if allow-symlinks is on.
+**
 ** Return the number of errors.  No error messages are generated.
 */
-static int getStat(const char *zFilename){
+static int getStat(const char *zFilename, int isWd){
   int rc = 0;
   if( zFilename==0 ){
     if( fileStatValid==0 ) rc = 1;
   }else{
-    char *zMbcs = fossil_utf8_to_mbcs(zFilename);
-    if( fossil_stat(zMbcs, &fileStat)!=0 ){
+    if( fossil_stat(zFilename, &fileStat, isWd)!=0 ){
       fileStatValid = 0;
       rc = 1;
     }else{
       fileStatValid = 1;
       rc = 0;
     }
-    fossil_mbcs_free(zMbcs);
   }
   return rc;
 }
-
 
 /*
 ** Return the size of a file in bytes.  Return -1 if the file does not
 ** exist.  If zFilename is NULL, return the size of the most recently
 ** stat-ed file.
 */
 i64 file_size(const char *zFilename){
-  return getStat(zFilename) ? -1 : fileStat.st_size;
+  return getStat(zFilename, 0) ? -1 : fileStat.st_size;
+}
+
+/*
+** Same as file_size(), but takes into account symlinks.
+*/
+i64 file_wd_size(const char *zFilename){
+  return getStat(zFilename, 1) ? -1 : fileStat.st_size;  
 }
 
 /*
 ** Return the modification time for a file.  Return -1 if the file
 ** does not exist.  If zFilename is NULL return the size of the most
 ** recently stat-ed file.
 */
 i64 file_mtime(const char *zFilename){
-  return getStat(zFilename) ? -1 : fileStat.st_mtime;
+  return getStat(zFilename, 0) ? -1 : fileStat.st_mtime;
+}
+
+/*
+** Same as file_mtime(), but takes into account symlinks.
+*/
+i64 file_wd_mtime(const char *zFilename){
+  return getStat(zFilename, 1) ? -1 : fileStat.st_mtime;
 }
 
 /*
 ** Return TRUE if the named file is an ordinary file or symlink 
 ** and symlinks are allowed.
 ** Return false for directories, devices, fifos, etc.
 */
-int file_isfile_or_link(const char *zFilename){
-#if !defined(_WIN32)
-  if ( g.allowSymlinks ){
-    return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode) || S_ISLNK(fileStat.st_mode);
-  }
-#endif
-  return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);    
+int file_wd_isfile_or_link(const char *zFilename){
+  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode) ||
+                                     S_ISLNK(fileStat.st_mode);
 }
 
 /*
 ** Return TRUE if the named file is an ordinary file.  Return false
 ** for directories, devices, fifos, symlinks, etc.
 */
 int file_isfile(const char *zFilename){
-  return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
+  return getStat(zFilename, 0) ? 0 : S_ISREG(fileStat.st_mode);
+}
+
+int file_wd_isfile(const char *zFilename){
+  return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode);
 }
 
 /*
 ** Create symlink to file on Unix, or plain-text file with
 ** symlink target if "allow-symlinks" is off or we're on Windows.
 **
 ** Arguments: target file (symlink will point to it), link file
 **/
-void create_symlink(const char *zTargetFile, const char *zLinkFile){
+void symlink_create(const char *zTargetFile, const char *zLinkFile){
 #if !defined(_WIN32)
   if( g.allowSymlinks ){
     int i, nName;
     char *zName, zBuf[1000];
 
@@ -157,19 +190,29 @@
     blob_set(&content, zTargetFile);
     blob_write_to_file(&content, zLinkFile);
     blob_reset(&content);
   }
 }
+
+/*
+** Copy symbolic link from zFrom to zTo.
+*/
+void symlink_copy(const char *zFrom, const char *zTo){
+  Blob content;
+  blob_read_link(&content, zFrom);
+  symlink_create(blob_str(&content), zTo);
+  blob_reset(&content);
+}
 
 /*
 ** Return file permissions (normal, executable, or symlink):
 **   - PERM_EXE if file is executable;
 **   - PERM_LNK on Unix if file is symlink and allow-symlinks option is on;
 **   - PERM_REG for all other cases (regular file, directory, fifo, etc).
 */
-int file_perm(const char *zFilename){
-  if( getStat(zFilename) ) return PERM_REG;
+int file_wd_perm(const char *zFilename){
+  if( getStat(zFilename, 1) ) return PERM_REG;
 #if defined(_WIN32)
 #  if defined(__DMC__) || defined(_MSC_VER)
 #    define S_IXUSR  _S_IEXEC
 #  endif
   if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 )
@@ -189,22 +232,22 @@
 
 /*
 ** Return TRUE if the named file is an executable.  Return false
 ** for directories, devices, fifos, symlinks, etc.
 */
-int file_isexe(const char *zFilename){
-  return file_perm(zFilename)==PERM_EXE;
+int file_wd_isexe(const char *zFilename){
+  return file_wd_perm(zFilename)==PERM_EXE;
 }
 
 /*
 ** Return TRUE if the named file is a symlink and symlinks are allowed.
 ** Return false for all other cases.
 **
 ** On Windows, always return False.
 */
-int file_islink(const char *zFilename){
-  return file_perm(zFilename)==PERM_LNK;
+int file_wd_islink(const char *zFilename){
+  return file_wd_perm(zFilename)==PERM_LNK;
 }
 
 /*
 ** Return 1 if zFilename is a directory.  Return 0 if zFilename
 ** does not exist.  Return 2 if zFilename exists but is something
@@ -214,25 +257,35 @@
   int rc;
 
   if( zFilename ){
     char *zFN = mprintf("%s", zFilename);
     file_simplify_name(zFN, -1);
-    rc = getStat(zFN);
+    rc = getStat(zFN, 0);
+    free(zFN);
+  }else{
+    rc = getStat(0, 0);
+  }
+  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
+}
+
+/*
+** Same as file_isdir(), but takes into account symlinks.
+*/
+int file_wd_isdir(const char *zFilename){
+  int rc;
+
+  if( zFilename ){
+    char *zFN = mprintf("%s", zFilename);
+    file_simplify_name(zFN, -1);
+    rc = getStat(zFN, 1);
     free(zFN);
   }else{
-    rc = getStat(0);
+    rc = getStat(0, 1);
   }
-#if !defined(_WIN32)
-  if( g.allowSymlinks ){
-    return rc ? 0 : (S_ISDIR(fileStat.st_mode) && !S_ISLNK(fileStat.st_mode) ? 1 : 2);
-  }else{
-    return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);    
-  }
-#else
   return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
-#endif
 }
+
 
 /*
 ** Wrapper around the access() system call.
 */
 int file_access(const char *zFilename, int flags){
@@ -300,15 +353,15 @@
 
 /*
 ** Set or clear the execute bit on a file.  Return true if a change
 ** occurred and false if this routine is a no-op.
 */
-int file_setexe(const char *zFilename, int onoff){
+int file_wd_setexe(const char *zFilename, int onoff){
   int rc = 0;
 #if !defined(_WIN32)
   struct stat buf;
-  if( fossil_stat(zFilename, &buf)!=0 || S_ISLNK(buf.st_mode) ) return 0;
+  if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0;
   if( onoff ){
     int targetMode = (buf.st_mode & 0444)>>2;
     if( (buf.st_mode & 0111)!=targetMode ){
       chmod(zFilename, buf.st_mode | targetMode);
       rc = 1;
@@ -569,19 +622,19 @@
     char zBuf[100];
     const char *zName = g.argv[i];
     file_canonical_name(zName, &x);
     fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x));
     blob_reset(&x);
-    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName));
+    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName));
     fossil_print("  file_size   = %s\n", zBuf);
-    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName));
+    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName));
     fossil_print("  file_mtime  = %s\n", zBuf);
-    fossil_print("  file_isfile = %d\n", file_isfile(zName));
-    fossil_print("  file_isfile_or_link = %d\n", file_isfile_or_link(zName));
-    fossil_print("  file_islink = %d\n", file_islink(zName));
-    fossil_print("  file_isexe  = %d\n", file_isexe(zName));
-    fossil_print("  file_isdir  = %d\n", file_isdir(zName));
+    fossil_print("  file_isfile = %d\n", file_wd_isfile(zName));
+    fossil_print("  file_isfile_or_link = %d\n",file_wd_isfile_or_link(zName));
+    fossil_print("  file_islink = %d\n", file_wd_islink(zName));
+    fossil_print("  file_isexe  = %d\n", file_wd_isexe(zName));
+    fossil_print("  file_isdir  = %d\n", file_wd_isdir(zName));
   }
 }
 
 /*
 ** Return TRUE if the given filename is canonical.
@@ -846,11 +899,11 @@
   Blob onDisk;
 
   iSize = file_size(zName);
   if( iSize<0 ) return 0;
   if( iSize!=blob_size(pContent) ) return 0;
-  if( file_islink(zName) ){
+  if( file_wd_islink(zName) ){
     blob_read_link(&onDisk, zName);
   }else{
     blob_read_from_file(&onDisk, zName);
   }
   rc = blob_compare(&onDisk, pContent);

Index: src/merge.c
==================================================================
--- src/merge.c
+++ src/merge.c
@@ -391,11 +391,11 @@
       fossil_print("MERGE %s  (pivot=%d v1=%d v2=%d)\n", 
                    zName, ridp, ridm, ridv);
     }else{
       fossil_print("MERGE %s\n", zName);
     }
-    if( islinkv || islinkm /* || file_islink(zFullPath) */ ){
+    if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){
       fossil_print("***** Cannot merge symlink %s\n", zName);
       nConflict++;        
     }else{
       undo_save(zName);
       zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
@@ -408,11 +408,11 @@
         rc = merge_3way(&p, zFullPath, &m, &r);
       }
       if( rc>=0 ){
         if( !nochangeFlag ){
           blob_write_to_file(&r, zFullPath);
-          file_setexe(zFullPath, isExe);
+          file_wd_setexe(zFullPath, isExe);
         }
         db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
         if( rc>0 ){
           fossil_print("***** %d merge conflicts in %s\n", rc, zName);
           nConflict++;
@@ -480,11 +480,15 @@
       " WHERE id=%d AND vid=%d", zNewName, idv, vid
     );
     if( !nochangeFlag ){
       char *zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
       char *zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
-      file_copy(zFullOldPath, zFullNewPath);
+      if( file_wd_islink(zFullOldPath) ){
+        symlink_copy(zFullOldPath, zFullNewPath);
+      }else{
+        file_copy(zFullOldPath, zFullNewPath);
+      }
       file_delete(zFullOldPath);
       free(zFullNewPath);
       free(zFullOldPath);
     }
   }

Index: src/merge3.c
==================================================================
--- src/merge3.c
+++ src/merge3.c
@@ -422,11 +422,11 @@
       azSubst[4] = "%merge";     azSubst[5] = zOther;
       azSubst[6] = "%output";    azSubst[7] = zOut;
       zCmd = string_subst(zGMerge, 8, azSubst);
       printf("%s\n", zCmd); fflush(stdout);
       fossil_system(zCmd);
-      if( file_size(zOut)>=0 ){
+      if( file_wd_size(zOut)>=0 ){
         blob_read_from_file(pOut, zOut);
         file_delete(zPivot);
         file_delete(zOrig);
         file_delete(zOther);
         file_delete(zOut);

Index: src/sha1.c
==================================================================
--- src/sha1.c
+++ src/sha1.c
@@ -281,11 +281,11 @@
   FILE *in;
   SHA1Context ctx;
   unsigned char zResult[20];
   char zBuf[10240];
 
-  if( file_islink(zFilename) ){
+  if( file_wd_islink(zFilename) ){
     /* Instead of file content, return sha1 of link destination path */
     Blob destinationPath;
     int rc;
     
     blob_read_link(&destinationPath, zFilename);

Index: src/stash.c
==================================================================
--- src/stash.c
+++ src/stash.c
@@ -89,11 +89,11 @@
     int rid = db_column_int(&q, 3);
     const char *zName = db_column_text(&q, 4);
     const char *zOrig = db_column_text(&q, 5);
     char *zPath = mprintf("%s%s", g.zLocalRoot, zName);
     Blob content;
-    int isNewLink = file_islink(zPath);
+    int isNewLink = file_wd_islink(zPath);
 
     db_bind_int(&ins, ":rid", rid);
     db_bind_int(&ins, ":isadd", rid==0);
     db_bind_int(&ins, ":isrm", deleted);
     db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
@@ -200,18 +200,18 @@
     undo_save(zNew);
     blob_zero(&delta);
     if( rid==0 ){
       db_ephemeral_blob(&q, 6, &delta);
       blob_write_to_file(&delta, zNPath);
-      file_setexe(zNPath, isExec);
+      file_wd_setexe(zNPath, isExec);
       fossil_print("ADD %s\n", zNew);
     }else if( isRemoved ){
       fossil_print("DELETE %s\n", zOrig);
       file_delete(zOPath);
     }else{
       Blob a, b, out, disk;
-      int isNewLink = file_islink(zOPath);
+      int isNewLink = file_wd_islink(zOPath);
       db_ephemeral_blob(&q, 6, &delta);
       if( isNewLink ){
         blob_read_link(&disk, zOPath);
       }else{
         blob_read_from_file(&disk, zOPath);
@@ -221,15 +221,15 @@
       if( blob_compare(&disk, &a)==0 && isLink == isNewLink ){
         if( isLink || isNewLink ){
           file_delete(zNPath);
         }
         if( isLink ){
-          create_symlink(blob_str(&b), zNPath);
+          symlink_create(blob_str(&b), zNPath);
         }else{
           blob_write_to_file(&b, zNPath);          
         }
-        file_setexe(zNPath, isExec);
+        file_wd_setexe(zNPath, isExec);
         fossil_print("UPDATE %s\n", zNew);
       }else{
         int rc;
         if( isLink || isNewLink ){
           rc = -1;
@@ -237,11 +237,11 @@
           fossil_print("***** Cannot merge symlink %s\n", zNew);
         }else{
           rc = merge_3way(&a, zOPath, &b, &out);
           blob_write_to_file(&out, zNPath);          
           blob_reset(&out);
-          file_setexe(zNPath, isExec);
+          file_wd_setexe(zNPath, isExec);
         }
         if( rc ){
           fossil_print("CONFLICT %s\n", zNew);
           nConflict++;
         }else{
@@ -290,20 +290,20 @@
       fossil_print("ADDED %s\n", zNew);
       diff_print_index(zNew);
       diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0);
     }else if( isRemoved ){
       fossil_print("DELETE %s\n", zOrig);
-      if( file_islink(zOPath) ){
+      if( file_wd_islink(zOPath) ){
         blob_read_link(&delta, zOPath);
       }else{
         blob_read_from_file(&delta, zOPath);
       }
       diff_print_index(zNew);
       diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0);
     }else{
       Blob a, b, disk;
-      int isOrigLink = file_islink(zOPath);
+      int isOrigLink = file_wd_islink(zOPath);
       db_ephemeral_blob(&q, 6, &delta);
       if( isOrigLink ){
         blob_read_link(&disk, zOPath);
       }else{
         blob_read_from_file(&disk, zOPath);        

Index: src/tar.c
==================================================================
--- src/tar.c
+++ src/tar.c
@@ -416,11 +416,11 @@
 
 
 /*
 ** COMMAND: test-tarball
 **
-** Generate a GZIP-compresssed tarball in the file given by the first argument
+** Generate a GZIP-compressed tarball in the file given by the first argument
 ** that contains files given in the second and subsequent arguments.
 */
 void test_tarball_cmd(void){
   int i;
   Blob zip;
@@ -432,11 +432,11 @@
   tar_begin();
   for(i=3; i<g.argc; i++){
     blob_zero(&file);
     blob_read_from_file(&file, g.argv[i]);
     tar_add_file(g.argv[i], &file,
-                 file_perm(g.argv[i]), file_mtime(g.argv[i]));
+                 file_wd_perm(g.argv[i]), file_wd_mtime(g.argv[i]));
     blob_reset(&file);
   }
   tar_finish(&zip);
   blob_write_to_file(&zip, g.argv[2]);
 }

Index: src/undo.c
==================================================================
--- src/undo.c
+++ src/undo.c
@@ -45,19 +45,19 @@
     int old_link;
     Blob current;
     Blob new;
     zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
     old_link = db_column_int(&q, 3);
-    new_link = file_islink(zFullname);
-    new_exists = file_size(zFullname)>=0;
+    new_link = file_wd_islink(zFullname);
+    new_exists = file_wd_size(zFullname)>=0;
     if( new_exists ){
       if( new_link ){
         blob_read_link(&current, zFullname);
       }else{
         blob_read_from_file(&current, zFullname);        
       }
-      new_exe = file_isexe(zFullname);
+      new_exe = file_wd_isexe(zFullname);
     }else{
       blob_zero(&current);
       new_exe = 0;
     }
     blob_zero(&new);
@@ -74,15 +74,15 @@
       }
       if( new_exists && (new_link || old_link) ){
         file_delete(zFullname);
       }
       if( old_link ){
-        create_symlink(blob_str(&new), zFullname);
+        symlink_create(blob_str(&new), zFullname);
       }else{
         blob_write_to_file(&new, zFullname);
       }
-      file_setexe(zFullname, old_exe);
+      file_wd_setexe(zFullname, old_exe);
     }else{
       fossil_print("DELETE %s\n", zPathname);
       file_delete(zFullname);
     }
     blob_reset(&new);
@@ -270,17 +270,17 @@
   int isLink;
   Stmt q;
 
   if( !undoActive ) return;
   zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
-  existsFlag = file_size(zFullname)>=0;
-  isLink = file_islink(zFullname);
+  existsFlag = file_wd_size(zFullname)>=0;
+  isLink = file_wd_islink(zFullname);
   db_prepare(&q,
     "INSERT OR IGNORE INTO"
     "   undo(pathname,redoflag,existsflag,isExe,isLink,content)"
     " VALUES(%Q,0,%d,%d,%d,:c)",
-    zPathname, existsFlag, file_isexe(zFullname), isLink
+    zPathname, existsFlag, file_wd_isexe(zFullname), isLink
   );
   if( existsFlag ){
     if( isLink ){
       blob_read_link(&content, zFullname); 
     }else{

Index: src/update.c
==================================================================
--- src/update.c
+++ src/update.c
@@ -367,11 +367,11 @@
     }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){
       /* The file is unedited.  Change it to the target version */
       undo_save(zName);
       fossil_print("UPDATE %s\n", zName);
       if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
-    }else if( idt>0 && idv>0 && file_size(zFullPath)<0 ){
+    }else if( idt>0 && idv>0 && file_wd_size(zFullPath)<0 ){
       /* The file missing from the local check-out. Restore it to the
       ** version that appears in the target. */
       fossil_print("UPDATE %s\n", zName);
       undo_save(zName);
       if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
@@ -398,11 +398,11 @@
       if( nameChng ){
         fossil_print("MERGE %s -> %s\n", zName, zNewName);
       }else{
         fossil_print("MERGE %s\n", zName);
       }
-      if( islinkv || islinkt /* || file_islink(zFullPath) */ ){
+      if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){
         fossil_print("***** Cannot merge symlink %s\n", zNewName);
         nConflict++;        
       }else{
         undo_save(zName);
         content_get(ridt, &t);
@@ -409,20 +409,20 @@
         content_get(ridv, &v);
         rc = merge_3way(&v, zFullPath, &t, &r);
         if( rc>=0 ){
           if( !nochangeFlag ){
             blob_write_to_file(&r, zFullNewPath);
-            file_setexe(zFullNewPath, isexe);
+            file_wd_setexe(zFullNewPath, isexe);
           }
           if( rc>0 ){
             fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
             nConflict++;
           }
         }else{
           if( !nochangeFlag ){
             blob_write_to_file(&t, zFullNewPath);
-            file_setexe(zFullNewPath, isexe);
+            file_wd_setexe(zFullNewPath, isexe);
           }
           fossil_print("***** Cannot merge binary file %s\n", zNewName);
           nConflict++;
         }
       }
@@ -668,21 +668,21 @@
       }
       db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile);
     }else{
       sqlite3_int64 mtime;
       undo_save(zFile);
-      if( file_size(zFull)>=0 && (isLink || file_islink(zFull)) ){
+      if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(zFull)) ){
         file_delete(zFull);
       }
       if( isLink ){
-        create_symlink(blob_str(&record), zFull);
+        symlink_create(blob_str(&record), zFull);
       }else{
         blob_write_to_file(&record, zFull);
       }
-      file_setexe(zFull, isExe);
+      file_wd_setexe(zFull, isExe);
       fossil_print("REVERTED: %s\n", zFile);
-      mtime = file_mtime(zFull);
+      mtime = file_wd_mtime(zFull);
       db_multi_exec(
          "UPDATE vfile"
          "   SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d, mrid=rid,"
          "       pathname=coalesce(origname,pathname), origname=NULL"     
          " WHERE pathname=%Q",

Index: src/vfile.c
==================================================================
--- src/vfile.c
+++ src/vfile.c
@@ -166,11 +166,11 @@
     isDeleted = db_column_int(&q, 3);
     oldChnged = db_column_int(&q, 4);
     oldMtime = db_column_int64(&q, 7);
     if( isDeleted ){
       chnged = 1;
-    }else if( !file_isfile_or_link(zName) && file_size(0)>=0 ){
+    }else if( !file_wd_isfile_or_link(zName) && file_wd_size(0)>=0 ){
       if( notFileIsFatal ){
         fossil_warning("not an ordinary file: %s", zName);
         nErr++;
       }
       chnged = 1;
@@ -179,12 +179,12 @@
     }else if( rid==0 ){
       chnged = 1;
     }
     if( chnged!=1 ){
       i64 origSize = db_column_int64(&q, 6);
-      currentMtime = file_mtime(0);
-      if( origSize!=file_size(0) ){
+      currentMtime = file_wd_mtime(0);
+      if( origSize!=file_wd_size(0) ){
         /* A file size change is definitive - the file has changed.  No
         ** need to check the sha1sum */
         chnged = 1;
       }
     }
@@ -247,17 +247,17 @@
     isExe = db_column_int(&q, 3);
     isLink = db_column_int(&q, 4);
     content_get(rid, &content);
     if( file_is_the_same(&content, zName) ){
       blob_reset(&content);
-      if( file_setexe(zName, isExe) ){
+      if( file_wd_setexe(zName, isExe) ){
         db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
-                      file_mtime(zName), id);
+                      file_wd_mtime(zName), id);
       }
       continue;
     }
-    if( promptFlag && file_size(zName)>=0 ){
+    if( promptFlag && file_wd_size(zName)>=0 ){
       Blob ans;
       char *zMsg;
       char cReply;
       zMsg = mprintf("overwrite %s (a=always/y/N)? ", zName);
       prompt_user(zMsg, &ans);
@@ -276,22 +276,22 @@
     if( verbose ) fossil_print("%s\n", &zName[nRepos]);
     if( file_isdir(zName) == 1 ){
       /*TODO(dchest): remove directories? */
       fossil_fatal("%s is directory, cannot overwrite\n", zName);
     }    
-    if( file_size(zName)>=0 && (isLink || file_islink(zName)) ){
+    if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
       file_delete(zName);
     }
     if( isLink ){
-      create_symlink(blob_str(&content), zName);
+      symlink_create(blob_str(&content), zName);
     }else{
       blob_write_to_file(&content, zName);
     }
-    file_setexe(zName, isExe);
+    file_wd_setexe(zName, isExe);
     blob_reset(&content);
     db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
-                  file_mtime(zName), id);
+                  file_wd_mtime(zName), id);
   }
   db_finalize(&q);
 }
 
 
@@ -393,11 +393,11 @@
         /* do nothing */
       }else if( file_isdir(zPath)==1 ){
         if( !vfile_top_of_checkout(zPath) ){
           vfile_scan(pPath, nPrefix, allFlag, pIgnore);
         }
-      }else if( file_isfile_or_link(zPath) ){
+      }else if( file_wd_isfile_or_link(zPath) ){
         db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
         db_step(&ins);
         db_reset(&ins);
       }
       blob_resize(pPath, origSize);
@@ -452,11 +452,11 @@
     const char *zName = db_column_text(&q, 1);
     int isSelected = db_column_int(&q, 3);
 
     if( isSelected ){
       md5sum_step_text(zName, -1);
-      if( file_islink(zFullpath) ){
+      if( file_wd_islink(zFullpath) ){
         /* Instead of file content, use link destination path */
         Blob pathBuf;
 
         sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", 
                          blob_read_link(&pathBuf, zFullpath));
@@ -524,11 +524,11 @@
     const char *zFullpath = db_column_text(&q, 0);
     const char *zName = db_column_text(&q, 1);
     int rid = db_column_int(&q, 2);
 
     blob_zero(&disk);
-    if( file_islink(zFullpath) ){
+    if( file_wd_islink(zFullpath) ){
       rc = blob_read_link(&disk, zFullpath);
     }else{
       rc = blob_read_from_file(&disk, zFullpath);
     }
     if( rc<0 ){

Index: src/zip.c
==================================================================
--- src/zip.c
+++ src/zip.c
@@ -291,11 +291,11 @@
   }
   zip_open();
   for(i=3; i<g.argc; i++){
     blob_zero(&file);
     blob_read_from_file(&file, g.argv[i]);
-    zip_add_file(g.argv[i], &file, file_perm(g.argv[i]));
+    zip_add_file(g.argv[i], &file, file_wd_perm(g.argv[i]));
     blob_reset(&file);
   }
   zip_close(&zip);
   blob_write_to_file(&zip, g.argv[2]);
 }