Fossil

Check-in [8977dfb0]
Login

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

Overview
Comment:Change the schema of the vmerge table so that it remembers the hash of merged artifacts, not the RID. Schema updates are automatic, but once the update occurs, you are committed to using the newer version of Fossil moving forward.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rid-renumbering
Files: files | file ages | folders
SHA3-256:8977dfb03295226c720683a6756db00038ed4a2991535dc5910bbadc091cdf22
User & Date: drh 2019-01-19 21:31:01
Context
2019-01-19
23:47
Clean up some old comments on the schema. No code changes. check-in: f51856be user: drh tags: rid-renumbering
21:31
Change the schema of the vmerge table so that it remembers the hash of merged artifacts, not the RID. Schema updates are automatic, but once the update occurs, you are committed to using the newer version of Fossil moving forward. check-in: 8977dfb0 user: drh tags: rid-renumbering
18:29
Enhance the stash so that it stores hashes and no long depends on RID value. Do this is a way that is backwards compatible and transparent to the user. After running any "stash" command using this version of Fossil or later, the schema will automatically update and the stash should survive a subsequent RID renumbering event in the repository without damage. check-in: ed06585f user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/checkin.c.

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
....
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
....
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
....
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
  }
  blob_reset(&rewrittenPathname);
  db_finalize(&q);

  /* If C_MERGE, put merge contributors at the end of the report. */
skipFiles:
  if( flags & C_MERGE ){
    db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
                   " WHERE id<=0");
    while( db_step(&q)==SQLITE_ROW ){
      if( flags & C_COMMENT ){
        blob_append(report, "# ", 2);
      }
      if( flags & C_CLASSIFY ){
        const char *zClass;
        switch( db_column_int(&q, 1) ){
................................................................................
    }
    db_finalize(&q);
    blob_appendf(pOut, "\n");
  }
  free(zDate);

  db_prepare(&q,
    "SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || blob.uuid, merge"
    "  FROM vmerge, blob"
    " WHERE (vmerge.id=-1 OR vmerge.id=-2)"
    "   AND blob.rid=vmerge.merge"
    " ORDER BY 1");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zCherrypickUuid = db_column_text(&q, 0);
    int mid = db_column_int(&q, 1);
    if( mid != vid ){
      blob_appendf(pOut, "Q %s\n", zCherrypickUuid);
    }
................................................................................
  if( zColor && zColor[0] ){
    /* One-time background color */
    blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
  }
  if( p->closeFlag ){
    blob_appendf(pOut, "T +closed *\n");
  }
  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id %s ORDER BY 1",
                 p->integrateFlag ? "IN(0,-4)" : "=(-4)");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zIntegrateUuid = db_column_text(&q, 0);
    int rid = db_column_int(&q, 1);
    if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
        " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
................................................................................
                         dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){
    fossil_fatal("%s", g.zErrMsg);
  }
  assert( blob_is_reset(&manifest) );
  content_deltify(vid, &nvid, 1, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);

  db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=-4");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zIntegrateUuid = db_column_text(&q, 0);
    if( is_a_leaf(db_column_int(&q, 1)) ){
      fossil_print("Closed: %s\n", zIntegrateUuid);
    }else{
      fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid);
    }







|
<







 







|
|

<







 







|







 







<
|







305
306
307
308
309
310
311
312

313
314
315
316
317
318
319
....
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641

1642
1643
1644
1645
1646
1647
1648
....
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
....
2504
2505
2506
2507
2508
2509
2510

2511
2512
2513
2514
2515
2516
2517
2518
  }
  blob_reset(&rewrittenPathname);
  db_finalize(&q);

  /* If C_MERGE, put merge contributors at the end of the report. */
