/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
0.1.33 by Martin Pool
add gpl text
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0.1.1 by Martin Pool
Check in old existing knit code.
18
19
# Author: Martin Pool <mbp@canonical.com>
20
21
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
22
"""knit - a weave-like structure"""
23
0.1.35 by Martin Pool
Clean up Knit._delta method
24
# TODO: Perhaps have copy and comparison methods?
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
25
0.1.34 by Martin Pool
remove dead code
26
0.1.17 by Martin Pool
Use objects rather than tuples for tracking VerInfo for
27
class VerInfo(object):
28
    included = frozenset()
29
    def __init__(self, included=None):
30
        if included:
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
31
            self.included = frozenset(included)
0.1.17 by Martin Pool
Use objects rather than tuples for tracking VerInfo for
32
0.1.18 by Martin Pool
Better Knit.dump method
33
    def __repr__(self):
34
        s = self.__class__.__name__ + '('
35
        if self.included:
36
            s += 'included=%r' % (list(self.included))
37
        s += ')'
38
        return s
39
0.1.17 by Martin Pool
Use objects rather than tuples for tracking VerInfo for
40
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
41
class Knit(object):
42
    """knit - versioned text file storage.
43
    
44
    A Knit manages versions of line-based text files, keeping track of the
45
    originating version for each line.
46
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
47
    Texts can be identified in either of two ways:
48
49
    * a nonnegative index number.
50
51
    * a version-id string.
52
53
    Typically the index number will be valid only inside this knit and
54
    the version-id is used to reference it in the larger world.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
55
56
    _l
0.1.5 by Martin Pool
Add test for storing two text versions.
57
        List of edit instructions.
58
59
        Each line is stored as a tuple of (index-id, text).  The line
60
        is present in the version equal to index-id.
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
61
62
    _v
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
63
        List of versions, indexed by index number.
64
65
        For each version we store the tuple (included_versions), which
66
        lists the previous versions also considered active.
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
67
    """
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
68
    def __init__(self):
69
        self._l = []
70
        self._v = []
0.1.5 by Martin Pool
Add test for storing two text versions.
71
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
72
        
0.1.26 by Martin Pool
Refactor parameters to add command
73
    def add(self, parents, text):
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
74
        """Add a single text on top of the weave.
75
0.1.26 by Martin Pool
Refactor parameters to add command
76
        Returns the index number of the newly added version.
77
78
        parents
79
            List or set of parent version numbers.
80
81
        text
82
            Sequence of lines to be added in the new version."""
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
83
        if not isinstance(text, list):
84
            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.
85
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
86
        self._check_versions(parents)
87
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
88
        idx = len(self._v)
0.1.5 by Martin Pool
Add test for storing two text versions.
89
0.1.26 by Martin Pool
Refactor parameters to add command
90
        if parents:
91
            parents = frozenset(parents)
92
            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
93
0.1.31 by Martin Pool
Fix insertion of multiple regions, calculating the right line offset as we go.
94
            # offset gives the number of lines that have been inserted
95
            # into the weave up to the current point; if the original edit instruction
96
            # says to change line A then we actually change (A+offset)
97
            offset = 0
98
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
99
            for i1, i2, newlines in delta:
0.1.29 by Martin Pool
Better internal error
100
                assert 0 <= i1
101
                assert i1 <= i2
102
                assert i2 <= len(self._l)
103
                
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
104
                if i1 != i2:
0.1.29 by Martin Pool
Better internal error
105
                    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
106
                                              % (i1, i2))
0.1.32 by Martin Pool
Clean up code to insert into weave
107
                
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
108
                for line in newlines:
0.1.32 by Martin Pool
Clean up code to insert into weave
109
                    self._l.insert(i1 + offset, (idx, line))
110
                    offset += 1
0.1.25 by Martin Pool
Handle insertion of new weave layers that insert text on top of the basis
111
0.1.26 by Martin Pool
Refactor parameters to add command
112
            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
113
        else:
0.1.26 by Martin Pool
Refactor parameters to add command
114
            # 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
115
            # more quickly by just appending unconditionally
116
            for line in text:
117
                self._l.append((idx, line))
118
119
            self._v.append(VerInfo())
120
            
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
121
        return idx
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
122
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
123
124
    def _check_versions(self, indexes):
125
        """Check everything in the sequence of indexes is valid"""
126
        for i in indexes:
127
            try:
128
                self._v[i]
129
            except IndexError:
130
                raise IndexError("invalid version number %r" % i)
131
0.1.2 by Martin Pool
Import testsweet module adapted from bzr.
132
    
0.1.7 by Martin Pool
Add trivial annotate text
133
    def annotate(self, index):
134
        return list(self.annotate_iter(index))
135
136
137
    def annotate_iter(self, index):
138
        """Yield list of (index-id, line) pairs for the specified version.
139
140
        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
141
        try:
142
            vi = self._v[index]
143
        except IndexError:
144
            raise IndexError('version index %d out of range' % index)
0.1.20 by Martin Pool
Factor out Knit.extract() method
145
        included = set(vi.included)
146
        included.add(index)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
147
        return iter(self._extract(included))
148
149
150
    def _extract(self, included):
0.1.20 by Martin Pool
Factor out Knit.extract() method
151
        """Yield annotation of lines in included set.
