23
from __future__ import absolute_import
24
from cStringIO import StringIO
28
from ...lazy_import import lazy_import
27
from bzrlib.lazy_import import lazy_import
29
28
lazy_import(globals(), """
33
31
graph as _mod_graph,
36
from breezy.bzr import (
41
revision as _mod_revision,
55
from ...repository import (
58
from ...bzr.repository import (
59
RepositoryFormatMetaDir,
61
from ...sixish import (
65
from .store.text import TextStore
66
from ...bzr.versionedfile import (
48
from bzrlib.decorators import needs_read_lock, needs_write_lock
49
from bzrlib.repository import (
51
MetaDirVersionedFileRepository,
52
MetaDirRepositoryFormat,
56
from bzrlib.store.text import TextStore
57
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
58
from bzrlib.versionedfile import (
67
59
AbsentContentFactory,
68
60
FulltextContentFactory,
71
from ...bzr.vf_repository import (
72
InterSameDataRepository,
73
VersionedFileCommitBuilder,
74
VersionedFileRepository,
75
VersionedFileRepositoryFormat,
76
MetaDirVersionedFileRepository,
77
MetaDirVersionedFileRepositoryFormat,
80
from . import bzrdir as weave_bzrdir
83
class AllInOneRepository(VersionedFileRepository):
65
class AllInOneRepository(Repository):
84
66
"""Legacy support - the repository behaviour for all-in-one branches."""
88
70
return xml5.serializer_v5
90
72
def _escape(self, file_or_path):
91
if not isinstance(file_or_path, (str, text_type)):
73
if not isinstance(file_or_path, basestring):
92
74
file_or_path = '/'.join(file_or_path)
93
75
if file_or_path == '':
95
77
return urlutils.escape(osutils.safe_unicode(file_or_path))
97
def __init__(self, _format, a_controldir):
79
def __init__(self, _format, a_bzrdir):
98
80
# we reuse one control files instance.
99
dir_mode = a_controldir._get_dir_mode()
100
file_mode = a_controldir._get_file_mode()
81
dir_mode = a_bzrdir._get_dir_mode()
82
file_mode = a_bzrdir._get_file_mode()
102
84
def get_store(name, compressed=True, prefixed=False):
103
85
# FIXME: This approach of assuming stores are all entirely compressed
118
100
# which allows access to this old info.
119
101
self.inventory_store = get_store('inventory-store')
120
102
self._text_store = get_store('text-store')
121
super(AllInOneRepository, self).__init__(
122
_format, a_controldir, a_controldir._control_files)
103
super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
124
106
def _all_possible_ids(self):
125
107
"""Return all the possible revisions that we could find."""
126
108
if 'evil' in debug.debug_flags:
127
109
trace.mutter_callsite(
128
110
3, "_all_possible_ids scales with size of history.")
129
with self.lock_read():
130
return [key[-1] for key in self.inventories.keys()]
111
return [key[-1] for key in self.inventories.keys()]
132
114
def _all_revision_ids(self):
133
115
"""Returns a list of all the revision ids in the repository.
150
131
self.inventories.keys()
152
133
def _backup_inventory(self):
153
t = self.controldir._control_files._transport
134
t = self.bzrdir._control_files._transport
154
135
t.copy('inventory.weave', 'inventory.backup.weave')
156
137
def _temp_inventories(self):
157
t = self.controldir._control_files._transport
138
t = self.bzrdir._control_files._transport
158
139
return self._format._get_inventories(t, self, 'inventory.new')
160
141
def get_commit_builder(self, branch, parents, config, timestamp=None,
161
142
timezone=None, committer=None, revprops=None,
162
revision_id=None, lossy=False):
163
144
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
164
result = VersionedFileCommitBuilder(self, parents, config, timestamp,
165
timezone, committer, revprops, revision_id, lossy=lossy)
145
result = CommitBuilder(self, parents, config, timestamp, timezone,
146
committer, revprops, revision_id)
166
147
self.start_write_group()
151
def get_revisions(self, revision_ids):
152
revs = self._get_revisions(revision_ids)
169
155
def _inventory_add_lines(self, revision_id, parents, lines,
171
157
"""Store lines in inv_vf and return the sha1 of the inventory."""
172
158
present_parents = self.get_graph().get_parent_map(parents)
173
159
final_parents = []
196
183
"""Returns the policy for making working trees on new branches."""
186
def revision_graph_can_have_wrong_parents(self):
187
# XXX: This is an old format that we don't support full checking on, so
188
# just claim that checking for this inconsistency is not required.
200
192
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
201
193
"""A subclass of MetaDirRepository to set weave specific policy."""
203
def __init__(self, _format, a_controldir, control_files):
204
super(WeaveMetaDirRepository, self).__init__(
205
_format, a_controldir, control_files)
195
def __init__(self, _format, a_bzrdir, control_files):
196
super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
206
197
self._serializer = _format._serializer
208
200
def _all_possible_ids(self):
209
201
"""Return all the possible revisions that we could find."""
210
202
if 'evil' in debug.debug_flags:
211
203
trace.mutter_callsite(
212
204
3, "_all_possible_ids scales with size of history.")
213
with self.lock_read():
214
return [key[-1] for key in self.inventories.keys()]
205
return [key[-1] for key in self.inventories.keys()]
216
208
def _all_revision_ids(self):
217
209
"""Returns a list of all the revision ids in the repository.
244
235
def get_commit_builder(self, branch, parents, config, timestamp=None,
245
236
timezone=None, committer=None, revprops=None,
246
revision_id=None, lossy=False):
247
238
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
248
result = VersionedFileCommitBuilder(self, parents, config, timestamp,
249
timezone, committer, revprops, revision_id, lossy=lossy)
239
result = CommitBuilder(self, parents, config, timestamp, timezone,
240
committer, revprops, revision_id)
250
241
self.start_write_group()
253
245
def get_revision(self, revision_id):
254
246
"""Return the Revision object for a named revision"""
255
with self.lock_read():
256
return self.get_revision_reconcile(revision_id)
247
r = self.get_revision_reconcile(revision_id)
258
250
def _inventory_add_lines(self, revision_id, parents, lines,
260
252
"""Store lines in inv_vf and return the sha1 of the inventory."""
261
253
present_parents = self.get_graph().get_parent_map(parents)
262
254
final_parents = []
275
270
supports_ghosts = False
276
271
supports_external_lookups = False
277
272
supports_chks = False
278
supports_nesting_repositories = True
279
273
_fetch_order = 'topological'
280
274
_fetch_reconcile = True
281
275
fast_deltas = False
282
supports_leaving_lock = False
283
supports_overriding_transport = False
284
# XXX: This is an old format that we don't support full checking on, so
285
# just claim that checking for this inconsistency is not required.
286
revision_graph_can_have_wrong_parents = False
288
def initialize(self, a_controldir, shared=False, _internal=False):
277
def initialize(self, a_bzrdir, shared=False, _internal=False):
289
278
"""Create a weave repository."""
291
raise errors.IncompatibleFormat(self, a_controldir._format)
280
raise errors.IncompatibleFormat(self, a_bzrdir._format)
293
282
if not _internal:
294
283
# always initialized when the bzrdir is.
295
return self.open(a_controldir, _found=True)
284
return self.open(a_bzrdir, _found=True)
297
286
# Create an empty weave
299
288
weavefile.write_weave_v5(weave.Weave(), sio)
300
289
empty_weave = sio.getvalue()
302
trace.mutter('creating repository in %s.', a_controldir.transport.base)
291
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
304
293
# FIXME: RBC 20060125 don't peek under the covers
305
294
# NB: no need to escape relative paths that are url safe.
306
control_files = lockable_files.LockableFiles(a_controldir.transport,
307
'branch-lock', lockable_files.TransportLock)
295
control_files = lockable_files.LockableFiles(a_bzrdir.transport,
296
'branch-lock', lockable_files.TransportLock)
308
297
control_files.create_lock()
309
298
control_files.lock_write()
310
transport = a_controldir.transport
299
transport = a_bzrdir.transport
312
transport.mkdir('revision-store',
313
mode=a_controldir._get_dir_mode())
314
transport.mkdir('weaves', mode=a_controldir._get_dir_mode())
301
transport.mkdir_multi(['revision-store', 'weaves'],
302
mode=a_bzrdir._get_dir_mode())
315
303
transport.put_bytes_non_atomic('inventory.weave', empty_weave,
316
mode=a_controldir._get_file_mode())
304
mode=a_bzrdir._get_file_mode())
318
306
control_files.unlock()
319
repository = self.open(a_controldir, _found=True)
320
self._run_post_repo_init_hooks(repository, a_controldir, shared)
307
repository = self.open(a_bzrdir, _found=True)
308
self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
321
309
return repository
323
def open(self, a_controldir, _found=False):
311
def open(self, a_bzrdir, _found=False):
324
312
"""See RepositoryFormat.open()."""
326
314
# we are being called directly and must probe.
327
315
raise NotImplementedError
329
repo_transport = a_controldir.get_repository_transport(None)
330
result = AllInOneRepository(_format=self, a_controldir=a_controldir)
317
repo_transport = a_bzrdir.get_repository_transport(None)
318
control_files = a_bzrdir._control_files
319
result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
331
320
result.revisions = self._get_revisions(repo_transport, result)
332
321
result.signatures = self._get_signatures(repo_transport, result)
333
322
result.inventories = self._get_inventories(repo_transport, result)
379
363
def _get_revisions(self, repo_transport, repo):
380
from .xml4 import serializer_v4
364
from bzrlib.xml4 import serializer_v4
381
365
return RevisionTextStore(repo_transport.clone('revision-store'),
382
serializer_v4, True, versionedfile.PrefixMapper(),
383
repo.is_locked, repo.is_write_locked)
366
serializer_v4, True, versionedfile.PrefixMapper(),
367
repo.is_locked, repo.is_write_locked)
385
369
def _get_signatures(self, repo_transport, repo):
386
370
return SignatureTextStore(repo_transport.clone('revision-store'),
387
False, versionedfile.PrefixMapper(),
388
repo.is_locked, repo.is_write_locked)
371
False, versionedfile.PrefixMapper(),
372
repo.is_locked, repo.is_write_locked)
390
374
def _get_texts(self, repo_transport, repo):
415
397
def network_name(self):
416
398
"""The network name for this format is the control dirs disk label."""
417
return self._matchingcontroldir.get_format_string()
399
return self._matchingbzrdir.get_format_string()
419
401
def _get_inventories(self, repo_transport, repo, name='inventory'):
420
402
mapper = versionedfile.ConstantMapper(name)
421
403
return versionedfile.ThunkedVersionedFiles(repo_transport,
422
weave.WeaveFile, mapper, repo.is_locked)
404
weave.WeaveFile, mapper, repo.is_locked)
424
406
def _get_revisions(self, repo_transport, repo):
425
407
return RevisionTextStore(repo_transport.clone('revision-store'),
426
xml5.serializer_v5, False, versionedfile.PrefixMapper(),
427
repo.is_locked, repo.is_write_locked)
408
xml5.serializer_v5, False, versionedfile.PrefixMapper(),
409
repo.is_locked, repo.is_write_locked)
429
411
def _get_signatures(self, repo_transport, repo):
430
412
return SignatureTextStore(repo_transport.clone('revision-store'),
431
False, versionedfile.PrefixMapper(),
432
repo.is_locked, repo.is_write_locked)
413
False, versionedfile.PrefixMapper(),
414
repo.is_locked, repo.is_write_locked)
434
416
def _get_texts(self, repo_transport, repo):
435
417
mapper = versionedfile.PrefixMapper()
436
418
base_transport = repo_transport.clone('weaves')
437
419
return versionedfile.ThunkedVersionedFiles(base_transport,
438
weave.WeaveFile, mapper, repo.is_locked)
420
weave.WeaveFile, mapper, repo.is_locked)
441
423
class RepositoryFormat6(PreSplitOutRepositoryFormat):
462
442
def network_name(self):
463
443
"""The network name for this format is the control dirs disk label."""
464
return self._matchingcontroldir.get_format_string()
444
return self._matchingbzrdir.get_format_string()
466
446
def _get_inventories(self, repo_transport, repo, name='inventory'):
467
447
mapper = versionedfile.ConstantMapper(name)
468
448
return versionedfile.ThunkedVersionedFiles(repo_transport,
469
weave.WeaveFile, mapper, repo.is_locked)
449
weave.WeaveFile, mapper, repo.is_locked)
471
451
def _get_revisions(self, repo_transport, repo):
472
452
return RevisionTextStore(repo_transport.clone('revision-store'),
473
xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
474
repo.is_locked, repo.is_write_locked)
453
xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
454
repo.is_locked, repo.is_write_locked)
476
456
def _get_signatures(self, repo_transport, repo):
477
457
return SignatureTextStore(repo_transport.clone('revision-store'),
478
False, versionedfile.HashPrefixMapper(),
479
repo.is_locked, repo.is_write_locked)
458
False, versionedfile.HashPrefixMapper(),
459
repo.is_locked, repo.is_write_locked)
481
461
def _get_texts(self, repo_transport, repo):
482
462
mapper = versionedfile.HashPrefixMapper()
483
463
base_transport = repo_transport.clone('weaves')
484
464
return versionedfile.ThunkedVersionedFiles(base_transport,
485
weave.WeaveFile, mapper, repo.is_locked)
488
class RepositoryFormat7(MetaDirVersionedFileRepositoryFormat):
465
weave.WeaveFile, mapper, repo.is_locked)
468
class RepositoryFormat7(MetaDirRepositoryFormat):
489
469
"""Bzr repository 7.
491
471
This repository format has:
523
499
def _get_inventories(self, repo_transport, repo, name='inventory'):
524
500
mapper = versionedfile.ConstantMapper(name)
525
501
return versionedfile.ThunkedVersionedFiles(repo_transport,
526
weave.WeaveFile, mapper, repo.is_locked)
502
weave.WeaveFile, mapper, repo.is_locked)
528
504
def _get_revisions(self, repo_transport, repo):
529
505
return RevisionTextStore(repo_transport.clone('revision-store'),
530
xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
531
repo.is_locked, repo.is_write_locked)
506
xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
507
repo.is_locked, repo.is_write_locked)
533
509
def _get_signatures(self, repo_transport, repo):
534
510
return SignatureTextStore(repo_transport.clone('revision-store'),
535
True, versionedfile.HashPrefixMapper(),
536
repo.is_locked, repo.is_write_locked)
511
True, versionedfile.HashPrefixMapper(),
512
repo.is_locked, repo.is_write_locked)
538
514
def _get_texts(self, repo_transport, repo):
539
515
mapper = versionedfile.HashPrefixMapper()
540
516
base_transport = repo_transport.clone('weaves')
541
517
return versionedfile.ThunkedVersionedFiles(base_transport,
542
weave.WeaveFile, mapper, repo.is_locked)
518
weave.WeaveFile, mapper, repo.is_locked)
544
def initialize(self, a_controldir, shared=False):
520
def initialize(self, a_bzrdir, shared=False):
545
521
"""Create a weave repository.
547
523
:param shared: If true the repository will be initialized as a shared
550
526
# Create an empty weave
552
528
weavefile.write_weave_v5(weave.Weave(), sio)
553
529
empty_weave = sio.getvalue()
555
trace.mutter('creating repository in %s.', a_controldir.transport.base)
531
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
556
532
dirs = ['revision-store', 'weaves']
557
files = [('inventory.weave', BytesIO(empty_weave)),
533
files = [('inventory.weave', StringIO(empty_weave)),
559
535
utf8_files = [('format', self.get_format_string())]
561
self._upload_blank_content(
562
a_controldir, dirs, files, utf8_files, shared)
563
return self.open(a_controldir=a_controldir, _found=True)
537
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
538
return self.open(a_bzrdir=a_bzrdir, _found=True)
565
def open(self, a_controldir, _found=False, _override_transport=None):
540
def open(self, a_bzrdir, _found=False, _override_transport=None):
566
541
"""See RepositoryFormat.open().
568
543
:param _override_transport: INTERNAL USE ONLY. Allows opening the
570
545
than normal. I.e. during 'upgrade'.
573
format = RepositoryFormatMetaDir.find_format(a_controldir)
548
format = RepositoryFormat.find_format(a_bzrdir)
574
549
if _override_transport is not None:
575
550
repo_transport = _override_transport
577
repo_transport = a_controldir.get_repository_transport(None)
552
repo_transport = a_bzrdir.get_repository_transport(None)
578
553
control_files = lockable_files.LockableFiles(repo_transport,
579
'lock', lockdir.LockDir)
580
result = WeaveMetaDirRepository(_format=self, a_controldir=a_controldir,
581
control_files=control_files)
554
'lock', lockdir.LockDir)
555
result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
556
control_files=control_files)
582
557
result.revisions = self._get_revisions(repo_transport, result)
583
558
result.signatures = self._get_signatures(repo_transport, result)
584
559
result.inventories = self._get_inventories(repo_transport, result)
628
598
if record.storage_kind == 'absent':
629
599
raise errors.RevisionNotPresent([record.key[0]], self)
630
600
# adapt to non-tuple interface
631
if record.storage_kind in ('fulltext', 'chunks', 'lines'):
601
if record.storage_kind == 'fulltext':
632
602
self.add_lines(record.key, None,
633
record.get_bytes_as('lines'))
603
osutils.split_lines(record.get_bytes_as('fulltext')))
635
adapter_key = record.storage_kind, 'lines'
605
adapter_key = record.storage_kind, 'fulltext'
637
607
adapter = adapters[adapter_key]
639
609
adapter_factory = adapter_registry.get(adapter_key)
640
610
adapter = adapter_factory(self)
641
611
adapters[adapter_key] = adapter
642
lines = adapter.get_bytes(
643
record, record.get_bytes_as(record.storage_kind))
612
lines = osutils.split_lines(adapter.get_bytes(
613
record, record.get_bytes_as(record.storage_kind)))
645
615
self.add_lines(record.key, None, lines)
646
except errors.RevisionAlreadyPresent:
616
except RevisionAlreadyPresent:
649
619
def _load_text(self, key):
767
737
relpaths.add(relpath[:-4])
768
738
paths = list(relpaths)
769
return {self._mapper.unmap(path) for path in paths}
772
class InterWeaveRepo(InterSameDataRepository):
773
"""Optimised code paths between Weave based repositories.
777
def _get_repo_format_to_test(self):
778
return RepositoryFormat7()
781
def is_compatible(source, target):
782
"""Be compatible with known Weave formats.
784
We don't test for the stores being of specific types because that
785
could lead to confusing results, and there is no need to be
789
return (isinstance(source._format, (RepositoryFormat5,
792
and isinstance(target._format, (RepositoryFormat5,
795
except AttributeError:
798
def copy_content(self, revision_id=None):
799
"""See InterRepository.copy_content()."""
800
with self.lock_write():
801
# weave specific optimised path:
803
self.target.set_make_working_trees(
804
self.source.make_working_trees())
805
except (errors.RepositoryUpgradeRequired, NotImplementedError):
808
if self.source._transport.listable():
809
with ui.ui_factory.nested_progress_bar() as pb:
810
self.target.texts.insert_record_stream(
811
self.source.texts.get_record_stream(
812
self.source.texts.keys(), 'topological', False))
813
pb.update('Copying inventory', 0, 1)
814
self.target.inventories.insert_record_stream(
815
self.source.inventories.get_record_stream(
816
self.source.inventories.keys(), 'topological', False))
817
self.target.signatures.insert_record_stream(
818
self.source.signatures.get_record_stream(
819
self.source.signatures.keys(),
821
self.target.revisions.insert_record_stream(
822
self.source.revisions.get_record_stream(
823
self.source.revisions.keys(),
824
'topological', True))
826
self.target.fetch(self.source, revision_id=revision_id)
828
def search_missing_revision_ids(self, find_ghosts=True, revision_ids=None,
829
if_present_ids=None, limit=None):
830
"""See InterRepository.search_missing_revision_ids()."""
831
with self.lock_read():
832
# we want all revisions to satisfy revision_id in source.
833
# but we don't want to stat every file here and there.
834
# we want then, all revisions other needs to satisfy revision_id
835
# checked, but not those that we have locally.
836
# so the first thing is to get a subset of the revisions to
837
# satisfy revision_id in source, and then eliminate those that
838
# we do already have.
839
# this is slow on high latency connection to self, but as this
840
# disk format scales terribly for push anyway due to rewriting
841
# inventory.weave, this is considered acceptable.
843
source_ids_set = self._present_source_revisions_for(
844
revision_ids, if_present_ids)
845
# source_ids is the worst possible case we may need to pull.
846
# now we want to filter source_ids against what we actually
847
# have in target, but don't try to check for existence where we
848
# know we do not have a revision as that would be pointless.
849
target_ids = set(self.target._all_possible_ids())
850
possibly_present_revisions = target_ids.intersection(
852
actually_present_revisions = set(
853
self.target._eliminate_revisions_not_present(
854
possibly_present_revisions))
855
required_revisions = source_ids_set.difference(
856
actually_present_revisions)
857
if revision_ids is not None:
858
# we used get_ancestry to determine source_ids then we are
859
# assured all revisions referenced are present as they are
860
# installed in topological order. and the tip revision was
861
# validated by get_ancestry.
862
result_set = required_revisions
864
# if we just grabbed the possibly available ids, then
865
# we only have an estimate of whats available and need to
866
# validate that against the revision records.
868
self.source._eliminate_revisions_not_present(
870
if limit is not None:
871
topo_ordered = self.get_graph().iter_topo_order(result_set)
872
result_set = set(itertools.islice(topo_ordered, limit))
873
return self.source.revision_ids_to_search_result(result_set)
876
InterRepository.register_optimiser(InterWeaveRepo)
879
def get_extra_interrepo_test_combinations():
880
from ...bzr import knitrepo
881
return [(InterRepository, RepositoryFormat5(),
882
knitrepo.RepositoryFormatKnit3())]
739
return set([self._mapper.unmap(path) for path in paths])
741
_legacy_formats = [RepositoryFormat4(),