~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
78
78
        """Returns whether version is present."""
79
79
        raise NotImplementedError(self.has_version)
80
80
 
81
 
    def add_lines(self, version_id, parents, lines):
 
81
    def add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
 
82
        """Add a text to the versioned file via a pregenerated delta.
 
83
 
 
84
        :param version_id: The version id being added.
 
85
        :param parents: The parents of the version_id.
 
86
        :param delta_parent: The parent this delta was created against.
 
87
        :param sha1: The sha1 of the full text.
 
88
        :param delta: The delta instructions. See get_delta for details.
 
89
        """
 
90
        self._check_write_ok()
 
91
        if self.has_version(version_id):
 
92
            raise errors.RevisionAlreadyPresent(version_id, self)
 
93
        return self._add_delta(version_id, parents, delta_parent, sha1, noeol, delta)
 
94
 
 
95
    def _add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
 
96
        """Class specific routine to add a delta.
 
97
 
 
98
        This generic version simply applies the delta to the delta_parent and
 
99
        then inserts it.
 
100
        """
 
101
        # strip annotation from delta
 
102
        new_delta = []
 
103
        for start, stop, delta_len, delta_lines in delta:
 
104
            new_delta.append((start, stop, delta_len, [text for origin, text in delta_lines]))
 
105
        if delta_parent is not None:
 
106
            parent_full = self.get_lines(delta_parent)
 
107
        else:
 
108
            parent_full = []
 
109
        new_full = self._apply_delta(parent_full, new_delta)
 
110
        # its impossible to have noeol on an empty file
 
111
        if noeol and new_full[-1][-1] == '\n':
 
112
            new_full[-1] = new_full[-1][:-1]
 
113
        self.add_lines(version_id, parents, new_full)
 
114
 
 
115
    def add_lines(self, version_id, parents, lines, parent_texts=None):
82
116
        """Add a single text on top of the versioned file.
83
117
 
84
118
        Must raise RevisionAlreadyPresent if the new version is
86
120
 
87
121
        Must raise RevisionNotPresent if any of the given parents are
88
122
        not present in file history.
 
123
        :param parent_texts: An optional dictionary containing the opaque 
 
124
             representations of some or all of the parents of 
 
125
             version_id to allow delta optimisations. 
 
126
             VERY IMPORTANT: the texts must be those returned
 
127
             by add_lines or data corruption can be caused.
 
128
        :return: An opaque representation of the inserted version which can be
 
129
                 provided back to future add_lines calls in the parent_texts
 
130
                 dictionary.
89
131
        """
90
132
        self._check_write_ok()
91
 
        return self._add_lines(version_id, parents, lines)
 
133
        return self._add_lines(version_id, parents, lines, parent_texts)
92
134
 
93
 
    def _add_lines(self, version_id, parents, lines):
 
135
    def _add_lines(self, version_id, parents, lines, parent_texts):
94
136
        """Helper to do the class specific add_lines."""
95
137
        raise NotImplementedError(self.add_lines)
96
138
 
97
 
    def add_lines_with_ghosts(self, version_id, parents, lines):
98
 
        """Add lines to the versioned file, allowing ghosts to be present."""
 
139
    def add_lines_with_ghosts(self, version_id, parents, lines,
 
140
                              parent_texts=None):
 
141
        """Add lines to the versioned file, allowing ghosts to be present.
 
142
        
 
143
        This takes the same parameters as add_lines.
 
144
        """
99
145
        self._check_write_ok()
100
 
        return self._add_lines_with_ghosts(version_id, parents, lines)
 
146
        return self._add_lines_with_ghosts(version_id, parents, lines,
 
147
                                           parent_texts)
101
148
 
102
 
    def _add_lines_with_ghosts(self, version_id, parents, lines):
 
149
    def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts):
103
150
        """Helper to do class specific add_lines_with_ghosts."""
104
151
        raise NotImplementedError(self.add_lines_with_ghosts)
105
152
 
156
203
        """Helper for fix_parents."""
157
204
        raise NotImplementedError(self.fix_parents)
158
205
 
 
206
    def get_delta(self, version):
 