skipFiles:
  if( flags & C_MERGE ){
    db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0" );

    while( db_step(&q)==SQLITE_ROW ){
      if( flags & C_COMMENT ){
        blob_append(report, "# ", 2);
      }
      if( flags & C_CLASSIFY ){
        const char *zClass;
        switch( db_column_int(&q, 1) ){
................................................................................
    }
    db_finalize(&q);
    blob_appendf(pOut, "\n");
  }
  free(zDate);

  db_prepare(&q,
    "SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash, merge"
    "  FROM vmerge"
    " WHERE (vmerge.id=-1 OR vmerge.id=-2)"

    " ORDER BY 1");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zCherrypickUuid = db_column_text(&q, 0);
    int mid = db_column_int(&q, 1);
    if( mid != vid ){
      blob_appendf(pOut, "Q %s\n", zCherrypickUuid);
    }
................................................................................
  if( zColor && zColor[0] ){
    /* One-time background color */
    blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
  }
  if( p->closeFlag ){
    blob_appendf(pOut, "T +closed *\n");
  }
  db_prepare(&q, "SELECT mhash,merge FROM vmerge"
                 " WHERE id %s ORDER BY 1",
                 p->integrateFlag ? "IN(0,-4)" : "=(-4)");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zIntegrateUuid = db_column_text(&q, 0);
    int rid = db_column_int(&q, 1);
    if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
        " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
................................................................................
                         dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){
    fossil_fatal("%s", g.zErrMsg);
  }
  assert( blob_is_reset(&manifest) );
  content_deltify(vid, &nvid, 1, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);


  db_prepare(&q, "SELECT mhash,merge FROM vmerge WHERE id=-4");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zIntegrateUuid = db_column_text(&q, 0);
    if( is_a_leaf(db_column_int(&q, 1)) ){
      fossil_print("Closed: %s\n", zIntegrateUuid);
    }else{
      fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid);
    }

Changes to src/db.c.

1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484

1485
1486












1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512





1513
1514
1515
1516
1517
1518
1519
....
1654
1655
1656
1657
1658
1659
1660
1661


1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
















1692
1693
1694
1695
1696
1697
1698
....
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892

/*
** If zDbName is a valid local database file, open it and return
** true.  If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
  i64 lsize;
  char *zVFileDef;

  if( file_access(zDbName, F_OK) ) return 0;
  lsize = file_size(zDbName, ExtFILE);
  if( lsize%1024!=0 || lsize<4096 ) return 0;
  db_open_or_attach(zDbName, "localdb");
  zVFileDef = db_text(0, "SELECT sql FROM localdb.sqlite_master"

                         " WHERE name=='vfile'");
  if( zVFileDef==0 ) return 0;













  /* If the "isexe" column is missing from the vfile table, then
  ** add it now.   This code added on 2010-03-06.  After all users have
  ** upgraded, this code can be safely deleted.
  */
  if( sqlite3_strglob("* isexe *", zVFileDef)!=0 ){
    db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
  }

  /* If "islink"/"isLink" columns are missing from tables, then
  ** add them now.   This code added on 2011-01-17 and 2011-08-27.
  ** After all users have upgraded, this code can be safely deleted.
  */
  if( sqlite3_strglob("* islink *", zVFileDef)!=0 ){
    db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
    if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){
      db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0");
    }
    if( db_local_table_exists_but_lacks_column("undo", "isLink") ){
      db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
    }
    if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
      db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
    }
  }
  fossil_free(zVFileDef);





  return 1;
}

/*
** Locate the root directory of the local repository tree.  The root
** directory is found by searching for a file named "_FOSSIL_" or ".fslckout"
** that contains a valid repository database.
................................................................................
  }

  /* Make a change to the CHECK constraint on the BLOB table for
  ** version 2.0 and later.
  */
  rebuild_schema_update_2_0();   /* Do the Fossil-2.0 schema updates */

  /* If the checkout database was opened first, then check to make


  ** sure that the repository database that was just opened has not
  ** be replaced by a clone of the same project, with different RID
  ** values.
  */
  if( g.localOpen && !db_fingerprint_ok() ){
    /* Uncomment the following when we are ready for automatic recovery: */
#if 0
    stash_rid_renumbering_event();
#else
    fossil_print(
      "Oops. It looks like the repository database file located at\n"
      "    \"%s\"\n", zDbName
    );
    fossil_print(
      "has been swapped with a clone that may have different\n"
      "integer keys for the various artifacts. As of 2019-01-11,\n"
      "we are working on enhancing Fossil to be able to deal with\n"
      "that automatically, but we are not there yet. Sorry.\n\n"
    );
    fossil_print(
      "As an interim workaround, try:\n"
      "  %s close --force\n"
      "  %s open \"%s\" --keep\n"
      "Noting that any STASH and UNDO information "
      "WILL BE IRREVOCABLY LOST.\n\n",
      g.argv[0],
      g.argv[0], zDbName
    );
    fossil_fatal("bad fingerprint");
#endif
















  }
}

