Fossil

Changes On Branch rid-renumbering
Login

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

Changes In Branch rid-renumbering Excluding Merge-Ins

This is equivalent to a diff from 9c7fdea8 to 12a022b1

2019-01-21
16:54
When SQLite detects that the repository associated with a checkout has been replaced by a clone (such that the RID values potentially change) then automatically adjust the content of the checkout database. (check-in: fff37e62 user: drh tags: trunk)
09:38
Assorted improvements to the TLS/SSL docs. (check-in: 43166dcd user: wyoung tags: trunk)
2019-01-20
23:58
Pick up the cherrypick merge arrow display fix from trunk. (Closed-Leaf check-in: 12a022b1 user: drh tags: rid-renumbering)
23:57
Improved rendering of cherrypick merge arrows, especially on the "ardoise" and "eagle" skins. (check-in: 9c7fdea8 user: drh tags: trunk)
23:42
Minor comment changes. (check-in: e2a73756 user: drh tags: rid-renumbering)
16:47
Fix a hyperlink error on the graph-test page. (This should have been committed to trunk to begin with.) (check-in: 20431a7c user: drh tags: trunk)

Changes to src/add.c.

   185    185       db_multi_exec("UPDATE vfile SET deleted=0"
   186    186                     " WHERE pathname=%Q %s AND deleted",
   187    187                     zPath, filename_collation());
   188    188     }else{
   189    189       char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
   190    190       int isExe = file_isexe(zFullname, RepoFILE);
   191    191       db_multi_exec(
   192         -      "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
   193         -      "VALUES(%d,0,0,0,%Q,%d,%d)",
          192  +      "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)"
          193  +      "VALUES(%d,0,0,0,%Q,%d,%d,NULL)",
   194    194         vid, zPath, isExe, file_islink(0));
   195    195       fossil_free(zFullname);
   196    196     }
   197    197     if( db_changes() ){
   198    198       fossil_print("ADDED  %s\n", zPath);
   199    199       return 1;
   200    200     }else{

Changes to src/bisect.c.

   284    284           db_column_text(&q, 3),
   285    285           db_column_text(&q, 2),
   286    286           (db_column_int(&q, 4) && zGoodBad[0]!='C') ? " CURRENT" : "");
   287    287     }
   288    288     db_finalize(&q);
   289    289   }
   290    290   
          291  +
          292  +/*
          293  +** Reset the bisect subsystem.
          294  +*/
          295  +void bisect_reset(void){
          296  +  db_multi_exec(
          297  +    "DELETE FROM vvar WHERE name IN "
          298  +    " ('bisect-good', 'bisect-bad', 'bisect-log')"
          299  +  );
          300  +}
          301  +
   291    302   /*
   292    303   ** COMMAND: bisect
   293    304   **
   294    305   ** Usage: %fossil bisect SUBCOMMAND ...
   295    306   **
   296    307   ** Run various subcommands useful for searching for bugs.
   297    308   **
................................................................................
   487    498         if( i>=count(aBisectOption) ){
   488    499           fossil_fatal("no such bisect option: %s", g.argv[3]);
   489    500         }
   490    501       }else{
   491    502         usage("options ?NAME? ?VALUE?");
   492    503       }
   493    504     }else if( strncmp(zCmd, "reset", n)==0 ){
   494         -    db_multi_exec(
   495         -      "DELETE FROM vvar WHERE name IN "
   496         -      " ('bisect-good', 'bisect-bad', 'bisect-log')"
   497         -    );
          505  +    bisect_reset();
   498    506     }else if( strcmp(zCmd, "ui")==0 ){
   499    507       char *newArgv[8];
   500    508       newArgv[0] = g.argv[0];
   501    509       newArgv[1] = "ui";
   502    510       newArgv[2] = "--page";
   503    511       newArgv[3] = "timeline?bisect";
   504    512       newArgv[4] = 0;

Changes to src/checkin.c.

   305    305     }
   306    306     blob_reset(&rewrittenPathname);
   307    307     db_finalize(&q);
   308    308   
   309    309     /* If C_MERGE, put merge contributors at the end of the report. */
   310    310   skipFiles:
   311    311     if( flags & C_MERGE ){
   312         -    db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
   313         -                   " WHERE id<=0");
          312  +    db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0" );
   314    313       while( db_step(&q)==SQLITE_ROW ){
   315    314         if( flags & C_COMMENT ){
   316    315           blob_append(report, "# ", 2);
   317    316         }
   318    317         if( flags & C_CLASSIFY ){
   319    318           const char *zClass;
   320    319           switch( db_column_int(&q, 1) ){
................................................................................
  1633   1632       }
  1634   1633       db_finalize(&q);
  1635   1634       blob_appendf(pOut, "\n");
  1636   1635     }
  1637   1636     free(zDate);
  1638   1637   
  1639   1638     db_prepare(&q,
  1640         -    "SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || blob.uuid, merge"
  1641         -    "  FROM vmerge, blob"
         1639  +    "SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash, merge"
         1640  +    "  FROM vmerge"
  1642   1641       " WHERE (vmerge.id=-1 OR vmerge.id=-2)"
  1643         -    "   AND blob.rid=vmerge.merge"
  1644   1642       " ORDER BY 1");
  1645   1643     while( db_step(&q)==SQLITE_ROW ){
  1646   1644       const char *zCherrypickUuid = db_column_text(&q, 0);
  1647   1645       int mid = db_column_int(&q, 1);
  1648   1646       if( mid != vid ){
  1649   1647         blob_appendf(pOut, "Q %s\n", zCherrypickUuid);
  1650   1648       }
................................................................................
  1665   1663     if( zColor && zColor[0] ){
  1666   1664       /* One-time background color */
  1667   1665       blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
  1668   1666     }
  1669   1667     if( p->closeFlag ){
  1670   1668       blob_appendf(pOut, "T +closed *\n");
  1671   1669     }
  1672         -  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
         1670  +  db_prepare(&q, "SELECT mhash,merge FROM vmerge"
  1673   1671                    " WHERE id %s ORDER BY 1",
  1674   1672                    p->integrateFlag ? "IN(0,-4)" : "=(-4)");
  1675   1673     while( db_step(&q)==SQLITE_ROW ){
  1676   1674       const char *zIntegrateUuid = db_column_text(&q, 0);
  1677   1675       int rid = db_column_int(&q, 1);
  1678   1676       if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
  1679   1677           " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
................................................................................
  2398   2396         blob_reset(&fname);
  2399   2397       }
  2400   2398       nrid = content_put(&content);
  2401   2399       blob_reset(&content);
  2402   2400       if( rid>0 ){
  2403   2401         content_deltify(rid, &nrid, 1, 0);
  2404   2402       }
  2405         -    db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
         2403  +    db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d, mhash=NULL WHERE id=%d",
         2404  +                  nrid,nrid,id);
  2406   2405       db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
  2407   2406     }
  2408   2407     db_finalize(&q);
  2409   2408     if( nConflict && !allowConflict ){
  2410   2409       fossil_fatal("abort due to unresolved merge conflicts; "
  2411   2410                    "use --allow-conflict to override");
  2412   2411     }else if( abortCommit ){
................................................................................
  2506   2505                            dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){
  2507   2506       fossil_fatal("%s", g.zErrMsg);
  2508   2507     }
  2509   2508     assert( blob_is_reset(&manifest) );
  2510   2509     content_deltify(vid, &nvid, 1, 0);
  2511   2510     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
  2512   2511   
  2513         -  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
  2514         -                 " WHERE id=-4");
         2512  +  db_prepare(&q, "SELECT mhash,merge FROM vmerge WHERE id=-4");
  2515   2513     while( db_step(&q)==SQLITE_ROW ){
  2516   2514       const char *zIntegrateUuid = db_column_text(&q, 0);
  2517   2515       if( is_a_leaf(db_column_int(&q, 1)) ){
  2518   2516         fossil_print("Closed: %s\n", zIntegrateUuid);
  2519   2517       }else{
  2520   2518         fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid);
  2521   2519       }
