Changes On Branch diff-enhancements
Not logged in

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

Changes In Branch diff-enhancements Excluding Merge-Ins

This is equivalent to a diff from a75e2d2504 to a505abccc6

2011-10-21
21:55
Merge the side-by-side diff spacing bug fix into trunk. check-in: 54e730c339 user: drh tags: trunk
21:50
Fix a spacing bug in the display of side-by-side diffs. Closed-Leaf check-in: a505abccc6 user: drh tags: diff-enhancements
21:34
Merge the diff enhancements into trunk. check-in: c244605862 user: drh tags: trunk
21:31
Add support for side-by-side diff from the command-line "diff" command. check-in: ac81759f65 user: drh tags: diff-enhancements
20:24
Begin improvement efforts on the "diff" functions by adding the --context option to the "diff" command. check-in: 3bbbbdfd7d user: drh tags: diff-enhancements
12:52
Version 1.20. check-in: a75e2d2504 user: drh tags: trunk, release, version-1.20
2011-10-20
17:10
minor 1.20 changelog tweak. check-in: 1d12fcc416 user: stephan tags: trunk

Changes to src/diff.c.

19
20
21
22
23
24
25












26
27
28
29
30
31
32
...
283
284
285
286
287
288
289






































































































































































290
291
292
293
294
295
296
...
536
537
538
539
540
541
542
543
544
545
546


547
548




549
550
551
552
553
554
555
...
561
562
563
564
565
566
567
568





569

570
571
572
573
574
575
576
...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794








795
796
797
798
799
800
801
802
803
804
805
806
** text files.
*/
#include "config.h"
#include "diff.h"
#include <assert.h>














/*
** Maximum length of a line in a text file.  (8192)
*/
#define LENGTH_MASK_SZ  13
#define LENGTH_MASK     ((1<<LENGTH_MASK_SZ)-1)

