71
80
# not broken out yet because the controlweaves|inventory_store
72
# and text_store | weave_store bits are still different.
81
# and texts bits are still different.
73
82
if isinstance(_format, RepositoryFormat4):
74
83
# cannot remove these - there is still no consistent api
75
84
# which allows access to this old info.
76
85
self.inventory_store = get_store('inventory-store')
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)
79
if control_store is not None:
80
control_store.get_scope = self.get_transaction
81
text_store.get_scope = self.get_transaction
86
self._text_store = get_store('text-store')
87
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
84
90
def _all_possible_ids(self):
85
91
"""Return all the possible revisions that we could find."""
86
92
if 'evil' in debug.debug_flags:
87
93
mutter_callsite(3, "_all_possible_ids scales with size of history.")
88
return self.get_inventory_weave().versions()
94
return [key[-1] for key in self.inventories.keys()]
91
97
def _all_revision_ids(self):
95
101
present: for weaves ghosts may lead to a lack of correctness until
96
102
the reweave updates the parents list.
98
if self._revision_store.text_store.listable():
99
return self._revision_store.all_revision_ids(self.get_transaction())
100
result = self._all_possible_ids()
101
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
102
# ids. (It should, since _revision_store's API should change to
103
# return utf8 revision_ids)
104
return self._eliminate_revisions_not_present(result)
106
def _check_revision_parents(self, revision, inventory):
107
"""Private to Repository and Fetch.
109
This checks the parentage of revision in an inventory weave for
110
consistency and is only applicable to inventory-weave-for-ancestry
111
using repository formats & fetchers.
113
weave_parents = inventory.get_parent_map(
114
[revision.revision_id])[revision.revision_id]
115
parent_map = inventory.get_parent_map(revision.parent_ids)
116
for parent_id in revision.parent_ids:
117
if parent_id in parent_map:
118
# this parent must not be a ghost.
119
if not parent_id in weave_parents:
121
raise errors.CorruptRepository(self)
104
return [key[-1] for key in self.revisions.keys()]
106
def _activate_new_inventory(self):
107
"""Put a replacement inventory.new into use as inventories."""
108
# Copy the content across
109
t = self.bzrdir._control_files._transport
110
t.copy('inventory.new.weave', 'inventory.weave')
111
# delete the temp inventory
112
t.delete('inventory.new.weave')
113
# Check we can parse the new weave properly as a sanity check
114
self.inventories.keys()
116
def _backup_inventory(self):
117
t = self.bzrdir._control_files._transport
118
t.copy('inventory.weave', 'inventory.backup.weave')
120
def _temp_inventories(self):
121
t = self.bzrdir._control_files._transport
122
return self._format._get_inventories(t, self, 'inventory.new')
123
124
def get_commit_builder(self, branch, parents, config, timestamp=None,
124
125
timezone=None, committer=None, revprops=None,
125
126
revision_id=None):
126
127
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
127
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
128
result = CommitBuilder(self, parents, config, timestamp, timezone,
128
129
committer, revprops, revision_id)
129
130
self.start_write_group()
133
134
def get_revisions(self, revision_ids):
134
135
revs = self._get_revisions(revision_ids)
135
# weave corruption can lead to absent revision markers that should be
137
# the following test is reasonably cheap (it needs a single weave read)
138
# and the weave is cached in read transactions. In write transactions
139
# it is not cached but typically we only read a small number of
140
# revisions. For knits when they are introduced we will probably want
141
# to ensure that caching write transactions are in use.
142
inv = self.get_inventory_weave()
144
self._check_revision_parents(rev, inv)
147
def has_revisions(self, revision_ids):
148
"""See Repository.has_revisions()."""
150
transaction = self.get_transaction()
151
for revision_id in revision_ids:
152
if self._revision_store.has_revision_id(revision_id, transaction):
153
result.add(revision_id)
138
def _inventory_add_lines(self, revision_id, parents, lines,
140
"""Store lines in inv_vf and return the sha1 of the inventory."""
141
present_parents = self.get_graph().get_parent_map(parents)
143
for parent in parents:
144
if parent in present_parents:
145
final_parents.append((parent,))
146
return self.inventories.add_lines((revision_id,), final_parents, lines,
147
check_content=check_content)[0]
157
150
def is_shared(self):
200
193
present: for weaves ghosts may lead to a lack of correctness until
201
194
the reweave updates the parents list.
203
if self._revision_store.text_store.listable():
204
return self._revision_store.all_revision_ids(self.get_transaction())
205
result = self._all_possible_ids()
206
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
207
# ids. (It should, since _revision_store's API should change to
208
# return utf8 revision_ids)
209
return self._eliminate_revisions_not_present(result)
211
def _check_revision_parents(self, revision, inventory):
212
"""Private to Repository and Fetch.
214
This checks the parentage of revision in an inventory weave for
215
consistency and is only applicable to inventory-weave-for-ancestry
216
using repository formats & fetchers.
218
weave_parents = inventory.get_parent_map(
219
[revision.revision_id])[revision.revision_id]
220
parent_map = inventory.get_parent_map(revision.parent_ids)
221
for parent_id in revision.parent_ids:
222
if parent_id in parent_map:
223
# this parent must not be a ghost.
224
if not parent_id in weave_parents:
226
raise errors.CorruptRepository(self)
196
return [key[-1] for key in self.revisions.keys()]
198
def _activate_new_inventory(self):
199
"""Put a replacement inventory.new into use as inventories."""
200
# Copy the content across
202
t.copy('inventory.new.weave', 'inventory.weave')
203
# delete the temp inventory
204
t.delete('inventory.new.weave')
205
# Check we can parse the new weave properly as a sanity check
206
self.inventories.keys()
208
def _backup_inventory(self):
210
t.copy('inventory.weave', 'inventory.backup.weave')
212
def _temp_inventories(self):
214
return self._format._get_inventories(t, self, 'inventory.new')
228
216
def get_commit_builder(self, branch, parents, config, timestamp=None,
229
217
timezone=None, committer=None, revprops=None,
230
218
revision_id=None):
231
219
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
232
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
220
result = CommitBuilder(self, parents, config, timestamp, timezone,
233
221
committer, revprops, revision_id)
234
222
self.start_write_group()
238
226
def get_revision(self, revision_id):
239
227
"""Return the Revision object for a named revision"""
240
# TODO: jam 20070210 get_revision_reconcile should do this for us
241
228
r = self.get_revision_reconcile(revision_id)
242
# weave corruption can lead to absent revision markers that should be
244
# the following test is reasonably cheap (it needs a single weave read)
245
# and the weave is cached in read transactions. In write transactions
246
# it is not cached but typically we only read a small number of
247
# revisions. For knits when they are introduced we will probably want
248
# to ensure that caching write transactions are in use.
249
inv = self.get_inventory_weave()
250
self._check_revision_parents(r, inv)
253
def has_revisions(self, revision_ids):
254
"""See Repository.has_revisions()."""
256
transaction = self.get_transaction()
257
for revision_id in revision_ids:
258
if self._revision_store.has_revision_id(revision_id, transaction):
259
result.add(revision_id)
231
def _inventory_add_lines(self, revision_id, parents, lines,
233
"""Store lines in inv_vf and return the sha1 of the inventory."""
234
present_parents = self.get_graph().get_parent_map(parents)
236
for parent in parents:
237
if parent in present_parents:
238
final_parents.append((parent,))
239
return self.inventories.add_lines((revision_id,), final_parents, lines,
240
check_content=check_content)[0]
262
242
def revision_graph_can_have_wrong_parents(self):
263
# XXX: This is an old format that we don't support full checking on, so
264
# just claim that checking for this inconsistency is not required.
376
def _get_control_store(self, repo_transport, control_files):
377
"""Format 4 repositories have no formal control store at this point.
379
This will cause any control-file-needing apis to fail - this is desired.
341
def _get_inventories(self, repo_transport, repo, name='inventory'):
342
# No inventories store written so far.
383
def _get_revision_store(self, repo_transport, control_files):
384
"""See RepositoryFormat._get_revision_store()."""
345
def _get_revisions(self, repo_transport, repo):
385
346
from bzrlib.xml4 import serializer_v4
386
return self._get_text_rev_store(repo_transport,
389
serializer=serializer_v4)
391
def _get_text_store(self, transport, control_files):
392
"""See RepositoryFormat._get_text_store()."""
347
return RevisionTextStore(repo_transport.clone('revision-store'),
348
serializer_v4, True, versionedfile.PrefixMapper(),
349
repo.is_locked, repo.is_write_locked)
351
def _get_signatures(self, repo_transport, repo):
352
return SignatureTextStore(repo_transport.clone('revision-store'),
353
False, versionedfile.PrefixMapper(),
354
repo.is_locked, repo.is_write_locked)
356
def _get_texts(self, repo_transport, repo):
395
360
class RepositoryFormat5(PreSplitOutRepositoryFormat):
411
376
"""See RepositoryFormat.get_format_description()."""
412
377
return "Weave repository format 5"
414
def _get_revision_store(self, repo_transport, control_files):
415
"""See RepositoryFormat._get_revision_store()."""
416
"""Return the revision store object for this a_bzrdir."""
417
return self._get_text_rev_store(repo_transport,
422
def _get_text_store(self, transport, control_files):
423
"""See RepositoryFormat._get_text_store()."""
424
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
379
def _get_inventories(self, repo_transport, repo, name='inventory'):
380
mapper = versionedfile.ConstantMapper(name)
381
return versionedfile.ThunkedVersionedFiles(repo_transport,
382
weave.WeaveFile, mapper, repo.is_locked)
384
def _get_revisions(self, repo_transport, repo):
385
from bzrlib.xml5 import serializer_v5
386
return RevisionTextStore(repo_transport.clone('revision-store'),
387
serializer_v5, False, versionedfile.PrefixMapper(),
388
repo.is_locked, repo.is_write_locked)
390
def _get_signatures(self, repo_transport, repo):
391
return SignatureTextStore(repo_transport.clone('revision-store'),
392
False, versionedfile.PrefixMapper(),
393
repo.is_locked, repo.is_write_locked)
395
def _get_texts(self, repo_transport, repo):
396
mapper = versionedfile.PrefixMapper()
397
base_transport = repo_transport.clone('weaves')
398
return versionedfile.ThunkedVersionedFiles(base_transport,
399
weave.WeaveFile, mapper, repo.is_locked)
427
402
class RepositoryFormat6(PreSplitOutRepositoryFormat):
443
418
"""See RepositoryFormat.get_format_description()."""
444
419
return "Weave repository format 6"
446
def _get_revision_store(self, repo_transport, control_files):
447
"""See RepositoryFormat._get_revision_store()."""
448
return self._get_text_rev_store(repo_transport,
454
def _get_text_store(self, transport, control_files):
455
"""See RepositoryFormat._get_text_store()."""
456
return self._get_versioned_file_store('weaves', transport, control_files)
421
def _get_inventories(self, repo_transport, repo, name='inventory'):
422
mapper = versionedfile.ConstantMapper(name)
423
return versionedfile.ThunkedVersionedFiles(repo_transport,
424
weave.WeaveFile, mapper, repo.is_locked)
426
def _get_revisions(self, repo_transport, repo):
427
from bzrlib.xml5 import serializer_v5
428
return RevisionTextStore(repo_transport.clone('revision-store'),
429
serializer_v5, False, versionedfile.HashPrefixMapper(),
430
repo.is_locked, repo.is_write_locked)
432
def _get_signatures(self, repo_transport, repo):
433
return SignatureTextStore(repo_transport.clone('revision-store'),
434
False, versionedfile.HashPrefixMapper(),
435
repo.is_locked, repo.is_write_locked)
437
def _get_texts(self, repo_transport, repo):
438
mapper = versionedfile.HashPrefixMapper()
439
base_transport = repo_transport.clone('weaves')
440
return versionedfile.ThunkedVersionedFiles(base_transport,
441
weave.WeaveFile, mapper, repo.is_locked)
458
444
class RepositoryFormat7(MetaDirRepositoryFormat):
459
445
"""Bzr repository 7.
488
467
def check_conversion_target(self, target_format):
491
def _get_revision_store(self, repo_transport, control_files):
492
"""See RepositoryFormat._get_revision_store()."""
493
return self._get_text_rev_store(repo_transport,
500
def _get_text_store(self, transport, control_files):
501
"""See RepositoryFormat._get_text_store()."""
502
return self._get_versioned_file_store('weaves',
470
def _get_inventories(self, repo_transport, repo, name='inventory'):
471
mapper = versionedfile.ConstantMapper(name)
472
return versionedfile.ThunkedVersionedFiles(repo_transport,
473
weave.WeaveFile, mapper, repo.is_locked)
475
def _get_revisions(self, repo_transport, repo):
476
from bzrlib.xml5 import serializer_v5
477
return RevisionTextStore(repo_transport.clone('revision-store'),
478
serializer_v5, True, versionedfile.HashPrefixMapper(),
479
repo.is_locked, repo.is_write_locked)
481
def _get_signatures(self, repo_transport, repo):
482
return SignatureTextStore(repo_transport.clone('revision-store'),
483
True, versionedfile.HashPrefixMapper(),
484
repo.is_locked, repo.is_write_locked)
486
def _get_texts(self, repo_transport, repo):
487
mapper = versionedfile.HashPrefixMapper()
488
base_transport = repo_transport.clone('weaves')
489
return versionedfile.ThunkedVersionedFiles(base_transport,
490
weave.WeaveFile, mapper, repo.is_locked)
506
492
def initialize(self, a_bzrdir, shared=False):
507
493
"""Create a weave repository.
538
524
repo_transport = a_bzrdir.get_repository_transport(None)
539
525
control_files = lockable_files.LockableFiles(repo_transport,
540
526
'lock', lockdir.LockDir)
541
text_store = self._get_text_store(repo_transport, control_files)
542
control_store = self._get_control_store(repo_transport, control_files)
543
_revision_store = self._get_revision_store(repo_transport, control_files)
544
return WeaveMetaDirRepository(_format=self,
546
control_files=control_files,
547
_revision_store=_revision_store,
548
control_store=control_store,
549
text_store=text_store)
552
class WeaveCommitBuilder(CommitBuilder):
553
"""A builder for weave based repos that don't support ghosts."""
555
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
556
versionedfile = self.repository.weave_store.get_weave_or_empty(
557
file_id, self.repository.get_transaction())
558
result = versionedfile.add_lines(
559
self._new_revision_id, parents, new_lines,
560
nostore_sha=nostore_sha)[0:2]
527
result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
528
control_files=control_files)
529
result.revisions = self._get_revisions(repo_transport, result)
530
result.signatures = self._get_signatures(repo_transport, result)
531
result.inventories = self._get_inventories(repo_transport, result)
532
result.texts = self._get_texts(repo_transport, result)
533
result._transport = repo_transport
537
class TextVersionedFiles(VersionedFiles):
538
"""Just-a-bunch-of-files based VersionedFile stores."""
540
def __init__(self, transport, compressed, mapper, is_locked, can_write):
541
self._compressed = compressed
542
self._transport = transport
543
self._mapper = mapper
548
self._is_locked = is_locked
549
self._can_write = can_write
551
def add_lines(self, key, parents, lines):
552
"""Add a revision to the store."""
553
if not self._is_locked():
554
raise errors.ObjectNotLocked(self)
555
if not self._can_write():
556
raise errors.ReadOnlyError(self)
558
raise ValueError('bad idea to put / in %r' % key)
559
text = ''.join(lines)
561
text = bytes_to_gzip(text)
562
path = self._map(key)
563
self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
565
def insert_record_stream(self, stream):
567
for record in stream:
568
# Raise an error when a record is missing.
569
if record.storage_kind == 'absent':
570
raise errors.RevisionNotPresent([record.key[0]], self)
571
# adapt to non-tuple interface
572
if record.storage_kind == 'fulltext':
573
self.add_lines(record.key, None,
574
osutils.split_lines(record.get_bytes_as('fulltext')))
576
adapter_key = record.storage_kind, 'fulltext'
578
adapter = adapters[adapter_key]
580
adapter_factory = adapter_registry.get(adapter_key)
581
adapter = adapter_factory(self)
582
adapters[adapter_key] = adapter
583
lines = osutils.split_lines(adapter.get_bytes(
584
record, record.get_bytes_as(record.storage_kind)))
586
self.add_lines(record.key, None, lines)
587
except RevisionAlreadyPresent:
590
def _load_text(self, key):
591
if not self._is_locked():
592
raise errors.ObjectNotLocked(self)
593
path = self._map(key)
595
text = self._transport.get_bytes(path)
596
compressed = self._compressed
597
except errors.NoSuchFile:
599
# try without the .gz
602
text = self._transport.get_bytes(path)
604
except errors.NoSuchFile:
609
text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
613
return self._mapper.map(key) + self._ext
616
class RevisionTextStore(TextVersionedFiles):
617
"""Legacy thunk for format 4 repositories."""
619
def __init__(self, transport, serializer, compressed, mapper, is_locked,
621
"""Create a RevisionTextStore at transport with serializer."""
622
TextVersionedFiles.__init__(self, transport, compressed, mapper,
623
is_locked, can_write)
624
self._serializer = serializer
626
def _load_text_parents(self, key):
627
text = self._load_text(key)
630
parents = self._serializer.read_revision_from_string(text).parent_ids
631
return text, tuple((parent,) for parent in parents)
633
def get_parent_map(self, keys):
636
parents = self._load_text_parents(key)[1]
639
result[key] = parents
642
def get_record_stream(self, keys, sort_order, include_delta_closure):
644
text, parents = self._load_text_parents(key)
646
yield AbsentContentFactory(key)
648
yield FulltextContentFactory(key, parents, None, text)
651
if not self._is_locked():
652
raise errors.ObjectNotLocked(self)
654
for quoted_relpath in self._transport.iter_files_recursive():
655
relpath = urllib.unquote(quoted_relpath)
656
path, ext = os.path.splitext(relpath)
659
if '.sig' not in relpath:
660
relpaths.add(relpath)
661
paths = list(relpaths)
662
return set([self._mapper.unmap(path) for path in paths])
665
class SignatureTextStore(TextVersionedFiles):
666
"""Legacy thunk for format 4-7 repositories."""
668
def __init__(self, transport, compressed, mapper, is_locked, can_write):
669
TextVersionedFiles.__init__(self, transport, compressed, mapper,
670
is_locked, can_write)
671
self._ext = '.sig' + self._ext
673
def get_parent_map(self, keys):
676
text = self._load_text(key)
682
def get_record_stream(self, keys, sort_order, include_delta_closure):
684
text = self._load_text(key)
686
yield AbsentContentFactory(key)
688
yield FulltextContentFactory(key, None, None, text)
691
if not self._is_locked():
692
raise errors.ObjectNotLocked(self)
694
for quoted_relpath in self._transport.iter_files_recursive():
695
relpath = urllib.unquote(quoted_relpath)
696
path, ext = os.path.splitext(relpath)
699
if not relpath.endswith('.sig'):
701
relpaths.add(relpath[:-4])
702
paths = list(relpaths)
703
return set([self._mapper.unmap(path) for path in paths])
564
705
_legacy_formats = [RepositoryFormat4(),
565
706
RepositoryFormat5(),