................................................................................
  2533   2531     }
  2534   2532   
  2535   2533     /* Update the vfile and vmerge tables */
  2536   2534     db_multi_exec(
  2537   2535       "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);"
  2538   2536       "DELETE FROM vmerge;"
  2539   2537       "UPDATE vfile SET vid=%d;"
  2540         -    "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
         2538  +    "UPDATE vfile SET rid=mrid, mhash=NULL, chnged=0, deleted=0, origname=NULL"
  2541   2539       " WHERE is_selected(id);"
  2542   2540       , vid, nvid
  2543   2541     );
  2544   2542     db_set_checkout(nvid);
  2545   2543   
  2546   2544     /* Update the isexe and islink columns of the vfile table */
  2547   2545     db_prepare(&q,

Changes to src/db.c.

  1471   1471   
  1472   1472   /*
  1473   1473   ** If zDbName is a valid local database file, open it and return
  1474   1474   ** true.  If it is not a valid local database file, return 0.
  1475   1475   */
  1476   1476   static int isValidLocalDb(const char *zDbName){
  1477   1477     i64 lsize;
  1478         -  char *zVFileDef;
  1479   1478   
  1480   1479     if( file_access(zDbName, F_OK) ) return 0;
  1481   1480     lsize = file_size(zDbName, ExtFILE);
  1482   1481     if( lsize%1024!=0 || lsize<4096 ) return 0;
  1483   1482     db_open_or_attach(zDbName, "localdb");
  1484         -  zVFileDef = db_text(0, "SELECT sql FROM localdb.sqlite_master"
  1485         -                         " WHERE name=='vfile'");
  1486         -  if( zVFileDef==0 ) return 0;
         1483  +
         1484  +  /* Check to see if the checkout database has the lastest schema changes.
         1485  +  ** The most recent schema change (2019-01-19) is the addition of the
         1486  +  ** vmerge.mhash and vfile.mhash fields.  If the schema has the vmerge.mhash
         1487  +  ** column, assume everything else is up-to-date. 
         1488  +  */
         1489  +  if( db_table_has_column("localdb","vmerge","mhash") ){
         1490  +    return 1;   /* This is a checkout database with the latest schema */
         1491  +  }
         1492  +
         1493  +  /* If there is no vfile table, then assume we have picked up something
         1494  +  ** that is not even close to being a valid checkout database */
         1495  +  if( !db_table_exists("localdb","vfile") ){
         1496  +    return 0;  /* Not a  DB */
         1497  +  }
  1487   1498   
  1488   1499     /* If the "isexe" column is missing from the vfile table, then
  1489   1500     ** add it now.   This code added on 2010-03-06.  After all users have
  1490   1501     ** upgraded, this code can be safely deleted.
  1491   1502     */
  1492         -  if( sqlite3_strglob("* isexe *", zVFileDef)!=0 ){
         1503  +  if( !db_table_has_column("localdb","vfile","isexe") ){
  1493   1504       db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
  1494   1505     }
  1495   1506   
  1496   1507     /* If "islink"/"isLink" columns are missing from tables, then
  1497   1508     ** add them now.   This code added on 2011-01-17 and 2011-08-27.
  1498   1509     ** After all users have upgraded, this code can be safely deleted.
  1499   1510     */
  1500         -  if( sqlite3_strglob("* islink *", zVFileDef)!=0 ){
         1511  +  if( !db_table_has_column("localdb","vfile","isLink") ){
  1501   1512       db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
  1502   1513       if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){
  1503   1514         db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0");
  1504   1515       }
  1505   1516       if( db_local_table_exists_but_lacks_column("undo", "isLink") ){
  1506   1517         db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
  1507   1518       }
  1508   1519       if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
  1509   1520         db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
  1510   1521       }
  1511   1522     }
  1512         -  fossil_free(zVFileDef);
         1523  +
         1524  +  /* The design of the checkout database changed on 2019-01-19, adding the mhash
         1525  +  ** column to vfile and vmerge and changing the UNIQUE index on vmerge into
         1526  +  ** a PRIMARY KEY that includes the new mhash column.  However, we must have
         1527  +  ** the repository database at hand in order to do the migration, so that
         1528  +  ** step is deferred. */
  1513   1529     return 1;
  1514   1530   }
  1515   1531   
  1516   1532   /*
  1517   1533   ** Locate the root directory of the local repository tree.  The root
  1518   1534   ** directory is found by searching for a file named "_FOSSIL_" or ".fslckout"
  1519   1535   ** that contains a valid repository database.
................................................................................
  1654   1670     }
  1655   1671   
  1656   1672     /* Make a change to the CHECK constraint on the BLOB table for
  1657   1673     ** version 2.0 and later.
  1658   1674     */
  1659   1675     rebuild_schema_update_2_0();   /* Do the Fossil-2.0 schema updates */
  1660   1676   
  1661         -  /* If the checkout database was opened first, then check to make
  1662         -  ** sure that the repository database that was just opened has not
  1663         -  ** be replaced by a clone of the same project, with different RID
  1664         -  ** values.
  1665         -  */
  1666         -  if( g.localOpen && !db_fingerprint_ok() ){
  1667         -    /* Uncomment the following when we are ready for automatic recovery: */
  1668         -#if 0
  1669         -    stash_rid_renumbering_event();
  1670         -#else
  1671         -    fossil_print(
  1672         -      "Oops. It looks like the repository database file located at\n"
  1673         -      "    \"%s\"\n", zDbName
  1674         -    );
  1675         -    fossil_print(
  1676         -      "has been swapped with a clone that may have different\n"
  1677         -      "integer keys for the various artifacts. As of 2019-01-11,\n"
  1678         -      "we are working on enhancing Fossil to be able to deal with\n"
  1679         -      "that automatically, but we are not there yet. Sorry.\n\n"
  1680         -    );
  1681         -    fossil_print(
  1682         -      "As an interim workaround, try:\n"
  1683         -      "  %s close --force\n"
  1684         -      "  %s open \"%s\" --keep\n"
  1685         -      "Noting that any STASH and UNDO information "
  1686         -      "WILL BE IRREVOCABLY LOST.\n\n",
  1687         -      g.argv[0],
  1688         -      g.argv[0], zDbName
  1689         -    );
  1690         -    fossil_fatal("bad fingerprint");
  1691         -#endif
         1677  +  /* Additional checks that occur when opening the checkout database */
         1678  +  if( g.localOpen ){
         1679  +
         1680  +    /* If the repository database that was just opened has been
         1681  +    ** eplaced by a clone of the same project, with different RID
         1682  +    ** values, then renumber the RID values stored in various tables
         1683  +    ** of the checkout database, so that the repository and checkout
         1684  +    ** databases align.
         1685  +    */
         1686  +    if( !db_fingerprint_ok() ){
         1687  +      if( find_option("no-rid-adjust",0,0)!=0 ){
         1688  +        /* The --no-rid-adjust command-line option bypasses the RID value
         1689  +        ** updates. Intended for use during debugging, especially to be
         1690  +        ** able to run "fossil sql" after a database swap. */
         1691  +        fossil_print(
         1692  +          "WARNING: repository change detected, but no adjust made.\n"
         1693  +        );
         1694  +      }else if( find_option("rid-renumber-dryrun",0,0)!=0 ){
         1695  +        /* the --rid-renumber-dryrun option shows how RID values would be
         1696  +        ** renumbered, but does not actually perform the renumbering.
         1697  +        ** This is a debugging-only option. */
         1698  +        vfile_rid_renumbering_event(1);
         1699  +        exit(0);
         1700  +      }else{
         1701  +        char *z;
         1702  +        stash_rid_renumbering_event();
         1703  +        vfile_rid_renumbering_event(0);
         1704  +        undo_reset();
         1705  +        bisect_reset();
         1706  +        z = db_fingerprint(0);
         1707  +        db_lset("fingerprint", z);
         1708  +        fossil_free(z);
         1709  +        fossil_print(
         1710  +          "WARNING: The repository database has been replaced by a clone.\n"
         1711  +          "Bisect history and undo have been lost.\n"
         1712  +        );
         1713  +      }
         1714  +    }
         1715  +
         1716  +    /* Make sure the checkout database schema migration of 2019-01-20 
         1717  +    ** has occurred.
         1718  +    **
         1719  +    ** The 2019-01-19 migration is the addition of the vmerge.mhash and
         1720  +    ** vfile.mhash columns and making the vmerge.mhash column part of the
         1721  +    ** PRIMARY KEY for vmerge.
         1722  +    */
         1723  +    if( !db_table_has_column("localdb", "vfile", "mhash") ){
         1724  +      db_multi_exec("ALTER TABLE vfile ADD COLUMN mhash;");
         1725  +      db_multi_exec(
         1726  +        "UPDATE vfile"
         1727  +        "   SET mhash=(SELECT uuid FROM blob WHERE blob.rid=vfile.mrid)"
         1728  +        " WHERE mrid!=rid;"
         1729  +      );
         1730  +      if( !db_table_has_column("localdb", "vmerge", "mhash") ){
         1731  +        db_multi_exec("ALTER TABLE vmerge RENAME TO old_vmerge;");
         1732  +        db_multi_exec(zLocalSchemaVmerge /*works-like:""*/);
         1733  +        db_multi_exec(  
         1734  +           "INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
         1735  +           "  SELECT id, merge, blob.uuid FROM old_vmerge, blob"
         1736  +           "   WHERE old_vmerge.merge=blob.rid;"
         1737  +           "DROP TABLE old_vmerge;"
         1738  +        );
         1739  +      }
         1740  +    }
  1692   1741     }
  1693   1742   }
  1694   1743   
  1695   1744   /*
  1696   1745   ** Return true if there have been any changes to the repository
  1697   1746   ** database since it was opened.
  1698   1747   **
................................................................................
  2878   2927     }
  2879   2928   
  2880   2929   #if defined(_WIN32) || defined(__CYGWIN__)
  2881   2930   # define LOCALDB_NAME "./_FOSSIL_"
  2882   2931   #else
  2883   2932   # define LOCALDB_NAME "./.fslckout"
  2884   2933   #endif
  2885         -  db_init_database(LOCALDB_NAME, zLocalSchema,
         2934  +  db_init_database(LOCALDB_NAME, zLocalSchema, zLocalSchemaVmerge,
  2886   2935   #ifdef FOSSIL_LOCAL_WAL
  2887   2936                      "COMMIT; PRAGMA journal_mode=WAL; BEGIN;",
  2888   2937   #endif
  2889   2938                      (char*)0);
  2890   2939     db_delete_on_failure(LOCALDB_NAME);
  2891   2940     db_open_local(0);
  2892   2941     if( allowSymlinks>=0 ){

Changes to src/foci.c.

   119    119   **
   120    120   **   (0)     A full scan.  Visit every manifest in the repo.  (Slow)
   121    121   **   (1)     checkinID=?.  visit only the single manifest specified.
   122    122   **   (2)     symName=?     visit only the single manifest specified.
   123    123   */
   124    124   static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
   125    125     int i;
   126         -  pIdxInfo->estimatedCost = 10000.0;
          126  +  pIdxInfo->estimatedCost = 1000000000.0;
   127    127     for(i=0; i<pIdxInfo->nConstraint; i++){
          128  +    if( !pIdxInfo->aConstraint[i].usable ) continue;
   128    129       if( pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
   129    130        && (pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID
   130    131               || pIdxInfo->aConstraint[i].iColumn==FOCI_SYMNAME)
   131    132       ){
   132    133         if( pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID ){
   133    134           pIdxInfo->idxNum = 1;
   134    135         }else{

Changes to src/json_status.c.

   152    152     cson_object_set( oPay, "errorCount", json_new_int( nErr ) );
   153    153     db_finalize(&q);
   154    154   
   155    155   #if 0
   156    156     /* TODO: add "merged with" status.  First need (A) to decide on a
   157    157        structure and (B) to set up some tests for the multi-merge
   158    158        case.*/
   159         -  db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
   160         -                 " WHERE id<=0");
          159  +  db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
   161    160     while( db_step(&q)==SQLITE_ROW ){
   162    161       const char *zLabel = "MERGED_WITH";
   163    162       switch( db_column_int(&q, 1) ){
   164    163         case -1:  zLabel = "CHERRYPICK ";  break;
   165    164         case -2:  zLabel = "BACKOUT    ";  break;
   166    165         case -4:  zLabel = "INTEGRATE  ";  break;
   167    166       }

Changes to src/merge.c.

   162    162         );
   163    163       }
   164    164       free(zN);
   165    165       free(zV);
   166    166     }
   167    167     free(aChng);
   168    168   }
          169  +
          170  +/* Make an entry in the vmerge table for the given id, and rid.
          171  +*/
          172  +static void vmerge_insert(int id, int rid){
          173  +  db_multi_exec(
          174  +    "INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
          175  +    "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d))",
          176  +    id, rid, rid
          177  +  );
          178  +}
   169    179   
   170    180   /*
   171    181   ** COMMAND: merge
   172    182   **
   173    183   ** Usage: %fossil merge ?OPTIONS? ?VERSION?
   174    184   **
   175    185   ** The argument VERSION is a version that should be merged into the
................................................................................
   592    602       const char *zName = db_column_text(&q, 2);
   593    603       int islinkm = db_column_int(&q, 3);
   594    604       /* Copy content from idm over into idv.  Overwrite idv. */
   595    605       fossil_print("UPDATE %s\n", zName);
   596    606       if( !dryRunFlag ){
   597    607         undo_save(zName);
   598    608         db_multi_exec(
   599         -        "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d "
   600         -        " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, idv
          609  +        "UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
          610  +        " mhash=CASE WHEN rid<>%d"
          611  +                   " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
          612  +        " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
   601    613         );
   602    614         vfile_to_disk(0, idv, 0, 0);
   603    615       }
   604    616     }
   605    617     db_finalize(&q);
   606    618   
   607    619     /*
................................................................................
   662    674           fossil_print("***** Cannot merge binary file %s\n", zName);
   663    675           nConflict++;
   664    676         }
   665    677         blob_reset(&p);
   666    678         blob_reset(&m);
   667    679         blob_reset(&r);
   668    680       }
   669         -    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
   670         -                  idv,ridm);
          681  +    vmerge_insert(idv, ridm);
   671    682     }
   672    683     db_finalize(&q);
   673    684   
   674    685     /*
   675    686     ** Drop files that are in P and V but not in M
   676    687     */
   677    688     db_prepare(&q,
................................................................................
   784    795       " WHERE idp=0 AND idv=0 AND idm>0"
   785    796     );
   786    797     while( db_step(&q)==SQLITE_ROW ){
   787    798       int idm = db_column_int(&q, 0);
   788    799       const char *zName;
   789    800       char *zFullName;
   790    801       db_multi_exec(
   791         -      "REPLACE INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
   792         -      "  SELECT %d,%d,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
          802  +      "REPLACE INTO vfile(vid,chnged,deleted,rid,mrid,"
          803  +                         "isexe,islink,pathname,mhash)"
          804  +      "  SELECT %d,%d,0,rid,mrid,isexe,islink,pathname,"
          805  +              "CASE WHEN rid<>mrid"
          806  +              "     THEN (SELECT uuid FROM blob WHERE blob.rid=vfile.mrid) END "
          807  +              "FROM vfile WHERE id=%d",
   793    808         vid, integrateFlag?5:3, idm
   794    809       );
   795    810       zName = db_column_text(&q, 1);
   796    811       zFullName = mprintf("%s%s", g.zLocalRoot, zName);
   797    812       if( file_isfile_or_link(zFullName)
   798    813           && !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
   799    814         fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
................................................................................
   824    839     }
   825    840   
   826    841     /*
   827    842     ** Clean up the mid and pid VFILE entries.  Then commit the changes.
   828    843     */
   829    844     db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   830    845     if( pickFlag ){
   831         -    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-1,%d)",mid);
          846  +    vmerge_insert(-1, mid);
   832    847       /* For a cherry-pick merge, make the default check-in comment the same
   833    848       ** as the check-in comment on the check-in that is being merged in. */
   834    849       db_multi_exec(
   835    850          "REPLACE INTO vvar(name,value)"
   836    851          " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
   837    852          "  WHERE type='ci' AND objid=%d",
   838    853          mid
   839    854       );
   840    855     }else if( backoutFlag ){
   841         -    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-2,%d)",pid);
          856  +    vmerge_insert(-2, pid);
   842    857     }else if( integrateFlag ){
   843         -    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-4,%d)",mid);
          858  +    vmerge_insert(-4, mid);
   844    859     }else{
   845         -    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
          860  +    vmerge_insert(0, mid);
   846    861     }
   847    862     if( !dryRunFlag ) undo_finish();
   848    863     db_end_transaction(dryRunFlag);
   849    864   }