/*
................................................................................
    m = R[r+nr*3];
    if( m>nContext ) m = nContext;
    for(j=0; j<m; j++){
      appendDiffLine(pOut, " ", &B[b+j]);
    }
  }
}







































































































































































/*
** Compute the optimal longest common subsequence (LCS) using an
** exhaustive search.  This version of the LCS is only used for
** shorter input strings since runtime is O(N*N) where N is the
** input string length.
*/
................................................................................
** This diff utility does not work on binary files.  If a binary
** file is encountered, 0 is returned and pOut is written with
** text "cannot compute difference between binary files".
*/
int *text_diff(
  Blob *pA_Blob,   /* FROM file */
  Blob *pB_Blob,   /* TO file */
  Blob *pOut,      /* Write unified diff here if not NULL */
  int nContext,    /* Amount of context to unified diff */
  int ignoreEolWs  /* Ignore whitespace at the end of lines */
){


  DContext c;
 




  /* Prepare the input files */
  memset(&c, 0, sizeof(c));
  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
                             &c.nFrom, ignoreEolWs);
  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
                           &c.nTo, ignoreEolWs);
  if( c.aFrom==0 || c.aTo==0 ){
................................................................................
    return 0;
  }

  /* Compute the difference */
  diff_all(&c);

  if( pOut ){
    /* Compute a context diff if requested */





    contextDiff(&c, pOut, nContext);

    free(c.aFrom);
    free(c.aTo);
    free(c.aEdit);
    return 0;
  }else{
    /* If a context diff is not requested, then return the
    ** array of COPY/DELETE/INSERT triples.
................................................................................
  iFrom=iTo=0;
  i=0;
  while( i<c.nEdit ){
    int j;
    /* Copied lines */
    for( j=0; j<c.aEdit[i]; j++){
      /* Hide lines which are copied and are further away from block boundaries
      ** than nConext lines. For each block with hidden lines, show a row
      ** notifying the user about the hidden rows.
      */
      if( j<nContext || j>c.aEdit[i]-nContext-1 ){
        @ <tr>
      }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
        @ <tr>
        @ <td class="meta" colspan="5" style="white-space: nowrap;">
................................................................................
  int i;
  int *R;
  if( g.argc<4 ) usage("FILE1 FILE2 ...");
  blob_read_from_file(&a, g.argv[2]);
  for(i=3; i<g.argc; i++){
    if( i>3 ) fossil_print("-------------------------------\n");
    blob_read_from_file(&b, g.argv[i]);
    R = text_diff(&a, &b, 0, 0, 0);
    for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
      fossil_print(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
    }
    /* free(R); */
    blob_reset(&b);
  }
}

/*
** COMMAND: test-udiff
*/
void test_udiff_cmd(void){
  Blob a, b, out;








  if( g.argc!=4 ) usage("FILE1 FILE2");
  blob_read_from_file(&a, g.argv[2]);
  blob_read_from_file(&b, g.argv[3]);
  blob_zero(&out);
  text_diff(&a, &b, &out, 3, 0);
  blob_write_to_file(&out, "-");
}

/**************************************************************************
** The basic difference engine is above.  What follows is the annotation
** engine.  Both are in the same file since they share many components.
*/






>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
<

>
>

|
>
>
>
>







 







|
>
>
>
>
>
|
>







 







|







 







|













>
>
>
>
>
>
>
>
|



|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
...
714
715
716
717
718
719
720
721
722

723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
...
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
...
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
...
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
** text files.
*/
#include "config.h"
#include "diff.h"
#include <assert.h>


#if INTERFACE
/*
** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
*/
#define DIFF_CONTEXT_MASK  0x0000fff  /* Lines of context.  Default if 0 */
#define DIFF_WIDTH_MASK    0x00ff000  /* side-by-side column width */
#define DIFF_IGNORE_EOLWS  0x0100000  /* Ignore end-of-line whitespace */
#define DIFF_SIDEBYSIDE    0x0200000  /* Generate a side-by-side diff */
#define DIFF_NEWFILE       0x0400000  /* Missing files are as empty files */

#endif /* INTERFACE */

/*
** Maximum length of a line in a text file.  (8192)
*/
#define LENGTH_MASK_SZ  13
#define LENGTH_MASK     ((1<<LENGTH_MASK_SZ)-1)

/*
................................................................................
    m = R[r+nr*3];
    if( m>nContext ) m = nContext;
    for(j=0; j<m; j++){
      appendDiffLine(pOut, " ", &B[b+j]);
    }
  }
}

/*
** Append spaces to a blob
*/
static void appendSpace(Blob *pOut, int n){
  const char z100[101] = 
     "                                                  "
     "                                                  ";
  while( n>100 ){
    blob_append(pOut, z100, 100); n -= 100;
  }
  if( n>0 ){
    blob_append(pOut, z100, n);
  }
}

/*
** Append text to a sbs diff output
*/
static void appendSbsLine(Blob *pOut, DLine *pLine, int width, int pad){
  int sz = pLine->h & LENGTH_MASK;
  if( sz<width ){
    blob_append(pOut, pLine->z, sz);
    if( pad ) appendSpace(pOut, width-sz);
  }else{
    blob_append(pOut, pLine->z, width);
  }
}


/*
** Given a diff context in which the aEdit[] array has been filled
** in, compute a side-by-side diff into pOut.
*/
static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
  DLine *A;     /* Left side of the diff */
  DLine *B;     /* Right side of the diff */  
  int a = 0;    /* Index of next line in A[] */
  int b = 0;    /* Index of next line in B[] */
  int *R;       /* Array of COPY/DELETE/INSERT triples */
  int r;        /* Index into R[] */
  int nr;       /* Number of COPY/DELETE/INSERT triples to process */
  int mxr;      /* Maximum value for r */
  int na, nb;   /* Number of lines shown from A and B */
  int i, j;     /* Loop counters */
  int m, ma, mb;/* Number of lines to output */
  int skip;     /* Number of lines to skip */
  char zFormat[50];  /* Output format */

  sqlite3_snprintf(sizeof(zFormat), zFormat,
                   "%%4d %%%d.%ds %%c %%4d %%.%ds\n",
                   width, width, width);
  A = p->aFrom;
  B = p->aTo;
  R = p->aEdit;
  mxr = p->nEdit;
  while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
  for(r=0; r<mxr; r += 3*nr){
    /* Figure out how many triples to show in a single block */
    for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
    /* printf("r=%d nr=%d\n", r, nr); */

    /* For the current block comprising nr triples, figure out
    ** how many lines of A and B are to be displayed
    */
    if( R[r]>nContext ){
      na = nb = nContext;
      skip = R[r] - nContext;
    }else{
      na = nb = R[r];
      skip = 0;
    }
    for(i=0; i<nr; i++){
      na += R[r+i*3+1];
      nb += R[r+i*3+2];
    }
    if( R[r+nr*3]>nContext ){
      na += nContext;
      nb += nContext;
    }else{
      na += R[r+nr*3];
      nb += R[r+nr*3];
    }
    for(i=1; i<nr; i++){
      na += R[r+i*3];
      nb += R[r+i*3];
    }
    /*
     * If the patch changes an empty file or results in an empty file,
     * the block header must use 0,0 as position indicator and not 1,0.
     * Otherwise, patch would be confused and may reject the diff.
     */
    blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n",
      na ? a+skip+1 : 0, na,
      nb ? b+skip+1 : 0, nb);

    /* Show the initial common area */
    a += skip;
    b += skip;
    m = R[r] - skip;
    for(j=0; j<m; j++){
      blob_appendf(pOut, "%6d ", a+j);
      appendSbsLine(pOut, &A[a+j], width, 1);
      blob_appendf(pOut, "   %6d ", b+j);
      appendSbsLine(pOut, &B[b+j], width, 0);
      blob_append(pOut, "\n", 1);
    }
    a += m;
    b += m;

    /* Show the differences */
    for(i=0; i<nr; i++){
      ma = R[r+i*3+1];
      mb = R[r+i*3+2];
      m = ma<mb ? ma : mb;
      for(j=0; j<m; j++){
        blob_appendf(pOut, "%6d ", a+j);
        appendSbsLine(pOut, &A[a+j], width, 1);
        blob_appendf(pOut, " | %6d ", b+j);
        appendSbsLine(pOut, &B[b+j], width, 0);
        blob_append(pOut, "\n", 1);
      }
      a += m;
      b += m;
      ma -= m;
      mb -= m;
      for(j=0; j<ma; j++){
        blob_appendf(pOut, "%6d ", a+j);
        appendSbsLine(pOut, &A[a+j], width, 1);
        blob_append(pOut, " <\n", 3);
      }
      a += ma;
      for(j=0; j<mb; j++){
        appendSpace(pOut, width+7);
        blob_appendf(pOut, " > %6d ", b+j);
        appendSbsLine(pOut, &B[b+j], width, 0);
        blob_append(pOut, "\n", 1);
      }
      b += mb;
      if( i<nr-1 ){
        m = R[r+i*3+3];
        for(j=0; j<m; j++){
          blob_appendf(pOut, "%6d ", a+j);
          appendSbsLine(pOut, &A[a+j], width, 1);
          blob_appendf(pOut, "   %6d ", b+j);
          appendSbsLine(pOut, &B[b+j], width, 0);
          blob_append(pOut, "\n", 1);
        }
        b += m;
        a += m;
      }
    }

    /* Show the final common area */
    assert( nr==i );
    m = R[r+nr*3];
    if( m>nContext ) m = nContext;
    for(j=0; j<m; j++){
      blob_appendf(pOut, "%6d ", a+j);
      appendSbsLine(pOut, &A[a+j], width, 1);
      blob_appendf(pOut, "   %6d ", b+j);
      appendSbsLine(pOut, &B[b+j], width, 0);
      blob_append(pOut, "\n", 1);
    }
  }
}

