Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -99,12 +99,15 @@ } free(zFullName); } blob_reset(&rewrittenPathname); db_finalize(&q); + /* -3 holds the baseline-based merges - nor cherrypick nor backout. + * Now we can't report easily the baseline-based merges, because + * we don't store the baseline point in vmerge */ db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid" - " WHERE id<=0"); + " WHERE id<=0 AND id >= -2"); while( db_step(&q)==SQLITE_ROW ){ const char *zLabel = "MERGED_WITH"; switch( db_column_int(&q, 1) ){ case -1: zLabel = "CHERRYPICK "; break; case -2: zLabel = "BACKOUT "; break; @@ -1229,13 +1232,14 @@ blob_zero(&ans); prompt_user("continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ) fossil_exit(1);; } + /* id=0 means that it introduces a new parent */ isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0"); if( g.aCommitFile && isAMerge ){ - fossil_fatal("cannot do a partial commit of a merge"); + fossil_fatal("cannot do a partial commit of a graph merge"); } /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA" ** will generate a manifest that has two fileA entries, which is illegal. ** When you think about it, the sequence above makes no sense. So detect Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -2102,10 +2102,11 @@ { "encoding-glob", 0, 40, 1, "" }, { "gdiff-command", 0, 40, 0, "gdiff" }, { "gmerge-command",0, 40, 0, "" }, { "http-port", 0, 16, 0, "8080" }, { "https-login", 0, 0, 0, "off" }, + { "href-targets", 0, 0, 0, "on" }, { "ignore-glob", 0, 40, 1, "" }, { "localauth", 0, 0, 0, "off" }, { "main-branch", 0, 40, 0, "trunk" }, { "manifest", 0, 0, 1, "off" }, #ifdef FOSSIL_ENABLE_MARKDOWN Index: src/diff.c ================================================================== --- src/diff.c +++ src/diff.c @@ -82,10 +82,11 @@ /* ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes) */ #define LENGTH_MASK_SZ 13 +#define TABLENGTH 4 #define LENGTH_MASK ((1<z; char *z = &p->zLine[p->n]; int w = p->width; + int colorize = p->escHtml; if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){ colorize = 0; } - for(i=j=k=0; kiStart ){ int x = strlen(p->zStart); - memcpy(z+j, p->zStart, x); + if (w != 0) + memcpy(z+j, p->zStart, x); j += x; needEndSpan = 1; if( p->iStart2 ){ p->iStart = p->iStart2; p->zStart = p->zStart2; p->iStart2 = 0; } }else if( i==p->iEnd ){ - memcpy(z+j, "", 7); + if (w != 0) + memcpy(z+j, "", 7); j += 7; needEndSpan = 0; if( p->iEnd2 ){ p->iEnd = p->iEnd2; p->iEnd2 = 0; } } } if( c=='\t' ){ - z[j++] = ' '; - while( (k&7)!=7 && kescHtml ){ - memcpy(&z[j], "<", 4); - j += 4; + if (w != 0) { + memcpy(&z[j], "<", 4); + j += 4; + } }else if( c=='&' && p->escHtml ){ - memcpy(&z[j], "&", 5); - j += 5; + if (w != 0) { + memcpy(&z[j], "&", 5); + j += 5; + } }else if( c=='>' && p->escHtml ){ - memcpy(&z[j], ">", 4); - j += 4; + if (w != 0) { + memcpy(&z[j], ">", 4); + j += 4; + } }else if( c=='"' && p->escHtml ){ - memcpy(&z[j], """, 6); - j += 6; + if (w != 0) { + memcpy(&z[j], """, 6); + j += 6; + } }else{ - z[j++] = c; + if (w != 0) { + z[j++] = c; + } if( (c&0xc0)==0x80 ) k--; } } if( needEndSpan ){ - memcpy(&z[j], "", 7); - j += 7; + if (w != 0) { + memcpy(&z[j], "", 7); + j += 7; + } } + + if (k > maxwidth) + maxwidth = k; + if( (flags & SBS_PAD)!=0 ){ while( kcnt && 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); + const char *zUuidFile = db_column_text(&q, 1); + const char *zUuidParentFile = db_column_text(&q, 2); + const char *zDate = db_column_text(&q, 3); + const char *zUser = db_column_text(&q, 4); + int prevId = db_column_int(&q, 5); if( webLabel ){ - zLabel = mprintf( - "%.10s %s %13.13s", - zUuid, zUuid, zDate, zUser - ); + if (zUuidParentFile) { + zLabel = mprintf( + "%.10s " + "d " + "%s %13.13s", + zUuid, zInfoTarget, zUuid, + zUuidParentFile, zUuidFile, zDiffTarget, + zDate, zUser); + }else{ + zLabel = mprintf( + "%.10s " + "%s %13.13s", + zUuid, zInfoTarget, zUuid, + zDate, zUser); + } }else{ zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser); } p->nVers++; p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) ); @@ -2374,11 +2434,11 @@ fossil_redirect_home(); } compute_direct_ancestors(mid, 10000000); style_header("File Annotation"); if( P("filevers") ) annFlags |= ANN_FILE_VERS; - annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags); + annotate_file(&ann, P("filename"), fnid, mid, g.perm.Hyperlink, iLimit, annFlags); if( P("log") ){ int i; @

