/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.1.1 by Martin Pool
Check in old existing knit code.
1
#! /usr/bin/python
2
3
# Copyright (C) 2005 Canonical Ltd
4
5
# GNU GPL v2
6
7
# Author: Martin Pool <mbp@canonical.com>
8
9
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
10
"""knit - a weave-like structure"""
11
12
0.1.17 by Martin Pool
Use objects rather than tuples for tracking VerInfo for
13
class VerInfo(object):
14
    included = frozenset()
15
    def __init__(self, included=None):
16
        if included:
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
17
            self.included = frozenset(included)
0.1.17 by Martin Pool
Use objects rather than tuples for tracking VerInfo for
18
0.1.18 by Martin Pool
Better Knit.dump method
19
    def __repr__(self):
20
        s = self.__class__.__name__ + '('
21
        if self.included:
22
            s += 'included=%r' % (list(self.included))
23
        s += ')'
24
        return s
25
0.1.17 by Martin Pool
Use objects rather than tuples for tracking VerInfo for
26
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
27
class Knit(object):
28
    """knit - versioned text file storage.
29
    
30
    A Knit manages versions of line-based text files, keeping track of the
31
    originating version for each line.
32
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
33
    Texts can be identified in either of two ways:
34
35
    * a nonnegative index number.
36
37
    * a version-id string.
38
39
    Typically the index number will be valid only inside this knit and
40
    the version-id is used to reference it in the larger world.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
41
42
    _l
0.1.5 by Martin Pool
Add test for storing two text versions.
43
        List of edit instructions.
44
45
        Each line is stored as a tuple of (index-id, text).  The line
46
        is present in the version equal to index-id.
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
47
48
    _v
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
49
        List of versions, indexed by index number.
50
51
        For each version we store the tuple (included_versions), which
52
        lists the previous versions also considered active.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
53
    """
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
54
    def __init__(self):
55
        self._l = []
56
        self._v = []
0.1.5 by Martin Pool
Add test for storing two text versions.
57
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
58
        
0.1.26 by Martin Pool
Refactor parameters to add command
59
    def add(self, parents, text):
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
60
        """Add a single text on top of the weave.
61
0.1.26 by Martin Pool
Refactor parameters to add command
62
        Returns the index number of the newly added version.
63
64
        parents
65
            List or set of parent version numbers.
66
67
        text
68
            Sequence of lines to be added in the new version."""
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
69
        if not isinstance(text, list):
70
            raise ValueError("text should be a list, not %s" % type(text))
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
71
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
72
        self._check_versions(parents)
73
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
74
        idx = len(self._v)
0.1.5 by Martin Pool
Add test for storing two text versions.
75
0.1.26 by Martin Pool
Refactor parameters to add command
76
        if parents:
77
            parents = frozenset(parents)
78
            delta = self._delta(parents, text)
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
79
0.1.31 by Martin Pool
Fix insertion of multiple regions, calculating the right line offset as we go.
80
            # offset gives the number of lines that have been inserted
81
            # into the weave up to the current point; if the original edit instruction
82
            # says to change line A then we actually change (A+offset)
83
            offset = 0
84
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
85
            for i1, i2, newlines in delta:
0.1.29 by Martin Pool
Better internal error
86
                assert 0 <= i1
87
                assert i1 <= i2
88
                assert i2 <= len(self._l)
89
                
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
90
                if i1 != i2:
0.1.29 by Martin Pool
Better internal error
91
                    raise NotImplementedError("can't handle replacing weave [%d:%d] yet"
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
92
                                              % (i1, i2))
0.1.32 by Martin Pool
Clean up code to insert into weave
93
                
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
94
                for line in newlines:
0.1.32 by Martin Pool
Clean up code to insert into weave
95
                    self._l.insert(i1 + offset, (idx, line))
96
                    offset += 1
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
97
0.1.26 by Martin Pool
Refactor parameters to add command
98
            self._v.append(VerInfo(parents))
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
99
        else:
0.1.26 by Martin Pool
Refactor parameters to add command
100
            # special case; adding with no parents revision; can do this
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
101
            # more quickly by just appending unconditionally
102
            for line in text:
103
                self._l.append((idx, line))
104
105
            self._v.append(VerInfo())
106
            
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
107
        return idx
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
108
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
109
110
    def _check_versions(self, indexes):
111
        """Check everything in the sequence of indexes is valid"""
112
        for i in indexes:
113
            try:
114
                self._v[i]
115
            except IndexError:
116
                raise IndexError("invalid version number %r" % i)
117
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
118
    
0.1.7 by Martin Pool
Add trivial annotate text
119
    def annotate(self, index):
120
        return list(self.annotate_iter(index))
121
122
123
    def annotate_iter(self, index):
124
        """Yield list of (index-id, line) pairs for the specified version.
125
126
        The index indicates when the line originated in the weave."""
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
127
        try:
128
            vi = self._v[index]
129
        except IndexError:
130
            raise IndexError('version index %d out of range' % index)
0.1.20 by Martin Pool
Factor out Knit.extract() method
131
        included = set(vi.included)
132
        included.add(index)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
133
        return iter(self._extract(included))
134
135
136
    def _extract(self, included):
0.1.20 by Martin Pool
Factor out Knit.extract() method
137
        """Yield annotation of lines in included set.
138
139
        The set typically but not necessarily corresponds to a version.
140
        """
0.1.7 by Martin Pool
Add trivial annotate text
141
        for origin, line in self._l:
0.1.20 by Martin Pool
Factor out Knit.extract() method
142
            if origin in included:
0.1.7 by Martin Pool
Add trivial annotate text
143
                yield origin, line
0.1.20 by Martin Pool
Factor out Knit.extract() method
144
        