Changes to src/schema.c.

     1      1   /*
     2      2   ** Copyright (c) 2007 D. Richard Hipp
     3      3   **
     4      4   ** This program is free software; you can redistribute it and/or
     5      5   ** modify it under the terms of the Simplified BSD License (also
     6      6   ** known as the "2-Clause License" or "FreeBSD License".)
     7         -
            7  +**
     8      8   ** This program is distributed in the hope that it will be useful,
     9      9   ** but without any warranty; without even the implied warranty of
    10     10   ** merchantability or fitness for a particular purpose.
    11     11   **
    12     12   ** Author contact information:
    13     13   **   drh@hwaci.com
    14     14   **   http://www.hwaci.com/drh/
................................................................................
   350    350   @ -- used to reduce push operations to a single HTTP request in the
   351    351   @ -- common case when one repository only talks to a single server.
   352    352   @ --
   353    353   @ CREATE TABLE unsent(
   354    354   @   rid INTEGER PRIMARY KEY         -- Record ID of the phantom
   355    355   @ );
   356    356   @
   357         -@ -- Each baseline or manifest can have one or more tags.  A tag
          357  +@ -- Each artifact can have one or more tags.  A tag
   358    358   @ -- is defined by a row in the next table.
   359    359   @ --
   360    360   @ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of
   361    361   @ -- the wiki page.  Tickets changes are tagged with "ticket-UUID" where
   362    362   @ -- UUID is the indentifier of the ticket.  Tags used to assign symbolic
   363    363   @ -- names to baselines are branches are of the form "sym-NAME" where
   364    364   @ -- NAME is the symbolic name.
................................................................................
   375    375   @ INSERT INTO tag VALUES(6, 'private');         -- TAG_PRIVATE
   376    376   @ INSERT INTO tag VALUES(7, 'cluster');         -- TAG_CLUSTER
   377    377   @ INSERT INTO tag VALUES(8, 'branch');          -- TAG_BRANCH
   378    378   @ INSERT INTO tag VALUES(9, 'closed');          -- TAG_CLOSED
   379    379   @ INSERT INTO tag VALUES(10,'parent');          -- TAG_PARENT
   380    380   @ INSERT INTO tag VALUES(11,'note');            -- TAG_NOTE
   381    381   @
   382         -@ -- Assignments of tags to baselines.  Note that we allow tags to
          382  +@ -- Assignments of tags to artifacts.  Note that we allow tags to
   383    383   @ -- have values assigned to them.  So we are not really dealing with
   384    384   @ -- tags here.  These are really properties.  But we are going to
   385    385   @ -- keep calling them tags because in many cases the value is ignored.
   386    386   @ --
   387    387   @ CREATE TABLE tagxref(
   388    388   @   tagid INTEGER REFERENCES tag,   -- The tag that added or removed
   389    389   @   tagtype INTEGER,                -- 0:-,cancel  1:+,single  2:*,propagate
................................................................................
   488    488   # define TAG_PARENT     10    /* Change to parentage on a check-in */
   489    489   # define TAG_NOTE       11    /* Extra text appended to a check-in comment */
   490    490   #endif
   491    491   
   492    492   /*
   493    493   ** The schema for the local FOSSIL database file found at the root
   494    494   ** of every check-out.  This database contains the complete state of
   495         -** the checkout.
          495  +** the checkout.  See also the addendum in zLocalSchemaVmerge[].
   496    496   */
   497    497   const char zLocalSchema[] =
   498    498   @ -- The VVAR table holds miscellanous information about the local database
   499    499   @ -- in the form of name-value pairs.  This is similar to the VAR table
   500    500   @ -- table in the repository except that this table holds information that
   501    501   @ -- is specific to the local checkout.
   502    502   @ --