/*
** Compute the optimal longest common subsequence (LCS) using an
** exhaustive search.  This version of the LCS is only used for
** shorter input strings since runtime is O(N*N) where N is the
** input string length.
*/
................................................................................
** This diff utility does not work on binary files.  If a binary
** file is encountered, 0 is returned and pOut is written with
** text "cannot compute difference between binary files".
*/
int *text_diff(
  Blob *pA_Blob,   /* FROM file */
  Blob *pB_Blob,   /* TO file */
  Blob *pOut,      /* Write diff here if not NULL */
  int diffFlags    /* DIFF_* flags defined above */

){
  int ignoreEolWs; /* Ignore whitespace at the end of lines */
  int nContext;    /* Amount of context to display */	
  DContext c;

  nContext = diffFlags & DIFF_CONTEXT_MASK;
  if( nContext==0 ) nContext = 5;
  ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;

  /* Prepare the input files */
  memset(&c, 0, sizeof(c));
  c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
                             &c.nFrom, ignoreEolWs);
  c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
                           &c.nTo, ignoreEolWs);
  if( c.aFrom==0 || c.aTo==0 ){
................................................................................
    return 0;
  }

  /* Compute the difference */
  diff_all(&c);

  if( pOut ){
    /* Compute a context or side-by-side diff into pOut */
    if( diffFlags & DIFF_SIDEBYSIDE ){
      int width = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
      if( width==0 ) width = 80;
      sbsDiff(&c, pOut, nContext, width);
    }else{
      contextDiff(&c, pOut, nContext);
    }
    free(c.aFrom);
    free(c.aTo);
    free(c.aEdit);
    return 0;
  }else{
    /* If a context diff is not requested, then return the
    ** array of COPY/DELETE/INSERT triples.
................................................................................
  iFrom=iTo=0;
  i=0;
  while( i<c.nEdit ){
    int j;
    /* Copied lines */
    for( j=0; j<c.aEdit[i]; j++){
      /* Hide lines which are copied and are further away from block boundaries
      ** than nContext lines. For each block with hidden lines, show a row
      ** notifying the user about the hidden rows.
      */
      if( j<nContext || j>c.aEdit[i]-nContext-1 ){
        @ <tr>
      }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
        @ <tr>
        @ <td class="meta" colspan="5" style="white-space: nowrap;">
................................................................................
  int i;
  int *R;
  if( g.argc<4 ) usage("FILE1 FILE2 ...");
  blob_read_from_file(&a, g.argv[2]);
  for(i=3; i<g.argc; i++){
    if( i>3 ) fossil_print("-------------------------------\n");
    blob_read_from_file(&b, g.argv[i]);
    R = text_diff(&a, &b, 0, 0);
    for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
      fossil_print(" copy %4d  delete %4d  insert %4d\n", R[r], R[r+1], R[r+2]);
    }
    /* free(R); */
    blob_reset(&b);
  }
}

