Changes On Branch jan-sbsdiff
Not logged in

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

Changes In Branch jan-sbsdiff Excluding Merge-Ins

This is equivalent to a diff from fd486df492 to a92908a6b5

2011-10-17
01:19
Merge the side-by-side diff code into trunk. check-in: 0bde74ea1e user: drh tags: trunk
01:18
Remove unused variables and surplus blank lines from the side-by-side diff code. Closed-Leaf check-in: a92908a6b5 user: drh tags: jan-sbsdiff
01:12
Add new warnings for when Fossil overwrites an unmanged file on "update" or "merge". Undo has been and continues to be available to recover the overwritten files. check-in: 39f979b08c user: drh tags: trunk
00:26
Rephrasing and fixed a typo. check-in: 8e358aa8c6 user: jan tags: jan-sbsdiff
2011-10-16
23:24
Merge the latest trunk changes into the side-by-side diff branch. check-in: 23c3affad4 user: drh tags: jan-sbsdiff
23:10
Fix a typo in the timeline EVENT table entries generated by "fossil rebuild" in the previous check-in. check-in: fd486df492 user: drh tags: trunk
23:00
Show tag changes on the timeline. A "fossil rebuild" is required to take advantage of this new feature. Also put a • character in front of non-checkin timeline entries to make them stand out more. check-in: 87540ed6e6 user: drh tags: trunk

