Changes On Branch exe-permission-fix
Not logged in

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

Changes In Branch exe-permission-fix Excluding Merge-Ins

This is equivalent to a diff from 3c39caac39 to 157eed29f4

2011-02-28
14:18
All of the execute permission bit handling appears to be working now, though beta testing would be good. Ticket [baf9b6b11e08c1]. check-in: 3ad119b703 user: drh tags: trunk
13:26
Fix the "revert" command so that it distinguishes between files that were added versus files imported by merge. Added files are not unlinked. Ticket [baf9b6b11e08c1]. Closed-Leaf check-in: 157eed29f4 user: drh tags: exe-permission-fix
03:26
Try to get the "stash" command using execute permission bits correctly. Continuing work on the "revert" command - but it is still not working quite right. Ticket [baf9b6b11e08c1d0b]. check-in: ae3409bf49 user: drh tags: exe-permission-fix
2011-02-27
23:31
Change the ZIP file generator so that it sets the execute bit approprately. Ticket [baf9b6b11e08c1d]. check-in: b57bc473b0 user: drh tags: trunk
22:22
Untested changes trying to get execute permission to be set correctly following "update", "merge", "stash", etc. Ticket [baf9b6b11e08c1d]. This is a big mess and is going to take some time to get right. check-in: 081aefde56 user: drh tags: exe-permission-fix
21:45
Fix the "revert" command so that it restores the correct execute permission to the file. Ticket [baf9b6b11e08c1] check-in: 3c39caac39 user: drh tags: trunk
21:08
Merge the --private sync enhancement into the trunk. check-in: 8b8cc4f1b7 user: drh tags: trunk

Changes to src/add.c.

   146    146   #else
   147    147       if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
   148    148         db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);
   149    149       }
   150    150   #endif
   151    151       else{
   152    152         db_multi_exec(
   153         -        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
   154         -        "VALUES(%d,0,0,0,%Q)", vid, zPath);
          153  +        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe)"
          154  +        "VALUES(%d,0,0,0,%Q,%d)",
          155  +        vid, zPath,file_isexe(zName));
   155    156       }
   156    157       printf("ADDED  %s\n", zPath);
   157    158     }
   158    159     blob_reset(&pathname);
   159    160   }
   160    161   
   161    162   /*

Changes to src/file.c.

   176    176       fwrite(zBuf, 1, got, out);
   177    177     }
   178    178     fclose(in);
   179    179     fclose(out);
   180    180   }
   181    181   
   182    182   /*
   183         -** Set or clear the execute bit on a file.
          183  +** Set or clear the execute bit on a file.  Return true if a change
          184  +** occurred and false if this routine is a no-op.
   184    185   */
   185         -void file_setexe(const char *zFilename, int onoff){
          186  +int file_setexe(const char *zFilename, int onoff){
          187  +  int rc = 0;
   186    188   #if !defined(_WIN32)
   187    189     struct stat buf;
   188         -  if( stat(zFilename, &buf)!=0 ) return;
          190  +  if( stat(zFilename, &buf)!=0 ) return 0;
   189    191     if( onoff ){
   190    192       if( (buf.st_mode & 0111)!=0111 ){
   191    193         chmod(zFilename, buf.st_mode | 0111);
          194  +      rc = 1;
   192    195       }
   193    196     }else{
   194    197       if( (buf.st_mode & 0111)!=0 ){
   195    198         chmod(zFilename, buf.st_mode & ~0111);
          199  +      rc = 1;
   196    200       }
   197    201     }
   198    202   #endif /* _WIN32 */
          203  +  return rc;
   199    204   }
   200    205   
   201    206   /*
   202    207   ** Create the directory named in the argument, if it does not already
   203    208   ** exist.  If forceFlag is 1, delete any prior non-directory object 
   204    209   ** with the same name.
   205    210   **

Changes to src/merge.c.

   156    156       "  idv INTEGER,"              /* VFILE entry for current version */
   157    157       "  idp INTEGER,"              /* VFILE entry for the pivot */
   158    158       "  idm INTEGER,"              /* VFILE entry for version merging in */
   159    159       "  chnged BOOLEAN,"           /* True if current version has been edited */
   160    160       "  ridv INTEGER,"             /* Record ID for current version */
   161    161       "  ridp INTEGER,"             /* Record ID for pivot */
   162    162       "  ridm INTEGER,"             /* Record ID for merge */
          163  +    "  isexe BOOLEAN,"            /* Execute permission enabled */
   163    164       "  fnp TEXT,"                 /* The filename in the pivot */
   164    165       "  fnm TEXT"                  /* the filename in the merged version */
   165    166       ");"
   166    167     );
   167    168   
   168    169     /* Add files found in V
   169    170     */
   170    171     db_multi_exec(
   171         -    "INSERT OR IGNORE INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,chnged)"
   172         -    " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, chnged "
          172  +    "INSERT OR IGNORE"
          173  +    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
          174  +    " SELECT pathname, pathname, pathname, id, 0, 0, rid, 0, 0, isexe, chnged "
   173    175       " FROM vfile WHERE vid=%d",
   174    176       vid
   175    177     );
   176    178   
   177    179     /*
   178    180     ** Compute name changes from P->V
   179    181     */
