Diff
Not logged in

Differences From:

File src/update.c part of check-in [7b82a73bd3] - Remove the --yes option from the "revert" command. In its place, make the "revert" opration undoable. by drh on 2009-12-17 21:22:52. [view]

To:

File src/update.c part of check-in [2d996b080e] - Enhancements to the "update" command. Missing files are reverted. One or more files can be specified on the "update" command line after the VERSION and only the files named will be updated. by drh on 2009-12-17 22:55:26. [view]

@@ -37,18 +37,22 @@
 
 /*
 ** COMMAND: update
 **
-** Usage: %fossil update ?VERSION? ?OPTIONS?
+** Usage: %fossil update ?VERSION? ?FILES...?
 **
 ** Change the version of the current checkout to VERSION.  Any uncommitted
 ** changes are retained and applied to the new checkout.
 **
 ** The VERSION argument can be a specific version or tag or branch name.
 ** If the VERSION argument is omitted, then the leaf of the the subtree
 ** that begins at the current version is used, if there is only a single
-** leaf.  Instead of specifying VERSION, use the --latest option to update
-** to the most recent check-in.
+** leaf.  VERSION can also be "current" to select the leaf of the current
+** version or "latest" to select the most recent check-in.
+**
+** If one or more FILES are listed after the VERSION then only the
+** named files are candidates to be updated.  If FILES is omitted, all
+** files in the current checkout are subject to be updated.
 **
 ** The -n or --nochange option causes this command to do a "dry run".  It
 ** prints out what would have happened but does not actually make any
 ** changes to the current checkout or the repository.
@@ -67,11 +71,8 @@
   url_proxy_options();
   latestFlag = find_option("latest",0, 0)!=0;
   nochangeFlag = find_option("nochange","n",0)!=0;
   verboseFlag = find_option("verbose","v",0)!=0;
-  if( g.argc!=3 && g.argc!=2 ){
-    usage("?VERSION?");
-  }
   db_must_be_within_tree();
   vid = db_lget_int("checkout", 0);
   if( vid==0 ){
     fossil_fatal("cannot find current version");
@@ -79,15 +80,24 @@
   if( db_exists("SELECT 1 FROM vmerge") ){
     fossil_fatal("cannot update an uncommitted merge");
   }
 
-  if( g.argc==3 ){
-    tid = name_to_rid(g.argv[2]);
-    if( tid==0 ){
-      fossil_fatal("not a version: %s", g.argv[2]);
-    }
-    if( !is_a_version(tid) ){
-      fossil_fatal("not a version: %s", g.argv[2]);
+  if( g.argc>=3 ){
+    if( strcmp(g.argv[2], "current")==0 ){
+      /* If VERSION is "current", then use the same algorithm to find the
+      ** target as if VERSION were omitted. */
+    }else if( strcmp(g.argv[2], "latest")==0 ){
+      /* If VERSION is "latest", then use the same algorithm to find the
+      ** target as if VERSION were omitted and the --latest flag is present.
+      */
+      latestFlag = 1;
+    }else{
+      tid = name_to_rid(g.argv[2]);
+      if( tid==0 ){
+        fossil_fatal("no such version: %s", g.argv[2]);
+      }else if( !is_a_version(tid) ){
+        fossil_fatal("no such version: %s", g.argv[2]);
+      }
     }
   }
   if( !nochangeFlag ) autosync(AUTOSYNC_PULL);
 
@@ -121,9 +131,9 @@
   */
   db_multi_exec(
     "DROP TABLE IF EXISTS fv;"
     "CREATE TEMP TABLE fv("
-    "  fn TEXT PRIMARY KEY,"      /* The filename */
+    "  fn TEXT PRIMARY KEY,"      /* The filename relative to root */
     "  idv INTEGER,"              /* VFILE entry for current version */
     "  idt INTEGER,"              /* VFILE entry for target version */
     "  chnged BOOLEAN,"           /* True if current version has been edited */
     "  ridv INTEGER,"             /* Record ID for current version */
@@ -161,19 +171,43 @@
     );
   }
   db_finalize(&q);
 