................................................................................
   513    513   @
   514    514   @ -- Each entry in the vfile table represents a single file in the
   515    515   @ -- current checkout.
   516    516   @ --
   517    517   @ -- The file.rid field is 0 for files or folders that have been
   518    518   @ -- added but not yet committed.
   519    519   @ --
   520         -@ -- Vfile.chnged is 0 for unmodified files, 1 for files that have
   521         -@ -- been edited or which have been subjected to a 3-way merge.
   522         -@ -- Vfile.chnged is 2 if the file has been replaced from a different
   523         -@ -- version by the merge and 3 if the file has been added by a merge.
   524         -@ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been
   525         -@ -- done by an --integrate merge.  The difference between vfile.chnged==3|5
   526         -@ -- and a regular add is that with vfile.chnged==3|5 we know that the
   527         -@ -- current version of the file is already in the repository.
          520  +@ -- Vfile.chnged meaning:
          521  +@ --    0       File is unmodified
          522  +@ --    1       Manually edited and/or modified as part of a merge command
          523  +@ --    2       Replaced by a merge command
          524  +@ --    3       Added by a merge command
          525  +@ --    4,5     Same as 2,3 except merge using --integrate
   528    526   @ --
   529    527   @ CREATE TABLE vfile(
   530    528   @   id INTEGER PRIMARY KEY,           -- ID of the checked out file
   531         -@   vid INTEGER REFERENCES blob,      -- The baseline this file is part of.
          529  +@   vid INTEGER REFERENCES blob,      -- The checkin this file is part of.
   532    530   @   chnged INT DEFAULT 0,  -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add
   533    531   @   deleted BOOLEAN DEFAULT 0,        -- True if deleted
   534    532   @   isexe BOOLEAN,                    -- True if file should be executable
   535    533   @   islink BOOLEAN,                   -- True if file should be symlink
   536    534   @   rid INTEGER,                      -- Originally from this repository record
   537    535   @   mrid INTEGER,                     -- Based on this record due to a merge
   538    536   @   mtime INTEGER,                    -- Mtime of file on disk. sec since 1970
   539    537   @   pathname TEXT,                    -- Full pathname relative to root
   540    538   @   origname TEXT,                    -- Original pathname. NULL if unchanged
          539  +@   mhash TEXT,                       -- Hash of mrid iff mrid!=rid
   541    540   @   UNIQUE(pathname,vid)
   542    541   @ );
   543    542   @
          543  +@ -- Identifier for this file type.
          544  +@ -- The integer is the same as 'FSLC'.
          545  +@ PRAGMA application_id=252006674;
          546  +;
          547  +
          548  +/* Additional local database initialization following the schema
          549  +** enhancement of 2019-01-19, in which the mhash column was added
          550  +** to vmerge and vfile.
          551  +*/
          552  +const char zLocalSchemaVmerge[] =
   544    553   @ -- This table holds a record of uncommitted merges in the local
   545    554   @ -- file tree.  If a VFILE entry with id has merged with another
   546    555   @ -- record, there is an entry in this table with (id,merge) where
   547    556   @ -- merge is the RECORD table entry that the file merged against.
   548    557   @ -- An id of 0 or <-3 here means the version record itself.  When
   549    558   @ -- id==(-1) that is a cherrypick merge, id==(-2) that is a
   550    559   @ -- backout merge and id==(-4) is a integrate merge.
          560  +@ --
   551    561   @
   552    562   @ CREATE TABLE vmerge(
   553    563   @   id INTEGER REFERENCES vfile,      -- VFILE entry that has been merged
   554    564   @   merge INTEGER,                    -- Merged with this record
   555         -@   UNIQUE(id, merge)
          565  +@   mhash TEXT                        -- SHA1/SHA3 hash for merge object
   556    566   @ );
          567  +@ CREATE UNIQUE INDEX vmergex1 ON vmerge(id,mhash);
          568  +@
          569  +@ -- The following trigger will prevent older versions of Fossil that
          570  +@ -- do not know about the new vmerge.mhash column from updating the
          571  +@ -- vmerge table.  This must be done with a trigger, since legacy Fossil
          572  +@ -- uses INSERT OR IGNORE to update vmerge, and the OR IGNORE will cause
          573  +@ -- a NOT NULL constraint to be silently ignored.
   557    574   @
   558         -@ -- Identifier for this file type.
   559         -@ -- The integer is the same as 'FSLC'.
   560         -@ PRAGMA application_id=252006674;
          575  +@ CREATE TRIGGER vmerge_ck1 AFTER INSERT ON vmerge
          576  +@ WHEN new.mhash IS NULL BEGIN
          577  +@   SELECT raise(FAIL,
          578  +@   'trying to update a newer checkout with an older version of Fossil');
          579  +@ END;
          580  +@
   561    581   ;
   562    582   
   563    583   /*
   564    584   ** The following table holds information about forum posts.  It
   565    585   ** is created on-demand whenever the manifest parser encounters
   566    586   ** a forum-post artifact.
   567    587   */

