Changes On Branch annotate_links
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch annotate_links Excluding Merge-Ins

This is equivalent to a diff from 67f787dc4e to 96c3133576

2013-02-15
14:37
Fixing annotate. It was hanging. Leaf check-in: 96c3133576 user: viriketo tags: annotate_links
2013-02-13
11:31
Add a draft document describing the ticket tables and how they are generated from ticket change artifacts. check-in: d5be709c20 user: drh tags: trunk
03:14
Improvements to the UTF-16 BOM detection. check-in: 81c4d78137 user: mistachkin tags: utf16Bom
2013-02-12
20:17
Picking recent trunk fixes on tickets. check-in: 7a8ed6d7df user: viriketo tags: annotate_links
19:23
Revised ticket processing to align with coding style guide, and for clarity of presentation. check-in: 67f787dc4e user: drh tags: trunk
19:09
Fixing the rebuild of the ticket databases, so they get properly the comments included by manifests' "+comment".    drh says not to be able to reproduce this issue, but here is a fix that makes all work for me.    Feel free to rewrite this patch to match your taste. check-in: 9cca9398ab user: viriketo tags: trunk

Changes to src/checkin.c.

    97     97       }else if( isRenamed ){
    98     98         blob_appendf(report, "RENAMED    %s\n", zDisplayName);
    99     99       }
   100    100       free(zFullName);
   101    101     }
   102    102     blob_reset(&rewrittenPathname);
   103    103     db_finalize(&q);
          104  +  /* -3 holds the baseline-based merges - nor cherrypick nor backout.
          105  +   * Now we can't report easily the baseline-based merges, because
          106  +   * we don't store the baseline point in vmerge */
   104    107     db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
   105         -                 " WHERE id<=0");
          108  +                 " WHERE id<=0 AND id >= -2");
   106    109     while( db_step(&q)==SQLITE_ROW ){
   107    110       const char *zLabel = "MERGED_WITH";
   108    111       switch( db_column_int(&q, 1) ){
   109    112         case -1:  zLabel = "CHERRYPICK ";  break;
   110    113         case -2:  zLabel = "BACKOUT    ";  break;
   111    114       }
   112    115       blob_append(report, zPrefix, nPrefix);
................................................................................
  1209   1212     */
  1210   1213     if ( select_commit_files() ){
  1211   1214       blob_zero(&ans);
  1212   1215       prompt_user("continue (y/N)? ", &ans);
  1213   1216       cReply = blob_str(&ans)[0];
  1214   1217       if( cReply!='y' && cReply!='Y' ) fossil_exit(1);;
  1215   1218     }
         1219  +  /* id=0 means that it introduces a new parent */
  1216   1220     isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0");
  1217   1221     if( g.aCommitFile && isAMerge ){
  1218         -    fossil_fatal("cannot do a partial commit of a merge");
         1222  +    fossil_fatal("cannot do a partial commit of a graph merge");
  1219   1223     }
  1220   1224   
  1221   1225     /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA"
  1222   1226     ** will generate a manifest that has two fileA entries, which is illegal.
  1223   1227     ** When you think about it, the sequence above makes no sense.  So detect
  1224   1228     ** it and disallow it.  Ticket [0ff64b0a5fc8].
  1225   1229     */

Changes to src/db.c.

  2068   2068     { "editor",        0,               32, 0, ""                    },
  2069   2069     { "empty-dirs",    0,               40, 1, ""                    },
  2070   2070     { "encoding-glob",  0,              40, 1, ""                    },
  2071   2071     { "gdiff-command", 0,               40, 0, "gdiff"               },
  2072   2072     { "gmerge-command",0,               40, 0, ""                    },
  2073   2073     { "http-port",     0,               16, 0, "8080"                },
  2074   2074     { "https-login",   0,                0, 0, "off"                 },
         2075  +  { "href-targets",  0,                0, 0, "on"                  },
  2075   2076     { "ignore-glob",   0,               40, 1, ""                    },
  2076   2077     { "localauth",     0,                0, 0, "off"                 },
  2077   2078     { "main-branch",   0,               40, 0, "trunk"               },
  2078   2079     { "manifest",      0,                0, 1, "off"                 },
  2079   2080   #ifdef FOSSIL_ENABLE_MARKDOWN
  2080   2081     { "markdown",      0,                0, 0, "off"                 },
  2081   2082   #endif

Changes to src/diff.c.

    55     55   #define looks_like_binary(blob) (looks_like_utf8((blob)) == 0)
    56     56   #endif /* INTERFACE */
    57     57   
    58     58   /*
    59     59   ** Maximum length of a line in a text file, in bytes.  (2**13 = 8192 bytes)
    60     60   */
    61     61   #define LENGTH_MASK_SZ  13
           62  +#define TABLENGTH        4
    62     63   #define LENGTH_MASK     ((1<<LENGTH_MASK_SZ)-1)
    63     64   
    64     65   /*
    65     66   ** Information about each line of a file being diffed.
    66     67   **
    67     68   ** The lower LENGTH_MASK_SZ bits of the hash (DLine.h) are the length
    68     69   ** of the line.  If any line is longer than LENGTH_MASK characters,
................................................................................
   616    617       for(j=0; j<m; j++){
   617    618         if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html);
   618    619         appendDiffLine(pOut, ' ', &B[b+j], html, 0);
   619    620       }
   620    621     }
   621    622   }
   622    623   
          624  +static int maxwidth;
          625  +
   623    626   /*
   624    627   ** Status of a single output line
   625    628   */
   626    629   typedef struct SbsLine SbsLine;
   627    630   struct SbsLine {
   628    631     char *zLine;             /* The output line under construction */
   629    632     int n;                   /* Index of next unused slot in the zLine[] */
................................................................................
   657    660     int i;   /* Number of input characters consumed */
   658    661     int j;   /* Number of output characters generated */
   659    662     int k;   /* Cursor position */
   660    663     int needEndSpan = 0;
   661    664     const char *zIn = pLine->z;
   662    665     char *z = &p->zLine[p->n];
   663    666     int w = p->width;
          667  +
   664    668     int colorize = p->escHtml;
   665    669     if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){
   666    670       colorize = 0;
   667    671     }
   668         -  for(i=j=k=0; k<w && i<n; i++, k++){
          672  +
          673  +  /* In the case w == 0, we want to calculate the output line
          674  +   * width (k), but not write anything to 'z', because it has
          675  +   * a buffer of limited size. */
          676  +  for(i=j=k=0; (w == 0 || k<w) && i<n; i++, k++){
   669    677       char c = zIn[i];
   670    678       if( colorize ){
   671    679         if( i==p->iStart ){
   672    680           int x = strlen(p->zStart);
   673         -        memcpy(z+j, p->zStart, x);
          681  +        if (w != 0)
          682  +          memcpy(z+j, p->zStart, x);
   674    683           j += x;
   675    684           needEndSpan = 1;
   676    685           if( p->iStart2 ){
   677    686             p->iStart = p->iStart2;
   678    687             p->zStart = p->zStart2;
   679    688             p->iStart2 = 0;
   680    689           }
   681    690         }else if( i==p->iEnd ){
   682         -        memcpy(z+j, "</span>", 7);
          691  +        if (w != 0)
          692  +          memcpy(z+j, "</span>", 7);
   683    693           j += 7;
   684    694           needEndSpan = 0;
   685    695           if( p->iEnd2 ){
   686    696             p->iEnd = p->iEnd2;
   687    697             p->iEnd2 = 0;
   688    698           }
   689    699         }
   690    700       }
   691    701       if( c=='\t' ){
   692         -      z[j++] = ' ';
   693         -      while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; }
          702  +      if (w != 0)
          703  +        z[j++] = ' ';
          704  +      while( (k%TABLENGTH)!=0 && (w == 0 || k<w) ){
          705  +        if (w != 0)
          706  +          z[j++] = ' ';
          707  +        k++;
          708  +      }
   694    709       }else if( c=='\r' || c=='\f' ){
   695         -      z[j++] = ' ';
          710  +      if (w != 0)
          711  +        z[j++] = ' ';
   696    712       }else if( c=='<' && p->escHtml ){
   697         -      memcpy(&z[j], "&lt;", 4);
   698         -      j += 4;
          713  +      if (w != 0) {
          714  +        memcpy(&z[j], "&lt;", 4);
          715  +        j += 4;
          716  +      }
   699    717       }else if( c=='&' && p->escHtml ){
   700         -      memcpy(&z[j], "&amp;", 5);
   701         -      j += 5;
          718  +      if (w != 0) {
          719  +        memcpy(&z[j], "&amp;", 5);
          720  +        j += 5;
          721  +      }
   702    722       }else if( c=='>' && p->escHtml ){
   703         -      memcpy(&z[j], "&gt;", 4);
   704         -      j += 4;
          723  +      if (w != 0) {
          724  +        memcpy(&z[j], "&gt;", 4);
          725  +        j += 4;
          726  +      }
   705    727       }else if( c=='"' && p->escHtml ){
   706         -      memcpy(&z[j], "&quot;", 6);
   707         -      j += 6;
          728  +      if (w != 0) {
          729  +        memcpy(&z[j], "&quot;", 6);
          730  +        j += 6;
          731  +      }
   708    732       }else{
   709         -      z[j++] = c;
          733  +      if (w != 0) {
          734  +        z[j++] = c;
          735  +      }
   710    736         if( (c&0xc0)==0x80 ) k--;
   711    737       }
   712    738     }
   713    739     if( needEndSpan ){
   714         -    memcpy(&z[j], "</span>", 7);
   715         -    j += 7;
          740  +    if (w != 0) {
          741  +      memcpy(&z[j], "</span>", 7);
          742  +      j += 7;
          743  +    }
   716    744     }
          745  +
          746  +  if (k > maxwidth)
          747  +    maxwidth = k;
          748  +
   717    749     if( (flags & SBS_PAD)!=0 ){
   718    750       while( k<w ){ k++;  z[j++] = ' '; }
   719    751     }
   720    752     if( flags & SBS_NEWLINE ){
   721    753       z[j++] = '\n';
   722    754     }
   723    755     p->n += j;
................................................................................
  1239   1271     int nChunk = 0; /* Number of chunks of diff output seen so far */
  1240   1272     SbsLine s;    /* Output line buffer */
  1241   1273     int nContext; /* Lines of context above and below each change */
  1242   1274     int showDivider = 0;  /* True to show the divider */
  1243   1275   
  1244   1276     memset(&s, 0, sizeof(s));
  1245   1277     s.width = diff_width(diffFlags);
         1278  +  if( s.width == 0 ) { /* May Autocalculate */
         1279  +    Blob dump;
         1280  +    maxwidth = -1; /* The webserver may call sbsDiff more than once per process */
         1281  +    blob_zero(&dump);
         1282  +    sbsDiff(p, &dump, pRe, diffFlags | DIFF_WIDTH_MASK);
         1283  +    s.width = maxwidth;
         1284  +    blob_reset(&dump);
         1285  +  }
  1246   1286     s.zLine = fossil_malloc( 15*s.width + 200 );
  1247   1287     if( s.zLine==0 ) return;
  1248   1288     nContext = diff_context_lines(diffFlags);
  1249   1289     s.escHtml = (diffFlags & DIFF_HTML)!=0;
  1250   1290     s.pRe = pRe;
  1251   1291     s.iStart = -1;
  1252   1292     s.iStart2 = 0;
................................................................................
  1861   1901   
  1862   1902   /*
  1863   1903   ** Extract the width of columns for side-by-side diff.  Supply an
  1864   1904   ** appropriate default if no width is given.
  1865   1905   */
  1866   1906   int diff_width(u64 diffFlags){
  1867   1907     int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
  1868         -  if( w==0 ) w = 80;
  1869   1908     return w;
  1870   1909   }
  1871   1910   
  1872   1911   /*
  1873   1912   ** Generate a report of the differences between files pA and pB.
  1874   1913   ** If pOut is not NULL then a unified diff is appended there.  It
  1875   1914   ** is assumed that pOut has already been initialized.  If pOut is
................................................................................
  2173   2212   /*
  2174   2213   ** Compute a complete annotation on a file.  The file is identified
  2175   2214   ** by its filename number (filename.fnid) and the baseline in which
  2176   2215   ** it was checked in (mlink.mid).
  2177   2216   */
  2178   2217   static void annotate_file(
  2179   2218     Annotator *p,        /* The annotator */
  2180         -  int fnid,            /* The name of the file to be annotated */
         2219  +  const char *zFilename,/* The name of the file to be annotated */
         2220  +  int fnid,            /* The file name id of the file to be annotated */
  2181   2221     int mid,             /* Use the version of the file in this check-in */
  2182   2222     int webLabel,        /* Use web-style annotations if true */
  2183   2223     int iLimit,          /* Limit the number of levels if greater than zero */
  2184   2224     int annFlags         /* Flags to alter the annotation */
  2185   2225   ){
  2186   2226     Blob toAnnotate;     /* Text of the final (mid) version of the file */
  2187   2227     Blob step;           /* Text of previous revision */
  2188   2228     int rid;             /* Artifact ID of the file being annotated */
  2189   2229     char *zLabel;        /* Label to apply to a line */
  2190   2230     Stmt q;              /* Query returning all ancestor versions */
  2191   2231     int cnt = 0;         /* Number of versions examined */
         2232  +  const char *zInfoTarget;     /* String for target info window */
         2233  +  const char *zDiffTarget;     /* String for target diff window */
         2234  +
         2235  +  zInfoTarget = db_get_boolean("href-targets", 1) ? "target='infowindow'" : "";
         2236  +  zDiffTarget = db_get_boolean("href-targets", 1) ? "target='diffwindow'" : "";
  2192   2237   
  2193   2238     /* Initialize the annotation */
  2194   2239     rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid);
  2195   2240     if( rid==0 ){
  2196   2241       fossil_panic("file #%d is unchanged in manifest #%d", fnid, mid);
  2197   2242     }
  2198   2243     if( !content_get(rid, &toAnnotate) ){
................................................................................
  2199   2244       fossil_panic("unable to retrieve content of artifact #%d", rid);
  2200   2245     }
  2201   2246     if( iLimit<=0 ) iLimit = 1000000000;
  2202   2247     annotation_start(p, &toAnnotate);
  2203   2248     
  2204   2249     db_prepare(&q,
  2205   2250       "SELECT (SELECT uuid FROM blob WHERE rid=mlink.%s),"
         2251  +    "       (SELECT uuid FROM blob WHERE rid=mlink.fid),"
         2252  +    "       (SELECT uuid FROM blob WHERE rid=mlink.pid),"
  2206   2253       "       date(event.mtime),"
  2207   2254       "       coalesce(event.euser,event.user),"
  2208   2255       "       mlink.pid"
  2209   2256       "  FROM mlink, event"
  2210   2257       " WHERE mlink.fid=:rid"
  2211   2258       "   AND event.objid=mlink.mid"
  2212   2259       " ORDER BY event.mtime",
................................................................................
  2213   2260       (annFlags & ANN_FILE_VERS)!=0 ? "fid" : "mid"
  2214   2261     );
  2215   2262     
  2216   2263     db_bind_int(&q, ":rid", rid);
  2217   2264     if( iLimit==0 ) iLimit = 1000000000;
  2218   2265     while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){
  2219   2266       const char *zUuid = db_column_text(&q, 0);
  2220         -    const char *zDate = db_column_text(&q, 1);
  2221         -    const char *zUser = db_column_text(&q, 2);
  2222         -    int prevId = db_column_int(&q, 3);
         2267  +    const char *zUuidFile = db_column_text(&q, 1);
         2268  +    const char *zUuidParentFile = db_column_text(&q, 2);
         2269  +    const char *zDate = db_column_text(&q, 3);
         2270  +    const char *zUser = db_column_text(&q, 4);
         2271  +    int prevId = db_column_int(&q, 5);
  2223   2272       if( webLabel ){
  2224         -      zLabel = mprintf(
  2225         -          "<a href='%R/info/%s' target='infowindow'>%.10s</a> %s %13.13s",
  2226         -          zUuid, zUuid, zDate, zUser
  2227         -      );
         2273  +      if (zUuidParentFile) {
         2274  +        zLabel = mprintf(
         2275  +            "<a href='%R/info/%s' %s>%.10s</a> "
         2276  +            "<a href='%R/fdiff?v1=%s&v2=%s' %s>d</a> "
         2277  +            "%s %13.13s", 
         2278  +            zUuid, zInfoTarget, zUuid,
         2279  +            zUuidParentFile, zUuidFile, zDiffTarget,
         2280  +            zDate, zUser);
         2281  +      }else{
         2282  +        zLabel = mprintf(
         2283  +            "<a href='%R/info/%s' %s>%.10s</a>   "
         2284  +            "%s %13.13s", 
         2285  +            zUuid, zInfoTarget, zUuid,
         2286  +            zDate, zUser);
         2287  +      }
  2228   2288       }else{
  2229   2289         zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser);
  2230   2290       }
  2231   2291       p->nVers++;
  2232   2292       p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) );
  2233   2293       p->azVers[p->nVers-1] = zLabel;
  2234   2294       content_get(rid, &step);