Changes to src/diff.c.

   576    576       ** array of COPY/DELETE/INSERT triples.
   577    577       */
   578    578       free(c.aFrom);
   579    579       free(c.aTo);
   580    580       return c.aEdit;
   581    581     }
   582    582   }
          583  +
          584  +/*
          585  +** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum
          586  +** line length limit.
          587  +*/
          588  +static char *copylimline(char *out, DLine *dl, int lim){
          589  +  int len;
          590  +  len = dl->h & LENGTH_MASK;
          591  +  if( lim && len > lim ){
          592  +    memcpy(out, dl->z, lim-3);
          593  +    strcpy(&out[lim-3], "...");
          594  +  }else{
          595  +    memcpy(out, dl->z, len);
          596  +    out[len] = '\0';
          597  +  }
          598  +  return out;
          599  +}
          600  +
          601  +/*
          602  +** Output table body of a side-by-side diff. Prior to the call, the caller
          603  +** should have output:
          604  +**   <table class="sbsdiff">
          605  +**   <tr><th colspan="2" class="diffhdr">Old title</th><th/>
          606  +**   <th colspan="2" class="diffhdr">New title</th></tr>
          607  +**
          608  +** And after the call, it should output:
          609  +**   </table>
          610  +**
          611  +** Some good reference diffs in the fossil repository for testing:
          612  +** /vdiff?from=080d27a&to=4b0f813&detail=1
          613  +** /vdiff?from=636804745b&to=c1d78e0556&detail=1
          614  +** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1
          615  +** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1
          616  +*/
          617  +int html_sbsdiff(
          618  +  Blob *pA_Blob,   /* FROM file */
          619  +  Blob *pB_Blob,   /* TO file */
          620  +  int nContext,    /* Amount of context to unified diff */
          621  +  int ignoreEolWs  /* Ignore whitespace at the end of lines */
          622  +){
          623  +  DContext c;
          624  +  int i;
          625  +  int iFrom, iTo;
          626  +  char *linebuf;
          627  +  int collim=0; /* Currently not settable; allows a column limit for diffs */
          628  +  int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */
          629  +
          630  +  /* Prepare the input files */
          631  +  memset(&c, 0, sizeof(c));
          632  +  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
          633  +                             &c.nFrom, ignoreEolWs);
          634  +  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
          635  +                           &c.nTo, ignoreEolWs);
          636  +  if( c.aFrom==0 || c.aTo==0 ){
          637  +    free(c.aFrom);
          638  +    free(c.aTo);
          639  +    /* Note: This would be generated within a table. */
          640  +    @ <p class="generalError" style="white-space: nowrap">cannot compute
          641  +    @ difference between binary files</p>
          642  +    return 0;
          643  +  }
          644  +
          645  +  collim = collim < 4 ? 0 : collim;
          646  +
          647  +  /* Compute the difference */
          648  +  diff_all(&c);
          649  +
          650  +  linebuf = fossil_malloc(LENGTH_MASK+1);
          651  +  if( !linebuf ){
          652  +    free(c.aFrom);
          653  +    free(c.aTo);
          654  +    free(c.aEdit);
          655  +    return 0;
          656  +  }
          657  +
          658  +  iFrom=iTo=0;
          659  +  i=0;
          660  +  while( i<c.nEdit ){
          661  +    int j;
          662  +    /* Copied lines */
          663  +    for( j=0; j<c.aEdit[i]; j++){
          664  +      /* Hide lines which are copied and are further away from block boundaries
          665  +      ** than nConext lines. For each block with hidden lines, show a row
          666  +      ** notifying the user about the hidden rows.
          667  +      */
          668  +      if( j<nContext || j>c.aEdit[i]-nContext-1 ){
          669  +        @ <tr>
          670  +      }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
          671  +        @ <tr>
          672  +        @ <td class="meta" colspan="5" style="white-space: nowrap;">
          673  +        @ %d(c.aEdit[i]-2*nContext) hidden lines</td>
          674  +        @ </tr>
          675  +        if( !allowExp )
          676  +           continue;
          677  +        @ <tr style="display:none;">
          678  +      }else{
          679  +        if( !allowExp )
          680  +           continue;
          681  +        @ <tr style="display:none;">
          682  +      }
          683  +
          684  +      copylimline(linebuf, &c.aFrom[iFrom+j], collim);
          685  +      @ <td class="lineno">%d(iFrom+j+1)</td>
          686  +      @ <td class="srcline">%h(linebuf)</td>
          687  +
          688  +      @ <td> </td>
          689  +
          690  +      copylimline(linebuf, &c.aTo[iTo+j], collim);
          691  +      @ <td class="lineno">%d(iTo+j+1)</td>
          692  +      @ <td class="srcline">%h(linebuf)</td>
          693  +
          694  +      @ </tr>
          695  +    }
          696  +    iFrom+=c.aEdit[i];
          697  +    iTo+=c.aEdit[i];
          698  +
          699  +    if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){
          700  +      int lim;
          701  +      lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2];
          702  +
          703  +      /* Assume changed lines */
          704  +      for( j=0; j<lim; j++ ){
          705  +        @ <tr>
          706  +
          707  +        if( j<c.aEdit[i+1] ){
          708  +          copylimline(linebuf, &c.aFrom[iFrom+j], collim);
          709  +          @ <td class="changed lineno">%d(iFrom+j+1)</td>
          710  +          @ <td class="changed srcline">%h(linebuf)</td>
          711  +        }else{
          712  +          @ <td colspan="2" class="changedvoid"/>
          713  +        }
          714  +
          715  +        @ <td class="changed">|</td>
          716  +
          717  +        if( j<c.aEdit[i+2] ){
          718  +          copylimline(linebuf, &c.aTo[iTo+j], collim);
          719  +          @ <td class="changed lineno">%d(iTo+j+1)</td>
          720  +          @ <td class="changed srcline">%h(linebuf)</td>
          721  +        }else{
          722  +          @ <td colspan="2" class="changedvoid"/>
          723  +        }
          724  +
          725  +        @ </tr>
          726  +      }
          727  +      iFrom+=c.aEdit[i+1];
          728  +      iTo+=c.aEdit[i+2];
          729  +    }else{
          730  +
          731  +      /* Process deleted lines */
          732  +      for( j=0; j<c.aEdit[i+1]; j++ ){
          733  +        @ <tr>
          734  +
          735  +        copylimline(linebuf, &c.aFrom[iFrom+j], collim);
          736  +        @ <td class="removed lineno">%d(iFrom+j+1)</td>
          737  +        @ <td class="removed srcline">%h(linebuf)</td>
          738  +        @ <td>&lt;</td>
          739  +        @ <td colspan="2" class="removedvoid"/>
          740  +        @ </tr>
          741  +      }
          742  +      iFrom+=c.aEdit[i+1];
          743  +
          744  +      /* Process inserted lines */
          745  +      for( j=0; j<c.aEdit[i+2]; j++ ){
          746  +        @ <tr>
          747  +        @ <td colspan="2" class="addedvoid"/>
          748  +        @ <td>&gt;</td>
          749  +        copylimline(linebuf, &c.aTo[iTo+j], collim);
          750  +        @ <td class="added lineno">%d(iTo+j+1)</td>
          751  +        @ <td class="added srcline">%h(linebuf)</td>
          752  +        @ </tr>
          753  +      }
          754  +      iTo+=c.aEdit[i+2];
          755  +    }
          756  +
          757  +    i+=3;
          758  +  }
          759  +
          760  +  free(linebuf);
          761  +  free(c.aFrom);
          762  +  free(c.aTo);
          763  +  free(c.aEdit);
          764  +  return 1;
          765  +}
          766  +
   583    767   
   584    768   /*
   585    769   ** COMMAND: test-rawdiff
   586    770   */
   587    771   void test_rawdiff_cmd(void){
   588    772     Blob a, b;
   589    773     int r;

Changes to src/info.c.

   274    274     blob_zero(&out);
   275    275     text_diff(&from, &to, &out, 5, 1);
   276    276     @ %h(blob_str(&out))
   277    277     blob_reset(&from);
   278    278     blob_reset(&to);
   279    279     blob_reset(&out);  
   280    280   }
          281  +
          282  +
          283  +/*
          284  +** Write the difference between two RIDs to the output
          285  +*/
          286  +static void generate_sbsdiff(const char *zFrom, const char *zTo){
          287  +  int fromid;
          288  +  int toid;
          289  +  Blob from, to;
          290  +  if( zFrom ){
          291  +    fromid = uuid_to_rid(zFrom, 0);
          292  +    content_get(fromid, &from);
          293  +  }else{
          294  +    blob_zero(&from);
          295  +  }
          296  +  if( zTo ){
          297  +    toid = uuid_to_rid(zTo, 0);
          298  +    content_get(toid, &to);
          299  +  }else{
          300  +    blob_zero(&to);
          301  +  }
          302  +  @ <table class="sbsdiff">
          303  +  @ <tr><th colspan="2" class="diffhdr">Old (%S(zFrom))</th><th/>
          304  +  @ <th colspan="2" class="diffhdr">New (%S(zTo))</th></tr>
          305  +  html_sbsdiff(&from, &to, 5, 1);
          306  +  @ </table>
          307  +  blob_reset(&from);
          308  +  blob_reset(&to);
          309  +}
          310  +
   281    311   
   282    312   /*
   283    313   ** Write a line of web-page output that shows changes that have occurred 
   284    314   ** to a file between two check-ins.
   285    315   */
   286    316   static void append_file_change_line(
   287    317     const char *zName,    /* Name of the file that has changed */
   288    318     const char *zOld,     /* blob.uuid before change.  NULL for added files */
   289    319     const char *zNew,     /* blob.uuid after change.  NULL for deletes */
   290    320     const char *zOldName, /* Prior name.  NULL if no name change. */
   291    321     int showDiff,         /* Show edit diffs if true */
          322  +  int sideBySide,       /* Show diffs side-by-side */
   292    323     int mperm             /* executable or symlink permission for zNew */
   293    324   ){
   294    325     if( !g.perm.History ){
   295    326       if( zNew==0 ){
   296    327         @ <p>Deleted %h(zName)</p>
   297    328       }else if( zOld==0 ){
   298    329         @ <p>Added %h(zName)</p>
................................................................................
   327    358         @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   328    359         @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
   329    360       }else{
   330    361         @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
   331    362         @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a>
   332    363       }
   333    364       if( showDiff ){
   334         -      @ <blockquote><pre>
   335         -      append_diff(zOld, zNew);
   336         -      @ </pre></blockquote>
          365  +      if( sideBySide ){
          366  +         generate_sbsdiff(zOld, zNew);
          367  +      }else{
          368  +        @ <blockquote><pre>
          369  +        append_diff(zOld, zNew);
          370  +        @ </pre></blockquote>
          371  +      }
   337    372       }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
   338    373         @ &nbsp;&nbsp;
   339    374         @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
   340    375       }
   341    376       @ </p>
   342    377     }
   343    378   }
