41
32
revision as _mod_revision,
47
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
48
38
from bzrlib.repository import (
50
MetaDirVersionedFileRepository,
51
41
MetaDirRepositoryFormat,
55
45
from bzrlib.store.text import TextStore
56
46
from bzrlib.trace import mutter
57
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
58
from bzrlib.versionedfile import (
60
FulltextContentFactory,
65
49
class AllInOneRepository(Repository):
66
50
"""Legacy support - the repository behaviour for all-in-one branches."""
69
def _serializer(self):
70
return xml5.serializer_v5
72
def _escape(self, file_or_path):
73
if not isinstance(file_or_path, basestring):
74
file_or_path = '/'.join(file_or_path)
75
if file_or_path == '':
77
return urlutils.escape(osutils.safe_unicode(file_or_path))
79
def __init__(self, _format, a_bzrdir):
52
_serializer = xml5.serializer_v5
54
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
80
55
# we reuse one control files instance.
81
dir_mode = a_bzrdir._get_dir_mode()
82
file_mode = a_bzrdir._get_file_mode()
56
dir_mode = a_bzrdir._control_files._dir_mode
57
file_mode = a_bzrdir._control_files._file_mode
84
59
def get_store(name, compressed=True, prefixed=False):
85
60
# FIXME: This approach of assuming stores are all entirely compressed
86
# or entirely uncompressed is tidy, but breaks upgrade from
87
# some existing branches where there's a mixture; we probably
61
# or entirely uncompressed is tidy, but breaks upgrade from
62
# some existing branches where there's a mixture; we probably
88
63
# still want the option to look for both.
89
relpath = self._escape(name)
90
store = TextStore(a_bzrdir.transport.clone(relpath),
64
relpath = a_bzrdir._control_files._escape(name)
65
store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
91
66
prefixed=prefixed, compressed=compressed,
93
68
file_mode=file_mode)
96
71
# not broken out yet because the controlweaves|inventory_store
97
# and texts bits are still different.
72
# and text_store | weave_store bits are still different.
98
73
if isinstance(_format, RepositoryFormat4):
99
# cannot remove these - there is still no consistent api
74
# cannot remove these - there is still no consistent api
100
75
# which allows access to this old info.
101
76
self.inventory_store = get_store('inventory-store')
102
self._text_store = get_store('text-store')
103
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
77
text_store = get_store('text-store')
78
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
106
81
def _all_possible_ids(self):
107
82
"""Return all the possible revisions that we could find."""
108
83
if 'evil' in debug.debug_flags:
109
84
mutter_callsite(3, "_all_possible_ids scales with size of history.")
110
return [key[-1] for key in self.inventories.keys()]
85
return self.get_inventory_weave().versions()
113
88
def _all_revision_ids(self):
114
"""Returns a list of all the revision ids in the repository.
89
"""Returns a list of all the revision ids in the repository.
116
These are in as much topological order as the underlying store can
91
These are in as much topological order as the underlying store can
117
92
present: for weaves ghosts may lead to a lack of correctness until
118
93
the reweave updates the parents list.
120
return [key[-1] for key in self.revisions.keys()]
122
def _activate_new_inventory(self):
123
"""Put a replacement inventory.new into use as inventories."""
124
# Copy the content across
125
t = self.bzrdir._control_files._transport
126
t.copy('inventory.new.weave', 'inventory.weave')
127
# delete the temp inventory
128
t.delete('inventory.new.weave')
129
# Check we can parse the new weave properly as a sanity check
130
self.inventories.keys()
132
def _backup_inventory(self):
133
t = self.bzrdir._control_files._transport
134
t.copy('inventory.weave', 'inventory.backup.weave')
136
def _temp_inventories(self):
137
t = self.bzrdir._control_files._transport
138
return self._format._get_inventories(t, self, 'inventory.new')
95
if self._revision_store.text_store.listable():
96
return self._revision_store.all_revision_ids(self.get_transaction())
97
result = self._all_possible_ids()
98
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
99
# ids. (It should, since _revision_store's API should change to
100
# return utf8 revision_ids)
101
return self._eliminate_revisions_not_present(result)
103
def _check_revision_parents(self, revision, inventory):
104
"""Private to Repository and Fetch.
106
This checks the parentage of revision in an inventory weave for
107
consistency and is only applicable to inventory-weave-for-ancestry
108
using repository formats & fetchers.
110
weave_parents = inventory.get_parents(revision.revision_id)
111
weave_names = inventory.versions()
112
for parent_id in revision.parent_ids:
113
if parent_id in weave_names:
114
# this parent must not be a ghost.
115
if not parent_id in weave_parents:
117
raise errors.CorruptRepository(self)
140
119
def get_commit_builder(self, branch, parents, config, timestamp=None,
141
120
timezone=None, committer=None, revprops=None,
142
121
revision_id=None):
143
122
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
144
result = CommitBuilder(self, parents, config, timestamp, timezone,
123
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
145
124
committer, revprops, revision_id)
146
125
self.start_write_group()
150
129
def get_revisions(self, revision_ids):
151
130
revs = self._get_revisions(revision_ids)
131
# weave corruption can lead to absent revision markers that should be
133
# the following test is reasonably cheap (it needs a single weave read)
134
# and the weave is cached in read transactions. In write transactions
135
# it is not cached but typically we only read a small number of
136
# revisions. For knits when they are introduced we will probably want
137
# to ensure that caching write transactions are in use.
138
inv = self.get_inventory_weave()
140
self._check_revision_parents(rev, inv)
154
def _inventory_add_lines(self, revision_id, parents, lines,
156
"""Store lines in inv_vf and return the sha1 of the inventory."""
157
present_parents = self.get_graph().get_parent_map(parents)
159
for parent in parents:
160
if parent in present_parents:
161
final_parents.append((parent,))
162
return self.inventories.add_lines((revision_id,), final_parents, lines,
163
check_content=check_content)[0]
144
def get_revision_graph(self, revision_id=None):
145
"""Return a dictionary containing the revision graph.
147
:param revision_id: The revision_id to get a graph from. If None, then
148
the entire revision graph is returned. This is a deprecated mode of
149
operation and will be removed in the future.
150
:return: a dictionary of revision_id->revision_parents_list.
152
if 'evil' in debug.debug_flags:
154
"get_revision_graph scales with size of history.")
155
# special case NULL_REVISION
156
if revision_id == _mod_revision.NULL_REVISION:
158
a_weave = self.get_inventory_weave()
159
all_revisions = self._eliminate_revisions_not_present(
161
entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for
162
node in all_revisions])
163
if revision_id is None:
165
elif revision_id not in entire_graph:
166
raise errors.NoSuchRevision(self, revision_id)
168
# add what can be reached from revision_id
170
pending = set([revision_id])
171
while len(pending) > 0:
173
result[node] = entire_graph[node]
174
for revision_id in result[node]:
175
if revision_id not in result:
176
pending.add(revision_id)
165
180
def is_shared(self):
166
181
"""AllInOne repositories cannot be shared."""
191
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
206
class WeaveMetaDirRepository(MetaDirRepository):
192
207
"""A subclass of MetaDirRepository to set weave specific policy."""
194
def __init__(self, _format, a_bzrdir, control_files):
195
super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
196
self._serializer = _format._serializer
209
_serializer = xml5.serializer_v5
199
212
def _all_possible_ids(self):
200
213
"""Return all the possible revisions that we could find."""
201
214
if 'evil' in debug.debug_flags:
202
215
mutter_callsite(3, "_all_possible_ids scales with size of history.")
203
return [key[-1] for key in self.inventories.keys()]
216
return self.get_inventory_weave().versions()
206
219
def _all_revision_ids(self):
207
"""Returns a list of all the revision ids in the repository.
220
"""Returns a list of all the revision ids in the repository.
209
These are in as much topological order as the underlying store can
222
These are in as much topological order as the underlying store can
210
223
present: for weaves ghosts may lead to a lack of correctness until
211
224
the reweave updates the parents list.
213
return [key[-1] for key in self.revisions.keys()]
215
def _activate_new_inventory(self):
216
"""Put a replacement inventory.new into use as inventories."""
217
# Copy the content across
219
t.copy('inventory.new.weave', 'inventory.weave')
220
# delete the temp inventory
221
t.delete('inventory.new.weave')
222
# Check we can parse the new weave properly as a sanity check
223
self.inventories.keys()
225
def _backup_inventory(self):
227
t.copy('inventory.weave', 'inventory.backup.weave')
229
def _temp_inventories(self):
231
return self._format._get_inventories(t, self, 'inventory.new')
226
if self._revision_store.text_store.listable():
227
return self._revision_store.all_revision_ids(self.get_transaction())
228
result = self._all_possible_ids()
229
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
230
# ids. (It should, since _revision_store's API should change to
231
# return utf8 revision_ids)
232
return self._eliminate_revisions_not_present(result)
234
def _check_revision_parents(self, revision, inventory):
235
"""Private to Repository and Fetch.
237
This checks the parentage of revision in an inventory weave for
238
consistency and is only applicable to inventory-weave-for-ancestry
239
using repository formats & fetchers.
241
weave_parents = inventory.get_parents(revision.revision_id)
242
weave_names = inventory.versions()
243
for parent_id in revision.parent_ids:
244
if parent_id in weave_names:
245
# this parent must not be a ghost.
246
if not parent_id in weave_parents:
248
raise errors.CorruptRepository(self)
233
250
def get_commit_builder(self, branch, parents, config, timestamp=None,
234
251
timezone=None, committer=None, revprops=None,
235
252
revision_id=None):
236
253
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
237
result = CommitBuilder(self, parents, config, timestamp, timezone,
254
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
238
255
committer, revprops, revision_id)
239
256
self.start_write_group()
243
260
def get_revision(self, revision_id):
244
261
"""Return the Revision object for a named revision"""
262
# TODO: jam 20070210 get_revision_reconcile should do this for us
245
263
r = self.get_revision_reconcile(revision_id)
264
# weave corruption can lead to absent revision markers that should be
266
# the following test is reasonably cheap (it needs a single weave read)
267
# and the weave is cached in read transactions. In write transactions
268
# it is not cached but typically we only read a small number of
269
# revisions. For knits when they are introduced we will probably want
270
# to ensure that caching write transactions are in use.
271
inv = self.get_inventory_weave()
272
self._check_revision_parents(r, inv)
248
def _inventory_add_lines(self, revision_id, parents, lines,
250
"""Store lines in inv_vf and return the sha1 of the inventory."""
251
present_parents = self.get_graph().get_parent_map(parents)
253
for parent in parents:
254
if parent in present_parents:
255
final_parents.append((parent,))
256
return self.inventories.add_lines((revision_id,), final_parents, lines,
257
check_content=check_content)[0]
276
def get_revision_graph(self, revision_id=None):
277
"""Return a dictionary containing the revision graph.
279
:param revision_id: The revision_id to get a graph from. If None, then
280
the entire revision graph is returned. This is a deprecated mode of
281
operation and will be removed in the future.
282
:return: a dictionary of revision_id->revision_parents_list.
284
if 'evil' in debug.debug_flags:
286
"get_revision_graph scales with size of history.")
287
# special case NULL_REVISION
288
if revision_id == _mod_revision.NULL_REVISION:
290
a_weave = self.get_inventory_weave()
291
all_revisions = self._eliminate_revisions_not_present(
293
entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for
294
node in all_revisions])
295
if revision_id is None:
297
elif revision_id not in entire_graph:
298
raise errors.NoSuchRevision(self, revision_id)
300
# add what can be reached from revision_id
302
pending = set([revision_id])
303
while len(pending) > 0:
305
result[node] = entire_graph[node]
306
for revision_id in result[node]:
307
if revision_id not in result:
308
pending.add(revision_id)
259
311
def revision_graph_can_have_wrong_parents(self):
312
# XXX: This is an old format that we don't support full checking on, so
313
# just claim that checking for this inconsistency is not required.
266
320
rich_root_data = False
267
321
supports_tree_reference = False
268
supports_ghosts = False
269
supports_external_lookups = False
270
supports_chks = False
271
_fetch_order = 'topological'
272
_fetch_reconcile = True
275
323
def initialize(self, a_bzrdir, shared=False, _internal=False):
276
"""Create a weave repository."""
324
"""Create a weave repository.
326
TODO: when creating split out bzr branch formats, move this to a common
327
base for Format5, Format6. or something like that.
278
330
raise errors.IncompatibleFormat(self, a_bzrdir._format)
280
332
if not _internal:
281
333
# always initialized when the bzrdir is.
282
334
return self.open(a_bzrdir, _found=True)
284
336
# Create an empty weave
286
338
weavefile.write_weave_v5(weave.Weave(), sio)
287
339
empty_weave = sio.getvalue()
289
341
mutter('creating repository in %s.', a_bzrdir.transport.base)
342
dirs = ['revision-store', 'weaves']
343
files = [('inventory.weave', StringIO(empty_weave)),
291
346
# FIXME: RBC 20060125 don't peek under the covers
292
347
# NB: no need to escape relative paths that are url safe.
293
348
control_files = lockable_files.LockableFiles(a_bzrdir.transport,
294
'branch-lock', lockable_files.TransportLock)
349
'branch-lock', lockable_files.TransportLock)
295
350
control_files.create_lock()
296
351
control_files.lock_write()
297
transport = a_bzrdir.transport
352
control_files._transport.mkdir_multi(dirs,
353
mode=control_files._dir_mode)
299
transport.mkdir_multi(['revision-store', 'weaves'],
300
mode=a_bzrdir._get_dir_mode())
301
transport.put_bytes_non_atomic('inventory.weave', empty_weave,
302
mode=a_bzrdir._get_file_mode())
355
for file, content in files:
356
control_files.put(file, content)
304
358
control_files.unlock()
305
359
return self.open(a_bzrdir, _found=True)
361
def _get_control_store(self, repo_transport, control_files):
362
"""Return the control store for this repository."""
363
return self._get_versioned_file_store('',
368
def _get_text_store(self, transport, control_files):
369
"""Get a store for file texts for this format."""
370
raise NotImplementedError(self._get_text_store)
307
372
def open(self, a_bzrdir, _found=False):
308
373
"""See RepositoryFormat.open()."""
347
419
"""Format 4 is not supported.
349
421
It is not supported because the model changed from 4 to 5 and the
350
conversion logic is expensive - so doing it on the fly was not
422
conversion logic is expensive - so doing it on the fly was not
355
def _get_inventories(self, repo_transport, repo, name='inventory'):
356
# No inventories store written so far.
427
def _get_control_store(self, repo_transport, control_files):
428
"""Format 4 repositories have no formal control store at this point.
430
This will cause any control-file-needing apis to fail - this is desired.
359
def _get_revisions(self, repo_transport, repo):
434
def _get_revision_store(self, repo_transport, control_files):
435
"""See RepositoryFormat._get_revision_store()."""
360
436
from bzrlib.xml4 import serializer_v4
361
return RevisionTextStore(repo_transport.clone('revision-store'),
362
serializer_v4, True, versionedfile.PrefixMapper(),
363
repo.is_locked, repo.is_write_locked)
365
def _get_signatures(self, repo_transport, repo):
366
return SignatureTextStore(repo_transport.clone('revision-store'),
367
False, versionedfile.PrefixMapper(),
368
repo.is_locked, repo.is_write_locked)
370
def _get_texts(self, repo_transport, repo):
437
return self._get_text_rev_store(repo_transport,
440
serializer=serializer_v4)
442
def _get_text_store(self, transport, control_files):
443
"""See RepositoryFormat._get_text_store()."""
374
446
class RepositoryFormat5(PreSplitOutRepositoryFormat):
383
455
_versionedfile_class = weave.WeaveFile
384
456
_matchingbzrdir = bzrdir.BzrDirFormat5()
386
def _serializer(self):
387
return xml5.serializer_v5
459
super(RepositoryFormat5, self).__init__()
389
461
def get_format_description(self):
390
462
"""See RepositoryFormat.get_format_description()."""
391
463
return "Weave repository format 5"
393
def network_name(self):
394
"""The network name for this format is the control dirs disk label."""
395
return self._matchingbzrdir.get_format_string()
397
def _get_inventories(self, repo_transport, repo, name='inventory'):
398
mapper = versionedfile.ConstantMapper(name)
399
return versionedfile.ThunkedVersionedFiles(repo_transport,
400
weave.WeaveFile, mapper, repo.is_locked)
402
def _get_revisions(self, repo_transport, repo):
403
return RevisionTextStore(repo_transport.clone('revision-store'),
404
xml5.serializer_v5, False, versionedfile.PrefixMapper(),
405
repo.is_locked, repo.is_write_locked)
407
def _get_signatures(self, repo_transport, repo):
408
return SignatureTextStore(repo_transport.clone('revision-store'),
409
False, versionedfile.PrefixMapper(),
410
repo.is_locked, repo.is_write_locked)
412
def _get_texts(self, repo_transport, repo):
413
mapper = versionedfile.PrefixMapper()
414
base_transport = repo_transport.clone('weaves')
415
return versionedfile.ThunkedVersionedFiles(base_transport,
416
weave.WeaveFile, mapper, repo.is_locked)
465
def _get_revision_store(self, repo_transport, control_files):
466
"""See RepositoryFormat._get_revision_store()."""
467
"""Return the revision store object for this a_bzrdir."""
468
return self._get_text_rev_store(repo_transport,
473
def _get_text_store(self, transport, control_files):
474
"""See RepositoryFormat._get_text_store()."""
475
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
419
478
class RepositoryFormat6(PreSplitOutRepositoryFormat):
428
487
_versionedfile_class = weave.WeaveFile
429
488
_matchingbzrdir = bzrdir.BzrDirFormat6()
431
def _serializer(self):
432
return xml5.serializer_v5
491
super(RepositoryFormat6, self).__init__()
434
493
def get_format_description(self):
435
494
"""See RepositoryFormat.get_format_description()."""
436
495
return "Weave repository format 6"
438
def network_name(self):
439
"""The network name for this format is the control dirs disk label."""
440
return self._matchingbzrdir.get_format_string()
442
def _get_inventories(self, repo_transport, repo, name='inventory'):
443
mapper = versionedfile.ConstantMapper(name)
444
return versionedfile.ThunkedVersionedFiles(repo_transport,
445
weave.WeaveFile, mapper, repo.is_locked)
447
def _get_revisions(self, repo_transport, repo):
448
return RevisionTextStore(repo_transport.clone('revision-store'),
449
xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
450
repo.is_locked, repo.is_write_locked)
452
def _get_signatures(self, repo_transport, repo):
453
return SignatureTextStore(repo_transport.clone('revision-store'),
454
False, versionedfile.HashPrefixMapper(),
455
repo.is_locked, repo.is_write_locked)
457
def _get_texts(self, repo_transport, repo):
458
mapper = versionedfile.HashPrefixMapper()
459
base_transport = repo_transport.clone('weaves')
460
return versionedfile.ThunkedVersionedFiles(base_transport,
461
weave.WeaveFile, mapper, repo.is_locked)
497
def _get_revision_store(self, repo_transport, control_files):
498
"""See RepositoryFormat._get_revision_store()."""
499
return self._get_text_rev_store(repo_transport,
505
def _get_text_store(self, transport, control_files):
506
"""See RepositoryFormat._get_text_store()."""
507
return self._get_versioned_file_store('weaves', transport, control_files)
464
509
class RepositoryFormat7(MetaDirRepositoryFormat):
465
510
"""Bzr repository 7.
492
535
"""See RepositoryFormat.get_format_description()."""
493
536
return "Weave repository format 7"
495
def _get_inventories(self, repo_transport, repo, name='inventory'):
496
mapper = versionedfile.ConstantMapper(name)
497
return versionedfile.ThunkedVersionedFiles(repo_transport,
498
weave.WeaveFile, mapper, repo.is_locked)
500
def _get_revisions(self, repo_transport, repo):
501
return RevisionTextStore(repo_transport.clone('revision-store'),
502
xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
503
repo.is_locked, repo.is_write_locked)
505
def _get_signatures(self, repo_transport, repo):
506
return SignatureTextStore(repo_transport.clone('revision-store'),
507
True, versionedfile.HashPrefixMapper(),
508
repo.is_locked, repo.is_write_locked)
510
def _get_texts(self, repo_transport, repo):
511
mapper = versionedfile.HashPrefixMapper()
512
base_transport = repo_transport.clone('weaves')
513
return versionedfile.ThunkedVersionedFiles(base_transport,
514
weave.WeaveFile, mapper, repo.is_locked)
538
def check_conversion_target(self, target_format):
541
def _get_revision_store(self, repo_transport, control_files):
542
"""See RepositoryFormat._get_revision_store()."""
543
return self._get_text_rev_store(repo_transport,
550
def _get_text_store(self, transport, control_files):
551
"""See RepositoryFormat._get_text_store()."""
552
return self._get_versioned_file_store('weaves',
516
556
def initialize(self, a_bzrdir, shared=False):
517
557
"""Create a weave repository.
527
567
mutter('creating repository in %s.', a_bzrdir.transport.base)
528
568
dirs = ['revision-store', 'weaves']
529
files = [('inventory.weave', StringIO(empty_weave)),
569
files = [('inventory.weave', StringIO(empty_weave)),
531
571
utf8_files = [('format', self.get_format_string())]
533
573
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
534
574
return self.open(a_bzrdir=a_bzrdir, _found=True)
536
576
def open(self, a_bzrdir, _found=False, _override_transport=None):
537
577
"""See RepositoryFormat.open().
539
579
:param _override_transport: INTERNAL USE ONLY. Allows opening the
540
580
repository at a slightly different url
541
581
than normal. I.e. during 'upgrade'.
544
584
format = RepositoryFormat.find_format(a_bzrdir)
585
assert format.__class__ == self.__class__
545
586
if _override_transport is not None:
546
587
repo_transport = _override_transport
548
589
repo_transport = a_bzrdir.get_repository_transport(None)
549
590
control_files = lockable_files.LockableFiles(repo_transport,
550
591
'lock', lockdir.LockDir)
551
result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
552
control_files=control_files)
553
result.revisions = self._get_revisions(repo_transport, result)
554
result.signatures = self._get_signatures(repo_transport, result)
555
result.inventories = self._get_inventories(repo_transport, result)
556
result.texts = self._get_texts(repo_transport, result)
557
result.chk_bytes = None
558
result._transport = repo_transport
562
class TextVersionedFiles(VersionedFiles):
563
"""Just-a-bunch-of-files based VersionedFile stores."""
565
def __init__(self, transport, compressed, mapper, is_locked, can_write):
566
self._compressed = compressed
567
self._transport = transport
568
self._mapper = mapper
573
self._is_locked = is_locked
574
self._can_write = can_write
576
def add_lines(self, key, parents, lines):
577
"""Add a revision to the store."""
578
if not self._is_locked():
579
raise errors.ObjectNotLocked(self)
580
if not self._can_write():
581
raise errors.ReadOnlyError(self)
583
raise ValueError('bad idea to put / in %r' % (key,))
584
text = ''.join(lines)
586
text = bytes_to_gzip(text)
587
path = self._map(key)
588
self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
590
def insert_record_stream(self, stream):
592
for record in stream:
593
# Raise an error when a record is missing.
594
if record.storage_kind == 'absent':
595
raise errors.RevisionNotPresent([record.key[0]], self)
596
# adapt to non-tuple interface
597
if record.storage_kind == 'fulltext':
598
self.add_lines(record.key, None,
599
osutils.split_lines(record.get_bytes_as('fulltext')))
601
adapter_key = record.storage_kind, 'fulltext'
603
adapter = adapters[adapter_key]
605
adapter_factory = adapter_registry.get(adapter_key)
606
adapter = adapter_factory(self)
607
adapters[adapter_key] = adapter
608
lines = osutils.split_lines(adapter.get_bytes(
609
record, record.get_bytes_as(record.storage_kind)))
611
self.add_lines(record.key, None, lines)
612
except RevisionAlreadyPresent:
615
def _load_text(self, key):
616
if not self._is_locked():
617
raise errors.ObjectNotLocked(self)
618
path = self._map(key)
620
text = self._transport.get_bytes(path)
621
compressed = self._compressed
622
except errors.NoSuchFile:
624
# try without the .gz
627
text = self._transport.get_bytes(path)
629
except errors.NoSuchFile:
634
text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
638
return self._mapper.map(key) + self._ext
641
class RevisionTextStore(TextVersionedFiles):
642
"""Legacy thunk for format 4 repositories."""
644
def __init__(self, transport, serializer, compressed, mapper, is_locked,
646
"""Create a RevisionTextStore at transport with serializer."""
647
TextVersionedFiles.__init__(self, transport, compressed, mapper,
648
is_locked, can_write)
649
self._serializer = serializer
651
def _load_text_parents(self, key):
652
text = self._load_text(key)
655
parents = self._serializer.read_revision_from_string(text).parent_ids
656
return text, tuple((parent,) for parent in parents)
658
def get_parent_map(self, keys):
661
parents = self._load_text_parents(key)[1]
664
result[key] = parents
667
def get_known_graph_ancestry(self, keys):
668
"""Get a KnownGraph instance with the ancestry of keys."""
670
parent_map = self.get_parent_map(keys)
671
kg = _mod_graph.KnownGraph(parent_map)
674
def get_record_stream(self, keys, sort_order, include_delta_closure):
676
text, parents = self._load_text_parents(key)
678
yield AbsentContentFactory(key)
680
yield FulltextContentFactory(key, parents, None, text)
683
if not self._is_locked():
684
raise errors.ObjectNotLocked(self)
686
for quoted_relpath in self._transport.iter_files_recursive():
687
relpath = urllib.unquote(quoted_relpath)
688
path, ext = os.path.splitext(relpath)
691
if not relpath.endswith('.sig'):
692
relpaths.add(relpath)
693
paths = list(relpaths)
694
return set([self._mapper.unmap(path) for path in paths])
697
class SignatureTextStore(TextVersionedFiles):
698
"""Legacy thunk for format 4-7 repositories."""
700
def __init__(self, transport, compressed, mapper, is_locked, can_write):
701
TextVersionedFiles.__init__(self, transport, compressed, mapper,
702
is_locked, can_write)
703
self._ext = '.sig' + self._ext
705
def get_parent_map(self, keys):
708
text = self._load_text(key)
714
def get_record_stream(self, keys, sort_order, include_delta_closure):
716
text = self._load_text(key)
718
yield AbsentContentFactory(key)
720
yield FulltextContentFactory(key, None, None, text)
723
if not self._is_locked():
724
raise errors.ObjectNotLocked(self)
726
for quoted_relpath in self._transport.iter_files_recursive():
727
relpath = urllib.unquote(quoted_relpath)
728
path, ext = os.path.splitext(relpath)
731
if not relpath.endswith('.sig'):
733
relpaths.add(relpath[:-4])
734
paths = list(relpaths)
735
return set([self._mapper.unmap(path) for path in paths])
592
text_store = self._get_text_store(repo_transport, control_files)
593
control_store = self._get_control_store(repo_transport, control_files)
594
_revision_store = self._get_revision_store(repo_transport, control_files)
595
return WeaveMetaDirRepository(_format=self,
597
control_files=control_files,
598
_revision_store=_revision_store,
599
control_store=control_store,
600
text_store=text_store)
603
class WeaveCommitBuilder(CommitBuilder):
604
"""A builder for weave based repos that don't support ghosts."""
606
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
607
versionedfile = self.repository.weave_store.get_weave_or_empty(
608
file_id, self.repository.get_transaction())
609
result = versionedfile.add_lines(
610
self._new_revision_id, parents, new_lines,
611
nostore_sha=nostore_sha)[0:2]
612
versionedfile.clear_cache()
737
616
_legacy_formats = [RepositoryFormat4(),
738
617
RepositoryFormat5(),