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