................................................................................
  2269   2329     if( mid==0 || fnid==0 ){ fossil_redirect_home(); }
  2270   2330     iLimit = atoi(PD("limit","-1"));
  2271   2331     if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid) ){
  2272   2332       fossil_redirect_home();
  2273   2333     }
  2274   2334     style_header("File Annotation");
  2275   2335     if( P("filevers") ) annFlags |= ANN_FILE_VERS;
  2276         -  annotate_file(&ann, fnid, mid, g.perm.Hyperlink, iLimit, annFlags);
         2336  +  annotate_file(&ann, P("filename"), fnid, mid, g.perm.Hyperlink, iLimit, annFlags);
  2277   2337     if( P("log") ){
  2278   2338       int i;
  2279   2339       @ <h2>Versions analyzed:</h2>
  2280   2340       @ <ol>
  2281   2341       for(i=0; i<ann.nVers; i++){
  2282   2342         @ <li><tt>%s(ann.azVers[i])</tt></li>
  2283   2343       }
................................................................................
  2360   2420             " WHERE mlink.fid=%d AND mlink.fnid=%d AND mlink.mid=ancestor.rid"
  2361   2421             " ORDER BY ancestor.generation ASC LIMIT 1",
  2362   2422             fid, fnid);
  2363   2423     if( mid==0 ){
  2364   2424       fossil_panic("unable to find manifest");
  2365   2425     }
  2366   2426     if( fileVers ) annFlags |= ANN_FILE_VERS;
  2367         -  annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
         2427  +  annotate_file(&ann, zFilename, fnid, mid, 0, iLimit, annFlags);
  2368   2428     if( showLog ){
  2369   2429       for(i=0; i<ann.nVers; i++){
  2370   2430         printf("version %3d: %s\n", i+1, ann.azVers[i]);
  2371   2431       }
  2372   2432       printf("---------------------------------------------------\n");
  2373   2433     }
  2374   2434     for(i=0; i<ann.nOrig; i++){
  2375   2435       fossil_print("%s: %.*s\n",
  2376   2436                    ann.aOrig[i].zSrc, ann.aOrig[i].n, ann.aOrig[i].z);
  2377   2437     }
  2378   2438   }

