/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.9.31 by Aaron Bentley
Allow selecting MemoryVersionedFile from commandline
1
import errno
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
2
from itertools import chain
0.9.31 by Aaron Bentley
Allow selecting MemoryVersionedFile from commandline
3
import os
0.9.25 by Aaron Bentley
More messy hacking
4
from StringIO import StringIO
0.9.19 by Aaron Bentley
More tweakage
5
import sys
6
0.9.25 by Aaron Bentley
More messy hacking
7
from bzrlib import (
8
    patiencediff,
9
    trace,
10
    ui,
11
    )
12
13
from bzrlib.tuned_gzip import GzipFile
0.9.3 by Aaron Bentley
Get three-parent comparisions under test
14
0.9.26 by Aaron Bentley
Move topological iteration into an iterator
15
def topo_iter(vf):
16
    seen = set()
17
    descendants = {}
18
    for version_id in vf.versions():
19
        for parent_id in vf.get_parents(version_id):
20
            descendants.setdefault(parent_id, []).append(version_id)
21
    cur = [v for v in vf.versions() if len(vf.get_parents(v)) == 0]
22
    while len(cur) > 0:
23
        next = []
24
        for version_id in cur:
25
            if version_id in seen:
26
                continue
27
            parents = vf.get_parents(version_id)
28
            if not seen.issuperset(parents):
29
                continue
30
            next.extend(descendants.get(version_id, []))
31
            yield version_id
32
            seen.add(version_id)
33
        cur = next
34
35
0.9.1 by Aaron Bentley
Get trivial case passing
36
class MultiParent(object):
37
0.9.2 by Aaron Bentley
Get single-parent comparison working
38
    def __init__(self, hunks=None):
39
        if hunks is not None:
40
            self.hunks = hunks
41
        else:
42
            self.hunks = []
43
44
    def __repr__(self):
45
        return "MultiParent(%r)" % self.hunks
46
47
    def __eq__(self, other):
48
        if self.__class__ is not other.__class__:
49
            return False
50
        return (self.hunks == other.hunks)
0.9.1 by Aaron Bentley
Get trivial case passing
51
52
    @staticmethod
53
    def from_lines(text, parents=()):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
54
        """Produce a MultiParent from a list of lines and parents"""
0.9.2 by Aaron Bentley
Get single-parent comparison working
55
        def compare(parent):
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
56
            matcher = patiencediff.PatienceSequenceMatcher(None, parent,
57
                                                           text)
58
            return matcher.get_matching_blocks()
0.9.2 by Aaron Bentley
Get single-parent comparison working
59
        parent_comparisons = [compare(p) for p in parents]
60
        cur_line = 0
61
        new_text = NewText([])
62
        parent_text = []
63
        block_iter = [iter(i) for i in parent_comparisons]
64
        diff = MultiParent([])
65
        def next_block(p):
66
            try:
67
                return block_iter[p].next()
68
            except StopIteration:
69
                return None
70
        cur_block = [next_block(p) for p, i in enumerate(block_iter)]
71
        while cur_line < len(text):
72
            best_match = None
73
            for p, block in enumerate(cur_block):
74
                if block is None:
75
                    continue
76
                i, j, n = block
77
                while j + n < cur_line:
78
                    block = cur_block[p] = next_block(p)
79
                    if block is None:
80
                        break
81
                    i, j, n = block
82
                if block is None:
83
                    continue
84
                if j > cur_line:
85
                    continue
86
                offset = cur_line - j
87
                i += offset
88
                j = cur_line
89
                n -= offset
90
                if n == 0:
91
                    continue
92
                if best_match is None or n > best_match.num_lines:
93
                    best_match = ParentText(p, i, j, n)
94
            if best_match is None:
95
                new_text.lines.append(text[cur_line])
96
                cur_line += 1
97
            else:
98
                if len(new_text.lines) > 0:
99
                    diff.hunks.append(new_text)
100
                    new_text = NewText([])
101
                diff.hunks.append(best_match)
102
                cur_line += best_match.num_lines
103
        if len(new_text.lines) > 0:
