Fossil SCM
Need a command to find states of files for entire tree/subdir for Editors and IDEs.
7a27e10f1fe932d…
· opened 16 years, 3 months ago
- Type
- Feature_Request
- Priority
- Immediate
- Severity
- Important
- Resolution
- Overcome_By_Events
- Subsystem
- —
- Created
- Dec. 14, 2009 8:43 p.m.
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:
-
file.c: file_tree_name does not handle a trailing / on root dir.
-
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 : print a specific version to stdout
-
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:
Patch is:
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.
Comments (1)
fossil status --all
does this.