~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/mdiff.py

  • Committer: Martin Pool
  • Date: 2005-04-28 07:24:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050428072453-7b99afa993a1e549
todo

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
 
26
26
# TODO: maybe work on files not strings?
27
27
 
28
 
# FIXME: doesn't work properly on files without trailing newlines
29
28
 
30
 
import difflib
31
 
import struct
32
 
import sys
33
 
import unittest
 
29
import difflib, sys, struct
34
30
from cStringIO import StringIO
35
31
 
36
 
 
37
 
def linesplit(a):
38
 
    """Split into two lists: content and line positions.
39
 
 
40
 
    This returns (al, ap).
41
 
 
42
 
    al[i] is the string content of line i of the file, including its
43
 
    newline (if any).
44
 
 
45
 
    ap[i] is the byte position in the file where that line starts.
46
 
 
47
 
    ap[-1] is the byte position of the end of the file (i.e. the
48
 
    length of the file.)
49
 
 
50
 
    This transformation allows us to do a line-based diff and then map
51
 
    back to byte positions.
52
 
    """
53
 
 
54
 
    al, ap = [], []
55
 
    last = 0
56
 
 
57
 
    n = a.find("\n") + 1
58
 
    while n > 0:
59
 
        ap.append(last)
60
 
        al.append(a[last:n])
61
 
        last = n
62
 
        n = a.find("\n", n) + 1
63
 
 
64
 
    if last < len(a):
65
 
        al.append(a[last:])
66
 
        ap.append(last)
67
 
 
68
 
    # position at the end
69
 
    ap.append(len(a))
70
 
 
71
 
    return (al, ap)
72
 
 
73
 
 
74
32
def diff(a, b):
75
 
    # TODO: Use different splits, perhaps rsync-like, for binary files?
76
 
    
77
 
    (al, ap) = linesplit(a)
78
 
    (bl, bp) = linesplit(b)
79
 
 
80
 
    d = difflib.SequenceMatcher(None, al, bl)
 
33
    d = difflib.SequenceMatcher(None, a, b)
81
34
    
82
35
    ## sys.stderr.write('  ~ real_quick_ratio: %.4f\n' % d.real_quick_ratio())
83
36
    
85
38
        if o == 'equal': continue
86
39
        # a[m:n] should be replaced by b[s:t]
87
40
        if s == t:
88
 
            yield ap[m], ap[n], ''
 
41
            yield m, n, ''
89
42
        else:
90
 
            yield ap[m], ap[n], ''.join(bl[s:t])
 
43
            yield m, n, b[s:t]
91
44
 
92
45
 
93
46
def tobinary(ops):
141
94
 
142
95
 
143
96
 
144
 
class TestDiffPatch(unittest.TestCase):
145
 
    def doDiffPatch(self, old, new):
146
 
        diff = bdiff(old, new)
147
 
        result = bpatch(old, diff)
148
 
        self.assertEquals(new, result)
149
 
 
150
 
 
151
 
    def testSimpleDiff(self):
152
 
        """Simply add a line at the end"""
153
 
        self.doDiffPatch('a\nb\n', 'a\nb\nc\n')
154
 
        
155
 
 
156
 
        
157
 
    def testTrailingLine(self):
158
 
        """Test diff that adds an unterminated line.
159
 
 
160
 
        (Old versions didn't do this properly.)"""
161
 
        self.doDiffPatch('a\nb\nc\n',
162
 
                         'a\nb\nc\nd')
163
 
 
164
 
 
165
 
if __name__ == '__main__':
166
 
    unittest.main()