Check-in [22552fb803]
Not logged in
Overview

SHA1 Hash:22552fb803cfa78054677bef53d250e532e00832
Date: 2007-08-03 15:31:33
User: dan
Comment:Extend the commit command so that specific files can be committed. There are still some problems with doing this after a merge.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/checkin.c from [e13578a174] to [a1a87577e4].

@@ -35,12 +35,14 @@
 ** We assume that vfile_check_signature has been run.
 */
 static void status_report(Blob *report, const char *zPrefix){
   Stmt q;
   int nPrefix = strlen(zPrefix);
-  db_prepare(&q, "SELECT pathname, deleted, chnged, rid FROM vfile"
-                 " WHERE chnged OR deleted OR rid=0 ORDER BY 1");
+  db_prepare(&q,
+    "SELECT pathname, deleted, chnged, rid FROM vfile "
+    "WHERE file_is_selected(id) AND (chnged OR deleted OR rid=0) ORDER BY 1"
+  );
   while( db_step(&q)==SQLITE_ROW ){
     const char *zPathname = db_column_text(&q,0);
     int isDeleted = db_column_int(&q, 1);
     int isChnged = db_column_int(&q,2);
     int isNew = db_column_int(&q,3)==0;
@@ -211,14 +213,55 @@
   while( i>0 && isspace(zComment[i-1]) ){ i--; }
   blob_resize(pComment, i);
 }
 
 /*
+** Populate the Global.aCommitFile[] based on the command line arguments
+** to a [commit] command. Global.aCommitFile is an array of integers
+** sized at (N+1), where N is the number of arguments passed to [commit].
+** The contents are the [id] values from the vfile table corresponding
+** to the filenames passed as arguments.
+**
+** The last element of aCommitFile[] is always 0 - indicating the end
+** of the array.
+**
+** If there were no arguments passed to [commit], aCommitFile is not
+** allocated and remains NULL. Other parts of the code interpret this
+** to mean "all files".
+*/
+void select_commit_files(void){
+  if( g.argc>2 ){
+    int ii;
+    Blob b;
+    memset(&b, 0, sizeof(Blob));
+    g.aCommitFile = malloc(sizeof(int)*(g.argc-1));
+
+    for(ii=2; ii<g.argc; ii++){
+      int iId;
+      if( !file_tree_name(g.argv[ii], &b) ){
+        fossil_fatal("file is not in tree: %s", g.argv[ii]);
+      }
+      iId = db_int(-1, "SELECT id FROM vfile WHERE pathname=%Q", blob_str(&b));
+      if( iId<0 ){
+        fossil_fatal("fossil knows nothing about: %s", g.argv[ii]);
+      }
+      g.aCommitFile[ii-2] = iId;
+    }
+    g.aCommitFile[ii-2] = 0;
+  }
+}
+
+/*
 ** COMMAND: commit
 **
 ** Create a new version containing all of the changes in the current
-** checkout.
+** checkout. A commit is a three step process:
+**
+**   1) Add the new content to the blob table,
+**   2) Create and add the new manifest to the blob table,
+**   3) Update the vfile table,
+**   4) Run checks to make sure everything is still internally consistent.
 */
 void commit_cmd(void){
   int rc;
   int vid, nrid, nvid;
   Blob comment;
@@ -230,25 +273,58 @@
   Blob mcksum;           /* Self-checksum on the manifest */
   Blob cksum1, cksum2;   /* Before and after commit checksums */
   Blob cksum1b;          /* Checksum recorded in the manifest */
 
   db_must_be_within_tree();
+
+  /* There are two ways this command may be executed. If there are
+  ** no arguments following the word "commit", then all modified files
+  ** in the checked out directory are committed. If one or more arguments
+  ** follows "commit", then only those files are committed.
+  **
+  ** After the following function call has returned, the Global.aCommitFile[]
+  ** array is allocated to contain the "id" field from the vfile table
+  ** for each file to be committed. Or, if aCommitFile is NULL, all files
+  ** should be committed.
+  */
+  select_commit_files();
+
   user_select();
   db_begin_transaction();
   rc = unsaved_changes();
   if( rc==0 ){
     fossil_panic("nothing has changed");
   }
+
+  /* If one or more files that were named on the command line have not
+  ** been modified, bail out now.
+  */
+  if( g.aCommitFile ){
+    Blob unmodified;
+    memset(&unmodified, 0, sizeof(Blob));
+    blob_init(&unmodified, 0, 0);
+    db_blob(&unmodified,
+      "SELECT pathname FROM vfile WHERE chnged = 0 AND file_is_selected(id)"
+    );
+    if( strlen(blob_str(&unmodified)) ){
+      fossil_panic("file %s has not changed", blob_str(&unmodified));
+    }
+  }
+
   vid = db_lget_int("checkout", 0);
   vfile_aggregate_checksum_disk(vid, &cksum1);
   prepare_commit_comment(&comment);
 
+  /* Step 1: Insert records for all modified files into the blob
+  ** table. If there were arguments passed to this command, only
+  ** the identified fils are inserted (if they have been modified).
+  */
   db_prepare(&q,
-    "SELECT id, %Q || pathname, mrid FROM vfile"
-    " WHERE chnged==1 AND NOT deleted", g.zLocalRoot
-  );
-  db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
+    "SELECT id, %Q || pathname, mrid FROM vfile "
+    "WHERE chnged==1 AND NOT deleted AND file_is_selected(id)"
+    , g.zLocalRoot
+  );
   while( db_step(&q)==SQLITE_ROW ){
     int id, rid;
     const char *zFullname;
     Blob content;
 
@@ -282,10 +358,12 @@
     blob_appendf(&manifest, "F %F %s\n", zName, zUuid);
   }
   db_finalize(&q);
   zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
   blob_appendf(&manifest, "P %s", zUuid);
+
+  db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id");
   db_bind_int(&q2, ":id", 0);
   while( db_step(&q2)==SQLITE_ROW ){
     int mid = db_column_int(&q2, 0);
     zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
     if( zUuid ){
@@ -292,10 +370,11 @@
       blob_appendf(&manifest, " %s", zUuid);
       free(zUuid);
     }
   }
   db_reset(&q2);
+
   blob_appendf(&manifest, "\n");
   blob_appendf(&manifest, "R %b\n", &cksum1);
   blob_appendf(&manifest, "U %F\n", g.zLogin);
   md5sum_blob(&manifest, &mcksum);
   blob_appendf(&manifest, "Z %b\n", &mcksum);
@@ -320,33 +399,44 @@
   manifest_crosslink(nvid, &manifest);
   content_deltify(vid, nvid, 0);
   zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
   printf("New_Version: %s\n", zUuid);
 
-  /* Update VFILE */
-  db_multi_exec("DELETE FROM vfile WHERE vid!=%d OR deleted", vid);
-  db_multi_exec("DELETE FROM vmerge");
-  db_multi_exec("UPDATE vfile SET vid=%d, rid=mrid, chnged=0, deleted=0", nvid);
+  /* Update the vfile and vmerge tables */
+  db_multi_exec(
+    "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND file_is_selected(id);"
+    "DELETE FROM vmerge WHERE file_is_selected(id) OR id=0;"
+    "UPDATE vfile SET vid=%d;"
+    "UPDATE vfile SET rid=mrid, chnged=0, deleted=0 WHERE file_is_selected(id);"
+    , vid, nvid
+  );
   db_lset_int("checkout", nvid);
 
-  /* Verify that the tree checksum is unchanged */
+  /* Verify that the repository checksum matches the expected checksum
+  ** calculated before the checkin started (and stored as the R record
+  ** of the manifest file).
+  */
   vfile_aggregate_checksum_repository(nvid, &cksum2);
   if( blob_compare(&cksum1, &cksum2) ){
     fossil_panic("tree checksum does not match repository after commit");
   }
+
+  /* Verify that the manifest checksum matches the expected checksum */
   vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b);
   if( blob_compare(&cksum1, &cksum1b) ){
     fossil_panic("manifest checksum does not agree with manifest: "
                  "%b versus %b", &cksum1, &cksum1b);
   }
   if( blob_compare(&cksum1, &cksum2) ){
     fossil_panic("tree checksum does not match manifest after commit: "
                  "%b versus %b", &cksum1, &cksum2);
   }
