/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Deprecated weave-based repository formats.
 
18
 
 
19
Weave based formats scaled linearly with history size and could not represent
 
20
ghosts.
 
21
"""
 
22
 
 
23
import os
 
24
from cStringIO import StringIO
 
25
import urllib
 
26
 
 
27
from bzrlib import (
 
28
    bzrdir,
 
29
    debug,
 
30
    errors,
 
31
    lockable_files,
 
32
    lockdir,
 
33
    osutils,
 
34
    revision as _mod_revision,
 
35
    versionedfile,
 
36
    weave,
 
37
    weavefile,
 
38
    xml5,
 
39
    )
 
40
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
41
from bzrlib.repository import (
 
42
    CommitBuilder,
 
43
    MetaDirVersionedFileRepository,
 
44
    MetaDirRepositoryFormat,
 
45
    Repository,
 
46
    RepositoryFormat,
 
47
    )
 
48
from bzrlib.store.text import TextStore
 
49
from bzrlib.trace import mutter
 
50
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
 
51
from bzrlib.versionedfile import (
 
52
    AbsentContentFactory,
 
53
    FulltextContentFactory,
 
54
    VersionedFiles,
 
55
    )
 
56
 
 
57
 
 
58
class AllInOneRepository(Repository):
 
59
    """Legacy support - the repository behaviour for all-in-one branches."""
 
60
 
 
61
    _serializer = xml5.serializer_v5
 
62
 
 
63
    def __init__(self, _format, a_bzrdir):
 
64
        # we reuse one control files instance.
 
65
        dir_mode = a_bzrdir._control_files._dir_mode
 
66
        file_mode = a_bzrdir._control_files._file_mode
 
67
 
 
68
        def get_store(name, compressed=True, prefixed=False):
 
69
            # FIXME: This approach of assuming stores are all entirely compressed
 
70
            # or entirely uncompressed is tidy, but breaks upgrade from 
 
71
            # some existing branches where there's a mixture; we probably 
 
72
            # still want the option to look for both.
 
73
            relpath = a_bzrdir._control_files._escape(name)
 
74
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
 
75
                              prefixed=prefixed, compressed=compressed,
 
76
                              dir_mode=dir_mode,
 
77
                              file_mode=file_mode)
 
78
            return store
 
79
 
 
80
        # not broken out yet because the controlweaves|inventory_store
 
81
        # and texts bits are still different.
 
82
        if isinstance(_format, RepositoryFormat4):
 
83
            # cannot remove these - there is still no consistent api 
 
84
            # which allows access to this old info.
 
85
            self.inventory_store = get_store('inventory-store')
 
86
            self._text_store = get_store('text-store')
 
87
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
 
88
 
 
89
    @needs_read_lock
 
90
    def _all_possible_ids(self):
 
91
        """Return all the possible revisions that we could find."""
 
92
        if 'evil' in debug.debug_flags:
 
93
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
94
        return [key[-1] for key in self.inventories.keys()]
 
95
 
 
96
    @needs_read_lock
 
97
    def _all_revision_ids(self):
 
98
        """Returns a list of all the revision ids in the repository. 
 
99
 
 
100
        These are in as much topological order as the underlying store can 
 
101
        present: for weaves ghosts may lead to a lack of correctness until
 
102
        the reweave updates the parents list.
 
103
        """
 
104
        return [key[-1] for key in self.revisions.keys()]
 
105
 
 
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()
 
115
 
 
116
    def _backup_inventory(self):
 
117
        t = self.bzrdir._control_files._transport
 
118
        t.copy('inventory.weave', 'inventory.backup.weave')
 
119
 
 
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,
 
125
                           timezone=None, committer=None, revprops=None,
 
126
                           revision_id=None):
 
127
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
 
128
        result = CommitBuilder(self, parents, config, timestamp, timezone,
 
129
                              committer, revprops, revision_id)
 
130
        self.start_write_group()
 
131
        return result
 
132
 
 
133
    @needs_read_lock
 
134
    def get_revisions(self, revision_ids):
 
135
        revs = self._get_revisions(revision_ids)
 
136
        return revs
 
137
 
 
138
    def _inventory_add_lines(self, revision_id, parents, lines,
 
139
        check_content=True):
 
140
        """Store lines in inv_vf and return the sha1 of the inventory."""
 
141
        present_parents = self.get_graph().get_parent_map(parents)
 
142
        final_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]
 
148
 
 
149
    @needs_read_lock
 
150
    def is_shared(self):
 
151
        """AllInOne repositories cannot be shared."""
 
152
        return False
 
153
 
 
154
    @needs_write_lock
 
155
    def set_make_working_trees(self, new_value):
 
156
        """Set the policy flag for making working trees when creating branches.
 