/*
** Return true if there have been any changes to the repository
** database since it was opened.
**
................................................................................
  }

#if defined(_WIN32) || defined(__CYGWIN__)
# define LOCALDB_NAME "./_FOSSIL_"
#else
# define LOCALDB_NAME "./.fslckout"
#endif
  db_init_database(LOCALDB_NAME, zLocalSchema,
#ifdef FOSSIL_LOCAL_WAL
                   "COMMIT; PRAGMA journal_mode=WAL; BEGIN;",
#endif
                   (char*)0);
  db_delete_on_failure(LOCALDB_NAME);
  db_open_local(0);
  if( allowSymlinks>=0 ){







<





<
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>





|







|











<
>
>
>
>
>







 







|
>
>
|
|
|
|
|
|

|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

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







 







|







1471
1472
1473
1474
1475
1476
1477

1478
1479
1480
1481
1482

1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522

1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
....
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
....
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925

/*
** If zDbName is a valid local database file, open it and return
** true.  If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
  i64 lsize;


  if( file_access(zDbName, F_OK) ) return 0;
  lsize = file_size(zDbName, ExtFILE);
  if( lsize%1024!=0 || lsize<4096 ) return 0;
  db_open_or_attach(zDbName, "localdb");


  /* Check to see if the checkout database has the lastest schema changes.
  ** The most recent schema change (2019-01-19) is the addition of the
  ** vmerge.mhash field.  If the schema has that one column, assume
  ** everything else is up-to-date. 
  */
  if( db_table_has_column("localdb","vmerge","mhash") ){
    return 1;   /* This is a checkout database with the latest schema */
  }

  /* If there is no vfile table, then assume we have picked up something
  ** that is not even close to being a valid checkout database */
  if( !db_table_exists("localdb","vfile") ){
    return 0;  /* Not a  DB */
  }

  /* If the "isexe" column is missing from the vfile table, then
  ** add it now.   This code added on 2010-03-06.  After all users have
  ** upgraded, this code can be safely deleted.
  */
  if( !db_table_has_column("localdb","vfile","isexe") ){
    db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
  }

  /* If "islink"/"isLink" columns are missing from tables, then
  ** add them now.   This code added on 2011-01-17 and 2011-08-27.
  ** After all users have upgraded, this code can be safely deleted.
  */
  if( !db_table_has_column("localdb","vfile","isLink") ){
    db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
    if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){
      db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0");
    }
    if( db_local_table_exists_but_lacks_column("undo", "isLink") ){
      db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
    }
    if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
      db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
    }
  }


  /* The design of the vmerge table changed on 2019-01-19, adding the mhash
  ** column and changing the UNIQUE index.  However, we must ahve the
  ** repository database at hand in order to do the migration, so that
  ** step is deferred. */
  return 1;
}

/*
** Locate the root directory of the local repository tree.  The root
** directory is found by searching for a file named "_FOSSIL_" or ".fslckout"
** that contains a valid repository database.
................................................................................
  }

  /* Make a change to the CHECK constraint on the BLOB table for
  ** version 2.0 and later.
  */
  rebuild_schema_update_2_0();   /* Do the Fossil-2.0 schema updates */

  /* Additional checks that occur when opening the checkout database */
  if( g.localOpen ){

    /* Ensure that the repository database that was just opened has not
    ** be replaced by a clone of the same project, with different RID
    ** values.
    */
    if( !db_fingerprint_ok() ){
      /* Uncomment the following when we are ready for automatic recovery: */
#if 0
      stash_rid_renumbering_event();
#else
      fossil_print(
        "Oops. It looks like the repository database file located at\n"
        "    \"%s\"\n", zDbName
      );
      fossil_print(
        "has been swapped with a clone that may have different\n"
        "integer keys for the various artifacts. As of 2019-01-11,\n"
        "we are working on enhancing Fossil to be able to deal with\n"
        "that automatically, but we are not there yet. Sorry.\n\n"
      );
      fossil_print(
        "As an interim workaround, try:\n"
        "  %s close --force\n"
        "  %s open \"%s\" --keep\n"
        "Noting that any STASH and UNDO information "
        "WILL BE IRREVOCABLY LOST.\n\n",
        g.argv[0],
        g.argv[0], zDbName
      );
      fossil_fatal("bad fingerprint");
#endif
    }else

    /* Make sure the checkout database schema migration of 2019-01-19 
    ** (the addition of the vmerge.mhash column and making that columns
    ** part of the PRIMARY KEY) has occurred.
    */
    if( !db_table_has_column("localdb", "vmerge", "mhash") ){
      db_multi_exec("ALTER TABLE vmerge RENAME TO old_vmerge;");
      db_multi_exec(zLocalSchemaVmerge /*works-like:""*/);
      db_multi_exec(  
         "INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
         "  SELECT id, merge, blob.uuid FROM old_vmerge, blob"
         "   WHERE old_vmerge.merge=blob.rid;"
         "DROP TABLE old_vmerge;"
      );
    }
  }
}

/*
** Return true if there have been any changes to the repository
** database since it was opened.
**
................................................................................
  }

#if defined(_WIN32) || defined(__CYGWIN__)
# define LOCALDB_NAME "./_FOSSIL_"
#else
# define LOCALDB_NAME "./.fslckout"
#endif
  db_init_database(LOCALDB_NAME, zLocalSchema, zLocalSchemaVmerge,
#ifdef FOSSIL_LOCAL_WAL
                   "COMMIT; PRAGMA journal_mode=WAL; BEGIN;",
#endif
                   (char*)0);
  db_delete_on_failure(LOCALDB_NAME);
  db_open_local(0);
  if( allowSymlinks>=0 ){

Changes to src/json_status.c.

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  cson_object_set( oPay, "errorCount", json_new_int( nErr ) );
  db_finalize(&q);

#if 0
  /* TODO: add "merged with" status.  First need (A) to decide on a
     structure and (B) to set up some tests for the multi-merge
     case.*/
  db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id<=0");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zLabel = "MERGED_WITH";
    switch( db_column_int(&q, 1) ){
      case -1:  zLabel = "CHERRYPICK ";  break;
      case -2:  zLabel = "BACKOUT    ";  break;
      case -4:  zLabel = "INTEGRATE  ";  break;
    }







<
|







152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
  cson_object_set( oPay, "errorCount", json_new_int( nErr ) );
  db_finalize(&q);

#if 0
  /* TODO: add "merged with" status.  First need (A) to decide on a
     structure and (B) to set up some tests for the multi-merge
     case.*/

  db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zLabel = "MERGED_WITH";
    switch( db_column_int(&q, 1) ){
      case -1:  zLabel = "CHERRYPICK ";  break;
      case -2:  zLabel = "BACKOUT    ";  break;
      case -4:  zLabel = "INTEGRATE  ";  break;
    }

Changes to src/merge.c.

162
163
164
165
166
167
168










169
170
171
172
173
174
175
...
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
...
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
      );
    }
    free(zN);
    free(zV);
  }
  free(aChng);
}











/*
** COMMAND: merge
**
** Usage: %fossil merge ?OPTIONS? ?VERSION?
**
** The argument VERSION is a version that should be merged into the
................................................................................
        fossil_print("***** Cannot merge binary file %s\n", zName);
        nConflict++;
      }
      blob_reset(&p);
      blob_reset(&m);
      blob_reset(&r);
    }
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
                  idv,ridm);
  }
  db_finalize(&q);

  /*
  ** Drop files that are in P and V but not in M
  */
  db_prepare(&q,
................................................................................
  }

  /*
  ** Clean up the mid and pid VFILE entries.  Then commit the changes.
  */
  db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
  if( pickFlag ){
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-1,%d)",mid);
    /* For a cherry-pick merge, make the default check-in comment the same
    ** as the check-in comment on the check-in that is being merged in. */
    db_multi_exec(
       "REPLACE INTO vvar(name,value)"
       " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
       "  WHERE type='ci' AND objid=%d",
       mid
    );
  }else if( backoutFlag ){
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-2,%d)",pid);
  }else if( integrateFlag ){
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(-4,%d)",mid);
  }else{
    db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
  }
  if( !dryRunFlag ) undo_finish();
  db_end_transaction(dryRunFlag);
}