Changes to src/diffcmd.c.

    48     48     char *z = 0;
    49     49     if( diffFlags & DIFF_BRIEF ){
    50     50       /* no-op */
    51     51     }else if( diffFlags & DIFF_SIDEBYSIDE ){
    52     52       int w = diff_width(diffFlags);
    53     53       int n1 = strlen(zLeft);
    54     54       int x;
    55         -    if( n1>w*2 ) n1 = w*2;
    56         -    x = w*2+17 - (n1+2);
           55  +    if (w > 0) {
           56  +        if( n1>w*2 ) n1 = w*2;
           57  +        x = w*2+17 - (n1+2);
           58  +    }else{
           59  +        /* Autocalculate width
           60  +         * We can't know the width in advance, so we'll make it
           61  +         * output three = around the name */
           62  +        x = 6;
           63  +    }
    57     64       z = mprintf("%.*c %.*s %.*c\n",
    58     65                   x/2, '=', n1, zLeft, (x+1)/2, '=');
    59     66     }else{
    60     67       z = mprintf("--- %s\n+++ %s\n", zLeft, zRight);
    61     68     }
    62     69     fossil_print("%s", z);
    63     70     fossil_free(z);

Changes to src/doc.c.

   186    186       { "mpeg",       4, "video/mpeg"                        },
   187    187       { "mpg",        3, "video/mpeg"                        },
   188    188       { "mpga",       4, "audio/mpeg"                        },
   189    189       { "ms",         2, "application/x-troff-ms"            },
   190    190       { "msh",        3, "model/mesh"                        },
   191    191       { "nc",         2, "application/x-netcdf"              },
   192    192       { "oda",        3, "application/oda"                   },
          193  +    { "odp",        3, "application/vnd.oasis.opendocument.presentation" },
          194  +    { "ods",        3, "application/vnd.oasis.opendocument.spreadsheet" },
          195  +    { "odt",        3, "application/vnd.oasis.opendocument.text" },
   193    196       { "ogg",        3, "application/ogg"                   },
   194    197       { "ogm",        3, "application/ogg"                   },
   195    198       { "pbm",        3, "image/x-portable-bitmap"           },
   196    199       { "pdb",        3, "chemical/x-pdb"                    },
   197    200       { "pdf",        3, "application/pdf"                   },
   198    201       { "pgm",        3, "image/x-portable-graymap"          },
   199    202       { "pgn",        3, "application/x-chess-pgn"           },