................................................................................
   359    394   ** "show-version-diffs" setting is turned on.
   360    395   */
   361    396   void ci_page(void){
   362    397     Stmt q;
   363    398     int rid;
   364    399     int isLeaf;
   365    400     int showDiff;
          401  +  int sideBySide;
   366    402     const char *zName;   /* Name of the checkin to be displayed */
   367    403     const char *zUuid;   /* UUID of zName */
   368    404     const char *zParent; /* UUID of the parent checkin (if any) */
   369    405   
   370    406     login_check_credentials();
   371    407     if( !g.perm.Read ){ login_needed(); return; }
   372    408     zName = P("name");
................................................................................
   388    424        "SELECT uuid, datetime(mtime, 'localtime'), user, comment,"
   389    425        "       datetime(omtime, 'localtime')"
   390    426        "  FROM blob, event"
   391    427        " WHERE blob.rid=%d"
   392    428        "   AND event.objid=%d",
   393    429        rid, rid
   394    430     );
          431  +  sideBySide = atoi(PD("sbs","1"));
   395    432     if( db_step(&q)==SQLITE_ROW ){
   396    433       const char *zUuid = db_column_text(&q, 0);
   397    434       char *zTitle = mprintf("Check-in [%.10s]", zUuid);
   398    435       char *zEUser, *zEComment;
   399    436       const char *zUser;
   400    437       const char *zComment;
   401    438       const char *zDate;
................................................................................
   509    546     if( zParent ){
   510    547       @ <div class="section">Changes</div>
   511    548       showDiff = g.zPath[0]!='c';
   512    549       if( db_get_boolean("show-version-diffs", 0)==0 ){
   513    550         showDiff = !showDiff;
   514    551         if( showDiff ){
   515    552           @ <a href="%s(g.zTop)/vinfo/%T(zName)">[hide&nbsp;diffs]</a>
          553  +        @ &nbsp;&nbsp;
          554  +        if( sideBySide ){
          555  +          @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=0">
          556  +          @ [unified&nbsp;diffs]</a>
          557  +        }else{
          558  +          @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=1">
          559  +          @ [side-by-side&nbsp;diffs]</a>
          560  +        }
   516    561         }else{
   517         -        @ <a href="%s(g.zTop)/ci/%T(zName)">[show&nbsp;diffs]</a>
          562  +        @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=0">
          563  +        @ [show&nbsp;unified&nbsp;diffs]</a>
          564  +        @ &nbsp;&nbsp;
          565  +        @ <a href="%s(g.zTop)/ci/%T(zName)?sbs=1">
          566  +        @ [show&nbsp;side-by-side&nbsp;diffs]</a>
   518    567         }
   519    568       }else{
   520    569         if( showDiff ){
   521    570           @ <a href="%s(g.zTop)/ci/%T(zName)">[hide&nbsp;diffs]</a>
          571  +        @ &nbsp;&nbsp;
          572  +        if( sideBySide ){
          573  +          @ <a href="%s(g.zTop)/info/%T(zName)?sbs=0">
          574  +          @ [unified&nbsp;diffs]</a>
          575  +        }else{
          576  +          @ <a href="%s(g.zTop)/info/%T(zName)?sbs=1">
          577  +          @ [side-by-side&nbsp;diffs]</a>
          578  +        }
   522    579         }else{
   523         -        @ <a href="%s(g.zTop)/vinfo/%T(zName)">[show&nbsp;diffs]</a>
          580  +        @ <a href="%s(g.zTop)/vinfo/%T(zName)?sbs=0">
          581  +        @ [show&nbsp;unified&nbsp;diffs]</a>
          582  +        @ &nbsp;&nbsp;
          583  +        @ <a href="%s(g.zTop)/vinfo/%T(zName)?sbs=1">
          584  +        @ [show&nbsp;side-by-side&nbsp;diffs]</a>
   524    585         }
   525    586       }
   526    587       @ &nbsp;&nbsp;
   527    588       @ <a href="%s(g.zTop)/vpatch?from=%S(zParent)&to=%S(zUuid)">[patch]</a><br/>
   528    589       db_prepare(&q,
   529    590          "SELECT name,"
   530    591          "       mperm,"
................................................................................
   538    599       );
   539    600       while( db_step(&q)==SQLITE_ROW ){
   540    601         const char *zName = db_column_text(&q,0);
   541    602         int mperm = db_column_int(&q, 1);
   542    603         const char *zOld = db_column_text(&q,2);
   543    604         const char *zNew = db_column_text(&q,3);
   544    605         const char *zOldName = db_column_text(&q, 4);
   545         -      append_file_change_line(zName, zOld, zNew, zOldName, showDiff, mperm);
          606  +      append_file_change_line(zName, zOld, zNew, zOldName, showDiff,
          607  +            sideBySide, mperm);
   546    608       }
   547    609       db_finalize(&q);
   548    610     }
   549    611     style_footer();
   550    612   }
   551    613   
   552    614   /*
................................................................................
   688    750     }
   689    751     db_finalize(&q);
   690    752   }
   691    753   
   692    754   
   693    755   /*
   694    756   ** WEBPAGE: vdiff
   695         -** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN
          757  +** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN;sbs=BOOLEAN
   696    758   **
   697    759   ** Show all differences between two checkins.  
   698    760   */
   699    761   void vdiff_page(void){
   700    762     int ridFrom, ridTo;
   701    763     int showDetail = 0;
          764  +  int sideBySide = 0;
   702    765     Manifest *pFrom, *pTo;
   703    766     ManifestFile *pFileFrom, *pFileTo;
   704    767   
   705    768     login_check_credentials();
   706    769     if( !g.perm.Read ){ login_needed(); return; }
   707    770     login_anonymous_available();
   708    771   
   709    772     pFrom = vdiff_parse_manifest("from", &ridFrom);
   710    773     if( pFrom==0 ) return;
   711    774     pTo = vdiff_parse_manifest("to", &ridTo);
   712    775     if( pTo==0 ) return;
   713    776     showDetail = atoi(PD("detail","0"));
          777  +  sideBySide = atoi(PD("sbs","1"));
          778  +  if( !sideBySide ){
          779  +    style_submenu_element("Side-by-side Diff", "sbsdiff",
          780  +                          "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1",
          781  +                          g.zTop, P("from"), P("to"), showDetail);
          782  +  }else{
          783  +    style_submenu_element("Unified Diff", "udiff",
          784  +                          "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0",
          785  +                          g.zTop, P("from"), P("to"), showDetail);
          786  +  }
   714    787     style_header("Check-in Differences");
   715    788     @ <h2>Difference From:</h2><blockquote>
   716    789     checkin_description(ridFrom);
   717    790     @ </blockquote><h2>To:</h2><blockquote>
   718    791     checkin_description(ridTo);
   719    792     @ </blockquote><hr /><p>
   720    793   
................................................................................
   729    802       }else if( pFileTo==0 ){
   730    803         cmp = -1;
   731    804       }else{
   732    805         cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
   733    806       }
   734    807       if( cmp<0 ){
   735    808         append_file_change_line(pFileFrom->zName, 
   736         -                              pFileFrom->zUuid, 0, 0, 0, 0);
          809  +                              pFileFrom->zUuid, 0, 0, 0, 0, 0);
   737    810         pFileFrom = manifest_file_next(pFrom, 0);
   738    811       }else if( cmp>0 ){
   739    812         append_file_change_line(pFileTo->zName, 
   740         -                              0, pFileTo->zUuid, 0, 0,
          813  +                              0, pFileTo->zUuid, 0, 0, 0,
   741    814                                 manifest_file_mperm(pFileTo));
   742    815         pFileTo = manifest_file_next(pTo, 0);
   743    816       }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
   744    817         /* No changes */
   745    818         pFileFrom = manifest_file_next(pFrom, 0);
   746    819         pFileTo = manifest_file_next(pTo, 0);
   747    820       }else{
   748    821         append_file_change_line(pFileFrom->zName, 
   749    822                                 pFileFrom->zUuid,
   750         -                              pFileTo->zUuid, 0, showDetail,
          823  +                              pFileTo->zUuid, 0, showDetail, sideBySide,
   751    824                                 manifest_file_mperm(pFileTo));
   752    825         pFileFrom = manifest_file_next(pFrom, 0);
   753    826         pFileTo = manifest_file_next(pTo, 0);
   754    827       }
   755    828     }
   756    829     manifest_destroy(pFrom);
   757    830     manifest_destroy(pTo);