157
 
 
158
        This only applies to branches that use this repository.
 
159
 
 
160
        The default is 'True'.
 
161
        :param new_value: True to restore the default, False to disable making
 
162
                          working trees.
 
163
        """
 
164
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
 
165
 
 
166
    def make_working_trees(self):
 
167
        """Returns the policy for making working trees on new branches."""
 
168
        return True
 
169
 
 
170
    def revision_graph_can_have_wrong_parents(self):
 
171
        # XXX: This is an old format that we don't support full checking on, so
 
172
        # just claim that checking for this inconsistency is not required.
 
173
        return False
 
174
 
 
175
 
 
176
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
 
177
    """A subclass of MetaDirRepository to set weave specific policy."""
 
178
 
 
179
    _serializer = xml5.serializer_v5
 
180
 
 
181
    @needs_read_lock
 
182
    def _all_possible_ids(self):
 
183
        """Return all the possible revisions that we could find."""
 
184
        if 'evil' in debug.debug_flags:
 
185
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
186
        return [key[-1] for key in self.inventories.keys()]
 
187
 
 
188
    @needs_read_lock
 
189
    def _all_revision_ids(self):
 
190
        """Returns a list of all the revision ids in the repository. 
 
191
 
 
192
        These are in as much topological order as the underlying store can 
 
193
        present: for weaves ghosts may lead to a lack of correctness until
 
194
        the reweave updates the parents list.
 
195
        """
 
196
        return [key[-1] for key in self.revisions.keys()]
 
197
 
 
198
    def _activate_new_inventory(self):
 
199
        """Put a replacement inventory.new into use as inventories."""
 
200
        # Copy the content across
 
201
        t = self._transport
 
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()
 
207
 
 
208
    def _backup_inventory(self):
 
209
        t = self._transport
 
210
        t.copy('inventory.weave', 'inventory.backup.weave')
 
211
 
 
212
    def _temp_inventories(self):
 
213
        t = self._transport
 
214
        return self._format._get_inventories(t, self, 'inventory.new')
 
215
 
 
216
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
217
                           timezone=None, committer=None, revprops=None,
 
218
                           revision_id=None):
 
219
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
 
220
        result = CommitBuilder(self, parents, config, timestamp, timezone,
 
221
                              committer, revprops, revision_id)
 
222
        self.start_write_group()
 
223
        return result
 
224
 
 
225
    @needs_read_lock
 
226
    def get_revision(self, revision_id):
 
227
        """Return the Revision object for a named revision"""
 
228
        r = self.get_revision_reconcile(revision_id)
 
229
        return r
 
230
 
 
231
    def _inventory_add_lines(self, revision_id, parents, lines,
 
232
        check_content=True):
 
233
        """Store lines in inv_vf and return the sha1 of the inventory."""
 
234
        present_parents = self.get_graph().get_parent_map(parents)
 
235
        final_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]
 
241
 
 
242
    def revision_graph_can_have_wrong_parents(self):
 
243
        return False
 
244
 
 
245
 
 
246
class PreSplitOutRepositoryFormat(RepositoryFormat):
 
247
    """Base class for the pre split out repository formats."""
 
248
 
 
249
    rich_root_data = False
 
250
    supports_tree_reference = False
 
251
    supports_ghosts = False
 
252
    supports_external_lookups = False
 
253
 
 
254
    def initialize(self, a_bzrdir, shared=False, _internal=False):
 
255
        """Create a weave repository."""
 
256
        if shared:
 
257
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
258
 
 
259
        if not _internal:
 
260
            # always initialized when the bzrdir is.
 
261
            return self.open(a_bzrdir, _found=True)
 
262
        
 
263
        # Create an empty weave
 
264
        sio = StringIO()
 
265
        weavefile.write_weave_v5(weave.Weave(), sio)
 
266
        empty_weave = sio.getvalue()
 
267
 
 
268
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
269
        dirs = ['revision-store', 'weaves']
 
270
        files = [('inventory.weave', StringIO(empty_weave)),
 
271
                 ]
 
272
        
 
273
        # FIXME: RBC 20060125 don't peek under the covers
 
274
        # NB: no need to escape relative paths that are url safe.
 
275
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
 
276
                                'branch-lock', lockable_files.TransportLock)
 
277
        control_files.create_lock()
 
278
        control_files.lock_write()
 
279
        control_files._transport.mkdir_multi(dirs,
 
280
                mode=control_files._dir_mode)
 
281
        try:
 
282
            for file, content in files:
 
283
                control_files.put(file, content)
 
284
        finally:
 
285
            control_files.unlock()
 
286
        return self.open(a_bzrdir, _found=True)
 
287
 
 
288
    def open(self, a_bzrdir, _found=False):
 
289
        """See RepositoryFormat.open()."""
 
290
        if not _found:
 
291
            # we are being called directly and must probe.
 
292
            raise NotImplementedError
 
293
 
 
294
        repo_transport = a_bzrdir.get_repository_transport(None)
 
295
        control_files = a_bzrdir._control_files
 
296
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
 
297
        result.revisions = self._get_revisions(repo_transport, result)
 
298
        result.signatures = self._get_signatures(repo_transport, result)
 
299
        result.inventories = self._get_inventories(repo_transport, result)
 
300
        result.texts = self._get_texts(repo_transport, result)
 
301
        return result
 
302
 
 
303
    def check_conversion_target(self, target_format):
 
304
        pass
 
305
 
 
306
 
 
307
class RepositoryFormat4(PreSplitOutRepositoryFormat):
 
308
    """Bzr repository format 4.
 