+  /* If FILES appear on the command-line, remove from the "fv" table
+  ** every entry that is not named on the command-line.
+  */
+  if( g.argc>=4 ){
+    Blob sql;              /* SQL statement to purge unwanted entries */
+    char *zSep = "(";      /* Separator in the list of filenames */
+    Blob treename;         /* Normalized filename */
+    int i;                 /* Loop counter */
+
+    blob_zero(&sql);
+    blob_append(&sql, "DELETE FROM fv WHERE fn NOT IN ", -1);
+    for(i=3; i<g.argc; i++){
+      file_tree_name(g.argv[i], &treename, 1);
+      blob_appendf(&sql, "%s'%q'", zSep, blob_str(&treename));
+      blob_reset(&treename);
+      zSep = ",";
+    }
+    blob_append(&sql, ")", -1);
+    db_multi_exec(blob_str(&sql));
+    blob_reset(&sql);
+  }
+
   db_prepare(&q,
     "SELECT fn, idv, ridv, idt, ridt, chnged FROM fv ORDER BY 1"
   );
   while( db_step(&q)==SQLITE_ROW ){
-    const char *zName = db_column_text(&q, 0);  /* The filename */
+    const char *zName = db_column_text(&q, 0);  /* The filename from root */
     int idv = db_column_int(&q, 1);             /* VFILE entry for current */
     int ridv = db_column_int(&q, 2);            /* RecordID for current */
     int idt = db_column_int(&q, 3);             /* VFILE entry for target */
     int ridt = db_column_int(&q, 4);            /* RecordID for target */
     int chnged = db_column_int(&q, 5);          /* Current is edited */
-
+    char *zFullPath;                            /* Full pathname of the file */
+
+    zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
     if( idv>0 && ridv==0 && idt>0 ){
       /* Conflict.  This file has been added to the current checkout
       ** but also exists in the target checkout.  Use the current version.
       */
@@ -187,14 +221,21 @@
       /* The file is unedited.  Change it to the target version */
       printf("UPDATE %s\n", zName);
       undo_save(zName);
       if( !nochangeFlag ) vfile_to_disk(0, idt, 0);
+    }else if( idt>0 && idv>0 && file_size(zFullPath)<0 ){
+      /* The file missing from the local check-out. Restore it to the
+      ** version that appears in the target. */
+      printf("UPDATE %s\n", zName);
+      undo_save(zName);
+      if( !nochangeFlag ) vfile_to_disk(0, idt, 0);
     }else if( idt==0 && idv>0 ){
       if( ridv==0 ){
         /* Added in current checkout.  Continue to hold the file as
         ** as an addition */
         db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
       }else if( chnged ){
+        /* Edited locally but deleted from the target.  Delete it. */
         printf("CONFLICT %s\n", zName);
       }else{
         char *zFullPath;
         printf("REMOVE %s\n", zName);
@@ -206,12 +247,10 @@
     }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
       /* Merge the changes in the current tree into the target version */
       Blob e, r, t, v;
       int rc;
-      char *zFullPath;
       printf("MERGE %s\n", zName);
       undo_save(zName);
-      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
       content_get(ridt, &t);
       content_get(ridv, &v);
       blob_zero(&e);
       blob_read_from_file(&e, zFullPath);
@@ -223,16 +262,16 @@
         }
       }else{
         printf("***** Cannot merge binary file %s\n", zName);
       }
-      free(zFullPath);
       blob_reset(&v);
       blob_reset(&e);
       blob_reset(&t);
       blob_reset(&r);
     }else if( verboseFlag ){
       printf("UNCHANGED %s\n", zName);
     }
+    free(zFullPath);
   }
   db_finalize(&q);
 
   /*
@@ -240,11 +279,18 @@
   */
   if( nochangeFlag ){
     db_end_transaction(1);  /* With --nochange, rollback changes */
   }else{
-    db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
-    manifest_to_disk(tid);
-    db_lset_int("checkout", tid);
+    if( g.argc<=3 ){
+      /* All files updated.  Shift the current checkout to the target. */
+      db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
+      manifest_to_disk(tid);
+      db_lset_int("checkout", tid);
+    }else{
+      /* A subset of files have been checked out.  Keep the current
+      ** checkout unchanged. */
+      db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
+    }
     db_end_transaction(0);
   }
 }