/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
72
        idx = len(self._v)
0.1.5 by Martin Pool
Add test for storing two text versions.
73
0.1.26 by Martin Pool
Refactor parameters to add command
74
        if parents:
75
            parents = frozenset(parents)
76
            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
77
78
            for i1, i2, newlines in delta:
79
                # TODO: handle lines being offset as we insert stuff
80
                if i1 != i2:
81
                    raise NotImplementedError("can't handle replacing weave lines %d-%d yet"
82
                                              % (i1, i2))
83
84
                # a pure insertion
85
                to_insert = []
86
                for line in newlines:
87
                    to_insert.append((idx, line))
88
                
89
                self._l[i1:i1] = to_insert
90
0.1.26 by Martin Pool
Refactor parameters to add command
91
            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
92
        else:
0.1.26 by Martin Pool
Refactor parameters to add command
93
            # 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
94
            # more quickly by just appending unconditionally
95
            for line in text:
96
                self._l.append((idx, line))
97
98
            self._v.append(VerInfo())
99
            
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
100
        return idx
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
101
102
    
0.1.7 by Martin Pool
Add trivial annotate text
103
    def annotate(self, index):
104
        return list(self.annotate_iter(index))
105
106
107
    def annotate_iter(self, index):
108
        """Yield list of (index-id, line) pairs for the specified version.
109
110
        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
111
        try:
112
            vi = self._v[index]
113
        except IndexError:
114
            raise IndexError('version index %d out of range' % index)
0.1.20 by Martin Pool
Factor out Knit.extract() method
115
        included = set(vi.included)
116
        included.add(index)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
117
        return iter(self._extract(included))
118
119
120
    def _extract(self, included):
0.1.20 by Martin Pool
Factor out Knit.extract() method
121
        """Yield annotation of lines in included set.
122
123
        The set typically but not necessarily corresponds to a version.
124
        """
0.1.7 by Martin Pool
Add trivial annotate text
125
        for origin, line in self._l:
0.1.20 by Martin Pool
Factor out Knit.extract() method
126
            if origin in included:
0.1.7 by Martin Pool
Add trivial annotate text
127
                yield origin, line
0.1.20 by Martin Pool
Factor out Knit.extract() method
128
        
0.1.7 by Martin Pool
Add trivial annotate text
129
130
0.1.5 by Martin Pool
Add test for storing two text versions.
131
    def getiter(self, index):
132
        """Yield lines for the specified version."""
0.1.8 by Martin Pool
Unify get/annotate code
133
        for origin, line in self.annotate_iter(index):
134
            yield line
0.1.5 by Martin Pool
Add test for storing two text versions.
135
136
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
137
    def get(self, index):
0.1.5 by Martin Pool
Add test for storing two text versions.
138
        return list(self.getiter(index))
0.1.1 by Martin Pool
Check in old existing knit code.
139
140
0.1.11 by Martin Pool
Add Knit.dump method
141
    def dump(self, to_file):
142
        from pprint import pprint
0.1.18 by Martin Pool
Better Knit.dump method
143
        print >>to_file, "Knit._l = ",
0.1.11 by Martin Pool
Add Knit.dump method
144
        pprint(self._l, to_file)
0.1.18 by Martin Pool
Better Knit.dump method
145
        print >>to_file, "Knit._v = ",
146
        pprint(self._v, to_file)
0.1.11 by Martin Pool
Add Knit.dump method
147
148
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
149
    def check(self):
150
        for vers_info in self._v:
151
            included = set()
152
            for vi in vers_info[0]:
153
                if vi < 0 or vi >= index:
0.1.16 by Martin Pool
formatting
154
                    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
155
                                     % (vi, index))
156
                if vi in included:
0.1.16 by Martin Pool
formatting
157
                    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
158
                                     % (vi, index))
159
                included.add(vi)
0.1.18 by Martin Pool
Better Knit.dump method
160
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
161
162
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
163
    def _delta(self, included, lines):
164
        """Return changes from basis to new revision.
