Fossil SCM

Merge the enhancements that adds the "diff-binary" setting for passing binary files to the external diff program.

drh 2012-10-02 22:59 trunk merge
Commit f8339c2743dfcf8434a9844480d6ad585789e95b
+5
--- src/db.c
+++ src/db.c
@@ -2014,10 +2014,11 @@
20142014
{ "binary-glob", 0, 32, 1, "" },
20152015
{ "clearsign", 0, 0, 0, "off" },
20162016
{ "case-sensitive",0, 0, 0, "on" },
20172017
{ "crnl-glob", 0, 16, 1, "" },
20182018
{ "default-perms", 0, 16, 0, "u" },
2019
+ { "diff-binary", 0, 0, 0, "on" },
20192020
{ "diff-command", 0, 16, 0, "" },
20202021
{ "dont-push", 0, 0, 0, "off" },
20212022
{ "editor", 0, 16, 0, "" },
20222023
{ "gdiff-command", 0, 16, 0, "gdiff" },
20232024
{ "gmerge-command",0, 40, 0, "" },
@@ -2106,10 +2107,14 @@
21062107
** Set to "*" to disable CR+NL checking.
21072108
**
21082109
** default-perms Permissions given automatically to new users. For more
21092110
** information on permissions see Users page in Server
21102111
** Administration of the HTTP UI. Default: u.
2112
+**
2113
+** diff-binary If TRUE (the default), permit files that may be binary
2114
+** or that match the "binary-glob" setting to be used with
2115
+** external diff programs. If FALSE, skip these files.
21112116
**
21122117
** diff-command External command to run when performing a diff.
21132118
** If undefined, the internal text diff will be used.
21142119
**
21152120
** dont-push Prevent this repository from pushing from client to
21162121
--- src/db.c
+++ src/db.c
@@ -2014,10 +2014,11 @@
2014 { "binary-glob", 0, 32, 1, "" },
2015 { "clearsign", 0, 0, 0, "off" },
2016 { "case-sensitive",0, 0, 0, "on" },
2017 { "crnl-glob", 0, 16, 1, "" },
2018 { "default-perms", 0, 16, 0, "u" },
 
