/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Martin Pool
  • Date: 2007-09-14 06:31:28 UTC
  • mfrom: (2822 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2823.
  • Revision ID: mbp@sourcefrog.net-20070914063128-0p7mh6zfb4pzdg9p
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
from bzrlib import (
26
26
    errors,
27
27
    osutils,
 
28
    multiparent,
28
29
    tsort,
29
30
    revision,
30
31
    ui,
32
33
from bzrlib.transport.memory import MemoryTransport
33
34
""")
34
35
 
 
36
from cStringIO import StringIO
 
37
 
35
38
from bzrlib.inter import InterObject
36
39
from bzrlib.textmerge import TextMerge
37
 
from bzrlib.symbol_versioning import (deprecated_function,
38
 
        deprecated_method,
39
 
        zero_eight,
40
 
        )
41
40
 
42
41
 
43
42
class VersionedFile(object):
66
65
        """Copy this versioned file to name on transport."""
67
66
        raise NotImplementedError(self.copy_to)
68
67
 
69
 
    @deprecated_method(zero_eight)
70
 
    def names(self):
71
 
        """Return a list of all the versions in this versioned file.
72
 
 
73
 
        Please use versionedfile.versions() now.
74
 
        """
75
 
        return self.versions()
76
 
 
77
68
    def versions(self):
78
69
        """Return a unsorted list of versions."""
79
70
        raise NotImplementedError(self.versions)
86
77
        """Returns whether version is present."""
87
78
        raise NotImplementedError(self.has_version)
88
79
 
89
 
    def add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
90
 
        """Add a text to the versioned file via a pregenerated delta.
91
 
 
92
 
        :param version_id: The version id being added.
93
 
        :param parents: The parents of the version_id.
94
 
        :param delta_parent: The parent this delta was created against.
95
 
        :param sha1: The sha1 of the full text.
96
 
        :param delta: The delta instructions. See get_delta for details.
97
 
        """
98
 
        version_id = osutils.safe_revision_id(version_id)
99
 
        parents = [osutils.safe_revision_id(v) for v in parents]
100
 
        self._check_write_ok()
101
 
        if self.has_version(version_id):
102
 
            raise errors.RevisionAlreadyPresent(version_id, self)
103
 
        return self._add_delta(version_id, parents, delta_parent, sha1, noeol, delta)
104
 
 
105
 
    def _add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
106
 
        """Class specific routine to add a delta.
107
 
 
108
 
        This generic version simply applies the delta to the delta_parent and
109
 
        then inserts it.
110
 
        """
111
 
        # strip annotation from delta
112
 
        new_delta = []
113
 
        for start, stop, delta_len, delta_lines in delta:
114
 
            new_delta.append((start, stop, delta_len, [text for origin, text in delta_lines]))
115
 
        if delta_parent is not None:
116
 
            parent_full = self.get_lines(delta_parent)
117
 
        else:
118
 
            parent_full = []
119
 
        new_full = self._apply_delta(parent_full, new_delta)
120
 
        # its impossible to have noeol on an empty file
121
 
        if noeol and new_full[-1][-1] == '\n':
122
 
            new_full[-1] = new_full[-1][:-1]
123
 
        self.add_lines(version_id, parents, new_full)
124
 
 
125
 
    def add_lines(self, version_id, parents, lines, parent_texts=None):
 
80
    def add_lines(self, version_id, parents, lines, parent_texts=None,
 
81
        left_matching_blocks=None, nostore_sha=None, random_id=False,
 
82
        check_content=True):
126
83
        """Add a single text on top of the versioned file.
127
84
 
128
85
        Must raise RevisionAlreadyPresent if the new version is
130
87
 
131
88
        Must raise RevisionNotPresent if any of the given parents are
132
89
        not present in file history.
 
90
 
 
91
        :param lines: A list of lines. Each line must be a bytestring. And all
 
92
            of them except the last must be terminated with \n and contain no
 
93
            other \n's. The last line may either contain no \n's or a single
 
94
            terminated \n. If the lines list does meet this constraint the add
 
95
            routine may error or may succeed - but you will be unable to read
 
96
            the data back accurately. (Checking the lines have been split
 
97
            correctly is expensive and extremely unlikely to catch bugs so it
 
98
            is not done at runtime unless check_content is True.)
133
99
        :param parent_texts: An optional dictionary containing the opaque 
134
 
             representations of some or all of the parents of 
135
 
             version_id to allow delta optimisations. 
136
 
             VERY IMPORTANT: the texts must be those returned
137
 
             by add_lines or data corruption can be caused.
138
 
        :return: An opaque representation of the inserted version which can be
139
 
                 provided back to future add_lines calls in the parent_texts
140
 
                 dictionary.
 
100
            representations of some or all of the parents of version_id to
 
101
            allow delta optimisations.  VERY IMPORTANT: the texts must be those
 
102
            returned by add_lines or data corruption can be caused.
 
103
        :param left_matching_blocks: a hint about which areas are common
 
104
            between the text and its left-hand-parent.  The format is
 
105
            the SequenceMatcher.get_matching_blocks format.
 
106
        :param nostore_sha: Raise ExistingContent and do not add the lines to
 
107
            the versioned file if the digest of the lines matches this.
 
108
        :param random_id: If True a random id has been selected rather than
 
109
            an id determined by some deterministic process such as a converter
 
110
            from a foreign VCS. When True the backend may choose not to check
 
111
            for uniqueness of the resulting key within the versioned file, so
 
112
            this should only be done when the result is expected to be unique
 
113
            anyway.
 
114
        :param check_content: If True, the lines supplied are verified to be
 
115
            bytestrings that are correctly formed lines.
 
116
        :return: The text sha1, the number of bytes in the text, and an opaque
 
117
                 representation of the inserted version which can be provided
 
118
                 back to future add_lines calls in the parent_texts dictionary.
141
119
        """
142
120
        version_id = osutils.safe_revision_id(version_id)
143
121
        parents = [osutils.safe_revision_id(v) for v in parents]
144
122
        self._check_write_ok()
145
 
        return self._add_lines(version_id, parents, lines, parent_texts)
 
123
        return self._add_lines(version_id, parents, lines, parent_texts,
 
124
            left_matching_blocks, nostore_sha, random_id, check_content)
146
125
 
147
 
    def _add_lines(self, version_id, parents, lines, parent_texts):
 
126
    def _add_lines(self, version_id, parents, lines, parent_texts,
 
127
        left_matching_blocks, nostore_sha, random_id, check_content):
148
128
        """Helper to do the class specific add_lines."""
149
129
        raise NotImplementedError(self.add_lines)
150
130
 
151
131
    def add_lines_with_ghosts(self, version_id, parents, lines,
152
 
                              parent_texts=None):
 
132
        parent_texts=None, nostore_sha=None, random_id=False,
 
133
        check_content=True):
153
134
        """Add lines to the versioned file, allowing ghosts to be present.
154
135
        
155
 
        This takes the same parameters as add_lines.
 
136
        This takes the same parameters as add_lines and returns the same.
156
137
        """
157
138
        version_id = osutils.safe_revision_id(version_id)
158
139
        parents = [osutils.safe_revision_id(v) for v in parents]
159
140
        self._check_write_ok()
160
141
        return self._add_lines_with_ghosts(version_id, parents, lines,
161
 
                                           parent_texts)
 
142
            parent_texts, nostore_sha, random_id, check_content)
162
143
 
163
 
    def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts):
 
144
    def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts,
 
145
        nostore_sha, random_id, check_content):
164
146
        """Helper to do class specific add_lines_with_ghosts."""
165
147
        raise NotImplementedError(self.add_lines_with_ghosts)
166
148
 
244
226
        """Helper for fix_parents."""
245
227
        raise NotImplementedError(self.fix_parents)
246
228
 
247
 
    def get_delta(self, version):
248
 
        """Get a delta for constructing version from some other version.
249
 
        
250
 
        :return: (delta_parent, sha1, noeol, delta)
251
 
        Where delta_parent is a version id or None to indicate no parent.
252
 
        """
253
 
        raise NotImplementedError(self.get_delta)
254
 
 
255
 
    def get_deltas(self, version_ids):
256
 
        """Get multiple deltas at once for constructing versions.
257
 
        
258
 
        :return: dict(version_id:(delta_parent, sha1, noeol, delta))
259
 
        Where delta_parent is a version id or None to indicate no parent, and
260
 
        version_id is the version_id created by that delta.
261
 
        """
262
 
        result = {}
263
 
        for version_id in version_ids:
264
 
            result[version_id] = self.get_delta(version_id)
265
 
        return result
 
229
    def get_format_signature(self):
 
230
        """Get a text description of the data encoding in this file.
 
231
        
 
232
        :since: 0.19
 
233
        """
 
234
        raise NotImplementedError(self.get_format_signature)
 
235
 
 
236
    def make_mpdiffs(self, version_ids):
 
237
        """Create multiparent diffs for specified versions"""
 
238
        knit_versions = set()
 
239
        for version_id in version_ids:
 
240
            knit_versions.add(version_id)
 
241
            knit_versions.update(self.get_parents(version_id))
 
242
        lines = dict(zip(knit_versions,
 
243
            self._get_lf_split_line_list(knit_versions)))
 
244
        diffs = []
 
245
        for version_id in version_ids:
 
246
            target = lines[version_id]
 
247
            parents = [lines[p] for p in self.get_parents(version_id)]
 
248
            if len(parents) > 0:
 
249
                left_parent_blocks = self._extract_blocks(version_id,
 
250
                                                          parents[0], target)
 
251
            else:
 
252
                left_parent_blocks = None
 
253
            diffs.append(multiparent.MultiParent.from_lines(target, parents,
 
254
                         left_parent_blocks))
 
255
        return diffs
 
256
 
 
257
    def _extract_blocks(self, version_id, source, target):
 
258
        return None
 
259
 
 
260
    def add_mpdiffs(self, records):
 
261
        """Add mpdiffs to this versionedfile
 
262
 
 
263
        Records should be iterables of version, parents, expected_sha1,
 
264
        mpdiff.  mpdiff should be a MultiParent instance.
 
265
        """
 
266
        vf_parents = {}
 
267
        mpvf = multiparent.MultiMemoryVersionedFile()
 
268
        versions = []
 
269
        for version, parent_ids, expected_sha1, mpdiff in records:
 
270
            versions.append(version)
 
271
            mpvf.add_diff(mpdiff, version, parent_ids)
 
272
        needed_parents = set()
 
273
        for version, parent_ids, expected_sha1, mpdiff in records:
 
274
            needed_parents.update(p for p in parent_ids
 
275
                                  if not mpvf.has_version(p))
 
276
        for parent_id, lines in zip(needed_parents,
 
277
                                 self._get_lf_split_line_list(needed_parents)):
 
278
            mpvf.add_version(lines, parent_id, [])
 
279
        for (version, parent_ids, expected_sha1, mpdiff), lines in\
 
280
            zip(records, mpvf.get_line_list(versions)):
 
281
            if len(parent_ids) == 1:
 
282
                left_matching_blocks = list(mpdiff.get_matching_blocks(0,
 
283
                    mpvf.get_diff(parent_ids[0]).num_lines()))
 
284
            else:
 
285
                left_matching_blocks = None
 
286
            _, _, version_text = self.add_lines(version, parent_ids, lines,
 
287
                vf_parents, left_matching_blocks=left_matching_blocks)
 
288
            vf_parents[version] = version_text
 
289
        for (version, parent_ids, expected_sha1, mpdiff), sha1 in\
 
290
             zip(records, self.get_sha1s(versions)):
 
291
            if expected_sha1 != sha1:
 
292
                raise errors.VersionedFileInvalidChecksum(version)
266
293
 
267
294
    def get_sha1(self, version_id):
268
295
        """Get the stored sha1 sum for the given revision.
271
298
        """
272
299
        raise NotImplementedError(self.get_sha1)
273
300
 
 
301
    def get_sha1s(self, version_ids):
 
302
        """Get the stored sha1 sums for the given revisions.
 
303
 
 
304
        :param version_ids: The names of the versions to lookup
 
305
        :return: a list of sha1s in order according to the version_ids
 
306
        """
 
307
        raise NotImplementedError(self.get_sha1)
 
308
 
274
309
    def get_suffixes(self):
275
310
        """Return the file suffixes associated with this versioned file."""
276
311
        raise NotImplementedError(self.get_suffixes)
300
335
        """
301
336
        raise NotImplementedError(self.get_lines)
302
337
 
 
338
    def _get_lf_split_line_list(self, version_ids):
 
339
        return [StringIO(t).readlines() for t in self.get_texts(version_ids)]
 
340
 
303
341
    def get_ancestry(self, version_ids, topo_sorted=True):
304
342
        """Return a list of all ancestors of given version(s). This
305
343
        will not include the null revision.
332
370
        :param version_ids: Versions to select.
333
371
                            None means retrieve all versions.
334
372
        """
 
373
        if version_ids is None:
 
374
            return dict(self.iter_parents(self.versions()))
335
375
        result = {}
336
 
        if version_ids is None:
337
 
            for version in self.versions():
338
 
                result[version] = self.get_parents(version)
339
 
        else:
340
 
            pending = set(osutils.safe_revision_id(v) for v in version_ids)
341
 
            while pending:
342
 
                version = pending.pop()
343
 
                if version in result:
344
 
                    continue
345
 
                parents = self.get_parents(version)
 
376
        pending = set(osutils.safe_revision_id(v) for v in version_ids)
 
377
        while pending:
 
378
            this_iteration = pending
 
379
            pending = set()
 
380
            for version, parents in self.iter_parents(this_iteration):
 
381
                result[version] = parents
346
382
                for parent in parents:
347
383
                    if parent in result:
348
384
                        continue
349
385
                    pending.add(parent)
350
 
                result[version] = parents
351
386
        return result
352
387
 
353
388
    def get_graph_with_ghosts(self):
358
393
        """
359
394
        raise NotImplementedError(self.get_graph_with_ghosts)
360
395
 
361
 
    @deprecated_method(zero_eight)
362
 
    def parent_names(self, version):
363
 
        """Return version names for parents of a version.
364
 
        
365
 
        See get_parents for the current api.
366
 
        """
367
 
        return self.get_parents(version)
368
 
 
369
396
    def get_parents(self, version_id):
370
397
        """Return version names for parents of a version.
371
398
 
443
470
        """
444
471
        raise NotImplementedError(self.iter_lines_added_or_present_in_versions)
445
472
 
 
473
    def iter_parents(self, version_ids):
 
474
        """Iterate through the parents for many version ids.
 
475
 
 
476
        :param version_ids: An iterable yielding version_ids.
 
477
        :return: An iterator that yields (version_id, parents). Requested 
 
478
            version_ids not present in the versioned file are simply skipped.
 
479
            The order is undefined, allowing for different optimisations in
 
480
            the underlying implementation.
 
481
        """
 
482
        for version_id in version_ids:
 
483
            try:
 
484
                yield version_id, tuple(self.get_parents(version_id))
 
485
            except errors.RevisionNotPresent:
 
486
                pass
 
487
 
446
488
    def transaction_finished(self):
447
489
        """The transaction that this file was opened in has finished.
448
490
 
451
493
        """
452
494
        self.finished = True
453
495
 
454
 
    @deprecated_method(zero_eight)
455
 
    def walk(self, version_ids=None):
456
 
        """Walk the versioned file as a weave-like structure, for
457
 
        versions relative to version_ids.  Yields sequence of (lineno,
458
 
        insert, deletes, text) for each relevant line.
459
 
 
460
 
        Must raise RevisionNotPresent if any of the specified versions
461
 
        are not present in the file history.
462
 
 
463
 
        :param version_ids: the version_ids to walk with respect to. If not
464
 
                            supplied the entire weave-like structure is walked.
465
 
 
466
 
        walk is deprecated in favour of iter_lines_added_or_present_in_versions
467
 
        """
468
 
        raise NotImplementedError(self.walk)
469
 
 
470
 
    @deprecated_method(zero_eight)
471
 
    def iter_names(self):
472
 
        """Walk the names list."""
473
 
        return iter(self.versions())
474
 
 
475
496
    def plan_merge(self, ver_a, ver_b):
476
497
        """Return pseudo-annotation indicating how the two versions merge.
477
498
 
628
649
            # TODO: remove parent texts when they are not relevant any more for 
629
650
            # memory pressure reduction. RBC 20060313
630
651
            # pb.update('Converting versioned data', 0, len(order))
631
 
            # deltas = self.source.get_deltas(order)
632
652
            for index, version in enumerate(order):
633
653
                pb.update('Converting versioned data', index, len(order))
634
 
                parent_text = target.add_lines(version,
 
654
                _, _, parent_text = target.add_lines(version,
635
655
                                               self.source.get_parents(version),
636
656
                                               self.source.get_lines(version),
637
657
                                               parent_texts=parent_texts)
638
658
                parent_texts[version] = parent_text
639
 
                #delta_parent, sha1, noeol, delta = deltas[version]
640
 
                #target.add_delta(version,
641
 
                #                 self.source.get_parents(version),
642
 
                #                 delta_parent,
643
 
                #                 sha1,
644
 
                #                 noeol,
645
 
                #                 delta)
646
 
                #target.get_lines(version)
647
659
            
648
660
            # this should hit the native code path for target
649
661
            if target is not self.target: