File Annotation
Not logged in
dbda8d6ce9 2007-07-21       drh: /*
dbda8d6ce9 2007-07-21       drh: ** Copyright (c) 2007 D. Richard Hipp
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** This program is free software; you can redistribute it and/or
dbda8d6ce9 2007-07-21       drh: ** modify it under the terms of the GNU General Public
dbda8d6ce9 2007-07-21       drh: ** License version 2 as published by the Free Software Foundation.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** This program is distributed in the hope that it will be useful,
dbda8d6ce9 2007-07-21       drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of
dbda8d6ce9 2007-07-21       drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
dbda8d6ce9 2007-07-21       drh: ** General Public License for more details.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** You should have received a copy of the GNU General Public
dbda8d6ce9 2007-07-21       drh: ** License along with this library; if not, write to the
dbda8d6ce9 2007-07-21       drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
dbda8d6ce9 2007-07-21       drh: ** Boston, MA  02111-1307, USA.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** Author contact information:
dbda8d6ce9 2007-07-21       drh: **   drh@hwaci.com
dbda8d6ce9 2007-07-21       drh: **   http://www.hwaci.com/drh/
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: *******************************************************************************
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** This file contains code used to merge two or more branches into
dbda8d6ce9 2007-07-21       drh: ** a single tree.
dbda8d6ce9 2007-07-21       drh: */
dbda8d6ce9 2007-07-21       drh: #include "config.h"
dbda8d6ce9 2007-07-21       drh: #include "merge.h"
dbda8d6ce9 2007-07-21       drh: #include <assert.h>
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh: /*
dbda8d6ce9 2007-07-21       drh: ** COMMAND: merge
dbda8d6ce9 2007-07-21       drh: **
6607844a01 2007-08-18       drh: ** Usage: %fossil merge VERSION
6607844a01 2007-08-18       drh: **
dbda8d6ce9 2007-07-21       drh: ** The argument is a version that should be merged into the current
dbda8d6ce9 2007-07-21       drh: ** checkout.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** Only file content is merged.  The result continues to use the
dbda8d6ce9 2007-07-21       drh: ** file and directory names from the current check-out even if those
dbda8d6ce9 2007-07-21       drh: ** names might have been changed in the branch being merged in.
dbda8d6ce9 2007-07-21       drh: */
dbda8d6ce9 2007-07-21       drh: void merge_cmd(void){
dbda8d6ce9 2007-07-21       drh:   int vid;              /* Current version */
dbda8d6ce9 2007-07-21       drh:   int mid;              /* Version we are merging against */
dbda8d6ce9 2007-07-21       drh:   int pid;              /* The pivot version - most recent common ancestor */
dbda8d6ce9 2007-07-21       drh:   Stmt q;
50ff86afd0 2007-11-08       drh:   int detailFlag;
dbda8d6ce9 2007-07-21       drh: 
50ff86afd0 2007-11-08       drh:   detailFlag = find_option("detail",0,0)!=0;
dbda8d6ce9 2007-07-21       drh:   if( g.argc!=3 ){
dbda8d6ce9 2007-07-21       drh:     usage("VERSION");
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_must_be_within_tree();
dbda8d6ce9 2007-07-21       drh:   vid = db_lget_int("checkout", 0);
dbda8d6ce9 2007-07-21       drh:   if( vid==0 ){
dbda8d6ce9 2007-07-21       drh:     fossil_panic("nothing is checked out");
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   mid = name_to_rid(g.argv[2]);
dbda8d6ce9 2007-07-21       drh:   if( mid==0 ){
dbda8d6ce9 2007-07-21       drh:     fossil_panic("not a version: %s", g.argv[2]);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){
dbda8d6ce9 2007-07-21       drh:     fossil_panic("not a version: %s", g.argv[2]);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   pivot_set_primary(mid);
dbda8d6ce9 2007-07-21       drh:   pivot_set_secondary(vid);
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     pivot_set_secondary(db_column_int(&q,0));
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh:   pid = pivot_find();
dbda8d6ce9 2007-07-21       drh:   if( pid<=0 ){
dbda8d6ce9 2007-07-21       drh:     fossil_panic("cannot find a common ancestor between the current"
dbda8d6ce9 2007-07-21       drh:                  "checkout and %s", g.argv[2]);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   if( pid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", pid) ){
dbda8d6ce9 2007-07-21       drh:     fossil_panic("not a version: record #%d", mid);
dbda8d6ce9 2007-07-21       drh:   }
76f169fca6 2009-12-18       drh:   vfile_check_signature(vid, 1);
dbda8d6ce9 2007-07-21       drh:   db_begin_transaction();
a36177bcce 2007-09-11       drh:   undo_begin();
dbda8d6ce9 2007-07-21       drh:   load_vfile_from_rid(mid);
dbda8d6ce9 2007-07-21       drh:   load_vfile_from_rid(pid);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** The vfile.pathname field is used to match files against each other.  The
dbda8d6ce9 2007-07-21       drh:   ** FV table contains one row for each each unique filename in
dbda8d6ce9 2007-07-21       drh:   ** in the current checkout, the pivot, and the version being merged.
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_multi_exec(
dbda8d6ce9 2007-07-21       drh:     "DROP TABLE IF EXISTS fv;"
dbda8d6ce9 2007-07-21       drh:     "CREATE TEMP TABLE fv("
dbda8d6ce9 2007-07-21       drh:     "  fn TEXT PRIMARY KEY,"      /* The filename */
dbda8d6ce9 2007-07-21       drh:     "  idv INTEGER,"              /* VFILE entry for current version */
dbda8d6ce9 2007-07-21       drh:     "  idp INTEGER,"              /* VFILE entry for the pivot */
dbda8d6ce9 2007-07-21       drh:     "  idm INTEGER,"              /* VFILE entry for version merging in */
dbda8d6ce9 2007-07-21       drh:     "  chnged BOOLEAN,"           /* True if current version has been edited */
dbda8d6ce9 2007-07-21       drh:     "  ridv INTEGER,"             /* Record ID for current version */
dbda8d6ce9 2007-07-21       drh:     "  ridp INTEGER,"             /* Record ID for pivot */
dbda8d6ce9 2007-07-21       drh:     "  ridm INTEGER"              /* Record ID for merge */
dbda8d6ce9 2007-07-21       drh:     ");"
dbda8d6ce9 2007-07-21       drh:     "INSERT OR IGNORE INTO fv"
dbda8d6ce9 2007-07-21       drh:     " SELECT pathname, 0, 0, 0, 0, 0, 0, 0 FROM vfile"
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT id, pathname, rid FROM vfile"
dbda8d6ce9 2007-07-21       drh:     " WHERE vid=%d", pid
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int id = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     const char *fn = db_column_text(&q, 1);
dbda8d6ce9 2007-07-21       drh:     int rid = db_column_int(&q, 2);
dbda8d6ce9 2007-07-21       drh:     db_multi_exec(
dbda8d6ce9 2007-07-21       drh:       "UPDATE fv SET idp=%d, ridp=%d WHERE fn=%Q",
dbda8d6ce9 2007-07-21       drh:       id, rid, fn
dbda8d6ce9 2007-07-21       drh:     );
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT id, pathname, rid FROM vfile"
dbda8d6ce9 2007-07-21       drh:     " WHERE vid=%d", mid
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int id = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     const char *fn = db_column_text(&q, 1);
dbda8d6ce9 2007-07-21       drh:     int rid = db_column_int(&q, 2);
dbda8d6ce9 2007-07-21       drh:     db_multi_exec(
dbda8d6ce9 2007-07-21       drh:       "UPDATE fv SET idm=%d, ridm=%d WHERE fn=%Q",
dbda8d6ce9 2007-07-21       drh:       id, rid, fn
dbda8d6ce9 2007-07-21       drh:     );
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT id, pathname, rid, chnged FROM vfile"
dbda8d6ce9 2007-07-21       drh:     " WHERE vid=%d", vid
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int id = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     const char *fn = db_column_text(&q, 1);
dbda8d6ce9 2007-07-21       drh:     int rid = db_column_int(&q, 2);
dbda8d6ce9 2007-07-21       drh:     int chnged = db_column_int(&q, 3);
dbda8d6ce9 2007-07-21       drh:     db_multi_exec(
dbda8d6ce9 2007-07-21       drh:       "UPDATE fv SET idv=%d, ridv=%d, chnged=%d WHERE fn=%Q",
dbda8d6ce9 2007-07-21       drh:       id, rid, chnged, fn
dbda8d6ce9 2007-07-21       drh:     );
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** Find files in mid and vid but not in pid and report conflicts.
dbda8d6ce9 2007-07-21       drh:   ** The file in mid will be ignored.  It will be treated as if it
dbda8d6ce9 2007-07-21       drh:   ** does not exist.
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT idm FROM fv WHERE idp=0 AND idv>0 AND idm>0"
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int idm = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idm);
dbda8d6ce9 2007-07-21       drh:     printf("WARNING: conflict on %s\n", zName);
dbda8d6ce9 2007-07-21       drh:     free(zName);
dbda8d6ce9 2007-07-21       drh:     db_multi_exec("UPDATE fv SET idm=0 WHERE idm=%d", idm);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** Add to vid files that are not in pid but are in mid
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT idm, rowid, fn FROM fv WHERE idp=0 AND idv=0 AND idm>0"
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int idm = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     int rowid = db_column_int(&q, 1);
dbda8d6ce9 2007-07-21       drh:     int idv;
a36177bcce 2007-09-11       drh:     const char *zName;
dbda8d6ce9 2007-07-21       drh:     db_multi_exec(
dbda8d6ce9 2007-07-21       drh:       "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,pathname)"
dbda8d6ce9 2007-07-21       drh:       "  SELECT %d,3,0,rid,mrid,pathname FROM vfile WHERE id=%d",
dbda8d6ce9 2007-07-21       drh:       vid, idm
dbda8d6ce9 2007-07-21       drh:     );
dbda8d6ce9 2007-07-21       drh:     idv = db_last_insert_rowid();
dbda8d6ce9 2007-07-21       drh:     db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
a36177bcce 2007-09-11       drh:     zName = db_column_text(&q, 2);
a36177bcce 2007-09-11       drh:     printf("ADDED %s\n", zName);
a36177bcce 2007-09-11       drh:     undo_save(zName);
dbda8d6ce9 2007-07-21       drh:     vfile_to_disk(0, idm, 0);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** Find files that have changed from pid->mid but not pid->vid.
dbda8d6ce9 2007-07-21       drh:   ** Copy the mid content over into vid.
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT idv, ridm FROM fv"
dbda8d6ce9 2007-07-21       drh:     " WHERE idp>0 AND idv>0 AND idm>0"
dbda8d6ce9 2007-07-21       drh:     "   AND ridm!=ridp AND ridv=ridp AND NOT chnged"
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int idv = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     int ridm = db_column_int(&q, 1);
dbda8d6ce9 2007-07-21       drh:     char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
dbda8d6ce9 2007-07-21       drh:     /* Copy content from idm over into idv.  Overwrite idv. */
dbda8d6ce9 2007-07-21       drh:     printf("UPDATE %s\n", zName);
a36177bcce 2007-09-11       drh:     undo_save(zName);
dbda8d6ce9 2007-07-21       drh:     db_multi_exec(
dbda8d6ce9 2007-07-21       drh:       "UPDATE vfile SET mrid=%d, chnged=2 WHERE id=%d", ridm, idv
dbda8d6ce9 2007-07-21       drh:     );
dbda8d6ce9 2007-07-21       drh:     vfile_to_disk(0, idv, 0);
dbda8d6ce9 2007-07-21       drh:     free(zName);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** Do a three-way merge on files that have changes pid->mid and pid->vid
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
50ff86afd0 2007-11-08       drh:     "SELECT ridm, idv, ridp, ridv FROM fv"
dbda8d6ce9 2007-07-21       drh:     " WHERE idp>0 AND idv>0 AND idm>0"
dbda8d6ce9 2007-07-21       drh:     "   AND ridm!=ridp AND (ridv!=ridp OR chnged)"
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int ridm = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     int idv = db_column_int(&q, 1);
dbda8d6ce9 2007-07-21       drh:     int ridp = db_column_int(&q, 2);
50ff86afd0 2007-11-08       drh:     int ridv = db_column_int(&q, 3);
36b96b8616 2007-11-16       drh:     int rc;
dbda8d6ce9 2007-07-21       drh:     char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
dbda8d6ce9 2007-07-21       drh:     char *zFullPath;
dbda8d6ce9 2007-07-21       drh:     Blob m, p, v, r;
dbda8d6ce9 2007-07-21       drh:     /* Do a 3-way merge of idp->idm into idp->idv.  The results go into idv. */
50ff86afd0 2007-11-08       drh:     if( detailFlag ){
50ff86afd0 2007-11-08       drh:       printf("MERGE %s  (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv);
50ff86afd0 2007-11-08       drh:     }else{
50ff86afd0 2007-11-08       drh:       printf("MERGE %s\n", zName);
50ff86afd0 2007-11-08       drh:     }
a36177bcce 2007-09-11       drh:     undo_save(zName);
a36177bcce 2007-09-11       drh:     zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
dbda8d6ce9 2007-07-21       drh:     content_get(ridp, &p);
dbda8d6ce9 2007-07-21       drh:     content_get(ridm, &m);
dbda8d6ce9 2007-07-21       drh:     blob_zero(&v);
dbda8d6ce9 2007-07-21       drh:     blob_read_from_file(&v, zFullPath);
36b96b8616 2007-11-16       drh:     rc = blob_merge(&p, &m, &v, &r);
36b96b8616 2007-11-16       drh:     if( rc>=0 ){
36b96b8616 2007-11-16       drh:       blob_write_to_file(&r, zFullPath);
36b96b8616 2007-11-16       drh:       if( rc>0 ){
36b96b8616 2007-11-16       drh:         printf("***** %d merge conflicts in %s\n", rc, zName);
36b96b8616 2007-11-16       drh:       }
36b96b8616 2007-11-16       drh:     }else{
36b96b8616 2007-11-16       drh:       printf("***** Cannot merge binary file %s\n", zName);
36b96b8616 2007-11-16       drh:     }
36b96b8616 2007-11-16       drh:     free(zName);
dbda8d6ce9 2007-07-21       drh:     blob_reset(&p);
dbda8d6ce9 2007-07-21       drh:     blob_reset(&m);
dbda8d6ce9 2007-07-21       drh:     blob_reset(&v);
dbda8d6ce9 2007-07-21       drh:     blob_reset(&r);
4c82c7773f 2007-08-30       drh:     db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
4c82c7773f 2007-08-30       drh:                   idv,ridm);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** Drop files from vid that are in pid but not in mid
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_prepare(&q,
dbda8d6ce9 2007-07-21       drh:     "SELECT idv FROM fv"
dbda8d6ce9 2007-07-21       drh:     " WHERE idp>0 AND idv>0 AND idm=0"
dbda8d6ce9 2007-07-21       drh:   );
dbda8d6ce9 2007-07-21       drh:   while( db_step(&q)==SQLITE_ROW ){
dbda8d6ce9 2007-07-21       drh:     int idv = db_column_int(&q, 0);
dbda8d6ce9 2007-07-21       drh:     char *zName = db_text(0, "SELECT pathname FROM vfile WHERE id=%d", idv);
dbda8d6ce9 2007-07-21       drh:     /* Delete the file idv */
dbda8d6ce9 2007-07-21       drh:     printf("DELETE %s\n", zName);
a36177bcce 2007-09-11       drh:     undo_save(zName);
dbda8d6ce9 2007-07-21       drh:     db_multi_exec(
dbda8d6ce9 2007-07-21       drh:       "UPDATE vfile SET deleted=1 WHERE id=%d", idv
dbda8d6ce9 2007-07-21       drh:     );
dbda8d6ce9 2007-07-21       drh:     free(zName);
dbda8d6ce9 2007-07-21       drh:   }
dbda8d6ce9 2007-07-21       drh:   db_finalize(&q);
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh:   /*
dbda8d6ce9 2007-07-21       drh:   ** Clean up the mid and pid VFILE entries.  Then commit the changes.
dbda8d6ce9 2007-07-21       drh:   */
dbda8d6ce9 2007-07-21       drh:   db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
4c82c7773f 2007-08-30       drh:   db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(0,%d)", mid);
dbda8d6ce9 2007-07-21       drh:   db_end_transaction(0);
dbda8d6ce9 2007-07-21       drh: }