~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to multiparent/__init__.py

  • Committer: Aaron Bentley
  • Date: 2007-04-11 03:45:52 UTC
  • mto: (2520.4.1 bzr.mpbundle)
  • mto: This revision was merged to the branch mainline in revision 2631.
  • Revision ID: aaron.bentley@utoronto.ca-20070411034552-r3o32h9akqd7wrll
Text reconstruction seems to work

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
    @staticmethod
21
21
    def from_lines(text, parents=()):
 
22
        """Produce a MultiParent from a list of lines and parents"""
22
23
        def compare(parent):
23
24
            return SequenceMatcher(None, parent, text).get_matching_blocks()
24
25
        parent_comparisons = [compare(p) for p in parents]
71
72
 
72
73
    @classmethod
73
74
    def from_texts(cls, text, parents=()):
 
75
        """Produce a MultiParent from a text and list of parent text"""
74
76
        return cls.from_lines(text.splitlines(True),
75
77
                              [p.splitlines(True) for p in parents])
76
78
 
77
79
    def to_patch(self):
 
80
        """Yield text lines for a patch"""
78
81
        for hunk in self.hunks:
79
82
            for line in hunk.to_patch():
80
83
                yield line
81
84
 
82
85
    def range_iterator(self):
 
86
        """Iterate through the hunks, with range indicated
 
87
 
 
88
        kind is "new" or "parent".
 
89
        for "new", data is a list of lines.
 
90
        for "parent", data is (parent, parent_start, parent_end)
 
91
        :return: a generator of (start, end, kind, data)
 
92
        """
83
93
        start = 0
84
94
        for hunk in self.hunks:
85
95
            if isinstance(hunk, NewText):
97
107
 
98
108
 
99
109
class NewText(object):
 
110
    """The contents of text that is introduced by this text"""
100
111
 
101
112
    def __init__(self, lines):
102
113
        self.lines = lines
117
128
 
118
129
 
119
130
class ParentText(object):
 
131
    """A reference to text present in a parent text"""
120
132
 
121
133
    def __init__(self, parent, parent_pos, child_pos, num_lines):
122
134
        self.parent = parent
139
151
 
140
152
 
141
153
class MultiVersionedFile(object):
 
154
    """VersionedFile skeleton for MultiParent"""
142
155
 
143
156
    def __init__(self):
144
157
        self._diffs = {}
179
192
 
180
193
 
181
194
class _Reconstructor(object):
 
195
    """Build a text from the diffs, ancestry graph and cached lines"""
182
196
 
183
197
    def __init__(self, diffs, lines, parents):
184
 
 
185
198
        self.diffs = diffs
186
199
        self.lines = lines
187
200
        self.parents = parents
188
201
        self.cursor = {}
189
202
 
190
203
    def reconstruct(self, lines, parent_text, version_id):
 
204
        """Append the lines referred to by a ParentText to lines"""
191
205
        parent_id = self.parents[version_id][parent_text.parent]
192
206
        end = parent_text.parent_pos + parent_text.num_lines
193
207
        return self._reconstruct(lines, parent_id, parent_text.parent_pos, end)
194
208
 
195
209
    def _reconstruct(self, lines, req_version_id, req_start, req_end):
 
210
        """Append lines for the requested version_id range"""
 
211
        # stack of pending range requests
196
212
        pending_reqs = [(req_version_id, req_start, req_end)]
197
213
        while len(pending_reqs) > 0:
198
214
            req_version_id, req_start, req_end = pending_reqs.pop()
 
215
            # lazily allocate cursors for versions
199
216
            try:
200
217
                start, end, kind, data, iterator = self.cursor[req_version_id]
201
218
            except KeyError:
202
219
                iterator = self.diffs[req_version_id].range_iterator()
203
220
                start, end, kind, data = iterator.next()
204
 
            while end < req_start:
 
221
            # find the first hunk relevant to the request
 
222
            while end <= req_start:
205
223
                start, end, kind, data = iterator.next()
206
224
            self.cursor[req_version_id] = start, end, kind, data, iterator
 
225
            # if the hunk can't satisfy the whole request, split it in two,
 
226
            # and leave the second half for later.
 
227
            if req_end > end:
 
228
                pending_reqs.append((req_version_id, end, req_end))
 
229
                req_end = end
207
230
            if kind == 'new':
208
231
                lines.extend(data[req_start - start: (req_end - start)])
209
232
            else:
 
233
                # If the hunk is a ParentText, rewrite it as a range request
 
234
                # for the parent, and make it the next pending request.
210
235
                parent, parent_start, parent_end = data
211
 
                version_id = self.parents[req_version_id][parent]
212
 
                sub_start = parent_start + req_start - start
213
 
                sub_end = parent_end + req_end - end
214
 
                pending_reqs.append((version_id, sub_start, sub_end))
 
236
                new_version_id = self.parents[req_version_id][parent]
 
237
                new_start = parent_start + req_start - start
 
238
                new_end = parent_end + req_end - end
 
239
                pending_reqs.append((new_version_id, new_start, new_end))