Index: src/add.c
==================================================================
--- src/add.c
+++ src/add.c
@@ -148,12 +148,13 @@
       db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);
     }
 #endif
     else{
       db_multi_exec(
-        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
-        "VALUES(%d,0,0,0,%Q)", vid, zPath);
+        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe)"
+        "VALUES(%d,0,0,0,%Q,%d)",
+        vid, zPath,file_isexe(zName));
     }
     printf("ADDED  %s\n", zPath);
   }
   blob_reset(&pathname);
 }

Index: src/file.c
==================================================================
--- src/file.c
+++ src/file.c
@@ -178,26 +178,31 @@
   fclose(in);
   fclose(out);
 }
 
 /*
-** Set or clear the execute bit on a file.
+** Set or clear the execute bit on a file.  Return true if a change
+** occurred and false if this routine is a no-op.
 */
-void file_setexe(const char *zFilename, int onoff){
+int file_setexe(const char *zFilename, int onoff){
+  int rc = 0;
 #if !defined(_WIN32)
   struct stat buf;
-  if( stat(zFilename, &buf)!=0 ) return;
+  if( stat(zFilename, &buf)!=0 ) return 0;
   if( onoff ){
     if( (buf.st_mode & 0111)!=0111 ){
       chmod(zFilename, buf.st_mode | 0111);
+      rc = 1;
     }
   }else{
     if( (buf.st_mode & 0111)!=0 ){
       chmod(zFilename, buf.st_mode & ~0111);
+      rc = 1;
     }
   }
 #endif /* _WIN32 */
+  return rc;
 }
 
 /*
 ** Create the directory named in the argument, if it does not already
 ** exist.  If forceFlag is 1, delete any prior non-directory object 

Index: src/merge.c
==================================================================
--- src/merge.c
+++ src/merge.c
@@ -158,20 +158,22 @@
     "  idm INTEGER,"              /* VFILE entry for version merging in */
     "  chnged BOOLEAN,"           /* True if current version has been edited */
     "  ridv INTEGER,"             /* Record ID for current version */
     "  ridp INTEGER,"             /* Record ID for pivot */
     "  ridm INTEGER,"             /* Record ID for merge */
+    "  isexe BOOLEAN,"            /* Execute permission enabled */
     "  fnp TEXT,"                 /* The filename in the pivot */
     "  fnm TEXT"                  /* the filename in the merged version */
     ");"
   );
 
   /* Add files found in V
   */
   db_multi_exec(
-    "INSERT OR IGNORE INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,chnged)"
-    " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, chnged "
+    "INSERT OR IGNORE"
+    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
+    " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, isexe, chnged "
     " FROM vfile WHERE vid=%d",
     vid
   );
 
   /*
@@ -194,12 +196,13 @@
   }
 
   /* Add files found in P but not in V
   */
   db_multi_exec(
-    "INSERT OR IGNORE INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,chnged)"
-    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, 0 "
+    "INSERT OR IGNORE"
+    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
+    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
     "   FROM vfile"
     "  WHERE vid=%d AND pathname NOT IN (SELECT fnp FROM fv)",
     pid
   );
 
@@ -220,12 +223,13 @@
   }
 
   /* Add files found in M but not in P or V.
   */
   db_multi_exec(
-    "INSERT OR IGNORE INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,chnged)"
-    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, 0 "
+    "INSERT OR IGNORE"
+    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
+    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
     "   FROM vfile"
     "  WHERE vid=%d"
     "    AND pathname NOT IN (SELECT fnp FROM fv UNION SELECT fnm FROM fv)",
     mid
   );
@@ -242,19 +246,20 @@
     pid, pid, mid, mid
   );
 
   if( debugFlag ){
     db_prepare(&q,
-       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm FROM fv"
+       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, isexe FROM fv"
     );
     while( db_step(&q)==SQLITE_ROW ){
-       printf("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d\n",
+       printf("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d\n",
           db_column_int(&q, 0),
           db_column_int(&q, 5),
           db_column_int(&q, 6),
           db_column_int(&q, 7),
-          db_column_int(&q, 4));
+          db_column_int(&q, 4),
+          db_column_int(&q, 8));
        printf("     fn  = [%s]\n", db_column_text(&q, 1));
        printf("     fnp = [%s]\n", db_column_text(&q, 2));
        printf("     fnm = [%s]\n", db_column_text(&q, 3));
     }
     db_finalize(&q);
@@ -288,12 +293,12 @@
     int idm = db_column_int(&q, 0);
     int rowid = db_column_int(&q, 1);
     int idv;
     const char *zName;
     db_multi_exec(
-      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,pathname)"
-      "  SELECT %d,3,0,rid,mrid,pathname FROM vfile WHERE id=%d",
+      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,pathname)"
+      "  SELECT %d,3,0,rid,mrid,isexe,pathname FROM vfile WHERE id=%d",
       vid, idm
     );
     idv = db_last_insert_rowid();
     db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
     zName = db_column_text(&q, 2);
