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;
@@ -1211,13 +1214,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
@@ -2070,10 +2070,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
@@ -57,10 +57,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<<LENGTH_MASK_SZ)-1)
 
 /*
 ** Information about each line of a file being diffed.
 **
@@ -618,10 +619,12 @@
       appendDiffLine(pOut, ' ', &B[b+j], html, 0);
     }
   }
 }
 
+static int maxwidth;
+
 /*
 ** Status of a single output line
 */
 typedef struct SbsLine SbsLine;
 struct SbsLine {
@@ -659,63 +662,92 @@
   int k;   /* Cursor position */
   int needEndSpan = 0;
   const char *zIn = pLine->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; k<w && i<n; i++, k++){
+
+  /* In the case w == 0, we want to calculate the output line
+   * width (k), but not write anything to 'z', because it has
+   * a buffer of limited size. */
+  for(i=j=k=0; (w == 0 || k<w) && i<n; i++, k++){
     char c = zIn[i];
     if( colorize ){
       if( i==p->iStart ){
         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, "</span>", 7);
+        if (w != 0)
+          memcpy(z+j, "</span>", 7);
         j += 7;
         needEndSpan = 0;
         if( p->iEnd2 ){
           p->iEnd = p->iEnd2;
           p->iEnd2 = 0;
         }
       }
     }
     if( c=='\t' ){
-      z[j++] = ' ';
-      while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; }
+      if (w != 0)
+        z[j++] = ' ';
+      while( (k%TABLENGTH)!=0 && (w == 0 || k<w) ){
+        if (w != 0)
+          z[j++] = ' ';
+        k++;
+      }
     }else if( c=='\r' || c=='\f' ){
-      z[j++] = ' ';
+      if (w != 0)
+        z[j++] = ' ';
     }else if( c=='<' && p->escHtml ){
-      memcpy(&z[j], "&lt;", 4);
-      j += 4;
+      if (w != 0) {
+        memcpy(&z[j], "&lt;", 4);
+        j += 4;
+      }
     }else if( c=='&' && p->escHtml ){
-      memcpy(&z[j], "&amp;", 5);
-      j += 5;
+      if (w != 0) {
+        memcpy(&z[j], "&amp;", 5);
+        j += 5;
+      }
     }else if( c=='>' && p->escHtml ){
-      memcpy(&z[j], "&gt;", 4);
-      j += 4;
+      if (w != 0) {
+        memcpy(&z[j], "&gt;", 4);
+        j += 4;
+      }
     }else if( c=='"' && p->escHtml ){
-      memcpy(&z[j], "&quot;", 6);
-      j += 6;
+      if (w != 0) {
+        memcpy(&z[j], "&quot;", 6);
+        j += 6;
+      }
     }else{
-      z[j++] = c;
+      if (w != 0) {
+        z[j++] = c;
+      }
       if( (c&0xc0)==0x80 ) k--;
     }
   }
   if( needEndSpan ){
-    memcpy(&z[j], "</span>", 7);
-    j += 7;
+    if (w != 0) {
+      memcpy(&z[j], "</span>", 7);
+      j += 7;
+    }
   }