/*
** COMMAND: test-udiff
*/
void test_udiff_cmd(void){
  Blob a, b, out;
  int diffFlag = find_option("sbs",0,0)!=0 ? DIFF_SIDEBYSIDE : 0;
  int nContext = 5;
  const char *z;
  if( (z = find_option("context","c",1))!=0 && atoi(z)>0 ){
    nContext = atoi(z);
  }
  if( nContext<=0 ) nContext = 5;
  if( (nContext&DIFF_CONTEXT_MASK)!=nContext ) nContext = DIFF_CONTEXT_MASK;
  if( g.argc!=4 ) usage("[--sbs] [--context N] FILE1 FILE2");
  blob_read_from_file(&a, g.argv[2]);
  blob_read_from_file(&b, g.argv[3]);
  blob_zero(&out);
  text_diff(&a, &b, &out, nContext | diffFlag);
  blob_write_to_file(&out, "-");
}

/**************************************************************************
** The basic difference engine is above.  What follows is the annotation
** engine.  Both are in the same file since they share many components.
*/

Changes to src/diffcmd.c.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
..
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
...
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
...
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
...
454
455
456
457
458
459
460

461
462
463
464


465
466
467
468
469
470
471
472
473

474
475
476
477
478
479
480










481
482
483
484
485
486
487
**
** This file contains code used to implement the "diff" command
*/
#include "config.h"
#include "diffcmd.h"
#include <assert.h>

/*
** Diff option flags
*/
#define DIFF_NEWFILE  0x01    /* Treat non-existing fails as empty files */
#define DIFF_NOEOLWS  0x02    /* Ignore whitespace at the end of lines */

