Index: src/diff.c ================================================================== --- src/diff.c +++ src/diff.c @@ -1653,23 +1653,26 @@ int iS1, int iE1, /* Range of lines in p->aFrom[] */ int iS2, int iE2, /* Range of lines in p->aTo[] */ int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ int *piSY, int *piEY /* Write p->aTo[] common segment here */ ){ - double bestScore = -1e30; /* Best score seen so far */ int i, j, k; /* Loop counters */ int n; /* Loop limit */ DLine *pA, *pB; /* Pointers to lines */ int iSX, iSY, iEX, iEY; /* Current match */ - double score; /* Current score */ - int skew; /* How lopsided is the match */ - int dist; /* Distance of match from center */ + int skew = 0; /* How lopsided is the match */ + int dist = 0; /* Distance of match from center */ int mid; /* Center of the span */ int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ int iSXp, iSYp, iEXp, iEYp; /* Previous match */ - + sqlite3_int64 bestScore; /* Best score so far */ + sqlite3_int64 score; /* Score for current candidate LCS */ + int span; /* combined width of the input sequences */ + span = (iE1 - iS1) + (iE2 - iS2); + bestScore = -10000; + score = 0; iSXb = iSXp = iS1; iEXb = iEXp = iS1; iSYb = iSYp = iS2; iEYb = iEYp = iS2; mid = (iE1 + iS1)/2; @@ -1707,11 +1710,11 @@ iEY += k; skew = (iSX-iS1) - (iSY-iS2); if( skew<0 ) skew = -skew; dist = (iSX+iEX)/2 - mid; if( dist<0 ) dist = -dist; - score = (iEX - iSX) - 0.05*skew - 0.05*dist; + score = (iEX - iSX)*(sqlite3_int64)span - (skew + dist); if( score>bestScore ){ bestScore = score; iSXb = iSX; iSYb = iSY; iEXb = iEX; @@ -1731,12 +1734,10 @@ *piSX = iSXb; *piSY = iSYb; *piEX = iEXb; *piEY = iEYb; } - /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n", - iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */ } /* ** Expand the size of aEdit[] array to hold at least nEdit elements. */ @@ -2191,16 +2192,24 @@ DContext c; /* The diff-engine context */ struct AnnLine { /* Lines of the original files... */ const char *z; /* The text of the line */ short int n; /* Number of bytes (omitting trailing space and \n) */ short int iLevel; /* Level at which tag was set */ - const char *zSrc; /* Tag showing origin of this line */ + int iVers; /* aVers[] entry responsible for this line */ } *aOrig; int nOrig; /* Number of elements in aOrig[] */ int nNoSrc; /* Number of entries where aOrig[].zSrc==NULL */ int iLevel; /* Current level */ int nVers; /* Number of versions analyzed */ + struct AnnVers { + const char *zFUuid; /* File being analyzed */ + const char *zMUuid; /* Check-in containing the file */ + const char *zUser; /* User who did the check-in */ + const char *zDate; /* Date of the check-in */ + const char *zBgColor; /* Suggested background color */ + unsigned cnt; /* Number of lines contributed by this check-in */ + } *aVers; /* For each check-in analyzed */ char **azVers; /* Names of versions analyzed */ }; /* ** Initialize the annotation process by specifying the file that is @@ -2217,11 +2226,11 @@ } p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); for(i=0; ic.nTo; i++){ p->aOrig[i].z = p->c.aTo[i].z; p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; - p->aOrig[i].zSrc = 0; + p->aOrig[i].iVers = -1; } p->nOrig = p->c.nTo; return 0; } @@ -2230,15 +2239,13 @@ ** being annotated. Do another step of the annotation. Return true ** if additional annotation is required. zPName is the tag to insert ** on each line of the file being annotated that was contributed by ** pParent. Memory to hold zPName is leaked. */ -static int annotation_step(Annotator *p, Blob *pParent, char *zPName){ +static int annotation_step(Annotator *p, Blob *pParent, int iVers){ int i, j; int lnTo; - int iPrevLevel; - int iThisLevel; /* Prepare the parent file to be diffed */ p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent), &p->c.nFrom, 1); if( p->c.aFrom==0 ){ @@ -2248,24 +2255,22 @@ /* Compute the differences going from pParent to the file being ** annotated. */ diff_all(&p->c); /* Where new lines are inserted on this difference, record the - ** zPName as the source of the new line. + ** iVers as the source of the new line. */ - iPrevLevel = p->iLevel; p->iLevel++; - iThisLevel = p->iLevel; for(i=lnTo=0; ic.nEdit; i+=3){ - struct AnnLine *x = &p->aOrig[lnTo]; - for(j=0; jc.aEdit[i]; j++, lnTo++, x++){ - if( x->zSrc==0 || x->iLevel==iPrevLevel ){ - x->zSrc = zPName; - x->iLevel = iThisLevel; + int nCopy = p->c.aEdit[i]; + int nIns = p->c.aEdit[i+2]; + lnTo += nCopy; + for(j=0; jaOrig[lnTo].iVers<0 ){ + p->aOrig[lnTo].iVers = iVers; } } - lnTo += p->c.aEdit[i+2]; } /* Clear out the diff results */ fossil_free(p->c.aEdit); p->c.aEdit = 0; @@ -2278,39 +2283,10 @@ /* Return no errors */ return 0; } -/* -** COMMAND: test-annotate-step -*/ -void test_annotate_step_cmd(void){ - Blob orig, b; - Annotator x; - int i; - - if( g.argc<4 ) usage("RID1 RID2 ..."); - db_must_be_within_tree(); - blob_zero(&b); - content_get(name_to_rid(g.argv[2]), &orig); - if( annotation_start(&x, &orig) ){ - fossil_fatal("binary file"); - } - for(i=3; icnt && db_step(&q)==SQLITE_ROW ){ - const char *zUuid = db_column_text(&q, 0); - const char *zDate = db_column_text(&q, 1); - const char *zUser = db_column_text(&q, 2); - int prevId = db_column_int(&q, 3); - if( webLabel ){ - zLabel = mprintf( - "%.10s %s %13.13s", - zUuid, zUuid, zDate, zUser - ); - }else{ - zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser); + int prevId = db_column_int(&q, 4); + 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)); + if( p->nVers ){ + content_get(rid, &step); + annotation_step(p, &step, p->nVers-1); + blob_reset(&step); } p->nVers++; - p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) ); - p->azVers[p->nVers-1] = zLabel; - content_get(rid, &step); - annotation_step(p, &step, cnt==iLimit-1 ? "" : zLabel); db_bind_int(&ins, ":rid", rid); db_step(&ins); db_reset(&ins); - blob_reset(&step); db_reset(&q); rid = prevId; db_bind_int(&q, ":rid", prevId); cnt++; } db_finalize(&q); db_finalize(&ins); db_end_transaction(0); } + +/* +** Return a color from a gradient. +*/ +unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){ + unsigned c; /* Result color */ + unsigned x1, x2; + x1 = (c1>>16)&0xff; + x2 = (c2>>16)&0xff; + c = (x1*(n-i) + x2*i)/n<<16 & 0xff0000; + x1 = (c1>>8)&0xff; + x2 = (c2>>8)&0xff; + c |= (x1*(n-i) + x2*i)/n<<8 & 0xff00; + x1 = c1&0xff; + x2 = c2&0xff; + c |= (x1*(n-i) + x2*i)/n & 0xff; + return c; +} /* ** WEBPAGE: annotate ** ** Query parameters: @@ -2422,10 +2409,12 @@ int showLog = 0; /* True to display the log */ char zLn[10]; /* Line number buffer */ char zFormat[10]; /* Format string for line numbers */ Annotator ann; HQuery url; + struct AnnVers *p; + unsigned clr1, clr2, clr; showLn = atoi(PD("ln","1")); showLog = atoi(PD("log","1")); login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } @@ -2471,43 +2460,84 @@ } if( iLimit!=20 ){ style_submenu_element("20 Ancestors", "20 Ancestors", url_render(&url, "limit", "20", 0, 0)); } - annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags); + annotate_file(&ann, fnid, mid, iLimit, annFlags); + if( db_get_boolean("white-foreground", 0) ){ + clr1 = 0xa04040; + clr2 = 0x4059a0; + }else{ + clr1 = 0xffb5b5; /* Recent changes: red (hot) */ + clr2 = 0xb5e0ff; /* Older changes: blue (cold) */ + } + for(p=ann.aVers, i=0; iVersions analyzed: @
    - for(i=0; i%s(ann.azVers[i]) + for(p=ann.aVers, i=0; i%s(p->zDate) + @ check-in %z(href("%R/info/%S",p->zMUuid))%.10s(p->zMUuid) + @ artifact %z(href("%R/artifact/%S",p->zFUuid))%.10s(p->zFUuid) + @ +#if 0 + if( i>0 ){ + char *zLink = xhref("target='infowindow'", + "%R/fdiff?v1=%S&v2=%S&sbs=1", + p->zFUuid,ann.aVers[0].zFUuid); + @ %z(zLink)[diff-to-top] + if( i>1 ){ + zLink = xhref("target='infowindow'", + "%R/fdiff?v1=%S&v2=%S&sbs=1", + p->zFUuid,p[-1].zFUuid); + @ %z(zLink)[diff-to-previous] + } + } +#endif } @
@
} if( iLimit<0 ){ @

Annotation:

+ iLimit = ann.nVers+10; }else{ @

Annotation of %d(iLimit) most recent ancestors:

} if( showLn ){ sqlite3_snprintf(sizeof(zLn), zLn, "%d", ann.nOrig+1); - sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd: ", strlen(zLn)); + sqlite3_snprintf(sizeof(zFormat), zFormat, "%%%dd:", strlen(zLn)); }else{ zLn[0] = 0; } @
   for(i=0; iann.nVers && iVers<0 ) iVers = ann.nVers-1;
+    if( iVers>=0 ){
+      struct AnnVers *p = ann.aVers+iVers;
+      char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
+      sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+           ""
+           "%s%.10s %s %4d:",
+           p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
+      fossil_free(zLink);
+    }else{
+      sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
+    }
+    @ %s(zPrefix) %h(z)
+
   }
   @ 
style_footer(); } @@ -2571,28 +2601,36 @@ " ORDER BY ancestor.generation ASC LIMIT 1", fid, fnid); if( mid==0 ){ fossil_panic("unable to find manifest"); } - if( fileVers ) annFlags |= ANN_FILE_VERS; annFlags |= ANN_FILE_ANCEST; - annotate_file(&ann, fnid, mid, 0, iLimit, annFlags); + annotate_file(&ann, fnid, mid, iLimit, annFlags); if( showLog ){ - for(i=0; izDate, p->zMUuid, p->zFUuid); } - printf("---------------------------------------------------\n"); + fossil_print("---------------------------------------------------\n"); } for(i=0; iann.nVers && iVers<0 ) iVers = ann.nVers-1; + if( iVers>=0 ){ + struct AnnVers *p = ann.aVers+iVers; + sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%.10s %s", + fileVers ? p->zFUuid : p->zMUuid, p->zDate); + }else{ + zPrefix[0] = 0; + } + fossil_print("%21s %4d: %.*s\n", zPrefix, i+1, n, z); } } /* ** COMMAND: test-looks-like-utf