................................................................................
   192    194       fossil_free(aChng);
   193    195       db_multi_exec("UPDATE fv SET fnm=fnp WHERE fnp!=fn");
   194    196     }
   195    197   
   196    198     /* Add files found in P but not in V
   197    199     */
   198    200     db_multi_exec(
   199         -    "INSERT OR IGNORE INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,chnged)"
   200         -    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, 0 "
          201  +    "INSERT OR IGNORE"
          202  +    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
          203  +    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
   201    204       "   FROM vfile"
   202    205       "  WHERE vid=%d AND pathname NOT IN (SELECT fnp FROM fv)",
   203    206       pid
   204    207     );
   205    208   
   206    209     /*
   207    210     ** Compute name changes from P->M
................................................................................
   218    221       }
   219    222       fossil_free(aChng);
   220    223     }
   221    224   
   222    225     /* Add files found in M but not in P or V.
   223    226     */
   224    227     db_multi_exec(
   225         -    "INSERT OR IGNORE INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,chnged)"
   226         -    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, 0 "
          228  +    "INSERT OR IGNORE"
          229  +    " INTO fv(fn,fnp,fnm,idv,idp,idm,ridv,ridp,ridm,isexe,chnged)"
          230  +    " SELECT pathname, pathname, pathname, 0, 0, 0, 0, 0, 0, isexe, 0 "
   227    231       "   FROM vfile"
   228    232       "  WHERE vid=%d"
   229    233       "    AND pathname NOT IN (SELECT fnp FROM fv UNION SELECT fnm FROM fv)",
   230    234       mid
   231    235     );
   232    236   
   233    237     /*
................................................................................
   240    244       " idm=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnm),0),"
   241    245       " ridm=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnm),0)",
   242    246       pid, pid, mid, mid
   243    247     );
   244    248   
   245    249     if( debugFlag ){
   246    250       db_prepare(&q,
   247         -       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm FROM fv"
          251  +       "SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, isexe FROM fv"
   248    252       );
   249    253       while( db_step(&q)==SQLITE_ROW ){
   250         -       printf("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d\n",
          254  +       printf("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d\n",
   251    255             db_column_int(&q, 0),
   252    256             db_column_int(&q, 5),
   253    257             db_column_int(&q, 6),
   254    258             db_column_int(&q, 7),
   255         -          db_column_int(&q, 4));
          259  +          db_column_int(&q, 4),
          260  +          db_column_int(&q, 8));
   256    261          printf("     fn  = [%s]\n", db_column_text(&q, 1));
   257    262          printf("     fnp = [%s]\n", db_column_text(&q, 2));
   258    263          printf("     fnm = [%s]\n", db_column_text(&q, 3));
   259    264       }
   260    265       db_finalize(&q);
   261    266     }
   262    267   
................................................................................
   286    291     );
   287    292     while( db_step(&q)==SQLITE_ROW ){
   288    293       int idm = db_column_int(&q, 0);
   289    294       int rowid = db_column_int(&q, 1);
   290    295       int idv;
   291    296       const char *zName;
   292    297       db_multi_exec(
   293         -      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,pathname)"
   294         -      "  SELECT %d,3,0,rid,mrid,pathname FROM vfile WHERE id=%d",
          298  +      "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,pathname)"
          299  +      "  SELECT %d,3,0,rid,mrid,isexe,pathname FROM vfile WHERE id=%d",
   295    300         vid, idm
   296    301       );
   297    302       idv = db_last_insert_rowid();
   298    303       db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
   299    304       zName = db_column_text(&q, 2);
   300    305       printf("ADDED %s\n", zName);
   301    306       if( !nochangeFlag ){
................................................................................
   330    335     }
   331    336     db_finalize(&q);
   332    337   
   333    338     /*
   334    339     ** Do a three-way merge on files that have changes on both P->M and P->V.
   335    340     */
   336    341     db_prepare(&q,
   337         -    "SELECT ridm, idv, ridp, ridv, %s, fn FROM fv"
          342  +    "SELECT ridm, idv, ridp, ridv, %s, fn, isexe FROM fv"
   338    343       " WHERE idp>0 AND idv>0 AND idm>0"
   339    344       "   AND ridm!=ridp AND (ridv!=ridp OR chnged)",
   340    345       glob_expr("fv.fn", zBinGlob)
   341    346     );
   342    347     while( db_step(&q)==SQLITE_ROW ){
   343    348       int ridm = db_column_int(&q, 0);
   344    349       int idv = db_column_int(&q, 1);
   345    350       int ridp = db_column_int(&q, 2);
   346    351       int ridv = db_column_int(&q, 3);
   347    352       int isBinary = db_column_int(&q, 4);
   348    353       const char *zName = db_column_text(&q, 5);
          354  +    int isExe = db_column_int(&q, 6);
   349    355       int rc;
   350    356       char *zFullPath;
   351    357       Blob m, p, r;
   352    358       /* Do a 3-way merge of idp->idm into idp->idv.  The results go into idv. */
   353    359       if( detailFlag ){
   354    360         printf("MERGE %s  (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv);
   355    361       }else{
................................................................................
   364    370         blob_zero(&r);
   365    371       }else{
   366    372         rc = merge_3way(&p, zFullPath, &m, &r);
   367    373       }
   368    374       if( rc>=0 ){
   369    375         if( !nochangeFlag ){
   370    376           blob_write_to_file(&r, zFullPath);
          377  +        file_setexe(zFullPath, isExe);
   371    378         }
   372    379         db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
   373    380         if( rc>0 ){
   374    381           printf("***** %d merge conflicts in %s\n", rc, zName);
   375    382           nConflict++;
   376    383         }
   377    384       }else{

Changes to src/stash.c.

    90     90       const char *zOrig = db_column_text(&q, 4);
    91     91       char *zPath = mprintf("%s%s", g.zLocalRoot, zName);
    92     92       Blob content;
    93     93   
    94     94       db_bind_int(&ins, ":rid", rid);
    95     95       db_bind_int(&ins, ":isadd", rid==0);
    96     96       db_bind_int(&ins, ":isrm", deleted);
    97         -#ifdef _WIN32
    98     97       db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
    99         -#endif
   100     98       db_bind_text(&ins, ":orig", zOrig);
   101     99       db_bind_text(&ins, ":new", zName);
   102    100       if( rid==0 ){
   103    101         /* A new file */
   104    102         blob_read_from_file(&content, zPath);
   105    103         db_bind_blob(&ins, ":content", &content);
   106    104       }else if( deleted ){
................................................................................
   173    171        "SELECT rid, isRemoved, isExec, origname, newname, delta"
   174    172        "  FROM stashfile WHERE stashid=%d",
   175    173        stashid
   176    174     );
   177    175     while( db_step(&q)==SQLITE_ROW ){
   178    176       int rid = db_column_int(&q, 0);
   179    177       int isRemoved = db_column_int(&q, 1);
          178  +    int isExec = db_column_int(&q, 2);
   180    179       const char *zOrig = db_column_text(&q, 3);
   181    180       const char *zNew = db_column_text(&q, 4);
   182    181       char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
   183    182       char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew);
   184    183       Blob delta;
   185    184       undo_save(zNew);
   186    185       blob_zero(&delta);
   187    186       if( rid==0 ){
   188    187         db_ephemeral_blob(&q, 5, &delta);
   189    188         blob_write_to_file(&delta, zNPath);
          189  +      file_setexe(zNPath, isExec);
   190    190         printf("ADD %s\n", zNew);
   191    191       }else if( isRemoved ){
   192    192         printf("DELETE %s\n", zOrig);
   193    193         unlink(zOPath);
   194    194       }else{
   195    195         Blob a, b, out, disk;
   196    196         db_ephemeral_blob(&q, 5, &delta);
   197    197         blob_read_from_file(&disk, zOPath);     
   198    198         content_get(rid, &a);
   199    199         blob_delta_apply(&a, &delta, &b);
   200    200         if( blob_compare(&disk, &a)==0 ){
   201    201           blob_write_to_file(&b, zNPath);
          202  +        file_setexe(zNPath, isExec);
   202    203           printf("UPDATE %s\n", zNew);
   203    204         }else{
   204    205           int rc = merge_3way(&a, zOPath, &b, &out);
   205    206           blob_write_to_file(&out, zNPath);
          207  +        file_setexe(zNPath, isExec);
   206    208           if( rc ){
   207    209             printf("CONFLICT %s\n", zNew);
   208    210             nConflict++;
   209    211           }else{
   210    212             printf("MERGE %s\n", zNew);
   211    213           }
   212    214           blob_reset(&out);

Changes to src/undo.c.

    28     28   ** true the redo a change.  If there is nothing to undo (or redo) then
    29     29   ** this routine is a noop.
    30     30   */
    31     31   static void undo_one(const char *zPathname, int redoFlag){
    32     32     Stmt q;
    33     33     char *zFullname;
    34     34     db_prepare(&q,
    35         -    "SELECT content, existsflag FROM undo WHERE pathname=%Q AND redoflag=%d",
           35  +    "SELECT content, existsflag, isExe FROM undo"
           36  +    " WHERE pathname=%Q AND redoflag=%d",
    36     37        zPathname, redoFlag
    37     38     );
    38     39     if( db_step(&q)==SQLITE_ROW ){
    39     40       int old_exists;
    40     41       int new_exists;
           42  +    int old_exe;
           43  +    int new_exe;
    41     44       Blob current;
    42     45       Blob new;
    43     46       zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
    44     47       new_exists = file_size(zFullname)>=0;
    45     48       if( new_exists ){
    46     49         blob_read_from_file(&current, zFullname);
           50  +      new_exe = file_isexe(zFullname);
    47     51       }else{
    48     52         blob_zero(&current);
           53  +      new_exe = 0;
    49     54       }
    50     55       blob_zero(&new);
    51     56       old_exists = db_column_int(&q, 1);
           57  +    old_exe = db_column_int(&q, 2);
    52     58       if( old_exists ){
    53     59         db_ephemeral_blob(&q, 0, &new);
    54     60       }
    55     61       if( old_exists ){
    56     62         if( new_exists ){
    57     63           printf("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
    58     64         }else{
    59     65           printf("NEW %s\n", zPathname);
    60     66         }
    61     67         blob_write_to_file(&new, zFullname);
           68  +      file_setexe(zFullname, old_exe);
    62     69       }else{
    63     70         printf("DELETE %s\n", zPathname);
    64     71         unlink(zFullname);
    65     72       }
    66     73       blob_reset(&new);
    67     74       free(zFullname);
    68     75       db_finalize(&q);
    69     76       db_prepare(&q, 
    70         -       "UPDATE undo SET content=:c, existsflag=%d, redoflag=NOT redoflag"
           77  +       "UPDATE undo SET content=:c, existsflag=%d, isExe=%d,"
           78  +             " redoflag=NOT redoflag"
    71     79          " WHERE pathname=%Q",
    72         -       new_exists, zPathname
           80  +       new_exists, new_exe, zPathname
    73     81       );
    74     82       if( new_exists ){
    75     83         db_bind_blob(&q, ":c", &current);
    76     84       }
    77     85       db_step(&q);
    78     86       blob_reset(&current);
    79     87     }
................................................................................
   198    206     int cid;
   199    207     const char *zDb = db_name("localdb");
   200    208     static const char zSql[] = 
   201    209       @ CREATE TABLE %s.undo(
   202    210       @   pathname TEXT UNIQUE,             -- Name of the file
   203    211       @   redoflag BOOLEAN,                 -- 0 for undoable.  1 for redoable
   204    212       @   existsflag BOOLEAN,               -- True if the file exists
          213  +    @   isExe BOOLEAN,                    -- True if the file is executable
   205    214       @   content BLOB                      -- Saved content
   206    215       @ );
   207    216       @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
   208    217       @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
   209    218     ;
   210    219     if( undoDisable ) return;
   211    220     undo_reset();
................................................................................
   244    253     int existsFlag;
   245    254     Stmt q;
   246    255   
   247    256     if( !undoActive ) return;
   248    257     zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
   249    258     existsFlag = file_size(zFullname)>=0;
   250    259     db_prepare(&q,
   251         -    "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,content)"
   252         -    " VALUES(%Q,0,%d,:c)",
   253         -    zPathname, existsFlag
          260  +    "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,isExe,content)"
          261  +    " VALUES(%Q,0,%d,%d,:c)",
          262  +    zPathname, existsFlag, file_isexe(zFullname)
   254    263     );
   255    264     if( existsFlag ){
   256    265       blob_read_from_file(&content, zFullname);
   257    266       db_bind_blob(&q, ":c", &content);
   258    267     }
   259    268     free(zFullname);
   260    269     db_step(&q);

Changes to src/update.c.

   195    195       "CREATE TEMP TABLE fv("
   196    196       "  fn TEXT PRIMARY KEY,"      /* The filename relative to root */
   197    197       "  idv INTEGER,"              /* VFILE entry for current version */
   198    198       "  idt INTEGER,"              /* VFILE entry for target version */
   199    199       "  chnged BOOLEAN,"           /* True if current version has been edited */
   200    200       "  ridv INTEGER,"             /* Record ID for current version */
   201    201       "  ridt INTEGER,"             /* Record ID for target */
          202  +    "  isexe BOOLEAN,"            /* Does target have execute permission? */
   202    203       "  fnt TEXT"                  /* Filename of same file on target version */
   203    204       ");"
   204    205     );
   205    206   
   206    207     /* Add files found in the current version
   207    208     */
   208    209     db_multi_exec(
   209         -    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,chnged)"
   210         -    " SELECT pathname, pathname, id, 0, rid, 0, chnged FROM vfile WHERE vid=%d",
          210  +    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
          211  +    " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged"
          212  +    "   FROM vfile WHERE vid=%d",
   211    213       vid
   212    214     );
   213    215   
   214    216     /* Compute file name changes on V->T.  Record name changes in files that
   215    217     ** have changed locally.
   216    218     */
   217    219     find_filename_changes(vid, tid, &nChng, &aChng);
................................................................................
   227    229       fossil_free(aChng);
   228    230     }
   229    231   
   230    232     /* Add files found in the target version T but missing from the current
   231    233     ** version V.
   232    234     */
   233    235     db_multi_exec(
   234         -    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,chnged)"
   235         -    " SELECT pathname, pathname, 0, 0, 0, 0, 0 FROM vfile"
          236  +    "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
          237  +    " SELECT pathname, pathname, 0, 0, 0, 0, isexe, 0 FROM vfile"
   236    238       "  WHERE vid=%d"
   237    239       "    AND pathname NOT IN (SELECT fnt FROM fv)",
   238    240       tid
   239    241     );
   240    242   
   241    243     /*
   242    244     ** Compute the file version ids for T
................................................................................
   246    248       " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnt),0),"
   247    249       " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnt),0)",
   248    250       tid, tid
   249    251     );
   250    252   
   251    253     if( debugFlag ){
   252    254       db_prepare(&q,
   253         -       "SELECT rowid, fn, fnt, chnged, ridv, ridt FROM fv"
          255  +       "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe FROM fv"
   254    256       );
   255    257       while( db_step(&q)==SQLITE_ROW ){
   256         -       printf("%3d: ridv=%-4d ridt=%-4d chnged=%d\n",
          258  +       printf("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d\n",
   257    259             db_column_int(&q, 0),
   258    260             db_column_int(&q, 4),
   259    261             db_column_int(&q, 5),
   260         -          db_column_int(&q, 3));
          262  +          db_column_int(&q, 3),
          263  +          db_column_int(&q, 6));
   261    264          printf("     fnv = [%s]\n", db_column_text(&q, 1));
   262    265          printf("     fnt = [%s]\n", db_column_text(&q, 2));
   263    266       }
   264    267       db_finalize(&q);
   265    268     }
   266    269   
   267    270     /* If FILES appear on the command-line, remove from the "fv" table
................................................................................
   297    300     }
   298    301   
   299    302     /*
   300    303     ** Alter the content of the checkout so that it conforms with the
   301    304     ** target
   302    305     */
   303    306     db_prepare(&q, 
   304         -    "SELECT fn, idv, ridv, idt, ridt, chnged, fnt FROM fv ORDER BY 1"
          307  +    "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe FROM fv ORDER BY 1"
   305    308     );
   306    309     db_prepare(&mtimeXfer,
   307    310       "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
   308    311       " WHERE id=:idt"
   309    312     );
   310    313     assert( g.zLocalRoot!=0 );
   311    314     assert( strlen(g.zLocalRoot)>1 );
................................................................................
   314    317       const char *zName = db_column_text(&q, 0);  /* The filename from root */
   315    318       int idv = db_column_int(&q, 1);             /* VFILE entry for current */
   316    319       int ridv = db_column_int(&q, 2);            /* RecordID for current */
   317    320       int idt = db_column_int(&q, 3);             /* VFILE entry for target */
   318    321       int ridt = db_column_int(&q, 4);            /* RecordID for target */
   319    322       int chnged = db_column_int(&q, 5);          /* Current is edited */
   320    323       const char *zNewName = db_column_text(&q,6);/* New filename */
          324  +    int isexe = db_column_int(&q, 6);           /* EXE perm for new file */
   321    325       char *zFullPath;                            /* Full pathname of the file */
   322    326       char *zFullNewPath;                         /* Full pathname of dest */
   323    327       char nameChng;                              /* True if the name changed */
   324    328   
   325    329       zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
   326    330       zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
   327    331       nameChng = fossil_strcmp(zName, zNewName);
................................................................................
   372    376           printf("MERGE %s\n", zName);
   373    377         }
   374    378         undo_save(zName);
   375    379         content_get(ridt, &t);
   376    380         content_get(ridv, &v);
   377    381         rc = merge_3way(&v, zFullPath, &t, &r);
   378    382         if( rc>=0 ){
   379         -        if( !nochangeFlag ) blob_write_to_file(&r, zFullNewPath);
          383  +        if( !nochangeFlag ){
          384  +          blob_write_to_file(&r, zFullNewPath);
          385  +          file_setexe(zFullNewPath, isexe);
          386  +        }
   380    387           if( rc>0 ){
   381    388             printf("***** %d merge conflicts in %s\n", rc, zNewName);
   382    389             nConflict++;
   383    390           }
   384    391         }else{
   385         -        if( !nochangeFlag ) blob_write_to_file(&t, zFullNewPath);
          392  +        if( !nochangeFlag ){
          393  +          blob_write_to_file(&t, zFullNewPath);
          394  +          file_setexe(zFullNewPath, isexe);
          395  +        }
   386    396           printf("***** Cannot merge binary file %s\n", zNewName);
   387    397           nConflict++;
   388    398         }
   389    399         if( nameChng && !nochangeFlag ) unlink(zFullPath);
   390    400         blob_reset(&v);
   391    401         blob_reset(&t);
   392    402         blob_reset(&r);
................................................................................
   552    562     db_prepare(&q, "SELECT name FROM torevert");
   553    563     if( zRevision==0 ){
   554    564       int vid = db_lget_int("checkout", 0);
   555    565       zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
   556    566     }
   557    567     while( db_step(&q)==SQLITE_ROW ){
   558    568       int isExe = 0;
          569  +    char *zFull;
   559    570       zFile = db_column_text(&q, 0);
          571  +    zFull = mprintf("%/%/", g.zLocalRoot, zFile);
   560    572       errCode = historical_version_of_file(zRevision, zFile, &record, &isExe,2);
   561    573       if( errCode==2 ){
   562         -      fossil_warning("file not in repository: %s", zFile);
          574  +      if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
          575  +        printf("UNMANAGE: %s\n", zFile);
          576  +      }else{
          577  +        undo_save(zFile);
          578  +        unlink(zFull);
          579  +        printf("DELETE: %s\n", zFile);
          580  +      }
          581  +      db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile);
   563    582       }else{
   564         -      char *zFull = mprintf("%/%/", g.zLocalRoot, zFile);
          583  +      sqlite3_int64 mtime;
   565    584         undo_save(zFile);
   566    585         blob_write_to_file(&record, zFull);
   567    586         file_setexe(zFull, isExe);
   568    587         printf("REVERTED: %s\n", zFile);
   569         -      if( zRevision==0 ){
   570         -        sqlite3_int64 mtime = file_mtime(zFull);
   571         -        db_multi_exec(
   572         -           "UPDATE vfile"
   573         -           "   SET mtime=%lld, chnged=0, deleted=0, isexe=%d,"
   574         -           "       pathname=coalesce(origname,pathname), origname=NULL"     
   575         -           " WHERE pathname=%Q",
   576         -           mtime, isExe, zFile
   577         -        );
   578         -      }
   579         -      free(zFull);
          588  +      mtime = file_mtime(zFull);
          589  +      db_multi_exec(
          590  +         "UPDATE vfile"
          591  +         "   SET mtime=%lld, chnged=0, deleted=0, isexe=%d, mrid=rid,"
          592  +         "       pathname=coalesce(origname,pathname), origname=NULL"     
          593  +         " WHERE pathname=%Q",
          594  +         mtime, isExe, zFile
          595  +      );
   580    596       }
   581    597       blob_reset(&record);
          598  +    free(zFull);
   582    599     }
   583    600     db_finalize(&q);
   584    601     undo_finish();
   585    602     db_end_transaction(0);
   586    603   }