>
>
>
>
>
>
>
>
>
>







 







|
<







 







|









|

|

|




162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
672
673
674
675
676
677
678
679

680
681
682
683
684
685
686
...
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
      );
    }
    free(zN);
    free(zV);
  }
  free(aChng);
}

/* Make an entry in the vmerge table for the given id, and rid.
*/
static void vmerge_insert(int id, int rid){
  db_multi_exec(
    "INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
    "VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d))",
    id, rid, rid
  );
}

/*
** COMMAND: merge
**
** Usage: %fossil merge ?OPTIONS? ?VERSION?
**
** The argument VERSION is a version that should be merged into the
................................................................................
        fossil_print("***** Cannot merge binary file %s\n", zName);
        nConflict++;
      }
      blob_reset(&p);
      blob_reset(&m);
      blob_reset(&r);
    }
    vmerge_insert(idv, ridm);

  }
  db_finalize(&q);

  /*
  ** Drop files that are in P and V but not in M
  */
  db_prepare(&q,
................................................................................
  }

  /*
  ** Clean up the mid and pid VFILE entries.  Then commit the changes.
  */
  db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
  if( pickFlag ){
    vmerge_insert(-1, mid);
    /* For a cherry-pick merge, make the default check-in comment the same
    ** as the check-in comment on the check-in that is being merged in. */
    db_multi_exec(
       "REPLACE INTO vvar(name,value)"
       " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
       "  WHERE type='ci' AND objid=%d",
       mid
    );
  }else if( backoutFlag ){
    vmerge_insert(-2, pid);
  }else if( integrateFlag ){
    vmerge_insert(-4, mid);
  }else{
    vmerge_insert(0, mid);
  }
  if( !dryRunFlag ) undo_finish();
  db_end_transaction(dryRunFlag);
}

Changes to src/schema.c.

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
...
537
538
539
540
541
542
543










544
545
546
547
548
549
550

551
552
553
554
555
556

557
558
559
560










561
562
563
564
565
566
567
# define TAG_PARENT     10    /* Change to parentage on a check-in */
# define TAG_NOTE       11    /* Extra text appended to a check-in comment */
#endif

/*
** The schema for the local FOSSIL database file found at the root
** of every check-out.  This database contains the complete state of
** the checkout.
*/
const char zLocalSchema[] =
@ -- The VVAR table holds miscellanous information about the local database
@ -- in the form of name-value pairs.  This is similar to the VAR table
@ -- table in the repository except that this table holds information that
@ -- is specific to the local checkout.
@ --
................................................................................
@   mrid INTEGER,                     -- Based on this record due to a merge
@   mtime INTEGER,                    -- Mtime of file on disk. sec since 1970
@   pathname TEXT,                    -- Full pathname relative to root
@   origname TEXT,                    -- Original pathname. NULL if unchanged
@   UNIQUE(pathname,vid)
@ );
@










@ -- This table holds a record of uncommitted merges in the local
@ -- file tree.  If a VFILE entry with id has merged with another
@ -- record, there is an entry in this table with (id,merge) where
@ -- merge is the RECORD table entry that the file merged against.
@ -- An id of 0 or <-3 here means the version record itself.  When
@ -- id==(-1) that is a cherrypick merge, id==(-2) that is a
@ -- backout merge and id==(-4) is a integrate merge.

@
@ CREATE TABLE vmerge(
@   id INTEGER REFERENCES vfile,      -- VFILE entry that has been merged
@   merge INTEGER,                    -- Merged with this record
@   UNIQUE(id, merge)
@ );

