Fossil SCM

Add new --ignore-space-at-sol, -ignore-space-at-eol and -w options to "fossil diff" and "fossil stash diff" commands. Modify annotation/blame such that any change (eol-whitespace too) is considered a change, after ML request.

jan.nijtmans 2014-03-05 21:29 trunk merge
Commit e663d5e33099e21ce5f7aa752f2b6177e0515d5b
+83 -35
--- src/diff.c
+++ src/diff.c
@@ -28,18 +28,18 @@
2828
** Flag parameters to the text_diff() routine used to control the formatting
2929
** of the diff output.
3030
*/
3131
#define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
3232
#define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33
-#define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
34
-#define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
35
-#define DIFF_VERBOSE ((u64)0x04000000) /* Missing shown as empty files */
36
-#define DIFF_BRIEF ((u64)0x08000000) /* Show filenames only */
37
-#define DIFF_INLINE ((u64)0x00000000) /* Inline (not side-by-side) diff */
38
-#define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
39
-#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
40
-#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
33
+#define DIFF_IGNORE_SOLWS ((u64)0x01000000) /* Ignore start-of-line whitespace */
34
+#define DIFF_IGNORE_EOLWS ((u64)0x02000000) /* Ignore end-of-line whitespace */
35
+#define DIFF_SIDEBYSIDE ((u64)0x04000000) /* Generate a side-by-side diff */
36
+#define DIFF_VERBOSE ((u64)0x08000000) /* Missing shown as empty files */
37
+#define DIFF_BRIEF ((u64)0x00000000) /* Show filenames only */
38
+#define DIFF_INLINE ((u64)0x10000000) /* Inline (not side-by-side) diff */
39
+#define DIFF_HTML ((u64)0x20000000) /* Render for HTML */
40
+#define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */
4141
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
4242
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
4343
#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
4444
#define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
4545
@@ -54,10 +54,13 @@
5454
"cannot compute difference between symlink and regular file\n"
5555
5656
#define DIFF_TOO_MANY_CHANGES \
5757
"more than 10,000 changes\n"
5858
59
+#define DIFF_WHITESPACE_ONLY \
60
+ "whitespace changes only\n"
61
+
5962
/*
6063
** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
6164
*/
6265
#define LENGTH_MASK_SZ 13
6366
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -73,10 +76,11 @@
7376
*/
7477
typedef struct DLine DLine;
7578
struct DLine {
7679
const char *z; /* The text of the line */
7780
unsigned int h; /* Hash of the line */
81
+ unsigned short indent; /* Indent of the line. Only !=0 with --ignore-space-at sol option */
7882
unsigned int iNext; /* 1+(Index of next line with same the same hash) */
7983
8084
/* an array of DLine elements serves two purposes. The fields
8185
** above are one per line of input text. But each entry is also
8286
** a bucket in a hash table, as follows: */
@@ -125,12 +129,12 @@
125129
** too long.
126130
**
127131
** Profiling show that in most cases this routine consumes the bulk of
128132
** the CPU time on a diff.
129133
*/
130
-static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
131
- int nLine, i, j, k, x;
134
+static DLine *break_into_lines(const char *z, int n, int *pnLine, u64 diffFlags){
135
+ int nLine, i, j, k, s, indent, x;
132136
unsigned int h, h2;
133137
DLine *a;
134138
135139
/* Count the number of lines. Allocate space to hold
136140
** the returned array.
@@ -158,18 +162,33 @@
158162
return a;
159163
}
160164
161165
/* Fill in the array */
162166
for(i=0; i<nLine; i++){
163
- a[i].z = z;
164167
for(j=0; z[j] && z[j]!='\n'; j++){}
165168
k = j;
166
- while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; }
167
- for(h=0, x=0; x<k; x++){
169
+ s = 0;
170
+ indent = 0;
171
+ if( diffFlags & DIFF_IGNORE_EOLWS ){
172
+ while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
173
+ }
174
+ if( diffFlags & DIFF_IGNORE_SOLWS ){
175
+ while( s<k && fossil_isspace(z[s]) ){
176
+ if( z[s]=='\t' ){
177
+ indent = ((indent+9)/8)*8;
178
+ }else if( z[s]==' ' ){
179
+ indent++;
180
+ }
181
+ s++;
182
+ }
183
+ }
184
+ a[i].z = z+s;
185
+ a[i].indent = s;
186
+ for(h=0, x=s; x<k; x++){
168187
h = h ^ (h<<2) ^ z[x];
169188
}
170
- a[i].h = h = (h<<LENGTH_MASK_SZ) | k;
189
+ a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
171190
h2 = h % nLine;
172191
a[i].iNext = a[h2].iHash;
173192
a[h2].iHash = i+1;
174193
z += j+1;
175194
}
@@ -221,15 +240,21 @@
221240
}else if( cPrefix=='+' ){
222241
blob_append(pOut, "<span class=\"diffadd\">", -1);
223242
}else if( cPrefix=='-' ){
224243
blob_append(pOut, "<span class=\"diffrm\">", -1);
225244
}
245
+ if( pLine->indent ){
246
+ blob_appendf(pOut, "%*s", pLine->indent, " ");
247
+ }
226248
htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK));
227249
if( cPrefix!=' ' ){
228250
blob_append(pOut, "</span>", -1);
229251
}
230252
}else{
253
+ if( pLine->indent ){
254
+ blob_appendf(pOut, "%*s", pLine->indent, " ");
255
+ }
231256
blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
232257
}
233258
blob_append(pOut, "\n", 1);
234259
}
235260
@@ -507,10 +532,13 @@
507532
p->iEnd = p->iEnd2;
508533
p->iEnd2 = 0;
509534
}
510535
}
511536
}
537
+ if( pLine->indent && i==0 ){
538
+ blob_appendf(pCol, "%*s", pLine->indent, " ");
539
+ }
512540
if( c=='\t' && !p->escHtml ){
513541
blob_append(pCol, " ", 1);
514542
while( (k&7)!=7 && (p->escHtml || k<w) ){
515543
blob_append(pCol, " ", 1);
516544
k++;
@@ -534,11 +562,11 @@
534562
blob_append(pCol, "</span>", 7);
535563
}
536564
if( col==SBS_TXTB ){
537565
sbsWriteNewlines(p);
538566
}else if( !p->escHtml ){
539
- sbsWriteSpace(p, w-k, SBS_TXTA);
567
+ sbsWriteSpace(p, w-k-pLine->indent, SBS_TXTA);
540568
}
541569
}
542570
543571
/*
544572
** Append a column to the final output blob.
@@ -1753,28 +1781,28 @@
17531781
Blob *pB_Blob, /* TO file */
17541782
Blob *pOut, /* Write diff here if not NULL */
17551783
ReCompiled *pRe, /* Only output changes where this Regexp matches */
17561784
u64 diffFlags /* DIFF_* flags defined above */
17571785
){
1758
- int ignoreEolWs; /* Ignore whitespace at the end of lines */
1786
+ int ignoreWs; /* Ignore whitespace */
17591787
DContext c;
17601788
17611789
if( diffFlags & DIFF_INVERT ){
17621790
Blob *pTemp = pA_Blob;
17631791
pA_Blob = pB_Blob;
17641792
pB_Blob = pTemp;
17651793
}
1766
- ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1794
+ ignoreWs = (diffFlags & (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))!=0;
17671795
blob_to_utf8_no_bom(pA_Blob, 0);
17681796
blob_to_utf8_no_bom(pB_Blob, 0);
17691797
17701798
/* Prepare the input files */
17711799
memset(&c, 0, sizeof(c));
17721800
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1773
- &c.nFrom, ignoreEolWs);
1801
+ &c.nFrom, diffFlags);
17741802
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1775
- &c.nTo, ignoreEolWs);
1803
+ &c.nTo, diffFlags);
17761804
if( c.aFrom==0 || c.aTo==0 ){
17771805
fossil_free(c.aFrom);
17781806
fossil_free(c.aTo);
17791807
if( pOut ){
17801808
diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
@@ -1782,20 +1810,27 @@
17821810
return 0;
17831811
}
17841812
17851813
/* Compute the difference */
17861814
diff_all(&c);
1815
+ if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){
1816
+ fossil_free(c.aFrom);
1817
+ fossil_free(c.aTo);
1818
+ fossil_free(c.aEdit);
1819
+ if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags);
1820
+ return 0;
1821
+ }
17871822
if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
17881823
int i, m, n;
17891824
int *a = c.aEdit;
17901825
int mx = c.nEdit;
17911826
for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
17921827
if( n>10000 ){
17931828
fossil_free(c.aFrom);
17941829
fossil_free(c.aTo);
17951830
fossil_free(c.aEdit);
1796
- diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
1831
+ if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
17971832
return 0;
17981833
}
17991834
}
18001835
if( (diffFlags & DIFF_NOOPT)==0 ){
18011836
diff_optimize(&c);
@@ -1828,14 +1863,17 @@
18281863
**
18291864
** --brief Show filenames only DIFF_BRIEF
18301865
** --context|-c N N lines of context. DIFF_CONTEXT_MASK
18311866
** --html Format for HTML DIFF_HTML
18321867
** --invert Invert the diff DIFF_INVERT
1868
+** --ignore-space-at-eol Ignore eol-whitespaces DIFF_IGNORE_EOLWS
1869
+** --ignore-space-at-sol Ignore sol-whitespaces DIFF_IGNORE_SOLWS
18331870
** --linenum|-n Show line numbers DIFF_LINENO
18341871
** --noopt Disable optimization DIFF_NOOPT
18351872
** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
18361873
** --unified Unified diff. ~DIFF_SIDEBYSIDE
1874
+** -w Ignore all whitespaces DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS
18371875
** --width|-W N N character lines. DIFF_WIDTH_MASK
18381876
*/
18391877
u64 diff_options(void){
18401878
u64 diffFlags = 0;
18411879
const char *z;
@@ -1850,10 +1888,13 @@
18501888
f *= DIFF_CONTEXT_MASK+1;
18511889
if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
18521890
diffFlags |= f;
18531891
}
18541892
if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
1893
+ if( find_option("ignore-space-at-sol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_SOLWS;
1894
+ if( find_option("ignore-space-at-eol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_EOLWS;
1895
+ if( find_option("w",0,0)!=0 ) diffFlags |= (DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS);
18551896
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
18561897
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
18571898
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
18581899
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
18591900
return diffFlags;
@@ -1930,11 +1971,14 @@
19301971
typedef struct Annotator Annotator;
19311972
struct Annotator {
19321973
DContext c; /* The diff-engine context */
19331974
struct AnnLine { /* Lines of the original files... */
19341975
const char *z; /* The text of the line */
1935
- short int n; /* Number of bytes (omitting trailing space and \n) */
1976
+ short int n; /* Number of bytes. Whether this omits sol/eol spacing
1977
+ depends on the diffFlags) */
1978
+ unsigned short indent; /* Indenting (number of initial spaces, only used
1979
+ if sol-spacing is ignored in the diffFlags) */
19361980
short int iVers; /* Level at which tag was set */
19371981
} *aOrig;
19381982
int nOrig; /* Number of elements in aOrig[] */
19391983
int nVers; /* Number of versions analyzed */
19401984
int bLimit; /* True if the iLimit was reached */
@@ -1956,18 +2000,20 @@
19562000
*/
19572001
static int annotation_start(Annotator *p, Blob *pInput){
19582002
int i;
19592003
19602004
memset(p, 0, sizeof(*p));
1961
- p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
2005
+ p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
2006
+ 0);
19622007
if( p->c.aTo==0 ){
19632008
return 1;
19642009
}
19652010
p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
19662011
for(i=0; i<p->c.nTo; i++){
19672012
p->aOrig[i].z = p->c.aTo[i].z;
19682013
p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2014
+ p->aOrig[i].indent = p->c.aTo[i].indent;
19692015
p->aOrig[i].iVers = -1;
19702016
}
19712017
p->nOrig = p->c.nTo;
19722018
return 0;
19732019
}
@@ -1983,11 +2029,11 @@
19832029
int i, j;
19842030
int lnTo;
19852031
19862032
/* Prepare the parent file to be diffed */
19872033
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
1988
- &p->c.nFrom, 1);
2034
+ &p->c.nFrom, 0);
19892035
if( p->c.aFrom==0 ){
19902036
return 1;
19912037
}
19922038
19932039
/* Compute the differences going from pParent to the file being
@@ -2256,10 +2302,11 @@
22562302
@ <pre>
22572303
for(i=0; i<ann.nOrig; i++){
22582304
int iVers = ann.aOrig[i].iVers;
22592305
char *z = (char*)ann.aOrig[i].z;
22602306
int n = ann.aOrig[i].n;
2307
+ int indent = ann.aOrig[i].indent+1;
22612308
char zPrefix[300];
22622309
z[n] = 0;
22632310
if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
22642311
22652312
if( bBlame ){
@@ -2270,26 +2317,26 @@
22702317
"<span style='background-color:%s'>"
22712318
"%s%.10s</a> %s</span> %13.13s:",
22722319
p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser);
22732320
fossil_free(zLink);
22742321
}else{
2275
- sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", "");
2322
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%*s", indent, " ");
22762323
}
22772324
}else{
22782325
if( iVers>=0 ){
22792326
struct AnnVers *p = ann.aVers+iVers;
22802327
char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
22812328
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
22822329
"<span style='background-color:%s'>"
2283
- "%s%.10s</a> %s</span> %4d:",
2284
- p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
2330
+ "%s%.10s</a> %s</span> %4d:%*s",
2331
+ p->zBgColor, zLink, p->zMUuid, p->zDate, i+1, indent, " ");
22852332
fossil_free(zLink);
22862333
}else{
2287
- sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
2334
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:%*s", "", i+1, indent, " ");
22882335
}
22892336
}
2290
- @ %s(zPrefix) %h(z)
2337
+ @ %s(zPrefix)%h(z)
22912338
22922339
}
22932340
@ </pre>
22942341
style_footer();
22952342
}
@@ -2372,27 +2419,28 @@
23722419
fossil_print("---------------------------------------------------\n");
23732420
}
23742421
for(i=0; i<ann.nOrig; i++){
23752422
int iVers = ann.aOrig[i].iVers;
23762423
char *z = (char*)ann.aOrig[i].z;
2424
+ int indent = ann.aOrig[i].indent + 1;
23772425
int n = ann.aOrig[i].n;
23782426
struct AnnVers *p;
23792427
if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
23802428
p = ann.aVers + iVers;
23812429
if( bBlame ){
23822430
if( iVers>=0 ){
2383
- fossil_print("%.10s %s %13.13s: %.*s\n",
2384
- fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z);
2431
+ fossil_print("%.10s %s %13.13s:%*s%.*s\n",
2432
+ fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, indent, " ", n, z);
23852433
}else{
2386
- fossil_print("%35s %.*s\n", "", n, z);
2434
+ fossil_print("%35s %*s%.*s\n", "", indent, " ", n, z);
23872435
}
23882436
}else{
23892437
if( iVers>=0 ){
2390
- fossil_print("%.10s %s %5d: %.*s\n",
2391
- fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z);
2438
+ fossil_print("%.10s %s %5d:%*s%.*s\n",
2439
+ fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, indent, " ", n, z);
23922440
}else{
2393
- fossil_print("%21s %5d: %.*s\n",
2394
- "", i+1, n, z);
2441
+ fossil_print("%21s %5d:%*s%.*s\n",
2442
+ "", i+1, indent, " ", n, z);
23952443
}
23962444
}
23972445
}
23982446
}
23992447
--- src/diff.c
+++ src/diff.c
@@ -28,18 +28,18 @@
28 ** Flag parameters to the text_diff() routine used to control the formatting
29 ** of the diff output.
30 */
31 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
32 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33 #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
34 #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
35 #define DIFF_VERBOSE ((u64)0x04000000) /* Missing shown as empty files */
36 #define DIFF_BRIEF ((u64)0x08000000) /* Show filenames only */
37 #define DIFF_INLINE ((u64)0x00000000) /* Inline (not side-by-side) diff */
38 #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
39 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
40 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
41 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
42 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43 #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
44 #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
45
@@ -54,10 +54,13 @@
54 "cannot compute difference between symlink and regular file\n"
55
56 #define DIFF_TOO_MANY_CHANGES \
57 "more than 10,000 changes\n"
58
 
 
 
59 /*
60 ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
61 */
62 #define LENGTH_MASK_SZ 13
63 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -73,10 +76,11 @@
73 */
74 typedef struct DLine DLine;
75 struct DLine {
76 const char *z; /* The text of the line */
77 unsigned int h; /* Hash of the line */
 
78 unsigned int iNext; /* 1+(Index of next line with same the same hash) */
79
80 /* an array of DLine elements serves two purposes. The fields
81 ** above are one per line of input text. But each entry is also
82 ** a bucket in a hash table, as follows: */
@@ -125,12 +129,12 @@
125 ** too long.
126 **
127 ** Profiling show that in most cases this routine consumes the bulk of
128 ** the CPU time on a diff.
129 */
130 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
131 int nLine, i, j, k, x;
132 unsigned int h, h2;
133 DLine *a;
134
135 /* Count the number of lines. Allocate space to hold
136 ** the returned array.
@@ -158,18 +162,33 @@
158 return a;
159 }
160
161 /* Fill in the array */
162 for(i=0; i<nLine; i++){
163 a[i].z = z;
164 for(j=0; z[j] && z[j]!='\n'; j++){}
165 k = j;
166 while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; }
167 for(h=0, x=0; x<k; x++){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168 h = h ^ (h<<2) ^ z[x];
169 }
170 a[i].h = h = (h<<LENGTH_MASK_SZ) | k;
171 h2 = h % nLine;
172 a[i].iNext = a[h2].iHash;
173 a[h2].iHash = i+1;
174 z += j+1;
175 }
@@ -221,15 +240,21 @@
221 }else if( cPrefix=='+' ){
222 blob_append(pOut, "<span class=\"diffadd\">", -1);
223 }else if( cPrefix=='-' ){
224 blob_append(pOut, "<span class=\"diffrm\">", -1);
225 }
 
 
 
226 htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK));
227 if( cPrefix!=' ' ){
228 blob_append(pOut, "</span>", -1);
229 }
230 }else{
 
 
 
231 blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
232 }
233 blob_append(pOut, "\n", 1);
234 }
235
@@ -507,10 +532,13 @@
507 p->iEnd = p->iEnd2;
508 p->iEnd2 = 0;
509 }
510 }
511 }
 
 
 