152
153
        The set typically but not necessarily corresponds to a version.
154
        """
0.1.7 by Martin Pool
Add trivial annotate text
155
        for origin, line in self._l:
0.1.20 by Martin Pool
Factor out Knit.extract() method
156
            if origin in included:
0.1.7 by Martin Pool
Add trivial annotate text
157
                yield origin, line
0.1.20 by Martin Pool
Factor out Knit.extract() method
158
        
0.1.7 by Martin Pool
Add trivial annotate text
159
160
0.1.5 by Martin Pool
Add test for storing two text versions.
161
    def getiter(self, index):
162
        """Yield lines for the specified version."""
0.1.8 by Martin Pool
Unify get/annotate code
163
        for origin, line in self.annotate_iter(index):
164
            yield line
0.1.5 by Martin Pool
Add test for storing two text versions.
165
166
0.1.4 by Martin Pool
Start indexing knits by both integer and version string.
167
    def get(self, index):
0.1.5 by Martin Pool
Add test for storing two text versions.
168
        return list(self.getiter(index))
0.1.1 by Martin Pool
Check in old existing knit code.
169
170
0.1.11 by Martin Pool
Add Knit.dump method
171
    def dump(self, to_file):
172
        from pprint import pprint
0.1.18 by Martin Pool
Better Knit.dump method
173
        print >>to_file, "Knit._l = ",
0.1.11 by Martin Pool
Add Knit.dump method
174
        pprint(self._l, to_file)
0.1.18 by Martin Pool
Better Knit.dump method
175
        print >>to_file, "Knit._v = ",
176
        pprint(self._v, to_file)
0.1.11 by Martin Pool
Add Knit.dump method
177
178
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
179
    def check(self):
180
        for vers_info in self._v:
181
            included = set()
182
            for vi in vers_info[0]:
183
                if vi < 0 or vi >= index:
0.1.16 by Martin Pool
formatting
184
                    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
185
                                     % (vi, index))
186
                if vi in included:
0.1.16 by Martin Pool
formatting
187
                    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
188
                                     % (vi, index))
189
                included.add(vi)
0.1.18 by Martin Pool
Better Knit.dump method
190
0.1.13 by Martin Pool
Knit structure now allows for versions to include the lines present in other
191
192
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
193
    def _delta(self, included, lines):
194
        """Return changes from basis to new revision.
195
196
        The old text for comparison is the union of included revisions.
197
198
        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.
199
200
        Delta is returned as a sequence of (line1, line2, newlines),
201
        indicating that line1 through line2 of the old weave should be
202
        replaced by the sequence of lines in newlines.  Note that
203
        these line numbers are positions in the total weave and don't
204
        correspond to the lines in any extracted version, or even the
205
        extracted union of included versions.
206
207
        If line1=line2, this is a pure insert; if newlines=[] this is a
208
        pure delete.  (Similar to difflib.)
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
209
        """
210
0.1.27 by Martin Pool
Check that version numbers passed in are reasonable
211
        self._check_versions(included)
212
0.1.23 by Martin Pool
tidy up
213
        ##from pprint import pprint
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
214
215
        # first get basis for comparison
216
        # basis holds (lineno, origin, line)
217
        basis = []
218
0.1.23 by Martin Pool
tidy up
219
        ##print 'my lines:'
220
        ##pprint(self._l)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
221
        
222
        lineno = 0
223
        for origin, line in self._l:
224
            if origin in included:
225
                basis.append((lineno, line))
226
            lineno += 1
227
228
        assert lineno == len(self._l)
229
230
        # now make a parallel list with only the text, to pass to the differ
231
        basis_lines = [line for (lineno, line) in basis]
232
233
        # add a sentinal, because we can also match against the final line
234
        basis.append((len(self._l), None))
235
236
        # XXX: which line of the weave should we really consider matches the end of the file?
237
        # the current code says it's the last line of the weave?
238
239
        from difflib import SequenceMatcher
240
        s = SequenceMatcher(None, basis_lines, lines)
241
0.1.23 by Martin Pool
tidy up
242
        ##print 'basis sequence:'
243
        ##pprint(basis)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
244
245
        for tag, i1, i2, j1, j2 in s.get_opcodes():
0.1.23 by Martin Pool
tidy up
246
            ##print tag, i1, i2, j1, j2
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
247
248
            if tag == 'equal':
249
                continue
250
251
            # i1,i2 are given in offsets within basis_lines; we need to map them
252
            # back to offsets within the entire weave
253
            real_i1 = basis[i1][0]
254
            real_i2 = basis[i2][0]
255
0.1.35 by Martin Pool
Clean up Knit._delta method
256
            assert 0 <= j1
257
            assert j1 <= j2
258
            assert j2 <= len(lines)
0.1.22 by Martin Pool
Calculate delta for new versions relative to a set of parent versions.
259
0.1.35 by Martin Pool
Clean up Knit._delta method
260
            yield real_i1, real_i2, lines[j1:j2]
0.1.21 by Martin Pool
Start computing a delta to insert a new revision
261
0.1.1 by Martin Pool
Check in old existing knit code.
262