................................................................................
   981   1054       @ <a href="%s(g.zTop)/artifact/%S(zUuid)">[view]</a>
   982   1055     }
   983   1056   }
   984   1057   
   985   1058   
   986   1059   /*
   987   1060   ** WEBPAGE: fdiff
   988         -** URL: fdiff?v1=UUID&v2=UUID&patch
         1061  +** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN
   989   1062   **
   990         -** Two arguments, v1 and v2, identify the files to be diffed.  Show the 
   991         -** difference between the two artifacts.  Generate plaintext if "patch"
   992         -** is present.
         1063  +** Two arguments, v1 and v2, identify the files to be diffed.  Show the
         1064  +** difference between the two artifacts.  Show diff side by side unless sbs
         1065  +** is 0.  Generate plaintext if "patch" is present.
   993   1066   */
   994   1067   void diff_page(void){
   995   1068     int v1, v2;
   996   1069     int isPatch;
         1070  +  int sideBySide;
   997   1071     Blob c1, c2, diff, *pOut;
   998   1072     char *zV1;
   999   1073     char *zV2;
  1000   1074   
  1001   1075     login_check_credentials();
  1002   1076     if( !g.perm.Read ){ login_needed(); return; }
  1003   1077     v1 = name_to_rid_www("v1");
  1004   1078     v2 = name_to_rid_www("v2");
  1005   1079     if( v1==0 || v2==0 ) fossil_redirect_home();
         1080  +  sideBySide = atoi(PD("sbs","1"));
  1006   1081     zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
  1007   1082     zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
  1008   1083     isPatch = P("patch")!=0;
  1009   1084     if( isPatch ){
  1010   1085       pOut = cgi_output_blob();
  1011   1086       cgi_set_content_type("text/plain");
  1012   1087     }else{
  1013   1088       blob_zero(&diff);
  1014   1089       pOut = &diff;
  1015   1090     }
  1016         -  content_get(v1, &c1);
  1017         -  content_get(v2, &c2);
  1018         -  text_diff(&c1, &c2, pOut, 4, 1);
  1019         -  blob_reset(&c1);
  1020         -  blob_reset(&c2);
         1091  +  if( !sideBySide || isPatch ){
         1092  +    content_get(v1, &c1);
         1093  +    content_get(v2, &c2);
         1094  +    text_diff(&c1, &c2, pOut, 4, 1);
         1095  +    blob_reset(&c1);
         1096  +    blob_reset(&c2);
         1097  +  }
  1021   1098     if( !isPatch ){
  1022   1099       style_header("Diff");
  1023   1100       style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
  1024   1101                             g.zTop, P("v1"), P("v2"));
         1102  +    if( !sideBySide ){
         1103  +      style_submenu_element("Side-by-side Diff", "sbsdiff",
         1104  +                            "%s/fdiff?v1=%T&v2=%T&sbs=1",
         1105  +                            g.zTop, P("v1"), P("v2"));
         1106  +    }else{
         1107  +      style_submenu_element("Unified Diff", "udiff",
         1108  +                            "%s/fdiff?v1=%T&v2=%T&sbs=0",
         1109  +                            g.zTop, P("v1"), P("v2"));
         1110  +    }
         1111  +
  1025   1112       @ <h2>Differences From
  1026   1113       @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2>
  1027   1114       object_description(v1, 0, 0);
  1028   1115       @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2>
  1029   1116       object_description(v2, 0, 0);
  1030   1117       @ <hr />
  1031         -    @ <blockquote><pre>
  1032         -    @ %h(blob_str(&diff))
  1033         -    @ </pre></blockquote>
         1118  +    if( sideBySide ){
         1119  +      generate_sbsdiff(zV1, zV2);
         1120  +    }else{
         1121  +      @ <blockquote><pre>
         1122  +      @ %h(blob_str(&diff))
         1123  +      @ </pre></blockquote>
         1124  +    }
  1034   1125       blob_reset(&diff);
  1035   1126       style_footer();
  1036   1127     }
  1037   1128   }
  1038   1129   
  1039   1130   /*
  1040   1131   ** WEBPAGE: raw

Changes to src/skins.c.

   150    150   @ }
   151    151   @ 
   152    152   @ /* The label/value pairs on (for example) the vinfo page */
   153    153   @ table.label-value th {
   154    154   @   vertical-align: top;
   155    155   @   text-align: right;
   156    156   @   padding: 0.2ex 2ex;
          157  +@ }
          158  +@
          159  +@ /* Side-by-side diff */
          160  +@ table.sbsdiff {
          161  +@   background-color: white;
          162  +@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
          163  +@   font-size: 10pt;
          164  +@   border-collapse:collapse;
          165  +@   white-space: pre;
          166  +@   width: 98%;
          167  +@   border: 1px #000 dashed;
          168  +@ }
          169  +@
          170  +@ table.sbsdiff th.diffhdr {
          171  +@   border-bottom: dotted;
          172  +@   border-width: 1px;
          173  +@ }
          174  +@
          175  +@ table.sbsdiff tr td {
          176  +@   white-space: pre;
          177  +@   padding-left: 3px;
          178  +@   padding-right: 3px;
          179  +@   margin: 0px;
          180  +@ }
          181  +@
          182  +@ table.sbsdiff tr td.lineno {
          183  +@   text-align: right;
          184  +@ }
          185  +@
          186  +@ table.sbsdiff tr td.meta {
          187  +@   color: white;
          188  +@   background-color: rgb(20, 20, 20);
          189  +@   text-align: center;
          190  +@ }
          191  +@
          192  +@ table.sbsdiff tr td.added {
          193  +@   background-color: rgb(230, 230, 230);
          194  +@ }
          195  +@
          196  +@ table.sbsdiff tr td.removed {
          197  +@   background-color: rgb(200, 200, 200);
          198  +@ }
          199  +@
          200  +@ table.sbsdiff tr td.changed {
          201  +@   background-color: rgb(220, 220, 220);
   157    202   @ }');
   158    203   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   159    204   @ <head>
   160    205   @ <title>$<project_name>: $<title></title>
   161    206   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   162    207   @       href="$home/timeline.rss">
   163    208   @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