@@ -332,11 +337,11 @@
 
   /*
   ** Do a three-way merge on files that have changes on both P->M and P->V.
   */
   db_prepare(&q,
-    "SELECT ridm, idv, ridp, ridv, %s, fn FROM fv"
+    "SELECT ridm, idv, ridp, ridv, %s, fn, isexe FROM fv"
     " WHERE idp>0 AND idv>0 AND idm>0"
     "   AND ridm!=ridp AND (ridv!=ridp OR chnged)",
     glob_expr("fv.fn", zBinGlob)
   );
   while( db_step(&q)==SQLITE_ROW ){
@@ -344,10 +349,11 @@
     int idv = db_column_int(&q, 1);
     int ridp = db_column_int(&q, 2);
     int ridv = db_column_int(&q, 3);
     int isBinary = db_column_int(&q, 4);
     const char *zName = db_column_text(&q, 5);
+    int isExe = db_column_int(&q, 6);
     int rc;
     char *zFullPath;
     Blob m, p, r;
     /* Do a 3-way merge of idp->idm into idp->idv.  The results go into idv. */
     if( detailFlag ){
@@ -366,10 +372,11 @@
       rc = merge_3way(&p, zFullPath, &m, &r);
     }
     if( rc>=0 ){
       if( !nochangeFlag ){
         blob_write_to_file(&r, zFullPath);
+        file_setexe(zFullPath, isExe);
       }
       db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
       if( rc>0 ){
         printf("***** %d merge conflicts in %s\n", rc, zName);
         nConflict++;

Index: src/stash.c
==================================================================
--- src/stash.c
+++ src/stash.c
@@ -92,13 +92,11 @@
     Blob content;
 
     db_bind_int(&ins, ":rid", rid);
     db_bind_int(&ins, ":isadd", rid==0);
     db_bind_int(&ins, ":isrm", deleted);
-#ifdef _WIN32
     db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
-#endif
     db_bind_text(&ins, ":orig", zOrig);
     db_bind_text(&ins, ":new", zName);
     if( rid==0 ){
       /* A new file */
       blob_read_from_file(&content, zPath);
@@ -175,10 +173,11 @@
      stashid
   );
   while( db_step(&q)==SQLITE_ROW ){
     int rid = db_column_int(&q, 0);
     int isRemoved = db_column_int(&q, 1);
+    int isExec = db_column_int(&q, 2);
     const char *zOrig = db_column_text(&q, 3);
     const char *zNew = db_column_text(&q, 4);
     char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
     char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew);
     Blob delta;
@@ -185,10 +184,11 @@
     undo_save(zNew);
     blob_zero(&delta);
     if( rid==0 ){
       db_ephemeral_blob(&q, 5, &delta);
       blob_write_to_file(&delta, zNPath);
+      file_setexe(zNPath, isExec);
       printf("ADD %s\n", zNew);
     }else if( isRemoved ){
       printf("DELETE %s\n", zOrig);
       unlink(zOPath);
     }else{
@@ -197,14 +197,16 @@
       blob_read_from_file(&disk, zOPath);     
       content_get(rid, &a);
       blob_delta_apply(&a, &delta, &b);
       if( blob_compare(&disk, &a)==0 ){
         blob_write_to_file(&b, zNPath);
+        file_setexe(zNPath, isExec);
         printf("UPDATE %s\n", zNew);
       }else{
         int rc = merge_3way(&a, zOPath, &b, &out);
         blob_write_to_file(&out, zNPath);
+        file_setexe(zNPath, isExec);
         if( rc ){
           printf("CONFLICT %s\n", zNew);
           nConflict++;
         }else{
           printf("MERGE %s\n", zNew);

Index: src/undo.c
==================================================================
--- src/undo.c
+++ src/undo.c
@@ -30,27 +30,33 @@
 */
 static void undo_one(const char *zPathname, int redoFlag){
   Stmt q;
   char *zFullname;
   db_prepare(&q,
-    "SELECT content, existsflag FROM undo WHERE pathname=%Q AND redoflag=%d",
+    "SELECT content, existsflag, isExe FROM undo"
+    " WHERE pathname=%Q AND redoflag=%d",
      zPathname, redoFlag
   );
   if( db_step(&q)==SQLITE_ROW ){
     int old_exists;
     int new_exists;
+    int old_exe;
+    int new_exe;
     Blob current;
     Blob new;
     zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
     new_exists = file_size(zFullname)>=0;
     if( new_exists ){
       blob_read_from_file(&current, zFullname);
+      new_exe = file_isexe(zFullname);
     }else{
       blob_zero(&current);
+      new_exe = 0;
     }
     blob_zero(&new);
     old_exists = db_column_int(&q, 1);
+    old_exe = db_column_int(&q, 2);
     if( old_exists ){
       db_ephemeral_blob(&q, 0, &new);
     }
     if( old_exists ){
       if( new_exists ){
@@ -57,21 +63,23 @@
         printf("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
       }else{
         printf("NEW %s\n", zPathname);
       }
       blob_write_to_file(&new, zFullname);
+      file_setexe(zFullname, old_exe);
     }else{
       printf("DELETE %s\n", zPathname);
       unlink(zFullname);
     }
     blob_reset(&new);
     free(zFullname);
     db_finalize(&q);
     db_prepare(&q, 
-       "UPDATE undo SET content=:c, existsflag=%d, redoflag=NOT redoflag"
+       "UPDATE undo SET content=:c, existsflag=%d, isExe=%d,"
+             " redoflag=NOT redoflag"
        " WHERE pathname=%Q",
-       new_exists, zPathname
+       new_exists, new_exe, zPathname
     );
     if( new_exists ){
       db_bind_blob(&q, ":c", &current);
     }
     db_step(&q);
@@ -200,10 +208,11 @@
   static const char zSql[] = 
     @ CREATE TABLE %s.undo(
     @   pathname TEXT UNIQUE,             -- Name of the file
     @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
     @   existsflag BOOLEAN,               -- True if the file exists
+    @   isExe BOOLEAN,                    -- True if the file is executable
     @   content BLOB                      -- Saved content
     @ );
     @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
     @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
   ;
@@ -246,13 +255,13 @@
 
   if( !undoActive ) return;
   zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
   existsFlag = file_size(zFullname)>=0;
   db_prepare(&q,
-    "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,content)"
-    " VALUES(%Q,0,%d,:c)",
-    zPathname, existsFlag
+    "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,isExe,content)"
+    " VALUES(%Q,0,%d,%d,:c)",
+    zPathname, existsFlag, file_isexe(zFullname)
   );
   if( existsFlag ){
     blob_read_from_file(&content, zFullname);
     db_bind_blob(&q, ":c", &content);
   }

Index: src/update.c
==================================================================
--- src/update.c
+++ src/update.c
@@ -197,19 +197,21 @@
     "  idv INTEGER,"              /* VFILE entry for current version */
     "  idt INTEGER,"              /* VFILE entry for target version */
     "  chnged BOOLEAN,"           /* True if current version has been edited */
     "  ridv INTEGER,"             /* Record ID for current version */
     "  ridt INTEGER,"             /* Record ID for target */
+    "  isexe BOOLEAN,"            /* Does target have execute permission? */
     "  fnt TEXT"                  /* Filename of same file on target version */
     ");"
   );
 
   /* Add files found in the current version
   */
   db_multi_exec(
-    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,chnged)"
-    " SELECT pathname, pathname, id, 0, rid, 0, chnged FROM vfile WHERE vid=%d",
+    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
+    " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged"
+    "   FROM vfile WHERE vid=%d",
     vid
   );
 
   /* Compute file name changes on V->T.  Record name changes in files that
   ** have changed locally.
@@ -229,12 +231,12 @@
 
   /* Add files found in the target version T but missing from the current
   ** version V.
   */
   db_multi_exec(
-    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,chnged)"
-    " SELECT pathname, pathname, 0, 0, 0, 0, 0 FROM vfile"
+    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
+    " SELECT pathname, pathname, 0, 0, 0, 0, isexe, 0 FROM vfile"
     "  WHERE vid=%d"
     "    AND pathname NOT IN (SELECT fnt FROM fv)",
     tid
   );
 
@@ -248,18 +250,19 @@
     tid, tid
   );
 
   if( debugFlag ){
     db_prepare(&q,
-       "SELECT rowid, fn, fnt, chnged, ridv, ridt FROM fv"
+       "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe FROM fv"
     );
     while( db_step(&q)==SQLITE_ROW ){
-       printf("%3d: ridv=%-4d ridt=%-4d chnged=%d\n",
+       printf("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d\n",
           db_column_int(&q, 0),
           db_column_int(&q, 4),
           db_column_int(&q, 5),
-          db_column_int(&q, 3));
+          db_column_int(&q, 3),
+          db_column_int(&q, 6));
        printf("     fnv = [%s]\n", db_column_text(&q, 1));
        printf("     fnt = [%s]\n", db_column_text(&q, 2));
     }
     db_finalize(&q);
   }
@@ -299,11 +302,11 @@
   /*
   ** Alter the content of the checkout so that it conforms with the
   ** target
   */
   db_prepare(&q, 
-    "SELECT fn, idv, ridv, idt, ridt, chnged, fnt FROM fv ORDER BY 1"
+    "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe FROM fv ORDER BY 1"
   );
   db_prepare(&mtimeXfer,
     "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
     " WHERE id=:idt"
   );
@@ -316,10 +319,11 @@
     int ridv = db_column_int(&q, 2);            /* RecordID for current */
     int idt = db_column_int(&q, 3);             /* VFILE entry for target */
     int ridt = db_column_int(&q, 4);            /* RecordID for target */
     int chnged = db_column_int(&q, 5);          /* Current is edited */
     const char *zNewName = db_column_text(&q,6);/* New filename */
+    int isexe = db_column_int(&q, 6);           /* EXE perm for new file */
     char *zFullPath;                            /* Full pathname of the file */
     char *zFullNewPath;                         /* Full pathname of dest */
     char nameChng;                              /* True if the name changed */
 
     zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -374,17 +378,23 @@
       undo_save(zName);
       content_get(ridt, &t);
       content_get(ridv, &v);
       rc = merge_3way(&v, zFullPath, &t, &r);
       if( rc>=0 ){
-        if( !nochangeFlag ) blob_write_to_file(&r, zFullNewPath);
+        if( !nochangeFlag ){
+          blob_write_to_file(&r, zFullNewPath);
+          file_setexe(zFullNewPath, isexe);
+        }
         if( rc>0 ){
           printf("***** %d merge conflicts in %s\n", rc, zNewName);
           nConflict++;
         }
       }else{
-        if( !nochangeFlag ) blob_write_to_file(&t, zFullNewPath);
+        if( !nochangeFlag ){
+          blob_write_to_file(&t, zFullNewPath);
+          file_setexe(zFullNewPath, isexe);
+        }
         printf("***** Cannot merge binary file %s\n", zNewName);
         nConflict++;
       }
       if( nameChng && !nochangeFlag ) unlink(zFullPath);
       blob_reset(&v);
@@ -554,33 +564,40 @@
     int vid = db_lget_int("checkout", 0);
     zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
   }
   while( db_step(&q)==SQLITE_ROW ){
     int isExe = 0;
+    char *zFull;
     zFile = db_column_text(&q, 0);
+    zFull = mprintf("%/%/", g.zLocalRoot, zFile);
     errCode = historical_version_of_file(zRevision, zFile, &record, &isExe,2);
     if( errCode==2 ){
-      fossil_warning("file not in repository: %s", zFile);
+      if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
+        printf("UNMANAGE: %s\n", zFile);
+      }else{
+        undo_save(zFile);
+        unlink(zFull);
+        printf("DELETE: %s\n", zFile);
+      }
+      db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile);
     }else{
-      char *zFull = mprintf("%/%/", g.zLocalRoot, zFile);
+      sqlite3_int64 mtime;
       undo_save(zFile);
       blob_write_to_file(&record, zFull);
       file_setexe(zFull, isExe);
       printf("REVERTED: %s\n", zFile);
-      if( zRevision==0 ){
-        sqlite3_int64 mtime = file_mtime(zFull);
-        db_multi_exec(
-           "UPDATE vfile"
-           "   SET mtime=%lld, chnged=0, deleted=0, isexe=%d,"
-           "       pathname=coalesce(origname,pathname), origname=NULL"     
-           " WHERE pathname=%Q",
-           mtime, isExe, zFile
-        );
-      }
-      free(zFull);
+      mtime = file_mtime(zFull);
+      db_multi_exec(
+         "UPDATE vfile"
+         "   SET mtime=%lld, chnged=0, deleted=0, isexe=%d, mrid=rid,"
+         "       pathname=coalesce(origname,pathname), origname=NULL"     
+         " WHERE pathname=%Q",
+         mtime, isExe, zFile
+      );
     }
     blob_reset(&record);
+    free(zFull);
   }
   db_finalize(&q);
   undo_finish();
   db_end_transaction(0);
 }

Index: src/vfile.c
==================================================================
--- src/vfile.c
+++ src/vfile.c
@@ -85,21 +85,22 @@
   db_begin_transaction();
   p = manifest_get(vid, CFTYPE_MANIFEST);
   if( p==0 ) return;
   db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
   db_prepare(&ins,
-    "INSERT INTO vfile(vid,rid,mrid,pathname) "
-    " VALUES(:vid,:id,:id,:name)");
+    "INSERT INTO vfile(vid,isexe,rid,mrid,pathname) "
+    " VALUES(:vid,:isexe,:id,:id,:name)");
   db_bind_int(&ins, ":vid", vid);
   manifest_file_rewind(p);
   while( (pFile = manifest_file_next(p,0))!=0 ){
     if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
     rid = uuid_to_rid(pFile->zUuid, 0);
     if( rid==0 || content_size(rid, -1)<0 ){
       fossil_warning("content missing for %s", pFile->zName);
       continue;
     }
+    db_bind_int(&ins, ":isexe", manifest_file_mperm(pFile));
     db_bind_int(&ins, ":id", rid);
     db_bind_text(&ins, ":name", pFile->zName);
     db_step(&ins);
     db_reset(&ins);
   }
