Fossil SCM

Improvements to annotate. Faster. More correct. Added gradient background colors to the web view. Default to showing line numbers. Do not show the check-in user. Fix for ticket [58ce2f221ae2e64ed].

drh 2013-05-25 01:52 trunk merge
Commit 89bf6f13f9c407c448b6a20b79fe9f0a4192f4e6
1 file changed +135 -97
+135 -97
--- src/diff.c
+++ src/diff.c
@@ -1653,23 +1653,26 @@
16531653
int iS1, int iE1, /* Range of lines in p->aFrom[] */
16541654
int iS2, int iE2, /* Range of lines in p->aTo[] */
16551655
int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
16561656
int *piSY, int *piEY /* Write p->aTo[] common segment here */
16571657
){
1658
- double bestScore = -1e30; /* Best score seen so far */
16591658
int i, j, k; /* Loop counters */
16601659
int n; /* Loop limit */
16611660
DLine *pA, *pB; /* Pointers to lines */
16621661
int iSX, iSY, iEX, iEY; /* Current match */
1663
- double score; /* Current score */
1664
- int skew; /* How lopsided is the match */
1665
- int dist; /* Distance of match from center */
1662
+ int skew = 0; /* How lopsided is the match */
1663
+ int dist = 0; /* Distance of match from center */
16661664
int mid; /* Center of the span */
16671665
int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
16681666
int iSXp, iSYp, iEXp, iEYp; /* Previous match */
1669
-
1667
+ sqlite3_int64 bestScore; /* Best score so far */
1668
+ sqlite3_int64 score; /* Score for current candidate LCS */
1669
+ int span; /* combined width of the input sequences */
16701670
1671
+ span = (iE1 - iS1) + (iE2 - iS2);
1672
+ bestScore = -10000;
1673
+ score = 0;
16711674
iSXb = iSXp = iS1;
16721675
iEXb = iEXp = iS1;
16731676
iSYb = iSYp = iS2;
16741677
iEYb = iEYp = iS2;
16751678
mid = (iE1 + iS1)/2;
@@ -1707,11 +1710,11 @@
17071710
iEY += k;
17081711
skew = (iSX-iS1) - (iSY-iS2);
17091712
if( skew<0 ) skew = -skew;
17101713
dist = (iSX+iEX)/2 - mid;
17111714
if( dist<0 ) dist = -dist;
1712
- score = (iEX - iSX) - 0.05*skew - 0.05*dist;
1715
+ score = (iEX - iSX)*(sqlite3_int64)span - (skew + dist);
17131716
if( score>bestScore ){
17141717
bestScore = score;
17151718
iSXb = iSX;
17161719
iSYb = iSY;
17171720
iEXb = iEX;
@@ -1731,12 +1734,10 @@
17311734
*piSX = iSXb;
17321735
*piSY = iSYb;
17331736
*piEX = iEXb;
17341737
*piEY = iEYb;
17351738
}
1736
- /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
1737
- iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */
17381739
}
17391740
17401741
/*
17411742
** Expand the size of aEdit[] array to hold at least nEdit elements.
17421743
*/
@@ -2191,16 +2192,24 @@
21912192
DContext c; /* The diff-engine context */
21922193
struct AnnLine { /* Lines of the original files... */
21932194
const char *z; /* The text of the line */
21942195
short int n; /* Number of bytes (omitting trailing space and \n) */
21952196
short int iLevel; /* Level at which tag was set */
2196
- const char *zSrc; /* Tag showing origin of this line */
2197
+ int iVers; /* aVers[] entry responsible for this line */
21972198
} *aOrig;
21982199
int nOrig; /* Number of elements in aOrig[] */
21992200
int nNoSrc; /* Number of entries where aOrig[].zSrc==NULL */
22002201
int iLevel; /* Current level */
22012202
int nVers; /* Number of versions analyzed */
2203
+ struct AnnVers {
2204
+ const char *zFUuid; /* File being analyzed */
2205
+ const char *zMUuid; /* Check-in containing the file */
2206
+ const char *zUser; /* User who did the check-in */
2207
+ const char *zDate; /* Date of the check-in */
2208
+ const char *zBgColor; /* Suggested background color */
2209
+ unsigned cnt; /* Number of lines contributed by this check-in */
2210
+ } *aVers; /* For each check-in analyzed */
22022211
char **azVers; /* Names of versions analyzed */
22032212
};
22042213
22052214
/*
22062215
** Initialize the annotation process by specifying the file that is
@@ -2217,11 +2226,11 @@
22172226
}
22182227
p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
22192228
for(i=0; i<p->c.nTo; i++){
22202229
p->aOrig[i].z = p->c.aTo[i].z;
22212230
p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2222
- p->aOrig[i].zSrc = 0;
2231
+ p->aOrig[i].iVers = -1;
22232232
}
22242233
p->nOrig = p->c.nTo;
22252234
return 0;
22262235
}
22272236
@@ -2230,15 +2239,13 @@
22302239
** being annotated. Do another step of the annotation. Return true
22312240
** if additional annotation is required. zPName is the tag to insert
22322241
** on each line of the file being annotated that was contributed by
22332242
** pParent. Memory to hold zPName is leaked.
22342243
*/
2235
-static int annotation_step(Annotator *p, Blob *pParent, char *zPName){
2244
+static int annotation_step(Annotator *p, Blob *pParent, int iVers){
22362245
int i, j;
22372246
int lnTo;
2238
- int iPrevLevel;
2239
- int iThisLevel;
22402247
22412248
/* Prepare the parent file to be diffed */
22422249
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
22432250
&p->c.nFrom, 1);
22442251
if( p->c.aFrom==0 ){
@@ -2248,24 +2255,22 @@
22482255
/* Compute the differences going from pParent to the file being
22492256
** annotated. */
22502257
diff_all(&p->c);
22512258
22522259
/* Where new lines are inserted on this difference, record the
2253
- ** zPName as the source of the new line.
2260
+ ** iVers as the source of the new line.
22542261
*/
2255
- iPrevLevel = p->iLevel;
22562262
p->iLevel++;
2257
- iThisLevel = p->iLevel;
22582263
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
+ int nCopy = p->c.aEdit[i];
2265
+ int nIns = p->c.aEdit[i+2];
2266
+ lnTo += nCopy;
2267
+ for(j=0; j<nIns; j++, lnTo++){
2268
+ if( p->aOrig[lnTo].iVers<0 ){
2269
+ p->aOrig[lnTo].iVers = iVers;
22642270
}
22652271
}
2266
- lnTo += p->c.aEdit[i+2];
22672272
}
22682273
22692274
/* Clear out the diff results */
22702275
fossil_free(p->c.aEdit);
22712276
p->c.aEdit = 0;
@@ -2278,39 +2283,10 @@
22782283
/* Return no errors */
22792284
return 0;
22802285
}
22812286
22822287
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
-
23122288
/* Annotation flags */
23132289
#define ANN_FILE_VERS 0x01 /* Show file vers rather than commit vers */
23142290
#define ANN_FILE_ANCEST 0x02 /* Prefer check-ins in the ANCESTOR table */
23152291
23162292
/*
@@ -2320,11 +2296,10 @@
23202296
*/
23212297
static void annotate_file(
23222298
Annotator *p, /* The annotator */
23232299
int fnid, /* The name of the file to be annotated */
23242300
int mid, /* Use the version of the file in this check-in */
2325
- int webLabel, /* Use web-style annotations if true */
23262301
int iLimit, /* Limit the number of levels if greater than zero */
23272302
int annFlags /* Flags to alter the annotation */
23282303
){
23292304
Blob toAnnotate; /* Text of the final (mid) version of the file */
23302305
Blob step; /* Text of previous revision */
@@ -2350,57 +2325,69 @@
23502325
"DELETE FROM vseen;"
23512326
);
23522327
23532328
db_prepare(&ins, "INSERT OR IGNORE INTO vseen(rid) VALUES(:rid)");
23542329
db_prepare(&q,
2355
- "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
2330
+ "SELECT (SELECT uuid FROM blob WHERE rid=mlink.fid),"
2331
+ " (SELECT uuid FROM blob WHERE rid=mlink.mid),"
23562332
" date(event.mtime),"
23572333
" coalesce(event.euser,event.user),"
23582334
" mlink.pid"
23592335
" FROM mlink, event"
23602336
" WHERE mlink.fid=:rid"
23612337
" AND event.objid=mlink.mid"
23622338
" AND mlink.pid NOT IN vseen"
23632339
" ORDER BY %s event.mtime",
2364
- (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid",
23652340
(annFlags & ANN_FILE_ANCEST)!=0 ?
23662341
"(mlink.mid IN (SELECT rid FROM ancestor)) DESC,":""
23672342
);
23682343
23692344
db_bind_int(&q, ":rid", rid);
23702345
if( iLimit==0 ) iLimit = 1000000000;
23712346
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);
2347
+ int prevId = db_column_int(&q, 4);
2348
+ p->aVers = fossil_realloc(p->aVers, (p->nVers+1)*sizeof(p->aVers[0]));
2349
+ p->aVers[p->nVers].zFUuid = fossil_strdup(db_column_text(&q, 0));
2350
+ p->aVers[p->nVers].zMUuid = fossil_strdup(db_column_text(&q, 1));
2351
+ p->aVers[p->nVers].zDate = fossil_strdup(db_column_text(&q, 2));
2352
+ p->aVers[p->nVers].zUser = fossil_strdup(db_column_text(&q, 3));
2353
+ if( p->nVers ){
2354
+ content_get(rid, &step);
2355
+ annotation_step(p, &step, p->nVers-1);
2356
+ blob_reset(&step);
23832357
}
23842358
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);
23892359
db_bind_int(&ins, ":rid", rid);
23902360
db_step(&ins);
23912361
db_reset(&ins);
2392
- blob_reset(&step);
23932362
db_reset(&q);
23942363
rid = prevId;
23952364
db_bind_int(&q, ":rid", prevId);
23962365
cnt++;
23972366
}
23982367
db_finalize(&q);
23992368
db_finalize(&ins);
24002369
db_end_transaction(0);
24012370
}
2371
+
2372
+/*
2373
+** Return a color from a gradient.
2374
+*/
2375
+unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){
2376
+ unsigned c; /* Result color */
2377
+ unsigned x1, x2;
2378
+ x1 = (c1>>16)&0xff;
2379
+ x2 = (c2>>16)&0xff;
2380
+ c = (x1*(n-i) + x2*i)/n<<16 & 0xff0000;
2381
+ x1 = (c1>>8)&0xff;
2382
+ x2 = (c2>>8)&0xff;
2383
+ c |= (x1*(n-i) + x2*i)/n<<8 & 0xff00;
2384
+ x1 = c1&0xff;
2385
+ x2 = c2&0xff;
2386
+ c |= (x1*(n-i) + x2*i)/n & 0xff;
2387
+ return c;
2388
+}
24022389
24032390
/*
24042391
** WEBPAGE: annotate
24052392
**
24062393
** Query parameters:
@@ -2422,10 +2409,12 @@
24222409
int showLog = 0; /* True to display the log */
24232410
char zLn[10]; /* Line number buffer */
24242411
char zFormat[10]; /* Format string for line numbers */
24252412
Annotator ann;
24262413
HQuery url;
2414
+ struct AnnVers *p;
2415
+ unsigned clr1, clr2, clr;
24272416
24282417
showLn = atoi(PD("ln","1"));
24292418
showLog = atoi(PD("log","1"));
24302419
login_check_credentials();
24312420
if( !g.perm.Read ){ login_needed(); return; }
@@ -2471,43 +2460,84 @@
24712460
}
24722461
if( iLimit!=20 ){
24732462
style_submenu_element("20 Ancestors", "20 Ancestors",
24742463
url_render(&url, "limit", "20", 0, 0));
24752464
}
2476
- annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags);
2465
+ annotate_file(&ann, fnid, mid, iLimit, annFlags);
2466
+ if( db_get_boolean("white-foreground", 0) ){
2467
+ clr1 = 0xa04040;
2468
+ clr2 = 0x4059a0;
2469
+ }else{
2470
+ clr1 = 0xffb5b5; /* Recent changes: red (hot) */
2471
+ clr2 = 0xb5e0ff; /* Older changes: blue (cold) */
2472
+ }
2473
+ for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2474
+ clr = gradient_color(clr1, clr2, ann.nVers-1, i);
2475
+ ann.aVers[i].zBgColor = mprintf("#%06x", clr);
2476
+ ann.aVers[i].zUser = mprintf("%h", ann.aVers[i].zUser);
2477
+ }
2478
+
24772479
if( showLog ){
2478
- int i;
24792480
@ <h2>Versions analyzed:</h2>
24802481
@ <ol>
2481
- for(i=0; i<ann.nVers; i++){
2482
- @ <li><tt>%s(ann.azVers[i])</tt></li>
2482
+ for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2483
+ @ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
2484
+ @ check-in %z(href("%R/info/%S",p->zMUuid))%.10s(p->zMUuid)</a>
2485
+ @ artifact %z(href("%R/artifact/%S",p->zFUuid))%.10s(p->zFUuid)</a>
2486
+ @ </span>
2487
+#if 0
2488
+ if( i>0 ){
2489
+ char *zLink = xhref("target='infowindow'",
2490
+ "%R/fdiff?v1=%S&v2=%S&sbs=1",
2491
+ p->zFUuid,ann.aVers[0].zFUuid);
2492
+ @ %z(zLink)[diff-to-top]</a>
2493
+ if( i>1 ){
2494
+ zLink = xhref("target='infowindow'",
2495
+ "%R/fdiff?v1=%S&v2=%S&sbs=1",
2496
+ p->zFUuid,p[-1].zFUuid);
2497
+ @ %z(zLink)[diff-to-previous]</a>
2498
+ }
2499
+ }
2500
+#endif
24832501
}
24842502
@ </ol>
24852503
@ <hr>
24862504
}
24872505
if( iLimit<0 ){
24882506
@ <h2>Annotation:</h2>
2507
+ iLimit = ann.nVers+10;
24892508
}else{
24902509
@ <h2>Annotation of %d(iLimit) most recent ancestors:</h2>
24912510
}
24922511
if( showLn ){
24932512
sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1);
2494
- sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd: ", strlen(zLn));
2513
+ sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd:", strlen(zLn));
24952514
}else{
24962515
zLn[0] = 0;
24972516
}
24982517
@ <pre>
24992518
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)
2519
+ struct AnnVers *p;
2520
+ int iVers = ann.aOrig[i].iVers;
2521
+ char *z = (char*)ann.aOrig[i].z;
2522
+ int n = ann.aOrig[i].n;
2523
+ char zPrefix[300];
2524
+ z[n] = 0;
2525
+ if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2526
+ if( iVers>=0 ){
2527
+ struct AnnVers *p = ann.aVers+iVers;
2528
+ char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2529
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2530
+ "<span style='background-color:%s'>"
2531
+ "%s%.10s</a> %s</span> %4d:",
2532
+ p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
2533
+ fossil_free(zLink);
2534
+ }else{
2535
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
2536
+ }
2537
+ @ %s(zPrefix) %h(z)
2538
+
25092539
}
25102540
@ </pre>
25112541
style_footer();
25122542
}
25132543
@@ -2571,28 +2601,36 @@
25712601
" ORDER BY ancestor.generation ASC LIMIT 1",
25722602
fid, fnid);
25732603
if( mid==0 ){
25742604
fossil_panic("unable to find manifest");
25752605
}
2576
- if( fileVers ) annFlags |= ANN_FILE_VERS;
25772606
annFlags |= ANN_FILE_ANCEST;
2578
- annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
2607
+ annotate_file(&ann, fnid, mid, iLimit, annFlags);
25792608
if( showLog ){
2580
- for(i=0; i<ann.nVers; i++){
2581
- printf("version %3d: %s\n", i+1, ann.azVers[i]);
2609
+ struct AnnVers *p;
2610
+ for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2611
+ fossil_print("version %3d: %s %.10s file %.10s\n",
2612
+ i+1, p->zDate, p->zMUuid, p->zFUuid);
25822613
}
2583
- printf("---------------------------------------------------\n");
2614
+ fossil_print("---------------------------------------------------\n");
25842615
}
25852616
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);
2617
+ struct AnnVers *p;
2618
+ int iVers = ann.aOrig[i].iVers;
2619
+ char *z = (char*)ann.aOrig[i].z;
2620
+ int n = ann.aOrig[i].n;
2621
+ char zPrefix[200];
2622
+ z[n] = 0;
2623
+ if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2624
+ if( iVers>=0 ){
2625
+ struct AnnVers *p = ann.aVers+iVers;
2626
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%.10s %s",
2627
+ fileVers ? p->zFUuid : p->zMUuid, p->zDate);
2628
+ }else{
2629
+ zPrefix[0] = 0;
2630
+ }
2631
+ fossil_print("%21s %4d: %.*s\n", zPrefix, i+1, n, z);
25942632
}
25952633
}
25962634
25972635
/*
25982636
** COMMAND: test-looks-like-utf
25992637
--- src/diff.c
+++ src/diff.c
@@ -1653,23 +1653,26 @@
1653 int iS1, int iE1, /* Range of lines in p->aFrom[] */
1654 int iS2, int iE2, /* Range of lines in p->aTo[] */
1655 int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
1656 int *piSY, int *piEY /* Write p->aTo[] common segment here */
1657 ){
1658 double bestScore = -1e30; /* Best score seen so far */
1659 int i, j, k; /* Loop counters */
1660 int n; /* Loop limit */
1661 DLine *pA, *pB; /* Pointers to lines */
1662 int iSX, iSY, iEX, iEY; /* Current match */
1663 double score; /* Current score */
1664 int skew; /* How lopsided is the match */
1665 int dist; /* Distance of match from center */
1666 int mid; /* Center of the span */
1667 int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
1668 int iSXp, iSYp, iEXp, iEYp; /* Previous match */
1669
 
 
1670
 
 
 
1671 iSXb = iSXp = iS1;
1672 iEXb = iEXp = iS1;
1673 iSYb = iSYp = iS2;
1674 iEYb = iEYp = iS2;
1675 mid = (iE1 + iS1)/2;
@@ -1707,11 +1710,11 @@
1707 iEY += k;
1708 skew = (iSX-iS1) - (iSY-iS2);
1709 if( skew<0 ) skew = -skew;
1710 dist = (iSX+iEX)/2 - mid;
1711 if( dist<0 ) dist = -dist;
1712 score = (iEX - iSX) - 0.05*skew - 0.05*dist;
1713 if( score>bestScore ){
1714 bestScore = score;
1715 iSXb = iSX;
1716 iSYb = iSY;
1717 iEXb = iEX;
@@ -1731,12 +1734,10 @@
1731 *piSX = iSXb;
1732 *piSY = iSYb;
1733 *piEX = iEXb;
1734 *piEY = iEYb;
1735 }
1736 /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n",
1737 iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */
1738 }
1739
1740 /*
1741 ** Expand the size of aEdit[] array to hold at least nEdit elements.
1742 */
@@ -2191,16 +2192,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 +2226,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 +2239,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 +2255,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 +2283,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 +2296,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 +2325,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 +2409,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 +2460,84 @@
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 +2601,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
@@ -1653,23 +1653,26 @@
1653 int iS1, int iE1, /* Range of lines in p->aFrom[] */
1654 int iS2, int iE2, /* Range of lines in p->aTo[] */
1655 int *piSX, int *piEX, /* Write p->aFrom[] common segment here */
1656 int *piSY, int *piEY /* Write p->aTo[] common segment here */
1657 ){
 
1658 int i, j, k; /* Loop counters */
1659 int n; /* Loop limit */
1660 DLine *pA, *pB; /* Pointers to lines */
1661 int iSX, iSY, iEX, iEY; /* Current match */
1662 int skew = 0; /* How lopsided is the match */
1663 int dist = 0; /* Distance of match from center */
 
1664 int mid; /* Center of the span */
1665 int iSXb, iSYb, iEXb, iEYb; /* Best match so far */
1666 int iSXp, iSYp, iEXp, iEYp; /* Previous match */
1667 sqlite3_int64 bestScore; /* Best score so far */
1668 sqlite3_int64 score; /* Score for current candidate LCS */
1669 int span; /* combined width of the input sequences */
1670
1671 span = (iE1 - iS1) + (iE2 - iS2);
1672 bestScore = -10000;
1673 score = 0;
1674 iSXb = iSXp = iS1;
1675 iEXb = iEXp = iS1;
1676 iSYb = iSYp = iS2;
1677 iEYb = iEYp = iS2;
1678 mid = (iE1 + iS1)/2;
@@ -1707,11 +1710,11 @@
1710 iEY += k;
1711 skew = (iSX-iS1) - (iSY-iS2);
1712 if( skew<0 ) skew = -skew;
1713 dist = (iSX+iEX)/2 - mid;
1714 if( dist<0 ) dist = -dist;
1715 score = (iEX - iSX)*(sqlite3_int64)span - (skew + dist);
1716 if( score>bestScore ){
1717 bestScore = score;
1718 iSXb = iSX;
1719 iSYb = iSY;
1720 iEXb = iEX;
@@ -1731,12 +1734,10 @@
1734 *piSX = iSXb;
1735 *piSY = iSYb;
1736 *piEX = iEXb;
1737 *piEY = iEYb;
1738 }
 
 
1739 }
1740
1741 /*
1742 ** Expand the size of aEdit[] array to hold at least nEdit elements.
1743 */
@@ -2191,16 +2192,24 @@
2192 DContext c; /* The diff-engine context */
2193 struct AnnLine { /* Lines of the original files... */
2194 const char *z; /* The text of the line */
2195 short int n; /* Number of bytes (omitting trailing space and \n) */
2196 short int iLevel; /* Level at which tag was set */
2197 int iVers; /* aVers[] entry responsible for this line */
2198 } *aOrig;
2199 int nOrig; /* Number of elements in aOrig[] */
2200 int nNoSrc; /* Number of entries where aOrig[].zSrc==NULL */
2201 int iLevel; /* Current level */
2202 int nVers; /* Number of versions analyzed */
2203 struct AnnVers {
2204 const char *zFUuid; /* File being analyzed */
2205 const char *zMUuid; /* Check-in containing the file */
2206 const char *zUser; /* User who did the check-in */
2207 const char *zDate; /* Date of the check-in */
2208 const char *zBgColor; /* Suggested background color */
2209 unsigned cnt; /* Number of lines contributed by this check-in */
2210 } *aVers; /* For each check-in analyzed */
2211 char **azVers; /* Names of versions analyzed */
2212 };
2213
2214 /*
2215 ** Initialize the annotation process by specifying the file that is
@@ -2217,11 +2226,11 @@
2226 }
2227 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
2228 for(i=0; i<p->c.nTo; i++){
2229 p->aOrig[i].z = p->c.aTo[i].z;
2230 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2231 p->aOrig[i].iVers = -1;
2232 }
2233 p->nOrig = p->c.nTo;
2234 return 0;
2235 }
2236
@@ -2230,15 +2239,13 @@
2239 ** being annotated. Do another step of the annotation. Return true
2240 ** if additional annotation is required. zPName is the tag to insert
2241 ** on each line of the file being annotated that was contributed by
2242 ** pParent. Memory to hold zPName is leaked.
2243 */
2244 static int annotation_step(Annotator *p, Blob *pParent, int iVers){
2245 int i, j;
2246 int lnTo;
 
 
2247
2248 /* Prepare the parent file to be diffed */
2249 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
2250 &p->c.nFrom, 1);
2251 if( p->c.aFrom==0 ){
@@ -2248,24 +2255,22 @@
2255 /* Compute the differences going from pParent to the file being
2256 ** annotated. */
2257 diff_all(&p->c);
2258
2259 /* Where new lines are inserted on this difference, record the
2260 ** iVers as the source of the new line.
2261 */
 
2262 p->iLevel++;
 
2263 for(i=lnTo=0; i<p->c.nEdit; i+=3){
2264 int nCopy = p->c.aEdit[i];
2265 int nIns = p->c.aEdit[i+2];
2266 lnTo += nCopy;
2267 for(j=0; j<nIns; j++, lnTo++){
2268 if( p->aOrig[lnTo].iVers<0 ){
2269 p->aOrig[lnTo].iVers = iVers;
2270 }
2271 }
 
2272 }
2273
2274 /* Clear out the diff results */
2275 fossil_free(p->c.aEdit);
2276 p->c.aEdit = 0;
@@ -2278,39 +2283,10 @@
2283 /* Return no errors */
2284 return 0;
2285 }
2286
2287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2288 /* Annotation flags */
2289 #define ANN_FILE_VERS 0x01 /* Show file vers rather than commit vers */
2290 #define ANN_FILE_ANCEST 0x02 /* Prefer check-ins in the ANCESTOR table */
2291
2292 /*
@@ -2320,11 +2296,10 @@
2296 */
2297 static void annotate_file(
2298 Annotator *p, /* The annotator */
2299 int fnid, /* The name of the file to be annotated */
2300 int mid, /* Use the version of the file in this check-in */
 
2301 int iLimit, /* Limit the number of levels if greater than zero */
2302 int annFlags /* Flags to alter the annotation */
2303 ){
2304 Blob toAnnotate; /* Text of the final (mid) version of the file */
2305 Blob step; /* Text of previous revision */
@@ -2350,57 +2325,69 @@
2325 "DELETE FROM vseen;"
2326 );
2327
2328 db_prepare(&ins, "INSERT OR IGNORE INTO vseen(rid) VALUES(:rid)");
2329 db_prepare(&q,
2330 "SELECT (SELECT uuid FROM blob WHERE rid=mlink.fid),"
2331 " (SELECT uuid FROM blob WHERE rid=mlink.mid),"
2332 " date(event.mtime),"
2333 " coalesce(event.euser,event.user),"
2334 " mlink.pid"
2335 " FROM mlink, event"
2336 " WHERE mlink.fid=:rid"
2337 " AND event.objid=mlink.mid"
2338 " AND mlink.pid NOT IN vseen"
2339 " ORDER BY %s event.mtime",
 
2340 (annFlags & ANN_FILE_ANCEST)!=0 ?
2341 "(mlink.mid IN (SELECT rid FROM ancestor)) DESC,":""
2342 );
2343
2344 db_bind_int(&q, ":rid", rid);
2345 if( iLimit==0 ) iLimit = 1000000000;
2346 while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
2347 int prevId = db_column_int(&q, 4);
2348 p->aVers = fossil_realloc(p->aVers, (p->nVers+1)*sizeof(p->aVers[0]));
2349 p->aVers[p->nVers].zFUuid = fossil_strdup(db_column_text(&q, 0));
2350 p->aVers[p->nVers].zMUuid = fossil_strdup(db_column_text(&q, 1));
2351 p->aVers[p->nVers].zDate = fossil_strdup(db_column_text(&q, 2));
2352 p->aVers[p->nVers].zUser = fossil_strdup(db_column_text(&q, 3));
2353 if( p->nVers ){
2354 content_get(rid, &step);
2355 annotation_step(p, &step, p->nVers-1);
2356 blob_reset(&step);
 
2357 }
2358 p->nVers++;
 
 
 
 
2359 db_bind_int(&ins, ":rid", rid);
2360 db_step(&ins);
2361 db_reset(&ins);
 