Changes to src/vfile.c.

    83     83     ManifestFile *pFile;
    84     84   
    85     85     db_begin_transaction();
    86     86     p = manifest_get(vid, CFTYPE_MANIFEST);
    87     87     if( p==0 ) return;
    88     88     db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
    89     89     db_prepare(&ins,
    90         -    "INSERT INTO vfile(vid,rid,mrid,pathname) "
    91         -    " VALUES(:vid,:id,:id,:name)");
           90  +    "INSERT INTO vfile(vid,isexe,rid,mrid,pathname) "
           91  +    " VALUES(:vid,:isexe,:id,:id,:name)");
    92     92     db_bind_int(&ins, ":vid", vid);
    93     93     manifest_file_rewind(p);
    94     94     while( (pFile = manifest_file_next(p,0))!=0 ){
    95     95       if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
    96     96       rid = uuid_to_rid(pFile->zUuid, 0);
    97     97       if( rid==0 || content_size(rid, -1)<0 ){
    98     98         fossil_warning("content missing for %s", pFile->zName);
    99     99         continue;
   100    100       }
          101  +    db_bind_int(&ins, ":isexe", manifest_file_mperm(pFile));
   101    102       db_bind_int(&ins, ":id", rid);
   102    103       db_bind_text(&ins, ":name", pFile->zName);
   103    104       db_step(&ins);
   104    105       db_reset(&ins);
   105    106     }
   106    107     db_finalize(&ins);
   107    108     manifest_destroy(p);
