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])
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():
82
85
def range_iterator(self):
86
"""Iterate through the hunks, with range indicated
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)
84
94
for hunk in self.hunks:
85
95
if isinstance(hunk, NewText):
181
194
class _Reconstructor(object):
195
"""Build a text from the diffs, ancestry graph and cached lines"""
183
197
def __init__(self, diffs, lines, parents):
185
198
self.diffs = diffs
186
199
self.lines = lines
187
200
self.parents = parents
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)
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
200
217
start, end, kind, data, iterator = self.cursor[req_version_id]
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.
228
pending_reqs.append((req_version_id, end, req_end))
207
230
if kind == 'new':
208
231
lines.extend(data[req_start - start: (req_end - start)])
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))