309
 
 
310
    This repository format has:
 
311
     - flat stores
 
312
     - TextStores for texts, inventories,revisions.
 
313
 
 
314
    This format is deprecated: it indexes texts using a text id which is
 
315
    removed in format 5; initialization and write support for this format
 
316
    has been removed.
 
317
    """
 
318
 
 
319
    _matchingbzrdir = bzrdir.BzrDirFormat4()
 
320
 
 
321
    def __init__(self):
 
322
        super(RepositoryFormat4, self).__init__()
 
323
 
 
324
    def get_format_description(self):
 
325
        """See RepositoryFormat.get_format_description()."""
 
326
        return "Repository format 4"
 
327
 
 
328
    def initialize(self, url, shared=False, _internal=False):
 
329
        """Format 4 branches cannot be created."""
 
330
        raise errors.UninitializableFormat(self)
 
331
 
 
332
    def is_supported(self):
 
333
        """Format 4 is not supported.
 
334
 
 
335
        It is not supported because the model changed from 4 to 5 and the
 
336
        conversion logic is expensive - so doing it on the fly was not 
 
337
        feasible.
 
338
        """
 
339
        return False
 
340
 
 
341
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
342
        # No inventories store written so far.
 
343
        return None
 
344
 
 
345
    def _get_revisions(self, repo_transport, repo):
 
346
        from bzrlib.xml4 import serializer_v4
 
347
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
348
            serializer_v4, True, versionedfile.PrefixMapper(),
 
349
            repo.is_locked, repo.is_write_locked)
 
350
 
 
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)
 
355
 
 
356
    def _get_texts(self, repo_transport, repo):
 
357
        return None
 
358
 
 
359
 
 
360
class RepositoryFormat5(PreSplitOutRepositoryFormat):
 
361
    """Bzr control format 5.
 
362
 
 
363
    This repository format has:
 
364
     - weaves for file texts and inventory
 
365
     - flat stores
 
366
     - TextStores for revisions and signatures.
 
367
    """
 
368
 
 
369
    _versionedfile_class = weave.WeaveFile
 
370
    _matchingbzrdir = bzrdir.BzrDirFormat5()
 
371
 
 
372
    def __init__(self):
 
373
        super(RepositoryFormat5, self).__init__()
 
374
 
 
375
    def get_format_description(self):
 
376
        """See RepositoryFormat.get_format_description()."""
 
377
        return "Weave repository format 5"
 
378
 
 
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)
 
383
 
 
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)
 
389
 
 
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)
 
394
 
 
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)
 
400
 
 
401
 
 
402
class RepositoryFormat6(PreSplitOutRepositoryFormat):
 
403
    """Bzr control format 6.
 