2362 db_reset(&q);
2363 rid = prevId;
2364 db_bind_int(&q, ":rid", prevId);
2365 cnt++;
2366 }
2367 db_finalize(&q);
2368 db_finalize(&ins);
2369 db_end_transaction(0);
2370 }
2371
2372 /*
2373 ** Return a color from a gradient.
2374 */
2375 unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){
2376 unsigned c; /* Result color */
2377 unsigned x1, x2;
2378 x1 = (c1>>16)&0xff;
2379 x2 = (c2>>16)&0xff;
2380 c = (x1*(n-i) + x2*i)/n<<16 & 0xff0000;
2381 x1 = (c1>>8)&0xff;
2382 x2 = (c2>>8)&0xff;
2383 c |= (x1*(n-i) + x2*i)/n<<8 & 0xff00;
2384 x1 = c1&0xff;
2385 x2 = c2&0xff;
2386 c |= (x1*(n-i) + x2*i)/n & 0xff;
2387 return c;
2388 }
2389
2390 /*
2391 ** WEBPAGE: annotate
2392 **
2393 ** Query parameters:
@@ -2422,10 +2409,12 @@
2409 int showLog = 0; /* True to display the log */
2410 char zLn[10]; /* Line number buffer */
2411 char zFormat[10]; /* Format string for line numbers */
2412 Annotator ann;
2413 HQuery url;
2414 struct AnnVers *p;
2415 unsigned clr1, clr2, clr;
2416
2417 showLn = atoi(PD("ln","1"));
2418 showLog = atoi(PD("log","1"));
2419 login_check_credentials();
2420 if( !g.perm.Read ){ login_needed(); return; }
@@ -2471,43 +2460,84 @@
2460 }
2461 if( iLimit!=20 ){
2462 style_submenu_element("20 Ancestors", "20 Ancestors",
2463 url_render(&url, "limit", "20", 0, 0));
2464 }
2465 annotate_file(&ann, fnid, mid, iLimit, annFlags);
2466 if( db_get_boolean("white-foreground", 0) ){
2467 clr1 = 0xa04040;
2468 clr2 = 0x4059a0;
2469 }else{
2470 clr1 = 0xffb5b5; /* Recent changes: red (hot) */
2471 clr2 = 0xb5e0ff; /* Older changes: blue (cold) */
2472 }
2473 for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2474 clr = gradient_color(clr1, clr2, ann.nVers-1, i);
2475 ann.aVers[i].zBgColor = mprintf("#%06x", clr);
2476 ann.aVers[i].zUser = mprintf("%h", ann.aVers[i].zUser);
2477 }
2478
2479 if( showLog ){
 
2480 @ <h2>Versions analyzed:</h2>
2481 @ <ol>
2482 for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2483 @ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
2484 @ check-in %z(href("%R/info/%S",p->zMUuid))%.10s(p->zMUuid)</a>
2485 @ artifact %z(href("%R/artifact/%S",p->zFUuid))%.10s(p->zFUuid)</a>
2486 @ </span>
2487 #if 0
2488 if( i>0 ){
2489 char *zLink = xhref("target='infowindow'",
2490 "%R/fdiff?v1=%S&v2=%S&sbs=1",
2491 p->zFUuid,ann.aVers[0].zFUuid);
2492 @ %z(zLink)[diff-to-top]</a>
2493 if( i>1 ){
2494 zLink = xhref("target='infowindow'",
2495 "%R/fdiff?v1=%S&v2=%S&sbs=1",
2496 p->zFUuid,p[-1].zFUuid);
2497 @ %z(zLink)[diff-to-previous]</a>
2498 }
2499 }
2500 #endif
2501 }
2502 @ </ol>
2503 @ <hr>
2504 }
2505 if( iLimit<0 ){
2506 @ <h2>Annotation:</h2>
2507 iLimit = ann.nVers+10;
2508 }else{
2509 @ <h2>Annotation of %d(iLimit) most recent ancestors:</h2>
2510 }
2511 if( showLn ){
2512 sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1);
2513 sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd:", strlen(zLn));
2514 }else{
2515 zLn[0] = 0;
2516 }
2517 @ <pre>
2518 for(i=0; i<ann.nOrig; i++){
2519 struct AnnVers *p;
2520 int iVers = ann.aOrig[i].iVers;
2521 char *z = (char*)ann.aOrig[i].z;
2522 int n = ann.aOrig[i].n;
2523 char zPrefix[300];
2524 z[n] = 0;
2525 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2526 if( iVers>=0 ){
2527 struct AnnVers *p = ann.aVers+iVers;
2528 char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2529 sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2530 "<span style='background-color:%s'>"
2531 "%s%.10s</a> %s</span> %4d:",
2532 p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
2533 fossil_free(zLink);
2534 }else{
2535 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
2536 }
2537 @ %s(zPrefix) %h(z)
2538
2539 }
2540 @ </pre>
2541 style_footer();
2542 }
2543
@@ -2571,28 +2601,36 @@
2601 " ORDER BY ancestor.generation ASC LIMIT 1",
2602 fid, fnid);
2603 if( mid==0 ){
2604 fossil_panic("unable to find manifest");
2605 }
 