512 if( c=='\t' && !p->escHtml ){
513 blob_append(pCol, " ", 1);
514 while( (k&7)!=7 && (p->escHtml || k<w) ){
515 blob_append(pCol, " ", 1);
516 k++;
@@ -534,11 +562,11 @@
534 blob_append(pCol, "</span>", 7);
535 }
536 if( col==SBS_TXTB ){
537 sbsWriteNewlines(p);
538 }else if( !p->escHtml ){
539 sbsWriteSpace(p, w-k, SBS_TXTA);
540 }
541 }
542
543 /*
544 ** Append a column to the final output blob.
@@ -1753,28 +1781,28 @@
1753 Blob *pB_Blob, /* TO file */
1754 Blob *pOut, /* Write diff here if not NULL */
1755 ReCompiled *pRe, /* Only output changes where this Regexp matches */
1756 u64 diffFlags /* DIFF_* flags defined above */
1757 ){
1758 int ignoreEolWs; /* Ignore whitespace at the end of lines */
1759 DContext c;
1760
1761 if( diffFlags & DIFF_INVERT ){
1762 Blob *pTemp = pA_Blob;
1763 pA_Blob = pB_Blob;
1764 pB_Blob = pTemp;
1765 }
1766 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1767 blob_to_utf8_no_bom(pA_Blob, 0);
1768 blob_to_utf8_no_bom(pB_Blob, 0);
1769
1770 /* Prepare the input files */
1771 memset(&c, 0, sizeof(c));
1772 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1773 &c.nFrom, ignoreEolWs);
1774 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1775 &c.nTo, ignoreEolWs);
1776 if( c.aFrom==0 || c.aTo==0 ){
1777 fossil_free(c.aFrom);
1778 fossil_free(c.aTo);
1779 if( pOut ){
1780 diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
@@ -1782,20 +1810,27 @@
1782 return 0;
1783 }
1784
1785 /* Compute the difference */
1786 diff_all(&c);
 
 
 
 
 
 
 
1787 if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
1788 int i, m, n;
1789 int *a = c.aEdit;
1790 int mx = c.nEdit;
1791 for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
1792 if( n>10000 ){
1793 fossil_free(c.aFrom);
1794 fossil_free(c.aTo);
1795 fossil_free(c.aEdit);
1796 diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
1797 return 0;
1798 }
1799 }
1800 if( (diffFlags & DIFF_NOOPT)==0 ){
1801 diff_optimize(&c);
@@ -1828,14 +1863,17 @@
1828 **
1829 ** --brief Show filenames only DIFF_BRIEF
1830 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK
1831 ** --html Format for HTML DIFF_HTML
1832 ** --invert Invert the diff DIFF_INVERT
 
 
1833 ** --linenum|-n Show line numbers DIFF_LINENO
1834 ** --noopt Disable optimization DIFF_NOOPT
1835 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1836 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
 
1837 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1838 */
1839 u64 diff_options(void){
1840 u64 diffFlags = 0;
1841 const char *z;
@@ -1850,10 +1888,13 @@
1850 f *= DIFF_CONTEXT_MASK+1;
1851 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1852 diffFlags |= f;
1853 }
1854 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
 
 
 
1855 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
1856 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
1857 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
1858 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
1859 return diffFlags;
@@ -1930,11 +1971,14 @@
1930 typedef struct Annotator Annotator;
1931 struct Annotator {
1932 DContext c; /* The diff-engine context */
1933 struct AnnLine { /* Lines of the original files... */
1934 const char *z; /* The text of the line */
1935 short int n; /* Number of bytes (omitting trailing space and \n) */
 
 
 
1936 short int iVers; /* Level at which tag was set */
1937 } *aOrig;
1938 int nOrig; /* Number of elements in aOrig[] */
1939 int nVers; /* Number of versions analyzed */
1940 int bLimit; /* True if the iLimit was reached */
@@ -1956,18 +2000,20 @@
1956 */
1957 static int annotation_start(Annotator *p, Blob *pInput){
1958 int i;
1959
1960 memset(p, 0, sizeof(*p));
1961 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
 
1962 if( p->c.aTo==0 ){
1963 return 1;
1964 }
1965 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
1966 for(i=0; i<p->c.nTo; i++){
1967 p->aOrig[i].z = p->c.aTo[i].z;
1968 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
 
1969 p->aOrig[i].iVers = -1;
1970 }
1971 p->nOrig = p->c.nTo;
1972 return 0;
1973 }
@@ -1983,11 +2029,11 @@
1983 int i, j;
1984 int lnTo;
1985
1986 /* Prepare the parent file to be diffed */
1987 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
1988 &p->c.nFrom, 1);
1989 if( p->c.aFrom==0 ){
1990 return 1;
1991 }
1992
1993 /* Compute the differences going from pParent to the file being
@@ -2256,10 +2302,11 @@
2256 @ <pre>
2257 for(i=0; i<ann.nOrig; i++){
2258 int iVers = ann.aOrig[i].iVers;
2259 char *z = (char*)ann.aOrig[i].z;
2260 int n = ann.aOrig[i].n;
 
2261 char zPrefix[300];
2262 z[n] = 0;
2263 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2264
2265 if( bBlame ){
@@ -2270,26 +2317,26 @@
2270 "<span style='background-color:%s'>"
2271 "%s%.10s</a> %s</span> %13.13s:",
2272 p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser);
2273 fossil_free(zLink);
2274 }else{
2275 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", "");
2276 }
2277 }else{
2278 if( iVers>=0 ){
2279 struct AnnVers *p = ann.aVers+iVers;
2280 char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2281 sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2282 "<span style='background-color:%s'>"
2283 "%s%.10s</a> %s</span> %4d:",
2284 p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
2285 fossil_free(zLink);
2286 }else{
2287 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
2288 }
2289 }
2290 @ %s(zPrefix) %h(z)
2291
2292 }
2293 @ </pre>
2294 style_footer();
2295 }
@@ -2372,27 +2419,28 @@
2372 fossil_print("---------------------------------------------------\n");
2373 }
2374 for(i=0; i<ann.nOrig; i++){
2375 int iVers = ann.aOrig[i].iVers;
2376 char *z = (char*)ann.aOrig[i].z;
 
2377 int n = ann.aOrig[i].n;
2378 struct AnnVers *p;
2379 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2380 p = ann.aVers + iVers;
2381 if( bBlame ){
2382 if( iVers>=0 ){
2383 fossil_print("%.10s %s %13.13s: %.*s\n",
2384 fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z);
2385 }else{
2386 fossil_print("%35s %.*s\n", "", n, z);
2387 }
2388 }else{
2389 if( iVers>=0 ){
2390 fossil_print("%.10s %s %5d: %.*s\n",
2391 fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z);
2392 }else{
2393 fossil_print("%21s %5d: %.*s\n",
2394 "", i+1, n, z);
2395 }
2396 }
2397 }
2398 }
2399
--- src/diff.c
+++ src/diff.c
@@ -28,18 +28,18 @@
28 ** Flag parameters to the text_diff() routine used to control the formatting
29 ** of the diff output.
30 */
31 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
32 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33 #define DIFF_IGNORE_SOLWS ((u64)0x01000000) /* Ignore start-of-line whitespace */
34 #define DIFF_IGNORE_EOLWS ((u64)0x02000000) /* Ignore end-of-line whitespace */
35 #define DIFF_SIDEBYSIDE ((u64)0x04000000) /* Generate a side-by-side diff */
36 #define DIFF_VERBOSE ((u64)0x08000000) /* Missing shown as empty files */
37 #define DIFF_BRIEF ((u64)0x00000000) /* Show filenames only */
38 #define DIFF_INLINE ((u64)0x10000000) /* Inline (not side-by-side) diff */
39 #define DIFF_HTML ((u64)0x20000000) /* Render for HTML */
40 #define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */
41 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
42 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43 #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
44 #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
45
@@ -54,10 +54,13 @@
54 "cannot compute difference between symlink and regular file\n"
55
56 #define DIFF_TOO_MANY_CHANGES \
57 "more than 10,000 changes\n"
58
59 #define DIFF_WHITESPACE_ONLY \
60 "whitespace changes only\n"
61
62 /*
63 ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
64 */
65 #define LENGTH_MASK_SZ 13
66 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -73,10 +76,11 @@
76 */
77 typedef struct DLine DLine;
78 struct DLine {
79 const char *z; /* The text of the line */
80 unsigned int h; /* Hash of the line */
81 unsigned short indent; /* Indent of the line. Only !=0 with --ignore-space-at sol option */
82 unsigned int iNext; /* 1+(Index of next line with same the same hash) */
83
84 /* an array of DLine elements serves two purposes. The fields
85 ** above are one per line of input text. But each entry is also
86 ** a bucket in a hash table, as follows: */
@@ -125,12 +129,12 @@
129 ** too long.
130 **
131 ** Profiling show that in most cases this routine consumes the bulk of
132 ** the CPU time on a diff.
133 */
134 static DLine *break_into_lines(const char *z, int n, int *pnLine, u64 diffFlags){
135 int nLine, i, j, k, s, indent, x;
136 unsigned int h, h2;
137 DLine *a;
138
139 /* Count the number of lines. Allocate space to hold
140 ** the returned array.
@@ -158,18 +162,33 @@
162 return a;
163 }
164
165 /* Fill in the array */
166 for(i=0; i<nLine; i++){
 
167 for(j=0; z[j] && z[j]!='\n'; j++){}
168 k = j;
169 s = 0;
170 indent = 0;
171 if( diffFlags & DIFF_IGNORE_EOLWS ){
172 while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
173 }
174 if( diffFlags & DIFF_IGNORE_SOLWS ){
175 while( s<k && fossil_isspace(z[s]) ){
176 if( z[s]=='\t' ){
177 indent = ((indent+9)/8)*8;
178 }else if( z[s]==' ' ){
179 indent++;
180 }
181 s++;
182 }
183 }
184 a[i].z = z+s;
185 a[i].indent = s;
186 for(h=0, x=s; x<k; x++){
187 h = h ^ (h<<2) ^ z[x];
188 }
189 a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
190 h2 = h % nLine;
191 a[i].iNext = a[h2].iHash;
192 a[h2].iHash = i+1;
193 z += j+1;
194 }
@@ -221,15 +240,21 @@
240 }else if( cPrefix=='+' ){
241 blob_append(pOut, "<span class=\"diffadd\">", -1);
242 }else if( cPrefix=='-' ){
243 blob_append(pOut, "<span class=\"diffrm\">", -1);
244 }
245 if( pLine->indent ){
246 blob_appendf(pOut, "%*s", pLine->indent, " ");
247 }
248 htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK));
249 if( cPrefix!=' ' ){
250 blob_append(pOut, "</span>", -1);
251 }
252 }else{
253 if( pLine->indent ){
254 blob_appendf(pOut, "%*s", pLine->indent, " ");
255 }
256 blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
257 }
258 blob_append(pOut, "\n", 1);
259 }
260
@@ -507,10 +532,13 @@
532 p->iEnd = p->iEnd2;
533 p->iEnd2 = 0;
534 }
535 }
536 }
537 if( pLine->indent && i==0 ){
538 blob_appendf(pCol, "%*s", pLine->indent, " ");
539 }
540 if( c=='\t' && !p->escHtml ){
541 blob_append(pCol, " ", 1);
542 while( (k&7)!=7 && (p->escHtml || k<w) ){
543 blob_append(pCol, " ", 1);
544 k++;
@@ -534,11 +562,11 @@
562 blob_append(pCol, "</span>", 7);
563 }
564 if( col==SBS_TXTB ){
565 sbsWriteNewlines(p);
566 }else if( !p->escHtml ){
567 sbsWriteSpace(p, w-k-pLine->indent, SBS_TXTA);
568 }
569 }
570
571 /*
572 ** Append a column to the final output blob.
@@ -1753,28 +1781,28 @@
1781 Blob *pB_Blob, /* TO file */
1782 Blob *pOut, /* Write diff here if not NULL */
1783 ReCompiled *pRe, /* Only output changes where this Regexp matches */
1784 u64 diffFlags /* DIFF_* flags defined above */
1785 ){
1786 int ignoreWs; /* Ignore whitespace */
1787 DContext c;
1788
1789 if( diffFlags & DIFF_INVERT ){
1790 Blob *pTemp = pA_Blob;
1791 pA_Blob = pB_Blob;
1792 pB_Blob = pTemp;
1793 }
1794 ignoreWs = (diffFlags & (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))!=0;
1795 blob_to_utf8_no_bom(pA_Blob, 0);
1796 blob_to_utf8_no_bom(pB_Blob, 0);
1797
1798 /* Prepare the input files */
1799 memset(&c, 0, sizeof(c));
1800 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1801 &c.nFrom, diffFlags);
1802 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1803 &c.nTo, diffFlags);
1804 if( c.aFrom==0 || c.aTo==0 ){
1805 fossil_free(c.aFrom);
1806 fossil_free(c.aTo);
1807 if( pOut ){
1808 diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
@@ -1782,20 +1810,27 @@
1810 return 0;
1811 }
1812
1813 /* Compute the difference */
1814 diff_all(&c);
1815 if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){
1816 fossil_free(c.aFrom);
1817 fossil_free(c.aTo);
1818 fossil_free(c.aEdit);
1819 if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags);
1820 return 0;
1821 }
1822 if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
1823 int i, m, n;
1824 int *a = c.aEdit;
1825 int mx = c.nEdit;
1826 for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
1827 if( n>10000 ){
1828 fossil_free(c.aFrom);
1829 fossil_free(c.aTo);
1830 fossil_free(c.aEdit);
1831 if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
1832 return 0;
1833 }
1834 }
1835 if( (diffFlags & DIFF_NOOPT)==0 ){
1836 diff_optimize(&c);
@@ -1828,14 +1863,17 @@
1863 **
1864 ** --brief Show filenames only DIFF_BRIEF
1865 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK
1866 ** --html Format for HTML DIFF_HTML
1867 ** --invert Invert the diff DIFF_INVERT
1868 ** --ignore-space-at-eol Ignore eol-whitespaces DIFF_IGNORE_EOLWS
1869 ** --ignore-space-at-sol Ignore sol-whitespaces DIFF_IGNORE_SOLWS
1870 ** --linenum|-n Show line numbers DIFF_LINENO
1871 ** --noopt Disable optimization DIFF_NOOPT
1872 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1873 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
1874 ** -w Ignore all whitespaces DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS
1875 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1876 */
1877 u64 diff_options(void){
1878 u64 diffFlags = 0;
1879 const char *z;
@@ -1850,10 +1888,13 @@
1888 f *= DIFF_CONTEXT_MASK+1;
1889 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1890 diffFlags |= f;
1891 }
1892 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
1893 if( find_option("ignore-space-at-sol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_SOLWS;
1894 if( find_option("ignore-space-at-eol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_EOLWS;
1895 if( find_option("w",0,0)!=0 ) diffFlags |= (DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS);
1896 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
1897 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
1898 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
1899 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
1900 return diffFlags;
@@ -1930,11 +1971,14 @@
1971 typedef struct Annotator Annotator;
1972 struct Annotator {
1973 DContext c; /* The diff-engine context */
1974 struct AnnLine { /* Lines of the original files... */
1975 const char *z; /* The text of the line */
1976 short int n; /* Number of bytes. Whether this omits sol/eol spacing
1977 depends on the diffFlags) */
1978 unsigned short indent; /* Indenting (number of initial spaces, only used
1979 if sol-spacing is ignored in the diffFlags) */
1980 short int iVers; /* Level at which tag was set */
1981 } *aOrig;
1982 int nOrig; /* Number of elements in aOrig[] */
1983 int nVers; /* Number of versions analyzed */
1984 int bLimit; /* True if the iLimit was reached */
@@ -1956,18 +2000,20 @@
2000 */
2001 static int annotation_start(Annotator *p, Blob *pInput){
2002 int i;
2003
2004 memset(p, 0, sizeof(*p));
2005 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
2006 0);
2007 if( p->c.aTo==0 ){
2008 return 1;
2009 }
2010 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
2011 for(i=0; i<p->c.nTo; i++){
2012 p->aOrig[i].z = p->c.aTo[i].z;
2013 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2014 p->aOrig[i].indent = p->c.aTo[i].indent;
2015 p->aOrig[i].iVers = -1;
2016 }
2017 p->nOrig = p->c.nTo;
2018 return 0;
2019 }
@@ -1983,11 +2029,11 @@
2029 int i, j;
2030 int lnTo;
2031
2032 /* Prepare the parent file to be diffed */
2033 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
2034 &p->c.nFrom, 0);
2035 if( p->c.aFrom==0 ){
2036 return 1;
2037 }
2038
2039 /* Compute the differences going from pParent to the file being
@@ -2256,10 +2302,11 @@
2302 @ <pre>
2303 for(i=0; i<ann.nOrig; i++){
2304 int iVers = ann.aOrig[i].iVers;
2305 char *z = (char*)ann.aOrig[i].z;
2306 int n = ann.aOrig[i].n;
2307 int indent = ann.aOrig[i].indent+1;
2308 char zPrefix[300];
2309 z[n] = 0;
2310 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2311
2312 if( bBlame ){
@@ -2270,26 +2317,26 @@
2317 "<span style='background-color:%s'>"
2318 "%s%.10s</a> %s</span> %13.13s:",
2319 p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser);
2320 fossil_free(zLink);
2321 }else{
2322 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%*s", indent, " ");
2323 }
2324 }else{
2325 if( iVers>=0 ){
2326 struct AnnVers *p = ann.aVers+iVers;
2327 char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2328 sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2329 "<span style='background-color:%s'>"
2330 "%s%.10s</a> %s</span> %4d:%*s",
2331 p->zBgColor, zLink, p->zMUuid, p->zDate, i+1, indent, " ");
2332 fossil_free(zLink);
2333 }else{
2334 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:%*s", "", i+1, indent, " ");
2335 }
2336 }
2337 @ %s(zPrefix)%h(z)
2338
2339 }
2340 @ </pre>
2341 style_footer();
2342 }
@@ -2372,27 +2419,28 @@
2419 fossil_print("---------------------------------------------------\n");
2420 }
2421 for(i=0; i<ann.nOrig; i++){
2422 int iVers = ann.aOrig[i].iVers;
2423 char *z = (char*)ann.aOrig[i].z;
2424 int indent = ann.aOrig[i].indent + 1;
2425 int n = ann.aOrig[i].n;
2426 struct AnnVers *p;
2427 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2428 p = ann.aVers + iVers;
2429 if( bBlame ){
2430 if( iVers>=0 ){
2431 fossil_print("%.10s %s %13.13s:%*s%.*s\n",
2432 fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, indent, " ", n, z);
2433 }else{
2434 fossil_print("%35s %*s%.*s\n", "", indent, " ", n, z);
2435 }
2436 }else{
2437 if( iVers>=0 ){
2438 fossil_print("%.10s %s %5d:%*s%.*s\n",
2439 fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, indent, " ", n, z);
2440 }else{
2441 fossil_print("%21s %5d:%*s%.*s\n",
2442 "", i+1, indent, " ", n, z);
2443 }
2444 }
2445 }
2446 }
2447
+83 -35
--- src/diff.c
+++ src/diff.c
@@ -28,18 +28,18 @@
2828
** Flag parameters to the text_diff() routine used to control the formatting
2929
** of the diff output.
3030
*/
3131
#define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
3232
#define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33
-#define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
34
-#define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
35
-#define DIFF_VERBOSE ((u64)0x04000000) /* Missing shown as empty files */
36
-#define DIFF_BRIEF ((u64)0x08000000) /* Show filenames only */
37
-#define DIFF_INLINE ((u64)0x00000000) /* Inline (not side-by-side) diff */
38
-#define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
39
-#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
40
-#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
33
+#define DIFF_IGNORE_SOLWS ((u64)0x01000000) /* Ignore start-of-line whitespace */
34
+#define DIFF_IGNORE_EOLWS ((u64)0x02000000) /* Ignore end-of-line whitespace */
35
+#define DIFF_SIDEBYSIDE ((u64)0x04000000) /* Generate a side-by-side diff */
36
+#define DIFF_VERBOSE ((u64)0x08000000) /* Missing shown as empty files */
37
+#define DIFF_BRIEF ((u64)0x00000000) /* Show filenames only */
38
+#define DIFF_INLINE ((u64)0x10000000) /* Inline (not side-by-side) diff */
39
+#define DIFF_HTML ((u64)0x20000000) /* Render for HTML */
40
+#define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */
4141
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
4242
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
4343
#define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
4444
#define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
4545
@@ -54,10 +54,13 @@
5454
"cannot compute difference between symlink and regular file\n"
5555
5656
#define DIFF_TOO_MANY_CHANGES \
5757
"more than 10,000 changes\n"
5858
59
+#define DIFF_WHITESPACE_ONLY \
60
+ "whitespace changes only\n"
61
+
5962
/*
6063
** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
6164
*/
6265
#define LENGTH_MASK_SZ 13
6366
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -73,10 +76,11 @@
7376
*/
7477
typedef struct DLine DLine;
7578
struct DLine {
7679
const char *z; /* The text of the line */
7780
unsigned int h; /* Hash of the line */
81
+ unsigned short indent; /* Indent of the line. Only !=0 with --ignore-space-at sol option */
7882
unsigned int iNext; /* 1+(Index of next line with same the same hash) */
7983
8084
/* an array of DLine elements serves two purposes. The fields
8185
** above are one per line of input text. But each entry is also
8286
** a bucket in a hash table, as follows: */
@@ -125,12 +129,12 @@
125129
** too long.
126130
**
127131
** Profiling show that in most cases this routine consumes the bulk of
128132
** the CPU time on a diff.
129133
*/
130
-static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
131
- int nLine, i, j, k, x;
134
+static DLine *break_into_lines(const char *z, int n, int *pnLine, u64 diffFlags){
135
+ int nLine, i, j, k, s, indent, x;
132136
unsigned int h, h2;
133137
DLine *a;
134138
135139
/* Count the number of lines. Allocate space to hold
136140
** the returned array.
@@ -158,18 +162,33 @@
158162
return a;
159163
}
160164
161165
/* Fill in the array */
162166
for(i=0; i<nLine; i++){
163
- a[i].z = z;
164167
for(j=0; z[j] && z[j]!='\n'; j++){}
165168
k = j;
166
- while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; }
167
- for(h=0, x=0; x<k; x++){
169
+ s = 0;
170
+ indent = 0;
171
+ if( diffFlags & DIFF_IGNORE_EOLWS ){
172
+ while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
173
+ }
174
+ if( diffFlags & DIFF_IGNORE_SOLWS ){
175
+ while( s<k && fossil_isspace(z[s]) ){
176
+ if( z[s]=='\t' ){
177
+ indent = ((indent+9)/8)*8;
178
+ }else if( z[s]==' ' ){
179
+ indent++;
180
+ }
181
+ s++;
182
+ }
183
+ }
184
+ a[i].z = z+s;
185
+ a[i].indent = s;
186
+ for(h=0, x=s; x<k; x++){
168187
h = h ^ (h<<2) ^ z[x];
169188
}
170
- a[i].h = h = (h<<LENGTH_MASK_SZ) | k;
189
+ a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
171190
h2 = h % nLine;
172191
a[i].iNext = a[h2].iHash;
173192
a[h2].iHash = i+1;
174193
z += j+1;
175194
}
@@ -221,15 +240,21 @@
221240
}else if( cPrefix=='+' ){
222241
blob_append(pOut, "<span class=\"diffadd\">", -1);
223242
}else if( cPrefix=='-' ){
224243
blob_append(pOut, "<span class=\"diffrm\">", -1);
225244
}
245
+ if( pLine->indent ){
246
+ blob_appendf(pOut, "%*s", pLine->indent, " ");
247
+ }
226248
htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK));
227249
if( cPrefix!=' ' ){
228250
blob_append(pOut, "</span>", -1);
229251
}
230252
}else{
253
+ if( pLine->indent ){
254
+ blob_appendf(pOut, "%*s", pLine->indent, " ");
255
+ }
231256
blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
232257
}
233258
blob_append(pOut, "\n", 1);
234259
}
235260
@@ -507,10 +532,13 @@
507532
p->iEnd = p->iEnd2;
508533
p->iEnd2 = 0;
509534
}
510535
}
511536
}
537
+ if( pLine->indent && i==0 ){
538
+ blob_appendf(pCol, "%*s", pLine->indent, " ");
539
+ }
512540
if( c=='\t' && !p->escHtml ){
513541
blob_append(pCol, " ", 1);
514542
while( (k&7)!=7 && (p->escHtml || k<w) ){
515543
blob_append(pCol, " ", 1);
516544
k++;
@@ -534,11 +562,11 @@
534562
blob_append(pCol, "</span>", 7);
535563
}
536564
if( col==SBS_TXTB ){
537565
sbsWriteNewlines(p);
538566
}else if( !p->escHtml ){
539
- sbsWriteSpace(p, w-k, SBS_TXTA);
567
+ sbsWriteSpace(p, w-k-pLine->indent, SBS_TXTA);
540568
}
541569
}
542570
543571
/*
544572
** Append a column to the final output blob.
@@ -1753,28 +1781,28 @@
17531781
Blob *pB_Blob, /* TO file */
17541782
Blob *pOut, /* Write diff here if not NULL */
17551783
ReCompiled *pRe, /* Only output changes where this Regexp matches */
17561784
u64 diffFlags /* DIFF_* flags defined above */
17571785
){
1758
- int ignoreEolWs; /* Ignore whitespace at the end of lines */
1786
+ int ignoreWs; /* Ignore whitespace */
17591787
DContext c;
17601788
17611789
if( diffFlags & DIFF_INVERT ){
17621790
Blob *pTemp = pA_Blob;
17631791
pA_Blob = pB_Blob;
17641792
pB_Blob = pTemp;
17651793
}
1766
- ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1794
+ ignoreWs = (diffFlags & (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))!=0;
17671795
blob_to_utf8_no_bom(pA_Blob, 0);
17681796
blob_to_utf8_no_bom(pB_Blob, 0);
17691797
17701798
/* Prepare the input files */
17711799
memset(&c, 0, sizeof(c));
17721800
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1773
- &c.nFrom, ignoreEolWs);
1801
+ &c.nFrom, diffFlags);
17741802
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1775
- &c.nTo, ignoreEolWs);
1803
+ &c.nTo, diffFlags);
17761804
if( c.aFrom==0 || c.aTo==0 ){
17771805
fossil_free(c.aFrom);
17781806
fossil_free(c.aTo);
17791807
if( pOut ){
17801808
diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
@@ -1782,20 +1810,27 @@
17821810
return 0;
17831811
}
17841812
17851813
/* Compute the difference */
17861814
diff_all(&c);
1815
+ if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){
1816
+ fossil_free(c.aFrom);
1817
+ fossil_free(c.aTo);
1818
+ fossil_free(c.aEdit);
1819
+ if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags);
1820
+ return 0;
1821
+ }
17871822
if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
17881823
int i, m, n;
17891824
int *a = c.aEdit;
17901825
int mx = c.nEdit;
17911826
for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
17921827
if( n>10000 ){
17931828
fossil_free(c.aFrom);
17941829
fossil_free(c.aTo);
17951830
fossil_free(c.aEdit);
1796
- diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
1831
+ if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
17971832
return 0;
17981833
}
17991834
}
18001835
if( (diffFlags & DIFF_NOOPT)==0 ){
18011836
diff_optimize(&c);
@@ -1828,14 +1863,17 @@
18281863
**
18291864
** --brief Show filenames only DIFF_BRIEF
18301865
** --context|-c N N lines of context. DIFF_CONTEXT_MASK
18311866
** --html Format for HTML DIFF_HTML
18321867
** --invert Invert the diff DIFF_INVERT
1868
+** --ignore-space-at-eol Ignore eol-whitespaces DIFF_IGNORE_EOLWS
1869
+** --ignore-space-at-sol Ignore sol-whitespaces DIFF_IGNORE_SOLWS
18331870
** --linenum|-n Show line numbers DIFF_LINENO
18341871
** --noopt Disable optimization DIFF_NOOPT
18351872
** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
18361873
** --unified Unified diff. ~DIFF_SIDEBYSIDE
1874
+** -w Ignore all whitespaces DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS
18371875
** --width|-W N N character lines. DIFF_WIDTH_MASK
18381876
*/
18391877
u64 diff_options(void){
18401878
u64 diffFlags = 0;
18411879
const char *z;
@@ -1850,10 +1888,13 @@
18501888
f *= DIFF_CONTEXT_MASK+1;
18511889
if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
18521890
diffFlags |= f;
18531891
}
18541892
if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
1893
+ if( find_option("ignore-space-at-sol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_SOLWS;
1894
+ if( find_option("ignore-space-at-eol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_EOLWS;
1895
+ if( find_option("w",0,0)!=0 ) diffFlags |= (DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS);
18551896
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
18561897
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
18571898
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
18581899
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
18591900
return diffFlags;
@@ -1930,11 +1971,14 @@
19301971
typedef struct Annotator Annotator;
19311972
struct Annotator {
19321973
DContext c; /* The diff-engine context */
19331974
struct AnnLine { /* Lines of the original files... */
19341975
const char *z; /* The text of the line */
1935
- short int n; /* Number of bytes (omitting trailing space and \n) */
1976
+ short int n; /* Number of bytes. Whether this omits sol/eol spacing
1977
+ depends on the diffFlags) */
1978
+ unsigned short indent; /* Indenting (number of initial spaces, only used
1979
+ if sol-spacing is ignored in the diffFlags) */
19361980
short int iVers; /* Level at which tag was set */
19371981
} *aOrig;
19381982
int nOrig; /* Number of elements in aOrig[] */
19391983
int nVers; /* Number of versions analyzed */
19401984
int bLimit; /* True if the iLimit was reached */
@@ -1956,18 +2000,20 @@
19562000
*/
19572001
static int annotation_start(Annotator *p, Blob *pInput){
19582002
int i;
19592003
19602004
memset(p, 0, sizeof(*p));
1961
- p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
2005
+ p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
2006
+ 0);
19622007
if( p->c.aTo==0 ){
19632008
return 1;
19642009
}
19652010
p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
19662011
for(i=0; i<p->c.nTo; i++){
19672012
p->aOrig[i].z = p->c.aTo[i].z;
19682013
p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2014
+ p->aOrig[i].indent = p->c.aTo[i].indent;
19692015
p->aOrig[i].iVers = -1;
19702016
}
19712017
p->nOrig = p->c.nTo;
19722018
return 0;
19732019
}
@@ -1983,11 +2029,11 @@
19832029
int i, j;
19842030
int lnTo;
19852031
19862032
/* Prepare the parent file to be diffed */
19872033
p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
1988
- &p->c.nFrom, 1);
2034
+ &p->c.nFrom, 0);
19892035
if( p->c.aFrom==0 ){
19902036
return 1;
19912037
}
19922038
19932039
/* Compute the differences going from pParent to the file being
@@ -2256,10 +2302,11 @@
22562302
@ <pre>
22572303
for(i=0; i<ann.nOrig; i++){
22582304
int iVers = ann.aOrig[i].iVers;
22592305
char *z = (char*)ann.aOrig[i].z;
22602306
int n = ann.aOrig[i].n;
2307
+ int indent = ann.aOrig[i].indent+1;
22612308
char zPrefix[300];
22622309
z[n] = 0;
22632310
if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
22642311
22652312
if( bBlame ){
@@ -2270,26 +2317,26 @@
22702317
"<span style='background-color:%s'>"
22712318
"%s%.10s</a> %s</span> %13.13s:",
22722319
p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser);
22732320
fossil_free(zLink);
22742321
}else{
2275
- sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", "");
2322
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%*s", indent, " ");
22762323
}
22772324
}else{
22782325
if( iVers>=0 ){
22792326
struct AnnVers *p = ann.aVers+iVers;
22802327
char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
22812328
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
22822329
"<span style='background-color:%s'>"
2283
- "%s%.10s</a> %s</span> %4d:",
2284
- p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
2330
+ "%s%.10s</a> %s</span> %4d:%*s",
2331
+ p->zBgColor, zLink, p->zMUuid, p->zDate, i+1, indent, " ");
22852332
fossil_free(zLink);
22862333
}else{
2287
- sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
2334
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:%*s", "", i+1, indent, " ");
22882335
}
22892336
}
2290
- @ %s(zPrefix) %h(z)
2337
+ @ %s(zPrefix)%h(z)
22912338
22922339
}
22932340
@ </pre>
22942341
style_footer();
22952342
}
@@ -2372,27 +2419,28 @@
23722419
fossil_print("---------------------------------------------------\n");
23732420
}
23742421
for(i=0; i<ann.nOrig; i++){
23752422
int iVers = ann.aOrig[i].iVers;
23762423
char *z = (char*)ann.aOrig[i].z;
2424
+ int indent = ann.aOrig[i].indent + 1;
23772425
int n = ann.aOrig[i].n;
23782426
struct AnnVers *p;
23792427
if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
23802428
p = ann.aVers + iVers;
23812429
if( bBlame ){
23822430
if( iVers>=0 ){
2383
- fossil_print("%.10s %s %13.13s: %.*s\n",
2384
- fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z);
2431
+ fossil_print("%.10s %s %13.13s:%*s%.*s\n",
2432
+ fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, indent, " ", n, z);
23852433
}else{
2386
- fossil_print("%35s %.*s\n", "", n, z);
2434
+ fossil_print("%35s %*s%.*s\n", "", indent, " ", n, z);
23872435
}
23882436
}else{
23892437
if( iVers>=0 ){
2390
- fossil_print("%.10s %s %5d: %.*s\n",
2391
- fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z);
2438
+ fossil_print("%.10s %s %5d:%*s%.*s\n",
2439
+ fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, indent, " ", n, z);
23922440
}else{
2393
- fossil_print("%21s %5d: %.*s\n",
2394
- "", i+1, n, z);
2441
+ fossil_print("%21s %5d:%*s%.*s\n",
2442
+ "", i+1, indent, " ", n, z);
23952443
}
23962444
}
23972445
}
23982446
}
23992447
--- src/diff.c
+++ src/diff.c
@@ -28,18 +28,18 @@
28 ** Flag parameters to the text_diff() routine used to control the formatting
29 ** of the diff output.
30 */
31 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
32 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33 #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */
34 #define DIFF_SIDEBYSIDE ((u64)0x02000000) /* Generate a side-by-side diff */
35 #define DIFF_VERBOSE ((u64)0x04000000) /* Missing shown as empty files */
36 #define DIFF_BRIEF ((u64)0x08000000) /* Show filenames only */
37 #define DIFF_INLINE ((u64)0x00000000) /* Inline (not side-by-side) diff */
38 #define DIFF_HTML ((u64)0x10000000) /* Render for HTML */
39 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
40 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
41 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
42 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43 #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
44 #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
45
@@ -54,10 +54,13 @@
54 "cannot compute difference between symlink and regular file\n"
55
56 #define DIFF_TOO_MANY_CHANGES \
57 "more than 10,000 changes\n"
58
 
 
 
59 /*
60 ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
61 */
62 #define LENGTH_MASK_SZ 13
63 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -73,10 +76,11 @@
73 */
74 typedef struct DLine DLine;
75 struct DLine {
76 const char *z; /* The text of the line */
77 unsigned int h; /* Hash of the line */
 
78 unsigned int iNext; /* 1+(Index of next line with same the same hash) */
79
80 /* an array of DLine elements serves two purposes. The fields
81 ** above are one per line of input text. But each entry is also
82 ** a bucket in a hash table, as follows: */
@@ -125,12 +129,12 @@
125 ** too long.
126 **
127 ** Profiling show that in most cases this routine consumes the bulk of
128 ** the CPU time on a diff.
129 */
130 static DLine *break_into_lines(const char *z, int n, int *pnLine, int ignoreWS){
131 int nLine, i, j, k, x;
132 unsigned int h, h2;
133 DLine *a;
134
135 /* Count the number of lines. Allocate space to hold
136 ** the returned array.
@@ -158,18 +162,33 @@
158 return a;
159 }
160
161 /* Fill in the array */
162 for(i=0; i<nLine; i++){
163 a[i].z = z;
164 for(j=0; z[j] && z[j]!='\n'; j++){}
165 k = j;
166 while( ignoreWS && k>0 && fossil_isspace(z[k-1]) ){ k--; }
167 for(h=0, x=0; x<k; x++){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168 h = h ^ (h<<2) ^ z[x];
169 }
170 a[i].h = h = (h<<LENGTH_MASK_SZ) | k;
171 h2 = h % nLine;
172 a[i].iNext = a[h2].iHash;
173 a[h2].iHash = i+1;
174 z += j+1;
175 }
@@ -221,15 +240,21 @@
221 }else if( cPrefix=='+' ){
222 blob_append(pOut, "<span class=\"diffadd\">", -1);
223 }else if( cPrefix=='-' ){
224 blob_append(pOut, "<span class=\"diffrm\">", -1);
225 }
 
 
 
226 htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK));
227 if( cPrefix!=' ' ){
228 blob_append(pOut, "</span>", -1);
229 }
230 }else{
 
 
 
231 blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
232 }
233 blob_append(pOut, "\n", 1);
234 }
235
@@ -507,10 +532,13 @@
507 p->iEnd = p->iEnd2;
508 p->iEnd2 = 0;
509 }
510 }
511 }
 
 
 
