Fossil SCM

Show gradient background colors on the web annotation screen, with deeper colors the further back in time we go.

drh 2013-05-24 21:11 UTC trunk
Commit 0b0ab858a8545edc426114640ec57ccb42172276
1 file changed +113 -89
+113 -89
--- src/diff.c
+++ src/diff.c
@@ -2191,16 +2191,24 @@
21912191
DContext c; /* The diff-engine context */
21922192
struct AnnLine { /* Lines of the original files... */
21932193
const char *z; /* The text of the line */
21942194
short int n; /* Number of bytes (omitting trailing space and \n) */
21952195
short int iLevel; /* Level at which tag was set */
2196
- const char *zSrc; /* Tag showing origin of this line */
2196
+ int iVers; /* aVers[] entry responsible for this line */
21972197
} *aOrig;
21982198
int nOrig; /* Number of elements in aOrig[] */
21992199
int nNoSrc; /* Number of entries where aOrig[].zSrc==NULL */
22002200
int iLevel; /* Current level */
22012201
int nVers; /* Number of versions analyzed */
2202
+ struct AnnVers {
2203
+ const char *zFUuid; /* File being analyzed */
2204
+ const char *zMUuid; /* Check-in containing the file */
2205
+ const char *zUser; /* User who did the check-in */
2206
+ const char *zDate; /* Date of the check-in */
2207
+ const char *zBgColor; /* Suggested background color */
2208
+ unsigned cnt; /* Number of lines contributed by this check-in */
2209
+ } *aVers; /* For each check-in analyzed */
22022210
char **azVers; /* Names of versions analyzed */
22032211
};
22042212
22052213
/*
22062214
** Initialize the annotation process by specifying the file that is
@@ -2217,11 +2225,11 @@
22172225
}
22182226
p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
22192227
for(i=0; i<p->c.nTo; i++){
22202228
p->aOrig[i].z = p->c.aTo[i].z;
22212229
p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2222
- p->aOrig[i].zSrc = 0;
2230
+ p->aOrig[i].iVers = -1;
22232231
}
22242232
p->nOrig = p->c.nTo;
22252233
return 0;
22262234
}
22272235
@@ -2230,15 +2238,13 @@
22302238
** being annotated. Do another step of the annotation. Return true
22312239
** if additional annotation is required. zPName is the tag to insert
22322240
** on each line of the file being annotated that was contributed by
22332241
** pParent. Memory to hold zPName is leaked.
22342242
*/
2235
-static int annotation_step(Annotator *p, Blob *pParent, char *zPName){
2243
+static int annotation_step(Annotator *p, Blob *pParent, int iVers){
22362244
int i, j;
22372245
int lnTo;
2238
- int iPrevLevel;
2239
- int iThisLevel;
22402246
22412247
/* Prepare the parent file to be diffed */
22422248
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
22432249
&p->c.nFrom, 1);
22442250
if( p->c.aFrom==0 ){
@@ -2248,24 +2254,22 @@
22482254
/* Compute the differences going from pParent to the file being
22492255
** annotated. */
22502256
diff_all(&p->c);
22512257
22522258
/* Where new lines are inserted on this difference, record the
2253
- ** zPName as the source of the new line.
2259
+ ** iVers as the source of the new line.
22542260
*/
2255
- iPrevLevel = p->iLevel;
22562261
p->iLevel++;
2257
- iThisLevel = p->iLevel;
22582262
for(i=lnTo=0; i<p->c.nEdit; i+=3){
2259
- struct AnnLine *x = &p->aOrig[lnTo];
2260
- for(j=0; j<p->c.aEdit[i]; j++, lnTo++, x++){
2261
- if( x->zSrc==0 || x->iLevel==iPrevLevel ){
2262
- x->zSrc = zPName;
2263
- x->iLevel = iThisLevel;
2263
+ int nCopy = p->c.aEdit[i];
2264
+ int nIns = p->c.aEdit[i+2];
2265
+ lnTo += nCopy;
2266
+ for(j=0; j<nIns; j++, lnTo++){
2267
+ if( p->aOrig[lnTo].iVers<0 ){
2268
+ p->aOrig[lnTo].iVers = iVers;
22642269
}
22652270
}
2266
- lnTo += p->c.aEdit[i+2];
22672271
}
22682272
22692273
/* Clear out the diff results */
22702274
fossil_free(p->c.aEdit);
22712275
p->c.aEdit = 0;
@@ -2278,39 +2282,10 @@
22782282
/* Return no errors */
22792283
return 0;
22802284
}
22812285
22822286
2283
-/*
2284
-** COMMAND: test-annotate-step
2285
-*/
2286
-void test_annotate_step_cmd(void){
2287
- Blob orig, b;
2288
- Annotator x;
2289
- int i;
2290
-
2291
- if( g.argc<4 ) usage("RID1 RID2 ...");
2292
- db_must_be_within_tree();
2293
- blob_zero(&b);
2294
- content_get(name_to_rid(g.argv[2]), &orig);
2295
- if( annotation_start(&x, &orig) ){
2296
- fossil_fatal("binary file");
2297
- }
2298
- for(i=3; i<g.argc; i++){
2299
- blob_zero(&b);
2300
- content_get(name_to_rid(g.argv[i]), &b);
2301
- if( annotation_step(&x, &b, g.argv[i-1]) ){
2302
- fossil_fatal("binary file");
2303
- }
2304
- }
2305
- for(i=0; i<x.nOrig; i++){
2306
- const char *zSrc = x.aOrig[i].zSrc;
2307
- if( zSrc==0 ) zSrc = g.argv[g.argc-1];
2308
- fossil_print("%10s: %.*s\n", zSrc, x.aOrig[i].n, x.aOrig[i].z);
2309
- }
2310
-}
2311
-
23122287
/* Annotation flags */
23132288
#define ANN_FILE_VERS 0x01 /* Show file vers rather than commit vers */
23142289
#define ANN_FILE_ANCEST 0x02 /* Prefer check-ins in the ANCESTOR table */
23152290
23162291
/*
@@ -2320,11 +2295,10 @@
23202295
*/
23212296
static void annotate_file(
23222297
Annotator *p, /* The annotator */
23232298
int fnid, /* The name of the file to be annotated */
23242299
int mid, /* Use the version of the file in this check-in */
2325
- int webLabel, /* Use web-style annotations if true */
23262300
int iLimit, /* Limit the number of levels if greater than zero */
23272301
int annFlags /* Flags to alter the annotation */
23282302
){
23292303
Blob toAnnotate; /* Text of the final (mid) version of the file */
23302304
Blob step; /* Text of previous revision */
@@ -2350,57 +2324,69 @@
23502324
"DELETE FROM vseen;"
23512325
);
23522326
23532327
db_prepare(&ins, "INSERT OR IGNORE INTO vseen(rid) VALUES(:rid)");
23542328
db_prepare(&q,
2355
- "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2329
+ "SELECT (SELECT uuid FROM blob WHERE rid=mlink.fid),"
2330
+ " (SELECT uuid FROM blob WHERE rid=mlink.mid),"
23562331
" date(event.mtime),"
23572332
" coalesce(event.euser,event.user),"
23582333
" mlink.pid"
23592334
" FROM mlink, event"
23602335
" WHERE mlink.fid=:rid"
23612336
" AND event.objid=mlink.mid"
23622337
" AND mlink.pid NOT IN vseen"
23632338
" ORDER BY %s event.mtime",
2364
- (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
23652339
(annFlags & ANN_FILE_ANCEST)!=0 ?
23662340
"(mlink.mid IN (SELECT rid FROM ancestor)) DESC,":""
23672341
);
23682342
23692343
db_bind_int(&q, ":rid", rid);
23702344
if( iLimit==0 ) iLimit = 1000000000;
23712345
while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2372
- const char *zUuid = db_column_text(&q, 0);
2373
- const char *zDate = db_column_text(&q, 1);
2374
- const char *zUser = db_column_text(&q, 2);
2375
- int prevId = db_column_int(&q, 3);
2376
- if( webLabel ){
2377
- zLabel = mprintf(
2378
- "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
2379
- zUuid, zUuid, zDate, zUser
2380
- );
2381
- }else{
2382
- zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
2346
+ int prevId = db_column_int(&q, 4);
2347
+ p->aVers = fossil_realloc(p->aVers, (p->nVers+1)*sizeof(p->aVers[0]));
2348
+ p->aVers[p->nVers].zFUuid = fossil_strdup(db_column_text(&q, 0));
2349
+ p->aVers[p->nVers].zMUuid = fossil_strdup(db_column_text(&q, 1));
2350
+ p->aVers[p->nVers].zDate = fossil_strdup(db_column_text(&q, 2));
2351
+ p->aVers[p->nVers].zUser = fossil_strdup(db_column_text(&q, 3));
2352
+ if( p->nVers ){
2353
+ content_get(rid, &step);
2354
+ annotation_step(p, &step, p->nVers-1);
2355
+ blob_reset(&step);
23832356
}
23842357
p->nVers++;
2385
- p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
2386
- p->azVers[p->nVers-1] = zLabel;
2387
- content_get(rid, &step);
2388
- annotation_step(p, &step, cnt==iLimit-1 ? "" : zLabel);
23892358
db_bind_int(&ins, ":rid", rid);
23902359
db_step(&ins);
23912360
db_reset(&ins);
2392
- blob_reset(&step);
23932361
db_reset(&q);
23942362
rid = prevId;
23952363
db_bind_int(&q, ":rid", prevId);
23962364
cnt++;
23972365
}
23982366
db_finalize(&q);
23992367
db_finalize(&ins);
24002368
db_end_transaction(0);
24012369
}
2370
+
2371
+/*
2372
+** Return a color from a gradient.
2373
+*/
2374
+unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){
2375
+ unsigned c; /* Result color */
2376
+ unsigned x1, x2;
2377
+ x1 = (c1>>16)&0xff;
2378
+ x2 = (c2>>16)&0xff;
2379
+ c = (x1*i + x2*(n-i))/n<<16 & 0xff0000;
2380
+ x1 = (c1>>8)&0xff;
2381
+ x2 = (c2>>8)&0xff;
2382
+ c |= (x1*i + x2*(n-i))/n<<8 & 0xff00;
2383
+ x1 = c1&0xff;
2384
+ x2 = c2&0xff;
2385
+ c |= (x1*i + x2*(n-i))/n & 0xff;
2386
+ return c;
2387
+}
24022388
24032389
/*
24042390
** WEBPAGE: annotate
24052391
**
24062392
** Query parameters:
@@ -2422,10 +2408,12 @@
24222408
int showLog = 0; /* True to display the log */
24232409
char zLn[10]; /* Line number buffer */
24242410
char zFormat[10]; /* Format string for line numbers */
24252411
Annotator ann;
24262412
HQuery url;
2413
+ struct AnnVers *p;
2414
+ unsigned clr1, clr2, clr;
24272415
24282416
showLn = atoi(PD("ln","1"));
24292417
showLog = atoi(PD("log","1"));
24302418
login_check_credentials();
24312419
if( !g.perm.Read ){ login_needed(); return; }
@@ -2471,43 +2459,71 @@
24712459
}
24722460
if( iLimit!=20 ){
24732461
style_submenu_element("20 Ancestors", "20 Ancestors",
24742462
url_render(&url, "limit", "20", 0, 0));
24752463
}
2476
- annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags);
2464
+ annotate_file(&ann, fnid, mid, iLimit, annFlags);
2465
+ if( db_get_boolean("white-foreground", 0) ){
2466
+ clr1 = 0x000000;
2467
+ clr2 = 0x0078ff;
2468
+ }else{
2469
+ clr1 = 0x007fff;
2470
+ clr2 = 0xf0f7ff;
2471
+ }
2472
+ for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2473
+ clr = gradient_color(clr1, clr2, ann.nVers-1, i);
2474
+ ann.aVers[i].zBgColor = mprintf("#%06x", clr);
2475
+ ann.aVers[i].zUser = mprintf("%h", ann.aVers[i].zUser);
2476
+ }
2477
+
24772478
if( showLog ){
2478
- int i;
24792479
@ <h2>Versions analyzed:</h2>
24802480
@ <ol>
2481
- for(i=0; i<ann.nVers; i++){
2482
- @ <li><tt>%s(ann.azVers[i])</tt></li>
2481
+ for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2482
+ @ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
2483
+ @ check-in %z(href("%R/info/%S",p->zMUuid))%.10s(p->zMUuid)</a>
2484
+ @ by %h(p->zUser)
2485
+ @ artifact %z(href("%R/artifact/%S",p->zFUuid))%.10s(p->zFUuid)</a>
2486
+ @ </span></li>
24832487
}
24842488
@ </ol>
24852489
@ <hr>
24862490
}
24872491
if( iLimit<0 ){
24882492
@ <h2>Annotation:</h2>
2493
+ iLimit = ann.nVers+10;
24892494
}else{
24902495
@ <h2>Annotation of %d(iLimit) most recent ancestors:</h2>
24912496
}
24922497
if( showLn ){
24932498
sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1);
2494
- sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd: ", strlen(zLn));
2499
+ sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd:", strlen(zLn));
24952500
}else{
24962501
zLn[0] = 0;
24972502
}
24982503
@ <pre>
24992504
for(i=0; i<ann.nOrig; i++){
2500
- const char *zSrc = ann.aOrig[i].zSrc;
2501
- const char *zSep = ":";
2502
- if( zSrc[0]==0 ){
2503
- zSrc = " ";
2504
- zSep = " ";
2505
- }
2506
- ((char*)ann.aOrig[i].z)[ann.aOrig[i].n] = 0;
2507
- if( showLn ) sqlite3_snprintf(sizeof(zLn), zLn, zFormat, i+1);
2508
- @ %s(zSrc)%s(zSep)%s(zLn) %h(ann.aOrig[i].z)
2505
+ struct AnnVers *p;
2506
+ int iVers = ann.aOrig[i].iVers;
2507
+ char *z = (char*)ann.aOrig[i].z;
2508
+ int n = ann.aOrig[i].n;
2509
+ char zPrefix[300];
2510
+ z[n] = 0;
2511
+ if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2512
+ if( iVers>=0 ){
2513
+ struct AnnVers *p = ann.aVers+iVers;
2514
+ char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2515
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2516
+ "<span style='background-color:%s'>"
2517
+ "%s%.10s</a> %s %13.13s</span> %4d:",
2518
+ p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser, i+1);
2519
+ fossil_free(zLink);
2520
+ }else{
2521
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%4d: ", "", i+1);
2522
+ }
2523
+ @ %s(zPrefix) %h(z)
2524
+
25092525
}
25102526
@ </pre>
25112527
style_footer();
25122528
}
25132529
@@ -2571,28 +2587,36 @@
25712587
" ORDER BY ancestor.generation ASC LIMIT 1",
25722588
fid, fnid);
25732589
if( mid==0 ){
25742590
fossil_panic("unable to find manifest");
25752591
}
2576
- if( fileVers ) annFlags |= ANN_FILE_VERS;
25772592
annFlags |= ANN_FILE_ANCEST;
2578
- annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
2593
+ annotate_file(&ann, fnid, mid, iLimit, annFlags);
25792594
if( showLog ){
2580
- for(i=0; i<ann.nVers; i++){
2581
- printf("version %3d: %s\n", i+1, ann.azVers[i]);
2595
+ struct AnnVers *p;
2596
+ for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2597
+ fossil_print("version %3d: %s %.10s by %-13s file %.10s\n",
2598
+ i+1, p->zDate, p->zMUuid, p->zUser, p->zFUuid);
25822599
}
2583
- printf("---------------------------------------------------\n");
2600
+ fossil_print("---------------------------------------------------\n");
25842601
}
25852602
for(i=0; i<ann.nOrig; i++){
2586
- const char *zSrc = ann.aOrig[i].zSrc;
2587
- const char *zSep = ":";
2588
- if( zSrc[0]==0 ){
2589
- zSrc = "";
2590
- zSep = " ";
2591
- }
2592
- fossil_print("%36s%s%.*s\n",
2593
- zSrc, zSep, ann.aOrig[i].n, ann.aOrig[i].z);
2603
+ struct AnnVers *p;
2604
+ int iVers = ann.aOrig[i].iVers;
2605
+ char *z = (char*)ann.aOrig[i].z;
2606
+ int n = ann.aOrig[i].n;
2607
+ char zPrefix[200];
2608
+ z[n] = 0;
2609
+ if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2610
+ if( iVers>=0 ){
2611
+ struct AnnVers *p = ann.aVers+iVers;
2612
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%.10s %s %13.13s",
2613
+ fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser);
2614
+ }else{
2615
+ zPrefix[0] = 0;
2616
+ }
2617
+ fossil_print("%35s %4d: %.*s\n", zPrefix, i+1, n, z);
25942618
}
25952619
}
25962620
25972621
/*
25982622
** COMMAND: test-looks-like-utf
25992623
--- src/diff.c
+++ src/diff.c
@@ -2191,16 +2191,24 @@
2191 DContext c; /* The diff-engine context */
2192 struct AnnLine { /* Lines of the original files... */
2193 const char *z; /* The text of the line */
2194 short int n; /* Number of bytes (omitting trailing space and \n) */
2195 short int iLevel; /* Level at which tag was set */
2196 const char *zSrc; /* Tag showing origin of this line */
2197 } *aOrig;
2198 int nOrig; /* Number of elements in aOrig[] */
2199 int nNoSrc; /* Number of entries where aOrig[].zSrc==NULL */
2200 int iLevel; /* Current level */
2201 int nVers; /* Number of versions analyzed */
 
 
 
 
 
 
 
 
2202 char **azVers; /* Names of versions analyzed */
2203 };
2204
2205 /*
2206 ** Initialize the annotation process by specifying the file that is
@@ -2217,11 +2225,11 @@
2217 }
2218 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
2219 for(i=0; i<p->c.nTo; i++){
2220 p->aOrig[i].z = p->c.aTo[i].z;
2221 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2222 p->aOrig[i].zSrc = 0;
2223 }
2224 p->nOrig = p->c.nTo;
2225 return 0;
2226 }
2227
@@ -2230,15 +2238,13 @@
2230 ** being annotated. Do another step of the annotation. Return true
2231 ** if additional annotation is required. zPName is the tag to insert
2232 ** on each line of the file being annotated that was contributed by
2233 ** pParent. Memory to hold zPName is leaked.
2234 */
2235 static int annotation_step(Annotator *p, Blob *pParent, char *zPName){
2236 int i, j;
2237 int lnTo;
2238 int iPrevLevel;
2239 int iThisLevel;
2240
2241 /* Prepare the parent file to be diffed */
2242 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
2243 &p->c.nFrom, 1);
2244 if( p->c.aFrom==0 ){
@@ -2248,24 +2254,22 @@
2248 /* Compute the differences going from pParent to the file being
2249 ** annotated. */
2250 diff_all(&p->c);
2251
2252 /* Where new lines are inserted on this difference, record the
2253 ** zPName as the source of the new line.
2254 */
2255 iPrevLevel = p->iLevel;
2256 p->iLevel++;
2257 iThisLevel = p->iLevel;
2258 for(i=lnTo=0; i<p->c.nEdit; i+=3){
2259 struct AnnLine *x = &p->aOrig[lnTo];
2260 for(j=0; j<p->c.aEdit[i]; j++, lnTo++, x++){
2261 if( x->zSrc==0 || x->iLevel==iPrevLevel ){
2262 x->zSrc = zPName;
2263 x->iLevel = iThisLevel;
 
2264 }
2265 }
2266 lnTo += p->c.aEdit[i+2];
2267 }
2268
2269 /* Clear out the diff results */
2270 fossil_free(p->c.aEdit);
2271 p->c.aEdit = 0;
@@ -2278,39 +2282,10 @@
2278 /* Return no errors */
2279 return 0;
2280 }
2281
2282
2283 /*
2284 ** COMMAND: test-annotate-step
2285 */
2286 void test_annotate_step_cmd(void){
2287 Blob orig, b;
2288 Annotator x;
2289 int i;
2290
2291 if( g.argc<4 ) usage("RID1 RID2 ...");
2292 db_must_be_within_tree();
2293 blob_zero(&b);
2294 content_get(name_to_rid(g.argv[2]), &orig);
2295 if( annotation_start(&x, &orig) ){
2296 fossil_fatal("binary file");
2297 }
2298 for(i=3; i<g.argc; i++){
2299 blob_zero(&b);
2300 content_get(name_to_rid(g.argv[i]), &b);
2301 if( annotation_step(&x, &b, g.argv[i-1]) ){
2302 fossil_fatal("binary file");
2303 }
2304 }
2305 for(i=0; i<x.nOrig; i++){
2306 const char *zSrc = x.aOrig[i].zSrc;
2307 if( zSrc==0 ) zSrc = g.argv[g.argc-1];
2308 fossil_print("%10s: %.*s\n", zSrc, x.aOrig[i].n, x.aOrig[i].z);
2309 }
2310 }
2311
2312 /* Annotation flags */
2313 #define ANN_FILE_VERS 0x01 /* Show file vers rather than commit vers */
2314 #define ANN_FILE_ANCEST 0x02 /* Prefer check-ins in the ANCESTOR table */
2315
2316 /*
@@ -2320,11 +2295,10 @@
2320 */
2321 static void annotate_file(
2322 Annotator *p, /* The annotator */
2323 int fnid, /* The name of the file to be annotated */
2324 int mid, /* Use the version of the file in this check-in */
2325 int webLabel, /* Use web-style annotations if true */
2326 int iLimit, /* Limit the number of levels if greater than zero */
2327 int annFlags /* Flags to alter the annotation */
2328 ){
2329 Blob toAnnotate; /* Text of the final (mid) version of the file */
2330 Blob step; /* Text of previous revision */
@@ -2350,57 +2324,69 @@
2350 "DELETE FROM vseen;"
2351 );
2352
2353 db_prepare(&ins, "INSERT OR IGNORE INTO vseen(rid) VALUES(:rid)");
2354 db_prepare(&q,
2355 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
 
2356 " date(event.mtime),"
2357 " coalesce(event.euser,event.user),"
2358 " mlink.pid"
2359 " FROM mlink, event"
2360 " WHERE mlink.fid=:rid"
2361 " AND event.objid=mlink.mid"
2362 " AND mlink.pid NOT IN vseen"
2363 " ORDER BY %s event.mtime",
2364 (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
2365 (annFlags & ANN_FILE_ANCEST)!=0 ?
2366 "(mlink.mid IN (SELECT rid FROM ancestor)) DESC,":""
2367 );
2368
2369 db_bind_int(&q, ":rid", rid);
2370 if( iLimit==0 ) iLimit = 1000000000;
2371 while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2372 const char *zUuid = db_column_text(&q, 0);
2373 const char *zDate = db_column_text(&q, 1);
2374 const char *zUser = db_column_text(&q, 2);
2375 int prevId = db_column_int(&q, 3);
2376 if( webLabel ){
2377 zLabel = mprintf(
2378 "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
2379 zUuid, zUuid, zDate, zUser
2380 );
2381 }else{
2382 zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
2383 }
2384 p->nVers++;
2385 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
2386 p->azVers[p->nVers-1] = zLabel;
2387 content_get(rid, &step);
2388 annotation_step(p, &step, cnt==iLimit-1 ? "" : zLabel);
2389 db_bind_int(&ins, ":rid", rid);
2390 db_step(&ins);
2391 db_reset(&ins);
2392 blob_reset(&step);
2393 db_reset(&q);
2394 rid = prevId;
2395 db_bind_int(&q, ":rid", prevId);
2396 cnt++;
2397 }
2398 db_finalize(&q);
2399 db_finalize(&ins);
2400 db_end_transaction(0);
2401 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2402
2403 /*
2404 ** WEBPAGE: annotate
2405 **
2406 ** Query parameters:
@@ -2422,10 +2408,12 @@
2422 int showLog = 0; /* True to display the log */
2423 char zLn[10]; /* Line number buffer */
2424 char zFormat[10]; /* Format string for line numbers */
2425 Annotator ann;
2426 HQuery url;
 
 
2427
2428 showLn = atoi(PD("ln","1"));
2429 showLog = atoi(PD("log","1"));
2430 login_check_credentials();
2431 if( !g.perm.Read ){ login_needed(); return; }
@@ -2471,43 +2459,71 @@
2471 }
2472 if( iLimit!=20 ){
2473 style_submenu_element("20 Ancestors", "20 Ancestors",
2474 url_render(&url, "limit", "20", 0, 0));
2475 }
2476 annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags);
 
 
 
 
 
 
 
 
 
 
 
 
 
