2484.1.1
by John Arbash Meinel
Add an initial function to read knit indexes in pyrex. |
1 |
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
2 |
#
|
3 |
# This program is free software; you can redistribute it and/or modify
|
|
4 |
# it under the terms of the GNU General Public License as published by
|
|
5 |
# the Free Software Foundation; either version 2 of the License, or
|
|
6 |
# (at your option) any later version.
|
|
7 |
#
|
|
8 |
# This program is distributed in the hope that it will be useful,
|
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11 |
# GNU General Public License for more details.
|
|
12 |
#
|
|
13 |
# You should have received a copy of the GNU General Public License
|
|
14 |
# along with this program; if not, write to the Free Software
|
|
15 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
16 |
||
17 |
"""Knit versionedfile implementation.
|
|
18 |
||
19 |
A knit is a versioned file implementation that supports efficient append only
|
|
20 |
updates.
|
|
1563.2.6
by Robert Collins
Start check tests for knits (pending), and remove dead code. |
21 |
|
22 |
Knit file layout:
|
|
23 |
lifeless: the data file is made up of "delta records". each delta record has a delta header
|
|
24 |
that contains; (1) a version id, (2) the size of the delta (in lines), and (3) the digest of
|
|
25 |
the -expanded data- (ie, the delta applied to the parent). the delta also ends with a
|
|
26 |
end-marker; simply "end VERSION"
|
|
27 |
||
28 |
delta can be line or full contents.a
|
|
29 |
... the 8's there are the index number of the annotation.
|
|
30 |
version robertc@robertcollins.net-20051003014215-ee2990904cc4c7ad 7 c7d23b2a5bd6ca00e8e266cec0ec228158ee9f9e
|
|
31 |
59,59,3
|
|
32 |
8
|
|
33 |
8 if ie.executable:
|
|
34 |
8 e.set('executable', 'yes')
|
|
35 |
130,130,2
|
|
36 |
8 if elt.get('executable') == 'yes':
|
|
37 |
8 ie.executable = True
|
|
38 |
end robertc@robertcollins.net-20051003014215-ee2990904cc4c7ad
|
|
39 |
||
40 |
||
41 |
whats in an index:
|
|
42 |
09:33 < jrydberg> lifeless: each index is made up of a tuple of; version id, options, position, size, parents
|
|
43 |
09:33 < jrydberg> lifeless: the parents are currently dictionary compressed
|
|
44 |
09:33 < jrydberg> lifeless: (meaning it currently does not support ghosts)
|
|
45 |
09:33 < lifeless> right
|
|
46 |
09:33 < jrydberg> lifeless: the position and size is the range in the data file
|
|
47 |
||
48 |
||
49 |
so the index sequence is the dictionary compressed sequence number used
|
|
50 |
in the deltas to provide line annotation
|
|
51 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
52 |
"""
|
53 |
||
1563.2.6
by Robert Collins
Start check tests for knits (pending), and remove dead code. |
54 |
# TODOS:
|
55 |
# 10:16 < lifeless> make partial index writes safe
|
|
56 |
# 10:16 < lifeless> implement 'knit.check()' like weave.check()
|
|
57 |
# 10:17 < lifeless> record known ghosts so we can detect when they are filled in rather than the current 'reweave
|
|
58 |
# always' approach.
|
|
1563.2.11
by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis. |
59 |
# move sha1 out of the content so that join is faster at verifying parents
|
60 |
# record content length ?
|
|
1563.2.6
by Robert Collins
Start check tests for knits (pending), and remove dead code. |
61 |
|
62 |
||
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
63 |
from copy import copy |
1563.2.11
by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis. |
64 |
from cStringIO import StringIO |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
65 |
import difflib |
1596.2.28
by Robert Collins
more knit profile based tuning. |
66 |
from itertools import izip, chain |
1756.2.17
by Aaron Bentley
Fixes suggested by John Meinel |
67 |
import operator |
1563.2.6
by Robert Collins
Start check tests for knits (pending), and remove dead code. |
68 |
import os |
1628.1.2
by Robert Collins
More knit micro-optimisations. |
69 |
import sys |
1756.2.29
by Aaron Bentley
Remove basis knit support |
70 |
import warnings |
1594.2.19
by Robert Collins
More coalescing tweaks, and knit feedback. |
71 |
|
1594.2.17
by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading. |
72 |
import bzrlib |
1911.2.3
by John Arbash Meinel
Moving everything into a new location so that we can cache more than just revision ids |
73 |
from bzrlib import ( |
74 |
cache_utf8, |
|
75 |
errors, |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
76 |
osutils, |
2104.4.2
by John Arbash Meinel
Small cleanup and NEWS entry about fixing bug #65714 |
77 |
patiencediff, |
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
78 |
progress, |
1551.15.46
by Aaron Bentley
Move plan merge to tree |
79 |
merge, |
2196.2.1
by John Arbash Meinel
Merge Dmitry's optimizations and minimize the actual diff. |
80 |
ui, |
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
81 |
)
|
82 |
from bzrlib.errors import ( |
|
83 |
FileExists, |
|
84 |
NoSuchFile, |
|
85 |
KnitError, |
|
86 |
InvalidRevisionId, |
|
87 |
KnitCorrupt, |
|
88 |
KnitHeaderError, |
|
89 |
RevisionNotPresent, |
|
90 |
RevisionAlreadyPresent, |
|
91 |
)
|
|
1773.4.1
by Martin Pool
Add pyflakes makefile target; fix many warnings |
92 |
from bzrlib.tuned_gzip import GzipFile |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
93 |
from bzrlib.trace import mutter |
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
94 |
from bzrlib.osutils import ( |
95 |
contains_whitespace, |
|
96 |
contains_linebreaks, |
|
97 |
sha_strings, |
|
98 |
)
|
|
1756.2.29
by Aaron Bentley
Remove basis knit support |
99 |
from bzrlib.symbol_versioning import DEPRECATED_PARAMETER, deprecated_passed |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
100 |
from bzrlib.tsort import topo_sort |
2094.3.5
by John Arbash Meinel
Fix imports to ensure modules are loaded before they are used |
101 |
import bzrlib.ui |
1684.3.3
by Robert Collins
Add a special cased weaves to knit converter. |
102 |
import bzrlib.weave |
1911.2.1
by John Arbash Meinel
Cache encode/decode operations, saves memory and time. Especially when committing a new kernel tree with 7.7M new lines to annotate |
103 |
from bzrlib.versionedfile import VersionedFile, InterVersionedFile |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
104 |
|
105 |
||
106 |
# TODO: Split out code specific to this format into an associated object.
|
|
107 |
||
108 |
# TODO: Can we put in some kind of value to check that the index and data
|
|
109 |
# files belong together?
|
|
110 |
||
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
111 |
# TODO: accommodate binaries, perhaps by storing a byte count
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
112 |
|
113 |
# TODO: function to check whole file
|
|
114 |
||
115 |
# TODO: atomically append data, then measure backwards from the cursor
|
|
116 |
# position after writing to work out where it was located. we may need to
|
|
117 |
# bypass python file buffering.
|
|
118 |
||
119 |
DATA_SUFFIX = '.knit' |
|
120 |
INDEX_SUFFIX = '.kndx' |
|
121 |
||
122 |
||
123 |
class KnitContent(object): |
|
124 |
"""Content of a knit version to which deltas can be applied."""
|
|
125 |
||
126 |
def __init__(self, lines): |
|
127 |
self._lines = lines |
|
128 |
||
129 |
def annotate_iter(self): |
|
130 |
"""Yield tuples of (origin, text) for each content line."""
|
|
2151.1.1
by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests |
131 |
return iter(self._lines) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
132 |
|
133 |
def annotate(self): |
|
134 |
"""Return a list of (origin, text) tuples."""
|
|
135 |
return list(self.annotate_iter()) |
|
136 |
||
137 |
def line_delta_iter(self, new_lines): |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
138 |
"""Generate line-based delta from this content to new_lines."""
|
2151.1.1
by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests |
139 |
new_texts = new_lines.text() |
140 |
old_texts = self.text() |
|
1711.2.11
by John Arbash Meinel
Rename patiencediff.SequenceMatcher => PatienceSequenceMatcher and knit.SequenceMatcher => KnitSequenceMatcher |
141 |
s = KnitSequenceMatcher(None, old_texts, new_texts) |
2151.1.1
by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests |
142 |
for tag, i1, i2, j1, j2 in s.get_opcodes(): |
143 |
if tag == 'equal': |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
144 |
continue
|
2151.1.1
by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests |
145 |
# ofrom, oto, length, data
|
146 |
yield i1, i2, j2 - j1, new_lines._lines[j1:j2] |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
147 |
|
148 |
def line_delta(self, new_lines): |
|
149 |
return list(self.line_delta_iter(new_lines)) |
|
150 |
||
151 |
def text(self): |
|
152 |
return [text for origin, text in self._lines] |
|
153 |
||
1756.3.7
by Aaron Bentley
Avoid re-parsing texts version components |
154 |
def copy(self): |
155 |
return KnitContent(self._lines[:]) |
|
156 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
157 |
|
158 |
class _KnitFactory(object): |
|
159 |
"""Base factory for creating content objects."""
|
|
160 |
||
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
161 |
def make(self, lines, version_id): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
162 |
num_lines = len(lines) |
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
163 |
return KnitContent(zip([version_id] * num_lines, lines)) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
164 |
|
165 |
||
166 |
class KnitAnnotateFactory(_KnitFactory): |
|
167 |
"""Factory for creating annotated Content objects."""
|
|
168 |
||
169 |
annotated = True |
|
170 |
||
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
171 |
def parse_fulltext(self, content, version_id): |
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
172 |
"""Convert fulltext to internal representation
|
173 |
||
174 |
fulltext content is of the format
|
|
175 |
revid(utf8) plaintext\n
|
|
176 |
internal representation is of the format:
|
|
177 |
(revid, plaintext)
|
|
178 |
"""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
179 |
# TODO: jam 20070209 The tests expect this to be returned as tuples,
|
180 |
# but the code itself doesn't really depend on that.
|
|
181 |
# Figure out a way to not require the overhead of turning the
|
|
182 |
# list back into tuples.
|
|
183 |
lines = [tuple(line.split(' ', 1)) for line in content] |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
184 |
return KnitContent(lines) |
185 |
||
186 |
def parse_line_delta_iter(self, lines): |
|
2163.1.2
by John Arbash Meinel
Don't modify the list during parse_line_delta |
187 |
return iter(self.parse_line_delta(lines)) |
1628.1.2
by Robert Collins
More knit micro-optimisations. |
188 |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
189 |
def parse_line_delta(self, lines, version_id): |
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
190 |
"""Convert a line based delta into internal representation.
|
191 |
||
192 |
line delta is in the form of:
|
|
193 |
intstart intend intcount
|
|
194 |
1..count lines:
|
|
195 |
revid(utf8) newline\n
|
|
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
196 |
internal representation is
|
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
197 |
(start, end, count, [1..count tuples (revid, newline)])
|
198 |
"""
|
|
1628.1.2
by Robert Collins
More knit micro-optimisations. |
199 |
result = [] |
200 |
lines = iter(lines) |
|
201 |
next = lines.next |
|
2249.5.1
by John Arbash Meinel
Leave revision-ids in utf-8 when reading. |
202 |
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
203 |
cache = {} |
204 |
def cache_and_return(line): |
|
205 |
origin, text = line.split(' ', 1) |
|
206 |
return cache.setdefault(origin, origin), text |
|
207 |
||
1628.1.2
by Robert Collins
More knit micro-optimisations. |
208 |
# walk through the lines parsing.
|
209 |
for header in lines: |
|
210 |
start, end, count = [int(n) for n in header.split(',')] |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
211 |
contents = [tuple(next().split(' ', 1)) for i in xrange(count)] |
1628.1.2
by Robert Collins
More knit micro-optimisations. |
212 |
result.append((start, end, count, contents)) |
213 |
return result |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
214 |
|
2163.2.2
by John Arbash Meinel
Don't deal with annotations when we don't care about them. Saves another 300+ms |
215 |
def get_fulltext_content(self, lines): |
216 |
"""Extract just the content lines from a fulltext."""
|
|
217 |
return (line.split(' ', 1)[1] for line in lines) |
|
218 |
||
219 |
def get_linedelta_content(self, lines): |
|
220 |
"""Extract just the content from a line delta.
|
|
221 |
||
222 |
This doesn't return all of the extra information stored in a delta.
|
|
223 |
Only the actual content lines.
|
|
224 |
"""
|
|
225 |
lines = iter(lines) |
|
226 |
next = lines.next |
|
227 |
for header in lines: |
|
228 |
header = header.split(',') |
|
229 |
count = int(header[2]) |
|
230 |
for i in xrange(count): |
|
231 |
origin, text = next().split(' ', 1) |
|
232 |
yield text |
|
233 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
234 |
def lower_fulltext(self, content): |
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
235 |
"""convert a fulltext content record into a serializable form.
|
236 |
||
237 |
see parse_fulltext which this inverts.
|
|
238 |
"""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
239 |
# TODO: jam 20070209 We only do the caching thing to make sure that
|
240 |
# the origin is a valid utf-8 line, eventually we could remove it
|
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
241 |
return ['%s %s' % (o, t) for o, t in content._lines] |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
242 |
|
243 |
def lower_line_delta(self, delta): |
|
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
244 |
"""convert a delta into a serializable form.
|
245 |
||
1628.1.2
by Robert Collins
More knit micro-optimisations. |
246 |
See parse_line_delta which this inverts.
|
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
247 |
"""
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
248 |
# TODO: jam 20070209 We only do the caching thing to make sure that
|
249 |
# the origin is a valid utf-8 line, eventually we could remove it
|
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
250 |
out = [] |
251 |
for start, end, c, lines in delta: |
|
252 |
out.append('%d,%d,%d\n' % (start, end, c)) |
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
253 |
out.extend(origin + ' ' + text |
1911.2.1
by John Arbash Meinel
Cache encode/decode operations, saves memory and time. Especially when committing a new kernel tree with 7.7M new lines to annotate |
254 |
for origin, text in lines) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
255 |
return out |
256 |
||
257 |
||
258 |
class KnitPlainFactory(_KnitFactory): |
|
259 |
"""Factory for creating plain Content objects."""
|
|
260 |
||
261 |
annotated = False |
|
262 |
||
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
263 |
def parse_fulltext(self, content, version_id): |
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
264 |
"""This parses an unannotated fulltext.
|
265 |
||
266 |
Note that this is not a noop - the internal representation
|
|
267 |
has (versionid, line) - its just a constant versionid.
|
|
268 |
"""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
269 |
return self.make(content, version_id) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
270 |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
271 |
def parse_line_delta_iter(self, lines, version_id): |
2163.1.2
by John Arbash Meinel
Don't modify the list during parse_line_delta |
272 |
cur = 0 |
273 |
num_lines = len(lines) |
|
274 |
while cur < num_lines: |
|
275 |
header = lines[cur] |
|
276 |
cur += 1 |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
277 |
start, end, c = [int(n) for n in header.split(',')] |
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
278 |
yield start, end, c, zip([version_id] * c, lines[cur:cur+c]) |
2163.1.2
by John Arbash Meinel
Don't modify the list during parse_line_delta |
279 |
cur += c |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
280 |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
281 |
def parse_line_delta(self, lines, version_id): |
282 |
return list(self.parse_line_delta_iter(lines, version_id)) |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
283 |
|
2163.2.2
by John Arbash Meinel
Don't deal with annotations when we don't care about them. Saves another 300+ms |
284 |
def get_fulltext_content(self, lines): |
285 |
"""Extract just the content lines from a fulltext."""
|
|
286 |
return iter(lines) |
|
287 |
||
288 |
def get_linedelta_content(self, lines): |
|
289 |
"""Extract just the content from a line delta.
|
|
290 |
||
291 |
This doesn't return all of the extra information stored in a delta.
|
|
292 |
Only the actual content lines.
|
|
293 |
"""
|
|
294 |
lines = iter(lines) |
|
295 |
next = lines.next |
|
296 |
for header in lines: |
|
297 |
header = header.split(',') |
|
298 |
count = int(header[2]) |
|
299 |
for i in xrange(count): |
|
300 |
yield next() |
|
301 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
302 |
def lower_fulltext(self, content): |
303 |
return content.text() |
|
304 |
||
305 |
def lower_line_delta(self, delta): |
|
306 |
out = [] |
|
307 |
for start, end, c, lines in delta: |
|
308 |
out.append('%d,%d,%d\n' % (start, end, c)) |
|
309 |
out.extend([text for origin, text in lines]) |
|
310 |
return out |
|
311 |
||
312 |
||
313 |
def make_empty_knit(transport, relpath): |
|
314 |
"""Construct a empty knit at the specified location."""
|
|
1563.2.5
by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface. |
315 |
k = KnitVersionedFile(transport, relpath, 'w', KnitPlainFactory) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
316 |
k._data._open_file() |
317 |
||
318 |
||
319 |
class KnitVersionedFile(VersionedFile): |
|
320 |
"""Weave-like structure with faster random access.
|
|
321 |
||
322 |
A knit stores a number of texts and a summary of the relationships
|
|
323 |
between them. Texts are identified by a string version-id. Texts
|
|
324 |
are normally stored and retrieved as a series of lines, but can
|
|
325 |
also be passed as single strings.
|
|
326 |
||
327 |
Lines are stored with the trailing newline (if any) included, to
|
|
328 |
avoid special cases for files with no final newline. Lines are
|
|
329 |
composed of 8-bit characters, not unicode. The combination of
|
|
330 |
these approaches should mean any 'binary' file can be safely
|
|
331 |
stored and retrieved.
|
|
332 |
"""
|
|
333 |
||
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
334 |
def __init__(self, relpath, transport, file_mode=None, access_mode=None, |
1756.2.29
by Aaron Bentley
Remove basis knit support |
335 |
factory=None, basis_knit=DEPRECATED_PARAMETER, delta=True, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
336 |
create=False, create_parent_dir=False, delay_create=False, |
337 |
dir_mode=None): |
|
1563.2.25
by Robert Collins
Merge in upstream. |
338 |
"""Construct a knit at location specified by relpath.
|
339 |
|
|
340 |
:param create: If not True, only open an existing knit.
|
|
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
341 |
:param create_parent_dir: If True, create the parent directory if
|
342 |
creating the file fails. (This is used for stores with
|
|
343 |
hash-prefixes that may not exist yet)
|
|
344 |
:param delay_create: The calling code is aware that the knit won't
|
|
345 |
actually be created until the first data is stored.
|
|
1563.2.25
by Robert Collins
Merge in upstream. |
346 |
"""
|
1756.2.29
by Aaron Bentley
Remove basis knit support |
347 |
if deprecated_passed(basis_knit): |
348 |
warnings.warn("KnitVersionedFile.__(): The basis_knit parameter is" |
|
349 |
" deprecated as of bzr 0.9.", |
|
350 |
DeprecationWarning, stacklevel=2) |
|
1563.2.16
by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable. |
351 |
if access_mode is None: |
352 |
access_mode = 'w' |
|
1594.2.23
by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files. |
353 |
super(KnitVersionedFile, self).__init__(access_mode) |
1563.2.16
by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable. |
354 |
assert access_mode in ('r', 'w'), "invalid mode specified %r" % access_mode |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
355 |
self.transport = transport |
356 |
self.filename = relpath |
|
1563.2.16
by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable. |
357 |
self.factory = factory or KnitAnnotateFactory() |
358 |
self.writable = (access_mode == 'w') |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
359 |
self.delta = delta |
360 |
||
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
361 |
self._max_delta_chain = 200 |
362 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
363 |
self._index = _KnitIndex(transport, relpath + INDEX_SUFFIX, |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
364 |
access_mode, create=create, file_mode=file_mode, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
365 |
create_parent_dir=create_parent_dir, delay_create=delay_create, |
366 |
dir_mode=dir_mode) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
367 |
self._data = _KnitData(transport, relpath + DATA_SUFFIX, |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
368 |
access_mode, create=create and not len(self), file_mode=file_mode, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
369 |
create_parent_dir=create_parent_dir, delay_create=delay_create, |
370 |
dir_mode=dir_mode) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
371 |
|
1704.2.10
by Martin Pool
Add KnitVersionedFile.__repr__ method |
372 |
def __repr__(self): |
373 |
return '%s(%s)' % (self.__class__.__name__, |
|
374 |
self.transport.abspath(self.filename)) |
|
375 |
||
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
376 |
def _check_should_delta(self, first_parents): |
377 |
"""Iterate back through the parent listing, looking for a fulltext.
|
|
378 |
||
379 |
This is used when we want to decide whether to add a delta or a new
|
|
380 |
fulltext. It searches for _max_delta_chain parents. When it finds a
|
|
381 |
fulltext parent, it sees if the total size of the deltas leading up to
|
|
382 |
it is large enough to indicate that we want a new full text anyway.
|
|
383 |
||
384 |
Return True if we should create a new delta, False if we should use a
|
|
385 |
full text.
|
|
386 |
"""
|
|
387 |
delta_size = 0 |
|
388 |
fulltext_size = None |
|
389 |
delta_parents = first_parents |
|
2147.1.2
by John Arbash Meinel
Simplify the knit max-chain detection code. |
390 |
for count in xrange(self._max_delta_chain): |
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
391 |
parent = delta_parents[0] |
392 |
method = self._index.get_method(parent) |
|
393 |
pos, size = self._index.get_position(parent) |
|
394 |
if method == 'fulltext': |
|
395 |
fulltext_size = size |
|
396 |
break
|
|
397 |
delta_size += size |
|
398 |
delta_parents = self._index.get_parents(parent) |
|
2147.1.2
by John Arbash Meinel
Simplify the knit max-chain detection code. |
399 |
else: |
400 |
# We couldn't find a fulltext, so we must create a new one
|
|
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
401 |
return False |
2147.1.2
by John Arbash Meinel
Simplify the knit max-chain detection code. |
402 |
|
403 |
return fulltext_size > delta_size |
|
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
404 |
|
1596.2.37
by Robert Collins
Switch to delta based content copying in the generic versioned file copier. |
405 |
def _add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta): |
406 |
"""See VersionedFile._add_delta()."""
|
|
407 |
self._check_add(version_id, []) # should we check the lines ? |
|
408 |
self._check_versions_present(parents) |
|
409 |
present_parents = [] |
|
410 |
ghosts = [] |
|
411 |
parent_texts = {} |
|
412 |
for parent in parents: |
|
413 |
if not self.has_version(parent): |
|
414 |
ghosts.append(parent) |
|
415 |
else: |
|
416 |
present_parents.append(parent) |
|
417 |
||
418 |
if delta_parent is None: |
|
419 |
# reconstitute as full text.
|
|
420 |
assert len(delta) == 1 or len(delta) == 0 |
|
421 |
if len(delta): |
|
422 |
assert delta[0][0] == 0 |
|
1596.2.38
by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready. |
423 |
assert delta[0][1] == 0, delta[0][1] |
1596.2.37
by Robert Collins
Switch to delta based content copying in the generic versioned file copier. |
424 |
return super(KnitVersionedFile, self)._add_delta(version_id, |
425 |
parents, |
|
426 |
delta_parent, |
|
427 |
sha1, |
|
428 |
noeol, |
|
429 |
delta) |
|
430 |
||
431 |
digest = sha1 |
|
432 |
||
433 |
options = [] |
|
434 |
if noeol: |
|
435 |
options.append('no-eol') |
|
436 |
||
437 |
if delta_parent is not None: |
|
438 |
# determine the current delta chain length.
|
|
439 |
# To speed the extract of texts the delta chain is limited
|
|
440 |
# to a fixed number of deltas. This should minimize both
|
|
441 |
# I/O and the time spend applying deltas.
|
|
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
442 |
# The window was changed to a maximum of 200 deltas, but also added
|
443 |
# was a check that the total compressed size of the deltas is
|
|
444 |
# smaller than the compressed size of the fulltext.
|
|
445 |
if not self._check_should_delta([delta_parent]): |
|
446 |
# We don't want a delta here, just do a normal insertion.
|
|
1596.2.37
by Robert Collins
Switch to delta based content copying in the generic versioned file copier. |
447 |
return super(KnitVersionedFile, self)._add_delta(version_id, |
448 |
parents, |
|
449 |
delta_parent, |
|
450 |
sha1, |
|
451 |
noeol, |
|
452 |
delta) |
|
453 |
||
454 |
options.append('line-delta') |
|
455 |
store_lines = self.factory.lower_line_delta(delta) |
|
456 |
||
457 |
where, size = self._data.add_record(version_id, digest, store_lines) |
|
458 |
self._index.add_version(version_id, options, where, size, parents) |
|
459 |
||
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
460 |
def _add_raw_records(self, records, data): |
461 |
"""Add all the records 'records' with data pre-joined in 'data'.
|
|
462 |
||
463 |
:param records: A list of tuples(version_id, options, parents, size).
|
|
464 |
:param data: The data for the records. When it is written, the records
|
|
465 |
are adjusted to have pos pointing into data by the sum of
|
|
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
466 |
the preceding records sizes.
|
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
467 |
"""
|
468 |
# write all the data
|
|
469 |
pos = self._data.add_raw_record(data) |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
470 |
offset = 0 |
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
471 |
index_entries = [] |
472 |
for (version_id, options, parents, size) in records: |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
473 |
index_entries.append((version_id, options, pos+offset, |
474 |
size, parents)) |
|
475 |
if self._data._do_cache: |
|
476 |
self._data._cache[version_id] = data[offset:offset+size] |
|
477 |
offset += size |
|
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
478 |
self._index.add_versions(index_entries) |
479 |
||
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
480 |
def enable_cache(self): |
481 |
"""Start caching data for this knit"""
|
|
482 |
self._data.enable_cache() |
|
483 |
||
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
484 |
def clear_cache(self): |
485 |
"""Clear the data cache only."""
|
|
486 |
self._data.clear_cache() |
|
487 |
||
1563.2.15
by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages. |
488 |
def copy_to(self, name, transport): |
489 |
"""See VersionedFile.copy_to()."""
|
|
490 |
# copy the current index to a temp index to avoid racing with local
|
|
491 |
# writes
|
|
1955.3.30
by John Arbash Meinel
fix small bug |
492 |
transport.put_file_non_atomic(name + INDEX_SUFFIX + '.tmp', |
1955.3.24
by John Arbash Meinel
Update Knit to use the new non_atomic_foo functions |
493 |
self.transport.get(self._index._filename)) |
1563.2.15
by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages. |
494 |
# copy the data file
|
1711.7.25
by John Arbash Meinel
try/finally to close files, _KnitData was keeping a handle to a file it never used again, and using transport.rename() when it wanted transport.move() |
495 |
f = self._data._open_file() |
496 |
try: |
|
1955.3.8
by John Arbash Meinel
avoid some deprecation warnings in other parts of the code |
497 |
transport.put_file(name + DATA_SUFFIX, f) |
1711.7.25
by John Arbash Meinel
try/finally to close files, _KnitData was keeping a handle to a file it never used again, and using transport.rename() when it wanted transport.move() |
498 |
finally: |
499 |
f.close() |
|
500 |
# move the copied index into place
|
|
501 |
transport.move(name + INDEX_SUFFIX + '.tmp', name + INDEX_SUFFIX) |
|
1563.2.15
by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages. |
502 |
|
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
503 |
def create_empty(self, name, transport, mode=None): |
1955.3.8
by John Arbash Meinel
avoid some deprecation warnings in other parts of the code |
504 |
return KnitVersionedFile(name, transport, factory=self.factory, |
505 |
delta=self.delta, create=True) |
|
1563.2.15
by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages. |
506 |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
507 |
def _fix_parents(self, version_id, new_parents): |
1594.2.7
by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc. |
508 |
"""Fix the parents list for version.
|
509 |
|
|
510 |
This is done by appending a new version to the index
|
|
511 |
with identical data except for the parents list.
|
|
512 |
the parents list must be a superset of the current
|
|
513 |
list.
|
|
514 |
"""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
515 |
current_values = self._index._cache[version_id] |
1594.2.7
by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc. |
516 |
assert set(current_values[4]).difference(set(new_parents)) == set() |
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
517 |
self._index.add_version(version_id, |
1594.2.7
by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc. |
518 |
current_values[1], |
519 |
current_values[2], |
|
520 |
current_values[3], |
|
521 |
new_parents) |
|
522 |
||
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
523 |
def get_delta(self, version_id): |
524 |
"""Get a delta for constructing version from some other version."""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
525 |
version_id = osutils.safe_revision_id(version_id) |
2229.2.3
by Aaron Bentley
change reserved_id to is_reserved_id, add check_not_reserved for DRY |
526 |
self.check_not_reserved_id(version_id) |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
527 |
if not self.has_version(version_id): |
528 |
raise RevisionNotPresent(version_id, self.filename) |
|
529 |
||
530 |
parents = self.get_parents(version_id) |
|
531 |
if len(parents): |
|
532 |
parent = parents[0] |
|
533 |
else: |
|
534 |
parent = None |
|
535 |
data_pos, data_size = self._index.get_position(version_id) |
|
536 |
data, sha1 = self._data.read_records(((version_id, data_pos, data_size),))[version_id] |
|
1596.2.37
by Robert Collins
Switch to delta based content copying in the generic versioned file copier. |
537 |
noeol = 'no-eol' in self._index.get_options(version_id) |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
538 |
if 'fulltext' == self._index.get_method(version_id): |
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
539 |
new_content = self.factory.parse_fulltext(data, version_id) |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
540 |
if parent is not None: |
541 |
reference_content = self._get_content(parent) |
|
542 |
old_texts = reference_content.text() |
|
543 |
else: |
|
544 |
old_texts = [] |
|
545 |
new_texts = new_content.text() |
|
1711.2.11
by John Arbash Meinel
Rename patiencediff.SequenceMatcher => PatienceSequenceMatcher and knit.SequenceMatcher => KnitSequenceMatcher |
546 |
delta_seq = KnitSequenceMatcher(None, old_texts, new_texts) |
1596.2.37
by Robert Collins
Switch to delta based content copying in the generic versioned file copier. |
547 |
return parent, sha1, noeol, self._make_line_delta(delta_seq, new_content) |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
548 |
else: |
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
549 |
delta = self.factory.parse_line_delta(data, version_id) |
1596.2.37
by Robert Collins
Switch to delta based content copying in the generic versioned file copier. |
550 |
return parent, sha1, noeol, delta |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
551 |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
552 |
def get_graph_with_ghosts(self): |
553 |
"""See VersionedFile.get_graph_with_ghosts()."""
|
|
554 |
graph_items = self._index.get_graph() |
|
555 |
return dict(graph_items) |
|
556 |
||
1666.1.6
by Robert Collins
Make knit the default format. |
557 |
def get_sha1(self, version_id): |
558 |
"""See VersionedFile.get_sha1()."""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
559 |
version_id = osutils.safe_revision_id(version_id) |
1910.2.65
by Aaron Bentley
Remove the check-parent patch |
560 |
record_map = self._get_record_map([version_id]) |
561 |
method, content, digest, next = record_map[version_id] |
|
562 |
return digest |
|
1666.1.6
by Robert Collins
Make knit the default format. |
563 |
|
1563.2.15
by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages. |
564 |
@staticmethod
|
565 |
def get_suffixes(): |
|
566 |
"""See VersionedFile.get_suffixes()."""
|
|
567 |
return [DATA_SUFFIX, INDEX_SUFFIX] |
|
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
568 |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
569 |
def has_ghost(self, version_id): |
570 |
"""True if there is a ghost reference in the file to version_id."""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
571 |
version_id = osutils.safe_revision_id(version_id) |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
572 |
# maybe we have it
|
573 |
if self.has_version(version_id): |
|
574 |
return False |
|
1759.2.2
by Jelmer Vernooij
Revert some of my spelling fixes and fix some typos after review by Aaron. |
575 |
# optimisable if needed by memoising the _ghosts set.
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
576 |
items = self._index.get_graph() |
577 |
for node, parents in items: |
|
578 |
for parent in parents: |
|
579 |
if parent not in self._index._cache: |
|
580 |
if parent == version_id: |
|
581 |
return True |
|
582 |
return False |
|
583 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
584 |
def versions(self): |
585 |
"""See VersionedFile.versions."""
|
|
586 |
return self._index.get_versions() |
|
587 |
||
588 |
def has_version(self, version_id): |
|
589 |
"""See VersionedFile.has_version."""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
590 |
version_id = osutils.safe_revision_id(version_id) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
591 |
return self._index.has_version(version_id) |
592 |
||
593 |
__contains__ = has_version |
|
594 |
||
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
595 |
def _merge_annotations(self, content, parents, parent_texts={}, |
596 |
delta=None, annotated=None): |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
597 |
"""Merge annotations for content. This is done by comparing
|
1596.2.27
by Robert Collins
Note potential improvements in knit adds. |
598 |
the annotations based on changed to the text.
|
599 |
"""
|
|
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
600 |
if annotated: |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
601 |
delta_seq = None |
602 |
for parent_id in parents: |
|
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
603 |
merge_content = self._get_content(parent_id, parent_texts) |
2104.4.2
by John Arbash Meinel
Small cleanup and NEWS entry about fixing bug #65714 |
604 |
seq = patiencediff.PatienceSequenceMatcher( |
2100.2.1
by wang
Replace python's difflib by patiencediff because the worst case |
605 |
None, merge_content.text(), content.text()) |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
606 |
if delta_seq is None: |
607 |
# setup a delta seq to reuse.
|
|
608 |
delta_seq = seq |
|
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
609 |
for i, j, n in seq.get_matching_blocks(): |
610 |
if n == 0: |
|
611 |
continue
|
|
612 |
# this appears to copy (origin, text) pairs across to the new
|
|
613 |
# content for any line that matches the last-checked parent.
|
|
614 |
# FIXME: save the sequence control data for delta compression
|
|
615 |
# against the most relevant parent rather than rediffing.
|
|
616 |
content._lines[j:j+n] = merge_content._lines[i:i+n] |
|
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
617 |
if delta: |
618 |
if not annotated: |
|
619 |
reference_content = self._get_content(parents[0], parent_texts) |
|
620 |
new_texts = content.text() |
|
621 |
old_texts = reference_content.text() |
|
2104.4.2
by John Arbash Meinel
Small cleanup and NEWS entry about fixing bug #65714 |
622 |
delta_seq = patiencediff.PatienceSequenceMatcher( |
2100.2.1
by wang
Replace python's difflib by patiencediff because the worst case |
623 |
None, old_texts, new_texts) |
1596.2.36
by Robert Collins
add a get_delta api to versioned_file. |
624 |
return self._make_line_delta(delta_seq, content) |
625 |
||
626 |
def _make_line_delta(self, delta_seq, new_content): |
|
627 |
"""Generate a line delta from delta_seq and new_content."""
|
|
628 |
diff_hunks = [] |
|
629 |
for op in delta_seq.get_opcodes(): |
|
630 |
if op[0] == 'equal': |
|
631 |
continue
|
|
632 |
diff_hunks.append((op[1], op[2], op[4]-op[3], new_content._lines[op[3]:op[4]])) |
|
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
633 |
return diff_hunks |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
634 |
|
1756.3.17
by Aaron Bentley
Combine get_components_positions with get_components_versions |
635 |
def _get_components_positions(self, version_ids): |
1756.3.19
by Aaron Bentley
Documentation and cleanups |
636 |
"""Produce a map of position data for the components of versions.
|
637 |
||
1756.3.22
by Aaron Bentley
Tweaks from review |
638 |
This data is intended to be used for retrieving the knit records.
|
1756.3.19
by Aaron Bentley
Documentation and cleanups |
639 |
|
640 |
A dict of version_id to (method, data_pos, data_size, next) is
|
|
641 |
returned.
|
|
642 |
method is the way referenced data should be applied.
|
|
643 |
data_pos is the position of the data in the knit.
|
|
644 |
data_size is the size of the data in the knit.
|
|
645 |
next is the build-parent of the version, or None for fulltexts.
|
|
646 |
"""
|
|
1756.3.9
by Aaron Bentley
More optimization refactoring |
647 |
component_data = {} |
648 |
for version_id in version_ids: |
|
649 |
cursor = version_id |
|
650 |
||
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
651 |
while cursor is not None and cursor not in component_data: |
1756.2.29
by Aaron Bentley
Remove basis knit support |
652 |
method = self._index.get_method(cursor) |
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
653 |
if method == 'fulltext': |
654 |
next = None |
|
655 |
else: |
|
1756.2.29
by Aaron Bentley
Remove basis knit support |
656 |
next = self.get_parents(cursor)[0] |
1756.3.17
by Aaron Bentley
Combine get_components_positions with get_components_versions |
657 |
data_pos, data_size = self._index.get_position(cursor) |
658 |
component_data[cursor] = (method, data_pos, data_size, next) |
|
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
659 |
cursor = next |
660 |
return component_data |
|
1756.3.18
by Aaron Bentley
More cleanup |
661 |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
662 |
def _get_content(self, version_id, parent_texts={}): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
663 |
"""Returns a content object that makes up the specified
|
664 |
version."""
|
|
665 |
if not self.has_version(version_id): |
|
666 |
raise RevisionNotPresent(version_id, self.filename) |
|
667 |
||
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
668 |
cached_version = parent_texts.get(version_id, None) |
669 |
if cached_version is not None: |
|
670 |
return cached_version |
|
671 |
||
1756.3.22
by Aaron Bentley
Tweaks from review |
672 |
text_map, contents_map = self._get_content_maps([version_id]) |
673 |
return contents_map[version_id] |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
674 |
|
675 |
def _check_versions_present(self, version_ids): |
|
676 |
"""Check that all specified versions are present."""
|
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
677 |
self._index.check_versions_present(version_ids) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
678 |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
679 |
def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts): |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
680 |
"""See VersionedFile.add_lines_with_ghosts()."""
|
681 |
self._check_add(version_id, lines) |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
682 |
return self._add(version_id, lines[:], parents, self.delta, parent_texts) |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
683 |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
684 |
def _add_lines(self, version_id, parents, lines, parent_texts): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
685 |
"""See VersionedFile.add_lines."""
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
686 |
self._check_add(version_id, lines) |
687 |
self._check_versions_present(parents) |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
688 |
return self._add(version_id, lines[:], parents, self.delta, parent_texts) |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
689 |
|
690 |
def _check_add(self, version_id, lines): |
|
691 |
"""check that version_id and lines are safe to add."""
|
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
692 |
assert self.writable, "knit is not opened for write" |
693 |
### FIXME escape. RBC 20060228
|
|
694 |
if contains_whitespace(version_id): |
|
1668.5.1
by Olaf Conradi
Fix bug in knits when raising InvalidRevisionId without the required |
695 |
raise InvalidRevisionId(version_id, self.filename) |
2229.2.3
by Aaron Bentley
change reserved_id to is_reserved_id, add check_not_reserved for DRY |
696 |
self.check_not_reserved_id(version_id) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
697 |
if self.has_version(version_id): |
698 |
raise RevisionAlreadyPresent(version_id, self.filename) |
|
1666.1.6
by Robert Collins
Make knit the default format. |
699 |
self._check_lines_not_unicode(lines) |
700 |
self._check_lines_are_lines(lines) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
701 |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
702 |
def _add(self, version_id, lines, parents, delta, parent_texts): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
703 |
"""Add a set of lines on top of version specified by parents.
|
704 |
||
705 |
If delta is true, compress the text as a line-delta against
|
|
706 |
the first parent.
|
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
707 |
|
708 |
Any versions not present will be converted into ghosts.
|
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
709 |
"""
|
1596.2.28
by Robert Collins
more knit profile based tuning. |
710 |
# 461 0 6546.0390 43.9100 bzrlib.knit:489(_add)
|
711 |
# +400 0 889.4890 418.9790 +bzrlib.knit:192(lower_fulltext)
|
|
712 |
# +461 0 1364.8070 108.8030 +bzrlib.knit:996(add_record)
|
|
713 |
# +461 0 193.3940 41.5720 +bzrlib.knit:898(add_version)
|
|
714 |
# +461 0 134.0590 18.3810 +bzrlib.osutils:361(sha_strings)
|
|
715 |
# +461 0 36.3420 15.4540 +bzrlib.knit:146(make)
|
|
716 |
# +1383 0 8.0370 8.0370 +<len>
|
|
717 |
# +61 0 13.5770 7.9190 +bzrlib.knit:199(lower_line_delta)
|
|
718 |
# +61 0 963.3470 7.8740 +bzrlib.knit:427(_get_content)
|
|
719 |
# +61 0 973.9950 5.2950 +bzrlib.knit:136(line_delta)
|
|
720 |
# +61 0 1918.1800 5.2640 +bzrlib.knit:359(_merge_annotations)
|
|
721 |
||
1596.2.10
by Robert Collins
Reviewer feedback on knit branches. |
722 |
present_parents = [] |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
723 |
ghosts = [] |
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
724 |
if parent_texts is None: |
725 |
parent_texts = {} |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
726 |
for parent in parents: |
727 |
if not self.has_version(parent): |
|
728 |
ghosts.append(parent) |
|
1594.2.9
by Robert Collins
Teach Knit repositories how to handle ghosts without corrupting at all. |
729 |
else: |
1596.2.10
by Robert Collins
Reviewer feedback on knit branches. |
730 |
present_parents.append(parent) |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
731 |
|
1596.2.10
by Robert Collins
Reviewer feedback on knit branches. |
732 |
if delta and not len(present_parents): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
733 |
delta = False |
734 |
||
735 |
digest = sha_strings(lines) |
|
736 |
options = [] |
|
737 |
if lines: |
|
738 |
if lines[-1][-1] != '\n': |
|
739 |
options.append('no-eol') |
|
740 |
lines[-1] = lines[-1] + '\n' |
|
741 |
||
1596.2.10
by Robert Collins
Reviewer feedback on knit branches. |
742 |
if len(present_parents) and delta: |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
743 |
# To speed the extract of texts the delta chain is limited
|
744 |
# to a fixed number of deltas. This should minimize both
|
|
745 |
# I/O and the time spend applying deltas.
|
|
2147.1.1
by John Arbash Meinel
Factor the common knit delta selection into a helper func, and allow the fulltext to be chosen based on cumulative delta size |
746 |
delta = self._check_should_delta(present_parents) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
747 |
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
748 |
assert isinstance(version_id, str) |
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
749 |
lines = self.factory.make(lines, version_id) |
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
750 |
if delta or (self.factory.annotated and len(present_parents) > 0): |
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
751 |
# Merge annotations from parent texts if so is needed.
|
1596.2.34
by Robert Collins
Optimise knit add to only diff once per parent, not once per parent + once for the delta generation. |
752 |
delta_hunks = self._merge_annotations(lines, present_parents, parent_texts, |
753 |
delta, self.factory.annotated) |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
754 |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
755 |
if delta: |
756 |
options.append('line-delta') |
|
757 |
store_lines = self.factory.lower_line_delta(delta_hunks) |
|
758 |
else: |
|
759 |
options.append('fulltext') |
|
760 |
store_lines = self.factory.lower_fulltext(lines) |
|
761 |
||
762 |
where, size = self._data.add_record(version_id, digest, store_lines) |
|
763 |
self._index.add_version(version_id, options, where, size, parents) |
|
1596.2.32
by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility. |
764 |
return lines |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
765 |
|
1563.2.19
by Robert Collins
stub out a check for knits. |
766 |
def check(self, progress_bar=None): |
767 |
"""See VersionedFile.check()."""
|
|
768 |
||
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
769 |
def _clone_text(self, new_version_id, old_version_id, parents): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
770 |
"""See VersionedFile.clone_text()."""
|
1756.2.8
by Aaron Bentley
Implement get_line_list, cleanups |
771 |
# FIXME RBC 20060228 make fast by only inserting an index with null
|
772 |
# delta.
|
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
773 |
self.add_lines(new_version_id, parents, self.get_lines(old_version_id)) |
774 |
||
775 |
def get_lines(self, version_id): |
|
776 |
"""See VersionedFile.get_lines()."""
|
|
1756.2.8
by Aaron Bentley
Implement get_line_list, cleanups |
777 |
return self.get_line_list([version_id])[0] |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
778 |
|
1756.3.12
by Aaron Bentley
Stuff all text-building data in record_map |
779 |
def _get_record_map(self, version_ids): |
1756.3.19
by Aaron Bentley
Documentation and cleanups |
780 |
"""Produce a dictionary of knit records.
|
781 |
|
|
782 |
The keys are version_ids, the values are tuples of (method, content,
|
|
783 |
digest, next).
|
|
784 |
method is the way the content should be applied.
|
|
785 |
content is a KnitContent object.
|
|
786 |
digest is the SHA1 digest of this version id after all steps are done
|
|
787 |
next is the build-parent of the version, i.e. the leftmost ancestor.
|
|
788 |
If the method is fulltext, next will be None.
|
|
789 |
"""
|
|
1756.3.12
by Aaron Bentley
Stuff all text-building data in record_map |
790 |
position_map = self._get_components_positions(version_ids) |
1756.3.22
by Aaron Bentley
Tweaks from review |
791 |
# c = component_id, m = method, p = position, s = size, n = next
|
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
792 |
records = [(c, p, s) for c, (m, p, s, n) in position_map.iteritems()] |
1756.3.12
by Aaron Bentley
Stuff all text-building data in record_map |
793 |
record_map = {} |
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
794 |
for component_id, content, digest in \ |
1863.1.9
by John Arbash Meinel
Switching to have 'read_records_iter' return in random order. |
795 |
self._data.read_records_iter(records): |
1756.3.12
by Aaron Bentley
Stuff all text-building data in record_map |
796 |
method, position, size, next = position_map[component_id] |
797 |
record_map[component_id] = method, content, digest, next |
|
798 |
||
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
799 |
return record_map |
1756.2.5
by Aaron Bentley
Reduced read_records calls to 1 |
800 |
|
1756.2.7
by Aaron Bentley
Implement get_text in terms of get_texts |
801 |
def get_text(self, version_id): |
802 |
"""See VersionedFile.get_text"""
|
|
803 |
return self.get_texts([version_id])[0] |
|
804 |
||
1756.2.1
by Aaron Bentley
Implement get_texts |
805 |
def get_texts(self, version_ids): |
1756.2.8
by Aaron Bentley
Implement get_line_list, cleanups |
806 |
return [''.join(l) for l in self.get_line_list(version_ids)] |
807 |
||
808 |
def get_line_list(self, version_ids): |
|
1756.2.1
by Aaron Bentley
Implement get_texts |
809 |
"""Return the texts of listed versions as a list of strings."""
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
810 |
version_ids = [osutils.safe_revision_id(v) for v in version_ids] |
2229.2.1
by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository |
811 |
for version_id in version_ids: |
2229.2.3
by Aaron Bentley
change reserved_id to is_reserved_id, add check_not_reserved for DRY |
812 |
self.check_not_reserved_id(version_id) |
1756.3.13
by Aaron Bentley
Refactor get_line_list into _get_content |
813 |
text_map, content_map = self._get_content_maps(version_ids) |
814 |
return [text_map[v] for v in version_ids] |
|
815 |
||
816 |
def _get_content_maps(self, version_ids): |
|
1756.3.19
by Aaron Bentley
Documentation and cleanups |
817 |
"""Produce maps of text and KnitContents
|
818 |
|
|
819 |
:return: (text_map, content_map) where text_map contains the texts for
|
|
820 |
the requested versions and content_map contains the KnitContents.
|
|
1756.3.22
by Aaron Bentley
Tweaks from review |
821 |
Both dicts take version_ids as their keys.
|
1756.3.19
by Aaron Bentley
Documentation and cleanups |
822 |
"""
|
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
823 |
for version_id in version_ids: |
1756.2.1
by Aaron Bentley
Implement get_texts |
824 |
if not self.has_version(version_id): |
825 |
raise RevisionNotPresent(version_id, self.filename) |
|
1756.3.12
by Aaron Bentley
Stuff all text-building data in record_map |
826 |
record_map = self._get_record_map(version_ids) |
1756.2.5
by Aaron Bentley
Reduced read_records calls to 1 |
827 |
|
1756.2.8
by Aaron Bentley
Implement get_line_list, cleanups |
828 |
text_map = {} |
1756.3.7
by Aaron Bentley
Avoid re-parsing texts version components |
829 |
content_map = {} |
1756.3.14
by Aaron Bentley
Handle the intermediate and final representations of no-final-eol texts |
830 |
final_content = {} |
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
831 |
for version_id in version_ids: |
832 |
components = [] |
|
833 |
cursor = version_id |
|
834 |
while cursor is not None: |
|
1756.3.12
by Aaron Bentley
Stuff all text-building data in record_map |
835 |
method, data, digest, next = record_map[cursor] |
1756.3.10
by Aaron Bentley
Optimize selection and retrieval of records |
836 |
components.append((cursor, method, data, digest)) |
837 |
if cursor in content_map: |
|
838 |
break
|
|
839 |
cursor = next |
|
840 |
||
1756.2.1
by Aaron Bentley
Implement get_texts |
841 |
content = None |
1756.2.7
by Aaron Bentley
Implement get_text in terms of get_texts |
842 |
for component_id, method, data, digest in reversed(components): |
1756.3.7
by Aaron Bentley
Avoid re-parsing texts version components |
843 |
if component_id in content_map: |
844 |
content = content_map[component_id] |
|
1756.3.8
by Aaron Bentley
Avoid unused calls, use generators, sets instead of lists |
845 |
else: |
846 |
if method == 'fulltext': |
|
847 |
assert content is None |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
848 |
content = self.factory.parse_fulltext(data, version_id) |
1756.3.8
by Aaron Bentley
Avoid unused calls, use generators, sets instead of lists |
849 |
elif method == 'line-delta': |
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
850 |
delta = self.factory.parse_line_delta(data, version_id) |
1756.3.8
by Aaron Bentley
Avoid unused calls, use generators, sets instead of lists |
851 |
content = content.copy() |
852 |
content._lines = self._apply_delta(content._lines, |
|
853 |
delta) |
|
1756.3.14
by Aaron Bentley
Handle the intermediate and final representations of no-final-eol texts |
854 |
content_map[component_id] = content |
1756.2.1
by Aaron Bentley
Implement get_texts |
855 |
|
856 |
if 'no-eol' in self._index.get_options(version_id): |
|
1756.3.14
by Aaron Bentley
Handle the intermediate and final representations of no-final-eol texts |
857 |
content = content.copy() |
1756.2.1
by Aaron Bentley
Implement get_texts |
858 |
line = content._lines[-1][1].rstrip('\n') |
859 |
content._lines[-1] = (content._lines[-1][0], line) |
|
1756.3.14
by Aaron Bentley
Handle the intermediate and final representations of no-final-eol texts |
860 |
final_content[version_id] = content |
1756.2.1
by Aaron Bentley
Implement get_texts |
861 |
|
862 |
# digest here is the digest from the last applied component.
|
|
1756.3.6
by Aaron Bentley
More multi-text extraction |
863 |
text = content.text() |
864 |
if sha_strings(text) != digest: |
|
1756.2.8
by Aaron Bentley
Implement get_line_list, cleanups |
865 |
raise KnitCorrupt(self.filename, |
866 |
'sha-1 does not match %s' % version_id) |
|
1756.2.1
by Aaron Bentley
Implement get_texts |
867 |
|
1756.3.6
by Aaron Bentley
More multi-text extraction |
868 |
text_map[version_id] = text |
1756.3.14
by Aaron Bentley
Handle the intermediate and final representations of no-final-eol texts |
869 |
return text_map, final_content |
1756.2.1
by Aaron Bentley
Implement get_texts |
870 |
|
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
871 |
def iter_lines_added_or_present_in_versions(self, version_ids=None, |
872 |
pb=None): |
|
1594.2.6
by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved. |
873 |
"""See VersionedFile.iter_lines_added_or_present_in_versions()."""
|
874 |
if version_ids is None: |
|
875 |
version_ids = self.versions() |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
876 |
else: |
877 |
version_ids = [osutils.safe_revision_id(v) for v in version_ids] |
|
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
878 |
if pb is None: |
879 |
pb = progress.DummyProgress() |
|
1759.2.2
by Jelmer Vernooij
Revert some of my spelling fixes and fix some typos after review by Aaron. |
880 |
# we don't care about inclusions, the caller cares.
|
1594.2.6
by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved. |
881 |
# but we need to setup a list of records to visit.
|
882 |
# we need version_id, position, length
|
|
883 |
version_id_records = [] |
|
2163.1.1
by John Arbash Meinel
Use a set to make iter_lines_added_or_present *much* faster |
884 |
requested_versions = set(version_ids) |
1594.3.1
by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit. |
885 |
# filter for available versions
|
886 |
for version_id in requested_versions: |
|
1594.2.6
by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved. |
887 |
if not self.has_version(version_id): |
888 |
raise RevisionNotPresent(version_id, self.filename) |
|
1594.3.1
by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit. |
889 |
# get a in-component-order queue:
|
890 |
for version_id in self.versions(): |
|
891 |
if version_id in requested_versions: |
|
892 |
data_pos, length = self._index.get_position(version_id) |
|
893 |
version_id_records.append((version_id, data_pos, length)) |
|
894 |
||
1594.2.17
by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading. |
895 |
total = len(version_id_records) |
2147.1.3
by John Arbash Meinel
In knit.py we were re-using a variable in 2 loops, causing bogus progress messages to be generated. |
896 |
for version_idx, (version_id, data, sha_value) in \ |
897 |
enumerate(self._data.read_records_iter(version_id_records)): |
|
898 |
pb.update('Walking content.', version_idx, total) |
|
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
899 |
method = self._index.get_method(version_id) |
2163.1.7
by John Arbash Meinel
Switch the line iterator as suggested by Aaron Bentley |
900 |
|
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
901 |
assert method in ('fulltext', 'line-delta') |
902 |
if method == 'fulltext': |
|
2163.1.7
by John Arbash Meinel
Switch the line iterator as suggested by Aaron Bentley |
903 |
line_iterator = self.factory.get_fulltext_content(data) |
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
904 |
else: |
2163.1.7
by John Arbash Meinel
Switch the line iterator as suggested by Aaron Bentley |
905 |
line_iterator = self.factory.get_linedelta_content(data) |
906 |
for line in line_iterator: |
|
907 |
yield line |
|
908 |
||
2039.1.1
by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000) |
909 |
pb.update('Walking content.', total, total) |
1594.2.6
by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved. |
910 |
|
1563.2.18
by Robert Collins
get knit repositories really using knits for text storage. |
911 |
def num_versions(self): |
912 |
"""See VersionedFile.num_versions()."""
|
|
913 |
return self._index.num_versions() |
|
914 |
||
915 |
__len__ = num_versions |
|
916 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
917 |
def annotate_iter(self, version_id): |
918 |
"""See VersionedFile.annotate_iter."""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
919 |
version_id = osutils.safe_revision_id(version_id) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
920 |
content = self._get_content(version_id) |
921 |
for origin, text in content.annotate_iter(): |
|
1596.2.7
by Robert Collins
Remove the requirement for reannotation in knit joins. |
922 |
yield origin, text |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
923 |
|
924 |
def get_parents(self, version_id): |
|
925 |
"""See VersionedFile.get_parents."""
|
|
1628.1.2
by Robert Collins
More knit micro-optimisations. |
926 |
# perf notes:
|
927 |
# optimism counts!
|
|
928 |
# 52554 calls in 1264 872 internal down from 3674
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
929 |
version_id = osutils.safe_revision_id(version_id) |
1628.1.2
by Robert Collins
More knit micro-optimisations. |
930 |
try: |
931 |
return self._index.get_parents(version_id) |
|
932 |
except KeyError: |
|
933 |
raise RevisionNotPresent(version_id, self.filename) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
934 |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
935 |
def get_parents_with_ghosts(self, version_id): |
936 |
"""See VersionedFile.get_parents."""
|
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
937 |
version_id = osutils.safe_revision_id(version_id) |
1628.1.2
by Robert Collins
More knit micro-optimisations. |
938 |
try: |
939 |
return self._index.get_parents_with_ghosts(version_id) |
|
940 |
except KeyError: |
|
941 |
raise RevisionNotPresent(version_id, self.filename) |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
942 |
|
2530.1.1
by Aaron Bentley
Make topological sorting optional for get_ancestry |
943 |
def get_ancestry(self, versions, topo_sorted=True): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
944 |
"""See VersionedFile.get_ancestry."""
|
945 |
if isinstance(versions, basestring): |
|
946 |
versions = [versions] |
|
947 |
if not versions: |
|
948 |
return [] |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
949 |
versions = [osutils.safe_revision_id(v) for v in versions] |
2530.1.1
by Aaron Bentley
Make topological sorting optional for get_ancestry |
950 |
return self._index.get_ancestry(versions, topo_sorted) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
951 |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
952 |
def get_ancestry_with_ghosts(self, versions): |
953 |
"""See VersionedFile.get_ancestry_with_ghosts."""
|
|
954 |
if isinstance(versions, basestring): |
|
955 |
versions = [versions] |
|
956 |
if not versions: |
|
957 |
return [] |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
958 |
versions = [osutils.safe_revision_id(v) for v in versions] |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
959 |
return self._index.get_ancestry_with_ghosts(versions) |
960 |
||
1594.2.6
by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved. |
961 |
#@deprecated_method(zero_eight)
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
962 |
def walk(self, version_ids): |
963 |
"""See VersionedFile.walk."""
|
|
964 |
# We take the short path here, and extract all relevant texts
|
|
965 |
# and put them in a weave and let that do all the work. Far
|
|
966 |
# from optimal, but is much simpler.
|
|
1563.2.6
by Robert Collins
Start check tests for knits (pending), and remove dead code. |
967 |
# FIXME RB 20060228 this really is inefficient!
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
968 |
from bzrlib.weave import Weave |
969 |
||
970 |
w = Weave(self.filename) |
|
2490.2.33
by Aaron Bentley
Disable topological sorting of get_ancestry where sensible |
971 |
ancestry = set(self.get_ancestry(version_ids, topo_sorted=False)) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
972 |
sorted_graph = topo_sort(self._index.get_graph()) |
973 |
version_list = [vid for vid in sorted_graph if vid in ancestry] |
|
974 |
||
975 |
for version_id in version_list: |
|
976 |
lines = self.get_lines(version_id) |
|
977 |
w.add_lines(version_id, self.get_parents(version_id), lines) |
|
978 |
||
979 |
for lineno, insert_id, dset, line in w.walk(version_ids): |
|
980 |
yield lineno, insert_id, dset, line |
|
981 |
||
1664.2.3
by Aaron Bentley
Add failing test case |
982 |
def plan_merge(self, ver_a, ver_b): |
1664.2.11
by Aaron Bentley
Clarifications from merge review |
983 |
"""See VersionedFile.plan_merge."""
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
984 |
ver_a = osutils.safe_revision_id(ver_a) |
985 |
ver_b = osutils.safe_revision_id(ver_b) |
|
2490.2.33
by Aaron Bentley
Disable topological sorting of get_ancestry where sensible |
986 |
ancestors_b = set(self.get_ancestry(ver_b, topo_sorted=False)) |
1664.2.6
by Aaron Bentley
Got plan-merge passing tests |
987 |
|
2490.2.33
by Aaron Bentley
Disable topological sorting of get_ancestry where sensible |
988 |
ancestors_a = set(self.get_ancestry(ver_a, topo_sorted=False)) |
1664.2.4
by Aaron Bentley
Identify unchanged lines correctly |
989 |
annotated_a = self.annotate(ver_a) |
990 |
annotated_b = self.annotate(ver_b) |
|
1551.15.46
by Aaron Bentley
Move plan merge to tree |
991 |
return merge._plan_annotate_merge(annotated_a, annotated_b, |
992 |
ancestors_a, ancestors_b) |
|
1664.2.4
by Aaron Bentley
Identify unchanged lines correctly |
993 |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
994 |
|
995 |
class _KnitComponentFile(object): |
|
996 |
"""One of the files used to implement a knit database"""
|
|
997 |
||
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
998 |
def __init__(self, transport, filename, mode, file_mode=None, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
999 |
create_parent_dir=False, dir_mode=None): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1000 |
self._transport = transport |
1001 |
self._filename = filename |
|
1002 |
self._mode = mode |
|
1946.2.3
by John Arbash Meinel
Pass around the file mode correctly |
1003 |
self._file_mode = file_mode |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
1004 |
self._dir_mode = dir_mode |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1005 |
self._create_parent_dir = create_parent_dir |
1006 |
self._need_to_create = False |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1007 |
|
2196.2.5
by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it |
1008 |
def _full_path(self): |
1009 |
"""Return the full path to this file."""
|
|
1010 |
return self._transport.base + self._filename |
|
1011 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1012 |
def check_header(self, fp): |
1641.1.2
by Robert Collins
Change knit index files to be robust in the presence of partial writes. |
1013 |
line = fp.readline() |
2171.1.1
by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt. |
1014 |
if line == '': |
1015 |
# An empty file can actually be treated as though the file doesn't
|
|
1016 |
# exist yet.
|
|
2196.2.5
by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it |
1017 |
raise errors.NoSuchFile(self._full_path()) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1018 |
if line != self.HEADER: |
2171.1.1
by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt. |
1019 |
raise KnitHeaderError(badline=line, |
1020 |
filename=self._transport.abspath(self._filename)) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1021 |
|
1022 |
def commit(self): |
|
1023 |
"""Commit is a nop."""
|
|
1024 |
||
1025 |
def __repr__(self): |
|
1026 |
return '%s(%s)' % (self.__class__.__name__, self._filename) |
|
1027 |
||
1028 |
||
1029 |
class _KnitIndex(_KnitComponentFile): |
|
1030 |
"""Manages knit index file.
|
|
1031 |
||
1032 |
The index is already kept in memory and read on startup, to enable
|
|
1033 |
fast lookups of revision information. The cursor of the index
|
|
1034 |
file is always pointing to the end, making it easy to append
|
|
1035 |
entries.
|
|
1036 |
||
1037 |
_cache is a cache for fast mapping from version id to a Index
|
|
1038 |
object.
|
|
1039 |
||
1040 |
_history is a cache for fast mapping from indexes to version ids.
|
|
1041 |
||
1042 |
The index data format is dictionary compressed when it comes to
|
|
1043 |
parent references; a index entry may only have parents that with a
|
|
1044 |
lover index number. As a result, the index is topological sorted.
|
|
1563.2.11
by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis. |
1045 |
|
1046 |
Duplicate entries may be written to the index for a single version id
|
|
1047 |
if this is done then the latter one completely replaces the former:
|
|
1048 |
this allows updates to correct version and parent information.
|
|
1049 |
Note that the two entries may share the delta, and that successive
|
|
1050 |
annotations and references MUST point to the first entry.
|
|
1641.1.2
by Robert Collins
Change knit index files to be robust in the presence of partial writes. |
1051 |
|
1052 |
The index file on disc contains a header, followed by one line per knit
|
|
1053 |
record. The same revision can be present in an index file more than once.
|
|
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
1054 |
The first occurrence gets assigned a sequence number starting from 0.
|
1641.1.2
by Robert Collins
Change knit index files to be robust in the presence of partial writes. |
1055 |
|
1056 |
The format of a single line is
|
|
1057 |
REVISION_ID FLAGS BYTE_OFFSET LENGTH( PARENT_ID|PARENT_SEQUENCE_ID)* :\n
|
|
1058 |
REVISION_ID is a utf8-encoded revision id
|
|
1059 |
FLAGS is a comma separated list of flags about the record. Values include
|
|
1060 |
no-eol, line-delta, fulltext.
|
|
1061 |
BYTE_OFFSET is the ascii representation of the byte offset in the data file
|
|
1062 |
that the the compressed data starts at.
|
|
1063 |
LENGTH is the ascii representation of the length of the data file.
|
|
1064 |
PARENT_ID a utf-8 revision id prefixed by a '.' that is a parent of
|
|
1065 |
REVISION_ID.
|
|
1066 |
PARENT_SEQUENCE_ID the ascii representation of the sequence number of a
|
|
1067 |
revision id already in the knit that is a parent of REVISION_ID.
|
|
1068 |
The ' :' marker is the end of record marker.
|
|
1069 |
|
|
1070 |
partial writes:
|
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1071 |
when a write is interrupted to the index file, it will result in a line
|
1072 |
that does not end in ' :'. If the ' :' is not present at the end of a line,
|
|
1073 |
or at the end of the file, then the record that is missing it will be
|
|
1074 |
ignored by the parser.
|
|
1641.1.2
by Robert Collins
Change knit index files to be robust in the presence of partial writes. |
1075 |
|
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
1076 |
When writing new records to the index file, the data is preceded by '\n'
|
1641.1.2
by Robert Collins
Change knit index files to be robust in the presence of partial writes. |
1077 |
to ensure that records always start on new lines even if the last write was
|
1078 |
interrupted. As a result its normal for the last line in the index to be
|
|
1079 |
missing a trailing newline. One can be added with no harmful effects.
|
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1080 |
"""
|
1081 |
||
1666.1.6
by Robert Collins
Make knit the default format. |
1082 |
HEADER = "# bzr knit index 8\n" |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1083 |
|
1596.2.18
by Robert Collins
More microopimisations on index reading, now down to 16000 records/seconds. |
1084 |
# speed of knit parsing went from 280 ms to 280 ms with slots addition.
|
1085 |
# __slots__ = ['_cache', '_history', '_transport', '_filename']
|
|
1086 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1087 |
def _cache_version(self, version_id, options, pos, size, parents): |
1596.2.18
by Robert Collins
More microopimisations on index reading, now down to 16000 records/seconds. |
1088 |
"""Cache a version record in the history array and index cache.
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1089 |
|
1090 |
This is inlined into _load_data for performance. KEEP IN SYNC.
|
|
1596.2.18
by Robert Collins
More microopimisations on index reading, now down to 16000 records/seconds. |
1091 |
(It saves 60ms, 25% of the __init__ overhead on local 4000 record
|
1092 |
indexes).
|
|
1093 |
"""
|
|
1596.2.14
by Robert Collins
Make knit parsing non quadratic? |
1094 |
# only want the _history index to reference the 1st index entry
|
1095 |
# for version_id
|
|
1596.2.18
by Robert Collins
More microopimisations on index reading, now down to 16000 records/seconds. |
1096 |
if version_id not in self._cache: |
1628.1.1
by Robert Collins
Cache the index number of versions in the knit index's self._cache so that |
1097 |
index = len(self._history) |
1596.2.14
by Robert Collins
Make knit parsing non quadratic? |
1098 |
self._history.append(version_id) |
1628.1.1
by Robert Collins
Cache the index number of versions in the knit index's self._cache so that |
1099 |
else: |
1100 |
index = self._cache[version_id][5] |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1101 |
self._cache[version_id] = (version_id, |
1628.1.1
by Robert Collins
Cache the index number of versions in the knit index's self._cache so that |
1102 |
options, |
1103 |
pos, |
|
1104 |
size, |
|
1105 |
parents, |
|
1106 |
index) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1107 |
|
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1108 |
def __init__(self, transport, filename, mode, create=False, file_mode=None, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
1109 |
create_parent_dir=False, delay_create=False, dir_mode=None): |
1110 |
_KnitComponentFile.__init__(self, transport, filename, mode, |
|
1111 |
file_mode=file_mode, |
|
1112 |
create_parent_dir=create_parent_dir, |
|
1113 |
dir_mode=dir_mode) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1114 |
self._cache = {} |
1563.2.11
by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis. |
1115 |
# position in _history is the 'official' index for a revision
|
1116 |
# but the values may have come from a newer entry.
|
|
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
1117 |
# so - wc -l of a knit index is != the number of unique names
|
1773.4.1
by Martin Pool
Add pyflakes makefile target; fix many warnings |
1118 |
# in the knit.
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1119 |
self._history = [] |
1120 |
try: |
|
2247.2.1
by John Arbash Meinel
Don't create pb for simple knit reading. |
1121 |
fp = self._transport.get(self._filename) |
1594.2.17
by Robert Collins
Better readv coalescing, now with test, and progress during knit index reading. |
1122 |
try: |
2247.2.1
by John Arbash Meinel
Don't create pb for simple knit reading. |
1123 |
# _load_data may raise NoSuchFile if the target knit is
|
1124 |
# completely empty.
|
|
2484.1.1
by John Arbash Meinel
Add an initial function to read knit indexes in pyrex. |
1125 |
_load_data(self, fp) |
2247.2.1
by John Arbash Meinel
Don't create pb for simple knit reading. |
1126 |
finally: |
1127 |
fp.close() |
|
1128 |
except NoSuchFile: |
|
1129 |
if mode != 'w' or not create: |
|
1130 |
raise
|
|
1131 |
elif delay_create: |
|
1132 |
self._need_to_create = True |
|
1133 |
else: |
|
1134 |
self._transport.put_bytes_non_atomic( |
|
1135 |
self._filename, self.HEADER, mode=self._file_mode) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1136 |
|
1137 |
def get_graph(self): |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1138 |
return [(vid, idx[4]) for vid, idx in self._cache.iteritems()] |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1139 |
|
2530.1.1
by Aaron Bentley
Make topological sorting optional for get_ancestry |
1140 |
def get_ancestry(self, versions, topo_sorted=True): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1141 |
"""See VersionedFile.get_ancestry."""
|
1563.2.35
by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too. |
1142 |
# get a graph of all the mentioned versions:
|
1143 |
graph = {} |
|
1144 |
pending = set(versions) |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1145 |
cache = self._cache |
1146 |
while pending: |
|
1563.2.35
by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too. |
1147 |
version = pending.pop() |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1148 |
# trim ghosts
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1149 |
try: |
1150 |
parents = [p for p in cache[version][4] if p in cache] |
|
1151 |
except KeyError: |
|
1152 |
raise RevisionNotPresent(version, self._filename) |
|
1153 |
# if not completed and not a ghost
|
|
1154 |
pending.update([p for p in parents if p not in graph]) |
|
1563.2.35
by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too. |
1155 |
graph[version] = parents |
2530.1.1
by Aaron Bentley
Make topological sorting optional for get_ancestry |
1156 |
if not topo_sorted: |
1157 |
return graph.keys() |
|
1563.2.35
by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too. |
1158 |
return topo_sort(graph.items()) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1159 |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1160 |
def get_ancestry_with_ghosts(self, versions): |
1161 |
"""See VersionedFile.get_ancestry_with_ghosts."""
|
|
1162 |
# get a graph of all the mentioned versions:
|
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1163 |
self.check_versions_present(versions) |
1164 |
cache = self._cache |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1165 |
graph = {} |
1166 |
pending = set(versions) |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1167 |
while pending: |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1168 |
version = pending.pop() |
1169 |
try: |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1170 |
parents = cache[version][4] |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1171 |
except KeyError: |
1172 |
# ghost, fake it
|
|
1173 |
graph[version] = [] |
|
1174 |
else: |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1175 |
# if not completed
|
1176 |
pending.update([p for p in parents if p not in graph]) |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1177 |
graph[version] = parents |
1178 |
return topo_sort(graph.items()) |
|
1179 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1180 |
def num_versions(self): |
1181 |
return len(self._history) |
|
1182 |
||
1183 |
__len__ = num_versions |
|
1184 |
||
1185 |
def get_versions(self): |
|
1186 |
return self._history |
|
1187 |
||
1188 |
def idx_to_name(self, idx): |
|
1189 |
return self._history[idx] |
|
1190 |
||
1191 |
def lookup(self, version_id): |
|
1192 |
assert version_id in self._cache |
|
1628.1.1
by Robert Collins
Cache the index number of versions in the knit index's self._cache so that |
1193 |
return self._cache[version_id][5] |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1194 |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1195 |
def _version_list_to_index(self, versions): |
1196 |
result_list = [] |
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1197 |
cache = self._cache |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1198 |
for version in versions: |
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1199 |
if version in cache: |
1628.1.1
by Robert Collins
Cache the index number of versions in the knit index's self._cache so that |
1200 |
# -- inlined lookup() --
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1201 |
result_list.append(str(cache[version][5])) |
1628.1.1
by Robert Collins
Cache the index number of versions in the knit index's self._cache so that |
1202 |
# -- end lookup () --
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1203 |
else: |
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
1204 |
result_list.append('.' + version) |
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1205 |
return ' '.join(result_list) |
1206 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1207 |
def add_version(self, version_id, options, pos, size, parents): |
1208 |
"""Add a version record to the index."""
|
|
1692.4.1
by Robert Collins
Multiple merges: |
1209 |
self.add_versions(((version_id, options, pos, size, parents),)) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1210 |
|
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
1211 |
def add_versions(self, versions): |
1212 |
"""Add multiple versions to the index.
|
|
1213 |
|
|
1214 |
:param versions: a list of tuples:
|
|
1215 |
(version_id, options, pos, size, parents).
|
|
1216 |
"""
|
|
1217 |
lines = [] |
|
2102.2.1
by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions |
1218 |
orig_history = self._history[:] |
1219 |
orig_cache = self._cache.copy() |
|
1220 |
||
1221 |
try: |
|
1222 |
for version_id, options, pos, size, parents in versions: |
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
1223 |
line = "\n%s %s %s %s %s :" % (version_id, |
2102.2.1
by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions |
1224 |
','.join(options), |
1225 |
pos, |
|
1226 |
size, |
|
1227 |
self._version_list_to_index(parents)) |
|
1228 |
assert isinstance(line, str), \ |
|
1229 |
'content must be utf-8 encoded: %r' % (line,) |
|
1230 |
lines.append(line) |
|
1231 |
self._cache_version(version_id, options, pos, size, parents) |
|
1232 |
if not self._need_to_create: |
|
1233 |
self._transport.append_bytes(self._filename, ''.join(lines)) |
|
1234 |
else: |
|
1235 |
sio = StringIO() |
|
1236 |
sio.write(self.HEADER) |
|
1237 |
sio.writelines(lines) |
|
1238 |
sio.seek(0) |
|
1239 |
self._transport.put_file_non_atomic(self._filename, sio, |
|
1240 |
create_parent_dir=self._create_parent_dir, |
|
1241 |
mode=self._file_mode, |
|
1242 |
dir_mode=self._dir_mode) |
|
1243 |
self._need_to_create = False |
|
1244 |
except: |
|
1245 |
# If any problems happen, restore the original values and re-raise
|
|
1246 |
self._history = orig_history |
|
1247 |
self._cache = orig_cache |
|
1248 |
raise
|
|
1249 |
||
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1250 |
def has_version(self, version_id): |
1251 |
"""True if the version is in the index."""
|
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1252 |
return version_id in self._cache |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1253 |
|
1254 |
def get_position(self, version_id): |
|
1255 |
"""Return data position and size of specified version."""
|
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1256 |
entry = self._cache[version_id] |
1257 |
return entry[2], entry[3] |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1258 |
|
1259 |
def get_method(self, version_id): |
|
1260 |
"""Return compression method of specified version."""
|
|
1261 |
options = self._cache[version_id][1] |
|
1262 |
if 'fulltext' in options: |
|
1263 |
return 'fulltext' |
|
1264 |
else: |
|
2196.2.5
by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it |
1265 |
if 'line-delta' not in options: |
1266 |
raise errors.KnitIndexUnknownMethod(self._full_path(), options) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1267 |
return 'line-delta' |
1268 |
||
1269 |
def get_options(self, version_id): |
|
1270 |
return self._cache[version_id][1] |
|
1271 |
||
1272 |
def get_parents(self, version_id): |
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1273 |
"""Return parents of specified version ignoring ghosts."""
|
1274 |
return [parent for parent in self._cache[version_id][4] |
|
1275 |
if parent in self._cache] |
|
1276 |
||
1277 |
def get_parents_with_ghosts(self, version_id): |
|
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
1278 |
"""Return parents of specified version with ghosts."""
|
1594.2.8
by Robert Collins
add ghost aware apis to knits. |
1279 |
return self._cache[version_id][4] |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1280 |
|
1281 |
def check_versions_present(self, version_ids): |
|
1282 |
"""Check that all specified versions are present."""
|
|
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1283 |
cache = self._cache |
1284 |
for version_id in version_ids: |
|
1285 |
if version_id not in cache: |
|
1286 |
raise RevisionNotPresent(version_id, self._filename) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1287 |
|
1288 |
||
1289 |
class _KnitData(_KnitComponentFile): |
|
1290 |
"""Contents of the knit data file"""
|
|
1291 |
||
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1292 |
def __init__(self, transport, filename, mode, create=False, file_mode=None, |
1946.2.16
by John Arbash Meinel
Small cleanup |
1293 |
create_parent_dir=False, delay_create=False, |
1294 |
dir_mode=None): |
|
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1295 |
_KnitComponentFile.__init__(self, transport, filename, mode, |
1946.2.3
by John Arbash Meinel
Pass around the file mode correctly |
1296 |
file_mode=file_mode, |
1946.2.16
by John Arbash Meinel
Small cleanup |
1297 |
create_parent_dir=create_parent_dir, |
1298 |
dir_mode=dir_mode) |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1299 |
self._checked = False |
1863.1.8
by John Arbash Meinel
Removing disk-backed-cache |
1300 |
# TODO: jam 20060713 conceptually, this could spill to disk
|
1301 |
# if the cached size gets larger than a certain amount
|
|
1302 |
# but it complicates the model a bit, so for now just use
|
|
1303 |
# a simple dictionary
|
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1304 |
self._cache = {} |
1305 |
self._do_cache = False |
|
1563.2.35
by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too. |
1306 |
if create: |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1307 |
if delay_create: |
1308 |
self._need_to_create = create |
|
1309 |
else: |
|
1946.2.10
by John Arbash Meinel
[merge] up-to-date with Transport.put_*_non_atomic |
1310 |
self._transport.put_bytes_non_atomic(self._filename, '', |
1946.2.8
by John Arbash Meinel
[merge] transport_bytes: bring knit churn up-to-date with new *{bytes,file} functions |
1311 |
mode=self._file_mode) |
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1312 |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1313 |
def enable_cache(self): |
1314 |
"""Enable caching of reads."""
|
|
1863.1.8
by John Arbash Meinel
Removing disk-backed-cache |
1315 |
self._do_cache = True |
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1316 |
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1317 |
def clear_cache(self): |
1318 |
"""Clear the record cache."""
|
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1319 |
self._do_cache = False |
1320 |
self._cache = {} |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1321 |
|
1322 |
def _open_file(self): |
|
1711.7.25
by John Arbash Meinel
try/finally to close files, _KnitData was keeping a handle to a file it never used again, and using transport.rename() when it wanted transport.move() |
1323 |
try: |
1324 |
return self._transport.get(self._filename) |
|
1325 |
except NoSuchFile: |
|
1326 |
pass
|
|
1327 |
return None |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1328 |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1329 |
def _record_to_data(self, version_id, digest, lines): |
1330 |
"""Convert version_id, digest, lines into a raw data block.
|
|
1331 |
|
|
1332 |
:return: (len, a StringIO instance with the raw data ready to read.)
|
|
1333 |
"""
|
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1334 |
sio = StringIO() |
1335 |
data_file = GzipFile(None, mode='wb', fileobj=sio) |
|
1908.4.10
by John Arbash Meinel
Small cleanups |
1336 |
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
1337 |
assert isinstance(version_id, str) |
1908.4.5
by John Arbash Meinel
Some small tweaks to knit and tuned_gzip to shave off another couple seconds |
1338 |
data_file.writelines(chain( |
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
1339 |
["version %s %d %s\n" % (version_id, |
1596.2.28
by Robert Collins
more knit profile based tuning. |
1340 |
len(lines), |
1341 |
digest)], |
|
1342 |
lines, |
|
2249.5.15
by John Arbash Meinel
remove get_cached_utf8 checks which were slowing things down. |
1343 |
["end %s\n" % version_id])) |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1344 |
data_file.close() |
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1345 |
length= sio.tell() |
1596.2.28
by Robert Collins
more knit profile based tuning. |
1346 |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1347 |
sio.seek(0) |
1348 |
return length, sio |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1349 |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1350 |
def add_raw_record(self, raw_data): |
1692.4.1
by Robert Collins
Multiple merges: |
1351 |
"""Append a prepared record to the data file.
|
2329.1.2
by John Arbash Meinel
Remove some spurious whitespace changes. |
1352 |
|
1692.4.1
by Robert Collins
Multiple merges: |
1353 |
:return: the offset in the data file raw_data was written.
|
1354 |
"""
|
|
1596.2.9
by Robert Collins
Utf8 safety in knit indexes. |
1355 |
assert isinstance(raw_data, str), 'data must be plain bytes' |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1356 |
if not self._need_to_create: |
1946.2.8
by John Arbash Meinel
[merge] transport_bytes: bring knit churn up-to-date with new *{bytes,file} functions |
1357 |
return self._transport.append_bytes(self._filename, raw_data) |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1358 |
else: |
1946.2.10
by John Arbash Meinel
[merge] up-to-date with Transport.put_*_non_atomic |
1359 |
self._transport.put_bytes_non_atomic(self._filename, raw_data, |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1360 |
create_parent_dir=self._create_parent_dir, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
1361 |
mode=self._file_mode, |
1362 |
dir_mode=self._dir_mode) |
|
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1363 |
self._need_to_create = False |
1364 |
return 0 |
|
2329.1.2
by John Arbash Meinel
Remove some spurious whitespace changes. |
1365 |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1366 |
def add_record(self, version_id, digest, lines): |
1367 |
"""Write new text record to disk. Returns the position in the
|
|
1368 |
file where it was written."""
|
|
1369 |
size, sio = self._record_to_data(version_id, digest, lines) |
|
1370 |
# write to disk
|
|
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1371 |
if not self._need_to_create: |
1946.2.8
by John Arbash Meinel
[merge] transport_bytes: bring knit churn up-to-date with new *{bytes,file} functions |
1372 |
start_pos = self._transport.append_file(self._filename, sio) |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1373 |
else: |
1946.2.10
by John Arbash Meinel
[merge] up-to-date with Transport.put_*_non_atomic |
1374 |
self._transport.put_file_non_atomic(self._filename, sio, |
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1375 |
create_parent_dir=self._create_parent_dir, |
1946.2.12
by John Arbash Meinel
Add ability to pass a directory mode to non_atomic_put |
1376 |
mode=self._file_mode, |
1377 |
dir_mode=self._dir_mode) |
|
1946.2.1
by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories |
1378 |
self._need_to_create = False |
1379 |
start_pos = 0 |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1380 |
if self._do_cache: |
1381 |
self._cache[version_id] = sio.getvalue() |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1382 |
return start_pos, size |
1383 |
||
1384 |
def _parse_record_header(self, version_id, raw_data): |
|
1385 |
"""Parse a record header for consistency.
|
|
1386 |
||
1387 |
:return: the header and the decompressor stream.
|
|
1388 |
as (stream, header_record)
|
|
1389 |
"""
|
|
1390 |
df = GzipFile(mode='rb', fileobj=StringIO(raw_data)) |
|
2329.1.1
by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption. |
1391 |
try: |
1392 |
rec = self._check_header(version_id, df.readline()) |
|
1393 |
except Exception, e: |
|
1394 |
raise KnitCorrupt(self._filename, |
|
1395 |
"While reading {%s} got %s(%s)" |
|
1396 |
% (version_id, e.__class__.__name__, str(e))) |
|
2163.2.4
by John Arbash Meinel
Split _KnitData._parse_header up, so that we have 1 readlines() call, rather than readline+readlines() |
1397 |
return df, rec |
1398 |
||
1399 |
def _check_header(self, version_id, line): |
|
1400 |
rec = line.split() |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1401 |
if len(rec) != 4: |
2163.2.4
by John Arbash Meinel
Split _KnitData._parse_header up, so that we have 1 readlines() call, rather than readline+readlines() |
1402 |
raise KnitCorrupt(self._filename, |
1403 |
'unexpected number of elements in record header') |
|
2249.5.12
by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8 |
1404 |
if rec[1] != version_id: |
2163.2.4
by John Arbash Meinel
Split _KnitData._parse_header up, so that we have 1 readlines() call, rather than readline+readlines() |
1405 |
raise KnitCorrupt(self._filename, |
1406 |
'unexpected version, wanted %r, got %r' |
|
1407 |
% (version_id, rec[1])) |
|
1408 |
return rec |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1409 |
|
1410 |
def _parse_record(self, version_id, data): |
|
1628.1.2
by Robert Collins
More knit micro-optimisations. |
1411 |
# profiling notes:
|
1412 |
# 4168 calls in 2880 217 internal
|
|
1413 |
# 4168 calls to _parse_record_header in 2121
|
|
1414 |
# 4168 calls to readlines in 330
|
|
2163.2.4
by John Arbash Meinel
Split _KnitData._parse_header up, so that we have 1 readlines() call, rather than readline+readlines() |
1415 |
df = GzipFile(mode='rb', fileobj=StringIO(data)) |
1416 |
||
2329.1.1
by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption. |
1417 |
try: |
1418 |
record_contents = df.readlines() |
|
1419 |
except Exception, e: |
|
1420 |
raise KnitCorrupt(self._filename, |
|
1421 |
"While reading {%s} got %s(%s)" |
|
1422 |
% (version_id, e.__class__.__name__, str(e))) |
|
2163.2.4
by John Arbash Meinel
Split _KnitData._parse_header up, so that we have 1 readlines() call, rather than readline+readlines() |
1423 |
header = record_contents.pop(0) |
1424 |
rec = self._check_header(version_id, header) |
|
1425 |
||
1426 |
last_line = record_contents.pop() |
|
2329.1.1
by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption. |
1427 |
if len(record_contents) != int(rec[2]): |
1428 |
raise KnitCorrupt(self._filename, |
|
1429 |
'incorrect number of lines %s != %s' |
|
1430 |
' for version {%s}' |
|
1431 |
% (len(record_contents), int(rec[2]), |
|
1432 |
version_id)) |
|
2163.2.4
by John Arbash Meinel
Split _KnitData._parse_header up, so that we have 1 readlines() call, rather than readline+readlines() |
1433 |
if last_line != 'end %s\n' % rec[1]: |
1434 |
raise KnitCorrupt(self._filename, |
|
1435 |
'unexpected version end line %r, wanted %r' |
|
1436 |
% (last_line, version_id)) |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1437 |
df.close() |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1438 |
return record_contents, rec[3] |
1439 |
||
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1440 |
def read_records_iter_raw(self, records): |
1441 |
"""Read text records from data file and yield raw data.
|
|
1442 |
||
1443 |
This unpacks enough of the text record to validate the id is
|
|
1444 |
as expected but thats all.
|
|
1445 |
"""
|
|
1446 |
# setup an iterator of the external records:
|
|
1447 |
# uses readv so nice and fast we hope.
|
|
1756.3.23
by Aaron Bentley
Remove knit caches |
1448 |
if len(records): |
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1449 |
# grab the disk data needed.
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1450 |
if self._cache: |
1451 |
# Don't check _cache if it is empty
|
|
1452 |
needed_offsets = [(pos, size) for version_id, pos, size |
|
1453 |
in records |
|
1454 |
if version_id not in self._cache] |
|
1455 |
else: |
|
1456 |
needed_offsets = [(pos, size) for version_id, pos, size |
|
1457 |
in records] |
|
1458 |
||
1459 |
raw_records = self._transport.readv(self._filename, needed_offsets) |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1460 |
|
1461 |
for version_id, pos, size in records: |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1462 |
if version_id in self._cache: |
1463 |
# This data has already been validated
|
|
1464 |
data = self._cache[version_id] |
|
1465 |
else: |
|
1466 |
pos, data = raw_records.next() |
|
1467 |
if self._do_cache: |
|
1468 |
self._cache[version_id] = data |
|
1469 |
||
1470 |
# validate the header
|
|
1471 |
df, rec = self._parse_record_header(version_id, data) |
|
1472 |
df.close() |
|
1756.3.23
by Aaron Bentley
Remove knit caches |
1473 |
yield version_id, data |
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1474 |
|
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1475 |
def read_records_iter(self, records): |
1476 |
"""Read text records from data file and yield result.
|
|
1477 |
||
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
1478 |
The result will be returned in whatever is the fastest to read.
|
1479 |
Not by the order requested. Also, multiple requests for the same
|
|
1480 |
record will only yield 1 response.
|
|
1481 |
:param records: A list of (version_id, pos, len) entries
|
|
1482 |
:return: Yields (version_id, contents, digest) in the order
|
|
1483 |
read, not the order requested
|
|
1484 |
"""
|
|
1485 |
if not records: |
|
1486 |
return
|
|
1487 |
||
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1488 |
if self._cache: |
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
1489 |
# Skip records we have alread seen
|
1490 |
yielded_records = set() |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1491 |
needed_records = set() |
1492 |
for record in records: |
|
1493 |
if record[0] in self._cache: |
|
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
1494 |
if record[0] in yielded_records: |
1495 |
continue
|
|
1496 |
yielded_records.add(record[0]) |
|
1497 |
data = self._cache[record[0]] |
|
1498 |
content, digest = self._parse_record(record[0], data) |
|
1499 |
yield (record[0], content, digest) |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1500 |
else: |
1501 |
needed_records.add(record) |
|
1502 |
needed_records = sorted(needed_records, key=operator.itemgetter(1)) |
|
1503 |
else: |
|
1504 |
needed_records = sorted(set(records), key=operator.itemgetter(1)) |
|
1756.3.23
by Aaron Bentley
Remove knit caches |
1505 |
|
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
1506 |
if not needed_records: |
1507 |
return
|
|
1508 |
||
1509 |
# The transport optimizes the fetching as well
|
|
1510 |
# (ie, reads continuous ranges.)
|
|
1511 |
readv_response = self._transport.readv(self._filename, |
|
1512 |
[(pos, size) for version_id, pos, size in needed_records]) |
|
1513 |
||
1514 |
for (version_id, pos, size), (pos, data) in \ |
|
1515 |
izip(iter(needed_records), readv_response): |
|
1516 |
content, digest = self._parse_record(version_id, data) |
|
1863.1.1
by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit |
1517 |
if self._do_cache: |
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
1518 |
self._cache[version_id] = data |
1756.3.23
by Aaron Bentley
Remove knit caches |
1519 |
yield version_id, content, digest |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1520 |
|
1521 |
def read_records(self, records): |
|
1522 |
"""Read records into a dictionary."""
|
|
1523 |
components = {} |
|
1863.1.5
by John Arbash Meinel
Add a read_records_iter_unsorted, which can return records in any order. |
1524 |
for record_id, content, digest in \ |
1863.1.9
by John Arbash Meinel
Switching to have 'read_records_iter' return in random order. |
1525 |
self.read_records_iter(records): |
1563.2.4
by Robert Collins
First cut at including the knit implementation of versioned_file. |
1526 |
components[record_id] = (content, digest) |
1527 |
return components |
|
1528 |
||
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
1529 |
|
1530 |
class InterKnit(InterVersionedFile): |
|
1531 |
"""Optimised code paths for knit to knit operations."""
|
|
1532 |
||
1684.3.3
by Robert Collins
Add a special cased weaves to knit converter. |
1533 |
_matching_file_from_factory = KnitVersionedFile |
1534 |
_matching_file_to_factory = KnitVersionedFile |
|
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
1535 |
|
1536 |
@staticmethod
|
|
1537 |
def is_compatible(source, target): |
|
1538 |
"""Be compatible with knits. """
|
|
1539 |
try: |
|
1540 |
return (isinstance(source, KnitVersionedFile) and |
|
1541 |
isinstance(target, KnitVersionedFile)) |
|
1542 |
except AttributeError: |
|
1543 |
return False |
|
1544 |
||
1563.2.31
by Robert Collins
Convert Knit repositories to use knits. |
1545 |
def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False): |
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
1546 |
"""See InterVersionedFile.join."""
|
1547 |
assert isinstance(self.source, KnitVersionedFile) |
|
1548 |
assert isinstance(self.target, KnitVersionedFile) |
|
1549 |
||
1684.3.2
by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile. |
1550 |
version_ids = self._get_source_version_ids(version_ids, ignore_missing) |
1563.2.31
by Robert Collins
Convert Knit repositories to use knits. |
1551 |
|
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
1552 |
if not version_ids: |
1553 |
return 0 |
|
1554 |
||
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1555 |
pb = ui.ui_factory.nested_progress_bar() |
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1556 |
try: |
1557 |
version_ids = list(version_ids) |
|
1558 |
if None in version_ids: |
|
1559 |
version_ids.remove(None) |
|
1560 |
||
1561 |
self.source_ancestry = set(self.source.get_ancestry(version_ids)) |
|
1562 |
this_versions = set(self.target._index.get_versions()) |
|
1563 |
needed_versions = self.source_ancestry - this_versions |
|
1564 |
cross_check_versions = self.source_ancestry.intersection(this_versions) |
|
1565 |
mismatched_versions = set() |
|
1566 |
for version in cross_check_versions: |
|
1567 |
# scan to include needed parents.
|
|
1568 |
n1 = set(self.target.get_parents_with_ghosts(version)) |
|
1569 |
n2 = set(self.source.get_parents_with_ghosts(version)) |
|
1570 |
if n1 != n2: |
|
1571 |
# FIXME TEST this check for cycles being introduced works
|
|
1572 |
# the logic is we have a cycle if in our graph we are an
|
|
1573 |
# ancestor of any of the n2 revisions.
|
|
1574 |
for parent in n2: |
|
1575 |
if parent in n1: |
|
1576 |
# safe
|
|
1577 |
continue
|
|
1578 |
else: |
|
1579 |
parent_ancestors = self.source.get_ancestry(parent) |
|
1580 |
if version in parent_ancestors: |
|
1581 |
raise errors.GraphCycleError([parent, version]) |
|
1582 |
# ensure this parent will be available later.
|
|
1583 |
new_parents = n2.difference(n1) |
|
1584 |
needed_versions.update(new_parents.difference(this_versions)) |
|
1585 |
mismatched_versions.add(version) |
|
1586 |
||
1684.3.3
by Robert Collins
Add a special cased weaves to knit converter. |
1587 |
if not needed_versions and not mismatched_versions: |
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1588 |
return 0 |
1910.2.65
by Aaron Bentley
Remove the check-parent patch |
1589 |
full_list = topo_sort(self.source.get_graph()) |
1590 |
||
1591 |
version_list = [i for i in full_list if (not self.target.has_version(i) |
|
1592 |
and i in needed_versions)] |
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1593 |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1594 |
# plan the join:
|
1595 |
copy_queue = [] |
|
1596 |
copy_queue_records = [] |
|
1597 |
copy_set = set() |
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1598 |
for version_id in version_list: |
1599 |
options = self.source._index.get_options(version_id) |
|
1600 |
parents = self.source._index.get_parents_with_ghosts(version_id) |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1601 |
# check that its will be a consistent copy:
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1602 |
for parent in parents: |
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1603 |
# if source has the parent, we must :
|
1604 |
# * already have it or
|
|
1605 |
# * have it scheduled already
|
|
1759.2.2
by Jelmer Vernooij
Revert some of my spelling fixes and fix some typos after review by Aaron. |
1606 |
# otherwise we don't care
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1607 |
assert (self.target.has_version(parent) or |
1608 |
parent in copy_set or |
|
1609 |
not self.source.has_version(parent)) |
|
1610 |
data_pos, data_size = self.source._index.get_position(version_id) |
|
1611 |
copy_queue_records.append((version_id, data_pos, data_size)) |
|
1612 |
copy_queue.append((version_id, options, parents)) |
|
1613 |
copy_set.add(version_id) |
|
1614 |
||
1615 |
# data suck the join:
|
|
1616 |
count = 0 |
|
1617 |
total = len(version_list) |
|
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
1618 |
raw_datum = [] |
1619 |
raw_records = [] |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1620 |
for (version_id, raw_data), \ |
1621 |
(version_id2, options, parents) in \ |
|
1622 |
izip(self.source._data.read_records_iter_raw(copy_queue_records), |
|
1623 |
copy_queue): |
|
1624 |
assert version_id == version_id2, 'logic error, inconsistent results' |
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1625 |
count = count + 1 |
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1626 |
pb.update("Joining knit", count, total) |
1692.2.1
by Robert Collins
Fix knit based push to only perform 2 appends to the target, rather that 2*new-versions. |
1627 |
raw_records.append((version_id, options, parents, len(raw_data))) |
1628 |
raw_datum.append(raw_data) |
|
1629 |
self.target._add_raw_records(raw_records, ''.join(raw_datum)) |
|
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1630 |
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1631 |
for version in mismatched_versions: |
1596.2.8
by Robert Collins
Join knits with the original gzipped data avoiding recompression. |
1632 |
# FIXME RBC 20060309 is this needed?
|
1594.2.24
by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching. |
1633 |
n1 = set(self.target.get_parents_with_ghosts(version)) |
1634 |
n2 = set(self.source.get_parents_with_ghosts(version)) |
|
1635 |
# write a combined record to our history preserving the current
|
|
1636 |
# parents as first in the list
|
|
1637 |
new_parents = self.target.get_parents_with_ghosts(version) + list(n2.difference(n1)) |
|
1638 |
self.target.fix_parents(version, new_parents) |
|
1639 |
return count |
|
1640 |
finally: |
|
1641 |
pb.finished() |
|
1563.2.13
by Robert Collins
InterVersionedFile implemented. |
1642 |
|
1643 |
||
1644 |
InterVersionedFile.register_optimiser(InterKnit) |
|
1596.2.24
by Robert Collins
Gzipfile was slightly slower than ideal. |
1645 |
|
1646 |
||
1684.3.3
by Robert Collins
Add a special cased weaves to knit converter. |
1647 |
class WeaveToKnit(InterVersionedFile): |
1648 |
"""Optimised code paths for weave to knit operations."""
|
|
1649 |
||
1650 |
_matching_file_from_factory = bzrlib.weave.WeaveFile |
|
1651 |
_matching_file_to_factory = KnitVersionedFile |
|
1652 |
||
1653 |
@staticmethod
|
|
1654 |
def is_compatible(source, target): |
|
1655 |
"""Be compatible with weaves to knits."""
|
|
1656 |
try: |
|
1657 |
return (isinstance(source, bzrlib.weave.Weave) and |
|
1658 |
isinstance(target, KnitVersionedFile)) |
|
1659 |
except AttributeError: |
|
1660 |
return False |
|
1661 |
||
1662 |
def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False): |
|
1663 |
"""See InterVersionedFile.join."""
|
|
1664 |
assert isinstance(self.source, bzrlib.weave.Weave) |
|
1665 |
assert isinstance(self.target, KnitVersionedFile) |
|
1666 |
||
1667 |
version_ids = self._get_source_version_ids(version_ids, ignore_missing) |
|
1668 |
||
1669 |
if not version_ids: |
|
1670 |
return 0 |
|
1671 |
||
2158.3.1
by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations |
1672 |
pb = ui.ui_factory.nested_progress_bar() |
1684.3.3
by Robert Collins
Add a special cased weaves to knit converter. |
1673 |
try: |
1674 |
version_ids = list(version_ids) |
|
1675 |
||
1676 |
self.source_ancestry = set(self.source.get_ancestry(version_ids)) |
|
1677 |
this_versions = set(self.target._index.get_versions()) |
|
1678 |
needed_versions = self.source_ancestry - this_versions |
|
1679 |
cross_check_versions = self.source_ancestry.intersection(this_versions) |
|
1680 |
mismatched_versions = set() |
|
1681 |
for version in cross_check_versions: |
|
1682 |
# scan to include needed parents.
|
|
1683 |
n1 = set(self.target.get_parents_with_ghosts(version)) |
|
1684 |
n2 = set(self.source.get_parents(version)) |
|
1685 |
# if all of n2's parents are in n1, then its fine.
|
|
1686 |
if n2.difference(n1): |
|
1687 |
# FIXME TEST this check for cycles being introduced works
|
|
1688 |
# the logic is we have a cycle if in our graph we are an
|
|
1689 |
# ancestor of any of the n2 revisions.
|
|
1690 |
for parent in n2: |
|
1691 |
if parent in n1: |
|
1692 |
# safe
|
|
1693 |
continue
|
|
1694 |
else: |
|
1695 |
parent_ancestors = self.source.get_ancestry(parent) |
|
1696 |
if version in parent_ancestors: |
|
1697 |
raise errors.GraphCycleError([parent, version]) |
|
1698 |
# ensure this parent will be available later.
|
|
1699 |
new_parents = n2.difference(n1) |
|
1700 |
needed_versions.update(new_parents.difference(this_versions)) |
|
1701 |
mismatched_versions.add(version) |
|
1702 |
||
1703 |
if not needed_versions and not mismatched_versions: |
|
1704 |
return 0 |
|
1705 |
full_list = topo_sort(self.source.get_graph()) |
|
1706 |
||
1707 |
version_list = [i for i in full_list if (not self.target.has_version(i) |
|
1708 |
and i in needed_versions)] |
|
1709 |
||
1710 |
# do the join:
|
|
1711 |
count = 0 |
|
1712 |
total = len(version_list) |
|
1713 |
for version_id in version_list: |
|
1714 |
pb.update("Converting to knit", count, total) |
|
1715 |
parents = self.source.get_parents(version_id) |
|
1716 |
# check that its will be a consistent copy:
|
|
1717 |
for parent in parents: |
|
1718 |
# if source has the parent, we must already have it
|
|
1719 |
assert (self.target.has_version(parent)) |
|
1720 |
self.target.add_lines( |
|
1721 |
version_id, parents, self.source.get_lines(version_id)) |
|
1722 |
count = count + 1 |
|
1723 |
||
1724 |
for version in mismatched_versions: |
|
1725 |
# FIXME RBC 20060309 is this needed?
|
|
1726 |
n1 = set(self.target.get_parents_with_ghosts(version)) |
|
1727 |
n2 = set(self.source.get_parents(version)) |
|
1728 |
# write a combined record to our history preserving the current
|
|
1729 |
# parents as first in the list
|
|
1730 |
new_parents = self.target.get_parents_with_ghosts(version) + list(n2.difference(n1)) |
|
1731 |
self.target.fix_parents(version, new_parents) |
|
1732 |
return count |
|
1733 |
finally: |
|
1734 |
pb.finished() |
|
1735 |
||
1736 |
||
1737 |
InterVersionedFile.register_optimiser(WeaveToKnit) |
|
1738 |
||
1739 |
||
1711.2.11
by John Arbash Meinel
Rename patiencediff.SequenceMatcher => PatienceSequenceMatcher and knit.SequenceMatcher => KnitSequenceMatcher |
1740 |
class KnitSequenceMatcher(difflib.SequenceMatcher): |
1596.2.35
by Robert Collins
Subclass SequenceMatcher to get a slightly faster (in our case) find_longest_match routine. |
1741 |
"""Knit tuned sequence matcher.
|
1742 |
||
1743 |
This is based on profiling of difflib which indicated some improvements
|
|
1744 |
for our usage pattern.
|
|
1745 |
"""
|
|
1746 |
||
1747 |
def find_longest_match(self, alo, ahi, blo, bhi): |
|
1748 |
"""Find longest matching block in a[alo:ahi] and b[blo:bhi].
|
|
1749 |
||
1750 |
If isjunk is not defined:
|
|
1751 |
||
1752 |
Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
|
|
1753 |
alo <= i <= i+k <= ahi
|
|
1754 |
blo <= j <= j+k <= bhi
|
|
1755 |
and for all (i',j',k') meeting those conditions,
|
|
1756 |
k >= k'
|
|
1757 |
i <= i'
|
|
1758 |
and if i == i', j <= j'
|
|
1759 |
||
1760 |
In other words, of all maximal matching blocks, return one that
|
|
1761 |
starts earliest in a, and of all those maximal matching blocks that
|
|
1762 |
start earliest in a, return the one that starts earliest in b.
|
|
1763 |
||
1764 |
>>> s = SequenceMatcher(None, " abcd", "abcd abcd")
|
|
1765 |
>>> s.find_longest_match(0, 5, 0, 9)
|
|
1766 |
(0, 4, 5)
|
|
1767 |
||
1768 |
If isjunk is defined, first the longest matching block is
|
|
1769 |
determined as above, but with the additional restriction that no
|
|
1770 |
junk element appears in the block. Then that block is extended as
|
|
1771 |
far as possible by matching (only) junk elements on both sides. So
|
|
1772 |
the resulting block never matches on junk except as identical junk
|
|
1773 |
happens to be adjacent to an "interesting" match.
|
|
1774 |
||
1775 |
Here's the same example as before, but considering blanks to be
|
|
1776 |
junk. That prevents " abcd" from matching the " abcd" at the tail
|
|
1777 |
end of the second sequence directly. Instead only the "abcd" can
|
|
1778 |
match, and matches the leftmost "abcd" in the second sequence:
|
|
1779 |
||
1780 |
>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
|
|
1781 |
>>> s.find_longest_match(0, 5, 0, 9)
|
|
1782 |
(1, 0, 4)
|
|
1783 |
||
1784 |
If no blocks match, return (alo, blo, 0).
|
|
1785 |
||
1786 |
>>> s = SequenceMatcher(None, "ab", "c")
|
|
1787 |
>>> s.find_longest_match(0, 2, 0, 1)
|
|
1788 |
(0, 0, 0)
|
|
1789 |
"""
|
|
1790 |
||
1791 |
# CAUTION: stripping common prefix or suffix would be incorrect.
|
|
1792 |
# E.g.,
|
|
1793 |
# ab
|
|
1794 |
# acab
|
|
1795 |
# Longest matching block is "ab", but if common prefix is
|
|
1796 |
# stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
|
|
1797 |
# strip, so ends up claiming that ab is changed to acab by
|
|
1798 |
# inserting "ca" in the middle. That's minimal but unintuitive:
|
|
1799 |
# "it's obvious" that someone inserted "ac" at the front.
|
|
1800 |
# Windiff ends up at the same place as diff, but by pairing up
|
|
1801 |
# the unique 'b's and then matching the first two 'a's.
|
|
1802 |
||
1803 |
a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.isbjunk |
|
1804 |
besti, bestj, bestsize = alo, blo, 0 |
|
1805 |
# find longest junk-free match
|
|
1806 |
# during an iteration of the loop, j2len[j] = length of longest
|
|
1807 |
# junk-free match ending with a[i-1] and b[j]
|
|
1808 |
j2len = {} |
|
1809 |
# nothing = []
|
|
1810 |
b2jget = b2j.get |
|
1811 |
for i in xrange(alo, ahi): |
|
1812 |
# look at all instances of a[i] in b; note that because
|
|
1813 |
# b2j has no junk keys, the loop is skipped if a[i] is junk
|
|
1814 |
j2lenget = j2len.get |
|
1815 |
newj2len = {} |
|
1816 |
||
1759.2.1
by Jelmer Vernooij
Fix some types (found using aspell). |
1817 |
# changing b2j.get(a[i], nothing) to a try:KeyError pair produced the
|
1596.2.35
by Robert Collins
Subclass SequenceMatcher to get a slightly faster (in our case) find_longest_match routine. |
1818 |
# following improvement
|
1819 |
# 704 0 4650.5320 2620.7410 bzrlib.knit:1336(find_longest_match)
|
|
1820 |
# +326674 0 1655.1210 1655.1210 +<method 'get' of 'dict' objects>
|
|
1821 |
# +76519 0 374.6700 374.6700 +<method 'has_key' of 'dict' objects>
|
|
1822 |
# to
|
|
1823 |
# 704 0 3733.2820 2209.6520 bzrlib.knit:1336(find_longest_match)
|
|
1824 |
# +211400 0 1147.3520 1147.3520 +<method 'get' of 'dict' objects>
|
|
1825 |
# +76519 0 376.2780 376.2780 +<method 'has_key' of 'dict' objects>
|
|
1826 |
||
1827 |
try: |
|
1828 |
js = b2j[a[i]] |
|
1829 |
except KeyError: |
|
1830 |
pass
|
|
1831 |
else: |
|
1832 |
for j in js: |
|
1833 |
# a[i] matches b[j]
|
|
1834 |
if j >= blo: |
|
1835 |
if j >= bhi: |
|
1836 |
break
|
|
1837 |
k = newj2len[j] = 1 + j2lenget(-1 + j, 0) |
|
1838 |
if k > bestsize: |
|
1839 |
besti, bestj, bestsize = 1 + i-k, 1 + j-k, k |
|
1840 |
j2len = newj2len |
|
1841 |
||
1842 |
# Extend the best by non-junk elements on each end. In particular,
|
|
1843 |
# "popular" non-junk elements aren't in b2j, which greatly speeds
|
|
1844 |
# the inner loop above, but also means "the best" match so far
|
|
1845 |
# doesn't contain any junk *or* popular non-junk elements.
|
|
1846 |
while besti > alo and bestj > blo and \ |
|
1847 |
not isbjunk(b[bestj-1]) and \ |
|
1848 |
a[besti-1] == b[bestj-1]: |
|
1849 |
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 |
|
1850 |
while besti+bestsize < ahi and bestj+bestsize < bhi and \ |
|
1851 |
not isbjunk(b[bestj+bestsize]) and \ |
|
1852 |
a[besti+bestsize] == b[bestj+bestsize]: |
|
1853 |
bestsize += 1 |
|
1854 |
||
1855 |
# Now that we have a wholly interesting match (albeit possibly
|
|
1856 |
# empty!), we may as well suck up the matching junk on each
|
|
1857 |
# side of it too. Can't think of a good reason not to, and it
|
|
1858 |
# saves post-processing the (possibly considerable) expense of
|
|
1859 |
# figuring out what to do with it. In the case of an empty
|
|
1860 |
# interesting match, this is clearly the right thing to do,
|
|
1861 |
# because no other kind of match is possible in the regions.
|
|
1862 |
while besti > alo and bestj > blo and \ |
|
1863 |
isbjunk(b[bestj-1]) and \ |
|
1864 |
a[besti-1] == b[bestj-1]: |
|
1865 |
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 |
|
1866 |
while besti+bestsize < ahi and bestj+bestsize < bhi and \ |
|
1867 |
isbjunk(b[bestj+bestsize]) and \ |
|
1868 |
a[besti+bestsize] == b[bestj+bestsize]: |
|
1869 |
bestsize = bestsize + 1 |
|
1870 |
||
1871 |
return besti, bestj, bestsize |
|
2484.1.1
by John Arbash Meinel
Add an initial function to read knit indexes in pyrex. |
1872 |
|
1873 |
||
1874 |
try: |
|
2484.1.12
by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx |
1875 |
from bzrlib._knit_load_data_c import _load_data_c as _load_data |
2484.1.1
by John Arbash Meinel
Add an initial function to read knit indexes in pyrex. |
1876 |
except ImportError: |
2484.1.12
by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx |
1877 |
from bzrlib._knit_load_data_py import _load_data_py as _load_data |