165
166
        The old text for comparison is the union of included revisions.
167
168
        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.
169
170
        Delta is returned as a sequence of (line1, line2, newlines),
171
        indicating that line1 through line2 of the old weave should be
172
        replaced by the sequence of lines in newlines.  Note that
173
        these line numbers are positions in the total weave and don't
174
        correspond to the lines in any extracted version, or even the
175
        extracted union of included versions.
176
177
        If line1=line2, this is a pure insert; if newlines=[] this is a
178
        pure delete.  (Similar to difflib.)
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
179
        """
180
0.1.23 by Martin Pool
tidy up
181
        ##from pprint import pprint
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
182
183
        # first get basis for comparison
184
        # basis holds (lineno, origin, line)
185
        basis = []
186
0.1.23 by Martin Pool
tidy up
187
        ##print 'my lines:'
188
        ##pprint(self._l)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
189
        
190
        lineno = 0
191
        for origin, line in self._l:
192
            if origin in included:
193
                basis.append((lineno, line))
194
            lineno += 1
195
196
        assert lineno == len(self._l)
197
198
        # now make a parallel list with only the text, to pass to the differ
199
        basis_lines = [line for (lineno, line) in basis]
200
201
        # add a sentinal, because we can also match against the final line
202
        basis.append((len(self._l), None))
203
204
        # XXX: which line of the weave should we really consider matches the end of the file?
205
        # the current code says it's the last line of the weave?
206
207
        from difflib import SequenceMatcher
208
        s = SequenceMatcher(None, basis_lines, lines)
209
0.1.23 by Martin Pool
tidy up
210
        ##print 'basis sequence:'
211
        ##pprint(basis)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
212
213
        for tag, i1, i2, j1, j2 in s.get_opcodes():
0.1.23 by Martin Pool
tidy up
214
            ##print tag, i1, i2, j1, j2
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
215
216
            if tag == 'equal':
217
                continue
218
219
            # i1,i2 are given in offsets within basis_lines; we need to map them
220
            # back to offsets within the entire weave
221
            real_i1 = basis[i1][0]
222
            real_i2 = basis[i2][0]
223
224
            # find the text identified by j:
225
            if j1 == j2:
226
                newlines = []
227
            else:
0.1.23 by Martin Pool
tidy up
228
                assert 0 <= j1
229
                assert j1 <= j2
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
230
                assert j2 <= len(lines)
231
                newlines = lines[j1:j2]
232
233
            yield real_i1, real_i2, newlines
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
234
0.1.1 by Martin Pool
Check in old existing knit code.
235
236
def update_knit(knit, new_vers, new_lines):
237
    """Return a new knit whose text matches new_lines.
238
239
    First of all the knit is diffed against the new lines, considering
240
    only the text of the lines from the knit.  This identifies lines
241
    unchanged from the knit, plus insertions and deletions.
242
243
    The deletions are marked as deleted.  The insertions are added
244
    with their new values.
245
246
    
247
    """
248
    if not isinstance(new_vers, int):
249
        raise TypeError('new version-id must be an int: %r' % new_vers)
250
    
251
    from difflib import SequenceMatcher
252
    knit_lines = knit2text(knit)
253
    m = SequenceMatcher(None, knit_lines, new_lines)
254
255
    for block in m.get_matching_blocks():
256
        print "a[%d] and b[%d] match for %d elements" % block
257
    
258
    new_knit = []
259
    for tag, i1, i2, j1, j2 in m.get_opcodes():
260
        print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
261
               (tag, i1, i2, knit_lines[i1:i2], j1, j2, new_lines[j1:j2]))
262
263
        if tag == 'equal':
264
            new_knit.extend(knit[i1:i2])
265
        elif tag == 'delete':
266
            for i in range(i1, i2):
267
                kl = knit[i]
268
                new_knit.append((kl[0], kl[1], False))
269
270
    return new_knit
271
        
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
272