2477 if( showLog ){
2478 int i;
2479 @ <h2>Versions analyzed:</h2>
2480 @ <ol>
2481 for(i=0; i<ann.nVers; i++){
2482 @ <li><tt>%s(ann.azVers[i])</tt></li>
 
 
 
 
2483 }
2484 @ </ol>
2485 @ <hr>
2486 }
2487 if( iLimit<0 ){
2488 @ <h2>Annotation:</h2>
 
2489 }else{
2490 @ <h2>Annotation of %d(iLimit) most recent ancestors:</h2>
2491 }
2492 if( showLn ){
2493 sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1);
2494 sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd: ", strlen(zLn));
2495 }else{
2496 zLn[0] = 0;
2497 }
2498 @ <pre>
2499 for(i=0; i<ann.nOrig; i++){
2500 const char *zSrc = ann.aOrig[i].zSrc;
2501 const char *zSep = ":";
2502 if( zSrc[0]==0 ){
2503 zSrc = " ";
2504 zSep = " ";
2505 }
2506 ((char*)ann.aOrig[i].z)[ann.aOrig[i].n] = 0;
2507 if( showLn ) sqlite3_snprintf(sizeof(zLn), zLn, zFormat, i+1);
2508 @ %s(zSrc)%s(zSep)%s(zLn) %h(ann.aOrig[i].z)
 
 
 
 
 
 
 
 
 
 
 
2509 }
2510 @ </pre>
2511 style_footer();
2512 }
2513
@@ -2571,28 +2587,36 @@
2571 " ORDER BY ancestor.generation ASC LIMIT 1",
2572 fid, fnid);
2573 if( mid==0 ){
2574 fossil_panic("unable to find manifest");
2575 }
2576 if( fileVers ) annFlags |= ANN_FILE_VERS;
2577 annFlags |= ANN_FILE_ANCEST;
2578 annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
2579 if( showLog ){
2580 for(i=0; i<ann.nVers; i++){
2581 printf("version %3d: %s\n", i+1, ann.azVers[i]);
 
 
2582 }
2583 printf("---------------------------------------------------\n");
2584 }
2585 for(i=0; i<ann.nOrig; i++){
2586 const char *zSrc = ann.aOrig[i].zSrc;
2587 const char *zSep = ":";
2588 if( zSrc[0]==0 ){
2589 zSrc = "";
2590 zSep = " ";
2591 }
2592 fossil_print("%36s%s%.*s\n",
2593 zSrc, zSep, ann.aOrig[i].n, ann.aOrig[i].z);
 
 
 
 
 
 
 
2594 }
2595 }
2596
2597 /*
2598 ** COMMAND: test-looks-like-utf
2599
--- src/diff.c
+++ src/diff.c
@@ -2191,16 +2191,24 @@
2191 DContext c; /* The diff-engine context */
2192 struct AnnLine { /* Lines of the original files... */
2193 const char *z; /* The text of the line */
2194 short int n; /* Number of bytes (omitting trailing space and \n) */
2195 short int iLevel; /* Level at which tag was set */
2196 int iVers; /* aVers[] entry responsible for this line */
2197 } *aOrig;
2198 int nOrig; /* Number of elements in aOrig[] */
2199 int nNoSrc; /* Number of entries where aOrig[].zSrc==NULL */
2200 int iLevel; /* Current level */
2201 int nVers; /* Number of versions analyzed */
2202 struct AnnVers {
2203 const char *zFUuid; /* File being analyzed */
2204 const char *zMUuid; /* Check-in containing the file */
2205 const char *zUser; /* User who did the check-in */
2206 const char *zDate; /* Date of the check-in */
2207 const char *zBgColor; /* Suggested background color */
2208 unsigned cnt; /* Number of lines contributed by this check-in */
2209 } *aVers; /* For each check-in analyzed */
2210 char **azVers; /* Names of versions analyzed */
2211 };
2212
2213 /*
2214 ** Initialize the annotation process by specifying the file that is
@@ -2217,11 +2225,11 @@
2225 }
2226 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
2227 for(i=0; i<p->c.nTo; i++){
2228 p->aOrig[i].z = p->c.aTo[i].z;
2229 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2230 p->aOrig[i].iVers = -1;
2231 }
2232 p->nOrig = p->c.nTo;
2233 return 0;
2234 }
2235
@@ -2230,15 +2238,13 @@
2238 ** being annotated. Do another step of the annotation. Return true
2239 ** if additional annotation is required. zPName is the tag to insert
2240 ** on each line of the file being annotated that was contributed by
2241 ** pParent. Memory to hold zPName is leaked.
2242 */
2243 static int annotation_step(Annotator *p, Blob *pParent, int iVers){
2244 int i, j;
2245 int lnTo;
 
 
2246
2247 /* Prepare the parent file to be diffed */
2248 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
2249 &p->c.nFrom, 1);
2250 if( p->c.aFrom==0 ){
@@ -2248,24 +2254,22 @@
2254 /* Compute the differences going from pParent to the file being
2255 ** annotated. */
2256 diff_all(&p->c);
2257
2258 /* Where new lines are inserted on this difference, record the
2259 ** iVers as the source of the new line.
2260 */
 
2261 p->iLevel++;
 
2262 for(i=lnTo=0; i<p->c.nEdit; i+=3){
2263 int nCopy = p->c.aEdit[i];
2264 int nIns = p->c.aEdit[i+2];
2265 lnTo += nCopy;
2266 for(j=0; j<nIns; j++, lnTo++){
2267 if( p->aOrig[lnTo].iVers<0 ){
2268 p->aOrig[lnTo].iVers = iVers;
2269 }
2270 }
 
2271 }
2272
2273 /* Clear out the diff results */
2274 fossil_free(p->c.aEdit);
2275 p->c.aEdit = 0;
@@ -2278,39 +2282,10 @@
2282 /* Return no errors */
2283 return 0;
2284 }
2285
2286
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2287 /* Annotation flags */
2288 #define ANN_FILE_VERS 0x01 /* Show file vers rather than commit vers */
2289 #define ANN_FILE_ANCEST 0x02 /* Prefer check-ins in the ANCESTOR table */
2290
2291 /*
@@ -2320,11 +2295,10 @@
2295 */
2296 static void annotate_file(
2297 Annotator *p, /* The annotator */
2298 int fnid, /* The name of the file to be annotated */
2299 int mid, /* Use the version of the file in this check-in */
 
2300 int iLimit, /* Limit the number of levels if greater than zero */
2301 int annFlags /* Flags to alter the annotation */
2302 ){
2303 Blob toAnnotate; /* Text of the final (mid) version of the file */
2304 Blob step; /* Text of previous revision */
@@ -2350,57 +2324,69 @@
2324 "DELETE FROM vseen;"
2325 );
2326
2327 db_prepare(&ins, "INSERT OR IGNORE INTO vseen(rid) VALUES(:rid)");
2328 db_prepare(&q,
2329 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.fid),"
2330 " (SELECT uuid FROM blob WHERE rid=mlink.mid),"
2331 " date(event.mtime),"
2332 " coalesce(event.euser,event.user),"
2333 " mlink.pid"
2334 " FROM mlink, event"
2335 " WHERE mlink.fid=:rid"
2336 " AND event.objid=mlink.mid"
2337 " AND mlink.pid NOT IN vseen"
2338 " ORDER BY %s event.mtime",
 