Changes to src/info.c.

   405    405     if( showDiff==0 ){
   406    406       diffFlags = 0;  /* Zero means do not show any diff */
   407    407     }else{
   408    408       int x;
   409    409       if( sideBySide ){
   410    410         diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS;
   411    411   
   412         -      /* "dw" query parameter determines width of each column */
   413         -      x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
          412  +      /* "dw" query parameter determines width of each column
          413  +       * 0 means autocalculate. */
          414  +      x = atoi(PD("dw","0"))*(DIFF_CONTEXT_MASK+1);
   414    415         if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
   415    416         diffFlags += x;
   416    417       }else{
   417    418         diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS;
   418    419       }
   419    420   
   420    421       /* "dc" query parameter determines lines of context */
................................................................................
   648    649         }
   649    650         @ </td></tr>
   650    651         @ <tr><th>Other&nbsp;Links:</th>
   651    652         @   <td>
   652    653         @     %z(href("%R/dir?ci=%S",zUuid))files</a>
   653    654         @   | %z(href("%R/fileage?name=%S",zUuid))file ages</a>
   654    655         @   | %z(href("%R/artifact/%S",zUuid))manifest</a>
          656  +      @   | <a href="%s(g.zTop)/vdiff?from=pbranch:%S(zUuid)&to=%S(zUuid)">
          657  +      @           vdiff to parent branch</a>
   655    658         if( g.perm.Write ){
   656    659           @   | %z(href("%R/ci_edit?r=%S",zUuid))edit</a>
   657    660         }
   658    661         @   </td>
   659    662         @ </tr>
   660    663       }
   661    664       @ </table>
................................................................................
   873    876   ** Output a description of a check-in
   874    877   */
   875    878   static void checkin_description(int rid){
   876    879     Stmt q;
   877    880     db_prepare(&q,
   878    881       "SELECT datetime(mtime), coalesce(euser,user),"
   879    882       "       coalesce(ecomment,comment), uuid,"
          883  +    "       coalesce((SELECT value FROM tagxref"
          884  +    "        WHERE tagid=%d AND tagtype>0 AND rid=blob.rid),'trunk'),"
   880    885       "      (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
   881    886       "        WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
   882    887       "          AND tagxref.rid=blob.rid AND tagxref.tagtype>0)"
   883    888       "  FROM event, blob"
   884    889       " WHERE event.objid=%d AND type='ci'"
   885    890       "   AND blob.rid=%d",
   886         -    rid, rid
          891  +    TAG_BRANCH, rid, rid
   887    892     );
   888    893     while( db_step(&q)==SQLITE_ROW ){
   889    894       const char *zDate = db_column_text(&q, 0);
   890    895       const char *zUser = db_column_text(&q, 1);
   891    896       const char *zUuid = db_column_text(&q, 3);
          897  +    const char *zBranch = db_column_text(&q, 4);
   892    898       const char *zTagList = db_column_text(&q, 4);
   893    899       Blob comment;
   894    900       int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
   895    901       if( db_get_boolean("timeline-block-markup", 0)==0 ){
   896    902         wikiFlags |= WIKI_NOBLOCK;
   897    903       }
   898    904       hyperlink_to_uuid(zUuid);
          905  +    @ on branch <a href="%R/timeline?r=%s(zBranch)&nd&c=%T(zDate)">
          906  +    @   %s(zBranch)</a> - 
   899    907       blob_zero(&comment);
   900    908       db_column_blob(&q, 2, &comment);
   901    909       wiki_convert(&comment, 0, wikiFlags);
   902    910       blob_reset(&comment);
   903    911       @ (user:
   904    912       hyperlink_to_user(zUser,zDate,",");
   905    913       if( zTagList && zTagList[0] && g.perm.Hyperlink ){
................................................................................
   972    980     pFrom = vdiff_parse_manifest("from", &ridFrom);
   973    981     if( pFrom==0 ) return;
   974    982     sideBySide = atoi(PD("sbs","1"));
   975    983     showDetail = atoi(PD("detail","0"));
   976    984     if( !showDetail && sideBySide ) showDetail = 1;
   977    985     zFrom = P("from");
   978    986     zTo = P("to");
   979         -  if( !sideBySide ){
   980         -    style_submenu_element("Side-by-side Diff", "sbsdiff",
   981         -                          "%R/vdiff?from=%T&to=%T&detail=%d&sbs=1",
   982         -                          zFrom, zTo, showDetail);
   983         -  }else{
          987  +  if (showDetail){
          988  +    style_submenu_element("Abstract", "abstract",
          989  +                          "%s/vdiff?from=%T&to=%T&detail=0&sbs=0",
          990  +                          g.zTop, zFrom, zTo);
          991  +  }
          992  +  if( !showDetail || sideBySide ){
   984    993       style_submenu_element("Unified Diff", "udiff",
   985         -                          "%R/vdiff?from=%T&to=%T&detail=%d&sbs=0",
   986         -                          zFrom, zTo, showDetail);
          994  +                          "%R/vdiff?from=%T&to=%T&detail=1&sbs=0",
          995  +                          zFrom, zTo);
   987    996     }
          997  +  if (!sideBySide){
          998  +    style_submenu_element("Side-by-side Diff", "sbsdiff",
          999  +                          "%R/vdiff?from=%T&to=%T&detail=1&sbs=1",
         1000  +                          zFrom, zTo);
         1001  +  }
         1002  +  style_submenu_element("Patch", "patch",
         1003  +                        "%s/vpatch?from=%T&to=%T",
         1004  +                        g.zTop, zFrom, zTo);
   988   1005     style_submenu_element("Invert", "invert",
   989   1006                           "%R/vdiff?from=%T&to=%T&detail=%d&sbs=%d",
   990   1007                           zTo, zFrom, showDetail, sideBySide);
   991   1008     style_header("Check-in Differences");
   992   1009     @ <h2>Difference From:</h2><blockquote>
   993   1010     checkin_description(ridFrom);
   994   1011     @ </blockquote><h2>To:</h2><blockquote>

Changes to src/merge.c.

   628    628     }
   629    629   
   630    630     /*
   631    631     ** Clean up the mid and pid VFILE entries.  Then commit the changes.
   632    632     */
   633    633     db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   634    634     db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
   635         -                pickFlag ? -1 : (backoutFlag ? -2 : 0), mid);
          635  +                pickFlag ? -1 : (backoutFlag ? -2 : (zPivot ? -3 : 0)), mid);
   636    636     if( pickFlag ){
   637    637       /* For a cherry-pick merge, make the default check-in comment the same
   638    638       ** as the check-in comment on the check-in that is being merged in. */
   639    639       db_multi_exec(
   640    640          "REPLACE INTO vvar(name,value)"
   641    641          " SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
   642    642          "  WHERE type='ci' AND objid=%d",
   643    643          mid
   644    644       );
   645    645     }
   646    646     undo_finish();
   647    647     db_end_transaction(nochangeFlag);
   648    648   }