@
@ -- Identifier for this file type.
@ -- The integer is the same as 'FSLC'.
@ PRAGMA application_id=252006674;










;

/*
** The following table holds information about forum posts.  It
** is created on-demand whenever the manifest parser encounters
** a forum-post artifact.
*/







|







 







>
>
>
>
>
>
>
>
>
>







>




|

>

<
|
|
>
>
>
>
>
>
>
>
>
>







488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
...
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569

570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# define TAG_PARENT     10    /* Change to parentage on a check-in */
# define TAG_NOTE       11    /* Extra text appended to a check-in comment */
#endif

/*
** The schema for the local FOSSIL database file found at the root
** of every check-out.  This database contains the complete state of
** the checkout.  See also the addendum in zLocalSchemaVmerge[].
*/
const char zLocalSchema[] =
@ -- The VVAR table holds miscellanous information about the local database
@ -- in the form of name-value pairs.  This is similar to the VAR table
@ -- table in the repository except that this table holds information that
@ -- is specific to the local checkout.
@ --
................................................................................
@   mrid INTEGER,                     -- Based on this record due to a merge
@   mtime INTEGER,                    -- Mtime of file on disk. sec since 1970
@   pathname TEXT,                    -- Full pathname relative to root
@   origname TEXT,                    -- Original pathname. NULL if unchanged
@   UNIQUE(pathname,vid)
@ );
@
@ -- Identifier for this file type.
@ -- The integer is the same as 'FSLC'.
@ PRAGMA application_id=252006674;
;

/* Additional local database initialization following the schema
** enhancement of 2019-01-19, in which the mhash column was added
** to vmerge.
*/
const char zLocalSchemaVmerge[] =
@ -- This table holds a record of uncommitted merges in the local
@ -- file tree.  If a VFILE entry with id has merged with another
@ -- record, there is an entry in this table with (id,merge) where
@ -- merge is the RECORD table entry that the file merged against.
@ -- An id of 0 or <-3 here means the version record itself.  When
@ -- id==(-1) that is a cherrypick merge, id==(-2) that is a
@ -- backout merge and id==(-4) is a integrate merge.
@ --
@
@ CREATE TABLE vmerge(
@   id INTEGER REFERENCES vfile,      -- VFILE entry that has been merged
@   merge INTEGER,                    -- Merged with this record
@   mhash TEXT                        -- SHA1/SHA3 hash for merge object
@ );
@ CREATE UNIQUE INDEX vmergex1 ON vmerge(id,mhash);
@

@ -- The following trigger will prevent older versions of Fossil that
@ -- do not know about the new vmerge.mhash column from updating the
@ -- vmerge table.  This must be done with a trigger, since legacy Fossil
@ -- uses INSERT OR IGNORE to update vmerge, and the OR IGNORE will cause
@ -- a NOT NULL constraint to be silently ignored.
@
@ CREATE TRIGGER vmerge_ck1 AFTER INSERT ON vmerge
@ WHEN new.mhash IS NULL BEGIN
@   SELECT raise(FAIL,
@   'trying to update a newer checkout with an older version of Fossil');
@ END;
@
;

/*
** The following table holds information about forum posts.  It
** is created on-demand whenever the manifest parser encounters
** a forum-post artifact.
*/

Changes to src/update.c.

527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  }

  /* Report on conflicts
  */
  if( !dryRunFlag ){
    Stmt q;
    int nMerge = 0;
    db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
                   " WHERE id<=0");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zLabel = "merge";
      switch( db_column_int(&q, 1) ){
        case -1:  zLabel = "cherrypick merge"; break;
        case -2:  zLabel = "backout merge";    break;
      }
      fossil_warning("uncommitted %s against %S.",







<
|







527
528
529
530
531
532
533

534
535
536
537
538
539
540
541
  }

  /* Report on conflicts
  */
  if( !dryRunFlag ){
    Stmt q;
    int nMerge = 0;

    db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zLabel = "merge";
      switch( db_column_int(&q, 1) ){
        case -1:  zLabel = "cherrypick merge"; break;
        case -2:  zLabel = "backout merge";    break;
      }
      fossil_warning("uncommitted %s against %S.",