2339 (annFlags & ANN_FILE_ANCEST)!=0 ?
2340 "(mlink.mid IN (SELECT rid FROM ancestor)) DESC,":""
2341 );
2342
2343 db_bind_int(&q, ":rid", rid);
2344 if( iLimit==0 ) iLimit = 1000000000;
2345 while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2346 int prevId = db_column_int(&q, 4);
2347 p->aVers = fossil_realloc(p->aVers, (p->nVers+1)*sizeof(p->aVers[0]));
2348 p->aVers[p->nVers].zFUuid = fossil_strdup(db_column_text(&q, 0));
2349 p->aVers[p->nVers].zMUuid = fossil_strdup(db_column_text(&q, 1));
2350 p->aVers[p->nVers].zDate = fossil_strdup(db_column_text(&q, 2));
2351 p->aVers[p->nVers].zUser = fossil_strdup(db_column_text(&q, 3));
2352 if( p->nVers ){
2353 content_get(rid, &step);
2354 annotation_step(p, &step, p->nVers-1);
2355 blob_reset(&step);
 
2356 }
2357 p->nVers++;
 
 
 
 
2358 db_bind_int(&ins, ":rid", rid);
2359 db_step(&ins);
2360 db_reset(&ins);
 
2361 db_reset(&q);
2362 rid = prevId;
2363 db_bind_int(&q, ":rid", prevId);
2364 cnt++;
2365 }
2366 db_finalize(&q);
2367 db_finalize(&ins);
2368 db_end_transaction(0);
2369 }
2370
2371 /*
2372 ** Return a color from a gradient.
2373 */
2374 unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){
2375 unsigned c; /* Result color */
2376 unsigned x1, x2;
2377 x1 = (c1>>16)&0xff;
2378 x2 = (c2>>16)&0xff;
2379 c = (x1*i + x2*(n-i))/n<<16 & 0xff0000;
2380 x1 = (c1>>8)&0xff;
2381 x2 = (c2>>8)&0xff;
2382 c |= (x1*i + x2*(n-i))/n<<8 & 0xff00;
2383 x1 = c1&0xff;
2384 x2 = c2&0xff;
2385 c |= (x1*i + x2*(n-i))/n & 0xff;
2386 return c;
2387 }
2388
2389 /*
2390 ** WEBPAGE: annotate
2391 **
2392 ** Query parameters:
@@ -2422,10 +2408,12 @@
2408 int showLog = 0; /* True to display the log */
2409 char zLn[10]; /* Line number buffer */
2410 char zFormat[10]; /* Format string for line numbers */
2411 Annotator ann;
2412 HQuery url;
2413 struct AnnVers *p;
2414 unsigned clr1, clr2, clr;
2415
2416 showLn = atoi(PD("ln","1"));
2417 showLog = atoi(PD("log","1"));
2418 login_check_credentials();
2419 if( !g.perm.Read ){ login_needed(); return; }
@@ -2471,43 +2459,71 @@
2459 }
2460 if( iLimit!=20 ){
2461 style_submenu_element("20 Ancestors", "20 Ancestors",
2462 url_render(&url, "limit", "20", 0, 0));
2463 }
2464 annotate_file(&ann, fnid, mid, iLimit, annFlags);
2465 if( db_get_boolean("white-foreground", 0) ){
2466 clr1 = 0x000000;
2467 clr2 = 0x0078ff;
2468 }else{
2469 clr1 = 0x007fff;
2470 clr2 = 0xf0f7ff;
2471 }
2472 for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2473 clr = gradient_color(clr1, clr2, ann.nVers-1, i);
2474 ann.aVers[i].zBgColor = mprintf("#%06x", clr);
2475 ann.aVers[i].zUser = mprintf("%h", ann.aVers[i].zUser);
2476 }
2477
2478 if( showLog ){
 
2479 @ <h2>Versions analyzed:</h2>
2480 @ <ol>
2481 for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2482 @ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
2483 @ check-in %z(href("%R/info/%S",p->zMUuid))%.10s(p->zMUuid)</a>
2484 @ by %h(p->zUser)
2485 @ artifact %z(href("%R/artifact/%S",p->zFUuid))%.10s(p->zFUuid)</a>
2486 @ </span></li>
2487 }
2488 @ </ol>
2489 @ <hr>
2490 }
2491 if( iLimit<0 ){
2492 @ <h2>Annotation:</h2>
2493 iLimit = ann.nVers+10;
2494 }else{
2495 @ <h2>Annotation of %d(iLimit) most recent ancestors:</h2>
2496 }
2497 if( showLn ){
2498 sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1);
2499 sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd:", strlen(zLn));
2500 }else{
2501 zLn[0] = 0;
2502 }
2503 @ <pre>
2504 for(i=0; i<ann.nOrig; i++){
2505 struct AnnVers *p;
2506 int iVers = ann.aOrig[i].iVers;
2507 char *z = (char*)ann.aOrig[i].z;
2508 int n = ann.aOrig[i].n;
2509 char zPrefix[300];
2510 z[n] = 0;
2511 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2512 if( iVers>=0 ){
2513 struct AnnVers *p = ann.aVers+iVers;
2514 char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2515 sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2516 "<span style='background-color:%s'>"
2517 "%s%.10s</a> %s %13.13s</span> %4d:",
2518 p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser, i+1);
2519 fossil_free(zLink);
2520 }else{
2521 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%4d: ", "", i+1);
2522 }
2523 @ %s(zPrefix) %h(z)
2524
2525 }
2526 @ </pre>
2527 style_footer();
2528 }
2529
@@ -2571,28 +2587,36 @@
2587 " ORDER BY ancestor.generation ASC LIMIT 1",
2588 fid, fnid);
2589 if( mid==0 ){
2590 fossil_panic("unable to find manifest");
2591 }
 
2592 annFlags |= ANN_FILE_ANCEST;
2593 annotate_file(&ann, fnid, mid, iLimit, annFlags);
2594 if( showLog ){
2595 struct AnnVers *p;
2596 for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2597 fossil_print("version %3d: %s %.10s by %-13s file %.10s\n",
2598 i+1, p->zDate, p->zMUuid, p->zUser, p->zFUuid);
2599 }
2600 fossil_print("---------------------------------------------------\n");
2601 }
2602 for(i=0; i<ann.nOrig; i++){
2603 struct AnnVers *p;
2604 int iVers = ann.aOrig[i].iVers;
2605 char *z = (char*)ann.aOrig[i].z;
2606 int n = ann.aOrig[i].n;
2607 char zPrefix[200];
2608 z[n] = 0;
2609 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2610 if( iVers>=0 ){
2611 struct AnnVers *p = ann.aVers+iVers;
2612 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%.10s %s %13.13s",
2613 fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser);
2614 }else{
2615 zPrefix[0] = 0;
2616 }
2617 fossil_print("%35s %4d: %.*s\n", zPrefix, i+1, n, z);
2618 }
2619 }
2620
2621 /*
2622 ** COMMAND: test-looks-like-utf
2623

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button