/*
** Output the results of a diff.  Output goes to stdout for command-line
** or to the CGI/HTTP result buffer for web pages.
*/
static void diff_printf(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
................................................................................
** command zDiffCmd to do the diffing.
*/
void diff_file(
  Blob *pFile1,             /* In memory content to compare from */
  const char *zFile2,       /* On disk content to compare to */
  const char *zName,        /* Display name of the file */
  const char *zDiffCmd,     /* Command for comparison */
  int ignoreEolWs           /* Ignore whitespace at end of line */
){
  if( zDiffCmd==0 ){
    Blob out;                 /* Diff output text */
    Blob file2;               /* Content of zFile2 */
    const char *zName2;       /* Name of zFile2 for display */

    /* Read content of zFile2 into memory */
................................................................................
        blob_read_from_file(&file2, zFile2);
      }
      zName2 = zName;
    }

    /* Compute and output the differences */
    blob_zero(&out);
    text_diff(pFile1, &file2, &out, 5, ignoreEolWs);
    if( blob_size(&out) ){
      diff_printf("--- %s\n+++ %s\n", zName, zName2);
      diff_printf("%s\n", blob_str(&out));
    }

    /* Release memory resources */
    blob_reset(&file2);
................................................................................
** command zDiffCmd to do the diffing.
*/
void diff_file_mem(
  Blob *pFile1,             /* In memory content to compare from */
  Blob *pFile2,             /* In memory content to compare to */
  const char *zName,        /* Display name of the file */
  const char *zDiffCmd,     /* Command for comparison */
  int ignoreEolWs           /* Ignore whitespace at end of lines */
){
  if( zDiffCmd==0 ){
    Blob out;      /* Diff output text */

    blob_zero(&out);
    text_diff(pFile1, pFile2, &out, 5, ignoreEolWs);
    diff_printf("--- %s\n+++ %s\n", zName, zName);
    diff_printf("%s\n", blob_str(&out));

    /* Release memory resources */
    blob_reset(&out);
  }else{
    Blob cmd;
................................................................................
/*
** Do a diff against a single file named in zFileTreeName from version zFrom
** against the same file on disk.
*/
static void diff_one_against_disk(
  const char *zFrom,        /* Name of file */
  const char *zDiffCmd,     /* Use this "diff" command */
  int ignoreEolWs,          /* Ignore whitespace changes at end of lines */
  const char *zFileTreeName
){
  Blob fname;
  Blob content;
  int isLink;
  file_tree_name(zFileTreeName, &fname, 1);
  historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
  if( !isLink != !file_wd_islink(zFrom) ){
    diff_printf("cannot compute difference between symlink and regular file\n");
  }else{
    diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
  }
  blob_reset(&content);
  blob_reset(&fname);
}

/*
** Run a diff between the version zFrom and files on disk.  zFrom might
................................................................................
  const char *zFrom,        /* Version to difference from */
  const char *zDiffCmd,     /* Use this diff command.  NULL for built-in */
  int diffFlags             /* Flags controlling diff output */
){
  int vid;
  Blob sql;
  Stmt q;
  int ignoreEolWs;          /* Ignore end-of-line whitespace */
  int asNewFile;            /* Treat non-existant files as empty files */

  ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0;
  asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid, 1, 0);
  blob_zero(&sql);
  db_begin_transaction();
  if( zFrom ){
    int rid = name_to_typed_rid(zFrom, "ci");
................................................................................
      }
      if( srcid>0 ){
        content_get(srcid, &content);
      }else{
        blob_zero(&content);
      }
      diff_print_index(zPathname);
      diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
      blob_reset(&content);
    }
    free(zToFree);
  }
  db_finalize(&q);
  db_end_transaction(1);  /* ROLLBACK */
}
................................................................................
** Output the differences between two versions of a single file.
** zFrom and zTo are the check-ins containing the two file versions.
*/
static void diff_one_two_versions(
  const char *zFrom,
  const char *zTo,
  const char *zDiffCmd,
  int ignoreEolWs,
  const char *zFileTreeName
){
  char *zName;
  Blob fname;
  Blob v1, v2;
  int isLink1, isLink2;
  file_tree_name(zFileTreeName, &fname, 1);
................................................................................
  zName = blob_str(&fname);
  historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
  historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
  if( isLink1 != isLink2 ){
    diff_printf("--- %s\n+++ %s\n", zName, zName);
    diff_printf("cannot compute difference between symlink and regular file\n");
  }else{
    diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
  }
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&fname);
}

/*
................................................................................
** Show the difference between two files identified by ManifestFile
** entries.
*/
static void diff_manifest_entry(
  struct ManifestFile *pFrom,
  struct ManifestFile *pTo,
  const char *zDiffCmd,
  int ignoreEolWs
){
  Blob f1, f2;
  int rid;
  const char *zName =  pFrom ? pFrom->zName : pTo->zName;
  diff_print_index(zName);
  if( pFrom ){
    rid = uuid_to_rid(pFrom->zUuid, 0);
................................................................................
  }
  if( pTo ){
    rid = uuid_to_rid(pTo->zUuid, 0);
    content_get(rid, &f2);
  }else{
    blob_zero(&f2);
  }
  diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs);
  blob_reset(&f1);
  blob_reset(&f2);
}

/*
** Output the differences between two check-ins.
*/
................................................................................
  const char *zFrom,
  const char *zTo,
  const char *zDiffCmd,
  int diffFlags
){
  Manifest *pFrom, *pTo;
  ManifestFile *pFromFile, *pToFile;
  int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
  int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;

  pFrom = manifest_get_by_name(zFrom, 0);
  manifest_file_rewind(pFrom);
  pFromFile = manifest_file_next(pFrom,0);
  pTo = manifest_get_by_name(zTo, 0);
  manifest_file_rewind(pTo);
................................................................................
      cmp = -1;
    }else{
      cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
    }
    if( cmp<0 ){
      diff_printf("DELETED %s\n", pFromFile->zName);
      if( asNewFlag ){
        diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
      }
      pFromFile = manifest_file_next(pFrom,0);
    }else if( cmp>0 ){
      diff_printf("ADDED   %s\n", pToFile->zName);
      if( asNewFlag ){
        diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
      }
      pToFile = manifest_file_next(pTo,0);
    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }else{
      /* diff_printf("CHANGED %s\n", pFromFile->zName); */
      diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }
  }
  manifest_destroy(pFrom);
  manifest_destroy(pTo);
}
................................................................................
** the "setting" command.  If no external diff program is configured, then
** the "-i" option is a no-op.  The "-i" option converts "gdiff" into "diff".
**
** The "-N" or "--new-file" option causes the complete text of added or
** deleted files to be displayed.
**
** Options:

**   --from|-r VERSION   select VERSION as source for the diff
**   --new-file|-N       output complete text of added or deleted files
**   -i                  use internal diff logic
**   --to VERSION        select VERSION as target for the diff