Versions analyzed:

@
    for(i=0; iw*2 ) n1 = w*2; - x = w*2+17 - (n1+2); + if (w > 0) { + if( n1>w*2 ) n1 = w*2; + x = w*2+17 - (n1+2); + }else{ + /* Autocalculate width + * We can't know the width in advance, so we'll make it + * output three = around the name */ + x = 6; + } z = mprintf("%.*c %.*s %.*c\n", x/2, '=', n1, zLeft, (x+1)/2, '='); }else{ z = mprintf("--- %s\n+++ %s\n", zLeft, zRight); } Index: src/doc.c ================================================================== --- src/doc.c +++ src/doc.c @@ -188,10 +188,13 @@ { "mpga", 4, "audio/mpeg" }, { "ms", 2, "application/x-troff-ms" }, { "msh", 3, "model/mesh" }, { "nc", 2, "application/x-netcdf" }, { "oda", 3, "application/oda" }, + { "odp", 3, "application/vnd.oasis.opendocument.presentation" }, + { "ods", 3, "application/vnd.oasis.opendocument.spreadsheet" }, + { "odt", 3, "application/vnd.oasis.opendocument.text" }, { "ogg", 3, "application/ogg" }, { "ogm", 3, "application/ogg" }, { "pbm", 3, "image/x-portable-bitmap" }, { "pdb", 3, "chemical/x-pdb" }, { "pdf", 3, "application/pdf" }, Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -406,12 +406,13 @@ }else{ int x; if( sideBySide ){ diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS; - /* "dw" query parameter determines width of each column */ - x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1); + /* "dw" query parameter determines width of each column + * 0 means autocalculate. */ + x = atoi(PD("dw","0"))*(DIFF_CONTEXT_MASK+1); if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK; diffFlags += x; }else{ diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS; } @@ -590,10 +591,12 @@ @ Other Links: @ @ %z(href("%R/dir?ci=%S",zUuid))files @ | %z(href("%R/fileage?name=%S",zUuid))file ages @ | %z(href("%R/artifact/%S",zUuid))manifest + @ | + @ vdiff to parent branch if( g.perm.Write ){ @ | %z(href("%R/ci_edit?r=%S",zUuid))edit } @ @ @@ -815,29 +818,34 @@ static void checkin_description(int rid){ Stmt q; db_prepare(&q, "SELECT datetime(mtime), coalesce(euser,user)," " coalesce(ecomment,comment), uuid," + " coalesce((SELECT value FROM tagxref" + " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid),'trunk')," " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref" " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)" " FROM event, blob" " WHERE event.objid=%d AND type='ci'" " AND blob.rid=%d", - rid, rid + TAG_BRANCH, rid, rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zUuid = db_column_text(&q, 3); + const char *zBranch = db_column_text(&q, 4); const char *zTagList = db_column_text(&q, 4); Blob comment; int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS; if( db_get_boolean("timeline-block-markup", 0)==0 ){ wikiFlags |= WIKI_NOBLOCK; } hyperlink_to_uuid(zUuid); + @ on branch + @ %s(zBranch) - blob_zero(&comment); db_column_blob(&q, 2, &comment); wiki_convert(&comment, 0, wikiFlags); blob_reset(&comment); @ (user: @@ -914,19 +922,28 @@ sideBySide = atoi(PD("sbs","1")); showDetail = atoi(PD("detail","0")); if( !showDetail && sideBySide ) showDetail = 1; zFrom = P("from"); zTo = P("to"); - if( !sideBySide ){ - style_submenu_element("Side-by-side Diff", "sbsdiff", - "%R/vdiff?from=%T&to=%T&detail=%d&sbs=1", - zFrom, zTo, showDetail); - }else{ + if (showDetail){ + style_submenu_element("Abstract", "abstract", + "%s/vdiff?from=%T&to=%T&detail=0&sbs=0", + g.zTop, zFrom, zTo); + } + if( !showDetail || sideBySide ){ style_submenu_element("Unified Diff", "udiff", - "%R/vdiff?from=%T&to=%T&detail=%d&sbs=0", - zFrom, zTo, showDetail); + "%R/vdiff?from=%T&to=%T&detail=1&sbs=0", + zFrom, zTo); + } + if (!sideBySide){ + style_submenu_element("Side-by-side Diff", "sbsdiff", + "%R/vdiff?from=%T&to=%T&detail=1&sbs=1", + zFrom, zTo); } + style_submenu_element("Patch", "patch", + "%s/vpatch?from=%T&to=%T", + g.zTop, zFrom, zTo); style_submenu_element("Invert", "invert", "%R/vdiff?from=%T&to=%T&detail=%d&sbs=%d", zTo, zFrom, showDetail, sideBySide); style_header("Check-in Differences"); @

    Difference From:

    Index: src/merge.c ================================================================== --- src/merge.c +++ src/merge.c @@ -630,11 +630,11 @@ /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)", - pickFlag ? -1 : (backoutFlag ? -2 : 0), mid); + pickFlag ? -1 : (backoutFlag ? -2 : (zPivot ? -3 : 0)), mid); if( pickFlag ){ /* For a cherry-pick merge, make the default check-in comment the same ** as the check-in comment on the check-in that is being merged in. */ db_multi_exec( "REPLACE INTO vvar(name,value)" Index: src/name.c ================================================================== --- src/name.c +++ src/name.c @@ -112,10 +112,18 @@ " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", &zTag[5], zType); return rid; } + + if( memcmp(zTag, "pbranch:", 8)==0 ){ + int branchRid = symbolic_name_to_rid(&zTag[8], zType); + if (branchRid == 0) return 0; + rid = get_parent_branch_rid(branchRid); + return rid; + } + if( fossil_isdate(zTag) ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", @@ -522,5 +530,97 @@ comment_print(db_column_text(&q,4), 10, 78); } db_finalize(&q); } } + +int get_parent_branch_rid(int ridRequested){ + Stmt s; + const char *branchName; /* Name of the branch requested at rid */ + const char *parentBranchName; /* Name of the parent branch */ + int rid; + + /* Get the name of the current branch */ + branchName = db_text(0, + "SELECT value FROM tagxref" + " WHERE tagid=%d" + " AND tagxref.tagtype>0" + " AND rid=%d", + TAG_BRANCH, ridRequested); + + if ( !branchName ) + return 0; + + /* Find the name of the branch this was forked from */ + db_prepare(&s, + "SELECT pid, tagxref.value FROM plink JOIN tagxref" + " WHERE cid=:rid" + " AND isprim=1" + " AND tagxref.tagid=%d" + " AND tagxref.tagtype>0" + " AND tagxref.rid=pid", + TAG_BRANCH); + + rid = ridRequested; + while( rid > 0 ) { + db_bind_int(&s, ":rid", rid); + if ( db_step(&s) == SQLITE_ROW ) { + rid = db_column_int(&s, 0); + parentBranchName = db_column_text(&s, 1); + if ( !parentBranchName ) { + rid = 0; + break; + } + + if ( fossil_strcmp(parentBranchName, branchName) ) { + parentBranchName = fossil_strdup(parentBranchName); + break; + } + }else{ + rid = 0; + break; + } + db_reset(&s); + } + db_finalize(&s); + + if (rid == 0) + return 0; + + /* Find the last checkin coming from the parent branch */ + db_prepare(&s, + "SELECT pid, tagxref.value FROM plink JOIN tagxref" + " WHERE cid=:rid" + " AND tagxref.tagid=%d" + " AND tagxref.tagtype>0" + " AND tagxref.rid=pid ORDER BY isprim ASC", + TAG_BRANCH); + + rid = ridRequested; + while( rid > 0 ) { + db_bind_int(&s, ":rid", rid); + int found = 0; + while ( db_step(&s) == SQLITE_ROW ) { + const char *branchNamePid; /* Branch name of the pid */ + + ++found; + rid = db_column_int(&s, 0); + branchNamePid = db_column_text(&s, 1); + if ( !branchNamePid ) { + break; + } + if ( fossil_strcmp(parentBranchName, branchNamePid)==0 ) { + /* Found the last merge from the parent branch */ + db_finalize(&s); + return rid; + } + } + + if (found == 0) { + break; + } + db_reset(&s); + } + db_finalize(&s); + + return 0; +} Index: src/timeline.c ================================================================== --- src/timeline.c +++ src/timeline.c @@ -119,40 +119,36 @@ */ char *hash_color(const char *z){ int i; /* Loop counter */ unsigned int h = 0; /* Hash on the branch name */ int r, g, b; /* Values for red, green, and blue */ - int h1, h2, h3, h4; /* Elements of the hash value */ - int mx, mn; /* Components of HSV */ static char zColor[10]; /* The resulting color */ - static int ix[2] = {0,0}; /* Color chooser parameters */ - - if( ix[0]==0 ){ - if( db_get_boolean("white-foreground", 0) ){ - ix[0] = 140; - ix[1] = 40; - }else{ - ix[0] = 216; - ix[1] = 16; - } - } - for(i=0; z[i]; i++ ){ + static int whitefg = -1; + int cpc = 4; /* colours per component */ + int cfactor = 128/cpc; /* Factor so n*cpc < 128 */ + int cmin = cfactor - 1; /* Factor so the max component is 127 + and the min is different than the bg */ + + if( whitefg = -1 ) + whitefg = db_get_boolean("white-foreground", 0); + + /* Calculate the hash based on the branch name */ + for( i=0; z[i]; i++ ){ h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i]; } - h1 = h % 6; h /= 6; - h3 = h % 30; h /= 30; - h4 = h % 40; h /= 40; - mx = ix[0] - h3; - mn = mx - h4 - ix[1]; - h2 = (h%(mx - mn)) + mn; - switch( h1 ){ - case 0: r = mx; g = h2, b = mn; break; - case 1: r = h2; g = mx, b = mn; break; - case 2: r = mn; g = mx, b = h2; break; - case 3: r = mn; g = h2, b = mx; break; - case 4: r = h2; g = mn, b = mx; break; - default: r = mx; g = mn, b = h2; break; + + /* 'cpc' different random values per component, between 'cmin' and 127 */ + r = cmin + (h % cpc) * cfactor; h /= cpc; + g = cmin + (h % cpc) * cfactor; h /= cpc; + b = cmin + (h % cpc) * cfactor; h /= cpc; + + /* In case of blackfg, get the inverse effect */ + if( !whitefg ) + { + r = 255 - r; + g = 255 - g; + b = 255 - b; } sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b); return zColor; } @@ -202,14 +198,17 @@ GraphContext *pGraph = 0; int prevWasDivider = 0; /* True if previous output row was
    */ int fchngQueryInit = 0; /* True if fchngQuery is initialized */ Stmt fchngQuery; /* Query for file changes on check-ins */ static Stmt qbranch; + const char *zDiffTarget; /* String for the target diff window */ int pendingEndTr = 0; /* True if a is needed */ zPrevDate[0] = 0; mxWikiLen = db_get_int("timeline-max-comment", 0); + zDiffTarget = db_get_boolean("href-targets", 1) ? + "target='diffwindow'": ""; if( tmFlags & TIMELINE_GRAPH ){ pGraph = graph_init(); /* style is not moved to css, because this is ** a technical div for the timeline graph */ @@ -447,25 +446,25 @@ @
      inUl = 1; } if( isNew ){ @
    • %h(zFilename) (new file)   - @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew)) + @ %z(xhref(zDiffTarget,"%R/artifact/%S",zNew)) @ [view]
    • }else if( isDel ){ @
    • %h(zFilename) (deleted)
    • }else if( fossil_strcmp(zOld,zNew)==0 && zOldName!=0 ){ @
    • %h(zOldName) → %h(zFilename) - @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew)) + @ %z(xhref(zDiffTarget,"%R/artifact/%S",zNew)) @ [view]
    • }else{ if( zOldName!=0 ){ @
    • %h(zOldName) → %h(zFilename) }else{ @
    • %h(zFilename)   } - @ %z(xhref("target='diffwindow'","%R/fdiff?v1=%S&v2=%S",zOld,zNew)) + @ %z(xhref(zDiffTarget,"%R/fdiff?v1=%S&v2=%S",zOld,zNew)) @ [diff]
    • } } db_reset(&fchngQuery); if( inUl ){