................................................................................
   354    399   @ 
   355    400   @ /* The label/value pairs on (for example) the ci page */
   356    401   @ table.label-value th {
   357    402   @   vertical-align: top;
   358    403   @   text-align: right;
   359    404   @   padding: 0.2ex 2ex;
   360    405   @ }
   361         -@ ');
          406  +@
          407  +@ /* Side-by-side diff */
          408  +@ table.sbsdiff {
          409  +@   background-color: #ffffc5;
          410  +@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
          411  +@   font-size: 10pt;
          412  +@   border-collapse:collapse;
          413  +@   white-space: pre;
          414  +@   width: 98%;
          415  +@   border: 1px #000 dashed;
          416  +@ }
          417  +@
          418  +@ table.sbsdiff th.diffhdr {
          419  +@   border-bottom: dotted;
          420  +@   border-width: 1px;
          421  +@ }
          422  +@
          423  +@ table.sbsdiff tr td {
          424  +@   white-space: pre;
          425  +@   padding-left: 3px;
          426  +@   padding-right: 3px;
          427  +@   margin: 0px;
          428  +@ }
          429  +@
          430  +@ table.sbsdiff tr td.lineno {
          431  +@   text-align: right;
          432  +@ }
          433  +@
          434  +@ table.sbsdiff tr td.meta {
          435  +@   background-color: #a09048;
          436  +@   text-align: center;
          437  +@ }
          438  +@
          439  +@ table.sbsdiff tr td.added {
          440  +@   background-color: rgb(210, 210, 100);
          441  +@ }
          442  +@
          443  +@ table.sbsdiff tr td.removed {
          444  +@   background-color: rgb(190, 200, 110);
          445  +@ }
          446  +@
          447  +@ table.sbsdiff tr td.changed {
          448  +@   background-color: rgb(200, 210, 120);
          449  +@ }');
   362    450   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   363    451   @ <head>
   364    452   @ <title>$<project_name>: $<title></title>
   365    453   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   366    454   @       href="$home/timeline.rss">
   367    455   @ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
   368    456   @       media="screen">