*/
void diff_cmd(void){
  int isGDiff;               /* True for gdiff.  False for normal diff */
  int isInternDiff;          /* True for internal diff */
  int hasNFlag;              /* True if -N or --new-file flag is used */
  const char *zFrom;         /* Source version number */
  const char *zTo;           /* Target version number */
  const char *zDiffCmd = 0;  /* External diff command. NULL for internal diff */
  int diffFlags = 0;         /* Flags to control the DIFF */

  int f;

  isGDiff = g.argv[1][0]=='g';
  isInternDiff = find_option("internal","i",0)!=0;
  zFrom = find_option("from", "r", 1);
  zTo = find_option("to", 0, 1);
  hasNFlag = find_option("new-file","N",0)!=0;












  if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
  if( zTo==0 ){
    db_must_be_within_tree();
    verify_all_options();
    if( !isInternDiff ){






<
<
<
<
<
<







 







|







 







|







 







|





|







 







|










|







 







<


<







 







|







 







|







 







|







 







|







 







|







 







<







 







|





|








|







 







>




>
>









>







>
>
>
>
>
>
>
>
>
>







17
18
19
20
21
22
23






24
25
26
27
28
29
30
..
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
...
207
208
209
210
211
212
213

214
215

216
217
218
219
220
221
222
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
...
371
372
373
374
375
376
377

378
379
380
381
382
383
384
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
**
** This file contains code used to implement the "diff" command
*/
#include "config.h"
#include "diffcmd.h"
#include <assert.h>







/*
** Output the results of a diff.  Output goes to stdout for command-line
** or to the CGI/HTTP result buffer for web pages.
*/
static void diff_printf(const char *zFormat, ...){
  va_list ap;
  va_start(ap, zFormat);
................................................................................
** command zDiffCmd to do the diffing.
*/
void diff_file(
  Blob *pFile1,             /* In memory content to compare from */
  const char *zFile2,       /* On disk content to compare to */
  const char *zName,        /* Display name of the file */
  const char *zDiffCmd,     /* Command for comparison */
  int diffFlags             /* Flags to control the diff */
){
  if( zDiffCmd==0 ){
    Blob out;                 /* Diff output text */
    Blob file2;               /* Content of zFile2 */
    const char *zName2;       /* Name of zFile2 for display */

    /* Read content of zFile2 into memory */
................................................................................
        blob_read_from_file(&file2, zFile2);
      }
      zName2 = zName;
    }

    /* Compute and output the differences */
    blob_zero(&out);
    text_diff(pFile1, &file2, &out, diffFlags);
    if( blob_size(&out) ){
      diff_printf("--- %s\n+++ %s\n", zName, zName2);
      diff_printf("%s\n", blob_str(&out));
    }

    /* Release memory resources */
    blob_reset(&file2);
................................................................................
** command zDiffCmd to do the diffing.
*/
void diff_file_mem(
  Blob *pFile1,             /* In memory content to compare from */
  Blob *pFile2,             /* In memory content to compare to */
  const char *zName,        /* Display name of the file */
  const char *zDiffCmd,     /* Command for comparison */
  int diffFlags             /* Diff flags */
){
  if( zDiffCmd==0 ){
    Blob out;      /* Diff output text */

    blob_zero(&out);
    text_diff(pFile1, pFile2, &out, diffFlags);
    diff_printf("--- %s\n+++ %s\n", zName, zName);
    diff_printf("%s\n", blob_str(&out));

    /* Release memory resources */
    blob_reset(&out);
  }else{
    Blob cmd;
................................................................................
/*
** Do a diff against a single file named in zFileTreeName from version zFrom
** against the same file on disk.
*/
static void diff_one_against_disk(
  const char *zFrom,        /* Name of file */
  const char *zDiffCmd,     /* Use this "diff" command */
  int diffFlags,            /* Diff control flags */
  const char *zFileTreeName
){
  Blob fname;
  Blob content;
  int isLink;
  file_tree_name(zFileTreeName, &fname, 1);
  historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
  if( !isLink != !file_wd_islink(zFrom) ){
    diff_printf("cannot compute difference between symlink and regular file\n");
  }else{
    diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
  }
  blob_reset(&content);
  blob_reset(&fname);
}

/*
** Run a diff between the version zFrom and files on disk.  zFrom might
................................................................................
  const char *zFrom,        /* Version to difference from */
  const char *zDiffCmd,     /* Use this diff command.  NULL for built-in */
  int diffFlags             /* Flags controlling diff output */
){
  int vid;
  Blob sql;
  Stmt q;

  int asNewFile;            /* Treat non-existant files as empty files */


  asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid, 1, 0);
  blob_zero(&sql);
  db_begin_transaction();
  if( zFrom ){
    int rid = name_to_typed_rid(zFrom, "ci");
................................................................................
      }
      if( srcid>0 ){
        content_get(srcid, &content);
      }else{
        blob_zero(&content);
      }
      diff_print_index(zPathname);
      diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
      blob_reset(&content);
    }
    free(zToFree);
  }
  db_finalize(&q);
  db_end_transaction(1);  /* ROLLBACK */
}
................................................................................
** Output the differences between two versions of a single file.
** zFrom and zTo are the check-ins containing the two file versions.
*/
static void diff_one_two_versions(
  const char *zFrom,
  const char *zTo,
  const char *zDiffCmd,
  int diffFlags,
  const char *zFileTreeName
){
  char *zName;
  Blob fname;
  Blob v1, v2;
  int isLink1, isLink2;
  file_tree_name(zFileTreeName, &fname, 1);
................................................................................
  zName = blob_str(&fname);
  historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
  historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
  if( isLink1 != isLink2 ){
    diff_printf("--- %s\n+++ %s\n", zName, zName);
    diff_printf("cannot compute difference between symlink and regular file\n");
  }else{
    diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
  }
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&fname);
}

/*
................................................................................
** Show the difference between two files identified by ManifestFile
** entries.
*/
static void diff_manifest_entry(
  struct ManifestFile *pFrom,
  struct ManifestFile *pTo,
  const char *zDiffCmd,
  int diffFlags
){
  Blob f1, f2;
  int rid;
  const char *zName =  pFrom ? pFrom->zName : pTo->zName;
  diff_print_index(zName);
  if( pFrom ){
    rid = uuid_to_rid(pFrom->zUuid, 0);
................................................................................
  }
  if( pTo ){
    rid = uuid_to_rid(pTo->zUuid, 0);
    content_get(rid, &f2);
  }else{
    blob_zero(&f2);
  }
  diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
  blob_reset(&f1);
  blob_reset(&f2);
}

/*
** Output the differences between two check-ins.
*/
................................................................................
  const char *zFrom,
  const char *zTo,
  const char *zDiffCmd,
  int diffFlags
){
  Manifest *pFrom, *pTo;
  ManifestFile *pFromFile, *pToFile;

  int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;

  pFrom = manifest_get_by_name(zFrom, 0);
  manifest_file_rewind(pFrom);
  pFromFile = manifest_file_next(pFrom,0);
  pTo = manifest_get_by_name(zTo, 0);
  manifest_file_rewind(pTo);
................................................................................
      cmp = -1;
    }else{
      cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
    }
    if( cmp<0 ){
      diff_printf("DELETED %s\n", pFromFile->zName);
      if( asNewFlag ){
        diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
      }
      pFromFile = manifest_file_next(pFrom,0);
    }else if( cmp>0 ){
      diff_printf("ADDED   %s\n", pToFile->zName);
      if( asNewFlag ){
        diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
      }
      pToFile = manifest_file_next(pTo,0);
    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }else{
      /* diff_printf("CHANGED %s\n", pFromFile->zName); */
      diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
      pFromFile = manifest_file_next(pFrom,0);
      pToFile = manifest_file_next(pTo,0);
    }
  }
  manifest_destroy(pFrom);
  manifest_destroy(pTo);
}
................................................................................
** the "setting" command.  If no external diff program is configured, then
** the "-i" option is a no-op.  The "-i" option converts "gdiff" into "diff".
**
** The "-N" or "--new-file" option causes the complete text of added or
** deleted files to be displayed.
**
** Options:
**   --context|-c N      Use N lines of context 
**   --from|-r VERSION   select VERSION as source for the diff
**   --new-file|-N       output complete text of added or deleted files
**   -i                  use internal diff logic
**   --to VERSION        select VERSION as target for the diff
**   --side-by-side|-y   side-by-side diff
**   --width|-W N        Width of lines in side-by-side diff 
*/
void diff_cmd(void){
  int isGDiff;               /* True for gdiff.  False for normal diff */
  int isInternDiff;          /* True for internal diff */
  int hasNFlag;              /* True if -N or --new-file flag is used */
  const char *zFrom;         /* Source version number */
  const char *zTo;           /* Target version number */
  const char *zDiffCmd = 0;  /* External diff command. NULL for internal diff */
  int diffFlags = 0;         /* Flags to control the DIFF */
  const char *z;
  int f;

  isGDiff = g.argv[1][0]=='g';
  isInternDiff = find_option("internal","i",0)!=0;
  zFrom = find_option("from", "r", 1);
  zTo = find_option("to", 0, 1);
  hasNFlag = find_option("new-file","N",0)!=0;
  if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
  if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
    if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
    diffFlags |= f;
  }
  if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
    f *= DIFF_CONTEXT_MASK+1;
    if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
    diffFlags |= f;
  }


  if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
  if( zTo==0 ){
    db_must_be_within_tree();
    verify_all_options();
    if( !isInternDiff ){

Changes to src/info.c.

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
....
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
  if( zTo ){
    toid = uuid_to_rid(zTo, 0);
    content_get(toid, &to);
  }else{
    blob_zero(&to);
  }
  blob_zero(&out);
  text_diff(&from, &to, &out, 5, 1);
  @ %h(blob_str(&out))
  blob_reset(&from);
  blob_reset(&to);
  blob_reset(&out);  
}


................................................................................
  }else{
    blob_zero(&diff);
    pOut = &diff;
  }
  if( !sideBySide || isPatch ){
    content_get(v1, &c1);
    content_get(v2, &c2);
    text_diff(&c1, &c2, pOut, 4, 1);
    blob_reset(&c1);
    blob_reset(&c2);
  }
  if( !isPatch ){
    style_header("Diff");
    style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
                          g.zTop, P("v1"), P("v2"));