404
 
 
405
    This repository format has:
 
406
     - weaves for file texts and inventory
 
407
     - hash subdirectory based stores.
 
408
     - TextStores for revisions and signatures.
 
409
    """
 
410
 
 
411
    _versionedfile_class = weave.WeaveFile
 
412
    _matchingbzrdir = bzrdir.BzrDirFormat6()
 
413
 
 
414
    def __init__(self):
 
415
        super(RepositoryFormat6, self).__init__()
 
416
 
 
417
    def get_format_description(self):
 
418
        """See RepositoryFormat.get_format_description()."""
 
419
        return "Weave repository format 6"
 
420
 
 
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)
 
425
 
 
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)
 
431
 
 
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)
 
436
 
 
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)
 
442
 
 
443
 
 
444
class RepositoryFormat7(MetaDirRepositoryFormat):
 
445
    """Bzr repository 7.
 
446
 
 
447
    This repository format has:
 
448
     - weaves for file texts and inventory
 
449
     - hash subdirectory based stores.
 
450
     - TextStores for revisions and signatures.
 
451
     - a format marker of its own
 
452
     - an optional 'shared-storage' flag
 
453
     - an optional 'no-working-trees' flag
 
454
    """
 
455
 
 
456
    _versionedfile_class = weave.WeaveFile
 
457
    supports_ghosts = False
 
458
 
 
459
    def get_format_string(self):
 
460
        """See RepositoryFormat.get_format_string()."""
 
461
        return "Bazaar-NG Repository format 7"
 
462
 
 
463
    def get_format_description(self):
 
464
        """See RepositoryFormat.get_format_description()."""
 
465
        return "Weave repository format 7"
 
466
 
 
467
    def check_conversion_target(self, target_format):
 
468
        pass
 
469
 
 
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)
 
474
 
 
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)
 
480
 
 
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)
 
485
 
 
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)
 
491
 
 
492
    def initialize(self, a_bzrdir, shared=False):
 
493
        """Create a weave repository.
 
494
 
 
495
        :param shared: If true the repository will be initialized as a shared
 
496
                       repository.
 
497
        """
 
498
        # Create an empty weave
 
499
        sio = StringIO()
 
500
        weavefile.write_weave_v5(weave.Weave(), sio)
 
501
        empty_weave = sio.getvalue()
 
502
 
 
503
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
504
        dirs = ['revision-store', 'weaves']
 
505
        files = [('inventory.weave', StringIO(empty_weave)), 
 
506
                 ]
 
507
        utf8_files = [('format', self.get_format_string())]
 
508
 
 
509
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
510
        return self.open(a_bzrdir=a_bzrdir, _found=True)
 
511
 
 
512
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
513
        """See RepositoryFormat.open().
 
514
        
 
515
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
516
                                    repository at a slightly different url
 
517
                                    than normal. I.e. during 'upgrade'.
 