................................................................................
   588    676   @ }
   589    677   @ 
   590    678   @ /* The label/value pairs on (for example) the ci page */
   591    679   @ table.label-value th {
   592    680   @   vertical-align: top;
   593    681   @   text-align: right;
   594    682   @   padding: 0.2ex 2ex;
          683  +@ }
          684  +@
          685  +@ /* Side-by-side diff */
          686  +@ table.sbsdiff {
          687  +@   background-color: white;
          688  +@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
          689  +@   font-size: 10pt;
          690  +@   border-collapse:collapse;
          691  +@   white-space: pre;
          692  +@   width: 98%;
          693  +@   border: 1px #000 dashed;
          694  +@ }
          695  +@
          696  +@ table.sbsdiff th.diffhdr {
          697  +@   border-bottom: dotted;
          698  +@   border-width: 1px;
          699  +@ }
          700  +@
          701  +@ table.sbsdiff tr td {
          702  +@   white-space: pre;
          703  +@   padding-left: 3px;
          704  +@   padding-right: 3px;
          705  +@   margin: 0px;
          706  +@ }
          707  +@
          708  +@ table.sbsdiff tr td.lineno {
          709  +@   text-align: right;
          710  +@ }
          711  +@
          712  +@ table.sbsdiff tr td.meta {
          713  +@   color: white;
          714  +@   background-color: black;
          715  +@   text-align: center;
          716  +@ }
          717  +@
          718  +@ table.sbsdiff tr td.added {
          719  +@   background-color: white;
          720  +@ }
          721  +@
          722  +@ table.sbsdiff tr td.removed {
          723  +@   background-color: white;
          724  +@   text-decoration: line-through;
          725  +@ }
          726  +@
          727  +@ table.sbsdiff tr td.changed {
          728  +@   background-color: white;
   595    729   @ }');
   596    730   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   597    731   @ <head>
   598    732   @ <title>$<project_name>: $<title></title>
   599    733   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   600    734   @       href="$home/timeline.rss">
   601    735   @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