Changes to src/name.c.

   110    110       rid = db_int(0, 
   111    111         "SELECT objid FROM event"
   112    112         " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'"
   113    113         " ORDER BY mtime DESC LIMIT 1",
   114    114         &zTag[5], zType);
   115    115       return rid;
   116    116     }
          117  +
          118  +  if( memcmp(zTag, "pbranch:", 8)==0 ){
          119  +    int branchRid = symbolic_name_to_rid(&zTag[8], zType);
          120  +    if (branchRid == 0) return 0;
          121  +    rid = get_parent_branch_rid(branchRid);
          122  +    return rid;
          123  +  }
          124  +
   117    125     if( fossil_isdate(zTag) ){
   118    126       rid = db_int(0, 
   119    127         "SELECT objid FROM event"
   120    128         " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'"
   121    129         " ORDER BY mtime DESC LIMIT 1",
   122    130         zTag, zType);
   123    131       if( rid) return rid;
................................................................................
   520    528           db_column_text(&q, 2));
   521    529         fossil_print("          ");
   522    530         comment_print(db_column_text(&q,4), 10, 78);
   523    531       }
   524    532       db_finalize(&q);
   525    533     }
   526    534   }
          535  +
          536  +int get_parent_branch_rid(int ridRequested){
          537  +  Stmt s;
          538  +  const char *branchName;    /* Name of the branch requested at rid */
          539  +  const char *parentBranchName; /* Name of the parent branch */
          540  +  int rid;
          541  +
          542  +  /* Get the name of the current branch */
          543  +  branchName = db_text(0,
          544  +    "SELECT value FROM tagxref"
          545  +    " WHERE tagid=%d"
          546  +    "   AND tagxref.tagtype>0"
          547  +    "   AND rid=%d",
          548  +    TAG_BRANCH, ridRequested);
          549  +
          550  +  if ( !branchName )
          551  +    return 0;
          552  +
          553  +  /* Find the name of the branch this was forked from */
          554  +  db_prepare(&s,
          555  +    "SELECT pid, tagxref.value FROM plink JOIN tagxref"
          556  +    " WHERE cid=:rid"
          557  +    "   AND isprim=1"
          558  +    "   AND tagxref.tagid=%d"
          559  +    "   AND tagxref.tagtype>0"
          560  +    "   AND tagxref.rid=pid",
          561  +    TAG_BRANCH);
          562  +
          563  +  rid = ridRequested;
          564  +  while( rid > 0 ) {
          565  +    db_bind_int(&s, ":rid", rid);
          566  +    if ( db_step(&s) == SQLITE_ROW ) {
          567  +      rid = db_column_int(&s, 0);
          568  +      parentBranchName = db_column_text(&s, 1);
          569  +      if ( !parentBranchName ) {
          570  +        rid = 0;
          571  +        break;
          572  +      }
          573  +
          574  +      if ( fossil_strcmp(parentBranchName, branchName) ) {
          575  +        parentBranchName = fossil_strdup(parentBranchName);
          576  +        break;
          577  +      }
          578  +    }else{
          579  +      rid = 0;
          580  +      break;
          581  +    }
          582  +    db_reset(&s);
          583  +  }
          584  +  db_finalize(&s);
          585  +
          586  +  if (rid == 0)
          587  +      return 0;
          588  +
          589  +  /* Find the last checkin coming from the parent branch */
          590  +  db_prepare(&s,
          591  +    "SELECT pid, tagxref.value FROM plink JOIN tagxref"
          592  +    " WHERE cid=:rid"
          593  +    "   AND tagxref.tagid=%d"
          594  +    "   AND tagxref.tagtype>0"
          595  +    "   AND tagxref.rid=pid ORDER BY isprim ASC",
          596  +    TAG_BRANCH);
          597  +
          598  +  rid = ridRequested;
          599  +  while( rid > 0 ) {
          600  +    db_bind_int(&s, ":rid", rid);
          601  +    int found = 0;
          602  +    while ( db_step(&s) == SQLITE_ROW ) {
          603  +      const char *branchNamePid; /* Branch name of the pid */
          604  +
          605  +      ++found;
          606  +      rid = db_column_int(&s, 0);
          607  +      branchNamePid = db_column_text(&s, 1);
          608  +      if ( !branchNamePid ) {
          609  +        break;
          610  +      }
          611  +      if ( fossil_strcmp(parentBranchName, branchNamePid)==0 ) {
          612  +        /* Found the last merge from the parent branch */
          613  +        db_finalize(&s);
          614  +        return rid;
          615  +      }
          616  +    }
          617  +    
          618  +    if (found == 0) {
          619  +      break;
          620  +    }
          621  +    db_reset(&s);
          622  +  }
          623  +  db_finalize(&s);
          624  +
          625  +  return 0;
          626  +}