0.1.7 by Martin Pool
Add trivial annotate text
145
146
0.1.5 by Martin Pool
Add test for storing two text versions.
147
    def getiter(self, index):
148
        """Yield lines for the specified version."""
0.1.8 by Martin Pool
Unify get/annotate code
149
        for origin, line in self.annotate_iter(index):
150
            yield line
0.1.5 by Martin Pool
Add test for storing two text versions.
151
152
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
153
    def get(self, index):
0.1.5 by Martin Pool
Add test for storing two text versions.
154
        return list(self.getiter(index))
0.1.1 by Martin Pool
Check in old existing knit code.
155
156
0.1.11 by Martin Pool
Add Knit.dump method
157
    def dump(self, to_file):
158
        from pprint import pprint
0.1.18 by Martin Pool
Better Knit.dump method
159
        print >>to_file, "Knit._l = ",
0.1.11 by Martin Pool
Add Knit.dump method
160
        pprint(self._l, to_file)
0.1.18 by Martin Pool
Better Knit.dump method
161
        print >>to_file, "Knit._v = ",
162
        pprint(self._v, to_file)
0.1.11 by Martin Pool
Add Knit.dump method
163
164
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
165
    def check(self):
166
        for vers_info in self._v:
167
            included = set()
168
            for vi in vers_info[0]:
169
                if vi < 0 or vi >= index:
0.1.16 by Martin Pool
formatting
170
                    raise ValueError("invalid included version %d for index %d"
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
171
                                     % (vi, index))
172
                if vi in included:
0.1.16 by Martin Pool
formatting
173
                    raise ValueError("repeated included version %d for index %d"
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
174
                                     % (vi, index))
175
                included.add(vi)
0.1.18 by Martin Pool
Better Knit.dump method
176
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
177
178
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
179
    def _delta(self, included, lines):
180
        """Return changes from basis to new revision.
181
182
        The old text for comparison is the union of included revisions.
183
184
        This is used in inserting a new text.
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
185
186
        Delta is returned as a sequence of (line1, line2, newlines),
187
        indicating that line1 through line2 of the old weave should be
188
        replaced by the sequence of lines in newlines.  Note that
189
        these line numbers are positions in the total weave and don't
190
        correspond to the lines in any extracted version, or even the
191
        extracted union of included versions.
192
193
        If line1=line2, this is a pure insert; if newlines=[] this is a
194
        pure delete.  (Similar to difflib.)
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
195
        """
196
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
197
        self._check_versions(included)
198
0.1.23 by Martin Pool
tidy up
199
        ##from pprint import pprint
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
200
201
        # first get basis for comparison
202
        # basis holds (lineno, origin, line)
203
        basis = []
204
0.1.23 by Martin Pool
tidy up
205
        ##print 'my lines:'
206
        ##pprint(self._l)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
207
        
208
        lineno = 0
209
        for origin, line in self._l:
210
            if origin in included:
211
                basis.append((lineno, line))
212
            lineno += 1
213
214
        assert lineno == len(self._l)
215
216
        # now make a parallel list with only the text, to pass to the differ
217
        basis_lines = [line for (lineno, line) in basis]
218
219
        # add a sentinal, because we can also match against the final line
220
        basis.append((len(self._l), None))
221
222
        # XXX: which line of the weave should we really consider matches the end of the file?
223
        # the current code says it's the last line of the weave?
224
225
        from difflib import SequenceMatcher
226
        s = SequenceMatcher(None, basis_lines, lines)
227
0.1.23 by Martin Pool
tidy up
228
        ##print 'basis sequence:'
229
        ##pprint(basis)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
230
231
        for tag, i1, i2, j1, j2 in s.get_opcodes():
0.1.23 by Martin Pool
tidy up
232
            ##print tag, i1, i2, j1, j2
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
233
234
            if tag == 'equal':
235
                continue
236
237
            # i1,i2 are given in offsets within basis_lines; we need to map them
238
            # back to offsets within the entire weave
239
            real_i1 = basis[i1][0]
240
            real_i2 = basis[i2][0]
241
242
            # find the text identified by j:
243
            if j1 == j2:
244
                newlines = []
245
            else:
0.1.23 by Martin Pool
tidy up
246
                assert 0 <= j1
247
                assert j1 <= j2
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
248
                assert j2 <= len(lines)
249
                newlines = lines[j1:j2]
250
251
            yield real_i1, real_i2, newlines
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
252
0.1.1 by Martin Pool
Check in old existing knit code.
253
254
def update_knit(knit, new_vers, new_lines):
255
    """Return a new knit whose text matches new_lines.
256
257
    First of all the knit is diffed against the new lines, considering
258
    only the text of the lines from the knit.  This identifies lines
259
    unchanged from the knit, plus insertions and deletions.
260
261
    The deletions are marked as deleted.  The insertions are added
262
    with their new values.
263
264
    
265
    """
266
    if not isinstance(new_vers, int):
267
        raise TypeError('new version-id must be an int: %r' % new_vers)
268
    
269
    from difflib import SequenceMatcher
270
    knit_lines = knit2text(knit)
271
    m = SequenceMatcher(None, knit_lines, new_lines)
272
273
    for block in m.get_matching_blocks():
274
        print "a[%d] and b[%d] match for %d elements" % block
275
    
276
    new_knit = []
277
    for tag, i1, i2, j1, j2 in m.get_opcodes():
278
        print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
279
               (tag, i1, i2, knit_lines[i1:i2], j1, j2, new_lines[j1:j2]))
280
281
        if tag == 'equal':
282
            new_knit.extend(knit[i1:i2])
283
        elif tag == 'delete':
284
            for i in range(i1, i2):
285
                kl = knit[i]
286
                new_knit.append((kl[0], kl[1], False))
287
288
    return new_knit
289
        
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
290