Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Improved limit handling for annotation. The limit can now be expressed in compute-time seconds rather than versions analyzed, and defaults to "1.0s", which is enough to compute a complete annotation on most reasonable files. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
517b9a5652649c95dbeae4c5f9f8621d |
| User & Date: | drh 2017-09-25 17:24:39.620 |
Context
|
2017-09-25
| ||
| 18:32 | Add the origin= query parameter to /annotate and the --origin option to the annotation command, to enable reverse annotations. check-in: c91b810620 user: drh tags: trunk | |
| 17:24 | Improved limit handling for annotation. The limit can now be expressed in compute-time seconds rather than versions analyzed, and defaults to "1.0s", which is enough to compute a complete annotation on most reasonable files. check-in: 517b9a5652 user: drh tags: trunk | |
| 15:36 | Change the "Log" submenu element of the annotation page so that it shows or hides the log using javascript, avoiding a server roundtrip and a recomputation of the annotation. check-in: 037e06b370 user: drh tags: trunk | |
Changes
Changes to src/diff.c.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 | #define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */ #define DIFF_NUMSTAT ((u64)0x80000000) /* Show line count of changes */ #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */ #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */ #define DIFF_STRIP_EOLCR (((u64)0x10)<<32) /* Strip trailing CR */ | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */
#define DIFF_NUMSTAT ((u64)0x80000000) /* Show line count of changes */
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
#define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
#define DIFF_STRIP_EOLCR (((u64)0x10)<<32) /* Strip trailing CR */
#define DIFF_SLOW_SBS (((u64)0x20)<<32) /* Better but slower side-by-side */
/*
** These error messages are shared in multiple locations. They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
"cannot compute difference between binary files\n"
|
| ︙ | ︙ | |||
2081 2082 2083 2084 2085 2086 2087 |
struct AnnLine { /* Lines of the original files... */
const char *z; /* The text of the line */
short int n; /* Number of bytes (omitting trailing \n) */
short int iVers; /* Level at which tag was set */
} *aOrig;
int nOrig; /* Number of elements in aOrig[] */
int nVers; /* Number of versions analyzed */
| | | 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 |
struct AnnLine { /* Lines of the original files... */
const char *z; /* The text of the line */
short int n; /* Number of bytes (omitting trailing \n) */
short int iVers; /* Level at which tag was set */
} *aOrig;
int nOrig; /* Number of elements in aOrig[] */
int nVers; /* Number of versions analyzed */
int bMoreToDo; /* True if the limit was reached */
struct AnnVers {
const char *zFUuid; /* File being analyzed */
const char *zMUuid; /* Check-in containing the file */
const char *zDate; /* Date of the check-in */
const char *zBgColor; /* Suggested background color */
const char *zUser; /* Name of user who did the check-in */
unsigned cnt; /* Number of lines contributed by this check-in */
|
| ︙ | ︙ | |||
2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 | /* Clear out the from file */ free(p->c.aFrom); /* Return no errors */ return 0; } /* ** Compute a complete annotation on a file. The file is identified by its ** filename and check-in name (NULL for current check-in). */ static void annotate_file( | > > > > > > > > > > > > > > | | | | | | | | | | | | | > > > > > > > > > > > | > > > > > > > | 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 |
/* Clear out the from file */
free(p->c.aFrom);
/* Return no errors */
return 0;
}
/* Return the current time as milliseconds since the Julian epoch */
static sqlite3_int64 current_time_in_milliseconds(void){
static sqlite3_vfs *clockVfs = 0;
sqlite3_int64 t;
if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
clockVfs->xCurrentTimeInt64(clockVfs, &t);
}else{
double r;
clockVfs->xCurrentTime(clockVfs, &r);
t = (sqlite3_int64)(r*86400000.0);
}
return t;
}
/*
** Compute a complete annotation on a file. The file is identified by its
** filename and check-in name (NULL for current check-in).
*/
static void annotate_file(
Annotator *p, /* The annotator */
const char *zFilename, /* The name of the file to be annotated */
const char *zRevision, /* Use the version of the file in this check-in */
const char *zLimit, /* Limit the number of versions analyzed */
u64 annFlags /* Flags to alter the annotation */
){
Blob toAnnotate; /* Text of the final (mid) version of the file */
Blob step; /* Text of previous revision */
Blob treename; /* FILENAME translated to canonical form */
int cid; /* Selected check-in ID */
int rid; /* Artifact ID of the file being annotated */
int fnid; /* Filename ID */
Stmt q; /* Query returning all ancestor versions */
int cnt = 0; /* Number of versions analyzed */
int iLimit; /* Maximum number of versions to analyze */
sqlite3_int64 mxTime; /* Halt at this time if not already complete */
if( zLimit ){
if( strcmp(zLimit,"none")==0 ){
iLimit = 0;
mxTime = 0;
}else if( sqlite3_strglob("*[0-9]s", zLimit)==0 ){
iLimit = 0;
mxTime = current_time_in_milliseconds() + 1000.0*atof(zLimit);
}else{
iLimit = atoi(zLimit);
if( iLimit<=0 ) iLimit = 30;
mxTime = 0;
}
}else{
/* Default limit is as much as we can do in 1.000 seconds */
iLimit = 0;
mxTime = current_time_in_milliseconds()+1000;
}
db_begin_transaction();
/* Get the artificate ID for the check-in begin analyzed */
if( zRevision ){
cid = name_to_typed_rid(zRevision, "ci");
}else{
db_must_be_within_tree();
|
| ︙ | ︙ | |||
2231 2232 2233 2234 2235 2236 2237 |
" AND ancestor.rid=mlink.mid"
" AND event.objid=mlink.mid"
" AND mlink.mid!=mlink.pid"
" ORDER BY ancestor.generation;",
fnid
);
| < | > > > > > > > > > < | 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 |
" AND ancestor.rid=mlink.mid"
" AND event.objid=mlink.mid"
" AND mlink.mid!=mlink.pid"
" ORDER BY ancestor.generation;",
fnid
);
while( db_step(&q)==SQLITE_ROW ){
if( cnt>=3 ){ /* Process at least 3 rows before imposing limits */
if( (iLimit>0 && cnt>=iLimit)
|| (cnt>0 && mxTime>0 && current_time_in_milliseconds()>mxTime)
){
p->bMoreToDo = 1;
break;
}
}
rid = db_column_int(&q, 4);
if( cnt==0 ){
if( !content_get(rid, &toAnnotate) ){
fossil_fatal("unable to retrieve content of artifact #%d", rid);
}
blob_to_utf8_no_bom(&toAnnotate, 0);
annotation_start(p, &toAnnotate, annFlags);
p->bMoreToDo = 0;
}
p->aVers = fossil_realloc(p->aVers, (p->nVers+1)*sizeof(p->aVers[0]));
p->aVers[p->nVers].zFUuid = fossil_strdup(db_column_text(&q, 0));
p->aVers[p->nVers].zMUuid = fossil_strdup(db_column_text(&q, 1));
p->aVers[p->nVers].zDate = fossil_strdup(db_column_text(&q, 2));
p->aVers[p->nVers].zUser = fossil_strdup(db_column_text(&q, 3));
p->nVers++;
if( cnt>0 ){
content_get(rid, &step);
blob_to_utf8_no_bom(&step, 0);
annotation_step(p, &step, p->nVers-1, annFlags);
blob_reset(&step);
}
cnt++;
}
db_finalize(&q);
db_end_transaction(0);
}
/*
** Return a color from a gradient.
*/
|
| ︙ | ︙ | |||
2298 2299 2300 2301 2302 2303 2304 | ** check-in). /blame and /praise also show the user who made the check-in. ** ** Query parameters: ** ** checkin=ID The manifest ID at which to start the annotation ** filename=FILENAME The filename. ** filevers=BOOLEAN Show file versions rather than check-in versions | > | > > | > | | | | | < | < < < < < < < | | < | > | | | | 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 |
** check-in). /blame and /praise also show the user who made the check-in.
**
** Query parameters:
**
** checkin=ID The manifest ID at which to start the annotation
** filename=FILENAME The filename.
** filevers=BOOLEAN Show file versions rather than check-in versions
** limit=LIMIT Limit the amount of analysis:
** "none" No limit
** "Xs" As much as can be computed in X seconds
** "N" N versions
** log=BOOLEAN Show a log of versions analyzed
** w=BOOLEAN Ignore whitespace
**
*/
void annotation_page(void){
int i;
const char *zLimit; /* Depth limit */
u64 annFlags = DIFF_STRIP_EOLCR;
int showLog; /* True to display the log */
int fileVers; /* Show file version instead of check-in versions */
int ignoreWs; /* Ignore whitespace */
const char *zFilename; /* Name of file to annotate */
const char *zRevision; /* Name of check-in from which to start annotation */
const char *zCI; /* The check-in containing zFilename */
int szHash; /* Number of characters in %S display */
char *zLink;
Annotator ann;
HQuery url;
struct AnnVers *p;
unsigned clr1, clr2, clr;
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
/* Gather query parameters */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( exclude_spiders() ) return;
load_control();
zFilename = P("filename");
zRevision = PD("checkin",0);
zLimit = P("limit");
showLog = PB("log");
fileVers = PB("filevers");
ignoreWs = PB("w");
if( ignoreWs ) annFlags |= DIFF_IGNORE_ALLWS;
/* compute the annotation */
annotate_file(&ann, zFilename, zRevision, zLimit, annFlags);
zCI = ann.aVers[0].zMUuid;
/* generate the web page */
style_header("Annotation For %h", zFilename);
if( bBlame ){
url_initialize(&url, "blame");
}else{
url_initialize(&url, "annotate");
}
url_add_parameter(&url, "checkin", P("checkin"));
url_add_parameter(&url, "filename", zFilename);
if( zLimit ){
url_add_parameter(&url, "limit", zLimit);
}
url_add_parameter(&url, "w", ignoreWs ? "1" : "0");
url_add_parameter(&url, "log", showLog ? "1" : "0");
url_add_parameter(&url, "filevers", fileVers ? "1" : "0");
style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
style_submenu_checkbox("log", "Log", 0, "toggle_annotation_log()");
style_submenu_checkbox("filevers", "Link to Files", 0, 0);
if( ann.bMoreToDo ){
style_submenu_element("All Ancestors", "%s",
url_render(&url, "limit", "none", 0, 0));
}
if( skin_detail_boolean("white-foreground") ){
clr1 = 0xa04040;
clr2 = 0x4059a0;
}else{
clr1 = 0xffb5b5; /* Recent changes: red (hot) */
clr2 = 0xb5e0ff; /* Older changes: blue (cold) */
}
for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
clr = gradient_color(clr1, clr2, ann.nVers-1, i);
ann.aVers[i].zBgColor = mprintf("#%06x", clr);
}
@ <div id="annotation_log" style='display:%s(showLog?"block":"none");'>
zLink = href("%R/finfo?name=%t&ci=%!S",zFilename,zCI);
@ <h2>Versions of %z(zLink)%h(zFilename)</a> analyzed:</h2>
@ <ol>
for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
@ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
@ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
@ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
@ </span>
}
@ </ol>
@ <hr />
@ </div>
@ <script>
@ function toggle_annotation_log(){
@ var w = gebi("annotation_log");
@ var x = document.forms["f01"].elements["log"].checked
@ w.style.display = x ? "block" : "none";
@ }
@ </script>
if( !ann.bMoreToDo ){
@ <h2>Origin for each line in
@ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
@ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
}else{
@ <h2>Lines added by the %d(ann.nVers) most recent ancestors of
@ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a>
@ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
}
@ <pre>
szHash = length_of_S_display();
for(i=0; i<ann.nOrig; i++){
int iVers = ann.aOrig[i].iVers;
char *z = (char*)ann.aOrig[i].z;
int n = ann.aOrig[i].n;
char zPrefix[300];
z[n] = 0;
if( iVers<0 && !ann.bMoreToDo ) iVers = ann.nVers-1;
if( bBlame ){
if( iVers>=0 ){
struct AnnVers *p = ann.aVers+iVers;
const char *zUuid = fileVers ? p->zFUuid : p->zMUuid;
char *zLink = xhref("target='infowindow'", "%R/info/%!S", zUuid);
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"<span style='background-color:%s'>"
"%s%.10s</a> %s</span> %13.13s:",
p->zBgColor, zLink, zUuid, p->zDate, p->zUser);
fossil_free(zLink);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s", szHash+28, "");
}
}else{
if( iVers>=0 ){
struct AnnVers *p = ann.aVers+iVers;
const char *zUuid = fileVers ? p->zFUuid : p->zMUuid;
char *zLink = xhref("target='infowindow'", "%R/info/%!S", zUuid);
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"<span style='background-color:%s'>"
"%s%.10s</a> %s</span> %4d:",
p->zBgColor, zLink, zUuid, p->zDate, i+1);
fossil_free(zLink);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s%4d:",szHash+14,"",i+1);
}
}
@ %s(zPrefix) %h(z)
}
@ </pre>
style_footer();
|
| ︙ | ︙ | |||
2471 2472 2473 2474 2475 2476 2477 | ** show the user who made each check-in and omits the line numbers. ** ** Options: ** --filevers Show file version numbers rather than ** check-in versions ** -r|--revision VERSION The specific check-in containing the file ** -l|--log List all versions analyzed | > > > | | | | < | | | | > < < < | > | < > | > < | | 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 |
** show the user who made each check-in and omits the line numbers.
**
** Options:
** --filevers Show file version numbers rather than
** check-in versions
** -r|--revision VERSION The specific check-in containing the file
** -l|--log List all versions analyzed
** -n|--limit LIMIT Limit the amount of analysis:
** N Up to N versions
** Xs As much as possible in X seconds
** none No limit
** -w|--ignore-all-space Ignore white space when comparing lines
** -Z|--ignore-trailing-space Ignore whitespace at line end
**
** See also: info, finfo, timeline
*/
void annotate_cmd(void){
const char *zRevision; /* Revision name, or NULL for current check-in */
Annotator ann; /* The annotation of the file */
int i; /* Loop counter */
const char *zLimit; /* The value to the -n|--limit option */
int showLog; /* True to show the log */
int fileVers; /* Show file version instead of check-in versions */
u64 annFlags = 0; /* Flags to control annotation properties */
int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */
int szHash; /* Display size of a version hash */
bBlame = g.argv[1][0]!='a';
zRevision = find_option("r","revision",1);
zLimit = find_option("limit","n",1);
showLog = find_option("log","l",0)!=0;
if( find_option("ignore-trailing-space","Z",0)!=0 ){
annFlags = DIFF_IGNORE_EOLWS;
}
if( find_option("ignore-all-space","w",0)!=0 ){
annFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
}
fileVers = find_option("filevers",0,0)!=0;
db_must_be_within_tree();
/* We should be done with options.. */
verify_all_options();
if( g.argc<3 ) {
usage("FILENAME");
}
annFlags |= DIFF_STRIP_EOLCR;
annotate_file(&ann, g.argv[2], zRevision, zLimit, annFlags);
if( showLog ){
struct AnnVers *p;
for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
fossil_print("version %3d: %s %S file %S\n",
i+1, p->zDate, p->zMUuid, p->zFUuid);
}
fossil_print("---------------------------------------------------\n");
}
szHash = length_of_S_display();
for(i=0; i<ann.nOrig; i++){
int iVers = ann.aOrig[i].iVers;
char *z = (char*)ann.aOrig[i].z;
int n = ann.aOrig[i].n;
struct AnnVers *p;
if( iVers<0 && !ann.bMoreToDo ) iVers = ann.nVers-1;
if( bBlame ){
if( iVers>=0 ){
p = ann.aVers + iVers;
fossil_print("%S %s %13.13s: %.*s\n",
fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z);
}else{
fossil_print("%*s %.*s\n", szHash+26, "", n, z);
}
}else{
if( iVers>=0 ){
p = ann.aVers + iVers;
fossil_print("%S %s %5d: %.*s\n",
fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z);
}else{
fossil_print("%*s %5d: %.*s\n", szHash+11, "", i+1, n, z);
}
}
}
}
|
Changes to src/printf.c.
| ︙ | ︙ | |||
59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
if( nDigitHuman > 40 ) nDigitHuman = 40;
nDigitUrl = nDigitHuman + 6;
if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL;
if( nDigitUrl > 40 ) nDigitUrl = 40;
}
return bForUrl ? nDigitUrl : nDigitHuman;
}
/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
#define etFLOAT 2 /* Floating point. %f */
| > > > > > > > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
if( nDigitHuman > 40 ) nDigitHuman = 40;
nDigitUrl = nDigitHuman + 6;
if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL;
if( nDigitUrl > 40 ) nDigitUrl = 40;
}
return bForUrl ? nDigitUrl : nDigitHuman;
}
/*
** Return the number of characters in a %S output.
*/
int length_of_S_display(void){
return hashDigits(0);
}
/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
#define etFLOAT 2 /* Floating point. %f */
|
| ︙ | ︙ |