|







 







|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
....
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
  if( zTo ){
    toid = uuid_to_rid(zTo, 0);
    content_get(toid, &to);
  }else{
    blob_zero(&to);
  }
  blob_zero(&out);
  text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5);
  @ %h(blob_str(&out))
  blob_reset(&from);
  blob_reset(&to);
  blob_reset(&out);  
}


................................................................................
  }else{
    blob_zero(&diff);
    pOut = &diff;
  }
  if( !sideBySide || isPatch ){
    content_get(v1, &c1);
    content_get(v2, &c2);
    text_diff(&c1, &c2, pOut, 4 | 0);
    blob_reset(&c1);
    blob_reset(&c2);
  }
  if( !isPatch ){
    style_header("Diff");
    style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
                          g.zTop, P("v1"), P("v2"));

Changes to src/merge3.c.

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  ** and pPivot => pV2 (into aC2).  Each of the aC1 and aC2 arrays is
  ** an array of integer triples.  Within each triple, the first integer
  ** is the number of lines of text to copy directly from the pivot,
  ** the second integer is the number of lines of text to omit from the
  ** pivot, and the third integer is the number of lines of text that are
  ** inserted.  The edit array ends with a triple of 0,0,0.
  */
  aC1 = text_diff(pPivot, pV1, 0, 0, 0);
  aC2 = text_diff(pPivot, pV2, 0, 0, 0);
  if( aC1==0 || aC2==0 ){
    free(aC1);
    free(aC2);
    return -1;
  }

  blob_rewind(pV1);        /* Rewind inputs:  Needed to reconstruct output */