104
            diff.hunks.append(new_text)
0.9.1 by Aaron Bentley
Get trivial case passing
105
        return diff
106
107
    @classmethod
108
    def from_texts(cls, text, parents=()):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
109
        """Produce a MultiParent from a text and list of parent text"""
0.9.1 by Aaron Bentley
Get trivial case passing
110
        return cls.from_lines(text.splitlines(True),
111
                              [p.splitlines(True) for p in parents])
112
0.9.4 by Aaron Bentley
Start supporting serialization
113
    def to_patch(self):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
114
        """Yield text lines for a patch"""
0.9.4 by Aaron Bentley
Start supporting serialization
115
        for hunk in self.hunks:
116
            for line in hunk.to_patch():
117
                yield line
118
0.9.25 by Aaron Bentley
More messy hacking
119
    def patch_len(self):
120
        return len(''.join(self.to_patch()))
121
122
    def zipped_patch_len(self):
123
        return len(gzip_string(self.to_patch()))
124
0.9.18 by Aaron Bentley
Implement from_patch
125
    @staticmethod
126
    def from_patch(lines):
0.9.19 by Aaron Bentley
More tweakage
127
        """Produce a MultiParent from a sequence of lines"""
0.9.18 by Aaron Bentley
Implement from_patch
128
        line_iter = iter(lines)
129
        hunks = []
130
        cur_line = None
131
        while(True):
132
            try:
133
                cur_line = line_iter.next()
134
            except StopIteration:
135
                break
136
            if cur_line[0] == 'i':
137
                num_lines = int(cur_line.split(' ')[1])
138
                hunk_lines = [line_iter.next() for x in xrange(num_lines)]
139
                hunk_lines[-1] = hunk_lines[-1][:-1]
140
                hunks.append(NewText(hunk_lines))
141
            elif cur_line[0] == '\n':
142
                hunks[-1].lines[-1] += '\n'
143
            else:
144
                assert cur_line[0] == 'c', cur_line[0]
145
                parent, parent_pos, child_pos, num_lines =\
146
                    [int(v) for v in cur_line.split(' ')[1:]]
147
                hunks.append(ParentText(parent, parent_pos, child_pos,
148
                                        num_lines))
149
        return MultiParent(hunks)
150
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
151
    def range_iterator(self):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
152
        """Iterate through the hunks, with range indicated
153
154
        kind is "new" or "parent".
155
        for "new", data is a list of lines.
156
        for "parent", data is (parent, parent_start, parent_end)
157
        :return: a generator of (start, end, kind, data)
158
        """
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
159
        start = 0
160
        for hunk in self.hunks:
161
            if isinstance(hunk, NewText):
162
                kind = 'new'
163
                end = start + len(hunk.lines)
164
                data = hunk.lines
165
            else:
166
                kind = 'parent'
167
                start = hunk.child_pos
168
                end = start + hunk.num_lines
169
                data = (hunk.parent, hunk.parent_pos, hunk.parent_pos +
170
                        hunk.num_lines)
171
            yield start, end, kind, data
172
            start = end
173
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
174
    def num_lines(self):
175
        extra_n = 0
176
        for hunk in reversed(self.hunks):
177
            if isinstance(hunk, ParentText):
178
               return hunk.child_pos + hunk.num_lines + extra_n
179
            extra_n += len(hunk.lines)
180
        return extra_n
181
0.9.25 by Aaron Bentley
More messy hacking
182
    def is_snapshot(self):
183
        if len(self.hunks) != 1:
184
            return False
185
        return (isinstance(self.hunks[0], NewText))
186
0.9.1 by Aaron Bentley
Get trivial case passing
187
188
class NewText(object):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
189
    """The contents of text that is introduced by this text"""
0.9.1 by Aaron Bentley
Get trivial case passing
190
191
    def __init__(self, lines):
192
        self.lines = lines
193
194
    def __eq__(self, other):
195
        if self.__class__ is not other.__class__:
196
            return False
197
        return (other.lines == self.lines)