512 if( c=='\t' && !p->escHtml ){
513 blob_append(pCol, " ", 1);
514 while( (k&7)!=7 && (p->escHtml || k<w) ){
515 blob_append(pCol, " ", 1);
516 k++;
@@ -534,11 +562,11 @@
534 blob_append(pCol, "</span>", 7);
535 }
536 if( col==SBS_TXTB ){
537 sbsWriteNewlines(p);
538 }else if( !p->escHtml ){
539 sbsWriteSpace(p, w-k, SBS_TXTA);
540 }
541 }
542
543 /*
544 ** Append a column to the final output blob.
@@ -1753,28 +1781,28 @@
1753 Blob *pB_Blob, /* TO file */
1754 Blob *pOut, /* Write diff here if not NULL */
1755 ReCompiled *pRe, /* Only output changes where this Regexp matches */
1756 u64 diffFlags /* DIFF_* flags defined above */
1757 ){
1758 int ignoreEolWs; /* Ignore whitespace at the end of lines */
1759 DContext c;
1760
1761 if( diffFlags & DIFF_INVERT ){
1762 Blob *pTemp = pA_Blob;
1763 pA_Blob = pB_Blob;
1764 pB_Blob = pTemp;
1765 }
1766 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
1767 blob_to_utf8_no_bom(pA_Blob, 0);
1768 blob_to_utf8_no_bom(pB_Blob, 0);
1769
1770 /* Prepare the input files */
1771 memset(&c, 0, sizeof(c));
1772 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1773 &c.nFrom, ignoreEolWs);
1774 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1775 &c.nTo, ignoreEolWs);
1776 if( c.aFrom==0 || c.aTo==0 ){
1777 fossil_free(c.aFrom);
1778 fossil_free(c.aTo);
1779 if( pOut ){
1780 diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
@@ -1782,20 +1810,27 @@
1782 return 0;
1783 }
1784
1785 /* Compute the difference */
1786 diff_all(&c);
 
 
 
 
 
 
 
1787 if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
1788 int i, m, n;
1789 int *a = c.aEdit;
1790 int mx = c.nEdit;
1791 for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
1792 if( n>10000 ){
1793 fossil_free(c.aFrom);
1794 fossil_free(c.aTo);
1795 fossil_free(c.aEdit);
1796 diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
1797 return 0;
1798 }
1799 }
1800 if( (diffFlags & DIFF_NOOPT)==0 ){
1801 diff_optimize(&c);
@@ -1828,14 +1863,17 @@
1828 **
1829 ** --brief Show filenames only DIFF_BRIEF
1830 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK
1831 ** --html Format for HTML DIFF_HTML
1832 ** --invert Invert the diff DIFF_INVERT
 
 
1833 ** --linenum|-n Show line numbers DIFF_LINENO
1834 ** --noopt Disable optimization DIFF_NOOPT
1835 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1836 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
 
1837 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1838 */
1839 u64 diff_options(void){
1840 u64 diffFlags = 0;
1841 const char *z;
@@ -1850,10 +1888,13 @@
1850 f *= DIFF_CONTEXT_MASK+1;
1851 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1852 diffFlags |= f;
1853 }
1854 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
 
 
 
1855 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
1856 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
1857 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
1858 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
1859 return diffFlags;
@@ -1930,11 +1971,14 @@
1930 typedef struct Annotator Annotator;
1931 struct Annotator {
1932 DContext c; /* The diff-engine context */
1933 struct AnnLine { /* Lines of the original files... */
1934 const char *z; /* The text of the line */
1935 short int n; /* Number of bytes (omitting trailing space and \n) */
 
 
 
1936 short int iVers; /* Level at which tag was set */
1937 } *aOrig;
1938 int nOrig; /* Number of elements in aOrig[] */
1939 int nVers; /* Number of versions analyzed */
1940 int bLimit; /* True if the iLimit was reached */
@@ -1956,18 +2000,20 @@
1956 */
1957 static int annotation_start(Annotator *p, Blob *pInput){
1958 int i;
1959
1960 memset(p, 0, sizeof(*p));
1961 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,1);
 
1962 if( p->c.aTo==0 ){
1963 return 1;
1964 }
1965 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
1966 for(i=0; i<p->c.nTo; i++){
1967 p->aOrig[i].z = p->c.aTo[i].z;
1968 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
 
1969 p->aOrig[i].iVers = -1;
1970 }
1971 p->nOrig = p->c.nTo;
1972 return 0;
1973 }
@@ -1983,11 +2029,11 @@
1983 int i, j;
1984 int lnTo;
1985
1986 /* Prepare the parent file to be diffed */
1987 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
1988 &p->c.nFrom, 1);
1989 if( p->c.aFrom==0 ){
1990 return 1;
1991 }
1992
1993 /* Compute the differences going from pParent to the file being
@@ -2256,10 +2302,11 @@
2256 @ <pre>
2257 for(i=0; i<ann.nOrig; i++){
2258 int iVers = ann.aOrig[i].iVers;
2259 char *z = (char*)ann.aOrig[i].z;
2260 int n = ann.aOrig[i].n;
 
2261 char zPrefix[300];
2262 z[n] = 0;
2263 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2264
2265 if( bBlame ){
@@ -2270,26 +2317,26 @@
2270 "<span style='background-color:%s'>"
2271 "%s%.10s</a> %s</span> %13.13s:",
2272 p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser);
2273 fossil_free(zLink);
2274 }else{
2275 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", "");
2276 }
2277 }else{
2278 if( iVers>=0 ){
2279 struct AnnVers *p = ann.aVers+iVers;
2280 char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2281 sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2282 "<span style='background-color:%s'>"
2283 "%s%.10s</a> %s</span> %4d:",
2284 p->zBgColor, zLink, p->zMUuid, p->zDate, i+1);
2285 fossil_free(zLink);
2286 }else{
2287 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1);
2288 }
2289 }
2290 @ %s(zPrefix) %h(z)
2291
2292 }
2293 @ </pre>
2294 style_footer();
2295 }
@@ -2372,27 +2419,28 @@
2372 fossil_print("---------------------------------------------------\n");
2373 }
2374 for(i=0; i<ann.nOrig; i++){
2375 int iVers = ann.aOrig[i].iVers;
2376 char *z = (char*)ann.aOrig[i].z;
 
2377 int n = ann.aOrig[i].n;
2378 struct AnnVers *p;
2379 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2380 p = ann.aVers + iVers;
2381 if( bBlame ){
2382 if( iVers>=0 ){
2383 fossil_print("%.10s %s %13.13s: %.*s\n",
2384 fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z);
2385 }else{
2386 fossil_print("%35s %.*s\n", "", n, z);
2387 }
2388 }else{
2389 if( iVers>=0 ){
2390 fossil_print("%.10s %s %5d: %.*s\n",
2391 fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z);
2392 }else{
2393 fossil_print("%21s %5d: %.*s\n",
2394 "", i+1, n, z);
2395 }
2396 }
2397 }
2398 }
2399
--- src/diff.c
+++ src/diff.c
@@ -28,18 +28,18 @@
28 ** Flag parameters to the text_diff() routine used to control the formatting
29 ** of the diff output.
30 */
31 #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */
32 #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */
33 #define DIFF_IGNORE_SOLWS ((u64)0x01000000) /* Ignore start-of-line whitespace */
34 #define DIFF_IGNORE_EOLWS ((u64)0x02000000) /* Ignore end-of-line whitespace */
35 #define DIFF_SIDEBYSIDE ((u64)0x04000000) /* Generate a side-by-side diff */
36 #define DIFF_VERBOSE ((u64)0x08000000) /* Missing shown as empty files */
37 #define DIFF_BRIEF ((u64)0x00000000) /* Show filenames only */
38 #define DIFF_INLINE ((u64)0x10000000) /* Inline (not side-by-side) diff */
39 #define DIFF_HTML ((u64)0x20000000) /* Render for HTML */
40 #define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */
41 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
42 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
43 #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */
44 #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */
45
@@ -54,10 +54,13 @@
54 "cannot compute difference between symlink and regular file\n"
55
56 #define DIFF_TOO_MANY_CHANGES \
57 "more than 10,000 changes\n"
58
59 #define DIFF_WHITESPACE_ONLY \
60 "whitespace changes only\n"
61
62 /*
63 ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes)
64 */
65 #define LENGTH_MASK_SZ 13
66 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -73,10 +76,11 @@
76 */
77 typedef struct DLine DLine;
78 struct DLine {
79 const char *z; /* The text of the line */
80 unsigned int h; /* Hash of the line */
81 unsigned short indent; /* Indent of the line. Only !=0 with --ignore-space-at sol option */
82 unsigned int iNext; /* 1+(Index of next line with same the same hash) */
83
84 /* an array of DLine elements serves two purposes. The fields
85 ** above are one per line of input text. But each entry is also
86 ** a bucket in a hash table, as follows: */
@@ -125,12 +129,12 @@
129 ** too long.
130 **
131 ** Profiling show that in most cases this routine consumes the bulk of
132 ** the CPU time on a diff.
133 */
134 static DLine *break_into_lines(const char *z, int n, int *pnLine, u64 diffFlags){
135 int nLine, i, j, k, s, indent, x;
136 unsigned int h, h2;
137 DLine *a;
138
139 /* Count the number of lines. Allocate space to hold
140 ** the returned array.
@@ -158,18 +162,33 @@
162 return a;
163 }
164
165 /* Fill in the array */
166 for(i=0; i<nLine; i++){
 
167 for(j=0; z[j] && z[j]!='\n'; j++){}
168 k = j;
169 s = 0;
170 indent = 0;
171 if( diffFlags & DIFF_IGNORE_EOLWS ){
172 while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
173 }
174 if( diffFlags & DIFF_IGNORE_SOLWS ){
175 while( s<k && fossil_isspace(z[s]) ){
176 if( z[s]=='\t' ){
177 indent = ((indent+9)/8)*8;
178 }else if( z[s]==' ' ){
179 indent++;
180 }
181 s++;
182 }
183 }
184 a[i].z = z+s;
185 a[i].indent = s;
186 for(h=0, x=s; x<k; x++){
187 h = h ^ (h<<2) ^ z[x];
188 }
189 a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
190 h2 = h % nLine;
191 a[i].iNext = a[h2].iHash;
192 a[h2].iHash = i+1;
193 z += j+1;
194 }
@@ -221,15 +240,21 @@
240 }else if( cPrefix=='+' ){
241 blob_append(pOut, "<span class=\"diffadd\">", -1);
242 }else if( cPrefix=='-' ){
243 blob_append(pOut, "<span class=\"diffrm\">", -1);
244 }
245 if( pLine->indent ){
246 blob_appendf(pOut, "%*s", pLine->indent, " ");
247 }
248 htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK));
249 if( cPrefix!=' ' ){
250 blob_append(pOut, "</span>", -1);
251 }
252 }else{
253 if( pLine->indent ){
254 blob_appendf(pOut, "%*s", pLine->indent, " ");
255 }
256 blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK);
257 }
258 blob_append(pOut, "\n", 1);
259 }
260
@@ -507,10 +532,13 @@
532 p->iEnd = p->iEnd2;
533 p->iEnd2 = 0;
534 }
535 }
536 }
537 if( pLine->indent && i==0 ){
538 blob_appendf(pCol, "%*s", pLine->indent, " ");
539 }
540 if( c=='\t' && !p->escHtml ){
541 blob_append(pCol, " ", 1);
542 while( (k&7)!=7 && (p->escHtml || k<w) ){
543 blob_append(pCol, " ", 1);
544 k++;
@@ -534,11 +562,11 @@
562 blob_append(pCol, "</span>", 7);
563 }
564 if( col==SBS_TXTB ){
565 sbsWriteNewlines(p);
566 }else if( !p->escHtml ){
567 sbsWriteSpace(p, w-k-pLine->indent, SBS_TXTA);
568 }
569 }
570
571 /*
572 ** Append a column to the final output blob.
@@ -1753,28 +1781,28 @@
1781 Blob *pB_Blob, /* TO file */
1782 Blob *pOut, /* Write diff here if not NULL */
1783 ReCompiled *pRe, /* Only output changes where this Regexp matches */
1784 u64 diffFlags /* DIFF_* flags defined above */
1785 ){
1786 int ignoreWs; /* Ignore whitespace */
1787 DContext c;
1788
1789 if( diffFlags & DIFF_INVERT ){
1790 Blob *pTemp = pA_Blob;
1791 pA_Blob = pB_Blob;
1792 pB_Blob = pTemp;
1793 }
1794 ignoreWs = (diffFlags & (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))!=0;
1795 blob_to_utf8_no_bom(pA_Blob, 0);
1796 blob_to_utf8_no_bom(pB_Blob, 0);
1797
1798 /* Prepare the input files */
1799 memset(&c, 0, sizeof(c));
1800 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1801 &c.nFrom, diffFlags);
1802 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1803 &c.nTo, diffFlags);
1804 if( c.aFrom==0 || c.aTo==0 ){
1805 fossil_free(c.aFrom);
1806 fossil_free(c.aTo);
1807 if( pOut ){
1808 diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags);
@@ -1782,20 +1810,27 @@
1810 return 0;
1811 }
1812
1813 /* Compute the difference */
1814 diff_all(&c);
1815 if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){
1816 fossil_free(c.aFrom);
1817 fossil_free(c.aTo);
1818 fossil_free(c.aEdit);
1819 if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags);
1820 return 0;
1821 }
1822 if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){
1823 int i, m, n;
1824 int *a = c.aEdit;
1825 int mx = c.nEdit;
1826 for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; }
1827 if( n>10000 ){
1828 fossil_free(c.aFrom);
1829 fossil_free(c.aTo);
1830 fossil_free(c.aEdit);
1831 if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags);
1832 return 0;
1833 }
1834 }
1835 if( (diffFlags & DIFF_NOOPT)==0 ){
1836 diff_optimize(&c);
@@ -1828,14 +1863,17 @@
1863 **
1864 ** --brief Show filenames only DIFF_BRIEF
1865 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK
1866 ** --html Format for HTML DIFF_HTML
1867 ** --invert Invert the diff DIFF_INVERT
1868 ** --ignore-space-at-eol Ignore eol-whitespaces DIFF_IGNORE_EOLWS
1869 ** --ignore-space-at-sol Ignore sol-whitespaces DIFF_IGNORE_SOLWS
1870 ** --linenum|-n Show line numbers DIFF_LINENO
1871 ** --noopt Disable optimization DIFF_NOOPT
1872 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1873 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
1874 ** -w Ignore all whitespaces DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS
1875 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1876 */
1877 u64 diff_options(void){
1878 u64 diffFlags = 0;
1879 const char *z;
@@ -1850,10 +1888,13 @@
1888 f *= DIFF_CONTEXT_MASK+1;
1889 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1890 diffFlags |= f;
1891 }
1892 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
1893 if( find_option("ignore-space-at-sol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_SOLWS;
1894 if( find_option("ignore-space-at-eol",0,0)!=0 ) diffFlags |= DIFF_IGNORE_EOLWS;
1895 if( find_option("w",0,0)!=0 ) diffFlags |= (DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS);
1896 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
1897 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
1898 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
1899 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
1900 return diffFlags;
@@ -1930,11 +1971,14 @@
1971 typedef struct Annotator Annotator;
1972 struct Annotator {
1973 DContext c; /* The diff-engine context */
1974 struct AnnLine { /* Lines of the original files... */
1975 const char *z; /* The text of the line */
1976 short int n; /* Number of bytes. Whether this omits sol/eol spacing
1977 depends on the diffFlags) */
1978 unsigned short indent; /* Indenting (number of initial spaces, only used
1979 if sol-spacing is ignored in the diffFlags) */
1980 short int iVers; /* Level at which tag was set */
1981 } *aOrig;
1982 int nOrig; /* Number of elements in aOrig[] */
1983 int nVers; /* Number of versions analyzed */
1984 int bLimit; /* True if the iLimit was reached */
@@ -1956,18 +2000,20 @@
2000 */
2001 static int annotation_start(Annotator *p, Blob *pInput){
2002 int i;
2003
2004 memset(p, 0, sizeof(*p));
2005 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
2006 0);
2007 if( p->c.aTo==0 ){
2008 return 1;
2009 }
2010 p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo );
2011 for(i=0; i<p->c.nTo; i++){
2012 p->aOrig[i].z = p->c.aTo[i].z;
2013 p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK;
2014 p->aOrig[i].indent = p->c.aTo[i].indent;
2015 p->aOrig[i].iVers = -1;
2016 }
2017 p->nOrig = p->c.nTo;
2018 return 0;
2019 }
@@ -1983,11 +2029,11 @@
2029 int i, j;
2030 int lnTo;
2031
2032 /* Prepare the parent file to be diffed */
2033 p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent),
2034 &p->c.nFrom, 0);
2035 if( p->c.aFrom==0 ){
2036 return 1;
2037 }
2038
2039 /* Compute the differences going from pParent to the file being
@@ -2256,10 +2302,11 @@
2302 @ <pre>
2303 for(i=0; i<ann.nOrig; i++){
2304 int iVers = ann.aOrig[i].iVers;
2305 char *z = (char*)ann.aOrig[i].z;
2306 int n = ann.aOrig[i].n;
2307 int indent = ann.aOrig[i].indent+1;
2308 char zPrefix[300];
2309 z[n] = 0;
2310 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2311
2312 if( bBlame ){
@@ -2270,26 +2317,26 @@
2317 "<span style='background-color:%s'>"
2318 "%s%.10s</a> %s</span> %13.13s:",
2319 p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser);
2320 fossil_free(zLink);
2321 }else{
2322 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%*s", indent, " ");
2323 }
2324 }else{
2325 if( iVers>=0 ){
2326 struct AnnVers *p = ann.aVers+iVers;
2327 char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid);
2328 sqlite3_snprintf(sizeof(zPrefix), zPrefix,
2329 "<span style='background-color:%s'>"
2330 "%s%.10s</a> %s</span> %4d:%*s",
2331 p->zBgColor, zLink, p->zMUuid, p->zDate, i+1, indent, " ");
2332 fossil_free(zLink);
2333 }else{
2334 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:%*s", "", i+1, indent, " ");
2335 }
2336 }
2337 @ %s(zPrefix)%h(z)
2338
2339 }
2340 @ </pre>
2341 style_footer();
2342 }
@@ -2372,27 +2419,28 @@
2419 fossil_print("---------------------------------------------------\n");
2420 }
2421 for(i=0; i<ann.nOrig; i++){
2422 int iVers = ann.aOrig[i].iVers;
2423 char *z = (char*)ann.aOrig[i].z;
2424 int indent = ann.aOrig[i].indent + 1;
2425 int n = ann.aOrig[i].n;
2426 struct AnnVers *p;
2427 if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1;
2428 p = ann.aVers + iVers;
2429 if( bBlame ){
2430 if( iVers>=0 ){
2431 fossil_print("%.10s %s %13.13s:%*s%.*s\n",
2432 fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, indent, " ", n, z);
2433 }else{
2434 fossil_print("%35s %*s%.*s\n", "", indent, " ", n, z);
2435 }
2436 }else{
2437 if( iVers>=0 ){
2438 fossil_print("%.10s %s %5d:%*s%.*s\n",
2439 fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, indent, " ", n, z);
2440 }else{
2441 fossil_print("%21s %5d:%*s%.*s\n",
2442 "", i+1, indent, " ", n, z);
2443 }
2444 }
2445 }
2446 }
2447
+30 -17
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -628,10 +628,11 @@
628628
@ HR_PAD_TOP 4
629629
@ HR_PAD_BTM 8
630630
@ FN_BG #444444
631631
@ FN_FG #ffffff
632632
@ FN_PAD 5
633
+@ ERR_FG #ee0000
633634
@ PADX 5
634635
@ WIDTH 80
635636
@ HEIGHT 45
636637
@ LB_HEIGHT 25
637638
@ }
@@ -680,20 +681,21 @@
680681
@ if {![regexp {^=+ (.*?) =+ versus =+ (.*?) =+$} $line all fn fn2]
681682
@ && ![regexp {^=+ (.*?) =+$} $line all fn]
682683
@ } {
683684
@ continue
684685
@ }
685
-@ if {[string compare -length 6 [getLine $difftxt $N ii] "<table"]} {
686
+@ set errMsg ""
687
+@ set line [getLine $difftxt $N ii]
688
+@ if {[string compare -length 6 $line "<table"]
689
+@ && ![regexp {<p[^>]*>(.+)} $line - errMsg]} {
686690
@ continue
687691
@ }
688692
@ incr nDiffs
689693
@ set idx [expr {$nDiffs > 1 ? [.txtA index end] : "1.0"}]
690694
@ .wfiles.lb insert end $fn
691695
@
692696
@ foreach c [cols] {
693
-@ while {[getLine $difftxt $N ii] ne "<pre>"} continue
694
-@
695697
@ if {$nDiffs > 1} {
696698
@ $c insert end \n -
697699
@ }
698700
@ if {[colType $c] eq "txt"} {
699701
@ $c insert end $fn\n fn
@@ -701,10 +703,12 @@
701703
@ } else {
702704
@ $c insert end \n fn
703705
@ }
704706
@ $c insert end \n -
705707
@
708
+@ if {$errMsg ne ""} continue
709
+@ while {[getLine $difftxt $N ii] ne "<pre>"} continue
706710
@ set type [colType $c]
707711
@ set str {}
708712
@ while {[set line [getLine $difftxt $N ii]] ne "</pre>"} {
709713
@ set len [string length [dehtml $line]]
710714
@ if {$len > $widths($type)} {
@@ -723,10 +727,15 @@
723727
@ } else {
724728
@ $c insert end [dehtml $pre] -
725729
@ }
726730
@ }
727731
@ }
732
+@
733
+@ if {$errMsg ne ""} {
734
+@ foreach c {.txtA .txtB} {$c insert end [string trim $errMsg] err}
735
+@ foreach c [cols] {$c insert end \n -}
736
+@ }
728737
@ }
729738
@
730739
@ foreach c [cols] {
731740
@ set type [colType $c]
732741
@ if {$type ne "txt"} {
@@ -894,10 +903,11 @@
894903
@ $txt tag config $tag -background $CFG([string toupper $tag]_BG)
895904
@ $txt tag lower $tag
896905
@ }
897906
@ $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
898907
@ -justify center
908
+@ $txt tag config err -foreground $CFG(ERR_FG)
899909
@ }
900910
@ text .mkr
901911
@
902912
@ foreach c [cols] {
903913
@ set keyPrefix [string toupper [colType $c]]_COL_
@@ -1079,23 +1089,27 @@
10791089
** The "--binary" option causes files matching the glob PATTERN to be treated
10801090
** as binary when considering if they should be used with external diff program.
10811091
** This option overrides the "binary-glob" setting.
10821092
**
10831093
** Options:
1084
-** --binary PATTERN Treat files that match the glob PATTERN as binary
1085
-** --branch BRANCH Show diff of all changes on BRANCH
1086
-** --brief Show filenames only
1087
-** --context|-c N Use N lines of context
1088
-** --diff-binary BOOL Include binary files when using external commands
1089
-** --from|-r VERSION select VERSION as source for the diff
1090
-** --internal|-i use internal diff logic
1091
-** --side-by-side|-y side-by-side diff
1092
-** --tk Launch a Tcl/Tk GUI for display
1093
-** --to VERSION select VERSION as target for the diff
1094
-** --unified unified diff
1095
-** -v|--verbose output complete text of added or deleted files
1096
-** -W|--width Width of lines in side-by-side diff
1094
+** --binary PATTERN Treat files that match the glob PATTERN as binary
1095
+** --branch BRANCH Show diff of all changes on BRANCH
1096
+** --brief Show filenames only
1097
+** --context|-c N Use N lines of context
1098
+** --diff-binary BOOL Include binary files when using external commands
1099
+** --from|-r VERSION select VERSION as source for the diff
1100
+** --ignore-space-at-eol Ignore changes to end-of-line whitespace
1101
+** --ignore-space-at-sol Ignore changes to start-of-line whitespace
1102
+** --internal|-i use internal diff logic
1103
+** --side-by-side|-y side-by-side diff
1104
+** --tk Launch a Tcl/Tk GUI for display
1105
+** --to VERSION select VERSION as target for the diff
1106
+** --unified unified diff
1107
+** -v|--verbose output complete text of added or deleted files
1108
+** -w Ignore changes to start-of-line and end-of-line
1109
+** whitespace
1110
+** -W|--width Width of lines in side-by-side diff
10971111
*/
10981112
void diff_cmd(void){
10991113
int isGDiff; /* True for gdiff. False for normal diff */
11001114
int isInternDiff; /* True for internal diff */
11011115
int verboseFlag; /* True if -v or --verbose flag is used */
@@ -1121,11 +1135,10 @@
11211135
verboseFlag = find_option("verbose","v",0)!=0;
11221136
if( !verboseFlag ){
11231137
verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */
11241138
}
11251139
if( verboseFlag ) diffFlags |= DIFF_VERBOSE;
1126
-
11271140
if( zBranch ){
11281141
if( zTo || zFrom ){
11291142
fossil_fatal("cannot use --from or --to with --branch");
11301143
}
11311144
zTo = zBranch;
11321145
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -628,10 +628,11 @@
628 @ HR_PAD_TOP 4
629 @ HR_PAD_BTM 8
630 @ FN_BG #444444
631 @ FN_FG #ffffff
632 @ FN_PAD 5
 
633 @ PADX 5
634 @ WIDTH 80
635 @ HEIGHT 45
636 @ LB_HEIGHT 25
637 @ }
@@ -680,20 +681,21 @@
680 @ if {![regexp {^=+ (.*?) =+ versus =+ (.*?) =+$} $line all fn fn2]
681 @ && ![regexp {^=+ (.*?) =+$} $line all fn]
682 @ } {
683 @ continue
684 @ }
685 @ if {[string compare -length 6 [getLine $difftxt $N ii] "<table"]} {
 
 
 
686 @ continue
687 @ }
688 @ incr nDiffs
689 @ set idx [expr {$nDiffs > 1 ? [.txtA index end] : "1.0"}]
690 @ .wfiles.lb insert end $fn
691 @
692 @ foreach c [cols] {
693 @ while {[getLine $difftxt $N ii] ne "<pre>"} continue
694 @
695 @ if {$nDiffs > 1} {
696 @ $c insert end \n -
697 @ }
698 @ if {[colType $c] eq "txt"} {
699 @ $c insert end $fn\n fn
@@ -701,10 +703,12 @@
701 @ } else {
702 @ $c insert end \n fn
703 @ }
704 @ $c insert end \n -
705 @
 
 
706 @ set type [colType $c]
707 @ set str {}
708 @ while {[set line [getLine $difftxt $N ii]] ne "</pre>"} {
709 @ set len [string length [dehtml $line]]
710 @ if {$len > $widths($type)} {
@@ -723,10 +727,15 @@
723 @ } else {
724 @ $c insert end [dehtml $pre] -
725 @ }
726 @ }
727 @ }
 
 
 
 
 
728 @ }
729 @
730 @ foreach c [cols] {
731 @ set type [colType $c]
732 @ if {$type ne "txt"} {
@@ -894,10 +903,11 @@
894 @ $txt tag config $tag -background $CFG([string toupper $tag]_BG)
895 @ $txt tag lower $tag
896 @ }
897 @ $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
898 @ -justify center
 
899 @ }
900 @ text .mkr
901 @
902 @ foreach c [cols] {
903 @ set keyPrefix [string toupper [colType $c]]_COL_
@@ -1079,23 +1089,27 @@
1079 ** The "--binary" option causes files matching the glob PATTERN to be treated
1080 ** as binary when considering if they should be used with external diff program.
1081 ** This option overrides the "binary-glob" setting.
1082 **
1083 ** Options:
1084 ** --binary PATTERN Treat files that match the glob PATTERN as binary
1085 ** --branch BRANCH Show diff of all changes on BRANCH
1086 ** --brief Show filenames only
1087 ** --context|-c N Use N lines of context
1088 ** --diff-binary BOOL Include binary files when using external commands
1089 ** --from|-r VERSION select VERSION as source for the diff
1090 ** --internal|-i use internal diff logic
1091 ** --side-by-side|-y side-by-side diff
1092 ** --tk Launch a Tcl/Tk GUI for display
1093 ** --to VERSION select VERSION as target for the diff
1094 ** --unified unified diff
1095 ** -v|--verbose output complete text of added or deleted files
1096 ** -W|--width Width of lines in side-by-side diff
 
 
 
 
1097 */
1098 void diff_cmd(void){
1099 int isGDiff; /* True for gdiff. False for normal diff */
1100 int isInternDiff; /* True for internal diff */
1101 int verboseFlag; /* True if -v or --verbose flag is used */
@@ -1121,11 +1135,10 @@
1121 verboseFlag = find_option("verbose","v",0)!=0;
1122 if( !verboseFlag ){
1123 verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */
1124 }
1125 if( verboseFlag ) diffFlags |= DIFF_VERBOSE;
1126
1127 if( zBranch ){
1128 if( zTo || zFrom ){
1129 fossil_fatal("cannot use --from or --to with --branch");
1130 }
1131 zTo = zBranch;
1132
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -628,10 +628,11 @@
628 @ HR_PAD_TOP 4
629 @ HR_PAD_BTM 8
630 @ FN_BG #444444
631 @ FN_FG #ffffff
632 @ FN_PAD 5
633 @ ERR_FG #ee0000
634 @ PADX 5
635 @ WIDTH 80
636 @ HEIGHT 45
637 @ LB_HEIGHT 25
638 @ }
@@ -680,20 +681,21 @@
681 @ if {![regexp {^=+ (.*?) =+ versus =+ (.*?) =+$} $line all fn fn2]
682 @ && ![regexp {^=+ (.*?) =+$} $line all fn]
683 @ } {
684 @ continue
685 @ }
686 @ set errMsg ""
687 @ set line [getLine $difftxt $N ii]
688 @ if {[string compare -length 6 $line "<table"]
689 @ && ![regexp {<p[^>]*>(.+)} $line - errMsg]} {
690 @ continue
691 @ }
692 @ incr nDiffs
693 @ set idx [expr {$nDiffs > 1 ? [.txtA index end] : "1.0"}]
694 @ .wfiles.lb insert end $fn
695 @
696 @ foreach c [cols] {
 
 
697 @ if {$nDiffs > 1} {
698 @ $c insert end \n -
699 @ }
700 @ if {[colType $c] eq "txt"} {
701 @ $c insert end $fn\n fn
@@ -701,10 +703,12 @@
703 @ } else {
704 @ $c insert end \n fn
705 @ }
706 @ $c insert end \n -
707 @
708 @ if {$errMsg ne ""} continue
709 @ while {[getLine $difftxt $N ii] ne "<pre>"} continue
710 @ set type [colType $c]
711 @ set str {}
712 @ while {[set line [getLine $difftxt $N ii]] ne "</pre>"} {
713 @ set len [string length [dehtml $line]]
714 @ if {$len > $widths($type)} {
@@ -723,10 +727,15 @@
727 @ } else {
728 @ $c insert end [dehtml $pre] -
729 @ }
730 @ }
731 @ }
732 @
733 @ if {$errMsg ne ""} {
734 @ foreach c {.txtA .txtB} {$c insert end [string trim $errMsg] err}
735 @ foreach c [cols] {$c insert end \n -}
736 @ }
737 @ }
738 @
739 @ foreach c [cols] {
740 @ set type [colType $c]
741 @ if {$type ne "txt"} {
@@ -894,10 +903,11 @@
903 @ $txt tag config $tag -background $CFG([string toupper $tag]_BG)
904 @ $txt tag lower $tag
905 @ }
906 @ $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
907 @ -justify center
908 @ $txt tag config err -foreground $CFG(ERR_FG)
909 @ }
910 @ text .mkr
911 @
912 @ foreach c [cols] {
913 @ set keyPrefix [string toupper [colType $c]]_COL_
@@ -1079,23 +1089,27 @@
1089 ** The "--binary" option causes files matching the glob PATTERN to be treated
1090 ** as binary when considering if they should be used with external diff program.
1091 ** This option overrides the "binary-glob" setting.
1092 **
1093 ** Options:
1094 ** --binary PATTERN Treat files that match the glob PATTERN as binary
1095 ** --branch BRANCH Show diff of all changes on BRANCH
1096 ** --brief Show filenames only
1097 ** --context|-c N Use N lines of context
1098 ** --diff-binary BOOL Include binary files when using external commands
1099 ** --from|-r VERSION select VERSION as source for the diff
1100 ** --ignore-space-at-eol Ignore changes to end-of-line whitespace
1101 ** --ignore-space-at-sol Ignore changes to start-of-line whitespace
1102 ** --internal|-i use internal diff logic
1103 ** --side-by-side|-y side-by-side diff
1104 ** --tk Launch a Tcl/Tk GUI for display
1105 ** --to VERSION select VERSION as target for the diff
1106 ** --unified unified diff
1107 ** -v|--verbose output complete text of added or deleted files
1108 ** -w Ignore changes to start-of-line and end-of-line
1109 ** whitespace
1110 ** -W|--width Width of lines in side-by-side diff
1111 */
1112 void diff_cmd(void){
1113 int isGDiff; /* True for gdiff. False for normal diff */
1114 int isInternDiff; /* True for internal diff */
1115 int verboseFlag; /* True if -v or --verbose flag is used */
@@ -1121,11 +1135,10 @@
1135 verboseFlag = find_option("verbose","v",0)!=0;
1136 if( !verboseFlag ){
1137 verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */
1138 }
1139 if( verboseFlag ) diffFlags |= DIFF_VERBOSE;
 
1140 if( zBranch ){
1141 if( zTo || zFrom ){
1142 fossil_fatal("cannot use --from or --to with --branch");
1143 }
1144 zTo = zBranch;
1145
+81 -55
--- src/info.c
+++ src/info.c
@@ -449,20 +449,23 @@
449449
if( verboseFlag==0 ){
450450
diffFlags = 0; /* Zero means do not show any diff */
451451
}else{
452452
int x;
453453
if( sideBySide ){
454
- diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS;
454
+ diffFlags = DIFF_SIDEBYSIDE;
455455
456456
/* "dw" query parameter determines width of each column */
457457
x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
458458
if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
459459
diffFlags += x;
460460
}else{
461
- diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS;
461
+ diffFlags = DIFF_INLINE;
462462
}
463463
464
+ if( P("w") ){
465
+ diffFlags |= (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS);
466
+ }
464467
/* "dc" query parameter determines lines of context */
465468
x = atoi(PD("dc","7"));
466469
if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
467470
diffFlags += x;
468471
@@ -469,11 +472,10 @@
469472
/* The "noopt" parameter disables diff optimization */
470473
if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
471474
}
472475
return diffFlags;
473476
}
474
-
475477
476478
/*
477479
** WEBPAGE: vinfo
478480
** WEBPAGE: ci
479481
** URL: /ci?name=RID|ARTIFACTID
@@ -664,50 +666,48 @@
664666
login_anonymous_available();
665667
}
666668
db_finalize(&q1);
667669
showTags(rid, "");
668670
if( zParent ){
671
+ const char *zW; /* URL param for ignoring whitespace */
672
+ const char *zPage = "vinfo"; /* Page that shows diffs */
673
+ const char *zPageHide = "ci"; /* Page that hides diffs */
669674
@ <div class="section">Changes</div>
670675
@ <div class="sectionmenu">
671676
verboseFlag = g.zPath[0]!='c';
672677
if( db_get_boolean("show-version-diffs", 0)==0 ){
673678
verboseFlag = !verboseFlag;
674
- if( verboseFlag ){
675
- @ %z(xhref("class='button'","%R/vinfo/%T",zName))
676
- @ hide&nbsp;diffs</a>
677
- if( sideBySide ){
678
- @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
679
- @ unified&nbsp;diffs</a>
680
- }else{
681
- @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
682
- @ side-by-side&nbsp;diffs</a>
683
- }
684
- }else{
685
- @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
686
- @ show&nbsp;unified&nbsp;diffs</a>
687
- @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
688
- @ show&nbsp;side-by-side&nbsp;diffs</a>
689
- }
690
- }else{
691
- if( verboseFlag ){
692
- @ %z(xhref("class='button'","%R/ci/%T",zName))hide&nbsp;diffs</a>
693
- if( sideBySide ){
694
- @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName))
695
- @ unified&nbsp;diffs</a>
696
- }else{
697
- @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName))
698
- @ side-by-side&nbsp;diffs</a>
699
- }
700
- }else{
701
- @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName))
702
- @ show&nbsp;unified&nbsp;diffs</a>
703
- @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName))
704
- @ show&nbsp;side-by-side&nbsp;diffs</a>
705
- }
679
+ zPage = "ci";
680
+ zPageHide = "vinfo";
681
+ }
682
+ diffFlags = construct_diff_flags(verboseFlag, sideBySide);
683
+ zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":"";
684
+ if( verboseFlag ){
685
+ @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
686
+ @ Hide&nbsp;Diffs</a>
687
+ if( sideBySide ){
688
+ @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))
689
+ @ Unified&nbsp;Diffs</a>
690
+ }else{
691
+ @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))
692
+ @ Side-by-Side&nbsp;Diffs</a>
693
+ }
694
+ if( *zW ){
695
+ @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
696
+ @ Show&nbsp;Whitespace&nbsp;Changes</a>
697
+ }else{
698
+ @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
699
+ @ Ignore&nbsp;Whitespace</a>
700
+ }
701
+ }else{
702
+ @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
703
+ @ Show&nbsp;Unified&nbsp;Diffs</a>
704
+ @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
705
+ @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
706706
}
707707
@ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
708
- @ patch</a></div>
708
+ @ Patch</a></div>
709709
if( pRe ){
710710
@ <p><b>Only differences that match regular expression "%h(zRe)"
711711
@ are shown.</b></p>
712712
}
713713
db_prepare(&q3,
@@ -721,11 +721,10 @@
721721
" AND (mlink.fid>0"
722722
" OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))"
723723
" ORDER BY name /*sort*/",
724724
rid, rid
725725
);
726
- diffFlags = construct_diff_flags(verboseFlag, sideBySide);
727726
while( db_step(&q3)==SQLITE_ROW ){
728727
const char *zName = db_column_text(&q3,0);
729728
int mperm = db_column_int(&q3, 1);
730729
const char *zOld = db_column_text(&q3,2);
731730
const char *zNew = db_column_text(&q3,3);
@@ -954,10 +953,11 @@
954953
ManifestFile *pFileFrom, *pFileTo;
955954
const char *zBranch;
956955
const char *zFrom;
957956
const char *zTo;
958957
const char *zRe;
958
+ const char *zW;
959959
const char *zVerbose;
960960
const char *zGlob;
961961
ReCompiled *pRe = 0;
962962
login_check_credentials();
963963
if( !g.perm.Read ){ login_needed(); return; }
@@ -987,39 +987,54 @@
987987
zFrom = P("from");
988988
zTo = P("to");
989989
if(zGlob && !*zGlob){
990990
zGlob = NULL;
991991
}
992
+ diffFlags = construct_diff_flags(verboseFlag, sideBySide);
993
+ zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":"";
992994
if( sideBySide || verboseFlag ){
993995
style_submenu_element("Hide Diff", "hidediff",
994
- "%R/vdiff?from=%T&to=%T&sbs=0%s%T",
996
+ "%R/vdiff?from=%T&to=%T&sbs=0%s%T%s",
995997
zFrom, zTo,
996
- zGlob ? "&glob=" : "", zGlob ? zGlob : "");
998
+ zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
997999
}
9981000
if( !sideBySide ){
999
- style_submenu_element("Side-by-side Diff", "sbsdiff",
1000
- "%R/vdiff?from=%T&to=%T&sbs=1%s%T",
1001
+ style_submenu_element("Side-by-Side Diff", "sbsdiff",
1002
+ "%R/vdiff?from=%T&to=%T&sbs=1%s%T%s",
10011003
zFrom, zTo,
1002
- zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1004
+ zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
10031005
}
10041006
if( sideBySide || !verboseFlag ) {
10051007
style_submenu_element("Unified Diff", "udiff",
1006
- "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T",
1008
+ "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T%s",
10071009
zFrom, zTo,
1008
- zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1010
+ zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
10091011
}
10101012
style_submenu_element("Invert", "invert",
1011
- "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T", zTo, zFrom,
1013
+ "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T%s", zTo, zFrom,
10121014
sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1013
- zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1015
+ zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
10141016
if( zGlob ){
10151017
style_submenu_element("Clear glob", "clearglob",
1016
- "%R/vdiff?from=%T&to=%T&sbs=%d%s", zFrom, zTo,
1017
- sideBySide, (verboseFlag && !sideBySide)?"&v":"");
1018
+ "%R/vdiff?from=%T&to=%T&sbs=%d%s%s", zFrom, zTo,
1019
+ sideBySide, (verboseFlag && !sideBySide)?"&v":"", zW);
10181020
}else{
10191021
style_submenu_element("Patch", "patch",
1020
- "%R/vpatch?from=%T&to=%T", zFrom, zTo);
1022
+ "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
1023
+ }
1024
+ if( sideBySide || verboseFlag ){
1025
+ if( *zW ){
1026
+ style_submenu_element("Show Whitespace Differences", "whitespace",
1027
+ "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T", zFrom, zTo,
1028
+ sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1029
+ zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1030
+ }else{
1031
+ style_submenu_element("Ignore Whitespace", "ignorews",
1032
+ "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T&w", zFrom, zTo,
1033
+ sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1034
+ zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1035
+ }
10211036
}
10221037
style_header("Check-in Differences");
10231038
@ <h2>Difference From:</h2><blockquote>
10241039
checkin_description(ridFrom);
10251040
@ </blockquote><h2>To:</h2><blockquote>
@@ -1036,11 +1051,10 @@
10361051
10371052
manifest_file_rewind(pFrom);
10381053
pFileFrom = manifest_file_next(pFrom, 0);
10391054
manifest_file_rewind(pTo);
10401055
pFileTo = manifest_file_next(pTo, 0);
1041
- diffFlags = construct_diff_flags(verboseFlag, sideBySide);
10421056
while( pFileFrom || pFileTo ){
10431057
int cmp;
10441058
if( pFileFrom==0 ){
10451059
cmp = +1;
10461060
}else if( pFileTo==0 ){
@@ -1337,10 +1351,11 @@
13371351
int isPatch;
13381352
int sideBySide;
13391353
char *zV1;
13401354
char *zV2;
13411355
const char *zRe;
1356
+ const char *zW; /* URL param for ignoring whitespace */
13421357
ReCompiled *pRe = 0;
13431358
u64 diffFlags;
13441359
13451360
login_check_credentials();
13461361
if( !g.perm.Read ){ login_needed(); return; }
@@ -1367,20 +1382,31 @@
13671382
zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
13681383
zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
13691384
diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML;
13701385
13711386
style_header("Diff");
1387
+ zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":"";
1388
+ if( *zW ){
1389
+ diffFlags |= (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS);
1390
+ style_submenu_element("Show Whitespace Changes", "Show Whitespace Changes",
1391
+ "%s/fdiff?v1=%T&v2=%T&sbs=%d",
1392
+ g.zTop, P("v1"), P("v2"), sideBySide);
1393
+ }else{
1394
+ style_submenu_element("Ignore Whitespace", "Ignore Whitespace",
1395
+ "%s/fdiff?v1=%T&v2=%T&sbs=%d&w",
1396
+ g.zTop, P("v1"), P("v2"), sideBySide);
1397
+ }
13721398
style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
13731399
g.zTop, P("v1"), P("v2"));
13741400
if( !sideBySide ){
1375
- style_submenu_element("Side-by-side Diff", "sbsdiff",
1376
- "%s/fdiff?v1=%T&v2=%T&sbs=1",
1377
- g.zTop, P("v1"), P("v2"));
1401
+ style_submenu_element("Side-by-Side Diff", "sbsdiff",
1402
+ "%s/fdiff?v1=%T&v2=%T&sbs=1%s",
1403
+ g.zTop, P("v1"), P("v2"), zW);
13781404
}else{
13791405
style_submenu_element("Unified Diff", "udiff",
1380
- "%s/fdiff?v1=%T&v2=%T&sbs=0",
1381
- g.zTop, P("v1"), P("v2"));
1406
+ "%s/fdiff?v1=%T&v2=%T&sbs=0%s",
1407
+ g.zTop, P("v1"), P("v2"), zW);
13821408
}
13831409
13841410
if( P("smhdr")!=0 ){
13851411
@ <h2>Differences From Artifact
13861412
@ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To
13871413
--- src/info.c
+++ src/info.c
@@ -449,20 +449,23 @@
449 if( verboseFlag==0 ){
450 diffFlags = 0; /* Zero means do not show any diff */
451 }else{
452 int x;
453 if( sideBySide ){
454 diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS;
455
456 /* "dw" query parameter determines width of each column */
457 x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
458 if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
459 diffFlags += x;
460 }else{
461 diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS;
462 }
463
 
 
 
464 /* "dc" query parameter determines lines of context */
465 x = atoi(PD("dc","7"));
466 if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
467 diffFlags += x;
468
@@ -469,11 +472,10 @@
469 /* The "noopt" parameter disables diff optimization */
470 if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
471 }
472 return diffFlags;
473 }
474
475
476 /*
477 ** WEBPAGE: vinfo
478 ** WEBPAGE: ci
479 ** URL: /ci?name=RID|ARTIFACTID
@@ -664,50 +666,48 @@
664 login_anonymous_available();
665 }
666 db_finalize(&q1);
667 showTags(rid, "");
668 if( zParent ){
 
 
 
669 @ <div class="section">Changes</div>
670 @ <div class="sectionmenu">
671 verboseFlag = g.zPath[0]!='c';
672 if( db_get_boolean("show-version-diffs", 0)==0 ){
673 verboseFlag = !verboseFlag;
674 if( verboseFlag ){
675 @ %z(xhref("class='button'","%R/vinfo/%T",zName))
676 @ hide&nbsp;diffs</a>
677 if( sideBySide ){
678 @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
679 @ unified&nbsp;diffs</a>
680 }else{
681 @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
682 @ side-by-side&nbsp;diffs</a>
683 }
684 }else{
685 @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName))
686 @ show&nbsp;unified&nbsp;diffs</a>
687 @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName))
688 @ show&nbsp;side-by-side&nbsp;diffs</a>
689 }
690 }else{
691 if( verboseFlag ){
692 @ %z(xhref("class='button'","%R/ci/%T",zName))hide&nbsp;diffs</a>
693 if( sideBySide ){
694 @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName))
695 @ unified&nbsp;diffs</a>
696 }else{
697 @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName))
698 @ side-by-side&nbsp;diffs</a>
699 }
700 }else{
701 @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName))
702 @ show&nbsp;unified&nbsp;diffs</a>
703 @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName))
704 @ show&nbsp;side-by-side&nbsp;diffs</a>
705 }
706 }
707 @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
708 @ patch</a></div>
709 if( pRe ){
710 @ <p><b>Only differences that match regular expression "%h(zRe)"
711 @ are shown.</b></p>
712 }
713 db_prepare(&q3,
@@ -721,11 +721,10 @@
721 " AND (mlink.fid>0"
722 " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))"
723 " ORDER BY name /*sort*/",
724 rid, rid
725 );
726 diffFlags = construct_diff_flags(verboseFlag, sideBySide);
727 while( db_step(&q3)==SQLITE_ROW ){
728 const char *zName = db_column_text(&q3,0);
729 int mperm = db_column_int(&q3, 1);
730 const char *zOld = db_column_text(&q3,2);
731 const char *zNew = db_column_text(&q3,3);
@@ -954,10 +953,11 @@
954 ManifestFile *pFileFrom, *pFileTo;
955 const char *zBranch;
956 const char *zFrom;
957 const char *zTo;
958 const char *zRe;
 
959 const char *zVerbose;
960 const char *zGlob;
961 ReCompiled *pRe = 0;
962 login_check_credentials();
963 if( !g.perm.Read ){ login_needed(); return; }
@@ -987,39 +987,54 @@
987 zFrom = P("from");
988 zTo = P("to");
989 if(zGlob && !*zGlob){
990 zGlob = NULL;
991 }
 
 
992 if( sideBySide || verboseFlag ){
993 style_submenu_element("Hide Diff", "hidediff",
994 "%R/vdiff?from=%T&to=%T&sbs=0%s%T",
995 zFrom, zTo,
996 zGlob ? "&glob=" : "", zGlob ? zGlob : "");
997 }
998 if( !sideBySide ){
999 style_submenu_element("Side-by-side Diff", "sbsdiff",
1000 "%R/vdiff?from=%T&to=%T&sbs=1%s%T",
1001 zFrom, zTo,
1002 zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1003 }
1004 if( sideBySide || !verboseFlag ) {
1005 style_submenu_element("Unified Diff", "udiff",
1006 "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T",
1007 zFrom, zTo,
1008 zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1009 }
1010 style_submenu_element("Invert", "invert",
1011 "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T", zTo, zFrom,
1012 sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1013 zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1014 if( zGlob ){
1015 style_submenu_element("Clear glob", "clearglob",
1016 "%R/vdiff?from=%T&to=%T&sbs=%d%s", zFrom, zTo,
1017 sideBySide, (verboseFlag && !sideBySide)?"&v":"");
1018 }else{
1019 style_submenu_element("Patch", "patch",
1020 "%R/vpatch?from=%T&to=%T", zFrom, zTo);
 
 
 
 
 
 
 
 
 
 
 
 
 
1021 }
1022 style_header("Check-in Differences");
1023 @ <h2>Difference From:</h2><blockquote>
1024 checkin_description(ridFrom);
1025 @ </blockquote><h2>To:</h2><blockquote>
@@ -1036,11 +1051,10 @@
1036
1037 manifest_file_rewind(pFrom);
1038 pFileFrom = manifest_file_next(pFrom, 0);
1039 manifest_file_rewind(pTo);
1040 pFileTo = manifest_file_next(pTo, 0);
1041 diffFlags = construct_diff_flags(verboseFlag, sideBySide);
1042 while( pFileFrom || pFileTo ){
1043 int cmp;
1044 if( pFileFrom==0 ){
1045 cmp = +1;
1046 }else if( pFileTo==0 ){
@@ -1337,10 +1351,11 @@
1337 int isPatch;
1338 int sideBySide;
1339 char *zV1;
1340 char *zV2;
1341 const char *zRe;
 
1342 ReCompiled *pRe = 0;
1343 u64 diffFlags;
1344
1345 login_check_credentials();
1346 if( !g.perm.Read ){ login_needed(); return; }
@@ -1367,20 +1382,31 @@
1367 zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
1368 zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
1369 diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML;
1370
1371 style_header("Diff");
 
 
 
 
 
 
 
 
 
 
 
1372 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
1373 g.zTop, P("v1"), P("v2"));
1374 if( !sideBySide ){
1375 style_submenu_element("Side-by-side Diff", "sbsdiff",
1376 "%s/fdiff?v1=%T&v2=%T&sbs=1",
1377 g.zTop, P("v1"), P("v2"));
1378 }else{
1379 style_submenu_element("Unified Diff", "udiff",
1380 "%s/fdiff?v1=%T&v2=%T&sbs=0",
1381 g.zTop, P("v1"), P("v2"));
1382 }
1383
1384 if( P("smhdr")!=0 ){
1385 @ <h2>Differences From Artifact
1386 @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To
1387
--- src/info.c
+++ src/info.c
@@ -449,20 +449,23 @@
449 if( verboseFlag==0 ){
450 diffFlags = 0; /* Zero means do not show any diff */
451 }else{
452 int x;
453 if( sideBySide ){
454 diffFlags = DIFF_SIDEBYSIDE;
455
456 /* "dw" query parameter determines width of each column */
457 x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1);
458 if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK;
459 diffFlags += x;
460 }else{
461 diffFlags = DIFF_INLINE;
462 }
463
464 if( P("w") ){
465 diffFlags |= (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS);
466 }
467 /* "dc" query parameter determines lines of context */
468 x = atoi(PD("dc","7"));
469 if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK;
470 diffFlags += x;
471
@@ -469,11 +472,10 @@
472 /* The "noopt" parameter disables diff optimization */
473 if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT;
474 }
475 return diffFlags;
476 }
 
477
478 /*
479 ** WEBPAGE: vinfo
480 ** WEBPAGE: ci
481 ** URL: /ci?name=RID|ARTIFACTID
@@ -664,50 +666,48 @@
666 login_anonymous_available();
667 }
668 db_finalize(&q1);
669 showTags(rid, "");
670 if( zParent ){
671 const char *zW; /* URL param for ignoring whitespace */
672 const char *zPage = "vinfo"; /* Page that shows diffs */
673 const char *zPageHide = "ci"; /* Page that hides diffs */
674 @ <div class="section">Changes</div>
675 @ <div class="sectionmenu">
676 verboseFlag = g.zPath[0]!='c';
677 if( db_get_boolean("show-version-diffs", 0)==0 ){
678 verboseFlag = !verboseFlag;
679 zPage = "ci";
680 zPageHide = "vinfo";
681 }
682 diffFlags = construct_diff_flags(verboseFlag, sideBySide);
683 zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":"";
684 if( verboseFlag ){
685 @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName))
686 @ Hide&nbsp;Diffs</a>
687 if( sideBySide ){
688 @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW))
689 @ Unified&nbsp;Diffs</a>
690 }else{
691 @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW))
692 @ Side-by-Side&nbsp;Diffs</a>
693 }
694 if( *zW ){
695 @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide))
696 @ Show&nbsp;Whitespace&nbsp;Changes</a>
697 }else{
698 @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide))
699 @ Ignore&nbsp;Whitespace</a>
700 }
701 }else{
702 @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName))
703 @ Show&nbsp;Unified&nbsp;Diffs</a>
704 @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName))
705 @ Show&nbsp;Side-by-Side&nbsp;Diffs</a>
 
 
 
 
 