................................................................................
   883   1017   @ 
   884   1018   @ table.report tr td {
   885   1019   @   padding: 3px 5px;
   886   1020   @ }
   887   1021   @ 
   888   1022   @ textarea {
   889   1023   @   font-size: 1em;
         1024  +@ }
         1025  +@
         1026  +@ /* Side-by-side diff */
         1027  +@ table.sbsdiff {
         1028  +@   background-color: white;
         1029  +@   font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
         1030  +@   font-size: 8pt;
         1031  +@   border-collapse:collapse;
         1032  +@   width: 98%;
         1033  +@   border: 1px #000 dashed;
         1034  +@   margin-left: auto;
         1035  +@   margin-right: auto;
         1036  +@ }
         1037  +@
         1038  +@ table.sbsdiff th.diffhdr {
         1039  +@   border-bottom: dotted;
         1040  +@   border-width: 1px;
         1041  +@ }
         1042  +@
         1043  +@ table.sbsdiff tr td {
         1044  +@   padding-left: 3px;
         1045  +@   padding-right: 3px;
         1046  +@   margin: 0px;
         1047  +@   vertical-align: top;
         1048  +@   white-space: pre-wrap;
         1049  +@ }
         1050  +@
         1051  +@ table.sbsdiff tr td.lineno {
         1052  +@   text-align: right;
         1053  +@   /* border-bottom: 1px solid rgb(220, 220, 220); */
         1054  +@ }
         1055  +@
         1056  +@ table.sbsdiff tr td.srcline {
         1057  +@   max-width: 400px;
         1058  +@   /* Note: May partially hide long lines without whitespaces */
         1059  +@   overflow: hidden;
         1060  +@   /* border-bottom: 1px solid rgb(220, 220, 220); */
         1061  +@ }
         1062  +@
         1063  +@ table.sbsdiff tr td.meta {
         1064  +@   background-color: rgb(170, 160, 255);
         1065  +@   padding-top: 0.25em;
         1066  +@   padding-bottom: 0.25em;
         1067  +@   text-align: center;
         1068  +@   -moz-border-radius: 5px;
         1069  +@   -moz-border-radius: 5px;
         1070  +@   -webkit-border-radius: 5px;
         1071  +@   -webkit-border-radius: 5px;
         1072  +@   -border-radius: 5px;
         1073  +@   -border-radius: 5px;
         1074  +@   border-radius: 5px;
         1075  +@   border-radius: 5px;
         1076  +@ }
         1077  +@
         1078  +@ table.sbsdiff tr td.added {
         1079  +@   background-color: rgb(180, 250, 180);
         1080  +@   /* border-bottom: 1px solid rgb(160, 230, 160); */
         1081  +@ }
         1082  +@
         1083  +@ table.sbsdiff tr td.removed {
         1084  +@   background-color: rgb(250, 130, 130);
         1085  +@   /* border-bottom: 1px solid rgb(230, 110, 110); */
         1086  +@ }
         1087  +@
         1088  +@ table.sbsdiff tr td.changed {
         1089  +@   background-color: rgb(210, 210, 200);
         1090  +@   /* border-bottom: 1px solid rgb(190, 190, 180); */
   890   1091   @ }');
   891   1092   @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
   892   1093   @ <head>
   893   1094   @ <title>$<project_name>: $<title></title>
   894   1095   @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
   895   1096   @       href="$home/timeline.rss">
   896   1097   @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"