0.9.2 by Aaron Bentley
Get single-parent comparison working
198
199
    def __repr__(self):
200
        return 'NewText(%r)' % self.lines
201
0.9.4 by Aaron Bentley
Start supporting serialization
202
    def to_patch(self):
203
        yield 'i %d\n' % len(self.lines)
204
        for line in self.lines:
205
            yield line
206
        yield '\n'
207
0.9.2 by Aaron Bentley
Get single-parent comparison working
208
209
class ParentText(object):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
210
    """A reference to text present in a parent text"""
0.9.2 by Aaron Bentley
Get single-parent comparison working
211
212
    def __init__(self, parent, parent_pos, child_pos, num_lines):
213
        self.parent = parent
214
        self.parent_pos = parent_pos
215
        self.child_pos = child_pos
216
        self.num_lines = num_lines
217
218
    def __repr__(self):
219
        return 'ParentText(%(parent)r, %(parent_pos)r, %(child_pos)r,'\
220
            ' %(num_lines)r)' % self.__dict__
221
222
    def __eq__(self, other):
223
        if self.__class__ != other.__class__:
224
            return False
225
        return (self.__dict__ == other.__dict__)
0.9.4 by Aaron Bentley
Start supporting serialization
226
227
    def to_patch(self):
228
        yield 'c %(parent)d %(parent_pos)d %(child_pos)d %(num_lines)d\n'\
229
            % self.__dict__
0.9.8 by Aaron Bentley
get add_version working
230
231
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
232
class BaseVersionedFile(object):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
233
    """VersionedFile skeleton for MultiParent"""
0.9.8 by Aaron Bentley
get add_version working
234
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
235
    def __init__(self, snapshot_interval=25, max_snapshots=None):
0.9.8 by Aaron Bentley
get add_version working
236
        self._lines = {}
237
        self._parents = {}
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
238
        self._snapshots = set()
0.9.12 by Aaron Bentley
Make benchmarks for mp
239
        self.snapshot_interval = snapshot_interval
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
240
        self.max_snapshots = max_snapshots
0.9.12 by Aaron Bentley
Make benchmarks for mp
241
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
242
    def versions(self):
243
        return iter(self._parents)
244
0.9.12 by Aaron Bentley
Make benchmarks for mp
245
    def do_snapshot(self, version_id, parent_ids):
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
246
        if self.snapshot_interval is None:
247
            return False
248
        if self.max_snapshots is not None and\
249
            len(self._snapshots) == self.max_snapshots:
0.9.14 by Aaron Bentley
Temporarily force snapshots to 44
250
            return False
0.9.12 by Aaron Bentley
Make benchmarks for mp
251
        if len(parent_ids) == 0:
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
252
            return True
253
        for ignored in xrange(self.snapshot_interval):
0.9.12 by Aaron Bentley
Make benchmarks for mp
254
            if len(parent_ids) == 0:
255
                return False
0.9.17 by Aaron Bentley
Dynamically select snapshots based on all parents
256
            version_ids = parent_ids
257
            parent_ids = []
258
            for version_id in version_ids:
259
                if version_id not in self._snapshots:
260
                    parent_ids.extend(self._parents[version_id])
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
261
        else:
262
            return True
0.9.8 by Aaron Bentley
get add_version working
263
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
264
    def add_version(self, lines, version_id, parent_ids,
0.9.20 by Aaron Bentley
Convert to a plugin
265
                    force_snapshot=None, single_parent=False):
0.9.16 by Aaron Bentley
More control over snapshotting, disable caching for inventory
266
        if force_snapshot is None:
267
            do_snapshot = self.do_snapshot(version_id, parent_ids)
268
        else:
269
            do_snapshot = force_snapshot
270
        if do_snapshot:
271
            self._snapshots.add(version_id)
0.9.12 by Aaron Bentley
Make benchmarks for mp
272
            diff = MultiParent([NewText(lines)])
273
        else:
0.9.20 by Aaron Bentley
Convert to a plugin
274
            if single_parent:
275
                parent_lines = self.get_line_list(parent_ids[:1])
276
            else:
