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.
Commit
e663d5e33099e21ce5f7aa752f2b6177e0515d5b
Parent
f0773f6370059a2…
5 files changed
+83
-35
+83
-35
+30
-17
+81
-55
+3
+83
-35
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -28,18 +28,18 @@ | ||
| 28 | 28 | ** Flag parameters to the text_diff() routine used to control the formatting |
| 29 | 29 | ** of the diff output. |
| 30 | 30 | */ |
| 31 | 31 | #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */ |
| 32 | 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 */ | |
| 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 | 41 | #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ |
| 42 | 42 | #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ |
| 43 | 43 | #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */ |
| 44 | 44 | #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */ |
| 45 | 45 | |
| @@ -54,10 +54,13 @@ | ||
| 54 | 54 | "cannot compute difference between symlink and regular file\n" |
| 55 | 55 | |
| 56 | 56 | #define DIFF_TOO_MANY_CHANGES \ |
| 57 | 57 | "more than 10,000 changes\n" |
| 58 | 58 | |
| 59 | +#define DIFF_WHITESPACE_ONLY \ | |
| 60 | + "whitespace changes only\n" | |
| 61 | + | |
| 59 | 62 | /* |
| 60 | 63 | ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes) |
| 61 | 64 | */ |
| 62 | 65 | #define LENGTH_MASK_SZ 13 |
| 63 | 66 | #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) |
| @@ -73,10 +76,11 @@ | ||
| 73 | 76 | */ |
| 74 | 77 | typedef struct DLine DLine; |
| 75 | 78 | struct DLine { |
| 76 | 79 | const char *z; /* The text of the line */ |
| 77 | 80 | unsigned int h; /* Hash of the line */ |
| 81 | + unsigned short indent; /* Indent of the line. Only !=0 with --ignore-space-at sol option */ | |
| 78 | 82 | unsigned int iNext; /* 1+(Index of next line with same the same hash) */ |
| 79 | 83 | |
| 80 | 84 | /* an array of DLine elements serves two purposes. The fields |
| 81 | 85 | ** above are one per line of input text. But each entry is also |
| 82 | 86 | ** a bucket in a hash table, as follows: */ |
| @@ -125,12 +129,12 @@ | ||
| 125 | 129 | ** too long. |
| 126 | 130 | ** |
| 127 | 131 | ** Profiling show that in most cases this routine consumes the bulk of |
| 128 | 132 | ** the CPU time on a diff. |
| 129 | 133 | */ |
| 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; | |
| 132 | 136 | unsigned int h, h2; |
| 133 | 137 | DLine *a; |
| 134 | 138 | |
| 135 | 139 | /* Count the number of lines. Allocate space to hold |
| 136 | 140 | ** the returned array. |
| @@ -158,18 +162,33 @@ | ||
| 158 | 162 | return a; |
| 159 | 163 | } |
| 160 | 164 | |
| 161 | 165 | /* Fill in the array */ |
| 162 | 166 | for(i=0; i<nLine; i++){ |
| 163 | - a[i].z = z; | |
| 164 | 167 | for(j=0; z[j] && z[j]!='\n'; j++){} |
| 165 | 168 | 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++){ | |
| 168 | 187 | h = h ^ (h<<2) ^ z[x]; |
| 169 | 188 | } |
| 170 | - a[i].h = h = (h<<LENGTH_MASK_SZ) | k; | |
| 189 | + a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s); | |
| 171 | 190 | h2 = h % nLine; |
| 172 | 191 | a[i].iNext = a[h2].iHash; |
| 173 | 192 | a[h2].iHash = i+1; |
| 174 | 193 | z += j+1; |
| 175 | 194 | } |
| @@ -221,15 +240,21 @@ | ||
| 221 | 240 | }else if( cPrefix=='+' ){ |
| 222 | 241 | blob_append(pOut, "<span class=\"diffadd\">", -1); |
| 223 | 242 | }else if( cPrefix=='-' ){ |
| 224 | 243 | blob_append(pOut, "<span class=\"diffrm\">", -1); |
| 225 | 244 | } |
| 245 | + if( pLine->indent ){ | |
| 246 | + blob_appendf(pOut, "%*s", pLine->indent, " "); | |
| 247 | + } | |
| 226 | 248 | htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK)); |
| 227 | 249 | if( cPrefix!=' ' ){ |
| 228 | 250 | blob_append(pOut, "</span>", -1); |
| 229 | 251 | } |
| 230 | 252 | }else{ |
| 253 | + if( pLine->indent ){ | |
| 254 | + blob_appendf(pOut, "%*s", pLine->indent, " "); | |
| 255 | + } | |
| 231 | 256 | blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK); |
| 232 | 257 | } |
| 233 | 258 | blob_append(pOut, "\n", 1); |
| 234 | 259 | } |
| 235 | 260 | |
| @@ -507,10 +532,13 @@ | ||
| 507 | 532 | p->iEnd = p->iEnd2; |
| 508 | 533 | p->iEnd2 = 0; |
| 509 | 534 | } |
| 510 | 535 | } |
| 511 | 536 | } |
| 537 | + if( pLine->indent && i==0 ){ | |
| 538 | + blob_appendf(pCol, "%*s", pLine->indent, " "); | |
| 539 | + } | |
| 512 | 540 | if( c=='\t' && !p->escHtml ){ |
| 513 | 541 | blob_append(pCol, " ", 1); |
| 514 | 542 | while( (k&7)!=7 && (p->escHtml || k<w) ){ |
| 515 | 543 | blob_append(pCol, " ", 1); |
| 516 | 544 | k++; |
| @@ -534,11 +562,11 @@ | ||
| 534 | 562 | blob_append(pCol, "</span>", 7); |
| 535 | 563 | } |
| 536 | 564 | if( col==SBS_TXTB ){ |
| 537 | 565 | sbsWriteNewlines(p); |
| 538 | 566 | }else if( !p->escHtml ){ |
| 539 | - sbsWriteSpace(p, w-k, SBS_TXTA); | |
| 567 | + sbsWriteSpace(p, w-k-pLine->indent, SBS_TXTA); | |
| 540 | 568 | } |
| 541 | 569 | } |
| 542 | 570 | |
| 543 | 571 | /* |
| 544 | 572 | ** Append a column to the final output blob. |
| @@ -1753,28 +1781,28 @@ | ||
| 1753 | 1781 | Blob *pB_Blob, /* TO file */ |
| 1754 | 1782 | Blob *pOut, /* Write diff here if not NULL */ |
| 1755 | 1783 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1756 | 1784 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1757 | 1785 | ){ |
| 1758 | - int ignoreEolWs; /* Ignore whitespace at the end of lines */ | |
| 1786 | + int ignoreWs; /* Ignore whitespace */ | |
| 1759 | 1787 | DContext c; |
| 1760 | 1788 | |
| 1761 | 1789 | if( diffFlags & DIFF_INVERT ){ |
| 1762 | 1790 | Blob *pTemp = pA_Blob; |
| 1763 | 1791 | pA_Blob = pB_Blob; |
| 1764 | 1792 | pB_Blob = pTemp; |
| 1765 | 1793 | } |
| 1766 | - ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; | |
| 1794 | + ignoreWs = (diffFlags & (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))!=0; | |
| 1767 | 1795 | blob_to_utf8_no_bom(pA_Blob, 0); |
| 1768 | 1796 | blob_to_utf8_no_bom(pB_Blob, 0); |
| 1769 | 1797 | |
| 1770 | 1798 | /* Prepare the input files */ |
| 1771 | 1799 | memset(&c, 0, sizeof(c)); |
| 1772 | 1800 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| 1773 | - &c.nFrom, ignoreEolWs); | |
| 1801 | + &c.nFrom, diffFlags); | |
| 1774 | 1802 | c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), |
| 1775 | - &c.nTo, ignoreEolWs); | |
| 1803 | + &c.nTo, diffFlags); | |
| 1776 | 1804 | if( c.aFrom==0 || c.aTo==0 ){ |
| 1777 | 1805 | fossil_free(c.aFrom); |
| 1778 | 1806 | fossil_free(c.aTo); |
| 1779 | 1807 | if( pOut ){ |
| 1780 | 1808 | diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags); |
| @@ -1782,20 +1810,27 @@ | ||
| 1782 | 1810 | return 0; |
| 1783 | 1811 | } |
| 1784 | 1812 | |
| 1785 | 1813 | /* Compute the difference */ |
| 1786 | 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 | + } | |
| 1787 | 1822 | if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){ |
| 1788 | 1823 | int i, m, n; |
| 1789 | 1824 | int *a = c.aEdit; |
| 1790 | 1825 | int mx = c.nEdit; |
| 1791 | 1826 | for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; } |
| 1792 | 1827 | if( n>10000 ){ |
| 1793 | 1828 | fossil_free(c.aFrom); |
| 1794 | 1829 | fossil_free(c.aTo); |
| 1795 | 1830 | fossil_free(c.aEdit); |
| 1796 | - diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags); | |
| 1831 | + if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags); | |
| 1797 | 1832 | return 0; |
| 1798 | 1833 | } |
| 1799 | 1834 | } |
| 1800 | 1835 | if( (diffFlags & DIFF_NOOPT)==0 ){ |
| 1801 | 1836 | diff_optimize(&c); |
| @@ -1828,14 +1863,17 @@ | ||
| 1828 | 1863 | ** |
| 1829 | 1864 | ** --brief Show filenames only DIFF_BRIEF |
| 1830 | 1865 | ** --context|-c N N lines of context. DIFF_CONTEXT_MASK |
| 1831 | 1866 | ** --html Format for HTML DIFF_HTML |
| 1832 | 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 | |
| 1833 | 1870 | ** --linenum|-n Show line numbers DIFF_LINENO |
| 1834 | 1871 | ** --noopt Disable optimization DIFF_NOOPT |
| 1835 | 1872 | ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE |
| 1836 | 1873 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 1874 | +** -w Ignore all whitespaces DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS | |
| 1837 | 1875 | ** --width|-W N N character lines. DIFF_WIDTH_MASK |
| 1838 | 1876 | */ |
| 1839 | 1877 | u64 diff_options(void){ |
| 1840 | 1878 | u64 diffFlags = 0; |
| 1841 | 1879 | const char *z; |
| @@ -1850,10 +1888,13 @@ | ||
| 1850 | 1888 | f *= DIFF_CONTEXT_MASK+1; |
| 1851 | 1889 | if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; |
| 1852 | 1890 | diffFlags |= f; |
| 1853 | 1891 | } |
| 1854 | 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); | |
| 1855 | 1896 | if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO; |
| 1856 | 1897 | if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT; |
| 1857 | 1898 | if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT; |
| 1858 | 1899 | if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF; |
| 1859 | 1900 | return diffFlags; |
| @@ -1930,11 +1971,14 @@ | ||
| 1930 | 1971 | typedef struct Annotator Annotator; |
| 1931 | 1972 | struct Annotator { |
| 1932 | 1973 | DContext c; /* The diff-engine context */ |
| 1933 | 1974 | struct AnnLine { /* Lines of the original files... */ |
| 1934 | 1975 | 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) */ | |
| 1936 | 1980 | short int iVers; /* Level at which tag was set */ |
| 1937 | 1981 | } *aOrig; |
| 1938 | 1982 | int nOrig; /* Number of elements in aOrig[] */ |
| 1939 | 1983 | int nVers; /* Number of versions analyzed */ |
| 1940 | 1984 | int bLimit; /* True if the iLimit was reached */ |
| @@ -1956,18 +2000,20 @@ | ||
| 1956 | 2000 | */ |
| 1957 | 2001 | static int annotation_start(Annotator *p, Blob *pInput){ |
| 1958 | 2002 | int i; |
| 1959 | 2003 | |
| 1960 | 2004 | 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); | |
| 1962 | 2007 | if( p->c.aTo==0 ){ |
| 1963 | 2008 | return 1; |
| 1964 | 2009 | } |
| 1965 | 2010 | p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); |
| 1966 | 2011 | for(i=0; i<p->c.nTo; i++){ |
| 1967 | 2012 | p->aOrig[i].z = p->c.aTo[i].z; |
| 1968 | 2013 | p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; |
| 2014 | + p->aOrig[i].indent = p->c.aTo[i].indent; | |
| 1969 | 2015 | p->aOrig[i].iVers = -1; |
| 1970 | 2016 | } |
| 1971 | 2017 | p->nOrig = p->c.nTo; |
| 1972 | 2018 | return 0; |
| 1973 | 2019 | } |
| @@ -1983,11 +2029,11 @@ | ||
| 1983 | 2029 | int i, j; |
| 1984 | 2030 | int lnTo; |
| 1985 | 2031 | |
| 1986 | 2032 | /* Prepare the parent file to be diffed */ |
| 1987 | 2033 | p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent), |
| 1988 | - &p->c.nFrom, 1); | |
| 2034 | + &p->c.nFrom, 0); | |
| 1989 | 2035 | if( p->c.aFrom==0 ){ |
| 1990 | 2036 | return 1; |
| 1991 | 2037 | } |
| 1992 | 2038 | |
| 1993 | 2039 | /* Compute the differences going from pParent to the file being |
| @@ -2256,10 +2302,11 @@ | ||
| 2256 | 2302 | @ <pre> |
| 2257 | 2303 | for(i=0; i<ann.nOrig; i++){ |
| 2258 | 2304 | int iVers = ann.aOrig[i].iVers; |
| 2259 | 2305 | char *z = (char*)ann.aOrig[i].z; |
| 2260 | 2306 | int n = ann.aOrig[i].n; |
| 2307 | + int indent = ann.aOrig[i].indent+1; | |
| 2261 | 2308 | char zPrefix[300]; |
| 2262 | 2309 | z[n] = 0; |
| 2263 | 2310 | if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1; |
| 2264 | 2311 | |
| 2265 | 2312 | if( bBlame ){ |
| @@ -2270,26 +2317,26 @@ | ||
| 2270 | 2317 | "<span style='background-color:%s'>" |
| 2271 | 2318 | "%s%.10s</a> %s</span> %13.13s:", |
| 2272 | 2319 | p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser); |
| 2273 | 2320 | fossil_free(zLink); |
| 2274 | 2321 | }else{ |
| 2275 | - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", ""); | |
| 2322 | + sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%*s", indent, " "); | |
| 2276 | 2323 | } |
| 2277 | 2324 | }else{ |
| 2278 | 2325 | if( iVers>=0 ){ |
| 2279 | 2326 | struct AnnVers *p = ann.aVers+iVers; |
| 2280 | 2327 | char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid); |
| 2281 | 2328 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
| 2282 | 2329 | "<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, " "); | |
| 2285 | 2332 | fossil_free(zLink); |
| 2286 | 2333 | }else{ |
| 2287 | - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1); | |
| 2334 | + sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:%*s", "", i+1, indent, " "); | |
| 2288 | 2335 | } |
| 2289 | 2336 | } |
| 2290 | - @ %s(zPrefix) %h(z) | |
| 2337 | + @ %s(zPrefix)%h(z) | |
| 2291 | 2338 | |
| 2292 | 2339 | } |
| 2293 | 2340 | @ </pre> |
| 2294 | 2341 | style_footer(); |
| 2295 | 2342 | } |
| @@ -2372,27 +2419,28 @@ | ||
| 2372 | 2419 | fossil_print("---------------------------------------------------\n"); |
| 2373 | 2420 | } |
| 2374 | 2421 | for(i=0; i<ann.nOrig; i++){ |
| 2375 | 2422 | int iVers = ann.aOrig[i].iVers; |
| 2376 | 2423 | char *z = (char*)ann.aOrig[i].z; |
| 2424 | + int indent = ann.aOrig[i].indent + 1; | |
| 2377 | 2425 | int n = ann.aOrig[i].n; |
| 2378 | 2426 | struct AnnVers *p; |
| 2379 | 2427 | if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1; |
| 2380 | 2428 | p = ann.aVers + iVers; |
| 2381 | 2429 | if( bBlame ){ |
| 2382 | 2430 | 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); | |
| 2385 | 2433 | }else{ |
| 2386 | - fossil_print("%35s %.*s\n", "", n, z); | |
| 2434 | + fossil_print("%35s %*s%.*s\n", "", indent, " ", n, z); | |
| 2387 | 2435 | } |
| 2388 | 2436 | }else{ |
| 2389 | 2437 | 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); | |
| 2392 | 2440 | }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); | |
| 2395 | 2443 | } |
| 2396 | 2444 | } |
| 2397 | 2445 | } |
| 2398 | 2446 | } |
| 2399 | 2447 |
| --- 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 @@ | ||
| 28 | 28 | ** Flag parameters to the text_diff() routine used to control the formatting |
| 29 | 29 | ** of the diff output. |
| 30 | 30 | */ |
| 31 | 31 | #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */ |
| 32 | 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 */ | |
| 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 | 41 | #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ |
| 42 | 42 | #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ |
| 43 | 43 | #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */ |
| 44 | 44 | #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */ |
| 45 | 45 | |
| @@ -54,10 +54,13 @@ | ||
| 54 | 54 | "cannot compute difference between symlink and regular file\n" |
| 55 | 55 | |
| 56 | 56 | #define DIFF_TOO_MANY_CHANGES \ |
| 57 | 57 | "more than 10,000 changes\n" |
| 58 | 58 | |
| 59 | +#define DIFF_WHITESPACE_ONLY \ | |
| 60 | + "whitespace changes only\n" | |
| 61 | + | |
| 59 | 62 | /* |
| 60 | 63 | ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes) |
| 61 | 64 | */ |
| 62 | 65 | #define LENGTH_MASK_SZ 13 |
| 63 | 66 | #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) |
| @@ -73,10 +76,11 @@ | ||
| 73 | 76 | */ |
| 74 | 77 | typedef struct DLine DLine; |
| 75 | 78 | struct DLine { |
| 76 | 79 | const char *z; /* The text of the line */ |
| 77 | 80 | unsigned int h; /* Hash of the line */ |
| 81 | + unsigned short indent; /* Indent of the line. Only !=0 with --ignore-space-at sol option */ | |
| 78 | 82 | unsigned int iNext; /* 1+(Index of next line with same the same hash) */ |
| 79 | 83 | |
| 80 | 84 | /* an array of DLine elements serves two purposes. The fields |
| 81 | 85 | ** above are one per line of input text. But each entry is also |
| 82 | 86 | ** a bucket in a hash table, as follows: */ |
| @@ -125,12 +129,12 @@ | ||
| 125 | 129 | ** too long. |
| 126 | 130 | ** |
| 127 | 131 | ** Profiling show that in most cases this routine consumes the bulk of |
| 128 | 132 | ** the CPU time on a diff. |
| 129 | 133 | */ |
| 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; | |
| 132 | 136 | unsigned int h, h2; |
| 133 | 137 | DLine *a; |
| 134 | 138 | |
| 135 | 139 | /* Count the number of lines. Allocate space to hold |
| 136 | 140 | ** the returned array. |
| @@ -158,18 +162,33 @@ | ||
| 158 | 162 | return a; |
| 159 | 163 | } |
| 160 | 164 | |
| 161 | 165 | /* Fill in the array */ |
| 162 | 166 | for(i=0; i<nLine; i++){ |
| 163 | - a[i].z = z; | |
| 164 | 167 | for(j=0; z[j] && z[j]!='\n'; j++){} |
| 165 | 168 | 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++){ | |
| 168 | 187 | h = h ^ (h<<2) ^ z[x]; |
| 169 | 188 | } |
| 170 | - a[i].h = h = (h<<LENGTH_MASK_SZ) | k; | |
| 189 | + a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s); | |
| 171 | 190 | h2 = h % nLine; |
| 172 | 191 | a[i].iNext = a[h2].iHash; |
| 173 | 192 | a[h2].iHash = i+1; |
| 174 | 193 | z += j+1; |
| 175 | 194 | } |
| @@ -221,15 +240,21 @@ | ||
| 221 | 240 | }else if( cPrefix=='+' ){ |
| 222 | 241 | blob_append(pOut, "<span class=\"diffadd\">", -1); |
| 223 | 242 | }else if( cPrefix=='-' ){ |
| 224 | 243 | blob_append(pOut, "<span class=\"diffrm\">", -1); |
| 225 | 244 | } |
| 245 | + if( pLine->indent ){ | |
| 246 | + blob_appendf(pOut, "%*s", pLine->indent, " "); | |
| 247 | + } | |
| 226 | 248 | htmlize_to_blob(pOut, pLine->z, (pLine->h & LENGTH_MASK)); |
| 227 | 249 | if( cPrefix!=' ' ){ |
| 228 | 250 | blob_append(pOut, "</span>", -1); |
| 229 | 251 | } |
| 230 | 252 | }else{ |
| 253 | + if( pLine->indent ){ | |
| 254 | + blob_appendf(pOut, "%*s", pLine->indent, " "); | |
| 255 | + } | |
| 231 | 256 | blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK); |
| 232 | 257 | } |
| 233 | 258 | blob_append(pOut, "\n", 1); |
| 234 | 259 | } |
| 235 | 260 | |
| @@ -507,10 +532,13 @@ | ||
| 507 | 532 | p->iEnd = p->iEnd2; |
| 508 | 533 | p->iEnd2 = 0; |
| 509 | 534 | } |
| 510 | 535 | } |
| 511 | 536 | } |
| 537 | + if( pLine->indent && i==0 ){ | |
| 538 | + blob_appendf(pCol, "%*s", pLine->indent, " "); | |
| 539 | + } | |
| 512 | 540 | if( c=='\t' && !p->escHtml ){ |
| 513 | 541 | blob_append(pCol, " ", 1); |
| 514 | 542 | while( (k&7)!=7 && (p->escHtml || k<w) ){ |
| 515 | 543 | blob_append(pCol, " ", 1); |
| 516 | 544 | k++; |
| @@ -534,11 +562,11 @@ | ||
| 534 | 562 | blob_append(pCol, "</span>", 7); |
| 535 | 563 | } |
| 536 | 564 | if( col==SBS_TXTB ){ |
| 537 | 565 | sbsWriteNewlines(p); |
| 538 | 566 | }else if( !p->escHtml ){ |
| 539 | - sbsWriteSpace(p, w-k, SBS_TXTA); | |
| 567 | + sbsWriteSpace(p, w-k-pLine->indent, SBS_TXTA); | |
| 540 | 568 | } |
| 541 | 569 | } |
| 542 | 570 | |
| 543 | 571 | /* |
| 544 | 572 | ** Append a column to the final output blob. |
| @@ -1753,28 +1781,28 @@ | ||
| 1753 | 1781 | Blob *pB_Blob, /* TO file */ |
| 1754 | 1782 | Blob *pOut, /* Write diff here if not NULL */ |
| 1755 | 1783 | ReCompiled *pRe, /* Only output changes where this Regexp matches */ |
| 1756 | 1784 | u64 diffFlags /* DIFF_* flags defined above */ |
| 1757 | 1785 | ){ |
| 1758 | - int ignoreEolWs; /* Ignore whitespace at the end of lines */ | |
| 1786 | + int ignoreWs; /* Ignore whitespace */ | |
| 1759 | 1787 | DContext c; |
| 1760 | 1788 | |
| 1761 | 1789 | if( diffFlags & DIFF_INVERT ){ |
| 1762 | 1790 | Blob *pTemp = pA_Blob; |
| 1763 | 1791 | pA_Blob = pB_Blob; |
| 1764 | 1792 | pB_Blob = pTemp; |
| 1765 | 1793 | } |
| 1766 | - ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; | |
| 1794 | + ignoreWs = (diffFlags & (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))!=0; | |
| 1767 | 1795 | blob_to_utf8_no_bom(pA_Blob, 0); |
| 1768 | 1796 | blob_to_utf8_no_bom(pB_Blob, 0); |
| 1769 | 1797 | |
| 1770 | 1798 | /* Prepare the input files */ |
| 1771 | 1799 | memset(&c, 0, sizeof(c)); |
| 1772 | 1800 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| 1773 | - &c.nFrom, ignoreEolWs); | |
| 1801 | + &c.nFrom, diffFlags); | |
| 1774 | 1802 | c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), |
| 1775 | - &c.nTo, ignoreEolWs); | |
| 1803 | + &c.nTo, diffFlags); | |
| 1776 | 1804 | if( c.aFrom==0 || c.aTo==0 ){ |
| 1777 | 1805 | fossil_free(c.aFrom); |
| 1778 | 1806 | fossil_free(c.aTo); |
| 1779 | 1807 | if( pOut ){ |
| 1780 | 1808 | diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags); |
| @@ -1782,20 +1810,27 @@ | ||
| 1782 | 1810 | return 0; |
| 1783 | 1811 | } |
| 1784 | 1812 | |
| 1785 | 1813 | /* Compute the difference */ |
| 1786 | 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 | + } | |
| 1787 | 1822 | if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){ |
| 1788 | 1823 | int i, m, n; |
| 1789 | 1824 | int *a = c.aEdit; |
| 1790 | 1825 | int mx = c.nEdit; |
| 1791 | 1826 | for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; } |
| 1792 | 1827 | if( n>10000 ){ |
| 1793 | 1828 | fossil_free(c.aFrom); |
| 1794 | 1829 | fossil_free(c.aTo); |
| 1795 | 1830 | fossil_free(c.aEdit); |
| 1796 | - diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags); | |
| 1831 | + if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags); | |
| 1797 | 1832 | return 0; |
| 1798 | 1833 | } |
| 1799 | 1834 | } |
| 1800 | 1835 | if( (diffFlags & DIFF_NOOPT)==0 ){ |
| 1801 | 1836 | diff_optimize(&c); |
| @@ -1828,14 +1863,17 @@ | ||
| 1828 | 1863 | ** |
| 1829 | 1864 | ** --brief Show filenames only DIFF_BRIEF |
| 1830 | 1865 | ** --context|-c N N lines of context. DIFF_CONTEXT_MASK |
| 1831 | 1866 | ** --html Format for HTML DIFF_HTML |
| 1832 | 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 | |
| 1833 | 1870 | ** --linenum|-n Show line numbers DIFF_LINENO |
| 1834 | 1871 | ** --noopt Disable optimization DIFF_NOOPT |
| 1835 | 1872 | ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE |
| 1836 | 1873 | ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| 1874 | +** -w Ignore all whitespaces DIFF_IGNORE_EOLWS|DIFF_IGNORE_SOLWS | |
| 1837 | 1875 | ** --width|-W N N character lines. DIFF_WIDTH_MASK |
| 1838 | 1876 | */ |
| 1839 | 1877 | u64 diff_options(void){ |
| 1840 | 1878 | u64 diffFlags = 0; |
| 1841 | 1879 | const char *z; |
| @@ -1850,10 +1888,13 @@ | ||
| 1850 | 1888 | f *= DIFF_CONTEXT_MASK+1; |
| 1851 | 1889 | if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; |
| 1852 | 1890 | diffFlags |= f; |
| 1853 | 1891 | } |
| 1854 | 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); | |
| 1855 | 1896 | if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO; |
| 1856 | 1897 | if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT; |
| 1857 | 1898 | if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT; |
| 1858 | 1899 | if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF; |
| 1859 | 1900 | return diffFlags; |
| @@ -1930,11 +1971,14 @@ | ||
| 1930 | 1971 | typedef struct Annotator Annotator; |
| 1931 | 1972 | struct Annotator { |
| 1932 | 1973 | DContext c; /* The diff-engine context */ |
| 1933 | 1974 | struct AnnLine { /* Lines of the original files... */ |
| 1934 | 1975 | 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) */ | |
| 1936 | 1980 | short int iVers; /* Level at which tag was set */ |
| 1937 | 1981 | } *aOrig; |
| 1938 | 1982 | int nOrig; /* Number of elements in aOrig[] */ |
| 1939 | 1983 | int nVers; /* Number of versions analyzed */ |
| 1940 | 1984 | int bLimit; /* True if the iLimit was reached */ |
| @@ -1956,18 +2000,20 @@ | ||
| 1956 | 2000 | */ |
| 1957 | 2001 | static int annotation_start(Annotator *p, Blob *pInput){ |
| 1958 | 2002 | int i; |
| 1959 | 2003 | |
| 1960 | 2004 | 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); | |
| 1962 | 2007 | if( p->c.aTo==0 ){ |
| 1963 | 2008 | return 1; |
| 1964 | 2009 | } |
| 1965 | 2010 | p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); |
| 1966 | 2011 | for(i=0; i<p->c.nTo; i++){ |
| 1967 | 2012 | p->aOrig[i].z = p->c.aTo[i].z; |
| 1968 | 2013 | p->aOrig[i].n = p->c.aTo[i].h & LENGTH_MASK; |
| 2014 | + p->aOrig[i].indent = p->c.aTo[i].indent; | |
| 1969 | 2015 | p->aOrig[i].iVers = -1; |
| 1970 | 2016 | } |
| 1971 | 2017 | p->nOrig = p->c.nTo; |
| 1972 | 2018 | return 0; |
| 1973 | 2019 | } |
| @@ -1983,11 +2029,11 @@ | ||
| 1983 | 2029 | int i, j; |
| 1984 | 2030 | int lnTo; |
| 1985 | 2031 | |
| 1986 | 2032 | /* Prepare the parent file to be diffed */ |
| 1987 | 2033 | p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent), |
| 1988 | - &p->c.nFrom, 1); | |
| 2034 | + &p->c.nFrom, 0); | |
| 1989 | 2035 | if( p->c.aFrom==0 ){ |
| 1990 | 2036 | return 1; |
| 1991 | 2037 | } |
| 1992 | 2038 | |
| 1993 | 2039 | /* Compute the differences going from pParent to the file being |
| @@ -2256,10 +2302,11 @@ | ||
| 2256 | 2302 | @ <pre> |
| 2257 | 2303 | for(i=0; i<ann.nOrig; i++){ |
| 2258 | 2304 | int iVers = ann.aOrig[i].iVers; |
| 2259 | 2305 | char *z = (char*)ann.aOrig[i].z; |
| 2260 | 2306 | int n = ann.aOrig[i].n; |
| 2307 | + int indent = ann.aOrig[i].indent+1; | |
| 2261 | 2308 | char zPrefix[300]; |
| 2262 | 2309 | z[n] = 0; |
| 2263 | 2310 | if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1; |
| 2264 | 2311 | |
| 2265 | 2312 | if( bBlame ){ |
| @@ -2270,26 +2317,26 @@ | ||
| 2270 | 2317 | "<span style='background-color:%s'>" |
| 2271 | 2318 | "%s%.10s</a> %s</span> %13.13s:", |
| 2272 | 2319 | p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser); |
| 2273 | 2320 | fossil_free(zLink); |
| 2274 | 2321 | }else{ |
| 2275 | - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", ""); | |
| 2322 | + sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s%*s", indent, " "); | |
| 2276 | 2323 | } |
| 2277 | 2324 | }else{ |
| 2278 | 2325 | if( iVers>=0 ){ |
| 2279 | 2326 | struct AnnVers *p = ann.aVers+iVers; |
| 2280 | 2327 | char *zLink = xhref("target='infowindow'", "%R/info/%S", p->zMUuid); |
| 2281 | 2328 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
| 2282 | 2329 | "<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, " "); | |
| 2285 | 2332 | fossil_free(zLink); |
| 2286 | 2333 | }else{ |
| 2287 | - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1); | |
| 2334 | + sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:%*s", "", i+1, indent, " "); | |
| 2288 | 2335 | } |
| 2289 | 2336 | } |
| 2290 | - @ %s(zPrefix) %h(z) | |
| 2337 | + @ %s(zPrefix)%h(z) | |
| 2291 | 2338 | |
| 2292 | 2339 | } |
| 2293 | 2340 | @ </pre> |
| 2294 | 2341 | style_footer(); |
| 2295 | 2342 | } |
| @@ -2372,27 +2419,28 @@ | ||
| 2372 | 2419 | fossil_print("---------------------------------------------------\n"); |
| 2373 | 2420 | } |
| 2374 | 2421 | for(i=0; i<ann.nOrig; i++){ |
| 2375 | 2422 | int iVers = ann.aOrig[i].iVers; |
| 2376 | 2423 | char *z = (char*)ann.aOrig[i].z; |
| 2424 | + int indent = ann.aOrig[i].indent + 1; | |
| 2377 | 2425 | int n = ann.aOrig[i].n; |
| 2378 | 2426 | struct AnnVers *p; |
| 2379 | 2427 | if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1; |
| 2380 | 2428 | p = ann.aVers + iVers; |
| 2381 | 2429 | if( bBlame ){ |
| 2382 | 2430 | 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); | |
| 2385 | 2433 | }else{ |
| 2386 | - fossil_print("%35s %.*s\n", "", n, z); | |
| 2434 | + fossil_print("%35s %*s%.*s\n", "", indent, " ", n, z); | |
| 2387 | 2435 | } |
| 2388 | 2436 | }else{ |
| 2389 | 2437 | 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); | |
| 2392 | 2440 | }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); | |
| 2395 | 2443 | } |
| 2396 | 2444 | } |
| 2397 | 2445 | } |
| 2398 | 2446 | } |
| 2399 | 2447 |
| --- 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 @@ | ||
| 628 | 628 | @ HR_PAD_TOP 4 |
| 629 | 629 | @ HR_PAD_BTM 8 |
| 630 | 630 | @ FN_BG #444444 |
| 631 | 631 | @ FN_FG #ffffff |
| 632 | 632 | @ FN_PAD 5 |
| 633 | +@ ERR_FG #ee0000 | |
| 633 | 634 | @ PADX 5 |
| 634 | 635 | @ WIDTH 80 |
| 635 | 636 | @ HEIGHT 45 |
| 636 | 637 | @ LB_HEIGHT 25 |
| 637 | 638 | @ } |
| @@ -680,20 +681,21 @@ | ||
| 680 | 681 | @ if {![regexp {^=+ (.*?) =+ versus =+ (.*?) =+$} $line all fn fn2] |
| 681 | 682 | @ && ![regexp {^=+ (.*?) =+$} $line all fn] |
| 682 | 683 | @ } { |
| 683 | 684 | @ continue |
| 684 | 685 | @ } |
| 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]} { | |
| 686 | 690 | @ continue |
| 687 | 691 | @ } |
| 688 | 692 | @ incr nDiffs |
| 689 | 693 | @ set idx [expr {$nDiffs > 1 ? [.txtA index end] : "1.0"}] |
| 690 | 694 | @ .wfiles.lb insert end $fn |
| 691 | 695 | @ |
| 692 | 696 | @ foreach c [cols] { |
| 693 | -@ while {[getLine $difftxt $N ii] ne "<pre>"} continue | |
| 694 | -@ | |
| 695 | 697 | @ if {$nDiffs > 1} { |
| 696 | 698 | @ $c insert end \n - |
| 697 | 699 | @ } |
| 698 | 700 | @ if {[colType $c] eq "txt"} { |
| 699 | 701 | @ $c insert end $fn\n fn |
| @@ -701,10 +703,12 @@ | ||
| 701 | 703 | @ } else { |
| 702 | 704 | @ $c insert end \n fn |
| 703 | 705 | @ } |
| 704 | 706 | @ $c insert end \n - |
| 705 | 707 | @ |
| 708 | +@ if {$errMsg ne ""} continue | |
| 709 | +@ while {[getLine $difftxt $N ii] ne "<pre>"} continue | |
| 706 | 710 | @ set type [colType $c] |
| 707 | 711 | @ set str {} |
| 708 | 712 | @ while {[set line [getLine $difftxt $N ii]] ne "</pre>"} { |
| 709 | 713 | @ set len [string length [dehtml $line]] |
| 710 | 714 | @ if {$len > $widths($type)} { |
| @@ -723,10 +727,15 @@ | ||
| 723 | 727 | @ } else { |
| 724 | 728 | @ $c insert end [dehtml $pre] - |
| 725 | 729 | @ } |
| 726 | 730 | @ } |
| 727 | 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 | +@ } | |
| 728 | 737 | @ } |
| 729 | 738 | @ |
| 730 | 739 | @ foreach c [cols] { |
| 731 | 740 | @ set type [colType $c] |
| 732 | 741 | @ if {$type ne "txt"} { |
| @@ -894,10 +903,11 @@ | ||
| 894 | 903 | @ $txt tag config $tag -background $CFG([string toupper $tag]_BG) |
| 895 | 904 | @ $txt tag lower $tag |
| 896 | 905 | @ } |
| 897 | 906 | @ $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \ |
| 898 | 907 | @ -justify center |
| 908 | +@ $txt tag config err -foreground $CFG(ERR_FG) | |
| 899 | 909 | @ } |
| 900 | 910 | @ text .mkr |
| 901 | 911 | @ |
| 902 | 912 | @ foreach c [cols] { |
| 903 | 913 | @ set keyPrefix [string toupper [colType $c]]_COL_ |
| @@ -1079,23 +1089,27 @@ | ||
| 1079 | 1089 | ** The "--binary" option causes files matching the glob PATTERN to be treated |
| 1080 | 1090 | ** as binary when considering if they should be used with external diff program. |
| 1081 | 1091 | ** This option overrides the "binary-glob" setting. |
| 1082 | 1092 | ** |
| 1083 | 1093 | ** 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 | |
| 1097 | 1111 | */ |
| 1098 | 1112 | void diff_cmd(void){ |
| 1099 | 1113 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 1100 | 1114 | int isInternDiff; /* True for internal diff */ |
| 1101 | 1115 | int verboseFlag; /* True if -v or --verbose flag is used */ |
| @@ -1121,11 +1135,10 @@ | ||
| 1121 | 1135 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1122 | 1136 | if( !verboseFlag ){ |
| 1123 | 1137 | verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */ |
| 1124 | 1138 | } |
| 1125 | 1139 | if( verboseFlag ) diffFlags |= DIFF_VERBOSE; |
| 1126 | - | |
| 1127 | 1140 | if( zBranch ){ |
| 1128 | 1141 | if( zTo || zFrom ){ |
| 1129 | 1142 | fossil_fatal("cannot use --from or --to with --branch"); |
| 1130 | 1143 | } |
| 1131 | 1144 | zTo = zBranch; |
| 1132 | 1145 |
| --- 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 @@ | ||
| 449 | 449 | if( verboseFlag==0 ){ |
| 450 | 450 | diffFlags = 0; /* Zero means do not show any diff */ |
| 451 | 451 | }else{ |
| 452 | 452 | int x; |
| 453 | 453 | if( sideBySide ){ |
| 454 | - diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS; | |
| 454 | + diffFlags = DIFF_SIDEBYSIDE; | |
| 455 | 455 | |
| 456 | 456 | /* "dw" query parameter determines width of each column */ |
| 457 | 457 | x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1); |
| 458 | 458 | if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK; |
| 459 | 459 | diffFlags += x; |
| 460 | 460 | }else{ |
| 461 | - diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS; | |
| 461 | + diffFlags = DIFF_INLINE; | |
| 462 | 462 | } |
| 463 | 463 | |
| 464 | + if( P("w") ){ | |
| 465 | + diffFlags |= (DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS); | |
| 466 | + } | |
| 464 | 467 | /* "dc" query parameter determines lines of context */ |
| 465 | 468 | x = atoi(PD("dc","7")); |
| 466 | 469 | if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK; |
| 467 | 470 | diffFlags += x; |
| 468 | 471 | |
| @@ -469,11 +472,10 @@ | ||
| 469 | 472 | /* The "noopt" parameter disables diff optimization */ |
| 470 | 473 | if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT; |
| 471 | 474 | } |
| 472 | 475 | return diffFlags; |
| 473 | 476 | } |
| 474 | - | |
| 475 | 477 | |
| 476 | 478 | /* |
| 477 | 479 | ** WEBPAGE: vinfo |
| 478 | 480 | ** WEBPAGE: ci |
| 479 | 481 | ** URL: /ci?name=RID|ARTIFACTID |
| @@ -664,50 +666,48 @@ | ||
| 664 | 666 | login_anonymous_available(); |
| 665 | 667 | } |
| 666 | 668 | db_finalize(&q1); |
| 667 | 669 | showTags(rid, ""); |
| 668 | 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 */ | |
| 669 | 674 | @ <div class="section">Changes</div> |
| 670 | 675 | @ <div class="sectionmenu"> |
| 671 | 676 | verboseFlag = g.zPath[0]!='c'; |
| 672 | 677 | if( db_get_boolean("show-version-diffs", 0)==0 ){ |
| 673 | 678 | verboseFlag = !verboseFlag; |
| 674 | - if( verboseFlag ){ | |
| 675 | - @ %z(xhref("class='button'","%R/vinfo/%T",zName)) | |
| 676 | - @ hide diffs</a> | |
| 677 | - if( sideBySide ){ | |
| 678 | - @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName)) | |
| 679 | - @ unified diffs</a> | |
| 680 | - }else{ | |
| 681 | - @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName)) | |
| 682 | - @ side-by-side diffs</a> | |
| 683 | - } | |
| 684 | - }else{ | |
| 685 | - @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName)) | |
| 686 | - @ show unified diffs</a> | |
| 687 | - @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName)) | |
| 688 | - @ show side-by-side diffs</a> | |
| 689 | - } | |
| 690 | - }else{ | |
| 691 | - if( verboseFlag ){ | |
| 692 | - @ %z(xhref("class='button'","%R/ci/%T",zName))hide diffs</a> | |
| 693 | - if( sideBySide ){ | |
| 694 | - @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName)) | |
| 695 | - @ unified diffs</a> | |
| 696 | - }else{ | |
| 697 | - @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName)) | |
| 698 | - @ side-by-side diffs</a> | |
| 699 | - } | |
| 700 | - }else{ | |
| 701 | - @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName)) | |
| 702 | - @ show unified diffs</a> | |
| 703 | - @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName)) | |
| 704 | - @ show side-by-side 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 Diffs</a> | |
| 687 | + if( sideBySide ){ | |
| 688 | + @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW)) | |
| 689 | + @ Unified Diffs</a> | |
| 690 | + }else{ | |
| 691 | + @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW)) | |
| 692 | + @ Side-by-Side Diffs</a> | |
| 693 | + } | |
| 694 | + if( *zW ){ | |
| 695 | + @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide)) | |
| 696 | + @ Show Whitespace Changes</a> | |
| 697 | + }else{ | |
| 698 | + @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide)) | |
| 699 | + @ Ignore Whitespace</a> | |
| 700 | + } | |
| 701 | + }else{ | |
| 702 | + @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName)) | |
| 703 | + @ Show Unified Diffs</a> | |
| 704 | + @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName)) | |
| 705 | + @ Show Side-by-Side Diffs</a> | |
| 706 | 706 | } |
| 707 | 707 | @ %z(xhref("class='button'","%R/vpatch?from=%S&to=%S",zParent,zUuid)) |
| 708 | - @ patch</a></div> | |
| 708 | + @ Patch</a></div> | |
| 709 | 709 | if( pRe ){ |
| 710 | 710 | @ <p><b>Only differences that match regular expression "%h(zRe)" |
| 711 | 711 | @ are shown.</b></p> |
| 712 | 712 | } |
| 713 | 713 | db_prepare(&q3, |
| @@ -721,11 +721,10 @@ | ||
| 721 | 721 | " AND (mlink.fid>0" |
| 722 | 722 | " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))" |
| 723 | 723 | " ORDER BY name /*sort*/", |
| 724 | 724 | rid, rid |
| 725 | 725 | ); |
| 726 | - diffFlags = construct_diff_flags(verboseFlag, sideBySide); | |
| 727 | 726 | while( db_step(&q3)==SQLITE_ROW ){ |
| 728 | 727 | const char *zName = db_column_text(&q3,0); |
| 729 | 728 | int mperm = db_column_int(&q3, 1); |
| 730 | 729 | const char *zOld = db_column_text(&q3,2); |
| 731 | 730 | const char *zNew = db_column_text(&q3,3); |
| @@ -954,10 +953,11 @@ | ||
| 954 | 953 | ManifestFile *pFileFrom, *pFileTo; |
| 955 | 954 | const char *zBranch; |
| 956 | 955 | const char *zFrom; |
| 957 | 956 | const char *zTo; |
| 958 | 957 | const char *zRe; |
| 958 | + const char *zW; | |
| 959 | 959 | const char *zVerbose; |
| 960 | 960 | const char *zGlob; |
| 961 | 961 | ReCompiled *pRe = 0; |
| 962 | 962 | login_check_credentials(); |
| 963 | 963 | if( !g.perm.Read ){ login_needed(); return; } |
| @@ -987,39 +987,54 @@ | ||
| 987 | 987 | zFrom = P("from"); |
| 988 | 988 | zTo = P("to"); |
| 989 | 989 | if(zGlob && !*zGlob){ |
| 990 | 990 | zGlob = NULL; |
| 991 | 991 | } |
| 992 | + diffFlags = construct_diff_flags(verboseFlag, sideBySide); | |
| 993 | + zW = (diffFlags&(DIFF_IGNORE_SOLWS|DIFF_IGNORE_EOLWS))?"&w":""; | |
| 992 | 994 | if( sideBySide || verboseFlag ){ |
| 993 | 995 | 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", | |
| 995 | 997 | zFrom, zTo, |
| 996 | - zGlob ? "&glob=" : "", zGlob ? zGlob : ""); | |
| 998 | + zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); | |
| 997 | 999 | } |
| 998 | 1000 | 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", | |
| 1001 | 1003 | zFrom, zTo, |
| 1002 | - zGlob ? "&glob=" : "", zGlob ? zGlob : ""); | |
| 1004 | + zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); | |
| 1003 | 1005 | } |
| 1004 | 1006 | if( sideBySide || !verboseFlag ) { |
| 1005 | 1007 | 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", | |
| 1007 | 1009 | zFrom, zTo, |
| 1008 | - zGlob ? "&glob=" : "", zGlob ? zGlob : ""); | |
| 1010 | + zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); | |
| 1009 | 1011 | } |
| 1010 | 1012 | 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, | |
| 1012 | 1014 | sideBySide, (verboseFlag && !sideBySide)?"&v":"", |
| 1013 | - zGlob ? "&glob=" : "", zGlob ? zGlob : ""); | |
| 1015 | + zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); | |
| 1014 | 1016 | if( zGlob ){ |
| 1015 | 1017 | 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); | |
| 1018 | 1020 | }else{ |
| 1019 | 1021 | 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 | + } | |
| 1021 | 1036 | } |
| 1022 | 1037 | style_header("Check-in Differences"); |
| 1023 | 1038 | @ <h2>Difference From:</h2><blockquote> |
| 1024 | 1039 | checkin_description(ridFrom); |
| 1025 | 1040 | @ </blockquote><h2>To:</h2><blockquote> |
| @@ -1036,11 +1051,10 @@ | ||
| 1036 | 1051 | |
| 1037 | 1052 | manifest_file_rewind(pFrom); |
| 1038 | 1053 | pFileFrom = manifest_file_next(pFrom, 0); |
| 1039 | 1054 | manifest_file_rewind(pTo); |
| 1040 | 1055 | pFileTo = manifest_file_next(pTo, 0); |
| 1041 | - diffFlags = construct_diff_flags(verboseFlag, sideBySide); | |
| 1042 | 1056 | while( pFileFrom || pFileTo ){ |
| 1043 | 1057 | int cmp; |
| 1044 | 1058 | if( pFileFrom==0 ){ |
| 1045 | 1059 | cmp = +1; |
| 1046 | 1060 | }else if( pFileTo==0 ){ |
| @@ -1337,10 +1351,11 @@ | ||
| 1337 | 1351 | int isPatch; |
| 1338 | 1352 | int sideBySide; |
| 1339 | 1353 | char *zV1; |
| 1340 | 1354 | char *zV2; |
| 1341 | 1355 | const char *zRe; |
| 1356 | + const char *zW; /* URL param for ignoring whitespace */ | |
| 1342 | 1357 | ReCompiled *pRe = 0; |
| 1343 | 1358 | u64 diffFlags; |
| 1344 | 1359 | |
| 1345 | 1360 | login_check_credentials(); |
| 1346 | 1361 | if( !g.perm.Read ){ login_needed(); return; } |
| @@ -1367,20 +1382,31 @@ | ||
| 1367 | 1382 | zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1); |
| 1368 | 1383 | zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2); |
| 1369 | 1384 | diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML; |
| 1370 | 1385 | |
| 1371 | 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 | + } | |
| 1372 | 1398 | style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", |
| 1373 | 1399 | g.zTop, P("v1"), P("v2")); |
| 1374 | 1400 | 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); | |
| 1378 | 1404 | }else{ |
| 1379 | 1405 | 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); | |
| 1382 | 1408 | } |
| 1383 | 1409 | |
| 1384 | 1410 | if( P("smhdr")!=0 ){ |
| 1385 | 1411 | @ <h2>Differences From Artifact |
| 1386 | 1412 | @ %z(href("%R/artifact/%S",zV1))[%S(zV1)]</a> To |
| 1387 | 1413 |
| --- 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 diffs</a> |
| 677 | if( sideBySide ){ |
| 678 | @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName)) |
| 679 | @ unified diffs</a> |
| 680 | }else{ |
| 681 | @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName)) |
| 682 | @ side-by-side diffs</a> |
| 683 | } |
| 684 | }else{ |
| 685 | @ %z(xhref("class='button'","%R/ci/%T?sbs=0",zName)) |
| 686 | @ show unified diffs</a> |
| 687 | @ %z(xhref("class='button'","%R/ci/%T?sbs=1",zName)) |
| 688 | @ show side-by-side diffs</a> |
| 689 | } |
| 690 | }else{ |
| 691 | if( verboseFlag ){ |
| 692 | @ %z(xhref("class='button'","%R/ci/%T",zName))hide diffs</a> |
| 693 | if( sideBySide ){ |
| 694 | @ %z(xhref("class='button'","%R/info/%T?sbs=0",zName)) |
| 695 | @ unified diffs</a> |
| 696 | }else{ |
| 697 | @ %z(xhref("class='button'","%R/info/%T?sbs=1",zName)) |
| 698 | @ side-by-side diffs</a> |
| 699 | } |
| 700 | }else{ |
| 701 | @ %z(xhref("class='button'","%R/vinfo/%T?sbs=0",zName)) |
| 702 | @ show unified diffs</a> |
| 703 | @ %z(xhref("class='button'","%R/vinfo/%T?sbs=1",zName)) |
| 704 | @ show side-by-side 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 Diffs</a> |
| 687 | if( sideBySide ){ |
| 688 | @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW)) |
| 689 | @ Unified Diffs</a> |
| 690 | }else{ |
| 691 | @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW)) |
| 692 | @ Side-by-Side Diffs</a> |
| 693 | } |
| 694 | if( *zW ){ |
| 695 | @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide)) |
| 696 | @ Show Whitespace Changes</a> |
| 697 | }else{ |
| 698 | @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide)) |
| 699 | @ Ignore Whitespace</a> |
| 700 | } |
| 701 | }else{ |
| 702 | @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName)) |
| 703 | @ Show Unified Diffs</a> |
| 704 | @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName)) |
| 705 | @ Show Side-by-Side 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 |
+3
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -11,10 +11,13 @@ | ||
| 11 | 11 | filter links. |
| 12 | 12 | * The [/help/info | info command] now shows leaf status of the checkout. |
| 13 | 13 | * Add support for tunneling https through a http proxy (Ticket [e854101c4f]). |
| 14 | 14 | * Add option --empty to the "[/help?cmd=open | fossil open]" command. |
| 15 | 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. | |
| 16 | 19 | |
| 17 | 20 | <h2>Changes For Version 1.28 (2014-01-27)</h2> |
| 18 | 21 | * Enhance [/help?cmd=/reports | /reports] to support event type filtering. |
| 19 | 22 | * When cloning a repository, the user name passed via the URL (if any) |
| 20 | 23 | is now used as the default local admin user's name. |
| 21 | 24 |
| --- 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 |