Changes to src/update.c.

   527    527     }
   528    528   
   529    529     /* Report on conflicts
   530    530     */
   531    531     if( !dryRunFlag ){
   532    532       Stmt q;
   533    533       int nMerge = 0;
   534         -    db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
   535         -                   " WHERE id<=0");
          534  +    db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
   536    535       while( db_step(&q)==SQLITE_ROW ){
   537    536         const char *zLabel = "merge";
   538    537         switch( db_column_int(&q, 1) ){
   539    538           case -1:  zLabel = "cherrypick merge"; break;
   540    539           case -2:  zLabel = "backout merge";    break;
   541    540         }
   542    541         fossil_warning("uncommitted %s against %S.",
................................................................................
   863    862           blob_write_to_file(&record, zFull);
   864    863         }
   865    864         file_setexe(zFull, rvPerm==PERM_EXE);
   866    865         fossil_print("REVERT   %s\n", zFile);
   867    866         mtime = file_mtime(zFull, RepoFILE);
   868    867         db_multi_exec(
   869    868            "UPDATE vfile"
   870         -         "   SET mtime=%lld, chnged=%d, deleted=0, isexe=%d, islink=%d,mrid=rid"
          869  +         "   SET mtime=%lld, chnged=%d, deleted=0, isexe=%d, islink=%d,"
          870  +         "       mrid=rid, mhash=NULL"
   871    871            " WHERE pathname=%Q OR origname=%Q",
   872    872            mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
   873    873         );
   874    874       }
   875    875       blob_reset(&record);
   876    876       free(zFull);
   877    877     }

