Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -2014,10 +2014,11 @@ { "binary-glob", 0, 32, 1, "" }, { "clearsign", 0, 0, 0, "off" }, { "case-sensitive",0, 0, 0, "on" }, { "crnl-glob", 0, 16, 1, "" }, { "default-perms", 0, 16, 0, "u" }, + { "diff-binary", 0, 0, 0, "on" }, { "diff-command", 0, 16, 0, "" }, { "dont-push", 0, 0, 0, "off" }, { "editor", 0, 16, 0, "" }, { "gdiff-command", 0, 16, 0, "gdiff" }, { "gmerge-command",0, 40, 0, "" }, @@ -2106,10 +2107,14 @@ ** Set to "*" to disable CR+NL checking. ** ** default-perms Permissions given automatically to new users. For more ** information on permissions see Users page in Server ** Administration of the HTTP UI. Default: u. +** +** diff-binary If TRUE (the default), permit files that may be binary +** or that match the "binary-glob" setting to be used with +** external diff programs. If FALSE, skip these files. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to Index: src/diff.c ================================================================== --- src/diff.c +++ src/diff.c @@ -38,10 +38,20 @@ #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */ #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */ #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ +/* +** These error messages are shared in multiple locations. They are defined +** here for consistency. +*/ +#define DIFF_CANNOT_COMPUTE_BINARY \ + "cannot compute difference between binary files\n" + +#define DIFF_CANNOT_COMPUTE_SYMLINK \ + "cannot compute difference between symlink and regular file\n" + #endif /* INTERFACE */ /* ** Maximum length of a line in a text file. (8192) */ @@ -157,10 +167,25 @@ /* Return results */ *pnLine = nLine; return a; } + +/* +** Returns non-zero if the specified content appears to be binary or +** contains a line that is too long. +*/ +int looks_like_binary(const char *z, int n){ + int nLine; + DLine *aContent = break_into_lines(z, n, &nLine, 0); + if( aContent ){ + fossil_free(aContent); + return 0; + }else{ + return 1; + } +} /* ** Return true if two DLine elements are identical. */ static int same_dline(DLine *pA, DLine *pB){ @@ -1499,14 +1524,14 @@ c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom, ignoreEolWs); c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo, ignoreEolWs); if( c.aFrom==0 || c.aTo==0 ){ - free(c.aFrom); - free(c.aTo); + fossil_free(c.aFrom); + fossil_free(c.aTo); if( pOut ){ - blob_appendf(pOut, "cannot compute difference between binary files\n"); + blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY); } return 0; } /* Compute the difference */ @@ -1521,13 +1546,13 @@ sbsDiff(&c, pOut, nContext, width, escHtml); }else{ int showLn = (diffFlags & DIFF_LINENO)!=0; contextDiff(&c, pOut, nContext, showLn, escHtml); } - free(c.aFrom); - free(c.aTo); - free(c.aEdit); + fossil_free(c.aFrom); + fossil_free(c.aTo); + fossil_free(c.aEdit); return 0; }else{ /* If a context diff is not requested, then return the ** array of COPY/DELETE/INSERT triples. */ @@ -1702,11 +1727,11 @@ } lnTo += p->c.aEdit[i+2]; } /* Clear out the diff results */ - free(p->c.aEdit); + fossil_free(p->c.aEdit); p->c.aEdit = 0; p->c.nEdit = 0; p->c.nEditAlloc = 0; /* Clear out the from file */ Index: src/diffcmd.c ================================================================== --- src/diffcmd.c +++ src/diffcmd.c @@ -69,16 +69,23 @@ ** The difference is the set of edits needed to transform pFile1 into ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ void diff_file( Blob *pFile1, /* In memory content to compare from */ + int isBin1, /* Does the 'from' content appear to be binary */ const char *zFile2, /* On disk content to compare to */ const char *zName, /* Display name of the file */ const char *zDiffCmd, /* Command for comparison */ + const char *zBinGlob, /* Treat file names matching this as binary */ + int fIncludeBinary, /* Include binary files for external diff */ u64 diffFlags /* Flags to control the diff */ ){ if( zDiffCmd==0 ){ Blob out; /* Diff output text */ Blob file2; /* Content of zFile2 */ @@ -116,10 +123,41 @@ blob_reset(&file2); }else{ int cnt = 0; Blob nameFile1; /* Name of temporary file to old pFile1 content */ Blob cmd; /* Text of command to run */ + + if( !fIncludeBinary ){ + Blob file2; + if( isBin1 ){ + fossil_print(DIFF_CANNOT_COMPUTE_BINARY); + return; + } + if( zBinGlob ){ + Glob *pBinary = glob_create(zBinGlob); + if( glob_match(pBinary, zName) ){ + fossil_print(DIFF_CANNOT_COMPUTE_BINARY); + glob_free(pBinary); + return; + } + glob_free(pBinary); + } + blob_zero(&file2); + if( file_wd_size(zFile2)>=0 ){ + if( file_wd_islink(zFile2) ){ + blob_read_link(&file2, zFile2); + }else{ + blob_read_from_file(&file2, zFile2); + } + } + if( looks_like_binary(blob_str(&file2), blob_size(&file2)) ){ + fossil_print(DIFF_CANNOT_COMPUTE_BINARY); + blob_reset(&file2); + return; + } + blob_reset(&file2); + } /* Construct a temporary file to hold pFile1 based on the name of ** zFile2 */ blob_zero(&nameFile1); do{ @@ -151,16 +189,24 @@ ** The difference is the set of edits needed to transform pFile1 into ** pFile2. ** ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the ** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ void diff_file_mem( Blob *pFile1, /* In memory content to compare from */ Blob *pFile2, /* In memory content to compare to */ + int isBin1, /* Does the 'from' content appear to be binary */ + int isBin2, /* Does the 'to' content appear to be binary */ const char *zName, /* Display name of the file */ const char *zDiffCmd, /* Command for comparison */ + const char *zBinGlob, /* Treat file names matching this as binary */ + int fIncludeBinary, /* Include binary files for external diff */ u64 diffFlags /* Diff flags */ ){ if( diffFlags & DIFF_BRIEF ) return; if( zDiffCmd==0 ){ Blob out; /* Diff output text */ @@ -174,10 +220,26 @@ blob_reset(&out); }else{ Blob cmd; char zTemp1[300]; char zTemp2[300]; + + if( !fIncludeBinary ){ + if( isBin1 || isBin2 ){ + fossil_print(DIFF_CANNOT_COMPUTE_BINARY); + return; + } + if( zBinGlob ){ + Glob *pBinary = glob_create(zBinGlob); + if( glob_match(pBinary, zName) ){ + fossil_print(DIFF_CANNOT_COMPUTE_BINARY); + glob_free(pBinary); + return; + } + glob_free(pBinary); + } + } /* Construct a temporary file names */ file_tempname(sizeof(zTemp1), zTemp1); file_tempname(sizeof(zTemp2), zTemp2); blob_write_to_file(pFile1, zTemp1); @@ -201,40 +263,60 @@ } /* ** Do a diff against a single file named in zFileTreeName from version zFrom ** against the same file on disk. +** +** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the +** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ static void diff_one_against_disk( const char *zFrom, /* Name of file */ const char *zDiffCmd, /* Use this "diff" command */ + const char *zBinGlob, /* Treat file names matching this as binary */ + int fIncludeBinary, /* Include binary files for external diff */ u64 diffFlags, /* Diff control flags */ const char *zFileTreeName ){ Blob fname; Blob content; int isLink; + int isBin; file_tree_name(zFileTreeName, &fname, 1); - historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0); + historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, + fIncludeBinary ? 0 : &isBin, 0); if( !isLink != !file_wd_islink(zFrom) ){ - fossil_print("cannot compute difference between " - "symlink and regular file\n"); + fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK); }else{ - diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags); + diff_file(&content, isBin, zFileTreeName, zFileTreeName, + zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); } blob_reset(&content); blob_reset(&fname); } /* ** Run a diff between the version zFrom and files on disk. zFrom might ** be NULL which means to simply show the difference between the edited ** files on disk and the check-out on which they are based. +** +** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the +** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ static void diff_all_against_disk( const char *zFrom, /* Version to difference from */ const char *zDiffCmd, /* Use this diff command. NULL for built-in */ + const char *zBinGlob, /* Treat file names matching this as binary */ + int fIncludeBinary, /* Treat file names matching this as binary */ u64 diffFlags /* Flags controlling diff output */ ){ int vid; Blob sql; Stmt q; @@ -307,24 +389,27 @@ srcid = 0; if( !asNewFile ){ showDiff = 0; } } if( showDiff ){ Blob content; + int isBin; if( !isLink != !file_wd_islink(zFullName) ){ diff_print_index(zPathname, diffFlags); diff_print_filenames(zPathname, zPathname, diffFlags); - fossil_print("cannot compute difference between " - "symlink and regular file\n"); + fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK); continue; } if( srcid>0 ){ content_get(srcid, &content); }else{ blob_zero(&content); } + isBin = fIncludeBinary ? 0 : looks_like_binary(blob_str(&content), + blob_size(&content)); diff_print_index(zPathname, diffFlags); - diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags); + diff_file(&content, isBin, zFullName, zPathname, zDiffCmd, + zBinGlob, fIncludeBinary, diffFlags); blob_reset(&content); } free(zToFree); } db_finalize(&q); @@ -332,50 +417,72 @@ } /* ** Output the differences between two versions of a single file. ** zFrom and zTo are the check-ins containing the two file versions. +** +** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the +** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ static void diff_one_two_versions( const char *zFrom, const char *zTo, const char *zDiffCmd, + const char *zBinGlob, + int fIncludeBinary, u64 diffFlags, const char *zFileTreeName ){ char *zName; Blob fname; Blob v1, v2; int isLink1, isLink2; + int isBin1, isBin2; if( diffFlags & DIFF_BRIEF ) return; file_tree_name(zFileTreeName, &fname, 1); zName = blob_str(&fname); - historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0); - historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0); + historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, + fIncludeBinary ? 0 : &isBin1, 0); + historical_version_of_file(zTo, zName, &v2, &isLink2, 0, + fIncludeBinary ? 0 : &isBin2, 0); if( isLink1 != isLink2 ){ diff_print_filenames(zName, zName, diffFlags); - fossil_print("cannot compute difference " - " between symlink and regular file\n"); + fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK); }else{ - diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags); + diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd, + zBinGlob, fIncludeBinary, diffFlags); } blob_reset(&v1); blob_reset(&v2); blob_reset(&fname); } /* ** Show the difference between two files identified by ManifestFile ** entries. +** +** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the +** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ static void diff_manifest_entry( struct ManifestFile *pFrom, struct ManifestFile *pTo, const char *zDiffCmd, + const char *zBinGlob, + int fIncludeBinary, u64 diffFlags ){ Blob f1, f2; + int isBin1, isBin2; int rid; const char *zName = pFrom ? pFrom->zName : pTo->zName; if( diffFlags & DIFF_BRIEF ) return; diff_print_index(zName, diffFlags); if( pFrom ){ @@ -388,22 +495,36 @@ rid = uuid_to_rid(pTo->zUuid, 0); content_get(rid, &f2); }else{ blob_zero(&f2); } - diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags); + isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&f1), + blob_size(&f1)); + isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&f2), + blob_size(&f2)); + diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd, + zBinGlob, fIncludeBinary, diffFlags); blob_reset(&f1); blob_reset(&f2); } /* ** Output the differences between two check-ins. +** +** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the +** command zDiffCmd to do the diffing. +** +** When using an external diff program, zBinGlob contains the GLOB patterns +** for file names to treat as binary. If fIncludeBinary is zero, these files +** will be skipped in addition to files that may contain binary content. */ static void diff_all_two_versions( const char *zFrom, const char *zTo, const char *zDiffCmd, + const char *zBinGlob, + int fIncludeBinary, u64 diffFlags ){ Manifest *pFrom, *pTo; ManifestFile *pFromFile, *pToFile; int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0; @@ -425,17 +546,19 @@ cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); } if( cmp<0 ){ fossil_print("DELETED %s\n", pFromFile->zName); if( asNewFlag ){ - diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags); + diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob, + fIncludeBinary, diffFlags); } pFromFile = manifest_file_next(pFrom,0); }else if( cmp>0 ){ fossil_print("ADDED %s\n", pToFile->zName); if( asNewFlag ){ - diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags); + diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob, + fIncludeBinary, diffFlags); } pToFile = manifest_file_next(pTo,0); }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ /* No changes */ pFromFile = manifest_file_next(pFrom,0); @@ -442,11 +565,12 @@ pToFile = manifest_file_next(pTo,0); }else{ if( diffFlags & DIFF_BRIEF ){ fossil_print("CHANGED %s\n", pFromFile->zName); }else{ - diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags); + diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob, + fIncludeBinary, diffFlags); } pFromFile = manifest_file_next(pFrom,0); pToFile = manifest_file_next(pTo,0); } } @@ -544,10 +668,34 @@ zTempFile = write_blob_to_temp_file(&script); zCmd = mprintf("tclsh \"%s\"", zTempFile); fossil_system(zCmd); file_delete(zTempFile); } + +/* +** Returns non-zero if files that may be binary should be used with external +** diff programs. +*/ +int diff_include_binary_files(void){ + if( is_truth(find_option("diff-binary", 0, 1)) ){ + return 1; + } + if( db_get_boolean("diff-binary", 1) ){ + return 1; + } + return 0; +} + +/* +** Returns the GLOB pattern for file names that should be treated as binary +** by the diff subsystem, if any. +*/ +const char *diff_get_binary_glob(void){ + const char *zBinGlob = find_option("binary", 0, 1); + if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); + return zBinGlob; +} /* ** COMMAND: diff ** COMMAND: gdiff ** @@ -573,10 +721,17 @@ ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". ** ** The "-N" or "--new-file" option causes the complete text of added or ** deleted files to be displayed. ** +** The "--diff-binary" option enables or disables the inclusion of binary files +** when using an external diff program. +** +** The "--binary" option causes files matching the glob PATTERN to be treated +** as binary when considering if they should be used with external diff program. +** This option overrides the "binary-glob" setting. +** ** Options: ** --branch BRANCH Show diff of all changes on BRANCH ** --brief Show filenames only ** --context|-c N Use N lines of context ** --from|-r VERSION select VERSION as source for the diff @@ -585,19 +740,23 @@ ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION select VERSION as target for the diff ** --side-by-side|-y side-by-side diff ** --unified unified diff ** --width|-W N Width of lines in side-by-side diff +** --diff-binary BOOL Include binary files when using external commands +** --binary PATTERN Treat files that match the glob PATTERN as binary */ void diff_cmd(void){ int isGDiff; /* True for gdiff. False for normal diff */ int isInternDiff; /* True for internal diff */ int hasNFlag; /* True if -N or --new-file flag is used */ const char *zFrom; /* Source version number */ const char *zTo; /* Target version number */ const char *zBranch; /* Branch to diff */ const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */ + const char *zBinGlob = 0; /* Treat file names matching this as binary */ + int fIncludeBinary = 0; /* Include binary files for external diff */ u64 diffFlags = 0; /* Flags to control the DIFF */ int f; if( find_option("tk",0,0)!=0 ){ diff_tk(); @@ -619,35 +778,43 @@ zTo = zBranch; zFrom = mprintf("root:%s", zBranch); } if( zTo==0 ){ db_must_be_within_tree(); - verify_all_options(); if( !isInternDiff ){ zDiffCmd = diff_command_external(isGDiff); } + zBinGlob = diff_get_binary_glob(); + fIncludeBinary = diff_include_binary_files(); + verify_all_options(); if( g.argc>=3 ){ for(f=2; f<g.argc; ++f){ - diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]); + diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, + diffFlags, g.argv[f]); } }else{ - diff_all_against_disk(zFrom, zDiffCmd, diffFlags); + diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary, + diffFlags); } }else if( zFrom==0 ){ fossil_fatal("must use --from if --to is present"); }else{ db_find_and_open_repository(0, 0); - verify_all_options(); if( !isInternDiff ){ zDiffCmd = diff_command_external(isGDiff); } + zBinGlob = diff_get_binary_glob(); + fIncludeBinary = diff_include_binary_files(); + verify_all_options(); if( g.argc>=3 ){ for(f=2; f<g.argc; ++f){ - diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]); + diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, + diffFlags, g.argv[f]); } }else{ - diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags); + diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary, + diffFlags); } } } /* @@ -660,7 +827,7 @@ login_check_credentials(); if( !g.perm.Read ){ login_needed(); return; } if( zFrom==0 || zTo==0 ) fossil_redirect_home(); cgi_set_content_type("text/plain"); - diff_all_two_versions(zFrom, zTo, 0, DIFF_NEWFILE); + diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE); } Index: src/finfo.c ================================================================== --- src/finfo.c +++ src/finfo.c @@ -115,11 +115,11 @@ Blob fname; const char *zRevision = find_option("revision", "r", 1); file_tree_name(g.argv[2], &fname, 1); if( zRevision ){ - historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0); + historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0); }else{ int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s", &fname, filename_collation()); if( rid==0 ){ fossil_fatal("no history for file: %b", &fname); Index: src/stash.c ================================================================== --- src/stash.c +++ src/stash.c @@ -267,11 +267,17 @@ } /* ** Show the diffs associate with a single stash. */ -static void stash_diff(int stashid, const char *zDiffCmd, u64 diffFlags){ +static void stash_diff( + int stashid, + const char *zDiffCmd, + const char *zBinGlob, + int fIncludeBinary, + u64 diffFlags +){ Stmt q; Blob empty; blob_zero(&empty); db_prepare(&q, "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta" @@ -280,28 +286,39 @@ ); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); int isLink = db_column_int(&q, 3); + int isBin1, isBin2; const char *zOrig = db_column_text(&q, 4); const char *zNew = db_column_text(&q, 5); char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); Blob delta; if( rid==0 ){ db_ephemeral_blob(&q, 6, &delta); fossil_print("ADDED %s\n", zNew); diff_print_index(zNew, diffFlags); - diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags); + isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&empty), + blob_size(&empty)); + isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&delta), + blob_size(&delta)); + diff_file_mem(&empty, &delta, isBin1, isBin2, zNew, zDiffCmd, + zBinGlob, fIncludeBinary, diffFlags); }else if( isRemoved ){ fossil_print("DELETE %s\n", zOrig); if( file_wd_islink(zOPath) ){ blob_read_link(&delta, zOPath); }else{ blob_read_from_file(&delta, zOPath); } diff_print_index(zNew, diffFlags); - diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags); + isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&delta), + blob_size(&delta)); + isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&empty), + blob_size(&empty)); + diff_file_mem(&delta, &empty, isBin1, isBin2, zOrig, zDiffCmd, + zBinGlob, fIncludeBinary, diffFlags); }else{ Blob a, b, disk; int isOrigLink = file_wd_islink(zOPath); db_ephemeral_blob(&q, 6, &delta); if( isOrigLink ){ @@ -311,15 +328,20 @@ } fossil_print("CHANGED %s\n", zNew); if( !isOrigLink != !isLink ){ diff_print_index(zNew, diffFlags); diff_print_filenames(zOrig, zNew, diffFlags); - printf("cannot compute difference between symlink and regular file\n"); + printf(DIFF_CANNOT_COMPUTE_SYMLINK); }else{ content_get(rid, &a); blob_delta_apply(&a, &delta, &b); - diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags); + isBin1 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&disk), + blob_size(&disk)); + isBin2 = fIncludeBinary ? 0 : looks_like_binary(blob_str(&b), + blob_size(&b)); + diff_file_mem(&disk, &b, isBin1, isBin2, zNew, zDiffCmd, + zBinGlob, fIncludeBinary, diffFlags); blob_reset(&a); blob_reset(&b); } blob_reset(&disk); } @@ -413,10 +435,11 @@ int nCmd; int stashid; undo_capture_command_line(); db_must_be_within_tree(); + db_open_config(0); db_begin_transaction(); zDb = db_name("localdb"); db_multi_exec(zStashInit, zDb, zDb); if( g.argc<=2 ){ zCmd = "save"; @@ -550,21 +573,33 @@ stashid); undo_finish(); }else if( memcmp(zCmd, "diff", nCmd)==0 ){ const char *zDiffCmd = diff_command_external(0); + const char *zBinGlob = 0; + int fIncludeBinary = 0; u64 diffFlags = diff_options(); if( g.argc>4 ) usage("diff STASHID"); + if( zDiffCmd ){ + zBinGlob = diff_get_binary_glob(); + fIncludeBinary = diff_include_binary_files(); + } stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); - stash_diff(stashid, zDiffCmd, diffFlags); + stash_diff(stashid, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); }else if( memcmp(zCmd, "gdiff", nCmd)==0 ){ const char *zDiffCmd = diff_command_external(1); + const char *zBinGlob = 0; + int fIncludeBinary = 0; u64 diffFlags = diff_options(); if( g.argc>4 ) usage("gdiff STASHID"); + if( zDiffCmd ){ + zBinGlob = diff_get_binary_glob(); + fIncludeBinary = diff_include_binary_files(); + } stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); - stash_diff(stashid, zDiffCmd, diffFlags); + stash_diff(stashid, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); }else if( memcmp(zCmd, "help", nCmd)==0 ){ g.argv[1] = "help"; g.argv[2] = "stash"; g.argc = 3; Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -595,12 +595,13 @@ */ int historical_version_of_file( const char *revision, /* The checkin containing the file */ const char *file, /* Full treename of the file */ Blob *content, /* Put the content here */ - int *pIsLink, /* Set to true if file is link. */ + int *pIsLink, /* Set to true if file is link. */ int *pIsExe, /* Set to true if file is executable */ + int *pIsBin, /* Set to true if file is binary */ int errCode /* Error code if file not found. Panic if 0. */ ){ Manifest *pManifest; ManifestFile *pFile; int rid=0; @@ -617,15 +618,20 @@ pManifest = manifest_get(rid, CFTYPE_MANIFEST); if( pManifest ){ pFile = manifest_file_find(pManifest, file); if( pFile ){ + int rc; rid = uuid_to_rid(pFile->zUuid, 0); if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE ); if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK ); manifest_destroy(pManifest); - return content_get(rid, content); + rc = content_get(rid, content); + if( rc && pIsBin ){ + *pIsBin = looks_like_binary(blob_str(content), blob_size(content)); + } + return rc; } manifest_destroy(pManifest); if( errCode<=0 ){ fossil_fatal("file %s does not exist in checkin: %s", file, revision); } @@ -712,11 +718,11 @@ int isLink = 0; char *zFull; zFile = db_column_text(&q, 0); zFull = mprintf("%/%/", g.zLocalRoot, zFile); errCode = historical_version_of_file(zRevision, zFile, &record, - &isLink, &isExe,2); + &isLink, &isExe, 0, 2); if( errCode==2 ){ if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){ fossil_print("UNMANAGE: %s\n", zFile); }else{ undo_save(zFile);