518
        """
 
519
        if not _found:
 
520
            format = RepositoryFormat.find_format(a_bzrdir)
 
521
        if _override_transport is not None:
 
522
            repo_transport = _override_transport
 
523
        else:
 
524
            repo_transport = a_bzrdir.get_repository_transport(None)
 
525
        control_files = lockable_files.LockableFiles(repo_transport,
 
526
                                'lock', lockdir.LockDir)
 
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
 
534
        return result
 
535
 
 
536
 
 
537
class TextVersionedFiles(VersionedFiles):
 
538
    """Just-a-bunch-of-files based VersionedFile stores."""
 
539
 
 
540
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
541
        self._compressed = compressed
 
542
        self._transport = transport
 
543
        self._mapper = mapper
 
544
        if self._compressed:
 
545
            self._ext = '.gz'
 
546
        else:
 
547
            self._ext = ''
 
548
        self._is_locked = is_locked
 
549
        self._can_write = can_write
 
550
 
 
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)
 
557
        if '/' in key[-1]:
 
558
            raise ValueError('bad idea to put / in %r' % key)
 
559
        text = ''.join(lines)
 
560
        if self._compressed:
 
561
            text = bytes_to_gzip(text)
 
562
        path = self._map(key)
 
563
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
 
564
 
 
565
    def insert_record_stream(self, stream):
 
566
        adapters = {}
 
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')))
 
575
            else:
 
576
                adapter_key = record.storage_kind, 'fulltext'
 
577
                try:
 
578
                    adapter = adapters[adapter_key]
 
579
                except KeyError:
 
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)))
 
585
                try:
 
586
                    self.add_lines(record.key, None, lines)
 
587
                except RevisionAlreadyPresent:
 
588
                    pass
 
589
 
 
590
    def _load_text(self, key):
 
591
        if not self._is_locked():
 
592
            raise errors.ObjectNotLocked(self)
 
593
        path = self._map(key)
 
594
        try:
 
595
            text = self._transport.get_bytes(path)
 
596
            compressed = self._compressed
 
597
        except errors.NoSuchFile:
 
598
            if self._compressed:
 
599
                # try without the .gz
 
600
                path = path[:-3]
 
601
                try:
 
602
                    text = self._transport.get_bytes(path)
 
603
                    compressed = False
 
604
                except errors.NoSuchFile:
 
605
                    return None
 
606
            else:
 
607
                return None
 
608
        if compressed:
 
609
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
 
610
        return text
 
611
 
 
612
    def _map(self, key):
 
613
        return self._mapper.map(key) + self._ext
 
614
 
 
615
 
 
616
class RevisionTextStore(TextVersionedFiles):
 
617
    """Legacy thunk for format 4 repositories."""
 
618
 
 
619
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
 
620
        can_write):
 
621
        """Create a RevisionTextStore at transport with serializer."""
 
622
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
623
            is_locked, can_write)
 
624
        self._serializer = serializer
 
625
 
 
626
    def _load_text_parents(self, key):
 
627
        text = self._load_text(key)
 
628
        if text is None:
 
629
            return None, None
 
630
        parents = self._serializer.read_revision_from_string(text).parent_ids
 
631
        return text, tuple((parent,) for parent in parents)
 
632
 
 
633
    def get_parent_map(self, keys):
 
634
        result = {}
 
635
        for key in keys:
 
636
            parents = self._load_text_parents(key)[1]
 
637
            if parents is None:
 
638
                continue
 
639
            result[key] = parents
 
640
        return result
 
641
    
 
642
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
643
        for key in keys:
 
644
            text, parents = self._load_text_parents(key)
 
645
            if text is None:
 
646
                yield AbsentContentFactory(key)
 
647
            else:
 
648
                yield FulltextContentFactory(key, parents, None, text)
 
649
 
 
650
    def keys(self):
 
651
        if not self._is_locked():
 
652
            raise errors.ObjectNotLocked(self)
 
653
        relpaths = set()
 
654
        for quoted_relpath in self._transport.iter_files_recursive():
 
655
            relpath = urllib.unquote(quoted_relpath)
 
656
            path, ext = os.path.splitext(relpath)
 
657
            if ext == '.gz':
 
658
                relpath = path
 
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])
 
663
 
 
664
 
 
665
class SignatureTextStore(TextVersionedFiles):
 
666
    """Legacy thunk for format 4-7 repositories."""
 
667
 
 
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
 
672
 
 
673
    def get_parent_map(self, keys):
 
674
        result = {}
 
675
        for key in keys:
 
676
            text = self._load_text(key)
 
677
            if text is None:
 
678
                continue
 
679
            result[key] = None
 
680
        return result
 
681
    
 
682
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
683
        for key in keys:
 
684
            text = self._load_text(key)
 
685
            if text is None:
 
686
                yield AbsentContentFactory(key)
 
687
            else:
 
688
                yield FulltextContentFactory(key, None, None, text)
 
689
 
 
690
    def keys(self):
 
691
        if not self._is_locked():
 
692
            raise errors.ObjectNotLocked(self)
 
693
        relpaths = set()
 
694
        for quoted_relpath in self._transport.iter_files_recursive():
 
695
            relpath = urllib.unquote(quoted_relpath)
 
696
            path, ext = os.path.splitext(relpath)
 
697
            if ext == '.gz':
 
698
                relpath = path
 
699
            if not relpath.endswith('.sig'):
 
700
                continue
 
701
            relpaths.add(relpath[:-4])
 
702
        paths = list(relpaths)
 
703
        return set([self._mapper.unmap(path) for path in paths])
 
704
 
 
705
_legacy_formats = [RepositoryFormat4(),
 
706
                   RepositoryFormat5(),
 
707
                   RepositoryFormat6()]