Changes to src/vfile.c.

    86     86     db_begin_transaction();
    87     87     p = manifest_get(vid, CFTYPE_MANIFEST, 0);
    88     88     if( p==0 ) {
    89     89       db_end_transaction(1);
    90     90       return 0;
    91     91     }
    92     92     db_prepare(&ins,
    93         -    "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) "
    94         -    " VALUES(:vid,:isexe,:islink,:id,:id,:name)");
           93  +    "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname,mhash) "
           94  +    " VALUES(:vid,:isexe,:islink,:id,:id,:name,NULL)");
    95     95     db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid");
    96     96     db_bind_int(&ins, ":vid", vid);
    97     97     manifest_file_rewind(p);
    98     98     nMissing = 0;
    99     99     while( (pFile = manifest_file_next(p,0))!=0 ){
   100    100       if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
   101    101       db_bind_text(&ridq, ":uuid", pFile->zUuid);
................................................................................
   240    240         ** if --hash is used, check to see if they have been edited by
   241    241         ** looking at their artifact hashes */
   242    242         const char *zUuid = db_column_text(&q, 5);
   243    243         int nUuid = db_column_bytes(&q, 5);
   244    244         assert( origSize==currentSize );
   245    245         if( !hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 1;
   246    246       }
   247         -    if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2 || chnged==4) ){
          247  +    if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2 || chnged==4)){
   248    248         i64 desiredMtime;
   249    249         if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){
   250    250           if( currentMtime!=desiredMtime ){
   251    251             file_set_mtime(zName, desiredMtime);
   252    252             currentMtime = file_mtime(zName, RepoFILE);
   253    253           }
   254    254         }