277
                parent_lines = self.get_line_list(parent_ids)
0.9.12 by Aaron Bentley
Make benchmarks for mp
278
            diff = MultiParent.from_lines(lines, parent_lines)
0.9.25 by Aaron Bentley
More messy hacking
279
            snapdiff = MultiParent([NewText(lines)])
280
            if diff.is_snapshot():
281
                self._snapshots.add(version_id)
282
            elif diff.patch_len() >= snapdiff.patch_len():
283
                trace.note("Forcing snapshot")
284
                self._snapshots.add(version_id)
0.9.8 by Aaron Bentley
get add_version working
285
        self.add_diff(diff, version_id, parent_ids)
286
        self._lines[version_id] = lines
287
0.9.20 by Aaron Bentley
Convert to a plugin
288
    def import_versionedfile(self, vf, snapshots, no_cache=True,
0.9.22 by Aaron Bentley
Fix restoration bug
289
                             single_parent=False, verify=False):
0.9.20 by Aaron Bentley
Convert to a plugin
290
        """Import all revisions of a versionedfile
291
292
        :param vf: The versionedfile to import
293
        :param snapshots: If provided, the revisions to make snapshots of.
294
            Otherwise, this will be auto-determined
295
        :param no_cache: If true, clear the cache after every add.
296
        :param single_parent: If true, omit all but one parent text, (but
297
            retain parent metadata).
298
        """
0.9.22 by Aaron Bentley
Fix restoration bug
299
        assert no_cache or not verify
0.9.19 by Aaron Bentley
More tweakage
300
        revisions = set(vf.versions())
301
        total = len(revisions)
0.9.20 by Aaron Bentley
Convert to a plugin
302
        pb = ui.ui_factory.nested_progress_bar()
303
        try:
304
            while len(revisions) > 0:
305
                added = set()
306
                for revision in revisions:
307
                    parents = vf.get_parents(revision)
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
308
                    if [p for p in parents if p not in self._parents] != []:
0.9.20 by Aaron Bentley
Convert to a plugin
309
                        continue
310
                    lines = [a + ' ' + l for a, l in
311
                             vf.annotate_iter(revision)]
0.9.21 by Aaron Bentley
finish converting ft_ to snapshots
312
                    if snapshots is None:
0.9.20 by Aaron Bentley
Convert to a plugin
313
                        force_snapshot = None
314
                    else:
0.9.21 by Aaron Bentley
finish converting ft_ to snapshots
315
                        force_snapshot = (revision in snapshots)
0.9.20 by Aaron Bentley
Convert to a plugin
316
                    self.add_version(lines, revision, parents, force_snapshot,
317
                                     single_parent)
318
                    added.add(revision)
319
                    if no_cache:
320
                        self.clear_cache()
0.9.25 by Aaron Bentley
More messy hacking
321
                        vf.clear_cache()
0.9.22 by Aaron Bentley
Fix restoration bug
322
                        if verify:
323
                            assert lines == self.get_line_list([revision])[0]
324
                            self.clear_cache()
0.9.20 by Aaron Bentley
Convert to a plugin
325
                    pb.update('Importing revisions',
326
                              (total - len(revisions)) + len(added), total)
327
                revisions = [r for r in revisions if r not in added]
328
        finally:
329
            pb.finished()
0.9.19 by Aaron Bentley
More tweakage
330
0.9.23 by Aaron Bentley
handle snapshots all at once
331
    def select_snapshots(self, vf):
0.9.28 by Aaron Bentley
Update snapshot-picking to use sets of ancestors
332
        build_ancestors = {}
0.9.23 by Aaron Bentley
handle snapshots all at once
333
        descendants = {}
334
        snapshots = set()
0.9.26 by Aaron Bentley
Move topological iteration into an iterator
335
        for version_id in topo_iter(vf):
0.9.28 by Aaron Bentley
Update snapshot-picking to use sets of ancestors
336
            potential_build_ancestors = set(vf.get_parents(version_id))
337
            parents = vf.get_parents(version_id)
338
            if len(parents) == 0:
0.9.26 by Aaron Bentley
Move topological iteration into an iterator
339
                snapshots.add(version_id)
0.9.28 by Aaron Bentley
Update snapshot-picking to use sets of ancestors
340
                build_ancestors[version_id] = set()
0.9.26 by Aaron Bentley
Move topological iteration into an iterator
341
            else:
0.9.28 by Aaron Bentley
Update snapshot-picking to use sets of ancestors
342
                for parent in vf.get_parents(version_id):
343
                    potential_build_ancestors.update(build_ancestors[parent])
344
                if len(potential_build_ancestors) > self.snapshot_interval:
345
                    snapshots.add(version_id)
346
                    build_ancestors[version_id] = set()
0.9.23 by Aaron Bentley
handle snapshots all at once
347
                else:
0.9.28 by Aaron Bentley
Update snapshot-picking to use sets of ancestors
348
                    build_ancestors[version_id] = potential_build_ancestors
0.9.23 by Aaron Bentley
handle snapshots all at once
349
        return snapshots
350
351
0.9.8 by Aaron Bentley
get add_version working
352
    def clear_cache(self):
353
        self._lines.clear()
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
354
355
    def get_line_list(self, version_ids):
356
        return [self.cache_version(v) for v in version_ids]
357
358
    def cache_version(self, version_id):
359
        try:
360
            return self._lines[version_id]
361
        except KeyError:
362
            pass
0.9.29 by Aaron Bentley
Support using disk for knit reconstruction
363
        diff = self.get_diff(version_id)
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
364
        lines = []
0.9.29 by Aaron Bentley
Support using disk for knit reconstruction
365
        reconstructor = _Reconstructor(self, self._lines,
0.9.17 by Aaron Bentley
Dynamically select snapshots based on all parents
366
                                       self._parents)
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
367
        reconstructor.reconstruct_version(lines, version_id)
0.9.25 by Aaron Bentley
More messy hacking
368
        #self._lines[version_id] = lines
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
369
        return lines
370
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
371
class MultiMemoryVersionedFile(BaseVersionedFile):
372
373
    def __init__(self, snapshot_interval=25, max_snapshots=None):
374
        BaseVersionedFile.__init__(self, snapshot_interval, max_snapshots)
375
        self._diffs = {}
376
377
    def add_diff(self, diff, version_id, parent_ids):
378
        self._diffs[version_id] = diff
379
        self._parents[version_id] = parent_ids
380
381
    def get_diff(self, version_id):
382
        return self._diffs[version_id]
383
0.9.31 by Aaron Bentley
Allow selecting MemoryVersionedFile from commandline
384
    def destroy(self):
385
        self._diffs = {}
386
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
387
388
class MultiVersionedFile(BaseVersionedFile):
389
390
    def __init__(self, filename, snapshot_interval=25, max_snapshots=None):
391
        BaseVersionedFile.__init__(self, snapshot_interval, max_snapshots)
392
        self._filename = filename
393
        self._diff_offset = {}
394
395
    def get_diff(self, version_id):
396
        start, count = self._diff_offset[version_id]
397
        infile = open(self._filename, 'rb')
398
        try:
399
            infile.seek(start)
400
            sio = StringIO(infile.read(count))
401
        finally:
402
            infile.close()
403
        zip_file = GzipFile(None, mode='rb', fileobj=sio)
404
        try:
405
            file_version_id = zip_file.readline()
406
            return MultiParent.from_patch(zip_file.readlines())
407
        finally:
408
            zip_file.close()
409
410
    def add_diff(self, diff, version_id, parent_ids):
411
        outfile = open(self._filename, 'ab')
412
        try:
413
            start = outfile.tell()
414
            try:
415
                zipfile = GzipFile(None, mode='ab', fileobj=outfile)
416
                zipfile.writelines(chain(['version %s\n' % version_id],
417
                                       diff.to_patch()))
418
            finally:
419
                zipfile.close()
420
            end = outfile.tell()
421
        finally:
422
            outfile.close()
423
        self._diff_offset[version_id] = (start, end-start)