+
+  /* Verify that the commit did not modify any disk images. */
   vfile_aggregate_checksum_disk(nvid, &cksum2);
   if( blob_compare(&cksum1, &cksum2) ){
     fossil_panic("tree checksums before and after commit do not match");
   }
 
   /* Commit */
   db_end_transaction(0);
 }

Modified src/db.c from [904ab46ad3] to [acf733f759].

@@ -724,17 +724,51 @@
 static void db_sql_trace(void *notUsed, const char *zSql){
   printf("%s\n", zSql);
 }
 
 /*
+** This is used by the [commit] command.
+**
+** Return true if either:
+**
+**     a) Global.aCommitFile is NULL, or
+**     b) Global.aCommitFile contains the integer passed as an argument.
+**
+** Otherwise return false.
+*/
+static void file_is_selected(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  assert(argc==1);
+  if( g.aCommitFile ){
+    int iId = sqlite3_value_int(argv[0]);
+    int ii;
+    for(ii=0; g.aCommitFile[ii]; ii++){
+      if( iId==g.aCommitFile[ii] ){
+        sqlite3_result_int(context, 1);
+        return;
+      }
+    }
+    sqlite3_result_int(context, 0);
+  }else{
+    sqlite3_result_int(context, 1);
+  }
+}
+
+/*
 ** This function registers auxiliary functions when the SQLite
 ** database connection is first established.
 */
 LOCAL void db_connection_init(void){
   static int once = 1;
   if( once ){
     sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
+    sqlite3_create_function(
+      g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
+    );
     if( g.fSqlTrace ){
       sqlite3_trace(g.db, db_sql_trace, 0);
     }
     once = 0;
   }

Modified src/main.c from [e3d8b5d0b9] to [efc0bca8ad].

@@ -64,10 +64,12 @@
   const char *zContentType;  /* The content type of the input HTTP request */
   int iErrPriority;       /* Priority of current error message */
   char *zErrMsg;          /* Text of an error message */
   Blob cgiIn;             /* Input to an xfer www method */
   int cgiPanic;           /* Write error messages to CGI */
+
+  int *aCommitFile;
 
   int urlIsFile;          /* True if a "file:" url */
   char *urlName;          /* Hostname for http: or filename for file: */
   int urlPort;            /* TCP port number for http: */
   char *urlPath;          /* Pathname for http: */

Modified src/vfile.c from [8f052c3289] to [520337f87b].

@@ -253,43 +253,66 @@
 
 /*
 ** Compute an aggregate MD5 checksum over the disk image of every
 ** file in vid.  The file names are part of the checksum.
 **
+** This function operates differently if the Global.aCommitFile
+** variable is not NULL. In that case, the disk image is used for
+** each file in aCommitFile[] and the repository image (see
+** vfile_aggregate_checksum_repository() is used for all others).
+**
 ** Return the resulting checksum in blob pOut.
 */
 void vfile_aggregate_checksum_disk(int vid, Blob *pOut){
   FILE *in;
   Stmt q;
   char zBuf[4096];
 
   db_must_be_within_tree();
-  db_prepare(&q, "SELECT %Q || pathname, pathname FROM vfile"
-                 " WHERE NOT deleted AND vid=%d"
-                 " ORDER BY pathname",
-                 g.zLocalRoot, vid);
+  db_prepare(&q,
+      "SELECT %Q || pathname, pathname, file_is_selected(id), rid FROM vfile"
+      " WHERE NOT deleted AND vid=%d"
+      " ORDER BY pathname",
+      g.zLocalRoot, vid
+  );
   md5sum_init();
   while( db_step(&q)==SQLITE_ROW ){
     const char *zFullpath = db_column_text(&q, 0);
     const char *zName = db_column_text(&q, 1);
+    int isSelected = db_column_int(&q, 2);
+
     md5sum_step_text(zName, -1);
-    in = fopen(zFullpath,"rb");
-    if( in==0 ){
-      md5sum_step_text(" 0\n", -1);
-      continue;
-    }
-    fseek(in, 0L, SEEK_END);
-    sprintf(zBuf, " %ld\n", ftell(in));
-    fseek(in, 0L, SEEK_SET);
-    md5sum_step_text(zBuf, -1);
-    for(;;){
-      int n;
-      n = fread(zBuf, 1, sizeof(zBuf), in);
-      if( n<=0 ) break;
-      md5sum_step_text(zBuf, n);
-    }
-    fclose(in);
+
+    if( isSelected ){
+      in = fopen(zFullpath,"rb");
+      if( in==0 ){
+        md5sum_step_text(" 0\n", -1);
+        continue;
+      }
+      fseek(in, 0L, SEEK_END);
+      sprintf(zBuf, " %ld\n", ftell(in));
+      fseek(in, 0L, SEEK_SET);
+      md5sum_step_text(zBuf, -1);
+      for(;;){
+        int n;
+        n = fread(zBuf, 1, sizeof(zBuf), in);
+        if( n<=0 ) break;
+        md5sum_step_text(zBuf, n);
+      }
+      fclose(in);
+    }else{
+      int rid = db_column_int(&q, 3);
+      char zBuf[100];
+      Blob file;
+
+      blob_zero(&file);
+      content_get(rid, &file);
+      sprintf(zBuf, " %d\n", blob_size(&file));
+      md5sum_step_text(zBuf, -1);
+      md5sum_step_blob(&file);
+      blob_reset(&file);
+    }
   }
   db_finalize(&q);
   md5sum_finish(pOut);
 }