706 }
707 @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid))
708 @ Patch</a></div>
709 if( pRe ){
710 @ <p><b>Only differences that match regular expression "%h(zRe)"
711 @ are shown.</b></p>
712 }
713 db_prepare(&q3,
@@ -721,11 +721,10 @@
721 " AND (mlink.fid>0"
722 " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))"
723 " ORDER BY name /*sort*/",
724 rid, rid
725 );
 
726 while( db_step(&q3)==SQLITE_ROW ){
727 const char *zName = db_column_text(&q3,0);
728 int mperm = db_column_int(&q3, 1);
729 const char *zOld = db_column_text(&q3,2);
730 const char *zNew = db_column_text(&q3,3);
@@ -954,10 +953,11 @@
953 ManifestFile *pFileFrom, *pFileTo;
954 const char *zBranch;
955 const char *zFrom;
956 const char *zTo;
957 const char *zRe;
958 const char *zW;
959 const char *zVerbose;
960 const char *zGlob;
961 ReCompiled *pRe = 0;
962 login_check_credentials();
963 if( !g.perm.Read ){ login_needed(); return; }
@@ -987,39 +987,54 @@
987 zFrom = P("from");
988 zTo = P("to");
989 if(zGlob && !*zGlob){
990 zGlob = NULL;
991 }
992 diffFlags = construct_diff_flags(verboseFlag, sideBySide);
993 zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":"";
994 if( sideBySide || verboseFlag ){
995 style_submenu_element("Hide Diff", "hidediff",
996 "%R/vdiff?from=%T&to=%T&sbs=0%s%T%s",
997 zFrom, zTo,
998 zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
999 }
1000 if( !sideBySide ){
1001 style_submenu_element("Side-by-Side Diff", "sbsdiff",
1002 "%R/vdiff?from=%T&to=%T&sbs=1%s%T%s",
1003 zFrom, zTo,
1004 zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
1005 }
1006 if( sideBySide || !verboseFlag ) {
1007 style_submenu_element("Unified Diff", "udiff",
1008 "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T%s",
1009 zFrom, zTo,
1010 zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
1011 }
1012 style_submenu_element("Invert", "invert",
1013 "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T%s", zTo, zFrom,
1014 sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1015 zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
1016 if( zGlob ){
1017 style_submenu_element("Clear glob", "clearglob",
1018 "%R/vdiff?from=%T&to=%T&sbs=%d%s%s", zFrom, zTo,
1019 sideBySide, (verboseFlag && !sideBySide)?"&v":"", zW);
1020 }else{
1021 style_submenu_element("Patch", "patch",
1022 "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
1023 }
1024 if( sideBySide || verboseFlag ){
1025 if( *zW ){
1026 style_submenu_element("Show Whitespace Differences", "whitespace",
1027 "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T", zFrom, zTo,
1028 sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1029 zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1030 }else{
1031 style_submenu_element("Ignore Whitespace", "ignorews",
1032 "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T&w", zFrom, zTo,
1033 sideBySide, (verboseFlag && !sideBySide)?"&v":"",
1034 zGlob ? "&glob=" : "", zGlob ? zGlob : "");
1035 }
1036 }
1037 style_header("Check-in Differences");
1038 @ <h2>Difference From:</h2><blockquote>
1039 checkin_description(ridFrom);
1040 @ </blockquote><h2>To:</h2><blockquote>
@@ -1036,11 +1051,10 @@
1051
1052 manifest_file_rewind(pFrom);
1053 pFileFrom = manifest_file_next(pFrom, 0);
1054 manifest_file_rewind(pTo);
1055 pFileTo = manifest_file_next(pTo, 0);
 
1056 while( pFileFrom || pFileTo ){
1057 int cmp;
1058 if( pFileFrom==0 ){
1059 cmp = +1;
1060 }else if( pFileTo==0 ){
@@ -1337,10 +1351,11 @@
1351 int isPatch;
1352 int sideBySide;
1353 char *zV1;
1354 char *zV2;
1355 const char *zRe;
1356 const char *zW; /* URL param for ignoring whitespace */
1357 ReCompiled *pRe = 0;
1358 u64 diffFlags;
1359
1360 login_check_credentials();
1361 if( !g.perm.Read ){ login_needed(); return; }
@@ -1367,20 +1382,31 @@
1382 zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
1383 zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
1384 diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML;
1385
1386 style_header("Diff");
1387 zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":"";
1388 if( *zW ){
1389 diffFlags |= (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS);
1390 style_submenu_element("Show Whitespace Changes", "Show Whitespace Changes",
1391 "%s/fdiff?v1=%T&v2=%T&sbs=%d",
1392 g.zTop, P("v1"), P("v2"), sideBySide);
1393 }else{
1394 style_submenu_element("Ignore Whitespace", "Ignore Whitespace",
1395 "%s/fdiff?v1=%T&v2=%T&sbs=%d&w",
1396 g.zTop, P("v1"), P("v2"), sideBySide);
1397 }
1398 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
1399 g.zTop, P("v1"), P("v2"));
1400 if( !sideBySide ){
1401 style_submenu_element("Side-by-Side Diff", "sbsdiff",
1402 "%s/fdiff?v1=%T&v2=%T&sbs=1%s",
1403 g.zTop, P("v1"), P("v2"), zW);
1404 }else{
1405 style_submenu_element("Unified Diff", "udiff",
1406 "%s/fdiff?v1=%T&v2=%T&sbs=0%s",
1407 g.zTop, P("v1"), P("v2"), zW);
1408 }
1409
1410 if( P("smhdr")!=0 ){
1411 @ <h2>Differences From Artifact
1412 @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To
1413
--- www/changes.wiki
+++ www/changes.wiki
@@ -11,10 +11,13 @@
1111
filter links.
1212
* The [/help/info | info command] now shows leaf status of the checkout.
1313
* Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
1414
* Add option --empty to the "[/help?cmd=open | fossil open]" command.
1515
* Enhanced [/help?cmd=/fileage|the fileage page] to support a glob parameter.
16
+ * Add --ignore-space-at-sol and --ignore-space-at-eol options to [/help?cmd=diff|fossil (g)diff],
17
+ [/help?cmd=stash|fossil stash diff]. The option -w activates both of them.
18
+ * Add button "Ignore Whitespace" to /ci, /vdiff and /fdiff UI pages.
1619
1720
<h2>Changes For Version 1.28 (2014-01-27)</h2>
1821
* Enhance [/help?cmd=/reports | /reports] to support event type filtering.
1922
* When cloning a repository, the user name passed via the URL (if any)
2023
is now used as the default local admin user's name.
2124
--- www/changes.wiki
+++ www/changes.wiki
@@ -11,10 +11,13 @@
11 filter links.
12 * The [/help/info | info command] now shows leaf status of the checkout.
13 * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
14 * Add option --empty to the "[/help?cmd=open | fossil open]" command.
15 * Enhanced [/help?cmd=/fileage|the fileage page] to support a glob parameter.
 
 
 
16
17 <h2>Changes For Version 1.28 (2014-01-27)</h2>
18 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
19 * When cloning a repository, the user name passed via the URL (if any)
20 is now used as the default local admin user's name.
21
--- www/changes.wiki
+++ www/changes.wiki
@@ -11,10 +11,13 @@
11 filter links.
12 * The [/help/info | info command] now shows leaf status of the checkout.
13 * Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
14 * Add option --empty to the "[/help?cmd=open | fossil open]" command.
15 * Enhanced [/help?cmd=/fileage|the fileage page] to support a glob parameter.
16 * Add --ignore-space-at-sol and --ignore-space-at-eol options to [/help?cmd=diff|fossil (g)diff],
17 [/help?cmd=stash|fossil stash diff]. The option -w activates both of them.
18 * Add button "Ignore Whitespace" to /ci, /vdiff and /fdiff UI pages.
19
20 <h2>Changes For Version 1.28 (2014-01-27)</h2>
21 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
22 * When cloning a repository, the user name passed via the URL (if any)
23 is now used as the default local admin user's name.
24

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button