| | @@ -21,75 +21,168 @@ |
| 21 | 21 | #include "finfo.h" |
| 22 | 22 | |
| 23 | 23 | /* |
| 24 | 24 | ** COMMAND: finfo |
| 25 | 25 | ** |
| 26 | | -** Usage: %fossil finfo FILENAME |
| 26 | +** Usage: %fossil finfo {?-l|--log? / -s|--status / --p|--print} FILENAME |
| 27 | +** |
| 28 | +** Print the complete change history for a single file going backwards |
| 29 | +** in time. The default is -l. |
| 30 | +** |
| 31 | +** For the -l|--log option: If "-b|--brief" is specified one line per revision |
| 32 | +** is printed, otherwise the full comment is printed. The "--limit N" |
| 33 | +** and "--offset P" options limits the output to the first N changes |
| 34 | +** after skipping P changes. |
| 27 | 35 | ** |
| 28 | | -** Print the change history for a single file. |
| 36 | +** In the -s form prints the status as <status> <revision>. This is |
| 37 | +** a quick status and does not check for up-to-date-ness of the file. |
| 29 | 38 | ** |
| 30 | | -** The "--limit N" and "--offset P" options limit the output to the first |
| 31 | | -** N changes after skipping P changes. |
| 39 | +** The -p form, there's an optional flag "-r|--revision REVISION". The |
| 40 | +** specified version (or the latest checked out version) is printed to |
| 41 | +** stdout. |
| 32 | 42 | */ |
| 33 | 43 | void finfo_cmd(void){ |
| 34 | | - Stmt q; |
| 35 | 44 | int vid; |
| 36 | | - Blob dest; |
| 37 | | - const char *zFilename; |
| 38 | | - const char *zLimit; |
| 39 | | - const char *zOffset; |
| 40 | | - int iLimit, iOffset; |
| 41 | 45 | |
| 42 | 46 | db_must_be_within_tree(); |
| 43 | 47 | vid = db_lget_int("checkout", 0); |
| 44 | 48 | if( vid==0 ){ |
| 45 | 49 | fossil_panic("no checkout to finfo files in"); |
| 46 | 50 | } |
| 47 | | - zLimit = find_option("limit",0,1); |
| 48 | | - iLimit = zLimit ? atoi(zLimit) : -1; |
| 49 | | - zOffset = find_option("offset",0,1); |
| 50 | | - iOffset = zOffset ? atoi(zOffset) : 0; |
| 51 | | - if (g.argc<3) { |
| 52 | | - usage("FILENAME"); |
| 53 | | - } |
| 54 | | - file_tree_name(g.argv[2], &dest, 1); |
| 55 | | - zFilename = blob_str(&dest); |
| 56 | | - db_prepare(&q, |
| 57 | | - "SELECT " |
| 58 | | - " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* New file */ |
| 59 | | - " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* The check-in */ |
| 60 | | - " date(event.mtime,'localtime')," |
| 61 | | - " coalesce(event.ecomment, event.comment)," |
| 62 | | - " coalesce(event.euser, event.user)" |
| 63 | | - " FROM mlink, event" |
| 64 | | - " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" |
| 65 | | - " AND event.objid=mlink.mid" |
| 66 | | - " ORDER BY event.mtime DESC LIMIT %d OFFSET %d /*sort*/", |
| 67 | | - zFilename, iLimit, iOffset |
| 68 | | - ); |
| 69 | | - |
| 70 | | - printf("History of %s\n", zFilename); |
| 71 | | - while( db_step(&q)==SQLITE_ROW ){ |
| 72 | | - const char *zFileUuid = db_column_text(&q, 0); |
| 73 | | - const char *zCiUuid = db_column_text(&q, 1); |
| 74 | | - const char *zDate = db_column_text(&q, 2); |
| 75 | | - const char *zCom = db_column_text(&q, 3); |
| 76 | | - const char *zUser = db_column_text(&q, 4); |
| 77 | | - char *zOut; |
| 78 | | - printf("%s ", zDate); |
| 79 | | - if( zFileUuid==0 ){ |
| 80 | | - zOut = sqlite3_mprintf("[%.10s] DELETED %s (user: %s)", |
| 81 | | - zCiUuid, zCom, zUser); |
| 51 | + vfile_check_signature(vid, 1); |
| 52 | + if (find_option("status","s",0)) { |
| 53 | + Stmt q; |
| 54 | + Blob line; |
| 55 | + Blob fname; |
| 56 | + |
| 57 | + if( g.argc!=3 ) usage("-s|--status FILENAME"); |
| 58 | + file_tree_name(g.argv[2], &fname, 1); |
| 59 | + db_prepare(&q, |
| 60 | + "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" |
| 61 | + " FROM vfile WHERE vfile.pathname=%B", &fname); |
| 62 | + blob_zero(&line); |
| 63 | + if ( db_step(&q)==SQLITE_ROW ) { |
| 64 | + Blob uuid; |
| 65 | + int isDeleted = db_column_int(&q, 1); |
| 66 | + int isNew = db_column_int(&q,2) == 0; |
| 67 | + int chnged = db_column_int(&q,3); |
| 68 | + int renamed = db_column_int(&q,4); |
| 69 | + |
| 70 | + blob_zero(&uuid); |
| 71 | + db_blob(&uuid, |
| 72 | + "SELECT uuid FROM blob, mlink, vfile WHERE " |
| 73 | + "blob.rid = mlink.mid AND mlink.fid = vfile.rid AND " |
| 74 | + "vfile.pathname=%B", |
| 75 | + &fname |
| 76 | + ); |
| 77 | + if( isNew ){ |
| 78 | + blob_appendf(&line, "new"); |
| 79 | + }else if( isDeleted ){ |
| 80 | + blob_appendf(&line, "deleted"); |
| 81 | + }else if( renamed ){ |
| 82 | + blob_appendf(&line, "renamed"); |
| 83 | + }else if( chnged ){ |
| 84 | + blob_appendf(&line, "edited"); |
| 85 | + }else{ |
| 86 | + blob_appendf(&line, "unchanged"); |
| 87 | + } |
| 88 | + blob_appendf(&line, " "); |
| 89 | + blob_appendf(&line, " %10.10s", blob_str(&uuid)); |
| 90 | + blob_reset(&uuid); |
| 91 | + }else{ |
| 92 | + blob_appendf(&line, "unknown 0000000000"); |
| 93 | + } |
| 94 | + db_finalize(&q); |
| 95 | + printf("%s\n", blob_str(&line)); |
| 96 | + blob_reset(&fname); |
| 97 | + blob_reset(&line); |
| 98 | + }else if( find_option("print","p",0) ){ |
| 99 | + Blob record; |
| 100 | + Blob fname; |
| 101 | + const char *zRevision = find_option("revision", "r", 1); |
| 102 | + |
| 103 | + file_tree_name(g.argv[2], &fname, 1); |
| 104 | + if( zRevision ){ |
| 105 | + historical_version_of_file(zRevision, blob_str(&fname), &record, 0); |
| 82 | 106 | }else{ |
| 83 | | - zOut = sqlite3_mprintf("[%.10s] %s (user: %s, artifact: [%.10s])", |
| 84 | | - zCiUuid, zCom, zUser, zFileUuid); |
| 85 | | - } |
| 86 | | - comment_print(zOut, 11, 79); |
| 87 | | - sqlite3_free(zOut); |
| 88 | | - } |
| 89 | | - db_finalize(&q); |
| 90 | | - blob_reset(&dest); |
| 107 | + int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); |
| 108 | + if( rid==0 ){ |
| 109 | + fossil_fatal("no history for file: %b", &fname); |
| 110 | + } |
| 111 | + content_get(rid, &record); |
| 112 | + } |
| 113 | + blob_write_to_file(&record, "-"); |
| 114 | + blob_reset(&record); |
| 115 | + blob_reset(&fname); |
| 116 | + }else{ |
| 117 | + Blob line; |
| 118 | + Stmt q; |
| 119 | + Blob fname; |
| 120 | + int rid; |
| 121 | + const char *zFilename; |
| 122 | + const char *zLimit; |
| 123 | + const char *zOffset; |
| 124 | + int iLimit, iOffset, iBrief; |
| 125 | + |
| 126 | + if( find_option("log","l",0) ){ |
| 127 | + /* this is the default, no-op */ |
| 128 | + } |
| 129 | + zLimit = find_option("limit",0,1); |
| 130 | + iLimit = zLimit ? atoi(zLimit) : -1; |
| 131 | + zOffset = find_option("offset",0,1); |
| 132 | + iOffset = zOffset ? atoi(zOffset) : 0; |
| 133 | + iBrief = (find_option("brief","b",0) == 0); |
| 134 | + if( g.argc!=3 ){ |
| 135 | + usage("?-l|--log? ?-b|--brief? FILENAME"); |
| 136 | + } |
| 137 | + file_tree_name(g.argv[2], &fname, 1); |
| 138 | + rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); |
| 139 | + if( rid==0 ){ |
| 140 | + fossil_fatal("no history for file: %b", &fname); |
| 141 | + } |
| 142 | + zFilename = blob_str(&fname); |
| 143 | + db_prepare(&q, |
| 144 | + "SELECT b.uuid, ci.uuid, date(event.mtime,'localtime')," |
| 145 | + " coalesce(event.ecomment, event.comment)," |
| 146 | + " coalesce(event.euser, event.user)" |
| 147 | + " FROM mlink, blob b, event, blob ci" |
| 148 | + " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" |
| 149 | + " AND b.rid=mlink.fid" |
| 150 | + " AND event.objid=mlink.mid" |
| 151 | + " AND event.objid=ci.rid" |
| 152 | + " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", |
| 153 | + zFilename, iLimit, iOffset |
| 154 | + ); |
| 155 | + blob_zero(&line); |
| 156 | + if( iBrief ){ |
| 157 | + printf("History of %s\n", blob_str(&fname)); |
| 158 | + } |
| 159 | + while( db_step(&q)==SQLITE_ROW ){ |
| 160 | + const char *zFileUuid = db_column_text(&q, 0); |
| 161 | + const char *zCiUuid = db_column_text(&q,1); |
| 162 | + const char *zDate = db_column_text(&q, 2); |
| 163 | + const char *zCom = db_column_text(&q, 3); |
| 164 | + const char *zUser = db_column_text(&q, 4); |
| 165 | + char *zOut; |
| 166 | + if( iBrief ){ |
| 167 | + printf("%s ", zDate); |
| 168 | + zOut = sqlite3_mprintf("[%.10s] %s (user: %s, artifact: [%.10s])", |
| 169 | + zCiUuid, zCom, zUser, zFileUuid); |
| 170 | + comment_print(zOut, 11, 79); |
| 171 | + sqlite3_free(zOut); |
| 172 | + }else{ |
| 173 | + blob_reset(&line); |
| 174 | + blob_appendf(&line, "%.10s ", zCiUuid); |
| 175 | + blob_appendf(&line, "%.10s ", zDate); |
| 176 | + blob_appendf(&line, "%8.8s ", zUser); |
| 177 | + blob_appendf(&line,"%-40.40s\n", zCom ); |
| 178 | + comment_print(blob_str(&line), 0, 79); |
| 179 | + } |
| 180 | + } |
| 181 | + db_finalize(&q); |
| 182 | + blob_reset(&fname); |
| 183 | + } |
| 91 | 184 | } |
| 92 | 185 | |
| 93 | 186 | |
| 94 | 187 | /* |
| 95 | 188 | ** WEBPAGE: finfo |
| 96 | 189 | |