2019 { "diff-command", 0, 16, 0, "" },
2020 { "dont-push", 0, 0, 0, "off" },
2021 { "editor", 0, 16, 0, "" },
2022 { "gdiff-command", 0, 16, 0, "gdiff" },
2023 { "gmerge-command",0, 40, 0, "" },
@@ -2106,10 +2107,14 @@
2106 ** Set to "*" to disable CR+NL checking.
2107 **
2108 ** default-perms Permissions given automatically to new users. For more
2109 ** information on permissions see Users page in Server
2110 ** Administration of the HTTP UI. Default: u.
 
 
 
 
2111 **
2112 ** diff-command External command to run when performing a diff.
2113 ** If undefined, the internal text diff will be used.
2114 **
2115 ** dont-push Prevent this repository from pushing from client to
2116
--- src/db.c
+++ src/db.c
@@ -2014,10 +2014,11 @@
2014 { "binary-glob", 0, 32, 1, "" },
2015 { "clearsign", 0, 0, 0, "off" },
2016 { "case-sensitive",0, 0, 0, "on" },
2017 { "crnl-glob", 0, 16, 1, "" },
2018 { "default-perms", 0, 16, 0, "u" },
2019 { "diff-binary", 0, 0, 0, "on" },
2020 { "diff-command", 0, 16, 0, "" },
2021 { "dont-push", 0, 0, 0, "off" },
2022 { "editor", 0, 16, 0, "" },
2023 { "gdiff-command", 0, 16, 0, "gdiff" },
2024 { "gmerge-command",0, 40, 0, "" },
@@ -2106,10 +2107,14 @@
2107 ** Set to "*" to disable CR+NL checking.
2108 **
2109 ** default-perms Permissions given automatically to new users. For more
2110 ** information on permissions see Users page in Server
2111 ** Administration of the HTTP UI. Default: u.
2112 **
2113 ** diff-binary If TRUE (the default), permit files that may be binary
2114 ** or that match the "binary-glob" setting to be used with
2115 ** external diff programs. If FALSE, skip these files.
2116 **
2117 ** diff-command External command to run when performing a diff.
2118 ** If undefined, the internal text diff will be used.
2119 **
2120 ** dont-push Prevent this repository from pushing from client to
2121
+32 -7
--- src/diff.c
+++ src/diff.c
@@ -38,10 +38,20 @@
3838
#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
3939
#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
4040
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
4141
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
4242
43
+/*
44
+** These error messages are shared in multiple locations. They are defined
45
+** here for consistency.
46
+*/
47
+#define DIFF_CANNOT_COMPUTE_BINARY \
48
+ "cannot compute difference between binary files\n"
49
+
50
+#define DIFF_CANNOT_COMPUTE_SYMLINK \
51
+ "cannot compute difference between symlink and regular file\n"
52
+
4353
#endif /* INTERFACE */
4454
4555
/*
4656
** Maximum length of a line in a text file. (8192)
4757
*/
@@ -157,10 +167,25 @@
157167
158168
/* Return results */
159169
*pnLine = nLine;
160170
return a;
161171
}
172
+
173
+/*
174
+** Returns non-zero if the specified content appears to be binary or
175
+** contains a line that is too long.
176
+*/
177
+int looks_like_binary(const char *z, int n){
178
+ int nLine;
179
+ DLine *aContent = break_into_lines(z, n, &nLine, 0);
180
+ if( aContent ){
181
+ fossil_free(aContent);
182
+ return 0;
183
+ }else{
184
+ return 1;
185
+ }
186
+}
162187
163188
/*
164189
** Return true if two DLine elements are identical.
165190
*/
166191
static int same_dline(DLine *pA, DLine *pB){
@@ -1499,14 +1524,14 @@
14991524
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
15001525
&c.nFrom, ignoreEolWs);
15011526
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
15021527
&c.nTo, ignoreEolWs);
15031528
if( c.aFrom==0 || c.aTo==0 ){
1504
- free(c.aFrom);
1505
- free(c.aTo);
1529
+ fossil_free(c.aFrom);
1530
+ fossil_free(c.aTo);
15061531
if( pOut ){
1507
- blob_appendf(pOut, "cannot compute difference between binary files\n");
1532
+ blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY);
15081533
}
15091534
return 0;
15101535
}
15111536
15121537
/* Compute the difference */
@@ -1521,13 +1546,13 @@
15211546
sbsDiff(&c, pOut, nContext, width, escHtml);
15221547
}else{
15231548
int showLn = (diffFlags & DIFF_LINENO)!=0;
15241549
contextDiff(&c, pOut, nContext, showLn, escHtml);
15251550
}
1526
- free(c.aFrom);
1527
- free(c.aTo);
1528
- free(c.aEdit);
1551
+ fossil_free(c.aFrom);
1552
+ fossil_free(c.aTo);
1553
+ fossil_free(c.aEdit);
15291554
return 0;
15301555
}else{
15311556
/* If a context diff is not requested, then return the
15321557
** array of COPY/DELETE/INSERT triples.
15331558
*/
@@ -1702,11 +1727,11 @@
17021727
}
17031728
lnTo += p->c.aEdit[i+2];
17041729
}
17051730
17061731
/* Clear out the diff results */
1707
- free(p->c.aEdit);
1732
+ fossil_free(p->c.aEdit);
17081733
p->c.aEdit = 0;
17091734
p->c.nEdit = 0;
17101735
p->c.nEditAlloc = 0;
17111736
17121737
/* Clear out the from file */
17131738
--- src/diff.c
+++ src/diff.c
@@ -38,10 +38,20 @@
38 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
39 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
40 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
41 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
42
 
 
 
 
 
 
 
 
 
 
43 #endif /* INTERFACE */
44
45 /*
46 ** Maximum length of a line in a text file. (8192)
47 */
@@ -157,10 +167,25 @@
157
158 /* Return results */
159 *pnLine = nLine;
160 return a;
161 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
163 /*
164 ** Return true if two DLine elements are identical.
165 */
166 static int same_dline(DLine *pA, DLine *pB){
@@ -1499,14 +1524,14 @@
1499 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1500 &c.nFrom, ignoreEolWs);
1501 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1502 &c.nTo, ignoreEolWs);
1503 if( c.aFrom==0 || c.aTo==0 ){
1504 free(c.aFrom);
1505 free(c.aTo);
1506 if( pOut ){
1507 blob_appendf(pOut, "cannot compute difference between binary files\n");
1508 }
1509 return 0;
1510 }
1511
1512 /* Compute the difference */
@@ -1521,13 +1546,13 @@
1521 sbsDiff(&c, pOut, nContext, width, escHtml);
1522 }else{
1523 int showLn = (diffFlags & DIFF_LINENO)!=0;
1524 contextDiff(&c, pOut, nContext, showLn, escHtml);
1525 }
1526 free(c.aFrom);
1527 free(c.aTo);
1528 free(c.aEdit);
1529 return 0;
1530 }else{
1531 /* If a context diff is not requested, then return the
1532 ** array of COPY/DELETE/INSERT triples.
1533 */
@@ -1702,11 +1727,11 @@
1702 }
1703 lnTo += p->c.aEdit[i+2];
1704 }
1705
1706 /* Clear out the diff results */
1707 free(p->c.aEdit);
1708 p->c.aEdit = 0;
1709 p->c.nEdit = 0;
1710 p->c.nEditAlloc = 0;
1711
1712 /* Clear out the from file */
1713
--- src/diff.c
+++ src/diff.c
@@ -38,10 +38,20 @@
38 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
39 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
40 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
41 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
42
43 /*
44 ** These error messages are shared in multiple locations. They are defined
45 ** here for consistency.
46 */
47 #define DIFF_CANNOT_COMPUTE_BINARY \
48 "cannot compute difference between binary files\n"
49
50 #define DIFF_CANNOT_COMPUTE_SYMLINK \
51 "cannot compute difference between symlink and regular file\n"
52
53 #endif /* INTERFACE */
54
55 /*
56 ** Maximum length of a line in a text file. (8192)
57 */
@@ -157,10 +167,25 @@
167
168 /* Return results */
169 *pnLine = nLine;
170 return a;
171 }
172
173 /*
174 ** Returns non-zero if the specified content appears to be binary or
175 ** contains a line that is too long.
176 */
177 int looks_like_binary(const char *z, int n){
178 int nLine;
179 DLine *aContent = break_into_lines(z, n, &nLine, 0);
180 if( aContent ){
181 fossil_free(aContent);
182 return 0;
183 }else{
184 return 1;
185 }
186 }
187
188 /*
189 ** Return true if two DLine elements are identical.
190 */
191 static int same_dline(DLine *pA, DLine *pB){
@@ -1499,14 +1524,14 @@
1524 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1525 &c.nFrom, ignoreEolWs);
1526 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1527 &c.nTo, ignoreEolWs);
1528 if( c.aFrom==0 || c.aTo==0 ){
1529 fossil_free(c.aFrom);
1530 fossil_free(c.aTo);
1531 if( pOut ){
1532 blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY);
1533 }
1534 return 0;
1535 }
1536
1537 /* Compute the difference */
@@ -1521,13 +1546,13 @@
1546 sbsDiff(&c, pOut, nContext, width, escHtml);
1547 }else{
1548 int showLn = (diffFlags & DIFF_LINENO)!=0;
1549 contextDiff(&c, pOut, nContext, showLn, escHtml);
1550 }
1551 fossil_free(c.aFrom);
1552 fossil_free(c.aTo);
1553 fossil_free(c.aEdit);
1554 return 0;
1555 }else{
1556 /* If a context diff is not requested, then return the
1557 ** array of COPY/DELETE/INSERT triples.
1558 */
@@ -1702,11 +1727,11 @@
1727 }
1728 lnTo += p->c.aEdit[i+2];
1729 }
1730
1731 /* Clear out the diff results */
1732 fossil_free(p->c.aEdit);
1733 p->c.aEdit = 0;
1734 p->c.nEdit = 0;
1735 p->c.nEditAlloc = 0;
1736
1737 /* Clear out the from file */
1738
+190 -23
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -69,16 +69,23 @@
6969
** The difference is the set of edits needed to transform pFile1 into
7070
** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
7171
**
7272
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
7373
** command zDiffCmd to do the diffing.
74
+**
75
+** When using an external diff program, zBinGlob contains the GLOB patterns
76
+** for file names to treat as binary. If fIncludeBinary is zero, these files
77
+** will be skipped in addition to files that may contain binary content.
7478
*/
7579
void diff_file(
7680
Blob *pFile1, /* In memory content to compare from */
81
+ int isBin1, /* Does the 'from' content appear to be binary */
7782
const char *zFile2, /* On disk content to compare to */
7883
const char *zName, /* Display name of the file */
7984
const char *zDiffCmd, /* Command for comparison */
85
+ const char *zBinGlob, /* Treat file names matching this as binary */
86
+ int fIncludeBinary, /* Include binary files for external diff */
8087
u64 diffFlags /* Flags to control the diff */
8188
){
8289
if( zDiffCmd==0 ){
8390
Blob out; /* Diff output text */
8491
Blob file2; /* Content of zFile2 */
@@ -116,10 +123,41 @@
116123
blob_reset(&file2);
117124
}else{
118125
int cnt = 0;
119126
Blob nameFile1; /* Name of temporary file to old pFile1 content */
120127
Blob cmd; /* Text of command to run */
128
+
129
+ if( !fIncludeBinary ){
130
+ Blob file2;
131
+ if( isBin1 ){
132
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
133
+ return;
134
+ }
135
+ if( zBinGlob ){
136
+ Glob *pBinary = glob_create(zBinGlob);
137
+ if( glob_match(pBinary, zName) ){
138
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
139
+ glob_free(pBinary);
140
+ return;
141
+ }
142
+ glob_free(pBinary);
143
+ }
144
+ blob_zero(&file2);
145
+ if( file_wd_size(zFile2)>=0 ){
146
+ if( file_wd_islink(zFile2) ){
147
+ blob_read_link(&file2, zFile2);
148
+ }else{
149
+ blob_read_from_file(&file2, zFile2);
150
+ }
151
+ }
152
+ if( looks_like_binary(blob_str(&file2), blob_size(&file2)) ){
153
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
154
+ blob_reset(&file2);
155
+ return;
156
+ }
157
+ blob_reset(&file2);
158
+ }
121159
122160
/* Construct a temporary file to hold pFile1 based on the name of
123161
** zFile2 */
124162
blob_zero(&nameFile1);
125163
do{
@@ -151,16 +189,24 @@
151189
** The difference is the set of edits needed to transform pFile1 into
152190
** pFile2.
153191
**
154192
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
155193
** command zDiffCmd to do the diffing.
194
+**
195
+** When using an external diff program, zBinGlob contains the GLOB patterns
196
+** for file names to treat as binary. If fIncludeBinary is zero, these files
197
+** will be skipped in addition to files that may contain binary content.
156198
*/
157199
void diff_file_mem(
158200
Blob *pFile1, /* In memory content to compare from */
159201
Blob *pFile2, /* In memory content to compare to */
202
+ int isBin1, /* Does the 'from' content appear to be binary */
203
+ int isBin2, /* Does the 'to' content appear to be binary */
160204
const char *zName, /* Display name of the file */
161205
const char *zDiffCmd, /* Command for comparison */
206
+ const char *zBinGlob, /* Treat file names matching this as binary */
207
+ int fIncludeBinary, /* Include binary files for external diff */
162208
u64 diffFlags /* Diff flags */
163209
){
164210
if( diffFlags & DIFF_BRIEF ) return;
165211
if( zDiffCmd==0 ){
166212
Blob out; /* Diff output text */
@@ -174,10 +220,26 @@
174220
blob_reset(&out);
175221
}else{
176222
Blob cmd;
177223
char zTemp1[300];
178224
char zTemp2[300];
225
+
226
+ if( !fIncludeBinary ){
227
+ if( isBin1 || isBin2 ){
228
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
229
+ return;
230
+ }
231
+ if( zBinGlob ){
232
+ Glob *pBinary = glob_create(zBinGlob);
233
+ if( glob_match(pBinary, zName) ){
234
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
235
+ glob_free(pBinary);
236
+ return;
237
+ }
238
+ glob_free(pBinary);
239
+ }
240
+ }
179241
180242
/* Construct a temporary file names */
181243
file_tempname(sizeof(zTemp1), zTemp1);
182244
file_tempname(sizeof(zTemp2), zTemp2);
183245
blob_write_to_file(pFile1, zTemp1);
@@ -201,40 +263,60 @@
201263
}
202264
203265
/*
204266
** Do a diff against a single file named in zFileTreeName from version zFrom
205267
** against the same file on disk.
268
+**
269
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
270
+** command zDiffCmd to do the diffing.
271
+**
272
+** When using an external diff program, zBinGlob contains the GLOB patterns
273
+** for file names to treat as binary. If fIncludeBinary is zero, these files
274
+** will be skipped in addition to files that may contain binary content.
206275
*/
207276
static void diff_one_against_disk(
208277
const char *zFrom, /* Name of file */
209278
const char *zDiffCmd, /* Use this "diff" command */
279
+ const char *zBinGlob, /* Treat file names matching this as binary */
280
+ int fIncludeBinary, /* Include binary files for external diff */
210281
u64 diffFlags, /* Diff control flags */
211282
const char *zFileTreeName
212283
){
213284
Blob fname;
214285
Blob content;
215286
int isLink;
287
+ int isBin;
216288
file_tree_name(zFileTreeName, &fname, 1);
217
- historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
289
+ historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
290
+ fIncludeBinary ? 0 : &isBin, 0);
218291
if( !isLink != !file_wd_islink(zFrom) ){
219
- fossil_print("cannot compute difference between "
220
- "symlink and regular file\n");
292
+ fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
221293
}else{
222
- diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
294
+ diff_file(&content, isBin, zFileTreeName, zFileTreeName,
295
+ zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
223296
}
224297
blob_reset(&content);
225298
blob_reset(&fname);
226299
}
227300
228301
/*
229302
** Run a diff between the version zFrom and files on disk. zFrom might
230303
** be NULL which means to simply show the difference between the edited
231304
** files on disk and the check-out on which they are based.
305
+**
306
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
307
+** command zDiffCmd to do the diffing.
308
+**
309
+** When using an external diff program, zBinGlob contains the GLOB patterns
310
+** for file names to treat as binary. If fIncludeBinary is zero, these files
311
+** will be skipped in addition to files that may contain binary content.
232312
*/
233313
static void diff_all_against_disk(
234314
const char *zFrom, /* Version to difference from */
235315
const char *zDiffCmd, /* Use this diff command. NULL for built-in */
316
+ const char *zBinGlob, /* Treat file names matching this as binary */
317
+ int fIncludeBinary, /* Treat file names matching this as binary */
236318
u64 diffFlags /* Flags controlling diff output */
237319
){
238320
int vid;
239321
Blob sql;
240322
Stmt q;
@@ -307,24 +389,27 @@
307389
srcid = 0;
308390
if( !asNewFile ){ showDiff = 0; }
309391
}
310392
if( showDiff ){
311393
Blob content;
394
+ int isBin;
312395
if( !isLink != !file_wd_islink(zFullName) ){
313396
diff_print_index(zPathname, diffFlags);
314397
diff_print_filenames(zPathname, zPathname, diffFlags);
315
- fossil_print("cannot compute difference between "
316
- "symlink and regular file\n");
398
+ fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
317399
continue;
318400
}
319401
if( srcid>0 ){
320402
content_get(srcid, &content);
321403
}else{
322404
blob_zero(&content);
323405
}
406
+ isBin = fIncludeBinary ? 0 : looks_like_binary(blob_str(&content),
407
+ blob_size(&content));
324408
diff_print_index(zPathname, diffFlags);
325
- diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
409
+ diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
410
+ zBinGlob, fIncludeBinary, diffFlags);
326411
blob_reset(&content);
327412
}
328413
free(zToFree);
329414
}
330415
db_finalize(&q);
@@ -332,50 +417,72 @@
332417
}
333418
334419
/*
335420
** Output the differences between two versions of a single file.
336421
** zFrom and zTo are the check-ins containing the two file versions.
422
+**
423
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
424
+** command zDiffCmd to do the diffing.
425
+**
426
+** When using an external diff program, zBinGlob contains the GLOB patterns
427
+** for file names to treat as binary. If fIncludeBinary is zero, these files
428
+** will be skipped in addition to files that may contain binary content.
337429
*/
338430
static void diff_one_two_versions(
339431
const char *zFrom,
340432
const char *zTo,
341433
const char *zDiffCmd,
434
+ const char *zBinGlob,
435
+ int fIncludeBinary,
342436
u64 diffFlags,
343437
const char *zFileTreeName
344438
){
345439
char *zName;
346440
Blob fname;
347441
Blob v1, v2;
348442
int isLink1, isLink2;
443
+ int isBin1, isBin2;
349444
if( diffFlags & DIFF_BRIEF ) return;
350445
file_tree_name(zFileTreeName, &fname, 1);
351446
zName = blob_str(&fname);
352
- historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
353
- historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
447
+ historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
448
+ fIncludeBinary ? 0 : &isBin1, 0);
449
+ historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
450
+ fIncludeBinary ? 0 : &isBin2, 0);
354451
if( isLink1 != isLink2 ){
355452
diff_print_filenames(zName, zName, diffFlags);
356
- fossil_print("cannot compute difference "
357
- " between symlink and regular file\n");
453
+ fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
358454
}else{
359
- diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
455
+ diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
456
+ zBinGlob, fIncludeBinary, diffFlags);
360457
}
361458
blob_reset(&v1);
362459
blob_reset(&v2);
363460
blob_reset(&fname);
364461
}
365462
366463
/*
367464
** Show the difference between two files identified by ManifestFile
368465
** entries.
466
+**
467
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
468
+** command zDiffCmd to do the diffing.
469
+**
470
+** When using an external diff program, zBinGlob contains the GLOB patterns
471
+** for file names to treat as binary. If fIncludeBinary is zero, these files
472
+** will be skipped in addition to files that may contain binary content.
369473
*/
370474
static void diff_manifest_entry(
371475
struct ManifestFile *pFrom,
372476
struct ManifestFile *pTo,
373477
const char *zDiffCmd,
478
+ const char *zBinGlob,
479
+ int fIncludeBinary,
374480
u64 diffFlags
375481
){
376482
Blob f1, f2;
483
+ int isBin1, isBin2;
377484
int rid;
378485
const char *zName = pFrom ? pFrom->zName : pTo->zName;
379486
if( diffFlags & DIFF_BRIEF ) return;
380487
diff_print_index(zName, diffFlags);
381488
if( pFrom ){
@@ -388,22 +495,36 @@
388495
rid = uuid_to_rid(pTo->zUuid, 0);
389496
content_get(rid, &f2);
390497
}else{
391498
blob_zero(&f2);
392499
}
393
- diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
500
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&f1),
501
+ blob_size(&f1));
502
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&f2),
503
+ blob_size(&f2));
504
+ diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
505
+ zBinGlob, fIncludeBinary, diffFlags);
394506
blob_reset(&f1);
395507
blob_reset(&f2);
396508
}
397509
398510
/*
399511
** Output the differences between two check-ins.
512
+**
513
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
514
+** command zDiffCmd to do the diffing.
515
+**
516
+** When using an external diff program, zBinGlob contains the GLOB patterns
517
+** for file names to treat as binary. If fIncludeBinary is zero, these files
518
+** will be skipped in addition to files that may contain binary content.
400519
*/
401520
static void diff_all_two_versions(
402521
const char *zFrom,
403522
const char *zTo,
404523
const char *zDiffCmd,
524
+ const char *zBinGlob,
525
+ int fIncludeBinary,
405526
u64 diffFlags
406527
){
407528
Manifest *pFrom, *pTo;
408529
ManifestFile *pFromFile, *pToFile;
409530
int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
@@ -425,17 +546,19 @@
425546
cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
426547
}
427548
if( cmp<0 ){
428549
fossil_print("DELETED %s\n", pFromFile->zName);
429550
if( asNewFlag ){
430
- diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
551
+ diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
552
+ fIncludeBinary, diffFlags);
431553
}
432554
pFromFile = manifest_file_next(pFrom,0);
433555
}else if( cmp>0 ){
434556
fossil_print("ADDED %s\n", pToFile->zName);
435557
if( asNewFlag ){
436
- diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
558
+ diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
559
+ fIncludeBinary, diffFlags);
437560
}
438561
pToFile = manifest_file_next(pTo,0);
439562
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
440563
/* No changes */
441564
pFromFile = manifest_file_next(pFrom,0);
@@ -442,11 +565,12 @@
442565
pToFile = manifest_file_next(pTo,0);
443566
}else{
444567
if( diffFlags & DIFF_BRIEF ){
445568
fossil_print("CHANGED %s\n", pFromFile->zName);
446569
}else{
447
- diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
570
+ diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
571
+ fIncludeBinary, diffFlags);
448572
}
449573
pFromFile = manifest_file_next(pFrom,0);
450574
pToFile = manifest_file_next(pTo,0);
451575
}
452576
}
@@ -544,10 +668,34 @@
544668
zTempFile = write_blob_to_temp_file(&script);
545669
zCmd = mprintf("tclsh \"%s\"", zTempFile);
546670
fossil_system(zCmd);
547671
file_delete(zTempFile);
548672
}
673
+
674
+/*
675
+** Returns non-zero if files that may be binary should be used with external
676
+** diff programs.
677
+*/
678
+int diff_include_binary_files(void){
679
+ if( is_truth(find_option("diff-binary", 0, 1)) ){
680
+ return 1;
681
+ }
682
+ if( db_get_boolean("diff-binary", 1) ){
683
+ return 1;
684
+ }
685
+ return 0;
686
+}
687
+
688
+/*
689
+** Returns the GLOB pattern for file names that should be treated as binary
690
+** by the diff subsystem, if any.
691
+*/
692
+const char *diff_get_binary_glob(void){
693
+ const char *zBinGlob = find_option("binary", 0, 1);
694
+ if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
695
+ return zBinGlob;
696
+}
549697
550698
/*
551699
** COMMAND: diff
552700
** COMMAND: gdiff
553701
**
@@ -573,10 +721,17 @@
573721
** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
574722
**
575723
** The "-N" or "--new-file" option causes the complete text of added or
576724
** deleted files to be displayed.
577725
**
726
+** The "--diff-binary" option enables or disables the inclusion of binary files
727
+** when using an external diff program.
728
+**
729
+** The "--binary" option causes files matching the glob PATTERN to be treated
730
+** as binary when considering if they should be used with external diff program.
731
+** This option overrides the "binary-glob" setting.
732
+**
578733
** Options:
579734
** --branch BRANCH Show diff of all changes on BRANCH
580735
** --brief Show filenames only
581736
** --context|-c N Use N lines of context
582737
** --from|-r VERSION select VERSION as source for the diff
@@ -585,19 +740,23 @@
585740
** --tk Launch a Tcl/Tk GUI for display
586741
** --to VERSION select VERSION as target for the diff
587742
** --side-by-side|-y side-by-side diff
588743
** --unified unified diff
589744
** --width|-W N Width of lines in side-by-side diff
745
+** --diff-binary BOOL Include binary files when using external commands
746
+** --binary PATTERN Treat files that match the glob PATTERN as binary
590747
*/
591748
void diff_cmd(void){
592749
int isGDiff; /* True for gdiff. False for normal diff */
593750
int isInternDiff; /* True for internal diff */
594751
int hasNFlag; /* True if -N or --new-file flag is used */
595752
const char *zFrom; /* Source version number */
596753
const char *zTo; /* Target version number */
597754
const char *zBranch; /* Branch to diff */
598755
const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
756
+ const char *zBinGlob = 0; /* Treat file names matching this as binary */
757
+ int fIncludeBinary = 0; /* Include binary files for external diff */
599758
u64 diffFlags = 0; /* Flags to control the DIFF */
600759
int f;
601760
602761
if( find_option("tk",0,0)!=0 ){
603762
diff_tk();
@@ -619,35 +778,43 @@
619778
zTo = zBranch;
620779
zFrom = mprintf("root:%s", zBranch);
621780
}
622781
if( zTo==0 ){
623782
db_must_be_within_tree();
624
- verify_all_options();
625783
if( !isInternDiff ){
626784
zDiffCmd = diff_command_external(isGDiff);
627785
}
786
+ zBinGlob = diff_get_binary_glob();
787
+ fIncludeBinary = diff_include_binary_files();
788
+ verify_all_options();
628789
if( g.argc>=3 ){
629790
for(f=2; f<g.argc; ++f){
630
- diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]);
791
+ diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
792
+ diffFlags, g.argv[f]);
631793
}
632794
}else{
633
- diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
795
+ diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
796
+ diffFlags);
634797
}
635798
}else if( zFrom==0 ){
636799
fossil_fatal("must use --from if --to is present");
637800
}else{
638801
db_find_and_open_repository(0, 0);
639
- verify_all_options();
640802
if( !isInternDiff ){
641803
zDiffCmd = diff_command_external(isGDiff);
642804
}
805
+ zBinGlob = diff_get_binary_glob();
806
+ fIncludeBinary = diff_include_binary_files();
807
+ verify_all_options();
643808
if( g.argc>=3 ){
644809
for(f=2; f<g.argc; ++f){
645
- diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]);
810
+ diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
811
+ diffFlags, g.argv[f]);
646812
}
647813
}else{
648
- diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
814
+ diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
815
+ diffFlags);
649816
}
650817
}
651818
}
652819
653820
/*
@@ -660,7 +827,7 @@
660827
login_check_credentials();
661828
if( !g.perm.Read ){ login_needed(); return; }
662829
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
663830
664831
cgi_set_content_type("text/plain");
665
- diff_all_two_versions(zFrom, zTo, 0, DIFF_NEWFILE);
832
+ diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE);
666833
}
667834
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -69,16 +69,23 @@
69 ** The difference is the set of edits needed to transform pFile1 into
70 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
71 **
72 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
73 ** command zDiffCmd to do the diffing.
 
 
 
 
74 */
75 void diff_file(
76 Blob *pFile1, /* In memory content to compare from */
 
77 const char *zFile2, /* On disk content to compare to */
78 const char *zName, /* Display name of the file */
79 const char *zDiffCmd, /* Command for comparison */
 
 
80 u64 diffFlags /* Flags to control the diff */
81 ){
82 if( zDiffCmd==0 ){
83 Blob out; /* Diff output text */
84 Blob file2; /* Content of zFile2 */
@@ -116,10 +123,41 @@
116 blob_reset(&file2);
117 }else{
118 int cnt = 0;
119 Blob nameFile1; /* Name of temporary file to old pFile1 content */
120 Blob cmd; /* Text of command to run */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
122 /* Construct a temporary file to hold pFile1 based on the name of
123 ** zFile2 */
124 blob_zero(&nameFile1);
125 do{
@@ -151,16 +189,24 @@
151 ** The difference is the set of edits needed to transform pFile1 into
152 ** pFile2.
153 **
154 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
155 ** command zDiffCmd to do the diffing.
 
 
 
 
156 */
157 void diff_file_mem(
158 Blob *pFile1, /* In memory content to compare from */
159 Blob *pFile2, /* In memory content to compare to */
 
 
160 const char *zName, /* Display name of the file */
161 const char *zDiffCmd, /* Command for comparison */
 
 
162 u64 diffFlags /* Diff flags */
163 ){
164 if( diffFlags & DIFF_BRIEF ) return;
165 if( zDiffCmd==0 ){
166 Blob out; /* Diff output text */
@@ -174,10 +220,26 @@
174 blob_reset(&out);
175 }else{
176 Blob cmd;
177 char zTemp1[300];
178 char zTemp2[300];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
180 /* Construct a temporary file names */
181 file_tempname(sizeof(zTemp1), zTemp1);
182 file_tempname(sizeof(zTemp2), zTemp2);
183 blob_write_to_file(pFile1, zTemp1);
@@ -201,40 +263,60 @@
201 }
202
203 /*
204 ** Do a diff against a single file named in zFileTreeName from version zFrom
205 ** against the same file on disk.
 
 
 
 
 
 
 
206 */
207 static void diff_one_against_disk(
208 const char *zFrom, /* Name of file */
209 const char *zDiffCmd, /* Use this "diff" command */
 
 
210 u64 diffFlags, /* Diff control flags */
211 const char *zFileTreeName
212 ){
213 Blob fname;
214 Blob content;
215 int isLink;
 
216 file_tree_name(zFileTreeName, &fname, 1);
217 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
 
218 if( !isLink != !file_wd_islink(zFrom) ){
219 fossil_print("cannot compute difference between "
220 "symlink and regular file\n");
221 }else{
222 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
 
223 }
224 blob_reset(&content);
225 blob_reset(&fname);
226 }
227
228 /*
229 ** Run a diff between the version zFrom and files on disk. zFrom might
230 ** be NULL which means to simply show the difference between the edited
231 ** files on disk and the check-out on which they are based.
 
 
 
 
 
 
 
232 */
233 static void diff_all_against_disk(
234 const char *zFrom, /* Version to difference from */
235 const char *zDiffCmd, /* Use this diff command. NULL for built-in */
 
 
236 u64 diffFlags /* Flags controlling diff output */
237 ){
238 int vid;
239 Blob sql;
240 Stmt q;
@@ -307,24 +389,27 @@
307 srcid = 0;
308 if( !asNewFile ){ showDiff = 0; }
309 }
310 if( showDiff ){
311 Blob content;
 
312 if( !isLink != !file_wd_islink(zFullName) ){
313 diff_print_index(zPathname, diffFlags);
314 diff_print_filenames(zPathname, zPathname, diffFlags);
315 fossil_print("cannot compute difference between "
316 "symlink and regular file\n");
317 continue;
318 }
319 if( srcid>0 ){
320 content_get(srcid, &content);
321 }else{
322 blob_zero(&content);
323 }
 
 
324 diff_print_index(zPathname, diffFlags);
325 diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
 
326 blob_reset(&content);
327 }
328 free(zToFree);
329 }
330 db_finalize(&q);
@@ -332,50 +417,72 @@
332 }
333
334 /*
335 ** Output the differences between two versions of a single file.
336 ** zFrom and zTo are the check-ins containing the two file versions.
 
 
 
 
 
 
 
337 */
338 static void diff_one_two_versions(
339 const char *zFrom,
340 const char *zTo,
341 const char *zDiffCmd,
 
 
342 u64 diffFlags,
343 const char *zFileTreeName
344 ){
345 char *zName;
346 Blob fname;
347 Blob v1, v2;
348 int isLink1, isLink2;
 
349 if( diffFlags & DIFF_BRIEF ) return;
350 file_tree_name(zFileTreeName, &fname, 1);
351 zName = blob_str(&fname);
352 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
353 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
 
 
354 if( isLink1 != isLink2 ){
355 diff_print_filenames(zName, zName, diffFlags);
356 fossil_print("cannot compute difference "
357 " between symlink and regular file\n");
358 }else{
359 diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
 
360 }
361 blob_reset(&v1);
362 blob_reset(&v2);
363 blob_reset(&fname);
364 }
365
366 /*
367 ** Show the difference between two files identified by ManifestFile
368 ** entries.
 
 
 
 
 
 
 
369 */
370 static void diff_manifest_entry(
371 struct ManifestFile *pFrom,
372 struct ManifestFile *pTo,
373 const char *zDiffCmd,
 
 
374 u64 diffFlags
375 ){
376 Blob f1, f2;
 
377 int rid;
378 const char *zName = pFrom ? pFrom->zName : pTo->zName;
379 if( diffFlags & DIFF_BRIEF ) return;
380 diff_print_index(zName, diffFlags);
381 if( pFrom ){
@@ -388,22 +495,36 @@
388 rid = uuid_to_rid(pTo->zUuid, 0);
389 content_get(rid, &f2);
390 }else{
391 blob_zero(&f2);
392 }
393 diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
 
 
 
 
 
394 blob_reset(&f1);
395 blob_reset(&f2);
396 }
397
398 /*
399 ** Output the differences between two check-ins.
 
 
 
 
 
 
 
400 */
401 static void diff_all_two_versions(
402 const char *zFrom,
403 const char *zTo,
404 const char *zDiffCmd,
 
 
405 u64 diffFlags
406 ){
407 Manifest *pFrom, *pTo;
408 ManifestFile *pFromFile, *pToFile;
409 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
@@ -425,17 +546,19 @@
425 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
426 }
427 if( cmp<0 ){
428 fossil_print("DELETED %s\n", pFromFile->zName);
429 if( asNewFlag ){
430 diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
 
431 }
432 pFromFile = manifest_file_next(pFrom,0);
433 }else if( cmp>0 ){
434 fossil_print("ADDED %s\n", pToFile->zName);
435 if( asNewFlag ){
436 diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
 
437 }
438 pToFile = manifest_file_next(pTo,0);
439 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
440 /* No changes */
441 pFromFile = manifest_file_next(pFrom,0);
@@ -442,11 +565,12 @@
442 pToFile = manifest_file_next(pTo,0);
443 }else{
444 if( diffFlags & DIFF_BRIEF ){
445 fossil_print("CHANGED %s\n", pFromFile->zName);
446 }else{
447 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
 
448 }
449 pFromFile = manifest_file_next(pFrom,0);
450 pToFile = manifest_file_next(pTo,0);
451 }
452 }
@@ -544,10 +668,34 @@
544 zTempFile = write_blob_to_temp_file(&script);
545 zCmd = mprintf("tclsh \"%s\"", zTempFile);
546 fossil_system(zCmd);
547 file_delete(zTempFile);
548 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549
550 /*
551 ** COMMAND: diff
552 ** COMMAND: gdiff
553 **
@@ -573,10 +721,17 @@
573 ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
574 **
575 ** The "-N" or "--new-file" option causes the complete text of added or
576 ** deleted files to be displayed.
577 **
 
 
 
 
 
 
 
578 ** Options:
579 ** --branch BRANCH Show diff of all changes on BRANCH
580 ** --brief Show filenames only
581 ** --context|-c N Use N lines of context
582 ** --from|-r VERSION select VERSION as source for the diff
@@ -585,19 +740,23 @@
585 ** --tk Launch a Tcl/Tk GUI for display
586 ** --to VERSION select VERSION as target for the diff
587 ** --side-by-side|-y side-by-side diff
588 ** --unified unified diff
589 ** --width|-W N Width of lines in side-by-side diff
 
 
590 */
591 void diff_cmd(void){
592 int isGDiff; /* True for gdiff. False for normal diff */
593 int isInternDiff; /* True for internal diff */
594 int hasNFlag; /* True if -N or --new-file flag is used */
595 const char *zFrom; /* Source version number */
596 const char *zTo; /* Target version number */
597 const char *zBranch; /* Branch to diff */
598 const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
 
 
599 u64 diffFlags = 0; /* Flags to control the DIFF */
600 int f;
601
602 if( find_option("tk",0,0)!=0 ){
603 diff_tk();
@@ -619,35 +778,43 @@
619 zTo = zBranch;
620 zFrom = mprintf("root:%s", zBranch);
621 }
622 if( zTo==0 ){
623 db_must_be_within_tree();
624 verify_all_options();
625 if( !isInternDiff ){
626 zDiffCmd = diff_command_external(isGDiff);
627 }
 
 
 
628 if( g.argc>=3 ){
629 for(f=2; f<g.argc; ++f){
630 diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]);
 
631 }
632 }else{
633 diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
 
634 }
635 }else if( zFrom==0 ){
636 fossil_fatal("must use --from if --to is present");
637 }else{
638 db_find_and_open_repository(0, 0);
639 verify_all_options();
640 if( !isInternDiff ){
641 zDiffCmd = diff_command_external(isGDiff);
642 }
 
 
 
643 if( g.argc>=3 ){
644 for(f=2; f<g.argc; ++f){
645 diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]);
 
646 }
647 }else{
648 diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
 
649 }
650 }
651 }
652
653 /*
@@ -660,7 +827,7 @@
660 login_check_credentials();
661 if( !g.perm.Read ){ login_needed(); return; }
662 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
663
664 cgi_set_content_type("text/plain");
665 diff_all_two_versions(zFrom, zTo, 0, DIFF_NEWFILE);
666 }
667
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -69,16 +69,23 @@
69 ** The difference is the set of edits needed to transform pFile1 into
70 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
71 **
72 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
73 ** command zDiffCmd to do the diffing.
74 **
75 ** When using an external diff program, zBinGlob contains the GLOB patterns
76 ** for file names to treat as binary. If fIncludeBinary is zero, these files
77 ** will be skipped in addition to files that may contain binary content.
78 */
79 void diff_file(
80 Blob *pFile1, /* In memory content to compare from */
81 int isBin1, /* Does the 'from' content appear to be binary */
82 const char *zFile2, /* On disk content to compare to */
83 const char *zName, /* Display name of the file */
84 const char *zDiffCmd, /* Command for comparison */
85 const char *zBinGlob, /* Treat file names matching this as binary */
86 int fIncludeBinary, /* Include binary files for external diff */
87 u64 diffFlags /* Flags to control the diff */
88 ){
89 if( zDiffCmd==0 ){
90 Blob out; /* Diff output text */
91 Blob file2; /* Content of zFile2 */
@@ -116,10 +123,41 @@
123 blob_reset(&file2);
124 }else{
125 int cnt = 0;
126 Blob nameFile1; /* Name of temporary file to old pFile1 content */
127 Blob cmd; /* Text of command to run */
128
129 if( !fIncludeBinary ){
130 Blob file2;
131 if( isBin1 ){
132 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
133 return;
134 }
135 if( zBinGlob ){
136 Glob *pBinary = glob_create(zBinGlob);
137 if( glob_match(pBinary, zName) ){
138 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
139 glob_free(pBinary);
140 return;
141 }
142 glob_free(pBinary);
143 }
144 blob_zero(&file2);
145 if( file_wd_size(zFile2)>=0 ){
146 if( file_wd_islink(zFile2) ){
147 blob_read_link(&file2, zFile2);
148 }else{
149 blob_read_from_file(&file2, zFile2);
150 }
151 }
152 if( looks_like_binary(blob_str(&file2), blob_size(&file2)) ){
153 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
154 blob_reset(&file2);
155 return;
156 }
157 blob_reset(&file2);
158 }
159
160 /* Construct a temporary file to hold pFile1 based on the name of
161 ** zFile2 */
162 blob_zero(&nameFile1);
163 do{
@@ -151,16 +189,24 @@
189 ** The difference is the set of edits needed to transform pFile1 into
190 ** pFile2.
191 **
192 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
193 ** command zDiffCmd to do the diffing.
194 **
195 ** When using an external diff program, zBinGlob contains the GLOB patterns
196 ** for file names to treat as binary. If fIncludeBinary is zero, these files
197 ** will be skipped in addition to files that may contain binary content.
198 */
199 void diff_file_mem(
200 Blob *pFile1, /* In memory content to compare from */
201 Blob *pFile2, /* In memory content to compare to */
202 int isBin1, /* Does the 'from' content appear to be binary */
203 int isBin2, /* Does the 'to' content appear to be binary */
204 const char *zName, /* Display name of the file */
205 const char *zDiffCmd, /* Command for comparison */
206 const char *zBinGlob, /* Treat file names matching this as binary */
207 int fIncludeBinary, /* Include binary files for external diff */
208 u64 diffFlags /* Diff flags */
209 ){
210 if( diffFlags & DIFF_BRIEF ) return;
211 if( zDiffCmd==0 ){
212 Blob out; /* Diff output text */
@@ -174,10 +220,26 @@
220 blob_reset(&out);
221 }else{
222 Blob cmd;
223 char zTemp1[300];
224 char zTemp2[300];
225
226 if( !fIncludeBinary ){
227 if( isBin1 || isBin2 ){
228 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
229 return;
230 }
231 if( zBinGlob ){
232 Glob *pBinary = glob_create(zBinGlob);
233 if( glob_match(pBinary, zName) ){
234 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
235 glob_free(pBinary);
236 return;
237 }
238 glob_free(pBinary);
239 }
240 }
241
242 /* Construct a temporary file names */
243 file_tempname(sizeof(zTemp1), zTemp1);
244 file_tempname(sizeof(zTemp2), zTemp2);
245 blob_write_to_file(pFile1, zTemp1);
@@ -201,40 +263,60 @@
263 }
264
265 /*
266 ** Do a diff against a single file named in zFileTreeName from version zFrom
267 ** against the same file on disk.
268 **
269 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
270 ** command zDiffCmd to do the diffing.
271 **
272 ** When using an external diff program, zBinGlob contains the GLOB patterns
273 ** for file names to treat as binary. If fIncludeBinary is zero, these files
274 ** will be skipped in addition to files that may contain binary content.
275 */
276 static void diff_one_against_disk(
277 const char *zFrom, /* Name of file */
278 const char *zDiffCmd, /* Use this "diff" command */
279 const char *zBinGlob, /* Treat file names matching this as binary */
280 int fIncludeBinary, /* Include binary files for external diff */
281 u64 diffFlags, /* Diff control flags */
282 const char *zFileTreeName
283 ){
284 Blob fname;
285 Blob content;
286 int isLink;
287 int isBin;
288 file_tree_name(zFileTreeName, &fname, 1);
289 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
290 fIncludeBinary ? 0 : &isBin, 0);
291 if( !isLink != !file_wd_islink(zFrom) ){
292 fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
 
293 }else{
294 diff_file(&content, isBin, zFileTreeName, zFileTreeName,
295 zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
296 }
297 blob_reset(&content);
298 blob_reset(&fname);
299 }
300
301 /*
302 ** Run a diff between the version zFrom and files on disk. zFrom might
303 ** be NULL which means to simply show the difference between the edited
304 ** files on disk and the check-out on which they are based.
305 **
306 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
307 ** command zDiffCmd to do the diffing.
308 **
309 ** When using an external diff program, zBinGlob contains the GLOB patterns
310 ** for file names to treat as binary. If fIncludeBinary is zero, these files
311 ** will be skipped in addition to files that may contain binary content.
312 */
313 static void diff_all_against_disk(
314 const char *zFrom, /* Version to difference from */
315 const char *zDiffCmd, /* Use this diff command. NULL for built-in */
316 const char *zBinGlob, /* Treat file names matching this as binary */
317 int fIncludeBinary, /* Treat file names matching this as binary */
318 u64 diffFlags /* Flags controlling diff output */
319 ){
320 int vid;
321 Blob sql;
322 Stmt q;
@@ -307,24 +389,27 @@
389 srcid = 0;
390 if( !asNewFile ){ showDiff = 0; }
391 }
392 if( showDiff ){
393 Blob content;
394 int isBin;
395 if( !isLink != !file_wd_islink(zFullName) ){
396 diff_print_index(zPathname, diffFlags);
397 diff_print_filenames(zPathname, zPathname, diffFlags);
398 fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
 
399 continue;
400 }
401 if( srcid>0 ){
402 content_get(srcid, &content);
403 }else{
404 blob_zero(&content);
405 }
406 isBin = fIncludeBinary ? 0 : looks_like_binary(blob_str(&content),
407 blob_size(&content));
408 diff_print_index(zPathname, diffFlags);
409 diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
410 zBinGlob, fIncludeBinary, diffFlags);
411 blob_reset(&content);
412 }
413 free(zToFree);
414 }
415 db_finalize(&q);
@@ -332,50 +417,72 @@
417 }
418
419 /*
420 ** Output the differences between two versions of a single file.
421 ** zFrom and zTo are the check-ins containing the two file versions.
422 **
423 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
424 ** command zDiffCmd to do the diffing.
425 **
426 ** When using an external diff program, zBinGlob contains the GLOB patterns
427 ** for file names to treat as binary. If fIncludeBinary is zero, these files
428 ** will be skipped in addition to files that may contain binary content.
429 */
430 static void diff_one_two_versions(
431 const char *zFrom,
432 const char *zTo,
433 const char *zDiffCmd,
434 const char *zBinGlob,
435 int fIncludeBinary,
436 u64 diffFlags,
437 const char *zFileTreeName
438 ){
439 char *zName;
440 Blob fname;
441 Blob v1, v2;
442 int isLink1, isLink2;
443 int isBin1, isBin2;
444 if( diffFlags & DIFF_BRIEF ) return;
445 file_tree_name(zFileTreeName, &fname, 1);
446 zName = blob_str(&fname);
447 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
448 fIncludeBinary ? 0 : &isBin1, 0);
449 historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
450 fIncludeBinary ? 0 : &isBin2, 0);
451 if( isLink1 != isLink2 ){
452 diff_print_filenames(zName, zName, diffFlags);
453 fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
 
454 }else{
455 diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
456 zBinGlob, fIncludeBinary, diffFlags);
457 }
458 blob_reset(&v1);
459 blob_reset(&v2);
460 blob_reset(&fname);
461 }
462
463 /*
464 ** Show the difference between two files identified by ManifestFile
465 ** entries.
466 **
467 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
468 ** command zDiffCmd to do the diffing.
469 **
470 ** When using an external diff program, zBinGlob contains the GLOB patterns
471 ** for file names to treat as binary. If fIncludeBinary is zero, these files
472 ** will be skipped in addition to files that may contain binary content.
473 */
474 static void diff_manifest_entry(
475 struct ManifestFile *pFrom,
476 struct ManifestFile *pTo,
477 const char *zDiffCmd,
478 const char *zBinGlob,
479 int fIncludeBinary,
480 u64 diffFlags
481 ){
482 Blob f1, f2;
483 int isBin1, isBin2;
484 int rid;
485 const char *zName = pFrom ? pFrom->zName : pTo->zName;
486 if( diffFlags & DIFF_BRIEF ) return;
487 diff_print_index(zName, diffFlags);
488 if( pFrom ){
@@ -388,22 +495,36 @@
495 rid = uuid_to_rid(pTo->zUuid, 0);
496 content_get(rid, &f2);
497 }else{
498 blob_zero(&f2);
499 }
500 isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&f1),
501 blob_size(&f1));
502 isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&f2),
503 blob_size(&f2));
504 diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
505 zBinGlob, fIncludeBinary, diffFlags);
506 blob_reset(&f1);
507 blob_reset(&f2);
508 }
509
510 /*
511 ** Output the differences between two check-ins.
512 **
513 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
514 ** command zDiffCmd to do the diffing.
515 **
516 ** When using an external diff program, zBinGlob contains the GLOB patterns
517 ** for file names to treat as binary. If fIncludeBinary is zero, these files
518 ** will be skipped in addition to files that may contain binary content.
519 */
520 static void diff_all_two_versions(
521 const char *zFrom,
522 const char *zTo,
523 const char *zDiffCmd,
524 const char *zBinGlob,
525 int fIncludeBinary,
526 u64 diffFlags
527 ){
528 Manifest *pFrom, *pTo;
529 ManifestFile *pFromFile, *pToFile;
530 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
@@ -425,17 +546,19 @@
546 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
547 }
548 if( cmp<0 ){
549 fossil_print("DELETED %s\n", pFromFile->zName);
550 if( asNewFlag ){
551 diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
552 fIncludeBinary, diffFlags);
553 }
554 pFromFile = manifest_file_next(pFrom,0);
555 }else if( cmp>0 ){
556 fossil_print("ADDED %s\n", pToFile->zName);
557 if( asNewFlag ){
558 diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
559 fIncludeBinary, diffFlags);
560 }
561 pToFile = manifest_file_next(pTo,0);
562 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
563 /* No changes */
564 pFromFile = manifest_file_next(pFrom,0);
@@ -442,11 +565,12 @@
565 pToFile = manifest_file_next(pTo,0);
566 }else{
567 if( diffFlags & DIFF_BRIEF ){
568 fossil_print("CHANGED %s\n", pFromFile->zName);
569 }else{
570 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
571 fIncludeBinary, diffFlags);
572 }
573 pFromFile = manifest_file_next(pFrom,0);
574 pToFile = manifest_file_next(pTo,0);
575 }
576 }
@@ -544,10 +668,34 @@
668 zTempFile = write_blob_to_temp_file(&script);
669 zCmd = mprintf("tclsh \"%s\"", zTempFile);
670 fossil_system(zCmd);
671 file_delete(zTempFile);
672 }
673
674 /*
675 ** Returns non-zero if files that may be binary should be used with external
676 ** diff programs.
677 */
678 int diff_include_binary_files(void){
679 if( is_truth(find_option("diff-binary", 0, 1)) ){
680 return 1;
681 }
682 if( db_get_boolean("diff-binary", 1) ){
683 return 1;
684 }
685 return 0;
686 }
687
688 /*
689 ** Returns the GLOB pattern for file names that should be treated as binary
690 ** by the diff subsystem, if any.
691 */
692 const char *diff_get_binary_glob(void){
693 const char *zBinGlob = find_option("binary", 0, 1);
694 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
695 return zBinGlob;
696 }
697
698 /*
699 ** COMMAND: diff
700 ** COMMAND: gdiff
701 **
@@ -573,10 +721,17 @@
721 ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
722 **
723 ** The "-N" or "--new-file" option causes the complete text of added or
724 ** deleted files to be displayed.
725 **
726 ** The "--diff-binary" option enables or disables the inclusion of binary files
727 ** when using an external diff program.
728 **
729 ** The "--binary" option causes files matching the glob PATTERN to be treated
730 ** as binary when considering if they should be used with external diff program.
731 ** This option overrides the "binary-glob" setting.
732 **
733 ** Options:
734 ** --branch BRANCH Show diff of all changes on BRANCH
735 ** --brief Show filenames only
736 ** --context|-c N Use N lines of context
737 ** --from|-r VERSION select VERSION as source for the diff
@@ -585,19 +740,23 @@
740 ** --tk Launch a Tcl/Tk GUI for display
741 ** --to VERSION select VERSION as target for the diff
742 ** --side-by-side|-y side-by-side diff
743 ** --unified unified diff
744 ** --width|-W N Width of lines in side-by-side diff
745 ** --diff-binary BOOL Include binary files when using external commands
746 ** --binary PATTERN Treat files that match the glob PATTERN as binary
747 */
748 void diff_cmd(void){
749 int isGDiff; /* True for gdiff. False for normal diff */
750 int isInternDiff; /* True for internal diff */
751 int hasNFlag; /* True if -N or --new-file flag is used */
752 const char *zFrom; /* Source version number */
753 const char *zTo; /* Target version number */
754 const char *zBranch; /* Branch to diff */
755 const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
756 const char *zBinGlob = 0; /* Treat file names matching this as binary */
757 int fIncludeBinary = 0; /* Include binary files for external diff */
758 u64 diffFlags = 0; /* Flags to control the DIFF */
759 int f;
760
761 if( find_option("tk",0,0)!=0 ){
762 diff_tk();
@@ -619,35 +778,43 @@
778 zTo = zBranch;
779 zFrom = mprintf("root:%s", zBranch);
780 }
781 if( zTo==0 ){
782 db_must_be_within_tree();
 
783 if( !isInternDiff ){
784 zDiffCmd = diff_command_external(isGDiff);
785 }
786 zBinGlob = diff_get_binary_glob();
787 fIncludeBinary = diff_include_binary_files();
788 verify_all_options();
789 if( g.argc>=3 ){
790 for(f=2; f<g.argc; ++f){
791 diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
792 diffFlags, g.argv[f]);
793 }
794 }else{
795 diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
796 diffFlags);
797 }
798 }else if( zFrom==0 ){
799 fossil_fatal("must use --from if --to is present");
800 }else{
801 db_find_and_open_repository(0, 0);
 
802 if( !isInternDiff ){
803 zDiffCmd = diff_command_external(isGDiff);
804 }
805 zBinGlob = diff_get_binary_glob();
806 fIncludeBinary = diff_include_binary_files();
807 verify_all_options();
808 if( g.argc>=3 ){
809 for(f=2; f<g.argc; ++f){
810 diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
811 diffFlags, g.argv[f]);
812 }
813 }else{
814 diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
815 diffFlags);
816 }
817 }
818 }
819
820 /*
@@ -660,7 +827,7 @@
827 login_check_credentials();
828 if( !g.perm.Read ){ login_needed(); return; }
829 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
830
831 cgi_set_content_type("text/plain");
832 diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE);
833 }
834
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -115,11 +115,11 @@
115115
Blob fname;
116116
const char *zRevision = find_option("revision", "r", 1);
117117
118118
file_tree_name(g.argv[2], &fname, 1);
119119
if( zRevision ){
120
- historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0);
120
+ historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0);
121121
}else{
122122
int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
123123
&fname, filename_collation());
124124
if( rid==0 ){
125125
fossil_fatal("no history for file: %b", &fname);
126126
--- src/finfo.c
+++ src/finfo.c
@@ -115,11 +115,11 @@
115 Blob fname;
116 const char *zRevision = find_option("revision", "r", 1);
117
118 file_tree_name(g.argv[2], &fname, 1);
119 if( zRevision ){
120 historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0);
121 }else{
122 int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
123 &fname, filename_collation());
124 if( rid==0 ){
125 fossil_fatal("no history for file: %b", &fname);
126
--- src/finfo.c
+++ src/finfo.c
@@ -115,11 +115,11 @@
115 Blob fname;
116 const char *zRevision = find_option("revision", "r", 1);
117
118 file_tree_name(g.argv[2], &fname, 1);
119 if( zRevision ){
120 historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0);
121 }else{
122 int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
123 &fname, filename_collation());
124 if( rid==0 ){
125 fossil_fatal("no history for file: %b", &fname);
126
+42 -7
--- src/stash.c
+++ src/stash.c
@@ -267,11 +267,17 @@
267267
}
268268
269269
/*
270270
** Show the diffs associate with a single stash.
271271
*/
272
-static void stash_diff(int stashid, const char *zDiffCmd, u64 diffFlags){
272
+static void stash_diff(
273
+ int stashid,
274
+ const char *zDiffCmd,
275
+ const char *zBinGlob,
276
+ int fIncludeBinary,
277
+ u64 diffFlags
278
+){
273279
Stmt q;
274280
Blob empty;
275281
blob_zero(&empty);
276282
db_prepare(&q,
277283
"SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -280,28 +286,39 @@
280286
);
281287
while( db_step(&q)==SQLITE_ROW ){
282288
int rid = db_column_int(&q, 0);
283289
int isRemoved = db_column_int(&q, 1);
284290
int isLink = db_column_int(&q, 3);
291
+ int isBin1, isBin2;
285292
const char *zOrig = db_column_text(&q, 4);
286293
const char *zNew = db_column_text(&q, 5);
287294
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
288295
Blob delta;
289296
if( rid==0 ){
290297
db_ephemeral_blob(&q, 6, &delta);
291298
fossil_print("ADDED %s\n", zNew);
292299
diff_print_index(zNew, diffFlags);
293
- diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags);
300
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&empty),
301
+ blob_size(&empty));
302
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&delta),
303
+ blob_size(&delta));
304
+ diff_file_mem(&empty, &delta, isBin1, isBin2, zNew, zDiffCmd,
305
+ zBinGlob, fIncludeBinary, diffFlags);
294306
}else if( isRemoved ){
295307
fossil_print("DELETE %s\n", zOrig);
296308
if( file_wd_islink(zOPath) ){
297309
blob_read_link(&delta, zOPath);
298310
}else{
299311
blob_read_from_file(&delta, zOPath);
300312
}
301313
diff_print_index(zNew, diffFlags);
302
- diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags);
314
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&delta),
315
+ blob_size(&delta));
316
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&empty),
317
+ blob_size(&empty));
318
+ diff_file_mem(&delta, &empty, isBin1, isBin2, zOrig, zDiffCmd,
319
+ zBinGlob, fIncludeBinary, diffFlags);
303320
}else{
304321
Blob a, b, disk;
305322
int isOrigLink = file_wd_islink(zOPath);
306323
db_ephemeral_blob(&q, 6, &delta);
307324
if( isOrigLink ){
@@ -311,15 +328,20 @@
311328
}
312329
fossil_print("CHANGED %s\n", zNew);
313330
if( !isOrigLink != !isLink ){
314331
diff_print_index(zNew, diffFlags);
315332
diff_print_filenames(zOrig, zNew, diffFlags);
316
- printf("cannot compute difference between symlink and regular file\n");
333
+ printf(DIFF_CANNOT_COMPUTE_SYMLINK);
317334
}else{
318335
content_get(rid, &a);
319336
blob_delta_apply(&a, &delta, &b);
320
- diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags);
337
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&disk),
338
+ blob_size(&disk));
339
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&b),
340
+ blob_size(&b));
341
+ diff_file_mem(&disk, &b, isBin1, isBin2, zNew, zDiffCmd,
342
+ zBinGlob, fIncludeBinary, diffFlags);
321343
blob_reset(&a);
322344
blob_reset(&b);
323345
}
324346
blob_reset(&disk);
325347
}
@@ -413,10 +435,11 @@
413435
int nCmd;
414436
int stashid;
415437
416438
undo_capture_command_line();
417439
db_must_be_within_tree();
440
+ db_open_config(0);
418441
db_begin_transaction();
419442
zDb = db_name("localdb");
420443
db_multi_exec(zStashInit, zDb, zDb);
421444
if( g.argc<=2 ){
422445
zCmd = "save";
@@ -550,21 +573,33 @@
550573
stashid);
551574
undo_finish();
552575
}else
553576
if( memcmp(zCmd, "diff", nCmd)==0 ){
554577
const char *zDiffCmd = diff_command_external(0);
578
+ const char *zBinGlob = 0;
579
+ int fIncludeBinary = 0;
555580
u64 diffFlags = diff_options();
556581
if( g.argc>4 ) usage("diff STASHID");
582
+ if( zDiffCmd ){
583
+ zBinGlob = diff_get_binary_glob();
584
+ fIncludeBinary = diff_include_binary_files();
585
+ }
557586
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
558
- stash_diff(stashid, zDiffCmd, diffFlags);
587
+ stash_diff(stashid, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
559588
}else
560589
if( memcmp(zCmd, "gdiff", nCmd)==0 ){
561590
const char *zDiffCmd = diff_command_external(1);
591
+ const char *zBinGlob = 0;
592
+ int fIncludeBinary = 0;
562593
u64 diffFlags = diff_options();
563594
if( g.argc>4 ) usage("gdiff STASHID");
595
+ if( zDiffCmd ){
596
+ zBinGlob = diff_get_binary_glob();
597
+ fIncludeBinary = diff_include_binary_files();
598
+ }
564599
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
565
- stash_diff(stashid, zDiffCmd, diffFlags);
600
+ stash_diff(stashid, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
566601
}else
567602
if( memcmp(zCmd, "help", nCmd)==0 ){
568603
g.argv[1] = "help";
569604
g.argv[2] = "stash";
570605
g.argc = 3;
571606
--- src/stash.c
+++ src/stash.c
@@ -267,11 +267,17 @@
267 }
268
269 /*
270 ** Show the diffs associate with a single stash.
271 */
272 static void stash_diff(int stashid, const char *zDiffCmd, u64 diffFlags){
 
 
 
 
 
 
273 Stmt q;
274 Blob empty;
275 blob_zero(&empty);
276 db_prepare(&q,
277 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -280,28 +286,39 @@
280 );
281 while( db_step(&q)==SQLITE_ROW ){
282 int rid = db_column_int(&q, 0);
283 int isRemoved = db_column_int(&q, 1);
284 int isLink = db_column_int(&q, 3);
 
285 const char *zOrig = db_column_text(&q, 4);
286 const char *zNew = db_column_text(&q, 5);
287 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
288 Blob delta;
289 if( rid==0 ){
290 db_ephemeral_blob(&q, 6, &delta);
291 fossil_print("ADDED %s\n", zNew);
292 diff_print_index(zNew, diffFlags);
293 diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags);
 
 
 
 
 
294 }else if( isRemoved ){
295 fossil_print("DELETE %s\n", zOrig);
296 if( file_wd_islink(zOPath) ){
297 blob_read_link(&delta, zOPath);
298 }else{
299 blob_read_from_file(&delta, zOPath);
300 }
301 diff_print_index(zNew, diffFlags);
302 diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags);
 
 
 
 
 
303 }else{
304 Blob a, b, disk;
305 int isOrigLink = file_wd_islink(zOPath);
306 db_ephemeral_blob(&q, 6, &delta);
307 if( isOrigLink ){
@@ -311,15 +328,20 @@
311 }
312 fossil_print("CHANGED %s\n", zNew);
313 if( !isOrigLink != !isLink ){
314 diff_print_index(zNew, diffFlags);
315 diff_print_filenames(zOrig, zNew, diffFlags);
316 printf("cannot compute difference between symlink and regular file\n");
317 }else{
318 content_get(rid, &a);
319 blob_delta_apply(&a, &delta, &b);
320 diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags);
 
 
 
 
 
321 blob_reset(&a);
322 blob_reset(&b);
323 }
324 blob_reset(&disk);
325 }
@@ -413,10 +435,11 @@
413 int nCmd;
414 int stashid;
415
416 undo_capture_command_line();
417 db_must_be_within_tree();
 
418 db_begin_transaction();
419 zDb = db_name("localdb");
420 db_multi_exec(zStashInit, zDb, zDb);
421 if( g.argc<=2 ){
422 zCmd = "save";
@@ -550,21 +573,33 @@
550 stashid);
551 undo_finish();
552 }else
553 if( memcmp(zCmd, "diff", nCmd)==0 ){
554 const char *zDiffCmd = diff_command_external(0);
 
 
555 u64 diffFlags = diff_options();
556 if( g.argc>4 ) usage("diff STASHID");
 
 
 
 
557 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
558 stash_diff(stashid, zDiffCmd, diffFlags);
559 }else
560 if( memcmp(zCmd, "gdiff", nCmd)==0 ){
561 const char *zDiffCmd = diff_command_external(1);
 
 
562 u64 diffFlags = diff_options();
563 if( g.argc>4 ) usage("gdiff STASHID");
 
 
 
 
564 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
565 stash_diff(stashid, zDiffCmd, diffFlags);
566 }else
567 if( memcmp(zCmd, "help", nCmd)==0 ){
568 g.argv[1] = "help";
569 g.argv[2] = "stash";
570 g.argc = 3;
571
--- src/stash.c
+++ src/stash.c
@@ -267,11 +267,17 @@
267 }
268
269 /*
270 ** Show the diffs associate with a single stash.
271 */
272 static void stash_diff(
273 int stashid,
274 const char *zDiffCmd,
275 const char *zBinGlob,
276 int fIncludeBinary,
277 u64 diffFlags
278 ){
279 Stmt q;
280 Blob empty;
281 blob_zero(&empty);
282 db_prepare(&q,
283 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -280,28 +286,39 @@
286 );
287 while( db_step(&q)==SQLITE_ROW ){
288 int rid = db_column_int(&q, 0);
289 int isRemoved = db_column_int(&q, 1);
290 int isLink = db_column_int(&q, 3);
291 int isBin1, isBin2;
292 const char *zOrig = db_column_text(&q, 4);
293 const char *zNew = db_column_text(&q, 5);
294 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
295 Blob delta;
296 if( rid==0 ){
297 db_ephemeral_blob(&q, 6, &delta);
298 fossil_print("ADDED %s\n", zNew);
299 diff_print_index(zNew, diffFlags);
300 isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&empty),
301 blob_size(&empty));
302 isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&delta),
303 blob_size(&delta));
304 diff_file_mem(&empty, &delta, isBin1, isBin2, zNew, zDiffCmd,
305 zBinGlob, fIncludeBinary, diffFlags);
306 }else if( isRemoved ){
307 fossil_print("DELETE %s\n", zOrig);
308 if( file_wd_islink(zOPath) ){
309 blob_read_link(&delta, zOPath);
310 }else{
311 blob_read_from_file(&delta, zOPath);
312 }
313 diff_print_index(zNew, diffFlags);
314 isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&delta),
315 blob_size(&delta));
316 isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&empty),
317 blob_size(&empty));
318 diff_file_mem(&delta, &empty, isBin1, isBin2, zOrig, zDiffCmd,
319 zBinGlob, fIncludeBinary, diffFlags);
320 }else{
321 Blob a, b, disk;
322 int isOrigLink = file_wd_islink(zOPath);
323 db_ephemeral_blob(&q, 6, &delta);
324 if( isOrigLink ){
@@ -311,15 +328,20 @@
328 }
329 fossil_print("CHANGED %s\n", zNew);
330 if( !isOrigLink != !isLink ){
331 diff_print_index(zNew, diffFlags);
332 diff_print_filenames(zOrig, zNew, diffFlags);
333 printf(DIFF_CANNOT_COMPUTE_SYMLINK);
334 }else{
335 content_get(rid, &a);
336 blob_delta_apply(&a, &delta, &b);
337 isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&disk),
338 blob_size(&disk));
339 isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&b),
340 blob_size(&b));
341 diff_file_mem(&disk, &b, isBin1, isBin2, zNew, zDiffCmd,
342 zBinGlob, fIncludeBinary, diffFlags);
343 blob_reset(&a);
344 blob_reset(&b);
345 }
346 blob_reset(&disk);
347 }
@@ -413,10 +435,11 @@
435 int nCmd;
436 int stashid;
437
438 undo_capture_command_line();
439 db_must_be_within_tree();
440 db_open_config(0);
441 db_begin_transaction();
442 zDb = db_name("localdb");
443 db_multi_exec(zStashInit, zDb, zDb);
444 if( g.argc<=2 ){
445 zCmd = "save";
@@ -550,21 +573,33 @@
573 stashid);
574 undo_finish();
575 }else
576 if( memcmp(zCmd, "diff", nCmd)==0 ){
577 const char *zDiffCmd = diff_command_external(0);
578 const char *zBinGlob = 0;
579 int fIncludeBinary = 0;
580 u64 diffFlags = diff_options();
581 if( g.argc>4 ) usage("diff STASHID");
582 if( zDiffCmd ){
583 zBinGlob = diff_get_binary_glob();
584 fIncludeBinary = diff_include_binary_files();
585 }
586 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
587 stash_diff(stashid, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
588 }else
589 if( memcmp(zCmd, "gdiff", nCmd)==0 ){
590 const char *zDiffCmd = diff_command_external(1);
591 const char *zBinGlob = 0;
592 int fIncludeBinary = 0;
593 u64 diffFlags = diff_options();
594 if( g.argc>4 ) usage("gdiff STASHID");
595 if( zDiffCmd ){
596 zBinGlob = diff_get_binary_glob();
597 fIncludeBinary = diff_include_binary_files();
598 }
599 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
600 stash_diff(stashid, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
601 }else
602 if( memcmp(zCmd, "help", nCmd)==0 ){
603 g.argv[1] = "help";
604 g.argv[2] = "stash";
605 g.argc = 3;
606
+9 -3
--- src/update.c
+++ src/update.c
@@ -595,12 +595,13 @@
595595
*/
596596
int historical_version_of_file(
597597
const char *revision, /* The checkin containing the file */
598598
const char *file, /* Full treename of the file */
599599
Blob *content, /* Put the content here */
600
- int *pIsLink, /* Set to true if file is link. */
600
+ int *pIsLink, /* Set to true if file is link. */
601601
int *pIsExe, /* Set to true if file is executable */
602
+ int *pIsBin, /* Set to true if file is binary */
602603
int errCode /* Error code if file not found. Panic if 0. */
603604
){
604605
Manifest *pManifest;
605606
ManifestFile *pFile;
606607
int rid=0;
@@ -617,15 +618,20 @@
617618
pManifest = manifest_get(rid, CFTYPE_MANIFEST);
618619
619620
if( pManifest ){
620621
pFile = manifest_file_find(pManifest, file);
621622
if( pFile ){
623
+ int rc;
622624
rid = uuid_to_rid(pFile->zUuid, 0);
623625
if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
624626
if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
625627
manifest_destroy(pManifest);
626
- return content_get(rid, content);
628
+ rc = content_get(rid, content);
629
+ if( rc && pIsBin ){
630
+ *pIsBin = looks_like_binary(blob_str(content), blob_size(content));
631
+ }
632
+ return rc;
627633
}
628634
manifest_destroy(pManifest);
629635
if( errCode<=0 ){
630636
fossil_fatal("file %s does not exist in checkin: %s", file, revision);
631637
}
@@ -712,11 +718,11 @@
712718
int isLink = 0;
713719
char *zFull;
714720
zFile = db_column_text(&q, 0);
715721
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
716722
errCode = historical_version_of_file(zRevision, zFile, &record,
717
- &isLink, &isExe,2);
723
+ &isLink, &isExe, 0, 2);
718724
if( errCode==2 ){
719725
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
720726
fossil_print("UNMANAGE: %s\n", zFile);
721727
}else{
722728
undo_save(zFile);
723729
--- src/update.c
+++ src/update.c
@@ -595,12 +595,13 @@
595 */
596 int historical_version_of_file(
597 const char *revision, /* The checkin containing the file */
598 const char *file, /* Full treename of the file */
599 Blob *content, /* Put the content here */
600 int *pIsLink, /* Set to true if file is link. */
601 int *pIsExe, /* Set to true if file is executable */
 
602 int errCode /* Error code if file not found. Panic if 0. */
603 ){
604 Manifest *pManifest;
605 ManifestFile *pFile;
606 int rid=0;
@@ -617,15 +618,20 @@
617 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
618
619 if( pManifest ){
620 pFile = manifest_file_find(pManifest, file);
621 if( pFile ){
 
622 rid = uuid_to_rid(pFile->zUuid, 0);
623 if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
624 if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
625 manifest_destroy(pManifest);
626 return content_get(rid, content);
 
 
 
 
627 }
628 manifest_destroy(pManifest);
629 if( errCode<=0 ){
630 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
631 }
@@ -712,11 +718,11 @@
712 int isLink = 0;
713 char *zFull;
714 zFile = db_column_text(&q, 0);
715 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
716 errCode = historical_version_of_file(zRevision, zFile, &record,
717 &isLink, &isExe,2);
718 if( errCode==2 ){
719 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
720 fossil_print("UNMANAGE: %s\n", zFile);
721 }else{
722 undo_save(zFile);
723
--- src/update.c
+++ src/update.c
@@ -595,12 +595,13 @@
595 */
596 int historical_version_of_file(
597 const char *revision, /* The checkin containing the file */
598 const char *file, /* Full treename of the file */
599 Blob *content, /* Put the content here */
600 int *pIsLink, /* Set to true if file is link. */
601 int *pIsExe, /* Set to true if file is executable */
602 int *pIsBin, /* Set to true if file is binary */
603 int errCode /* Error code if file not found. Panic if 0. */
604 ){
605 Manifest *pManifest;
606 ManifestFile *pFile;
607 int rid=0;
@@ -617,15 +618,20 @@
618 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
619
620 if( pManifest ){
621 pFile = manifest_file_find(pManifest, file);
622 if( pFile ){
623 int rc;
624 rid = uuid_to_rid(pFile->zUuid, 0);
625 if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
626 if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
627 manifest_destroy(pManifest);
628 rc = content_get(rid, content);
629 if( rc && pIsBin ){
630 *pIsBin = looks_like_binary(blob_str(content), blob_size(content));
631 }
632 return rc;
633 }
634 manifest_destroy(pManifest);
635 if( errCode<=0 ){
636 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
637 }
@@ -712,11 +718,11 @@
718 int isLink = 0;
719 char *zFull;
720 zFile = db_column_text(&q, 0);
721 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
722 errCode = historical_version_of_file(zRevision, zFile, &record,
723 &isLink, &isExe, 0, 2);
724 if( errCode==2 ){
725 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
726 fossil_print("UNMANAGE: %s\n", zFile);
727 }else{
728 undo_save(zFile);
729

Keyboard Shortcuts

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