@@ -207,31 +208,36 @@
   Stmt q;
   Blob content;
   int nRepos = strlen(g.zLocalRoot);
 
   if( vid>0 && id==0 ){
-    db_prepare(&q, "SELECT id, %Q || pathname, mrid"
+    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
                    "  FROM vfile"
                    " WHERE vid=%d AND mrid>0",
                    g.zLocalRoot, vid);
   }else{
     assert( vid==0 && id>0 );
-    db_prepare(&q, "SELECT id, %Q || pathname, mrid"
+    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
                    "  FROM vfile"
                    " WHERE id=%d AND mrid>0",
                    g.zLocalRoot, id);
   }
   while( db_step(&q)==SQLITE_ROW ){
-    int id, rid;
+    int id, rid, isExe;
     const char *zName;
 
     id = db_column_int(&q, 0);
     zName = db_column_text(&q, 1);
     rid = db_column_int(&q, 2);
+    isExe = db_column_int(&q, 3);
     content_get(rid, &content);
     if( file_is_the_same(&content, zName) ){
       blob_reset(&content);
+      if( file_setexe(zName, isExe) ){
+        db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
+                      file_mtime(zName), id);
+      }
       continue;
     }
     if( promptFlag && file_size(zName)>=0 ){
       Blob ans;
       char *zMsg;
@@ -250,10 +256,11 @@
         continue;
       }
     }
     if( verbose ) printf("%s\n", &zName[nRepos]);
     blob_write_to_file(&content, zName);
+    file_setexe(zName, isExe);
     blob_reset(&content);
     db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
                   file_mtime(zName), id);
   }
   db_finalize(&q);