424
        self._parents[version_id] = parent_ids
425
0.9.31 by Aaron Bentley
Allow selecting MemoryVersionedFile from commandline
426
    def destroy(self):
427
        try:
428
            os.unlink(self._filename)
429
        except OSError, e:
430
            if e.errno != errno.ENOENT:
431
                raise
432
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
433
434
class _Reconstructor(object):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
435
    """Build a text from the diffs, ancestry graph and cached lines"""
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
436
437
    def __init__(self, diffs, lines, parents):
438
        self.diffs = diffs
439
        self.lines = lines
440
        self.parents = parents
441
        self.cursor = {}
442
443
    def reconstruct(self, lines, parent_text, version_id):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
444
        """Append the lines referred to by a ParentText to lines"""
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
445
        parent_id = self.parents[version_id][parent_text.parent]
446
        end = parent_text.parent_pos + parent_text.num_lines
0.9.17 by Aaron Bentley
Dynamically select snapshots based on all parents
447
        return self._reconstruct(lines, parent_id, parent_text.parent_pos,
448
                                 end)
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
449
450
    def _reconstruct(self, lines, req_version_id, req_start, req_end):
0.9.10 by Aaron Bentley
Text reconstruction seems to work
451
        """Append lines for the requested version_id range"""
452
        # stack of pending range requests
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
453
        pending_reqs = [(req_version_id, req_start, req_end)]
454
        while len(pending_reqs) > 0:
455
            req_version_id, req_start, req_end = pending_reqs.pop()
0.9.10 by Aaron Bentley
Text reconstruction seems to work
456
            # lazily allocate cursors for versions
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
457
            try:
458
                start, end, kind, data, iterator = self.cursor[req_version_id]
459
            except KeyError:
0.9.29 by Aaron Bentley
Support using disk for knit reconstruction
460
                iterator = self.diffs.get_diff(req_version_id).range_iterator()
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
461
                start, end, kind, data = iterator.next()
0.9.22 by Aaron Bentley
Fix restoration bug
462
            if start > req_start:
0.9.29 by Aaron Bentley
Support using disk for knit reconstruction
463
                iterator = self.diffs.get_diff(req_version_id).range_iterator()
0.9.22 by Aaron Bentley
Fix restoration bug
464
                start, end, kind, data = iterator.next()
465
0.9.10 by Aaron Bentley
Text reconstruction seems to work
466
            # find the first hunk relevant to the request
467
            while end <= req_start:
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
468
                start, end, kind, data = iterator.next()
469
            self.cursor[req_version_id] = start, end, kind, data, iterator
0.9.10 by Aaron Bentley
Text reconstruction seems to work
470
            # if the hunk can't satisfy the whole request, split it in two,
471
            # and leave the second half for later.
472
            if req_end > end:
473
                pending_reqs.append((req_version_id, end, req_end))
474
                req_end = end
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
475
            if kind == 'new':
476
                lines.extend(data[req_start - start: (req_end - start)])
477
            else:
0.9.10 by Aaron Bentley
Text reconstruction seems to work
478
                # If the hunk is a ParentText, rewrite it as a range request
479
                # for the parent, and make it the next pending request.
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
480
                parent, parent_start, parent_end = data
0.9.10 by Aaron Bentley
Text reconstruction seems to work
481
                new_version_id = self.parents[req_version_id][parent]
482
                new_start = parent_start + req_start - start
483
                new_end = parent_end + req_end - end
484
                pending_reqs.append((new_version_id, new_start, new_end))
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
485
486
    def reconstruct_version(self, lines, version_id):
0.9.29 by Aaron Bentley
Support using disk for knit reconstruction
487
        length = self.diffs.get_diff(version_id).num_lines()
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
488
        return self._reconstruct(lines, version_id, 0, length)
0.9.25 by Aaron Bentley
More messy hacking
489
490
def gzip_string(lines):
491
    sio = StringIO()
492
    data_file = GzipFile(None, mode='wb', fileobj=sio)
493
    data_file.writelines(lines)
494
    data_file.close()
495
    return sio.getvalue()