32
41
revision as _mod_revision,
37
48
from bzrlib.decorators import needs_read_lock, needs_write_lock
38
49
from bzrlib.repository import (
51
MetaDirVersionedFileRepository,
41
52
MetaDirRepositoryFormat,
45
56
from bzrlib.store.text import TextStore
46
from bzrlib.symbol_versioning import deprecated_method, one_four
47
from bzrlib.trace import mutter
57
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
58
from bzrlib.versionedfile import (
60
FulltextContentFactory,
50
65
class AllInOneRepository(Repository):
51
66
"""Legacy support - the repository behaviour for all-in-one branches."""
53
_serializer = xml5.serializer_v5
55
def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
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):
56
80
# we reuse one control files instance.
57
dir_mode = a_bzrdir._control_files._dir_mode
58
file_mode = a_bzrdir._control_files._file_mode
81
dir_mode = a_bzrdir._get_dir_mode()
82
file_mode = a_bzrdir._get_file_mode()
60
84
def get_store(name, compressed=True, prefixed=False):
61
85
# FIXME: This approach of assuming stores are all entirely compressed
62
# or entirely uncompressed is tidy, but breaks upgrade from
63
# some existing branches where there's a mixture; we probably
86
# or entirely uncompressed is tidy, but breaks upgrade from
87
# some existing branches where there's a mixture; we probably
64
88
# still want the option to look for both.
65
relpath = a_bzrdir._control_files._escape(name)
66
store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
89
relpath = self._escape(name)
90
store = TextStore(a_bzrdir.transport.clone(relpath),
67
91
prefixed=prefixed, compressed=compressed,
69
93
file_mode=file_mode)
72
96
# not broken out yet because the controlweaves|inventory_store
73
# and text_store | weave_store bits are still different.
97
# and texts bits are still different.
74
98
if isinstance(_format, RepositoryFormat4):
75
# cannot remove these - there is still no consistent api
99
# cannot remove these - there is still no consistent api
76
100
# which allows access to this old info.
77
101
self.inventory_store = get_store('inventory-store')
78
text_store = get_store('text-store')
79
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
102
self._text_store = get_store('text-store')
103
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
82
106
def _all_possible_ids(self):
83
107
"""Return all the possible revisions that we could find."""
84
108
if 'evil' in debug.debug_flags:
85
mutter_callsite(3, "_all_possible_ids scales with size of history.")
86
return self.get_inventory_weave().versions()
109
trace.mutter_callsite(
110
3, "_all_possible_ids scales with size of history.")
111
return [key[-1] for key in self.inventories.keys()]
89
114
def _all_revision_ids(self):
90
"""Returns a list of all the revision ids in the repository.
115
"""Returns a list of all the revision ids in the repository.
92
These are in as much topological order as the underlying store can
117
These are in as much topological order as the underlying store can
93
118
present: for weaves ghosts may lead to a lack of correctness until
94
119
the reweave updates the parents list.
96
if self._revision_store.text_store.listable():
97
return self._revision_store.all_revision_ids(self.get_transaction())
98
result = self._all_possible_ids()
99
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
100
# ids. (It should, since _revision_store's API should change to
101
# return utf8 revision_ids)
102
return self._eliminate_revisions_not_present(result)
104
def _check_revision_parents(self, revision, inventory):
105
"""Private to Repository and Fetch.
107
This checks the parentage of revision in an inventory weave for
108
consistency and is only applicable to inventory-weave-for-ancestry
109
using repository formats & fetchers.
111
weave_parents = inventory.get_parent_map(
112
[revision.revision_id])[revision.revision_id]
113
parent_map = inventory.get_parent_map(revision.parent_ids)
114
for parent_id in revision.parent_ids:
115
if parent_id in parent_map:
116
# this parent must not be a ghost.
117
if not parent_id in weave_parents:
119
raise errors.CorruptRepository(self)
121
return [key[-1] for key in self.revisions.keys()]
123
def _activate_new_inventory(self):
124
"""Put a replacement inventory.new into use as inventories."""
125
# Copy the content across
126
t = self.bzrdir._control_files._transport
127
t.copy('inventory.new.weave', 'inventory.weave')
128
# delete the temp inventory
129
t.delete('inventory.new.weave')
130
# Check we can parse the new weave properly as a sanity check
131
self.inventories.keys()
133
def _backup_inventory(self):
134
t = self.bzrdir._control_files._transport
135
t.copy('inventory.weave', 'inventory.backup.weave')
137
def _temp_inventories(self):
138
t = self.bzrdir._control_files._transport
139
return self._format._get_inventories(t, self, 'inventory.new')
121
141
def get_commit_builder(self, branch, parents, config, timestamp=None,
122
142
timezone=None, committer=None, revprops=None,
123
143
revision_id=None):
124
144
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
125
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
145
result = CommitBuilder(self, parents, config, timestamp, timezone,
126
146
committer, revprops, revision_id)
127
147
self.start_write_group()
131
151
def get_revisions(self, revision_ids):
132
152
revs = self._get_revisions(revision_ids)
133
# weave corruption can lead to absent revision markers that should be
135
# the following test is reasonably cheap (it needs a single weave read)
136
# and the weave is cached in read transactions. In write transactions
137
# it is not cached but typically we only read a small number of
138
# revisions. For knits when they are introduced we will probably want
139
# to ensure that caching write transactions are in use.
140
inv = self.get_inventory_weave()
142
self._check_revision_parents(rev, inv)
145
@deprecated_method(one_four)
147
def get_revision_graph(self, revision_id=None):
148
"""Return a dictionary containing the revision graph.
150
:param revision_id: The revision_id to get a graph from. If None, then
151
the entire revision graph is returned. This is a deprecated mode of
152
operation and will be removed in the future.
153
:return: a dictionary of revision_id->revision_parents_list.
155
if 'evil' in debug.debug_flags:
157
"get_revision_graph scales with size of history.")
158
# special case NULL_REVISION
159
if revision_id == _mod_revision.NULL_REVISION:
161
a_weave = self.get_inventory_weave()
162
all_revisions = self._eliminate_revisions_not_present(
164
entire_graph = a_weave.get_parent_map(all_revisions)
165
if revision_id is None:
167
elif revision_id not in entire_graph:
168
raise errors.NoSuchRevision(self, revision_id)
170
# add what can be reached from revision_id
172
pending = set([revision_id])
173
while len(pending) > 0:
175
result[node] = entire_graph[node]
176
for revision_id in result[node]:
177
if revision_id not in result:
178
pending.add(revision_id)
181
def has_revisions(self, revision_ids):
182
"""See Repository.has_revisions()."""
184
transaction = self.get_transaction()
185
for revision_id in revision_ids:
186
if self._revision_store.has_revision_id(revision_id, transaction):
187
result.add(revision_id)
155
def _inventory_add_lines(self, revision_id, parents, lines,
157
"""Store lines in inv_vf and return the sha1 of the inventory."""
158
present_parents = self.get_graph().get_parent_map(parents)
160
for parent in parents:
161
if parent in present_parents:
162
final_parents.append((parent,))
163
return self.inventories.add_lines((revision_id,), final_parents, lines,
164
check_content=check_content)[0]
191
166
def is_shared(self):
192
167
"""AllInOne repositories cannot be shared."""
217
class WeaveMetaDirRepository(MetaDirRepository):
192
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
218
193
"""A subclass of MetaDirRepository to set weave specific policy."""
220
_serializer = xml5.serializer_v5
195
def __init__(self, _format, a_bzrdir, control_files):
196
super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
197
self._serializer = _format._serializer
223
200
def _all_possible_ids(self):
224
201
"""Return all the possible revisions that we could find."""
225
202
if 'evil' in debug.debug_flags:
226
mutter_callsite(3, "_all_possible_ids scales with size of history.")
227
return self.get_inventory_weave().versions()
203
trace.mutter_callsite(
204
3, "_all_possible_ids scales with size of history.")
205
return [key[-1] for key in self.inventories.keys()]
230
208
def _all_revision_ids(self):
231
"""Returns a list of all the revision ids in the repository.
209
"""Returns a list of all the revision ids in the repository.
233
These are in as much topological order as the underlying store can
211
These are in as much topological order as the underlying store can
234
212
present: for weaves ghosts may lead to a lack of correctness until
235
213
the reweave updates the parents list.
237
if self._revision_store.text_store.listable():
238
return self._revision_store.all_revision_ids(self.get_transaction())
239
result = self._all_possible_ids()
240
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
241
# ids. (It should, since _revision_store's API should change to
242
# return utf8 revision_ids)
243
return self._eliminate_revisions_not_present(result)
245
def _check_revision_parents(self, revision, inventory):
246
"""Private to Repository and Fetch.
248
This checks the parentage of revision in an inventory weave for
249
consistency and is only applicable to inventory-weave-for-ancestry
250
using repository formats & fetchers.
252
weave_parents = inventory.get_parent_map(
253
[revision.revision_id])[revision.revision_id]
254
parent_map = inventory.get_parent_map(revision.parent_ids)
255
for parent_id in revision.parent_ids:
256
if parent_id in parent_map:
257
# this parent must not be a ghost.
258
if not parent_id in weave_parents:
260
raise errors.CorruptRepository(self)
215
return [key[-1] for key in self.revisions.keys()]
217
def _activate_new_inventory(self):
218
"""Put a replacement inventory.new into use as inventories."""
219
# Copy the content across
221
t.copy('inventory.new.weave', 'inventory.weave')
222
# delete the temp inventory
223
t.delete('inventory.new.weave')
224
# Check we can parse the new weave properly as a sanity check
225
self.inventories.keys()
227
def _backup_inventory(self):
229
t.copy('inventory.weave', 'inventory.backup.weave')
231
def _temp_inventories(self):
233
return self._format._get_inventories(t, self, 'inventory.new')
262
235
def get_commit_builder(self, branch, parents, config, timestamp=None,
263
236
timezone=None, committer=None, revprops=None,
264
237
revision_id=None):
265
238
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
266
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
239
result = CommitBuilder(self, parents, config, timestamp, timezone,
267
240
committer, revprops, revision_id)
268
241
self.start_write_group()
272
245
def get_revision(self, revision_id):
273
246
"""Return the Revision object for a named revision"""
274
# TODO: jam 20070210 get_revision_reconcile should do this for us
275
247
r = self.get_revision_reconcile(revision_id)
276
# weave corruption can lead to absent revision markers that should be
278
# the following test is reasonably cheap (it needs a single weave read)
279
# and the weave is cached in read transactions. In write transactions
280
# it is not cached but typically we only read a small number of
281
# revisions. For knits when they are introduced we will probably want
282
# to ensure that caching write transactions are in use.
283
inv = self.get_inventory_weave()
284
self._check_revision_parents(r, inv)
287
@deprecated_method(one_four)
289
def get_revision_graph(self, revision_id=None):
290
"""Return a dictionary containing the revision graph.
292
:param revision_id: The revision_id to get a graph from. If None, then
293
the entire revision graph is returned. This is a deprecated mode of
294
operation and will be removed in the future.
295
:return: a dictionary of revision_id->revision_parents_list.
297
if 'evil' in debug.debug_flags:
299
"get_revision_graph scales with size of history.")
300
# special case NULL_REVISION
301
if revision_id == _mod_revision.NULL_REVISION:
303
a_weave = self.get_inventory_weave()
304
all_revisions = self._eliminate_revisions_not_present(
306
entire_graph = a_weave.get_parent_map(all_revisions)
307
if revision_id is None:
309
elif revision_id not in entire_graph:
310
raise errors.NoSuchRevision(self, revision_id)
312
# add what can be reached from revision_id
314
pending = set([revision_id])
315
while len(pending) > 0:
317
result[node] = entire_graph[node]
318
for revision_id in result[node]:
319
if revision_id not in result:
320
pending.add(revision_id)
323
def has_revisions(self, revision_ids):
324
"""See Repository.has_revisions()."""
326
transaction = self.get_transaction()
327
for revision_id in revision_ids:
328
if self._revision_store.has_revision_id(revision_id, transaction):
329
result.add(revision_id)
250
def _inventory_add_lines(self, revision_id, parents, lines,
252
"""Store lines in inv_vf and return the sha1 of the inventory."""
253
present_parents = self.get_graph().get_parent_map(parents)
255
for parent in parents:
256
if parent in present_parents:
257
final_parents.append((parent,))
258
return self.inventories.add_lines((revision_id,), final_parents, lines,
259
check_content=check_content)[0]
332
261
def revision_graph_can_have_wrong_parents(self):
333
# XXX: This is an old format that we don't support full checking on, so
334
# just claim that checking for this inconsistency is not required.
351
282
if not _internal:
352
283
# always initialized when the bzrdir is.
353
284
return self.open(a_bzrdir, _found=True)
355
286
# Create an empty weave
357
288
weavefile.write_weave_v5(weave.Weave(), sio)
358
289
empty_weave = sio.getvalue()
360
mutter('creating repository in %s.', a_bzrdir.transport.base)
361
dirs = ['revision-store', 'weaves']
362
files = [('inventory.weave', StringIO(empty_weave)),
291
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
365
293
# FIXME: RBC 20060125 don't peek under the covers
366
294
# NB: no need to escape relative paths that are url safe.
367
295
control_files = lockable_files.LockableFiles(a_bzrdir.transport,
368
'branch-lock', lockable_files.TransportLock)
296
'branch-lock', lockable_files.TransportLock)
369
297
control_files.create_lock()
370
298
control_files.lock_write()
371
control_files._transport.mkdir_multi(dirs,
372
mode=control_files._dir_mode)
299
transport = a_bzrdir.transport
374
for file, content in files:
375
control_files.put(file, content)
301
transport.mkdir_multi(['revision-store', 'weaves'],
302
mode=a_bzrdir._get_dir_mode())
303
transport.put_bytes_non_atomic('inventory.weave', empty_weave,
304
mode=a_bzrdir._get_file_mode())
377
306
control_files.unlock()
378
return self.open(a_bzrdir, _found=True)
380
def _get_control_store(self, repo_transport, control_files):
381
"""Return the control store for this repository."""
382
return self._get_versioned_file_store('',
387
def _get_text_store(self, transport, control_files):
388
"""Get a store for file texts for this format."""
389
raise NotImplementedError(self._get_text_store)
307
repository = self.open(a_bzrdir, _found=True)
308
self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
391
311
def open(self, a_bzrdir, _found=False):
392
312
"""See RepositoryFormat.open()."""
438
351
"""Format 4 is not supported.
440
353
It is not supported because the model changed from 4 to 5 and the
441
conversion logic is expensive - so doing it on the fly was not
354
conversion logic is expensive - so doing it on the fly was not
446
def _get_control_store(self, repo_transport, control_files):
447
"""Format 4 repositories have no formal control store at this point.
449
This will cause any control-file-needing apis to fail - this is desired.
359
def _get_inventories(self, repo_transport, repo, name='inventory'):
360
# No inventories store written so far.
453
def _get_revision_store(self, repo_transport, control_files):
454
"""See RepositoryFormat._get_revision_store()."""
363
def _get_revisions(self, repo_transport, repo):
455
364
from bzrlib.xml4 import serializer_v4
456
return self._get_text_rev_store(repo_transport,
459
serializer=serializer_v4)
461
def _get_text_store(self, transport, control_files):
462
"""See RepositoryFormat._get_text_store()."""
365
return RevisionTextStore(repo_transport.clone('revision-store'),
366
serializer_v4, True, versionedfile.PrefixMapper(),
367
repo.is_locked, repo.is_write_locked)
369
def _get_signatures(self, repo_transport, repo):
370
return SignatureTextStore(repo_transport.clone('revision-store'),
371
False, versionedfile.PrefixMapper(),
372
repo.is_locked, repo.is_write_locked)
374
def _get_texts(self, repo_transport, repo):
465
378
class RepositoryFormat5(PreSplitOutRepositoryFormat):
474
387
_versionedfile_class = weave.WeaveFile
475
388
_matchingbzrdir = bzrdir.BzrDirFormat5()
478
super(RepositoryFormat5, self).__init__()
390
def _serializer(self):
391
return xml5.serializer_v5
480
393
def get_format_description(self):
481
394
"""See RepositoryFormat.get_format_description()."""
482
395
return "Weave repository format 5"
484
def _get_revision_store(self, repo_transport, control_files):
485
"""See RepositoryFormat._get_revision_store()."""
486
"""Return the revision store object for this a_bzrdir."""
487
return self._get_text_rev_store(repo_transport,
492
def _get_text_store(self, transport, control_files):
493
"""See RepositoryFormat._get_text_store()."""
494
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
397
def network_name(self):
398
"""The network name for this format is the control dirs disk label."""
399
return self._matchingbzrdir.get_format_string()
401
def _get_inventories(self, repo_transport, repo, name='inventory'):
402
mapper = versionedfile.ConstantMapper(name)
403
return versionedfile.ThunkedVersionedFiles(repo_transport,
404
weave.WeaveFile, mapper, repo.is_locked)
406
def _get_revisions(self, repo_transport, repo):
407
return RevisionTextStore(repo_transport.clone('revision-store'),
408
xml5.serializer_v5, False, versionedfile.PrefixMapper(),
409
repo.is_locked, repo.is_write_locked)
411
def _get_signatures(self, repo_transport, repo):
412
return SignatureTextStore(repo_transport.clone('revision-store'),
413
False, versionedfile.PrefixMapper(),
414
repo.is_locked, repo.is_write_locked)
416
def _get_texts(self, repo_transport, repo):
417
mapper = versionedfile.PrefixMapper()
418
base_transport = repo_transport.clone('weaves')
419
return versionedfile.ThunkedVersionedFiles(base_transport,
420
weave.WeaveFile, mapper, repo.is_locked)
497
423
class RepositoryFormat6(PreSplitOutRepositoryFormat):
506
432
_versionedfile_class = weave.WeaveFile
507
433
_matchingbzrdir = bzrdir.BzrDirFormat6()
510
super(RepositoryFormat6, self).__init__()
435
def _serializer(self):
436
return xml5.serializer_v5
512
438
def get_format_description(self):
513
439
"""See RepositoryFormat.get_format_description()."""
514
440
return "Weave repository format 6"
516
def _get_revision_store(self, repo_transport, control_files):
517
"""See RepositoryFormat._get_revision_store()."""
518
return self._get_text_rev_store(repo_transport,
524
def _get_text_store(self, transport, control_files):
525
"""See RepositoryFormat._get_text_store()."""
526
return self._get_versioned_file_store('weaves', transport, control_files)
442
def network_name(self):
443
"""The network name for this format is the control dirs disk label."""
444
return self._matchingbzrdir.get_format_string()
446
def _get_inventories(self, repo_transport, repo, name='inventory'):
447
mapper = versionedfile.ConstantMapper(name)
448
return versionedfile.ThunkedVersionedFiles(repo_transport,
449
weave.WeaveFile, mapper, repo.is_locked)
451
def _get_revisions(self, repo_transport, repo):
452
return RevisionTextStore(repo_transport.clone('revision-store'),
453
xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
454
repo.is_locked, repo.is_write_locked)
456
def _get_signatures(self, repo_transport, repo):
457
return SignatureTextStore(repo_transport.clone('revision-store'),
458
False, versionedfile.HashPrefixMapper(),
459
repo.is_locked, repo.is_write_locked)
461
def _get_texts(self, repo_transport, repo):
462
mapper = versionedfile.HashPrefixMapper()
463
base_transport = repo_transport.clone('weaves')
464
return versionedfile.ThunkedVersionedFiles(base_transport,
465
weave.WeaveFile, mapper, repo.is_locked)
528
468
class RepositoryFormat7(MetaDirRepositoryFormat):
529
469
"""Bzr repository 7.
555
496
"""See RepositoryFormat.get_format_description()."""
556
497
return "Weave repository format 7"
558
def check_conversion_target(self, target_format):
561
def _get_revision_store(self, repo_transport, control_files):
562
"""See RepositoryFormat._get_revision_store()."""
563
return self._get_text_rev_store(repo_transport,
570
def _get_text_store(self, transport, control_files):
571
"""See RepositoryFormat._get_text_store()."""
572
return self._get_versioned_file_store('weaves',
499
def _get_inventories(self, repo_transport, repo, name='inventory'):
500
mapper = versionedfile.ConstantMapper(name)
501
return versionedfile.ThunkedVersionedFiles(repo_transport,
502
weave.WeaveFile, mapper, repo.is_locked)
504
def _get_revisions(self, repo_transport, repo):
505
return RevisionTextStore(repo_transport.clone('revision-store'),
506
xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
507
repo.is_locked, repo.is_write_locked)
509
def _get_signatures(self, repo_transport, repo):
510
return SignatureTextStore(repo_transport.clone('revision-store'),
511
True, versionedfile.HashPrefixMapper(),
512
repo.is_locked, repo.is_write_locked)
514
def _get_texts(self, repo_transport, repo):
515
mapper = versionedfile.HashPrefixMapper()
516
base_transport = repo_transport.clone('weaves')
517
return versionedfile.ThunkedVersionedFiles(base_transport,
518
weave.WeaveFile, mapper, repo.is_locked)
576
520
def initialize(self, a_bzrdir, shared=False):
577
521
"""Create a weave repository.
584
528
weavefile.write_weave_v5(weave.Weave(), sio)
585
529
empty_weave = sio.getvalue()
587
mutter('creating repository in %s.', a_bzrdir.transport.base)
531
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
588
532
dirs = ['revision-store', 'weaves']
589
files = [('inventory.weave', StringIO(empty_weave)),
533
files = [('inventory.weave', StringIO(empty_weave)),
591
535
utf8_files = [('format', self.get_format_string())]
593
537
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
594
538
return self.open(a_bzrdir=a_bzrdir, _found=True)
596
540
def open(self, a_bzrdir, _found=False, _override_transport=None):
597
541
"""See RepositoryFormat.open().
599
543
:param _override_transport: INTERNAL USE ONLY. Allows opening the
600
544
repository at a slightly different url
601
545
than normal. I.e. during 'upgrade'.
604
548
format = RepositoryFormat.find_format(a_bzrdir)
605
assert format.__class__ == self.__class__
606
549
if _override_transport is not None:
607
550
repo_transport = _override_transport
609
552
repo_transport = a_bzrdir.get_repository_transport(None)
610
553
control_files = lockable_files.LockableFiles(repo_transport,
611
554
'lock', lockdir.LockDir)
612
text_store = self._get_text_store(repo_transport, control_files)
613
control_store = self._get_control_store(repo_transport, control_files)
614
_revision_store = self._get_revision_store(repo_transport, control_files)
615
return WeaveMetaDirRepository(_format=self,
617
control_files=control_files,
618
_revision_store=_revision_store,
619
control_store=control_store,
620
text_store=text_store)
623
class WeaveCommitBuilder(CommitBuilder):
624
"""A builder for weave based repos that don't support ghosts."""
626
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
627
versionedfile = self.repository.weave_store.get_weave_or_empty(
628
file_id, self.repository.get_transaction())
629
result = versionedfile.add_lines(
630
self._new_revision_id, parents, new_lines,
631
nostore_sha=nostore_sha)[0:2]
632
versionedfile.clear_cache()
555
result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
556
control_files=control_files)
557
result.revisions = self._get_revisions(repo_transport, result)
558
result.signatures = self._get_signatures(repo_transport, result)
559
result.inventories = self._get_inventories(repo_transport, result)
560
result.texts = self._get_texts(repo_transport, result)
561
result.chk_bytes = None
562
result._transport = repo_transport
566
class TextVersionedFiles(VersionedFiles):
567
"""Just-a-bunch-of-files based VersionedFile stores."""
569
def __init__(self, transport, compressed, mapper, is_locked, can_write):
570
self._compressed = compressed
571
self._transport = transport
572
self._mapper = mapper
577
self._is_locked = is_locked
578
self._can_write = can_write
580
def add_lines(self, key, parents, lines):
581
"""Add a revision to the store."""
582
if not self._is_locked():
583
raise errors.ObjectNotLocked(self)
584
if not self._can_write():
585
raise errors.ReadOnlyError(self)
587
raise ValueError('bad idea to put / in %r' % (key,))
588
text = ''.join(lines)
590
text = bytes_to_gzip(text)
591
path = self._map(key)
592
self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
594
def insert_record_stream(self, stream):
596
for record in stream:
597
# Raise an error when a record is missing.
598
if record.storage_kind == 'absent':
599
raise errors.RevisionNotPresent([record.key[0]], self)
600
# adapt to non-tuple interface
601
if record.storage_kind == 'fulltext':
602
self.add_lines(record.key, None,
603
osutils.split_lines(record.get_bytes_as('fulltext')))
605
adapter_key = record.storage_kind, 'fulltext'
607
adapter = adapters[adapter_key]
609
adapter_factory = adapter_registry.get(adapter_key)
610
adapter = adapter_factory(self)
611
adapters[adapter_key] = adapter
612
lines = osutils.split_lines(adapter.get_bytes(
613
record, record.get_bytes_as(record.storage_kind)))
615
self.add_lines(record.key, None, lines)
616
except RevisionAlreadyPresent:
619
def _load_text(self, key):
620
if not self._is_locked():
621
raise errors.ObjectNotLocked(self)
622
path = self._map(key)
624
text = self._transport.get_bytes(path)
625
compressed = self._compressed
626
except errors.NoSuchFile:
628
# try without the .gz
631
text = self._transport.get_bytes(path)
633
except errors.NoSuchFile:
638
text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
642
return self._mapper.map(key) + self._ext
645
class RevisionTextStore(TextVersionedFiles):
646
"""Legacy thunk for format 4 repositories."""
648
def __init__(self, transport, serializer, compressed, mapper, is_locked,
650
"""Create a RevisionTextStore at transport with serializer."""
651
TextVersionedFiles.__init__(self, transport, compressed, mapper,
652
is_locked, can_write)
653
self._serializer = serializer
655
def _load_text_parents(self, key):
656
text = self._load_text(key)
659
parents = self._serializer.read_revision_from_string(text).parent_ids
660
return text, tuple((parent,) for parent in parents)
662
def get_parent_map(self, keys):
665
parents = self._load_text_parents(key)[1]
668
result[key] = parents
671
def get_known_graph_ancestry(self, keys):
672
"""Get a KnownGraph instance with the ancestry of keys."""
674
parent_map = self.get_parent_map(keys)
675
kg = _mod_graph.KnownGraph(parent_map)
678
def get_record_stream(self, keys, sort_order, include_delta_closure):
680
text, parents = self._load_text_parents(key)
682
yield AbsentContentFactory(key)
684
yield FulltextContentFactory(key, parents, None, text)
687
if not self._is_locked():
688
raise errors.ObjectNotLocked(self)
690
for quoted_relpath in self._transport.iter_files_recursive():
691
relpath = urllib.unquote(quoted_relpath)
692
path, ext = os.path.splitext(relpath)
695
if not relpath.endswith('.sig'):
696
relpaths.add(relpath)
697
paths = list(relpaths)
698
return set([self._mapper.unmap(path) for path in paths])
701
class SignatureTextStore(TextVersionedFiles):
702
"""Legacy thunk for format 4-7 repositories."""
704
def __init__(self, transport, compressed, mapper, is_locked, can_write):
705
TextVersionedFiles.__init__(self, transport, compressed, mapper,
706
is_locked, can_write)
707
self._ext = '.sig' + self._ext
709
def get_parent_map(self, keys):
712
text = self._load_text(key)
718
def get_record_stream(self, keys, sort_order, include_delta_closure):
720
text = self._load_text(key)
722
yield AbsentContentFactory(key)
724
yield FulltextContentFactory(key, None, None, text)
727
if not self._is_locked():
728
raise errors.ObjectNotLocked(self)
730
for quoted_relpath in self._transport.iter_files_recursive():
731
relpath = urllib.unquote(quoted_relpath)
732
path, ext = os.path.splitext(relpath)
735
if not relpath.endswith('.sig'):
737
relpaths.add(relpath[:-4])
738
paths = list(relpaths)
739
return set([self._mapper.unmap(path) for path in paths])
636
741
_legacy_formats = [RepositoryFormat4(),
637
742
RepositoryFormat5(),