207
        """Get a delta for constructing version from some other version.
 
208
        
 
209
        :return: (delta_parent, sha1, noeol, delta)
 
210
        Where delta_parent is a version id or None to indicate no parent.
 
211
        """
 
212
        raise NotImplementedError(self.get_delta)
 
213
 
 
214
    def get_deltas(self, versions):
 
215
        """Get multiple deltas at once for constructing versions.
 
216
        
 
217
        :return: dict(version_id:(delta_parent, sha1, noeol, delta))
 
218
        Where delta_parent is a version id or None to indicate no parent, and
 
219
        version_id is the version_id created by that delta.
 
220
        """
 
221
        result = {}
 
222
        for version in versions:
 
223
            result[version] = self.get_delta(version)
 
224
        return result
 
225
 
159
226
    def get_suffixes(self):
160
227
        """Return the file suffixes associated with this versioned file."""
161
228
        raise NotImplementedError(self.get_suffixes)
256
323
    def annotate(self, version_id):
257
324
        return list(self.annotate_iter(version_id))
258
325
 
 
326
    def _apply_delta(self, lines, delta):
 
327
        """Apply delta to lines."""
 
328
        lines = list(lines)
 
329
        offset = 0
 
330
        for start, end, count, delta_lines in delta:
 
331
            lines[offset+start:offset+end] = delta_lines
 
332
            offset = offset + (start - end) + count
 
333
        return lines
 
334
 
259
335
    def join(self, other, pb=None, msg=None, version_ids=None,
260
336
             ignore_missing=False):
261
337
        """Integrate versions from other into this versioned file.
372
448
        # 
373
449
        # TODO: Show some version information (e.g. author, date) on 
374
450
        # conflicted regions.
 
451
        
 
452
        # We previously considered either 'unchanged' or 'killed-both' lines
 
453
        # to be possible places to resynchronize.  However, assuming agreement
 
454
        # on killed-both lines may be too agressive. -- mbp 20060324
375
455
        for state, line in plan:
376
 
            if state == 'unchanged' or state == 'killed-both':
 
456
            if state == 'unchanged':
377
457
                # resync and flush queued conflicts changes if any
378
458
                if not lines_a and not lines_b:
379
459
                    pass
456
536
        graph = self.source.get_graph()
457
537
        order = topo_sort(graph.items())
458
538
        pb = ui.ui_factory.nested_progress_bar()
 
539
        parent_texts = {}
459
540
        try:
 
541
            # TODO for incremental cross-format work:
 
542
            # make a versioned file with the following content:
 
543
            # all revisions we have been asked to join
 
544
            # all their ancestors that are *not* in target already.
 
545
            # the immediate parents of the above two sets, with 
 
546
            # empty parent lists - these versions are in target already
 
547
            # and the incorrect version data will be ignored.
 
548
            # TODO: for all ancestors that are present in target already,
 
549
            # check them for consistent data, this requires moving sha1 from
 
550
            # 
 
551
            # TODO: remove parent texts when they are not relevant any more for 
 
552
            # memory pressure reduction. RBC 20060313
 
553
            # pb.update('Converting versioned data', 0, len(order))
 
554
            # deltas = self.source.get_deltas(order)
460
555
            for index, version in enumerate(order):
461
556
                pb.update('Converting versioned data', index, len(order))
462
 
                target.add_lines(version,
463
 
                                 self.source.get_parents(version),
464
 
                                 self.source.get_lines(version))
 
557
                parent_text = target.add_lines(version,
 
558
                                               self.source.get_parents(version),
 
559
                                               self.source.get_lines(version),
 
560
                                               parent_texts=parent_texts)
 
561
                parent_texts[version] = parent_text
 
562
                #delta_parent, sha1, noeol, delta = deltas[version]
 
563
                #target.add_delta(version,
 
564
                #                 self.source.get_parents(version),
 
565
                #                 delta_parent,
 
566
                #                 sha1,
 
567
                #                 noeol,
 
568
                #                 delta)
 
569
                #target.get_lines(version)
465
570
            
466
571
            # this should hit the native code path for target
467
572
            if target is not self.target: