Ticket UUID: | 7a27e10f1fe932d696f1c1b033aae8458c32e203 | ||
Title: | Need a command to find states of files for entire tree/subdir for Editors and IDEs. | ||
Status: | Open | Type: | Feature_Request |
Severity: | Important | Priority: | |
Subsystem: | Resolution: | Open | |
Last Modified: | 2009-12-17 14:54:34 | ||
Version Found In: | 70656d00f | ||
Description & Comments: | |||
This has been discussed a few times in the mailing list. My particular
requirement is for integration into emacs-23. The vc-dired mode requires
a command to run to give the status for each file that is registered
with fossil. At present "fossil ls" gives you a list of files and their
state, but it doesn't tell you if a file will change if you do an update.
My users are used to having emacs tell them what all will change if they
do an update.
The changes are to 3 files: 1. file.c: file_tree_name does not handle a trailing / on root dir. 2. finfo.c : added options to command line finfo: -l|--log (default), -b|--brief (only one line per revision) (optional) -s|--status: report checkin version for file. -p <revision>: print a specific version to stdout 3. update.c : added two flags: -v|--verbose : print status even for unchanged files -n|--nochange : don't do remote-pull, and print changes rather than applying them. I only need both flags together so they can be merged. I'll attach a patch to this ticket off the current tip. anonymous claiming to be Venkat added on 2009-12-14 20:48:03: venkat:../head/fossil:37> fossil diff Index: src/file.c =================================================================== --- src/file.c +++ src/file.c @@ -391,23 +391,26 @@ ** false, then simply return 0. ** ** The root of the tree is defined by the g.zLocalRoot variable. */ int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){ - int n; + int m,n; Blob full; db_must_be_within_tree(); file_canonical_name(zOrigName, &full); n = strlen(g.zLocalRoot); - if( blob_size(&full)<=n || memcmp(g.zLocalRoot, blob_buffer(&full), n) ){ + m = blob_size(&full); + if( m<n-1 || memcmp(g.zLocalRoot, blob_buffer(&full), n-1) ){ blob_reset(&full); if( errFatal ){ fossil_fatal("file outside of checkout tree: %s", zOrigName); } return 0; } blob_zero(pOut); + if (m == n - 1) + return 1; blob_append(pOut, blob_buffer(&full)+n, blob_size(&full)-n); return 1; } /* Index: src/finfo.c =================================================================== --- src/finfo.c +++ src/finfo.c @@ -27,71 +27,169 @@ #include "finfo.h" /* ** COMMAND: finfo ** -** Usage: %fossil finfo FILENAME +** Usage: %fossil finfo {?-l|--log? / -s|--status / --p|--print} REV?FILENAME +** +** Print the complete change history for a single file going backwards +** in time. The default is -l. +** +** For the -l|--log option: If "-b|--brief" is specified one line per revision +** is printed, otherwise the full comment is printed. The "--limit N" +** and "--offset P" options limits the output to the first N changes +** after skipping P changes. ** -** Print the change history for a single file. +** In the -s form prints the status as <status> <revision>. This is +** a quick status and does not check for up-to-date-ness of the file. ** -** The "--limit N" and "--offset P" options limits the output to the first -** N changes after skipping P changes. +** The -p form, there's an optional flag "-r|--revision REVISION". The +** specified version (or the latest checked out version) is printed to +** stdout. +** */ + void finfo_cmd(void){ - Stmt q; - int vid; - Blob dest; - const char *zFilename; - const char *zLimit; - const char *zOffset; - int iLimit, iOffset; + int vid; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_panic("no checkout to finfo files in"); } - zLimit = find_option("limit",0,1); - iLimit = zLimit ? atoi(zLimit) : -1; - zOffset = find_option("offset",0,1); - iOffset = zOffset ? atoi(zOffset) : 0; - if (g.argc<3) { - usage("FILENAME"); - } - file_tree_name(g.argv[2], &dest, 1); - zFilename = blob_str(&dest); - db_prepare(&q, - "SELECT b.uuid, ci.uuid, date(event.mtime,'localtime')," - " coalesce(event.ecomment, event.comment)," - " coalesce(event.euser, event.user)" - " FROM mlink, blob b, event, blob ci" - " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" - " AND b.rid=mlink.fid" - " AND event.objid=mlink.mid" - " AND event.objid=ci.rid" - " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", - zFilename, iLimit, iOffset - ); + vfile_check_signature(vid); + if (find_option("status","s",0)) { + Stmt q; + Blob line; + Blob fname; + + if (g.argc != 3) { + usage("-s|--status FILENAME"); + } + file_tree_name(g.argv[2], &fname, 1); + db_prepare(&q, + "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" + " FROM vfile WHERE vfile.pathname=%B", &fname); + blob_zero(&line); + if ( db_step(&q)==SQLITE_ROW ) { + Blob uuid; + int isDeleted = db_column_int(&q, 1); + int isNew = db_column_int(&q,2) == 0; + int chnged = db_column_int(&q,3); + int renamed = db_column_int(&q,4); + + blob_zero(&uuid); + db_blob(&uuid,"SELECT uuid FROM blob, mlink, vfile WHERE " + "blob.rid = mlink.mid AND mlink.fid = vfile.rid AND " + "vfile.pathname=%B",&fname); + if (isNew) { + blob_appendf(&line, "new"); + } else if (isDeleted) { + blob_appendf(&line, "deleted"); + } else if (renamed) { + blob_appendf(&line, "renamed"); + } else if (chnged) { + blob_appendf(&line, "edited"); + } else { + blob_appendf(&line, "unchanged"); + } + blob_appendf(&line, " "); + blob_appendf(&line, " %10.10s", blob_str(&uuid)); + blob_reset(&uuid); + } else { + blob_appendf(&line, "unknown 0000000000"); + } + db_finalize(&q); + printf("%s\n", blob_str(&line)); + blob_reset(&fname); + blob_reset(&line); + } else if (find_option("print","p",0)) { + Blob record; + Blob fname; + const char *zRevision = find_option("revision", "r", 1); + + file_tree_name(g.argv[2], &fname, 1); + if (zRevision) { + historical_version_of_file(zRevision, blob_str(&fname), &record); + } else { + int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); + if( rid==0 ){ + fossil_fatal("no history for file: %b", &fname); + } + content_get(rid, &record); + } + blob_write_to_file(&record, "-"); + blob_reset(&record); + blob_reset(&fname); + } else { + Blob line; + Stmt q; + Blob fname; + int rid; + const char *zFilename; + const char *zLimit; + const char *zOffset; + int iLimit, iOffset, iBrief; - printf("History of %s\n", zFilename); - while( db_step(&q)==SQLITE_ROW ){ - const char *zFileUuid = db_column_text(&q, 0); - const char *zCiUuid = db_column_text(&q, 1); - const char *zDate = db_column_text(&q, 2); - const char *zCom = db_column_text(&q, 3); - const char *zUser = db_column_text(&q, 4); - char *zOut; - printf("%s ", zDate); - zOut = sqlite3_mprintf("[%.10s] %s (user: %s, artifact: [%.10s])", - zCiUuid, zCom, zUser, zFileUuid); - comment_print(zOut, 11, 79); - sqlite3_free(zOut); + if (find_option("log","l",0)) { /* this is the default, no-op */ + } + zLimit = find_option("limit",0,1); + iLimit = zLimit ? atoi(zLimit) : -1; + zOffset = find_option("offset",0,1); + iOffset = zOffset ? atoi(zOffset) : 0; + iBrief = (find_option("brief","b",0) == 0); + if (g.argc != 3) { + usage("?-l|--log? ?-b|--brief? FILENAME"); + } + file_tree_name(g.argv[2], &fname, 1); + rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); + if( rid==0 ){ + fossil_fatal("no history for file: %b", &fname); + } + zFilename = blob_str(&fname); + db_prepare(&q, + "SELECT b.uuid, ci.uuid, date(event.mtime,'localtime')," + " coalesce(event.ecomment, event.comment)," + " coalesce(event.euser, event.user)" + " FROM mlink, blob b, event, blob ci" + " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" + " AND b.rid=mlink.fid" + " AND event.objid=mlink.mid" + " AND event.objid=ci.rid" + " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", + zFilename, iLimit, iOffset + ); + blob_zero(&line); + if (iBrief) { + printf("History of %s\n", blob_str(&fname)); + } + while( db_step(&q)==SQLITE_ROW ){ + const char *zFileUuid = db_column_text(&q, 0); + const char *zCiUuid = db_column_text(&q,1); + const char *zDate = db_column_text(&q, 2); + const char *zCom = db_column_text(&q, 3); + const char *zUser = db_column_text(&q, 4); + char *zOut; + if (iBrief) { + printf("%s ", zDate); + zOut = sqlite3_mprintf("[%.10s] %s (user: %s, artifact: [%.10s])", + zCiUuid, zCom, zUser, zFileUuid); + comment_print(zOut, 11, 79); + sqlite3_free(zOut); + } else { + blob_reset(&line); + blob_appendf(&line, "%.10s ", zCiUuid); + blob_appendf(&line, "%.10s ", zDate); + blob_appendf(&line, "%8.8s ", zUser); + blob_appendf(&line,"%-40.40s\n", zCom ); + comment_print(blob_str(&line), 0, 79); + } + } + db_finalize(&q); + blob_reset(&fname); } - db_finalize(&q); - blob_reset(&dest); -} - +} /* ** WEBPAGE: finfo ** URL: /finfo?name=FILENAME ** Index: src/update.c =================================================================== --- src/update.c +++ src/update.c @@ -46,21 +46,40 @@ ** single leaf. If there are a multiple leaves, the latest is used ** if the --latest flag is present. ** ** This command is different from the "checkout" in that edits are ** not overwritten. Edits are merged into the new version. +** +** If the "-n|--nochange" flag is specified, There is no auto-sync-pull, +** and no local files are modified. But it will still go through the +** files and print the status of files that are not up-to-date. +** +** If the "-v|--verbose" flag is specified, then it prints the status +** of the unmodified and up-to-date files as well. +** */ void update_cmd(void){ int vid; /* Current version */ int tid=0; /* Target version - version we are changing to */ Stmt q; int latestFlag; /* Pick the latest version if true */ int forceFlag; /* True force the update */ + int nochangeFlag; /* Do not modify any files other than repository */ + int verboseFlag; /* Print states of all files */ + const char *zFile; /* Name of file to update */ + Blob fname; url_proxy_options(); latestFlag = find_option("latest",0, 0)!=0; forceFlag = find_option("force","f",0)!=0; + nochangeFlag = find_option("nochange","n",0)!= 0; + verboseFlag = find_option("verbose","v",0)!= 0; + zFile = find_option("file",0,1); + if (zFile != 0) { + file_tree_name(zFile, &fname, 1); + } + if( g.argc!=3 && g.argc!=2 ){ usage("?VERSION?"); } db_must_be_within_tree(); vid = db_lget_int("checkout", 0); @@ -78,11 +97,20 @@ } if( !is_a_version(tid) ){ fossil_fatal("not a version: %s", g.argv[2]); } } - autosync(AUTOSYNC_PULL); + + if(nochangeFlag == 0){ + /* + ** Do an autosync pull prior to the update, if autosync is on and they + ** did not want a specific version (i.e. another branch, a past revision). + ** By not giving a specific version, they are asking for the latest, thus + ** pull to get the latest, then update. + */ + autosync(AUTOSYNC_PULL); + } if( tid==0 ){ compute_leaves(vid, 1); if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ){ db_prepare(&q, @@ -98,13 +126,16 @@ tid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); } - db_begin_transaction(); + if (!nochangeFlag) { + db_begin_transaction(); + } vfile_check_signature(vid); - undo_begin(); + if (!nochangeFlag) + undo_begin(); load_vfile_from_rid(tid); /* ** The record.fn field is used to match files against each other. The ** FV table contains one row for each each unique filename in @@ -151,10 +182,21 @@ id, rid, chnged, fn ); } db_finalize(&q); + if (zFile != 0) { + if (file_isdir(zFile) == 1) { + if (strlen(blob_str(&fname)) > 0) { + db_multi_exec("DELETE FROM fv WHERE fn NOT GLOB '%q/*'", + blob_str(&fname)); + } + } else { + db_multi_exec("DELETE FROM fv WHERE fn <> '%q'", blob_str(&fname)); + } + } + 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); @@ -170,70 +212,94 @@ */ printf("CONFLICT %s\n", zName); }else if( idt>0 && idv==0 ){ /* File added in the target. */ printf("ADD %s\n", zName); - undo_save(zName); - vfile_to_disk(0, idt, 0); + if (!nochangeFlag) { + undo_save(zName); + vfile_to_disk(0, idt, 0); + } + }else if ( idt>0 && idv>0 && ridt == ridv){ + /* We have latest version */ + if (verboseFlag) { + if (chnged) { + printf("EDITED %s\n", zName); + } else { + printf("UNCHANGED %s\n", zName); + } + } }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){ /* The file is unedited. Change it to the target version */ printf("UPDATE %s\n", zName); - undo_save(zName); - vfile_to_disk(0, idt, 0); + if (!nochangeFlag) { + undo_save(zName); + 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); + if (verboseFlag) { + printf("ADDED %s\n", zName); + } + if (!nochangeFlag) { + db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv); + } }else if( chnged ){ printf("CONFLICT %s\n", zName); }else{ char *zFullPath; printf("REMOVE %s\n", zName); - undo_save(zName); - zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); - unlink(zFullPath); - free(zFullPath); + if (!nochangeFlag) { + undo_save(zName); + zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); + unlink(zFullPath); + free(zFullPath); + } } }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); - rc = blob_merge(&v, &e, &t, &r); - if( rc>=0 ){ - blob_write_to_file(&r, zFullPath); - if( rc>0 ){ - printf("***** %d merge conflicts in %s\n", rc, zName); - } - }else{ - printf("***** Cannot merge binary file %s\n", zName); - } - free(zFullPath); - blob_reset(&v); - blob_reset(&e); - blob_reset(&t); - blob_reset(&r); - + if (!nochangeFlag) { + Blob e, r, t, v; + int rc; + char *zFullPath; + + 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); + rc = blob_merge(&v, &e, &t, &r); + if( rc>=0 ){ + blob_write_to_file(&r, zFullPath); + if( rc>0 ){ + printf("***** %d merge conflicts in %s\n", rc, zName); + } + }else{ + printf("***** Cannot merge binary file %s\n", zName); + } + free(zFullPath); + blob_reset(&v); + blob_reset(&e); + blob_reset(&t); + blob_reset(&r); + } } } db_finalize(&q); /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ - db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); - manifest_to_disk(tid); - db_lset_int("checkout", tid); - db_end_transaction(0); + if (!nochangeFlag) { + db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); + manifest_to_disk(tid); + db_lset_int("checkout", tid); + db_end_transaction(0); + } } /* ** Get the contents of a file within a given revision. |