Changes to src/timeline.c.

   117    117   /*
   118    118   ** Hash a string and use the hash to determine a background color.
   119    119   */
   120    120   char *hash_color(const char *z){
   121    121     int i;                       /* Loop counter */
   122    122     unsigned int h = 0;          /* Hash on the branch name */
   123    123     int r, g, b;                 /* Values for red, green, and blue */
   124         -  int h1, h2, h3, h4;          /* Elements of the hash value */
   125         -  int mx, mn;                  /* Components of HSV */
   126    124     static char zColor[10];      /* The resulting color */
   127         -  static int ix[2] = {0,0};    /* Color chooser parameters */
          125  +  static int whitefg = -1;
          126  +  int cpc = 4;                 /* colours per component */
          127  +  int cfactor = 128/cpc;       /* Factor so n*cpc < 128 */
          128  +  int cmin = cfactor - 1;      /* Factor so the max component is 127
          129  +                                  and the min is different than the bg */
   128    130   
   129         -  if( ix[0]==0 ){
   130         -    if( db_get_boolean("white-foreground", 0) ){
   131         -      ix[0] = 140;
   132         -      ix[1] = 40;
   133         -    }else{
   134         -      ix[0] = 216;
   135         -      ix[1] = 16;
   136         -    }
   137         -  }
   138         -  for(i=0; z[i]; i++ ){
          131  +  if( whitefg = -1 ) 
          132  +    whitefg = db_get_boolean("white-foreground", 0);
          133  +
          134  +  /* Calculate the hash based on the branch name */
          135  +  for( i=0; z[i]; i++ ){
   139    136       h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i];
   140    137     }
   141         -  h1 = h % 6;  h /= 6;
   142         -  h3 = h % 30; h /= 30;
   143         -  h4 = h % 40; h /= 40;
   144         -  mx = ix[0] - h3;
   145         -  mn = mx - h4 - ix[1];
   146         -  h2 = (h%(mx - mn)) + mn;
   147         -  switch( h1 ){
   148         -    case 0:  r = mx; g = h2, b = mn;  break;
   149         -    case 1:  r = h2; g = mx, b = mn;  break;
   150         -    case 2:  r = mn; g = mx, b = h2;  break;
   151         -    case 3:  r = mn; g = h2, b = mx;  break;
   152         -    case 4:  r = h2; g = mn, b = mx;  break;
   153         -    default: r = mx; g = mn, b = h2;  break;
          138  +
          139  +  /* 'cpc' different random values per component, between 'cmin' and 127 */
          140  +  r = cmin + (h % cpc) * cfactor;  h /= cpc;
          141  +  g = cmin + (h % cpc) * cfactor;  h /= cpc;
          142  +  b = cmin + (h % cpc) * cfactor;  h /= cpc;
          143  +
          144  +  /* In case of blackfg, get the inverse effect */
          145  +  if( !whitefg )
          146  +  {
          147  +      r = 255 - r;
          148  +      g = 255 - g;
          149  +      b = 255 - b;
   154    150     }
   155    151     sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b);
   156    152     return zColor;
   157    153   }
   158    154   
   159    155   /*
   160    156   ** COMMAND:  test-hash-color
................................................................................
   200    196     int suppressCnt = 0;
   201    197     char zPrevDate[20];
   202    198     GraphContext *pGraph = 0;
   203    199     int prevWasDivider = 0;     /* True if previous output row was <hr> */
   204    200     int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
   205    201     Stmt fchngQuery;            /* Query for file changes on check-ins */
   206    202     static Stmt qbranch;
          203  +  const char *zDiffTarget;    /* String for the target diff window */
   207    204     int pendingEndTr = 0;       /* True if a </td></tr> is needed */
   208    205   
   209    206     zPrevDate[0] = 0;
   210    207     mxWikiLen = db_get_int("timeline-max-comment", 0);
          208  +  zDiffTarget = db_get_boolean("href-targets", 1) ?
          209  +      "target='diffwindow'": "";
   211    210     if( tmFlags & TIMELINE_GRAPH ){
   212    211       pGraph = graph_init();
   213    212       /* style is not moved to css, because this is
   214    213       ** a technical div for the timeline graph
   215    214       */
   216    215       @ <div id="canvas" style="position:relative;height:0px;width:0px;"
   217    216       @  onclick="clickOnGraph(event)"></div>
................................................................................
   445    444           const char *zNew = db_column_text(&fchngQuery, 3);
   446    445           if( !inUl ){
   447    446             @ <ul class="filelist">
   448    447             inUl = 1;
   449    448           }
   450    449           if( isNew ){
   451    450             @ <li> %h(zFilename) (new file) &nbsp;
   452         -          @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew))
          451  +          @ %z(xhref(zDiffTarget,"%R/artifact/%S",zNew))
   453    452             @ [view]</a></li>
   454    453           }else if( isDel ){
   455    454             @ <li> %h(zFilename) (deleted)</li>
   456    455           }else if( fossil_strcmp(zOld,zNew)==0 && zOldName!=0 ){
   457    456             @ <li> %h(zOldName) &rarr; %h(zFilename)
   458         -          @ %z(xhref("target='diffwindow'","%R/artifact/%S",zNew))
          457  +          @ %z(xhref(zDiffTarget,"%R/artifact/%S",zNew))
   459    458             @ [view]</a></li>
   460    459           }else{
   461    460             if( zOldName!=0 ){
   462    461               @ <li> %h(zOldName) &rarr; %h(zFilename)
   463    462             }else{
   464    463               @ <li> %h(zFilename) &nbsp;
   465    464             }
   466         -          @ %z(xhref("target='diffwindow'","%R/fdiff?v1=%S&v2=%S",zOld,zNew))
          465  +          @ %z(xhref(zDiffTarget,"%R/fdiff?v1=%S&v2=%S",zOld,zNew))
   467    466             @ [diff]</a></li>
   468    467           }
   469    468         }
   470    469         db_reset(&fchngQuery);
   471    470         if( inUl ){
   472    471           @ </ul>
   473    472         }