................................................................................
   964    964     vfile_aggregate_checksum_repository(vid, &hash);
   965    965     printf("archive:  %s\n", blob_str(&hash));
   966    966     blob_reset(&hash);
   967    967     vfile_aggregate_checksum_manifest(vid, &hash, &hash2);
   968    968     printf("manifest: %s\n", blob_str(&hash));
   969    969     printf("recorded: %s\n", blob_str(&hash2));
   970    970   }
          971  +
          972  +/*
          973  +** This routine recomputes certain columns of the vfile and vmerge tables
          974  +** when the associated repository is swapped out for a clone of the same
          975  +** project, and the blob.rid value change.  The following columns are
          976  +** updated:
          977  +**
          978  +**      vmerge.merge
          979  +**      vfile.vid
          980  +**      vfile.rid
          981  +**      vfile.mrid
          982  +**
          983  +** Also:
          984  +**
          985  +**      vvar.value WHERE name='checkout'
          986  +*/
          987  +void vfile_rid_renumbering_event(int dryRun){
          988  +  int oldVid;
          989  +  int newVid;
          990  +  char *zUnresolved;
          991  +
          992  +  oldVid = db_lget_int("checkout", 0);
          993  +  newVid = db_int(0, "SELECT blob.rid FROM blob, vvar"
          994  +                     " WHERE blob.uuid=vvar.value"
          995  +                     "   AND vvar.name='checkout-hash'");
          996  +
          997  +  /* The idMap table will make old RID values into new ones */
          998  +  db_multi_exec(
          999  +    "CREATE TEMP TABLE idMap(oldrid INTEGER PRIMARY KEY, newrid INT);\n"
         1000  +  );
         1001  +
         1002  +  /* Add the RID value for the current check-out */
         1003  +  db_multi_exec(
         1004  +    "INSERT INTO idMap(oldrid, newrid) VALUES(%d,%d)",
         1005  +    oldVid, newVid
         1006  +  );
         1007  +
         1008  +  /* Add the RID values for any other check-ins that have been merged into
         1009  +  ** the current check-out. */
         1010  +  db_multi_exec(
         1011  +    "INSERT OR IGNORE INTO idMap(oldrid, newrid)"
         1012  +    "  SELECT vmerge.merge, blob.rid FROM vmerge, blob"
         1013  +    "   WHERE blob.uuid=vmerge.mhash;"
         1014  +  );
         1015  +
         1016  +  /* Add RID values for files in the current check-out */
         1017  +  db_multi_exec(
         1018  +    "CREATE TEMP TABLE hashoffile(name TEXT PRIMARY KEY, hash TEXT)"
         1019  +    "WITHOUT ROWID;"
         1020  +
         1021  +    "INSERT INTO hashoffile(name,hash)"
         1022  +    "  SELECT filename, uuid FROM vvar, files_of_checkin(vvar.value)"
         1023  +    "   WHERE vvar.name='checkout-hash';"
         1024  +
         1025  +    "INSERT OR IGNORE INTO idMap(oldrid, newrid)"
         1026  +    "  SELECT vfile.rid, blob.rid FROM vfile, hashoffile, blob"
         1027  +    "   WHERE hashoffile.name=coalesce(vfile.origname,vfile.pathname)"
         1028  +    "     AND blob.uuid=hashoffile.hash;"
         1029  +  );
         1030  +
         1031  +  /* Add RID values for merged-in files */
         1032  +  db_multi_exec(
         1033  +    "INSERT OR IGNORE INTO idMap(oldrid, newrid)"
         1034  +    " SELECT vfile.mrid, blob.rid FROM vfile, blob"
         1035  +    "  WHERE blob.uuid=vfile.mhash;"
         1036  +  );
         1037  +  
         1038  +  if( dryRun ){
         1039  +    Stmt q;
         1040  +    db_prepare(&q, "SELECT oldrid, newrid, blob.uuid"
         1041  +                   "  FROM idMap, blob WHERE blob.rid=idMap.newrid");
         1042  +    while( db_step(&q)==SQLITE_ROW ){
         1043  +      fossil_print("%8d -> %8d  %.25s\n", 
         1044  +         db_column_int(&q,0),
         1045  +         db_column_int(&q,1),
         1046  +         db_column_text(&q,2));
         1047  +    }
         1048  +    db_finalize(&q);
         1049  +  }
         1050  +
         1051  +  /* Verify that all RID values in the VFILE table and VMERGE table have
         1052  +  ** been resolved. */
         1053  +  zUnresolved = db_text("",
         1054  +     "WITH allrid(x) AS ("
         1055  +     "  SELECT rid FROM vfile"
         1056  +     "  UNION SELECT mrid FROM vfile"
         1057  +     "  UNION SELECT merge FROM vmerge"
         1058  +     "  UNION SELECT %d"
         1059  +     ")"
         1060  +     "SELECT group_concat(x,' ') FROM allrid"
         1061  +     " WHERE x NOT IN (SELECT oldrid FROM idMap);",
         1062  +     oldVid
         1063  +  );
         1064  +  if( zUnresolved[0] ){
         1065  +    fossil_fatal("Unresolved RID values: %s\n", zUnresolved);
         1066  +  }
         1067  +
         1068  +  /* Make the changes to the VFILE and VMERGE tables */
         1069  +  if( !dryRun ){
         1070  +    db_multi_exec(
         1071  +      "UPDATE vfile"
         1072  +      "   SET rid=(SELECT newrid FROM idMap WHERE oldrid=vfile.rid)"
         1073  +      " WHERE vid=%d AND rid>0;", oldVid);
         1074  +
         1075  +    db_multi_exec(
         1076  +      "UPDATE vfile"
         1077  +      "   SET mrid=(SELECT newrid FROM idMap WHERE oldrid=vfile.mrid)"
         1078  +      " WHERE vid=%d AND mrid>0;", oldVid);
         1079  +
         1080  +    db_multi_exec(
         1081  +      "UPDATE vfile"
         1082  +      "   SET vid=%d"
         1083  +      " WHERE vid=%d", newVid, oldVid);
         1084  +
         1085  +    db_multi_exec(
         1086  +      "UPDATE vmerge"
         1087  +      "   SET merge=(SELECT newrid FROM idMap WHERE oldrid=vmerge.merge);");
         1088  +
         1089  +    db_lset_int("checkout",newVid);
         1090  +  }
         1091  +
         1092  +  /* Clear out the TEMP tables we constructed */
         1093  +  db_multi_exec(
         1094  +    "DROP TABLE idMap;"
         1095  +    "DROP TABLE hashoffile;"
         1096  +  );
         1097  +}