................................................................................
   205    206     int promptFlag         /* Prompt user to confirm overwrites */
   206    207   ){
   207    208     Stmt q;
   208    209     Blob content;
   209    210     int nRepos = strlen(g.zLocalRoot);
   210    211   
   211    212     if( vid>0 && id==0 ){
   212         -    db_prepare(&q, "SELECT id, %Q || pathname, mrid"
          213  +    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
   213    214                      "  FROM vfile"
   214    215                      " WHERE vid=%d AND mrid>0",
   215    216                      g.zLocalRoot, vid);
   216    217     }else{
   217    218       assert( vid==0 && id>0 );
   218         -    db_prepare(&q, "SELECT id, %Q || pathname, mrid"
          219  +    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
   219    220                      "  FROM vfile"
   220    221                      " WHERE id=%d AND mrid>0",
   221    222                      g.zLocalRoot, id);
   222    223     }
   223    224     while( db_step(&q)==SQLITE_ROW ){
   224         -    int id, rid;
          225  +    int id, rid, isExe;
   225    226       const char *zName;
   226    227   
   227    228       id = db_column_int(&q, 0);
   228    229       zName = db_column_text(&q, 1);
   229    230       rid = db_column_int(&q, 2);
          231  +    isExe = db_column_int(&q, 3);
   230    232       content_get(rid, &content);
   231    233       if( file_is_the_same(&content, zName) ){
   232    234         blob_reset(&content);
          235  +      if( file_setexe(zName, isExe) ){
          236  +        db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
          237  +                      file_mtime(zName), id);
          238  +      }
   233    239         continue;
   234    240       }
   235    241       if( promptFlag && file_size(zName)>=0 ){
   236    242         Blob ans;
   237    243         char *zMsg;
   238    244         char cReply;
   239    245         zMsg = mprintf("overwrite %s (a=always/y/N)? ", zName);
................................................................................
   248    254         if( cReply=='n' || cReply=='N' ){
   249    255           blob_reset(&content);
   250    256           continue;
   251    257         }
   252    258       }
   253    259       if( verbose ) printf("%s\n", &zName[nRepos]);
   254    260       blob_write_to_file(&content, zName);
          261  +    file_setexe(zName, isExe);
   255    262       blob_reset(&content);
   256    263       db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
   257    264                     file_mtime(zName), id);
   258    265     }
   259    266     db_finalize(&q);
   260    267   }
   261    268