+
+  if (k > maxwidth)
+    maxwidth = k;
+
   if( (flags & SBS_PAD)!=0 ){
     while( k<w ){ k++;  z[j++] = ' '; }
   }
   if( flags & SBS_NEWLINE ){
     z[j++] = '\n';
@@ -1241,10 +1273,18 @@
   int nContext; /* Lines of context above and below each change */
   int showDivider = 0;  /* True to show the divider */
 
   memset(&s, 0, sizeof(s));
   s.width = diff_width(diffFlags);
+  if( s.width == 0 ) { /* May Autocalculate */
+    Blob dump;
+    maxwidth = -1; /* The webserver may call sbsDiff more than once per process */
+    blob_zero(&dump);
+    sbsDiff(p, &dump, pRe, diffFlags | DIFF_WIDTH_MASK);
+    s.width = maxwidth;
+    blob_reset(&dump);
+  }
   s.zLine = fossil_malloc( 15*s.width + 200 );
   if( s.zLine==0 ) return;
   nContext = diff_context_lines(diffFlags);
   s.escHtml = (diffFlags & DIFF_HTML)!=0;
   s.pRe = pRe;
@@ -1863,11 +1903,10 @@
 ** Extract the width of columns for side-by-side diff.  Supply an
 ** appropriate default if no width is given.
 */
 int diff_width(u64 diffFlags){
   int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
-  if( w==0 ) w = 80;
   return w;
 }
 
 /*
 ** Generate a report of the differences between files pA and pB.
@@ -2175,11 +2214,12 @@
 ** by its filename number (filename.fnid) and the baseline in which
 ** it was checked in (mlink.mid).
 */
 static void annotate_file(
   Annotator *p,        /* The annotator */
-  int fnid,            /* The name of the file to be annotated */
+  const char *zFilename,/* The name of the file to be annotated */
+  int fnid,            /* The file name id of the file to be annotated */
   int mid,             /* Use the version of the file in this check-in */
   int webLabel,        /* Use web-style annotations if true */
   int iLimit,          /* Limit the number of levels if greater than zero */
   int annFlags         /* Flags to alter the annotation */
 ){
@@ -2187,10 +2227,15 @@
   Blob step;           /* Text of previous revision */
   int rid;             /* Artifact ID of the file being annotated */
   char *zLabel;        /* Label to apply to a line */
   Stmt q;              /* Query returning all ancestor versions */
   int cnt = 0;         /* Number of versions examined */
+  const char *zInfoTarget;     /* String for target info window */
+  const char *zDiffTarget;     /* String for target diff window */
+
+  zInfoTarget = db_get_boolean("href-targets", 1) ? "target='infowindow'" : "";
+  zDiffTarget = db_get_boolean("href-targets", 1) ? "target='diffwindow'" : "";
 
   /* Initialize the annotation */
   rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
   if( rid==0 ){
     fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
@@ -2201,10 +2246,12 @@
   if( iLimit<=0 ) iLimit = 1000000000;
   annotation_start(p, &toAnnotate);
   
   db_prepare(&q,
     "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
+    "       (SELECT uuid FROM blob WHERE rid=mlink.fid),"
+    "       (SELECT uuid FROM blob WHERE rid=mlink.pid),"
     "       date(event.mtime),"
     "       coalesce(event.euser,event.user),"
     "       mlink.pid"
     "  FROM mlink, event"
     " WHERE mlink.fid=:rid"
@@ -2215,18 +2262,31 @@
   
   db_bind_int(&q, ":rid", rid);
   if( iLimit==0 ) iLimit = 1000000000;
   while( rid && iLimit>cnt && 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(
-          "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
-          zUuid, zUuid, zDate, zUser
-      );
+      if (zUuidParentFile) {
+        zLabel = mprintf(
+            "<a href='%R/info/%s' %s>%.10s</a> "
+            "<a href='%R/fdiff?v1=%s&v2=%s' %s>d</a> "
+            "%s %13.13s", 
+            zUuid, zInfoTarget, zUuid,
+            zUuidParentFile, zUuidFile, zDiffTarget,
+            zDate, zUser);
+      }else{
+        zLabel = mprintf(
+            "<a href='%R/info/%s' %s>%.10s</a>   "
+            "%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]) );
@@ -2271,11 +2331,11 @@
   if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid) ){
     fossil_redirect_home();
   }
   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;
     @ <h2>Versions analyzed:</h2>
     @ <ol>
     for(i=0; i<ann.nVers; i++){
@@ -2362,11 +2422,11 @@
           fid, fnid);
   if( mid==0 ){
     fossil_panic("unable to find manifest");
   }
   if( fileVers ) annFlags |= ANN_FILE_VERS;
-  annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
+  annotate_file(&ann, zFilename, fnid, mid, 0, iLimit, annFlags);
   if( showLog ){
     for(i=0; i<ann.nVers; i++){
       printf("version %3d: %s\n", i+1, ann.azVers[i]);
     }
     printf("---------------------------------------------------\n");

Index: src/diffcmd.c
==================================================================
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -50,12 +50,19 @@
     /* no-op */
   }else if( diffFlags & DIFF_SIDEBYSIDE ){
     int w = diff_width(diffFlags);
     int n1 = strlen(zLeft);
     int x;
-    if( n1>w*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
@@ -407,12 +407,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;
     }
@@ -650,10 +651,12 @@
       @ <tr><th>Other&nbsp;Links:</th>
       @   <td>
       @     %z(href("%R/dir?ci=%S",zUuid))files</a>
       @   | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
       @   | %z(href("%R/artifact/%S",zUuid))manifest</a>
+      @   | <a href="%s(g.zTop)/vdiff?from=pbranch:%S(zUuid)&to=%S(zUuid)">
+      @           vdiff to parent branch</a>
       if( g.perm.Write ){
         @   | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
       }
       @   </td>
       @ </tr>
@@ -875,29 +878,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 <a href="%R/timeline?r=%s(zBranch)&nd&c=%T(zDate)">
+    @   %s(zBranch)</a> - 
     blob_zero(&comment);
     db_column_blob(&q, 2, &comment);
     wiki_convert(&comment, 0, wikiFlags);
     blob_reset(&comment);
     @ (user:
@@ -974,19 +982,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");
   @ <h2>Difference From:</h2><blockquote>

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 */
+  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( 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++ ){
+  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 <hr> */
   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 </td></tr> 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 @@
           @ <ul class="filelist">
           inUl = 1;
         }
         if( isNew ){
           @ <li> %h(zFilename) (new file) &nbsp;
-          @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew))
+          @ %z(xhref(zDiffTarget,"%R/artifact/%S",zNew))
           @ [view]</a></li>
         }else if( isDel ){
           @ <li> %h(zFilename) (deleted)</li>
         }else if( fossil_strcmp(zOld,zNew)==0 && zOldName!=0 ){
           @ <li> %h(zOldName) &rarr; %h(zFilename)
-          @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew))
+          @ %z(xhref(zDiffTarget,"%R/artifact/%S",zNew))
           @ [view]</a></li>
         }else{
           if( zOldName!=0 ){
             @ <li> %h(zOldName) &rarr; %h(zFilename)
           }else{
             @ <li> %h(zFilename) &nbsp;
           }
-          @ %z(xhref("target='diffwindow'","%R/fdiff?v1=%S&v2=%S",zOld,zNew))
+          @ %z(xhref(zDiffTarget,"%R/fdiff?v1=%S&v2=%S",zOld,zNew))
           @ [diff]</a></li>
         }
       }
       db_reset(&fchngQuery);
       if( inUl ){