Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Extend the commit command so that specific files can be committed. There are still some problems with doing this after a merge. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
22552fb803cfa78054677bef53d250e5 |
| User & Date: | dan 2007-08-03 15:31:33.000 |
Context
|
2007-08-03
| ||
| 23:30 | Show an error if unrecognized command-line options appear on the commit command. Also add the (undocumented) "omit-ci-sig" configuration option on the database. Setting omit-ci-sig omits the PGP signature on check-in. check-in: 6aff11f03f user: drh tags: trunk | |
| 15:31 | Extend the commit command so that specific files can be committed. There are still some problems with doing this after a merge. check-in: 22552fb803 user: dan tags: trunk | |
|
2007-08-01
| ||
| 21:59 | Add the ability to sign manifests using GPG during a check-in. Due to a bug in the manifest parser, older versions will not be able to check-out signed manifests. check-in: e37451d9c2 user: drh tags: trunk | |
Changes
Changes to src/checkin.c.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
** of output.
**
** We assume that vfile_check_signature has been run.
*/
static void status_report(Blob *report, const char *zPrefix){
Stmt q;
int nPrefix = strlen(zPrefix);
| > | | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
** of output.
**
** 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 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;
blob_append(report, zPrefix, nPrefix);
if( isNew ){
|
| ︙ | ︙ | |||
207 208 209 210 211 212 213 214 215 216 217 218 |
}
blob_reset(&text);
zComment = blob_str(pComment);
i = strlen(zComment);
while( i>0 && isspace(zComment[i-1]) ){ i--; }
blob_resize(pComment, i);
}
/*
** COMMAND: commit
**
** Create a new version containing all of the changes in the current
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > < | | > > > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
}
blob_reset(&text);
zComment = blob_str(pComment);
i = strlen(zComment);
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. 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;
Stmt q;
Stmt q2;
char *zUuid, *zDate;
char *zManifestFile; /* Name of the manifest file */
Blob manifest;
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 AND file_is_selected(id)"
, g.zLocalRoot
);
while( db_step(&q)==SQLITE_ROW ){
int id, rid;
const char *zFullname;
Blob content;
id = db_column_int(&q, 0);
zFullname = db_column_text(&q, 1);
rid = db_column_int(&q, 2);
blob_zero(&content);
blob_read_from_file(&content, zFullname);
nrid = content_put(&content, 0);
if( rid>0 ){
content_deltify(rid, nrid, 0);
}
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
}
db_finalize(&q);
/* Create the manifest */
blob_zero(&manifest);
blob_appendf(&manifest, "C %F\n", blob_str(&comment));
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&manifest, "D %s\n", zDate);
db_prepare(&q,
"SELECT pathname, uuid FROM vfile JOIN blob ON vfile.mrid=blob.rid"
" WHERE NOT deleted AND vfile.vid=%d"
" ORDER BY 1", vid);
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const char *zUuid = db_column_text(&q, 1);
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 ){
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);
zManifestFile = mprintf("%smanifest", g.zLocalRoot);
if( clearsign(&manifest, &manifest) ){
|
| ︙ | ︙ | |||
318 319 320 321 322 323 324 |
fossil_panic("trouble committing manifest: %s", g.zErrMsg);
}
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);
| | | > | | > > > | > > > > > > > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
fossil_panic("trouble committing manifest: %s", g.zErrMsg);
}
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 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 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);
}
|
Changes to src/db.c.
| ︙ | ︙ | |||
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
printf("%s%c", sqlite3_value_text(argv[i]), c);
}
}
}
static void db_sql_trace(void *notUsed, const char *zSql){
printf("%s\n", zSql);
}
/*
** 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);
if( g.fSqlTrace ){
sqlite3_trace(g.db, db_sql_trace, 0);
}
once = 0;
}
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 |
printf("%s%c", sqlite3_value_text(argv[i]), c);
}
}
}
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;
}
}
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
62 63 64 65 66 67 68 69 70 71 72 73 74 75 | char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ 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 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: */ char *urlUser; /* User id for http: */ char *urlPasswd; /* Password for http: */ | > > | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ 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: */ char *urlUser; /* User id for http: */ char *urlPasswd; /* Password for http: */ |
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
closedir(d);
}
/*
** Compute an aggregate MD5 checksum over the disk image of every
** file in vid. The file names are part of the checksum.
**
** 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();
| > > > > > | > | | | > > > > > | | | | | | | | | | | | | | | | > > > > > > > > > > > > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
closedir(d);
}
/*
** 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, 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);
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);
}
/*
** Compute an aggregate MD5 checksum over the repository image of every
|
| ︙ | ︙ |