Changes to src/style.c.

   394    394   @
   395    395   @ /* The label/value pairs on (for example) the ci page */
   396    396   @ table.label-value th {
   397    397   @   vertical-align: top;
   398    398   @   text-align: right;
   399    399   @   padding: 0.2ex 2ex;
   400    400   @ }
          401  +@
          402  +@ /* Side-by-side diff */
          403  +@ table.sbsdiff {
          404  +@   background-color: white;
          405  +@   font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
          406  +@   font-size: 10pt;
          407  +@   border-collapse:collapse;
          408  +@   white-space: pre;
          409  +@   width: 98%;
          410  +@   border: 1px #000 dashed;
          411  +@   margin-left: auto;
          412  +@   margin-right: auto;
          413  +@ }
          414  +@
          415  +@ table.sbsdiff th.diffhdr {
          416  +@   border-bottom: dotted;
          417  +@   border-width: 1px;
          418  +@ }
          419  +@
          420  +@ table.sbsdiff tr td {
          421  +@   white-space: pre;
          422  +@   padding-left: 3px;
          423  +@   padding-right: 3px;
          424  +@   margin: 0px;
          425  +@   vertical-align: top;
          426  +@ }
          427  +@
          428  +@ table.sbsdiff tr td.lineno {
          429  +@   text-align: right;
          430  +@ }
          431  +@
          432  +@ table.sbsdiff tr td.srcline {
          433  +@ }
          434  +@
          435  +@ table.sbsdiff tr td.meta {
          436  +@   background-color: rgb(170, 160, 255);
          437  +@   text-align: center;
          438  +@ }
          439  +@
          440  +@ table.sbsdiff tr td.added {
          441  +@   background-color: rgb(180, 250, 180);
          442  +@ }
          443  +@ table.sbsdiff tr td.addedvoid {
          444  +@   background-color: rgb(190, 190, 180);
          445  +@ }
          446  +@
          447  +@ table.sbsdiff tr td.removed {
          448  +@   background-color: rgb(250, 130, 130);
          449  +@ }
          450  +@ table.sbsdiff tr td.removedvoid {
          451  +@   background-color: rgb(190, 190, 180);
          452  +@ }
          453  +@
          454  +@ table.sbsdiff tr td.changed {
          455  +@   background-color: rgb(210, 210, 200);
          456  +@ }
          457  +@ table.sbsdiff tr td.changedvoid {
          458  +@   background-color: rgb(190, 190, 180);
          459  +@ }
   401    460   @
   402    461   ;
   403    462   
   404    463   
   405    464   /* The following table contains bits of default CSS that must
   406    465   ** be included if they are not found in the application-defined
   407    466   ** CSS.