|
|







169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  ** and pPivot => pV2 (into aC2).  Each of the aC1 and aC2 arrays is
  ** an array of integer triples.  Within each triple, the first integer
  ** is the number of lines of text to copy directly from the pivot,
  ** the second integer is the number of lines of text to omit from the
  ** pivot, and the third integer is the number of lines of text that are
  ** inserted.  The edit array ends with a triple of 0,0,0.
  */
  aC1 = text_diff(pPivot, pV1, 0, 0);
  aC2 = text_diff(pPivot, pV2, 0, 0);
  if( aC1==0 || aC2==0 ){
    free(aC1);
    free(aC2);
    return -1;
  }

  blob_rewind(pV1);        /* Rewind inputs:  Needed to reconstruct output */

Changes to src/wiki.c.

619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
  if( pW1==0 ) fossil_redirect_home();
  blob_init(&w1, pW1->zWiki, -1);
  blob_zero(&w2);
  if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
    blob_init(&w2, pW2->zWiki, -1);
  }
  blob_zero(&d);
  text_diff(&w2, &w1, &d, 5, 1);
  @ <pre>
  @ %h(blob_str(&d))
  @ </pre>
  manifest_destroy(pW1);
  manifest_destroy(pW2);
  style_footer();
}






|







619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
  if( pW1==0 ) fossil_redirect_home();
  blob_init(&w1, pW1->zWiki, -1);
  blob_zero(&w2);
  if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
    blob_init(&w2, pW2->zWiki, -1);
  }
  blob_zero(&d);
  text_diff(&w2, &w1, &d, 5 | DIFF_IGNORE_EOLWS);
  @ <pre>
  @ %h(blob_str(&d))
  @ </pre>
  manifest_destroy(pW1);
  manifest_destroy(pW2);
  style_footer();
}