2606 annFlags |= ANN_FILE_ANCEST;
2607 annotate_file(&ann, fnid, mid, iLimit, annFlags);
2608 if( showLog ){
2609 struct AnnVers *p;
2610 for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
2611 fossil_print("version %3d: %s %.10s file %.10s\n",
2612 i+1, p->zDate, p->zMUuid, p->zFUuid);
2613 }
2614 fossil_print("---------------------------------------------------\n");
2615 }
2616 for(i=0; i<ann.nOrig; i++){
2617 struct AnnVers *p;
2618 int iVers = ann.aOrig[i].iVers;
2619 char *z = (char*)ann.aOrig[i].z;
2620 int n = ann.aOrig[i].n;
2621 char zPrefix[200];
2622 z[n] = 0;
2623 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2624 if( iVers>=0 ){
2625 struct AnnVers *p = ann.aVers+iVers;
2626 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%.10s %s",
2627 fileVers ? p->zFUuid : p->zMUuid, p->zDate);
2628 }else{
2629 zPrefix[0] = 0;
2630 }
2631 fossil_print("%21s %4d: %.*s\n", zPrefix, i+1, n, z);
2632 }
2633 }
2634
2635 /*
2636 ** COMMAND: test-looks-like-utf
2637

Keyboard Shortcuts

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