41
32
revision as _mod_revision,
48
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
49
38
from bzrlib.repository import (
51
MetaDirVersionedFileRepository,
52
41
MetaDirRepositoryFormat,
56
45
from bzrlib.store.text import TextStore
57
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
58
from bzrlib.versionedfile import (
60
FulltextContentFactory,
46
from bzrlib.trace import mutter
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
trace.mutter_callsite(
110
3, "_all_possible_ids scales with size of history.")
111
return [key[-1] for key in self.inventories.keys()]
84
mutter_callsite(3, "_all_possible_ids scales with size of history.")
85
return self.get_inventory_weave().versions()
114
88
def _all_revision_ids(self):
115
"""Returns a list of all the revision ids in the repository.
89
"""Returns a list of all the revision ids in the repository.
117
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
118
92
present: for weaves ghosts may lead to a lack of correctness until
119
93
the reweave updates the parents list.
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')
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)
141
119
def get_commit_builder(self, branch, parents, config, timestamp=None,
142
120
timezone=None, committer=None, revprops=None,
143
121
revision_id=None):
144
122
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
145
result = CommitBuilder(self, parents, config, timestamp, timezone,
123
revision_id = osutils.safe_revision_id(revision_id)
124
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
146
125
committer, revprops, revision_id)
147
126
self.start_write_group()
151
130
def get_revisions(self, revision_ids):
152
131
revs = self._get_revisions(revision_ids)
132
# weave corruption can lead to absent revision markers that should be
134
# the following test is reasonably cheap (it needs a single weave read)
135
# and the weave is cached in read transactions. In write transactions
136
# it is not cached but typically we only read a small number of
137
# revisions. For knits when they are introduced we will probably want
138
# to ensure that caching write transactions are in use.
139
inv = self.get_inventory_weave()
141
self._check_revision_parents(rev, inv)
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]
145
def get_revision_graph(self, revision_id=None):
146
"""Return a dictionary containing the revision graph.
148
:param revision_id: The revision_id to get a graph from. If None, then
149
the entire revision graph is returned. This is a deprecated mode of
150
operation and will be removed in the future.
151
:return: a dictionary of revision_id->revision_parents_list.
153
if 'evil' in debug.debug_flags:
155
"get_revision_graph scales with size of history.")
156
# special case NULL_REVISION
157
if revision_id == _mod_revision.NULL_REVISION:
159
revision_id = osutils.safe_revision_id(revision_id)
160
a_weave = self.get_inventory_weave()
161
all_revisions = self._eliminate_revisions_not_present(
163
entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for
164
node in 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)
166
182
def is_shared(self):
167
183
"""AllInOne repositories cannot be shared."""
177
193
:param new_value: True to restore the default, False to disable making
180
raise errors.RepositoryUpgradeRequired(self.user_url)
196
raise NotImplementedError(self.set_make_working_trees)
182
198
def make_working_trees(self):
183
199
"""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.
192
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
203
class WeaveMetaDirRepository(MetaDirRepository):
193
204
"""A subclass of MetaDirRepository to set weave specific policy."""
195
def __init__(self, _format, a_bzrdir, control_files):
196
super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
197
self._serializer = _format._serializer
206
_serializer = xml5.serializer_v5
200
209
def _all_possible_ids(self):
201
210
"""Return all the possible revisions that we could find."""
202
211
if 'evil' in debug.debug_flags:
203
trace.mutter_callsite(
204
3, "_all_possible_ids scales with size of history.")
205
return [key[-1] for key in self.inventories.keys()]
212
mutter_callsite(3, "_all_possible_ids scales with size of history.")
213
return self.get_inventory_weave().versions()
208
216
def _all_revision_ids(self):
209
"""Returns a list of all the revision ids in the repository.
217
"""Returns a list of all the revision ids in the repository.
211
These are in as much topological order as the underlying store can
219
These are in as much topological order as the underlying store can
212
220
present: for weaves ghosts may lead to a lack of correctness until
213
221
the reweave updates the parents list.
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')
223
if self._revision_store.text_store.listable():
224
return self._revision_store.all_revision_ids(self.get_transaction())
225
result = self._all_possible_ids()
226
# TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
227
# ids. (It should, since _revision_store's API should change to
228
# return utf8 revision_ids)
229
return self._eliminate_revisions_not_present(result)
231
def _check_revision_parents(self, revision, inventory):
232
"""Private to Repository and Fetch.
234
This checks the parentage of revision in an inventory weave for
235
consistency and is only applicable to inventory-weave-for-ancestry
236
using repository formats & fetchers.
238
weave_parents = inventory.get_parents(revision.revision_id)
239
weave_names = inventory.versions()
240
for parent_id in revision.parent_ids:
241
if parent_id in weave_names:
242
# this parent must not be a ghost.
243
if not parent_id in weave_parents:
245
raise errors.CorruptRepository(self)
235
247
def get_commit_builder(self, branch, parents, config, timestamp=None,
236
248
timezone=None, committer=None, revprops=None,
237
249
revision_id=None):
238
250
self._check_ascii_revisionid(revision_id, self.get_commit_builder)
239
result = CommitBuilder(self, parents, config, timestamp, timezone,
251
revision_id = osutils.safe_revision_id(revision_id)
252
result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
240
253
committer, revprops, revision_id)
241
254
self.start_write_group()
245
258
def get_revision(self, revision_id):
246
259
"""Return the Revision object for a named revision"""
260
# TODO: jam 20070210 get_revision_reconcile should do this for us
261
revision_id = osutils.safe_revision_id(revision_id)
247
262
r = self.get_revision_reconcile(revision_id)
263
# weave corruption can lead to absent revision markers that should be
265
# the following test is reasonably cheap (it needs a single weave read)
266
# and the weave is cached in read transactions. In write transactions
267
# it is not cached but typically we only read a small number of
268
# revisions. For knits when they are introduced we will probably want
269
# to ensure that caching write transactions are in use.
270
inv = self.get_inventory_weave()
271
self._check_revision_parents(r, inv)
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]
261
def revision_graph_can_have_wrong_parents(self):
275
def get_revision_graph(self, revision_id=None):
276
"""Return a dictionary containing the revision graph.
278
:param revision_id: The revision_id to get a graph from. If None, then
279
the entire revision graph is returned. This is a deprecated mode of
280
operation and will be removed in the future.
281
:return: a dictionary of revision_id->revision_parents_list.
283
if 'evil' in debug.debug_flags:
285
"get_revision_graph scales with size of history.")
286
# special case NULL_REVISION
287
if revision_id == _mod_revision.NULL_REVISION:
289
revision_id = osutils.safe_revision_id(revision_id)
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)
265
312
class PreSplitOutRepositoryFormat(RepositoryFormat):
268
315
rich_root_data = False
269
316
supports_tree_reference = False
270
supports_ghosts = False
271
supports_external_lookups = False
272
supports_chks = False
273
_fetch_order = 'topological'
274
_fetch_reconcile = True
277
318
def initialize(self, a_bzrdir, shared=False, _internal=False):
278
"""Create a weave repository."""
319
"""Create a weave repository.
321
TODO: when creating split out bzr branch formats, move this to a common
322
base for Format5, Format6. or something like that.
280
325
raise errors.IncompatibleFormat(self, a_bzrdir._format)
282
327
if not _internal:
283
328
# always initialized when the bzrdir is.
284
329
return self.open(a_bzrdir, _found=True)
286
331
# Create an empty weave
288
333
weavefile.write_weave_v5(weave.Weave(), sio)
289
334
empty_weave = sio.getvalue()
291
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
336
mutter('creating repository in %s.', a_bzrdir.transport.base)
337
dirs = ['revision-store', 'weaves']
338
files = [('inventory.weave', StringIO(empty_weave)),
293
341
# FIXME: RBC 20060125 don't peek under the covers
294
342
# NB: no need to escape relative paths that are url safe.
295
343
control_files = lockable_files.LockableFiles(a_bzrdir.transport,
296
'branch-lock', lockable_files.TransportLock)
344
'branch-lock', lockable_files.TransportLock)
297
345
control_files.create_lock()
298
346
control_files.lock_write()
299
transport = a_bzrdir.transport
347
control_files._transport.mkdir_multi(dirs,
348
mode=control_files._dir_mode)
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())
350
for file, content in files:
351
control_files.put(file, content)
306
353
control_files.unlock()
307
repository = self.open(a_bzrdir, _found=True)
308
self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
354
return self.open(a_bzrdir, _found=True)
356
def _get_control_store(self, repo_transport, control_files):
357
"""Return the control store for this repository."""
358
return self._get_versioned_file_store('',
363
def _get_text_store(self, transport, control_files):
364
"""Get a store for file texts for this format."""
365
raise NotImplementedError(self._get_text_store)
311
367
def open(self, a_bzrdir, _found=False):
312
368
"""See RepositoryFormat.open()."""
351
414
"""Format 4 is not supported.
353
416
It is not supported because the model changed from 4 to 5 and the
354
conversion logic is expensive - so doing it on the fly was not
417
conversion logic is expensive - so doing it on the fly was not
359
def _get_inventories(self, repo_transport, repo, name='inventory'):
360
# No inventories store written so far.
422
def _get_control_store(self, repo_transport, control_files):
423
"""Format 4 repositories have no formal control store at this point.
425
This will cause any control-file-needing apis to fail - this is desired.
363
def _get_revisions(self, repo_transport, repo):
429
def _get_revision_store(self, repo_transport, control_files):
430
"""See RepositoryFormat._get_revision_store()."""
364
431
from bzrlib.xml4 import serializer_v4
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):
432
return self._get_text_rev_store(repo_transport,
435
serializer=serializer_v4)
437
def _get_text_store(self, transport, control_files):
438
"""See RepositoryFormat._get_text_store()."""
378
441
class RepositoryFormat5(PreSplitOutRepositoryFormat):
387
450
_versionedfile_class = weave.WeaveFile
388
451
_matchingbzrdir = bzrdir.BzrDirFormat5()
390
def _serializer(self):
391
return xml5.serializer_v5
454
super(RepositoryFormat5, self).__init__()
393
456
def get_format_description(self):
394
457
"""See RepositoryFormat.get_format_description()."""
395
458
return "Weave repository format 5"
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)
460
def _get_revision_store(self, repo_transport, control_files):
461
"""See RepositoryFormat._get_revision_store()."""
462
"""Return the revision store object for this a_bzrdir."""
463
return self._get_text_rev_store(repo_transport,
468
def _get_text_store(self, transport, control_files):
469
"""See RepositoryFormat._get_text_store()."""
470
return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
423
473
class RepositoryFormat6(PreSplitOutRepositoryFormat):
432
482
_versionedfile_class = weave.WeaveFile
433
483
_matchingbzrdir = bzrdir.BzrDirFormat6()
435
def _serializer(self):
436
return xml5.serializer_v5
486
super(RepositoryFormat6, self).__init__()
438
488
def get_format_description(self):
439
489
"""See RepositoryFormat.get_format_description()."""
440
490
return "Weave repository format 6"
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)
492
def _get_revision_store(self, repo_transport, control_files):
493
"""See RepositoryFormat._get_revision_store()."""
494
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', transport, control_files)
468
505
class RepositoryFormat7(MetaDirRepositoryFormat):
496
531
"""See RepositoryFormat.get_format_description()."""
497
532
return "Weave repository format 7"
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)
534
def check_conversion_target(self, target_format):
537
def _get_revision_store(self, repo_transport, control_files):
538
"""See RepositoryFormat._get_revision_store()."""
539
return self._get_text_rev_store(repo_transport,
546
def _get_text_store(self, transport, control_files):
547
"""See RepositoryFormat._get_text_store()."""
548
return self._get_versioned_file_store('weaves',
520
552
def initialize(self, a_bzrdir, shared=False):
521
553
"""Create a weave repository.
528
560
weavefile.write_weave_v5(weave.Weave(), sio)
529
561
empty_weave = sio.getvalue()
531
trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
563
mutter('creating repository in %s.', a_bzrdir.transport.base)
532
564
dirs = ['revision-store', 'weaves']
533
files = [('inventory.weave', StringIO(empty_weave)),
565
files = [('inventory.weave', StringIO(empty_weave)),
535
567
utf8_files = [('format', self.get_format_string())]
537
569
self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
538
570
return self.open(a_bzrdir=a_bzrdir, _found=True)
540
572
def open(self, a_bzrdir, _found=False, _override_transport=None):
541
573
"""See RepositoryFormat.open().
543
575
:param _override_transport: INTERNAL USE ONLY. Allows opening the
544
576
repository at a slightly different url
545
577
than normal. I.e. during 'upgrade'.
548
580
format = RepositoryFormat.find_format(a_bzrdir)
581
assert format.__class__ == self.__class__
549
582
if _override_transport is not None:
550
583
repo_transport = _override_transport
552
585
repo_transport = a_bzrdir.get_repository_transport(None)
553
586
control_files = lockable_files.LockableFiles(repo_transport,
554
587
'lock', lockdir.LockDir)
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])
588
text_store = self._get_text_store(repo_transport, control_files)
589
control_store = self._get_control_store(repo_transport, control_files)
590
_revision_store = self._get_revision_store(repo_transport, control_files)
591
return WeaveMetaDirRepository(_format=self,
593
control_files=control_files,
594
_revision_store=_revision_store,
595
control_store=control_store,
596
text_store=text_store)
599
class WeaveCommitBuilder(CommitBuilder):
600
"""A builder for weave based repos that don't support ghosts."""
602
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
603
versionedfile = self.repository.weave_store.get_weave_or_empty(
604
file_id, self.repository.get_transaction())
605
result = versionedfile.add_lines(
606
self._new_revision_id, parents, new_lines,
607
nostore_sha=nostore_sha)[0:2]
608
versionedfile.clear_cache()
741
612
_legacy_formats = [RepositoryFormat4(),
742
613
RepositoryFormat5(),