Fossil SCM

Add --strip-trailing-cr option to [/help?cmd=diff|fossil (g)diff] and [/help?cmd=stash|fossil stash diff]. Make option -w|--ignore-all-space (for [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame], [/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff]) work the same as its "gnu diff" counterpart.

jan.nijtmans 2014-03-22 08:04 UTC trunk merge
Commit e753ce83102773d3b616ec2cdd09fcc2ff47d9e0
+76 -25
--- src/diff.c
+++ src/diff.c
@@ -113,10 +113,11 @@
113113
int nEditAlloc; /* Space allocated for aEdit[] */
114114
DLine *aFrom; /* File on left side of the diff */
115115
int nFrom; /* Number of lines in aFrom[] */
116116
DLine *aTo; /* File on right side of the diff */
117117
int nTo; /* Number of lines in aTo[] */
118
+ int (*same_fn)(const DLine *, const DLine *); /* Function to be used for comparing */
118119
};
119120
120121
/*
121122
** Return an array of DLine objects containing a pointer to the
122123
** start of each line and a hash of that line. The lower
@@ -175,16 +176,26 @@
175176
s = 0;
176177
if( diffFlags & DIFF_IGNORE_EOLWS ){
177178
while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
178179
}
179180
if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
181
+ int numws = 0;
180182
while( s<k && fossil_isspace(z[s]) ){ s++; }
183
+ for(h=0, x=s; x<k; x++){
184
+ if( fossil_isspace(z[x]) ){
185
+ ++numws;
186
+ }else{
187
+ h = h ^ (h<<2) ^ z[x];
188
+ }
189
+ }
190
+ k -= numws;
191
+ }else{
192
+ for(h=0, x=s; x<k; x++){
193
+ h = h ^ (h<<2) ^ z[x];
194
+ }
181195
}
182196
a[i].indent = s;
183
- for(h=0, x=s; x<k; x++){
184
- h = h ^ (h<<2) ^ z[x];
185
- }
186197
a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
187198
h2 = h % nLine;
188199
a[i].iNext = a[h2].iHash;
189200
a[h2].iHash = i+1;
190201
z += j+1;
@@ -196,13 +207,31 @@
196207
}
197208
198209
/*
199210
** Return true if two DLine elements are identical.
200211
*/
201
-static int same_dline(DLine *pA, DLine *pB){
202
- return pA->h==pB->h && memcmp(pA->z+pA->indent,pB->z+pB->indent,
203
- pA->h & LENGTH_MASK)==0;
212
+static int same_dline(const DLine *pA, const DLine *pB){
213
+ return pA->h==pB->h && memcmp(pA->z,pB->z, pA->h&LENGTH_MASK)==0;
214
+}
215
+
216
+/*
217
+** Return true if two DLine elements are identical, ignoring
218
+** all whitespace. The indent field of pA/pB already points
219
+** to the first non-space character in the string.
220
+*/
221
+
222
+static int same_dline_ignore_allws(const DLine *pA, const DLine *pB){
223
+ int a = pA->indent, b = pB->indent;
224
+ if( pA->h==pB->h ){
225
+ while( a<pA->n && b<pB->n ){
226
+ if( pA->z[a++] != pB->z[b++] ) return 0;
227
+ while( a<pA->n && fossil_isspace(pA->z[a])) ++a;
228
+ while( b<pB->n && fossil_isspace(pB->z[b])) ++b;
229
+ }
230
+ return pA->n-a == b<pB->n-b;
231
+ }
232
+ return 0;
204233
}
205234
206235
/*
207236
** Return true if the regular expression *pRe matches any of the
208237
** N dlines
@@ -1354,16 +1383,16 @@
13541383
int iSXb = iS1; /* Best match so far */
13551384
int iSYb = iS2; /* Best match so far */
13561385
13571386
for(i=iS1; i<iE1-mxLength; i++){
13581387
for(j=iS2; j<iE2-mxLength; j++){
1359
- if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue;
1360
- if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
1388
+ if( !p->same_fn(&p->aFrom[i], &p->aTo[j]) ) continue;
1389
+ if( mxLength && !p->same_fn(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
13611390
continue;
13621391
}
13631392
k = 1;
1364
- while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){
1393
+ while( i+k<iE1 && j+k<iE2 && p->same_fn(&p->aFrom[i+k],&p->aTo[j+k]) ){
13651394
k++;
13661395
}
13671396
if( k>mxLength ){
13681397
iSXb = i;
13691398
iSYb = j;
@@ -1425,11 +1454,11 @@
14251454
mid = (iE1 + iS1)/2;
14261455
for(i=iS1; i<iE1; i++){
14271456
int limit = 0;
14281457
j = p->aTo[p->aFrom[i].h % p->nTo].iHash;
14291458
while( j>0
1430
- && (j-1<iS2 || j>=iE2 || !same_dline(&p->aFrom[i], &p->aTo[j-1]))
1459
+ && (j-1<iS2 || j>=iE2 || !p->same_fn(&p->aFrom[i], &p->aTo[j-1]))
14311460
){
14321461
if( limit++ > 10 ){
14331462
j = 0;
14341463
break;
14351464
}
@@ -1442,19 +1471,19 @@
14421471
iSX = i;
14431472
iSY = j-1;
14441473
pA = &p->aFrom[iSX-1];
14451474
pB = &p->aTo[iSY-1];
14461475
n = minInt(iSX-iS1, iSY-iS2);
1447
- for(k=0; k<n && same_dline(pA,pB); k++, pA--, pB--){}
1476
+ for(k=0; k<n && p->same_fn(pA,pB); k++, pA--, pB--){}
14481477
iSX -= k;
14491478
iSY -= k;
14501479
iEX = i+1;
14511480
iEY = j;
14521481
pA = &p->aFrom[iEX];
14531482
pB = &p->aTo[iEY];
14541483
n = minInt(iE1-iEX, iE2-iEY);
1455
- for(k=0; k<n && same_dline(pA,pB); k++, pA++, pB++){}
1484
+ for(k=0; k<n && p->same_fn(pA,pB); k++, pA++, pB++){}
14561485
iEX += k;
14571486
iEY += k;
14581487
skew = (iSX-iS1) - (iSY-iS2);
14591488
if( skew<0 ) skew = -skew;
14601489
dist = (iSX+iEX)/2 - mid;
@@ -1591,16 +1620,16 @@
15911620
int mnE, iS, iE1, iE2;
15921621
15931622
/* Carve off the common header and footer */
15941623
iE1 = p->nFrom;
15951624
iE2 = p->nTo;
1596
- while( iE1>0 && iE2>0 && same_dline(&p->aFrom[iE1-1], &p->aTo[iE2-1]) ){
1625
+ while( iE1>0 && iE2>0 && p->same_fn(&p->aFrom[iE1-1], &p->aTo[iE2-1]) ){
15971626
iE1--;
15981627
iE2--;
15991628
}
16001629
mnE = iE1<iE2 ? iE1 : iE2;
1601
- for(iS=0; iS<mnE && same_dline(&p->aFrom[iS],&p->aTo[iS]); iS++){}
1630
+ for(iS=0; iS<mnE && p->same_fn(&p->aFrom[iS],&p->aTo[iS]); iS++){}
16021631
16031632
/* do the difference */
16041633
if( iS>0 ){
16051634
appendTriple(p, iS, 0, 0);
16061635
}
@@ -1665,11 +1694,11 @@
16651694
16661695
/* Shift insertions toward the beginning of the file */
16671696
while( cpy>0 && del==0 && ins>0 ){
16681697
DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of insert */
16691698
DLine *pBtm = &p->aTo[lnTo+ins-1]; /* Last line inserted */
1670
- if( same_dline(pTop, pBtm)==0 ) break;
1699
+ if( p->same_fn(pTop, pBtm)==0 ) break;
16711700
if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
16721701
lnFrom--;
16731702
lnTo--;
16741703
p->aEdit[r]--;
16751704
p->aEdit[r+3]++;
@@ -1678,11 +1707,11 @@
16781707
16791708
/* Shift insertions toward the end of the file */
16801709
while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){
16811710
DLine *pTop = &p->aTo[lnTo]; /* First line inserted */
16821711
DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */
1683
- if( same_dline(pTop, pBtm)==0 ) break;
1712
+ if( p->same_fn(pTop, pBtm)==0 ) break;
16841713
if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break;
16851714
lnFrom++;
16861715
lnTo++;
16871716
p->aEdit[r]++;
16881717
p->aEdit[r+3]--;
@@ -1691,11 +1720,11 @@
16911720
16921721
/* Shift deletions toward the beginning of the file */
16931722
while( cpy>0 && del>0 && ins==0 ){
16941723
DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of delete */
16951724
DLine *pBtm = &p->aFrom[lnFrom+del-1]; /* Last line deleted */
1696
- if( same_dline(pTop, pBtm)==0 ) break;
1725
+ if( p->same_fn(pTop, pBtm)==0 ) break;
16971726
if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
16981727
lnFrom--;
16991728
lnTo--;
17001729
p->aEdit[r]--;
17011730
p->aEdit[r+3]++;
@@ -1704,11 +1733,11 @@
17041733
17051734
/* Shift deletions toward the end of the file */
17061735
while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){
17071736
DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */
17081737
DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */
1709
- if( same_dline(pTop, pBtm)==0 ) break;
1738
+ if( p->same_fn(pTop, pBtm)==0 ) break;
17101739
if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break;
17111740
lnFrom++;
17121741
lnTo++;
17131742
p->aEdit[r]++;
17141743
p->aEdit[r+3]--;
@@ -1784,10 +1813,15 @@
17841813
blob_to_utf8_no_bom(pA_Blob, 0);
17851814
blob_to_utf8_no_bom(pB_Blob, 0);
17861815
17871816
/* Prepare the input files */
17881817
memset(&c, 0, sizeof(c));
1818
+ if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
1819
+ c.same_fn = same_dline_ignore_allws;
1820
+ }else{
1821
+ c.same_fn = same_dline;
1822
+ }
17891823
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
17901824
&c.nFrom, diffFlags);
17911825
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
17921826
&c.nTo, diffFlags);
17931827
if( c.aFrom==0 || c.aTo==0 ){
@@ -1854,20 +1888,30 @@
18541888
** -c|--context N N lines of context. DIFF_CONTEXT_MASK
18551889
** --html Format for HTML DIFF_HTML
18561890
** --invert Invert the diff DIFF_INVERT
18571891
** -n|--linenum Show line numbers DIFF_LINENO
18581892
** --noopt Disable optimization DIFF_NOOPT
1893
+** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
18591894
** --unified Unified diff. ~DIFF_SIDEBYSIDE
18601895
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
1861
-** --width|-W N N character lines. DIFF_WIDTH_MASK
1896
+** -W|--width N N character lines. DIFF_WIDTH_MASK
18621897
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
18631898
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
18641899
*/
18651900
u64 diff_options(void){
18661901
u64 diffFlags = 0;
18671902
const char *z;
18681903
int f;
1904
+ if( find_option("ignore-trailing-space","Z",0)!=0 ){
1905
+ diffFlags = DIFF_IGNORE_EOLWS;
1906
+ }
1907
+ if( find_option("ignore-all-space","w",0)!=0 ){
1908
+ diffFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
1909
+ }
1910
+ if( find_option("strip-trailing-cr",0,0)!=0 ){
1911
+ diffFlags |= DIFF_STRIP_EOLCR;
1912
+ }
18691913
if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
18701914
if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
18711915
if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
18721916
if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
18731917
diffFlags |= f + DIFF_CONTEXT_EX;
@@ -1876,12 +1920,10 @@
18761920
f *= DIFF_CONTEXT_MASK+1;
18771921
if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
18781922
diffFlags |= f;
18791923
}
18801924
if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
1881
- if( find_option("ignore-trailing-space","Z",0)!=0 ) diffFlags |= DIFF_IGNORE_EOLWS;
1882
- if( find_option("ignore-all-space","w",0)!=0 ) diffFlags |= DIFF_IGNORE_ALLWS;
18831925
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
18841926
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
18851927
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
18861928
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
18871929
return diffFlags;
@@ -1984,10 +2026,15 @@
19842026
*/
19852027
static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){
19862028
int i;
19872029
19882030
memset(p, 0, sizeof(*p));
2031
+ if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
2032
+ p->c.same_fn = same_dline_ignore_allws;
2033
+ }else{
2034
+ p->c.same_fn = same_dline;
2035
+ }
19892036
p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
19902037
diffFlags);
19912038
if( p->c.aTo==0 ){
19922039
return 1;
19932040
}
@@ -2353,12 +2400,12 @@
23532400
**
23542401
** Options:
23552402
** --filevers Show file version numbers rather than check-in versions
23562403
** -l|--log List all versions analyzed
23572404
** -n|--limit N Only look backwards in time by N versions
2358
-** -Z|--ignore-trailing-space Ignore eol-whitespaces
2359
-** -w|--ignore-all-space Ignore all whitespaces
2405
+** -w|--ignore-all-space Ignore white space when comparing lines
2406
+** -Z|--ignore-trailing-space Ignore whitespace at line end
23602407
**
23612408
** See also: info, finfo, timeline
23622409
*/
23632410
void annotate_cmd(void){
23642411
int fnid; /* Filename ID */
@@ -2379,12 +2426,16 @@
23792426
bBlame = g.argv[1][0]!='a';
23802427
zLimit = find_option("limit","n",1);
23812428
if( zLimit==0 || zLimit[0]==0 ) zLimit = "-1";
23822429
iLimit = atoi(zLimit);
23832430
showLog = find_option("log","l",0)!=0;
2384
- if( find_option("ignore-trailing-space","Z",0)!=0 ) annFlags |= DIFF_IGNORE_EOLWS;
2385
- if( find_option("ignore-all-space","w",0)!=0 ) annFlags |= DIFF_IGNORE_ALLWS;
2431
+ if( find_option("ignore-trailing-space","Z",0)!=0 ){
2432
+ annFlags = DIFF_IGNORE_EOLWS;
2433
+ }
2434
+ if( find_option("ignore-all-space","w",0)!=0 ){
2435
+ annFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
2436
+ }
23862437
fileVers = find_option("filevers",0,0)!=0;
23872438
db_must_be_within_tree();
23882439
if( g.argc<3 ) {
23892440
usage("FILENAME");
23902441
}
23912442
--- src/diff.c
+++ src/diff.c
@@ -113,10 +113,11 @@
113 int nEditAlloc; /* Space allocated for aEdit[] */
114 DLine *aFrom; /* File on left side of the diff */
115 int nFrom; /* Number of lines in aFrom[] */
116 DLine *aTo; /* File on right side of the diff */
117 int nTo; /* Number of lines in aTo[] */
 
118 };
119
120 /*
121 ** Return an array of DLine objects containing a pointer to the
122 ** start of each line and a hash of that line. The lower
@@ -175,16 +176,26 @@
175 s = 0;
176 if( diffFlags & DIFF_IGNORE_EOLWS ){
177 while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
178 }
179 if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
 
180 while( s<k && fossil_isspace(z[s]) ){ s++; }
 
 
 
 
 
 
 
 
 
 
 
 
181 }
182 a[i].indent = s;
183 for(h=0, x=s; x<k; x++){
184 h = h ^ (h<<2) ^ z[x];
185 }
186 a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
187 h2 = h % nLine;
188 a[i].iNext = a[h2].iHash;
189 a[h2].iHash = i+1;
190 z += j+1;
@@ -196,13 +207,31 @@
196 }
197
198 /*
199 ** Return true if two DLine elements are identical.
200 */
201 static int same_dline(DLine *pA, DLine *pB){
202 return pA->h==pB->h && memcmp(pA->z+pA->indent,pB->z+pB->indent,
203 pA->h & LENGTH_MASK)==0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204 }
205
206 /*
207 ** Return true if the regular expression *pRe matches any of the
208 ** N dlines
@@ -1354,16 +1383,16 @@
1354 int iSXb = iS1; /* Best match so far */
1355 int iSYb = iS2; /* Best match so far */
1356
1357 for(i=iS1; i<iE1-mxLength; i++){
1358 for(j=iS2; j<iE2-mxLength; j++){
1359 if( !same_dline(&p->aFrom[i], &p->aTo[j]) ) continue;
1360 if( mxLength && !same_dline(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
1361 continue;
1362 }
1363 k = 1;
1364 while( i+k<iE1 && j+k<iE2 && same_dline(&p->aFrom[i+k],&p->aTo[j+k]) ){
1365 k++;
1366 }
1367 if( k>mxLength ){
1368 iSXb = i;
1369 iSYb = j;
@@ -1425,11 +1454,11 @@
1425 mid = (iE1 + iS1)/2;
1426 for(i=iS1; i<iE1; i++){
1427 int limit = 0;
1428 j = p->aTo[p->aFrom[i].h % p->nTo].iHash;
1429 while( j>0
1430 && (j-1<iS2 || j>=iE2 || !same_dline(&p->aFrom[i], &p->aTo[j-1]))
1431 ){
1432 if( limit++ > 10 ){
1433 j = 0;
1434 break;
1435 }
@@ -1442,19 +1471,19 @@
1442 iSX = i;
1443 iSY = j-1;
1444 pA = &p->aFrom[iSX-1];
1445 pB = &p->aTo[iSY-1];
1446 n = minInt(iSX-iS1, iSY-iS2);
1447 for(k=0; k<n && same_dline(pA,pB); k++, pA--, pB--){}
1448 iSX -= k;
1449 iSY -= k;
1450 iEX = i+1;
1451 iEY = j;
1452 pA = &p->aFrom[iEX];
1453 pB = &p->aTo[iEY];
1454 n = minInt(iE1-iEX, iE2-iEY);
1455 for(k=0; k<n && same_dline(pA,pB); k++, pA++, pB++){}
1456 iEX += k;
1457 iEY += k;
1458 skew = (iSX-iS1) - (iSY-iS2);
1459 if( skew<0 ) skew = -skew;
1460 dist = (iSX+iEX)/2 - mid;
@@ -1591,16 +1620,16 @@
1591 int mnE, iS, iE1, iE2;
1592
1593 /* Carve off the common header and footer */
1594 iE1 = p->nFrom;
1595 iE2 = p->nTo;
1596 while( iE1>0 && iE2>0 && same_dline(&p->aFrom[iE1-1], &p->aTo[iE2-1]) ){
1597 iE1--;
1598 iE2--;
1599 }
1600 mnE = iE1<iE2 ? iE1 : iE2;
1601 for(iS=0; iS<mnE && same_dline(&p->aFrom[iS],&p->aTo[iS]); iS++){}
1602
1603 /* do the difference */
1604 if( iS>0 ){
1605 appendTriple(p, iS, 0, 0);
1606 }
@@ -1665,11 +1694,11 @@
1665
1666 /* Shift insertions toward the beginning of the file */
1667 while( cpy>0 && del==0 && ins>0 ){
1668 DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of insert */
1669 DLine *pBtm = &p->aTo[lnTo+ins-1]; /* Last line inserted */
1670 if( same_dline(pTop, pBtm)==0 ) break;
1671 if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
1672 lnFrom--;
1673 lnTo--;
1674 p->aEdit[r]--;
1675 p->aEdit[r+3]++;
@@ -1678,11 +1707,11 @@
1678
1679 /* Shift insertions toward the end of the file */
1680 while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){
1681 DLine *pTop = &p->aTo[lnTo]; /* First line inserted */
1682 DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */
1683 if( same_dline(pTop, pBtm)==0 ) break;
1684 if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break;
1685 lnFrom++;
1686 lnTo++;
1687 p->aEdit[r]++;
1688 p->aEdit[r+3]--;
@@ -1691,11 +1720,11 @@
1691
1692 /* Shift deletions toward the beginning of the file */
1693 while( cpy>0 && del>0 && ins==0 ){
1694 DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of delete */
1695 DLine *pBtm = &p->aFrom[lnFrom+del-1]; /* Last line deleted */
1696 if( same_dline(pTop, pBtm)==0 ) break;
1697 if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
1698 lnFrom--;
1699 lnTo--;
1700 p->aEdit[r]--;
1701 p->aEdit[r+3]++;
@@ -1704,11 +1733,11 @@
1704
1705 /* Shift deletions toward the end of the file */
1706 while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){
1707 DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */
1708 DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */
1709 if( same_dline(pTop, pBtm)==0 ) break;
1710 if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break;
1711 lnFrom++;
1712 lnTo++;
1713 p->aEdit[r]++;
1714 p->aEdit[r+3]--;
@@ -1784,10 +1813,15 @@
1784 blob_to_utf8_no_bom(pA_Blob, 0);
1785 blob_to_utf8_no_bom(pB_Blob, 0);
1786
1787 /* Prepare the input files */
1788 memset(&c, 0, sizeof(c));
 
 
 
 
 
1789 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1790 &c.nFrom, diffFlags);
1791 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1792 &c.nTo, diffFlags);
1793 if( c.aFrom==0 || c.aTo==0 ){
@@ -1854,20 +1888,30 @@
1854 ** -c|--context N N lines of context. DIFF_CONTEXT_MASK
1855 ** --html Format for HTML DIFF_HTML
1856 ** --invert Invert the diff DIFF_INVERT
1857 ** -n|--linenum Show line numbers DIFF_LINENO
1858 ** --noopt Disable optimization DIFF_NOOPT
 
1859 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
1860 ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
1861 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1862 ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
1863 ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
1864 */
1865 u64 diff_options(void){
1866 u64 diffFlags = 0;
1867 const char *z;
1868 int f;
 
 
 
 
 
 
 
 
 
1869 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
1870 if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
1871 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
1872 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1873 diffFlags |= f + DIFF_CONTEXT_EX;
@@ -1876,12 +1920,10 @@
1876 f *= DIFF_CONTEXT_MASK+1;
1877 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1878 diffFlags |= f;
1879 }
1880 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
1881 if( find_option("ignore-trailing-space","Z",0)!=0 ) diffFlags |= DIFF_IGNORE_EOLWS;
1882 if( find_option("ignore-all-space","w",0)!=0 ) diffFlags |= DIFF_IGNORE_ALLWS;
1883 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
1884 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
1885 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
1886 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
1887 return diffFlags;
@@ -1984,10 +2026,15 @@
1984 */
1985 static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){
1986 int i;
1987
1988 memset(p, 0, sizeof(*p));
 
 
 
 
 
1989 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
1990 diffFlags);
1991 if( p->c.aTo==0 ){
1992 return 1;
1993 }
@@ -2353,12 +2400,12 @@
2353 **
2354 ** Options:
2355 ** --filevers Show file version numbers rather than check-in versions
2356 ** -l|--log List all versions analyzed
2357 ** -n|--limit N Only look backwards in time by N versions
2358 ** -Z|--ignore-trailing-space Ignore eol-whitespaces
2359 ** -w|--ignore-all-space Ignore all whitespaces
2360 **
2361 ** See also: info, finfo, timeline
2362 */
2363 void annotate_cmd(void){
2364 int fnid; /* Filename ID */
@@ -2379,12 +2426,16 @@
2379 bBlame = g.argv[1][0]!='a';
2380 zLimit = find_option("limit","n",1);
2381 if( zLimit==0 || zLimit[0]==0 ) zLimit = "-1";
2382 iLimit = atoi(zLimit);
2383 showLog = find_option("log","l",0)!=0;
2384 if( find_option("ignore-trailing-space","Z",0)!=0 ) annFlags |= DIFF_IGNORE_EOLWS;
2385 if( find_option("ignore-all-space","w",0)!=0 ) annFlags |= DIFF_IGNORE_ALLWS;
 
 
 
 
2386 fileVers = find_option("filevers",0,0)!=0;
2387 db_must_be_within_tree();
2388 if( g.argc<3 ) {
2389 usage("FILENAME");
2390 }
2391
--- src/diff.c
+++ src/diff.c
@@ -113,10 +113,11 @@
113 int nEditAlloc; /* Space allocated for aEdit[] */
114 DLine *aFrom; /* File on left side of the diff */
115 int nFrom; /* Number of lines in aFrom[] */
116 DLine *aTo; /* File on right side of the diff */
117 int nTo; /* Number of lines in aTo[] */
118 int (*same_fn)(const DLine *, const DLine *); /* Function to be used for comparing */
119 };
120
121 /*
122 ** Return an array of DLine objects containing a pointer to the
123 ** start of each line and a hash of that line. The lower
@@ -175,16 +176,26 @@
176 s = 0;
177 if( diffFlags & DIFF_IGNORE_EOLWS ){
178 while( k>0 && fossil_isspace(z[k-1]) ){ k--; }
179 }
180 if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
181 int numws = 0;
182 while( s<k && fossil_isspace(z[s]) ){ s++; }
183 for(h=0, x=s; x<k; x++){
184 if( fossil_isspace(z[x]) ){
185 ++numws;
186 }else{
187 h = h ^ (h<<2) ^ z[x];
188 }
189 }
190 k -= numws;
191 }else{
192 for(h=0, x=s; x<k; x++){
193 h = h ^ (h<<2) ^ z[x];
194 }
195 }
196 a[i].indent = s;
 
 
 
197 a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s);
198 h2 = h % nLine;
199 a[i].iNext = a[h2].iHash;
200 a[h2].iHash = i+1;
201 z += j+1;
@@ -196,13 +207,31 @@
207 }
208
209 /*
210 ** Return true if two DLine elements are identical.
211 */
212 static int same_dline(const DLine *pA, const DLine *pB){
213 return pA->h==pB->h && memcmp(pA->z,pB->z, pA->h&LENGTH_MASK)==0;
214 }
215
216 /*
217 ** Return true if two DLine elements are identical, ignoring
218 ** all whitespace. The indent field of pA/pB already points
219 ** to the first non-space character in the string.
220 */
221
222 static int same_dline_ignore_allws(const DLine *pA, const DLine *pB){
223 int a = pA->indent, b = pB->indent;
224 if( pA->h==pB->h ){
225 while( a<pA->n && b<pB->n ){
226 if( pA->z[a++] != pB->z[b++] ) return 0;
227 while( a<pA->n && fossil_isspace(pA->z[a])) ++a;
228 while( b<pB->n && fossil_isspace(pB->z[b])) ++b;
229 }
230 return pA->n-a == b<pB->n-b;
231 }
232 return 0;
233 }
234
235 /*
236 ** Return true if the regular expression *pRe matches any of the
237 ** N dlines
@@ -1354,16 +1383,16 @@
1383 int iSXb = iS1; /* Best match so far */
1384 int iSYb = iS2; /* Best match so far */
1385
1386 for(i=iS1; i<iE1-mxLength; i++){
1387 for(j=iS2; j<iE2-mxLength; j++){
1388 if( !p->same_fn(&p->aFrom[i], &p->aTo[j]) ) continue;
1389 if( mxLength && !p->same_fn(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){
1390 continue;
1391 }
1392 k = 1;
1393 while( i+k<iE1 && j+k<iE2 && p->same_fn(&p->aFrom[i+k],&p->aTo[j+k]) ){
1394 k++;
1395 }
1396 if( k>mxLength ){
1397 iSXb = i;
1398 iSYb = j;
@@ -1425,11 +1454,11 @@
1454 mid = (iE1 + iS1)/2;
1455 for(i=iS1; i<iE1; i++){
1456 int limit = 0;
1457 j = p->aTo[p->aFrom[i].h % p->nTo].iHash;
1458 while( j>0
1459 && (j-1<iS2 || j>=iE2 || !p->same_fn(&p->aFrom[i], &p->aTo[j-1]))
1460 ){
1461 if( limit++ > 10 ){
1462 j = 0;
1463 break;
1464 }
@@ -1442,19 +1471,19 @@
1471 iSX = i;
1472 iSY = j-1;
1473 pA = &p->aFrom[iSX-1];
1474 pB = &p->aTo[iSY-1];
1475 n = minInt(iSX-iS1, iSY-iS2);
1476 for(k=0; k<n && p->same_fn(pA,pB); k++, pA--, pB--){}
1477 iSX -= k;
1478 iSY -= k;
1479 iEX = i+1;
1480 iEY = j;
1481 pA = &p->aFrom[iEX];
1482 pB = &p->aTo[iEY];
1483 n = minInt(iE1-iEX, iE2-iEY);
1484 for(k=0; k<n && p->same_fn(pA,pB); k++, pA++, pB++){}
1485 iEX += k;
1486 iEY += k;
1487 skew = (iSX-iS1) - (iSY-iS2);
1488 if( skew<0 ) skew = -skew;
1489 dist = (iSX+iEX)/2 - mid;
@@ -1591,16 +1620,16 @@
1620 int mnE, iS, iE1, iE2;
1621
1622 /* Carve off the common header and footer */
1623 iE1 = p->nFrom;
1624 iE2 = p->nTo;
1625 while( iE1>0 && iE2>0 && p->same_fn(&p->aFrom[iE1-1], &p->aTo[iE2-1]) ){
1626 iE1--;
1627 iE2--;
1628 }
1629 mnE = iE1<iE2 ? iE1 : iE2;
1630 for(iS=0; iS<mnE && p->same_fn(&p->aFrom[iS],&p->aTo[iS]); iS++){}
1631
1632 /* do the difference */
1633 if( iS>0 ){
1634 appendTriple(p, iS, 0, 0);
1635 }
@@ -1665,11 +1694,11 @@
1694
1695 /* Shift insertions toward the beginning of the file */
1696 while( cpy>0 && del==0 && ins>0 ){
1697 DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of insert */
1698 DLine *pBtm = &p->aTo[lnTo+ins-1]; /* Last line inserted */
1699 if( p->same_fn(pTop, pBtm)==0 ) break;
1700 if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
1701 lnFrom--;
1702 lnTo--;
1703 p->aEdit[r]--;
1704 p->aEdit[r+3]++;
@@ -1678,11 +1707,11 @@
1707
1708 /* Shift insertions toward the end of the file */
1709 while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){
1710 DLine *pTop = &p->aTo[lnTo]; /* First line inserted */
1711 DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */
1712 if( p->same_fn(pTop, pBtm)==0 ) break;
1713 if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break;
1714 lnFrom++;
1715 lnTo++;
1716 p->aEdit[r]++;
1717 p->aEdit[r+3]--;
@@ -1691,11 +1720,11 @@
1720
1721 /* Shift deletions toward the beginning of the file */
1722 while( cpy>0 && del>0 && ins==0 ){
1723 DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of delete */
1724 DLine *pBtm = &p->aFrom[lnFrom+del-1]; /* Last line deleted */
1725 if( p->same_fn(pTop, pBtm)==0 ) break;
1726 if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break;
1727 lnFrom--;
1728 lnTo--;
1729 p->aEdit[r]--;
1730 p->aEdit[r+3]++;
@@ -1704,11 +1733,11 @@
1733
1734 /* Shift deletions toward the end of the file */
1735 while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){
1736 DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */
1737 DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */
1738 if( p->same_fn(pTop, pBtm)==0 ) break;
1739 if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break;
1740 lnFrom++;
1741 lnTo++;
1742 p->aEdit[r]++;
1743 p->aEdit[r+3]--;
@@ -1784,10 +1813,15 @@
1813 blob_to_utf8_no_bom(pA_Blob, 0);
1814 blob_to_utf8_no_bom(pB_Blob, 0);
1815
1816 /* Prepare the input files */
1817 memset(&c, 0, sizeof(c));
1818 if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
1819 c.same_fn = same_dline_ignore_allws;
1820 }else{
1821 c.same_fn = same_dline;
1822 }
1823 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1824 &c.nFrom, diffFlags);
1825 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1826 &c.nTo, diffFlags);
1827 if( c.aFrom==0 || c.aTo==0 ){
@@ -1854,20 +1888,30 @@
1888 ** -c|--context N N lines of context. DIFF_CONTEXT_MASK
1889 ** --html Format for HTML DIFF_HTML
1890 ** --invert Invert the diff DIFF_INVERT
1891 ** -n|--linenum Show line numbers DIFF_LINENO
1892 ** --noopt Disable optimization DIFF_NOOPT
1893 ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
1894 ** --unified Unified diff. ~DIFF_SIDEBYSIDE
1895 ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
1896 ** -W|--width N N character lines. DIFF_WIDTH_MASK
1897 ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
1898 ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
1899 */
1900 u64 diff_options(void){
1901 u64 diffFlags = 0;
1902 const char *z;
1903 int f;
1904 if( find_option("ignore-trailing-space","Z",0)!=0 ){
1905 diffFlags = DIFF_IGNORE_EOLWS;
1906 }
1907 if( find_option("ignore-all-space","w",0)!=0 ){
1908 diffFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
1909 }
1910 if( find_option("strip-trailing-cr",0,0)!=0 ){
1911 diffFlags |= DIFF_STRIP_EOLCR;
1912 }
1913 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
1914 if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE;
1915 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){
1916 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1917 diffFlags |= f + DIFF_CONTEXT_EX;
@@ -1876,12 +1920,10 @@
1920 f *= DIFF_CONTEXT_MASK+1;
1921 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1922 diffFlags |= f;
1923 }
1924 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML;
 
 
1925 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
1926 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
1927 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
1928 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
1929 return diffFlags;
@@ -1984,10 +2026,15 @@
2026 */
2027 static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){
2028 int i;
2029
2030 memset(p, 0, sizeof(*p));
2031 if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
2032 p->c.same_fn = same_dline_ignore_allws;
2033 }else{
2034 p->c.same_fn = same_dline;
2035 }
2036 p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo,
2037 diffFlags);
2038 if( p->c.aTo==0 ){
2039 return 1;
2040 }
@@ -2353,12 +2400,12 @@
2400 **
2401 ** Options:
2402 ** --filevers Show file version numbers rather than check-in versions
2403 ** -l|--log List all versions analyzed
2404 ** -n|--limit N Only look backwards in time by N versions
2405 ** -w|--ignore-all-space Ignore white space when comparing lines
2406 ** -Z|--ignore-trailing-space Ignore whitespace at line end
2407 **
2408 ** See also: info, finfo, timeline
2409 */
2410 void annotate_cmd(void){
2411 int fnid; /* Filename ID */
@@ -2379,12 +2426,16 @@
2426 bBlame = g.argv[1][0]!='a';
2427 zLimit = find_option("limit","n",1);
2428 if( zLimit==0 || zLimit[0]==0 ) zLimit = "-1";
2429 iLimit = atoi(zLimit);
2430 showLog = find_option("log","l",0)!=0;
2431 if( find_option("ignore-trailing-space","Z",0)!=0 ){
2432 annFlags = DIFF_IGNORE_EOLWS;
2433 }
2434 if( find_option("ignore-all-space","w",0)!=0 ){
2435 annFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
2436 }
2437 fileVers = find_option("filevers",0,0)!=0;
2438 db_must_be_within_tree();
2439 if( g.argc<3 ) {
2440 usage("FILENAME");
2441 }
2442
+3 -3
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1097,17 +1097,17 @@
10971097
** --context|-c N Use N lines of context
10981098
** --diff-binary BOOL Include binary files when using external commands
10991099
** --from|-r VERSION select VERSION as source for the diff
11001100
** --internal|-i use internal diff logic
11011101
** --side-by-side|-y side-by-side diff
1102
+** --strip-trailing-cr Strip trailing CR
11021103
** --tk Launch a Tcl/Tk GUI for display
11031104
** --to VERSION select VERSION as target for the diff
11041105
** --unified unified diff
11051106
** -v|--verbose output complete text of added or deleted files
1106
-** -w|--ignore-all-space Ignore changes to start-of-line and end-of-line
1107
-** whitespace
1108
-** -W|--width Width of lines in side-by-side diff
1107
+** -w|--ignore-all-space Ignore white space when comparing lines
1108
+** -W|--width <num> Width of lines in side-by-side diff
11091109
** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
11101110
*/
11111111
void diff_cmd(void){
11121112
int isGDiff; /* True for gdiff. False for normal diff */
11131113
int isInternDiff; /* True for internal diff */
11141114
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1097,17 +1097,17 @@
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 ** --internal|-i use internal diff logic
1101 ** --side-by-side|-y side-by-side diff
 
1102 ** --tk Launch a Tcl/Tk GUI for display
1103 ** --to VERSION select VERSION as target for the diff
1104 ** --unified unified diff
1105 ** -v|--verbose output complete text of added or deleted files
1106 ** -w|--ignore-all-space Ignore changes to start-of-line and end-of-line
1107 ** whitespace
1108 ** -W|--width Width of lines in side-by-side diff
1109 ** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
1110 */
1111 void diff_cmd(void){
1112 int isGDiff; /* True for gdiff. False for normal diff */
1113 int isInternDiff; /* True for internal diff */
1114
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -1097,17 +1097,17 @@
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 ** --internal|-i use internal diff logic
1101 ** --side-by-side|-y side-by-side diff
1102 ** --strip-trailing-cr Strip trailing CR
1103 ** --tk Launch a Tcl/Tk GUI for display
1104 ** --to VERSION select VERSION as target for the diff
1105 ** --unified unified diff
1106 ** -v|--verbose output complete text of added or deleted files
1107 ** -w|--ignore-all-space Ignore white space when comparing lines
1108 ** -W|--width <num> Width of lines in side-by-side diff
 
1109 ** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
1110 */
1111 void diff_cmd(void){
1112 int isGDiff; /* True for gdiff. False for normal diff */
1113 int isInternDiff; /* True for internal diff */
1114
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
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.
1616
* Add -w|--ignore-all-space and -Z|--ignore-trailing-space options to
1717
[/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame],
1818
[/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff].
19
+ * Add --strip-trailing-cr option to [/help?cmd=diff|fossil (g)diff] and
20
+ [/help?cmd=stash|fossil stash diff].
1921
* Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
2022
and /vdiff UI pages.
2123
2224
<h2>Changes For Version 1.28 (2014-01-27)</h2>
2325
* Enhance [/help?cmd=/reports | /reports] to support event type filtering.
2426
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
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 -w|--ignore-all-space and -Z|--ignore-trailing-space options to
17 [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame],
18 [/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff].
 
 
19 * Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
20 and /vdiff UI pages.
21
22 <h2>Changes For Version 1.28 (2014-01-27)</h2>
23 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
24
--- www/changes.wiki
+++ www/changes.wiki
@@ -14,10 +14,12 @@
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 -w|--ignore-all-space and -Z|--ignore-trailing-space options to
17 [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame],
18 [/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff].
19 * Add --strip-trailing-cr option to [/help?cmd=diff|fossil (g)diff] and
20 [/help?cmd=stash|fossil stash diff].
21 * Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
22 and /vdiff UI pages.
23
24 <h2>Changes For Version 1.28 (2014-01-27)</h2>
25 * Enhance [/help?cmd=/reports | /reports] to support event type filtering.
26

Keyboard Shortcuts

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