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 the changes in the current dbda8d6ce9 2007-07-21 drh: ** checkout into a different version and switch to that version. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #include "config.h" dbda8d6ce9 2007-07-21 drh: #include "update.h" dbda8d6ce9 2007-07-21 drh: #include <assert.h> dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: update dbda8d6ce9 2007-07-21 drh: ** 6607844a01 2007-08-18 drh: ** Usage: %fossil update ?VERSION? 6607844a01 2007-08-18 drh: ** dbda8d6ce9 2007-07-21 drh: ** The optional argument is a version that should become the current dbda8d6ce9 2007-07-21 drh: ** version. If the argument is omitted, then use the leaf of the 6607844a01 2007-08-18 drh: ** tree that begins with the current version, if there is only a 6607844a01 2007-08-18 drh: ** single leaf. 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** This command is different from the "checkout" in that edits are 6607844a01 2007-08-18 drh: ** not overwritten. Edits are merged into the new version. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void update_cmd(void){ dbda8d6ce9 2007-07-21 drh: int vid; /* Current version */ dbda8d6ce9 2007-07-21 drh: int tid; /* Target version - version we are changing to */ dbda8d6ce9 2007-07-21 drh: Stmt q; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( g.argc!=3 && g.argc!=2 ){ 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: vid = 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( db_exists("SELECT 1 FROM vmerge") ){ dbda8d6ce9 2007-07-21 drh: fossil_fatal("cannot update an uncommitted merge"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( g.argc==3 ){ dbda8d6ce9 2007-07-21 drh: tid = name_to_rid(g.argv[2]); dbda8d6ce9 2007-07-21 drh: if( tid==0 ){ dbda8d6ce9 2007-07-21 drh: fossil_fatal("not a version: %s", g.argv[2]); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", tid) ){ dbda8d6ce9 2007-07-21 drh: fossil_fatal("not a version: %s", g.argv[2]); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: compute_leaves(vid); dbda8d6ce9 2007-07-21 drh: if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ dbda8d6ce9 2007-07-21 drh: fossil_fatal("multiple descendents"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: tid = db_int(0, "SELECT rid FROM leaves"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: db_begin_transaction(); dbda8d6ce9 2007-07-21 drh: vfile_check_signature(vid); a36177bcce 2007-09-11 drh: undo_begin(); dbda8d6ce9 2007-07-21 drh: load_vfile_from_rid(tid); dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** The record.fn 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: " idt INTEGER," /* VFILE entry for target version */ 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: " ridt INTEGER " /* Record ID for target */ 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 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", tid 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 idt=%d, ridt=%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: db_prepare(&q, dbda8d6ce9 2007-07-21 drh: "SELECT fn, idv, ridv, idt, ridt, chnged FROM fv ORDER BY 1" dbda8d6ce9 2007-07-21 drh: ); dbda8d6ce9 2007-07-21 drh: while( db_step(&q)==SQLITE_ROW ){ dbda8d6ce9 2007-07-21 drh: const char *zName = db_column_text(&q, 0); dbda8d6ce9 2007-07-21 drh: int idv = db_column_int(&q, 1); dbda8d6ce9 2007-07-21 drh: int ridv = db_column_int(&q, 2); dbda8d6ce9 2007-07-21 drh: int idt = db_column_int(&q, 3); dbda8d6ce9 2007-07-21 drh: int ridt = db_column_int(&q, 4); dbda8d6ce9 2007-07-21 drh: int chnged = db_column_int(&q, 5); dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( idv>0 && ridv==0 && idt>0 ){ dbda8d6ce9 2007-07-21 drh: /* Conflict. This file has been added to the current checkout dbda8d6ce9 2007-07-21 drh: ** but also exists in the target checkout. Use the current version. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: printf("CONFLICT %s\n", zName); dbda8d6ce9 2007-07-21 drh: }else if( idt>0 && idv==0 ){ dbda8d6ce9 2007-07-21 drh: /* File added in the target. */ dbda8d6ce9 2007-07-21 drh: printf("ADD %s\n", zName); a36177bcce 2007-09-11 drh: undo_save(zName); dbda8d6ce9 2007-07-21 drh: vfile_to_disk(0, idt, 0); dbda8d6ce9 2007-07-21 drh: }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){ dbda8d6ce9 2007-07-21 drh: /* The file is unedited. Change it to the target version */ dbda8d6ce9 2007-07-21 drh: printf("UPDATE %s\n", zName); a36177bcce 2007-09-11 drh: undo_save(zName); dbda8d6ce9 2007-07-21 drh: vfile_to_disk(0, idt, 0); dbda8d6ce9 2007-07-21 drh: }else if( idt==0 && idv>0 ){ fe6ee8a431 2007-08-08 drh: if( chnged ){ fe6ee8a431 2007-08-08 drh: printf("CONFLICT %s\n", zName); fe6ee8a431 2007-08-08 drh: }else{ fe6ee8a431 2007-08-08 drh: char *zFullPath; fe6ee8a431 2007-08-08 drh: printf("REMOVE %s\n", zName); a36177bcce 2007-09-11 drh: undo_save(zName); fe6ee8a431 2007-08-08 drh: zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); fe6ee8a431 2007-08-08 drh: unlink(zFullPath); fe6ee8a431 2007-08-08 drh: free(zFullPath); fe6ee8a431 2007-08-08 drh: } dbda8d6ce9 2007-07-21 drh: }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){ dbda8d6ce9 2007-07-21 drh: /* Merge the changes in the current tree into the target version */ dbda8d6ce9 2007-07-21 drh: Blob e, r, t, v; dbda8d6ce9 2007-07-21 drh: char *zFullPath; dbda8d6ce9 2007-07-21 drh: printf("MERGE %s\n", zName); a36177bcce 2007-09-11 drh: undo_save(zName); dbda8d6ce9 2007-07-21 drh: zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); dbda8d6ce9 2007-07-21 drh: content_get(ridt, &t); dbda8d6ce9 2007-07-21 drh: content_get(ridv, &v); dbda8d6ce9 2007-07-21 drh: blob_zero(&e); dbda8d6ce9 2007-07-21 drh: blob_read_from_file(&e, zFullPath); dbda8d6ce9 2007-07-21 drh: blob_merge(&v, &e, &t, &r); dbda8d6ce9 2007-07-21 drh: blob_write_to_file(&r, zFullPath); dbda8d6ce9 2007-07-21 drh: free(zFullPath); dbda8d6ce9 2007-07-21 drh: blob_reset(&v); dbda8d6ce9 2007-07-21 drh: blob_reset(&e); dbda8d6ce9 2007-07-21 drh: blob_reset(&t); dbda8d6ce9 2007-07-21 drh: blob_reset(&r); 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: ** 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", tid); a040ae6e17 2007-08-08 drh: manifest_to_disk(tid); dbda8d6ce9 2007-07-21 drh: db_lset_int("checkout", tid); dbda8d6ce9 2007-07-21 drh: db_end_transaction(0); dbda8d6ce9 2007-07-21 drh: }