Add automatic binary file detection to diff and export
authormpm@selenic.com
Tue, 23 Aug 2005 19:58:46 -0700
changeset 1015 22571b8d35d3
parent 1014 e37cd99fa909
child 1016 836667830fee
Add automatic binary file detection to diff and export Based on a patch by Fuming Wang - add util.binary which decides whether a file is binary if it has any NUL characters in the first 1K. - teach mdiff.unidiff to print "binary file <x> has changed" for binary files - add text flag to cause unidiff and dodiff to treat all files as text - add -a and --text flags (like diff(1)) to hg diff and export - update docs
doc/hg.1.txt
mercurial/commands.py
mercurial/mdiff.py
mercurial/util.py
--- a/doc/hg.1.txt	Tue Aug 23 18:58:26 2005 -0700
+++ b/doc/hg.1.txt	Tue Aug 23 19:58:46 2005 -0700
@@ -128,7 +128,7 @@
     
     This command takes effect for the next commit.
 
-diff [-r revision] [-r revision] [files ...]::
+diff [-a] [-r revision] [-r revision] [files ...]::
     Show differences between revisions for the specified files.
     
     Differences between files are shown using the unified diff format.
@@ -139,7 +139,12 @@
     revisions are specified, the working directory files are compared
     to its parent.
 
+    Without the -a option, diff will avoid generating diffs of files
+    it detects as binary. With -a, diff will generate a diff anyway,
+    probably with undesirable results.
+
     options:
+    -a, --text           treat all files as text
     -I, --include <pat>  include names matching the given patterns
     -X, --exclude <pat>  exclude names matching the given patterns
 
@@ -161,8 +166,12 @@
     %n   zero-padded sequence number, starting at 1
     %r   zero-padded changeset revision number
 
-    Options:
+    Without the -a option, export will avoid generating diffs of files
+    it detects as binary. With -a, export will generate a diff anyway,
+    probably with undesirable results.
 
+    options:
+    -a, --text                treat all files as text
     -o, --output <filespec>   print output to file with formatted named
 
 forget [options] [files]::
--- a/mercurial/commands.py	Tue Aug 23 18:58:26 2005 -0700
+++ b/mercurial/commands.py	Tue Aug 23 19:58:46 2005 -0700
@@ -141,7 +141,7 @@
                 mode)
 
 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
-           changes=None):
+           changes=None, text=False):
     def date(c):
         return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
 
@@ -183,15 +183,15 @@
         if f in mmap:
             to = repo.file(f).read(mmap[f])
         tn = read(f)
-        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
+        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
     for f in a:
         to = None
         tn = read(f)
-        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
+        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
     for f in d:
         to = repo.file(f).read(mmap[f])
         tn = None
-        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
+        fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
 
 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None, brinfo=None):
     """show a single changeset or file revision"""
@@ -644,11 +644,9 @@
 
 def diff(ui, repo, *pats, **opts):
     """diff working directory (or selected files)"""
-    revs = []
-    if opts['rev']:
-        revs = map(lambda x: repo.lookup(x), opts['rev'])
+    node1, node2 = None, None
+    revs = [repo.lookup(x) for x in opts['rev']]
 
-    node1, node2 = None, None
     if len(revs) > 0:
         node1 = revs[0]
     if len(revs) > 1:
@@ -663,7 +661,8 @@
         for src, abs, rel, exact in results:
             files.append(abs)
 
-    dodiff(sys.stdout, ui, repo, node1, node2, files, match=match)
+    dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
+           text=opts['text'])
 
 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
     node = repo.lookup(changeset)
@@ -685,7 +684,7 @@
     fp.write(change[4].rstrip())
     fp.write("\n\n")
 
-    dodiff(fp, ui, repo, prev, node)
+    dodiff(fp, ui, repo, prev, node, text=opts['text'])
     if fp != sys.stdout: fp.close()
 
 def export(ui, repo, *changesets, **opts):
@@ -1326,12 +1325,14 @@
     "^diff":
         (diff,
          [('r', 'rev', [], 'revision'),
+          ('a', 'text', None, 'treat all files as text'),
           ('I', 'include', [], 'include path in search'),
           ('X', 'exclude', [], 'exclude path from search')],
          'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
     "^export":
         (export,
-         [('o', 'output', "", 'output to file')],
+         [('o', 'output', "", 'output to file'),
+          ('a', 'text', None, 'treat all files as text')],
          "hg export [-o OUTFILE] REV..."),
     "forget":
         (forget,
--- a/mercurial/mdiff.py	Tue Aug 23 18:58:26 2005 -0700
+++ b/mercurial/mdiff.py	Tue Aug 23 19:58:46 2005 -0700
@@ -7,12 +7,15 @@
 
 import difflib, struct, bdiff
 from mpatch import *
+from util import *
 
-def unidiff(a, ad, b, bd, fn, r=None):
+def unidiff(a, ad, b, bd, fn, r=None, text=False):
 
     if not a and not b: return ""
 
-    if a == None:
+    if not text and (binary(a) or binary(b)):
+        l = ['Binary file %s has changed\n' % fn]
+    elif a == None:
         b = b.splitlines(1)
         l1 = "--- %s\t%s\n" % ("/dev/null", ad)
         l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
--- a/mercurial/util.py	Tue Aug 23 18:58:26 2005 -0700
+++ b/mercurial/util.py	Tue Aug 23 19:58:46 2005 -0700
@@ -9,6 +9,11 @@
 from demandload import *
 demandload(globals(), "re")
 
+def binary(s):
+    if s and '\0' in s[:4096]:
+        return True
+    return False
+
 def unique(g):
     seen = {}
     for f in g: