~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Robert Collins
  • Date: 2005-08-23 06:52:09 UTC
  • mto: (974.1.50) (1185.1.10) (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1139.
  • Revision ID: robertc@robertcollins.net-20050823065209-81cd5962c401751b
move io redirection into each test case from the global runner

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.lazy_import import lazy_import
28
 
lazy_import(globals(), """
29
 
from bzrlib import (
30
 
    xml5,
31
 
    )
32
 
""")
33
 
from bzrlib import (
34
 
    bzrdir,
35
 
    debug,
36
 
    errors,
37
 
    lockable_files,
38
 
    lockdir,
39
 
    osutils,
40
 
    revision as _mod_revision,
41
 
    urlutils,
42
 
    versionedfile,
43
 
    weave,
44
 
    weavefile,
45
 
    )
46
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
47
 
from bzrlib.repository import (
48
 
    CommitBuilder,
49
 
    MetaDirVersionedFileRepository,
50
 
    MetaDirRepositoryFormat,
51
 
    Repository,
52
 
    RepositoryFormat,
53
 
    )
54
 
from bzrlib.store.text import TextStore
55
 
from bzrlib.trace import mutter
56
 
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
57
 
from bzrlib.versionedfile import (
58
 
    AbsentContentFactory,
59
 
    FulltextContentFactory,
60
 
    VersionedFiles,
61
 
    )
62
 
 
63
 
 
64
 
class AllInOneRepository(Repository):
65
 
    """Legacy support - the repository behaviour for all-in-one branches."""
66
 
 
67
 
    @property
68
 
    def _serializer(self):
69
 
        return xml5.serializer_v5
70
 
 
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 == '':
75
 
            return u''
76
 
        return urlutils.escape(osutils.safe_unicode(file_or_path))
77
 
 
78
 
    def __init__(self, _format, a_bzrdir):
79
 
        # we reuse one control files instance.
80
 
        dir_mode = a_bzrdir._get_dir_mode()
81
 
        file_mode = a_bzrdir._get_file_mode()
82
 
 
83
 
        def get_store(name, compressed=True, prefixed=False):
84
 
            # 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
87
 
            # still want the option to look for both.
88
 
            relpath = self._escape(name)
89
 
            store = TextStore(a_bzrdir.transport.clone(relpath),
90
 
                              prefixed=prefixed, compressed=compressed,
91
 
                              dir_mode=dir_mode,
92
 
                              file_mode=file_mode)
93
 
            return store
94
 
 
95
 
        # not broken out yet because the controlweaves|inventory_store
96
 
        # and texts bits are still different.
97
 
        if isinstance(_format, RepositoryFormat4):
98
 
            # cannot remove these - there is still no consistent api
99
 
            # which allows access to this old info.
100
 
            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)
103
 
 
104
 
    @needs_read_lock
105
 
    def _all_possible_ids(self):
106
 
        """Return all the possible revisions that we could find."""
107
 
        if 'evil' in debug.debug_flags:
108
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
109
 
        return [key[-1] for key in self.inventories.keys()]
110
 
 
111
 
    @needs_read_lock
112
 
    def _all_revision_ids(self):
113
 
        """Returns a list of all the revision ids in the repository.
114
 
 
115
 
        These are in as much topological order as the underlying store can
116
 
        present: for weaves ghosts may lead to a lack of correctness until
117
 
        the reweave updates the parents list.
118
 
        """
119
 
        return [key[-1] for key in self.revisions.keys()]
120
 
 
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()
130
 
 
131
 
    def _backup_inventory(self):
132
 
        t = self.bzrdir._control_files._transport
133
 
        t.copy('inventory.weave', 'inventory.backup.weave')
134
 
 
135
 
    def _temp_inventories(self):
136
 
        t = self.bzrdir._control_files._transport
137
 
        return self._format._get_inventories(t, self, 'inventory.new')
138
 
 
139
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
140
 
                           timezone=None, committer=None, revprops=None,
141
 
                           revision_id=None):
142
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
143
 
        result = CommitBuilder(self, parents, config, timestamp, timezone,
144
 
                              committer, revprops, revision_id)
145
 
        self.start_write_group()
146
 
        return result
147
 
 
148
 
    @needs_read_lock
149
 
    def get_revisions(self, revision_ids):
150
 
        revs = self._get_revisions(revision_ids)
151
 
        return revs
152
 
 
153
 
    def _inventory_add_lines(self, revision_id, parents, lines,
154
 
        check_content=True):
155
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
156
 
        present_parents = self.get_graph().get_parent_map(parents)
157
 
        final_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]
163
 
 
164
 
    def is_shared(self):
165
 
        """AllInOne repositories cannot be shared."""
166
 
        return False
167
 
 
168
 
    @needs_write_lock
169
 
    def set_make_working_trees(self, new_value):
170
 
        """Set the policy flag for making working trees when creating branches.
171
 
 
172
 
        This only applies to branches that use this repository.
173
 
 
174
 
        The default is 'True'.
175
 
        :param new_value: True to restore the default, False to disable making
176
 
                          working trees.
177
 
        """
178
 
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
179
 
 
180
 
    def make_working_trees(self):
181
 
        """Returns the policy for making working trees on new branches."""
182
 
        return True
183
 
 
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.
187
 
        return False
188
 
 
189
 
 
190
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
191
 
    """A subclass of MetaDirRepository to set weave specific policy."""
192
 
 
193
 
    def __init__(self, _format, a_bzrdir, control_files):
194
 
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
195
 
        self._serializer = _format._serializer
196
 
 
197
 
    @needs_read_lock
198
 
    def _all_possible_ids(self):
199
 
        """Return all the possible revisions that we could find."""
200
 
        if 'evil' in debug.debug_flags:
201
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
202
 
        return [key[-1] for key in self.inventories.keys()]
203
 
 
204
 
    @needs_read_lock
205
 
    def _all_revision_ids(self):
206
 
        """Returns a list of all the revision ids in the repository.
207
 
 
208
 
        These are in as much topological order as the underlying store can
209
 
        present: for weaves ghosts may lead to a lack of correctness until
210
 
        the reweave updates the parents list.
211
 
        """
212
 
        return [key[-1] for key in self.revisions.keys()]
213
 
 
214
 
    def _activate_new_inventory(self):
215
 
        """Put a replacement inventory.new into use as inventories."""
216
 
        # Copy the content across
217
 
        t = self._transport
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()
223
 
 
224
 
    def _backup_inventory(self):
225
 
        t = self._transport
226
 
        t.copy('inventory.weave', 'inventory.backup.weave')
227
 
 
228
 
    def _temp_inventories(self):
229
 
        t = self._transport
230
 
        return self._format._get_inventories(t, self, 'inventory.new')
231
 
 
232
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
233
 
                           timezone=None, committer=None, revprops=None,
234
 
                           revision_id=None):
235
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
236
 
        result = CommitBuilder(self, parents, config, timestamp, timezone,
237
 
                              committer, revprops, revision_id)
238
 
        self.start_write_group()
239
 
        return result
240
 
 
241
 
    @needs_read_lock
242
 
    def get_revision(self, revision_id):
243
 
        """Return the Revision object for a named revision"""
244
 
        r = self.get_revision_reconcile(revision_id)
245
 
        return r
246
 
 
247
 
    def _inventory_add_lines(self, revision_id, parents, lines,
248
 
        check_content=True):
249
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
250
 
        present_parents = self.get_graph().get_parent_map(parents)
251
 
        final_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]
257
 
 
258
 
    def revision_graph_can_have_wrong_parents(self):
259
 
        return False
260
 
 
261
 
 
262
 
class PreSplitOutRepositoryFormat(RepositoryFormat):
263
 
    """Base class for the pre split out repository formats."""
264
 
 
265
 
    rich_root_data = False
266
 
    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
272
 
    fast_deltas = False
273
 
 
274
 
    def initialize(self, a_bzrdir, shared=False, _internal=False):
275
 
        """Create a weave repository."""
276
 
        if shared:
277
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
278
 
 
279
 
        if not _internal:
280
 
            # always initialized when the bzrdir is.
281
 
            return self.open(a_bzrdir, _found=True)
282
 
 
283
 
        # Create an empty weave
284
 
        sio = StringIO()
285
 
        weavefile.write_weave_v5(weave.Weave(), sio)
286
 
        empty_weave = sio.getvalue()
287
 
 
288
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
289
 
 
290
 
        # FIXME: RBC 20060125 don't peek under the covers
291
 
        # NB: no need to escape relative paths that are url safe.
292
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
293
 
            'branch-lock', lockable_files.TransportLock)
294
 
        control_files.create_lock()
295
 
        control_files.lock_write()
296
 
        transport = a_bzrdir.transport
297
 
        try:
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())
302
 
        finally:
303
 
            control_files.unlock()
304
 
        return self.open(a_bzrdir, _found=True)
305
 
 
306
 
    def open(self, a_bzrdir, _found=False):
307
 
        """See RepositoryFormat.open()."""
308
 
        if not _found:
309
 
            # we are being called directly and must probe.
310
 
            raise NotImplementedError
311
 
 
312
 
        repo_transport = a_bzrdir.get_repository_transport(None)
313
 
        control_files = a_bzrdir._control_files
314
 
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
315
 
        result.revisions = self._get_revisions(repo_transport, result)
316
 
        result.signatures = self._get_signatures(repo_transport, result)
317
 
        result.inventories = self._get_inventories(repo_transport, result)
318
 
        result.texts = self._get_texts(repo_transport, result)
319
 
        result.chk_bytes = None
320
 
        return result
321
 
 
322
 
    def check_conversion_target(self, target_format):
323
 
        pass
324
 
 
325
 
 
326
 
class RepositoryFormat4(PreSplitOutRepositoryFormat):
327
 
    """Bzr repository format 4.
328
 
 
329
 
    This repository format has:
330
 
     - flat stores
331
 
     - TextStores for texts, inventories,revisions.
332
 
 
333
 
    This format is deprecated: it indexes texts using a text id which is
334
 
    removed in format 5; initialization and write support for this format
335
 
    has been removed.
336
 
    """
337
 
 
338
 
    _matchingbzrdir = bzrdir.BzrDirFormat4()
339
 
 
340
 
    def get_format_description(self):
341
 
        """See RepositoryFormat.get_format_description()."""
342
 
        return "Repository format 4"
343
 
 
344
 
    def initialize(self, url, shared=False, _internal=False):
345
 
        """Format 4 branches cannot be created."""
346
 
        raise errors.UninitializableFormat(self)
347
 
 
348
 
    def is_supported(self):
349
 
        """Format 4 is not supported.
350
 
 
351
 
        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
353
 
        feasible.
354
 
        """
355
 
        return False
356
 
 
357
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
358
 
        # No inventories store written so far.
359
 
        return None
360
 
 
361
 
    def _get_revisions(self, repo_transport, repo):
362
 
        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)
366
 
 
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)
371
 
 
372
 
    def _get_texts(self, repo_transport, repo):
373
 
        return None
374
 
 
375
 
 
376
 
class RepositoryFormat5(PreSplitOutRepositoryFormat):
377
 
    """Bzr control format 5.
378
 
 
379
 
    This repository format has:
380
 
     - weaves for file texts and inventory
381
 
     - flat stores
382
 
     - TextStores for revisions and signatures.
383
 
    """
384
 
 
385
 
    _versionedfile_class = weave.WeaveFile
386
 
    _matchingbzrdir = bzrdir.BzrDirFormat5()
387
 
    @property
388
 
    def _serializer(self):
389
 
        return xml5.serializer_v5
390
 
 
391
 
    def get_format_description(self):
392
 
        """See RepositoryFormat.get_format_description()."""
393
 
        return "Weave repository format 5"
394
 
 
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()
398
 
 
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)
403
 
 
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)
408
 
 
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)
413
 
 
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)
419
 
 
420
 
 
421
 
class RepositoryFormat6(PreSplitOutRepositoryFormat):
422
 
    """Bzr control format 6.
423
 
 
424
 
    This repository format has:
425
 
     - weaves for file texts and inventory
426
 
     - hash subdirectory based stores.
427
 
     - TextStores for revisions and signatures.
428
 
    """
429
 
 
430
 
    _versionedfile_class = weave.WeaveFile
431
 
    _matchingbzrdir = bzrdir.BzrDirFormat6()
432
 
    @property
433
 
    def _serializer(self):
434
 
        return xml5.serializer_v5
435
 
 
436
 
    def get_format_description(self):
437
 
        """See RepositoryFormat.get_format_description()."""
438
 
        return "Weave repository format 6"
439
 
 
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()
443
 
 
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)
448
 
 
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)
453
 
 
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)
458
 
 
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)
464
 
 
465
 
 
466
 
class RepositoryFormat7(MetaDirRepositoryFormat):
467
 
    """Bzr repository 7.
468
 
 
469
 
    This repository format has:
470
 
     - weaves for file texts and inventory
471
 
     - hash subdirectory based stores.
472
 
     - TextStores for revisions and signatures.
473
 
     - a format marker of its own
474
 
     - an optional 'shared-storage' flag
475
 
     - an optional 'no-working-trees' flag
476
 
    """
477
 
 
478
 
    _versionedfile_class = weave.WeaveFile
479
 
    supports_ghosts = False
480
 
    supports_chks = False
481
 
 
482
 
    _fetch_order = 'topological'
483
 
    _fetch_reconcile = True
484
 
    fast_deltas = False
485
 
    @property
486
 
    def _serializer(self):
487
 
        return xml5.serializer_v5
488
 
 
489
 
    def get_format_string(self):
490
 
        """See RepositoryFormat.get_format_string()."""
491
 
        return "Bazaar-NG Repository format 7"
492
 
 
493
 
    def get_format_description(self):
494
 
        """See RepositoryFormat.get_format_description()."""
495
 
        return "Weave repository format 7"
496
 
 
497
 
    def check_conversion_target(self, target_format):
498
 
        pass
499
 
 
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)
504
 
 
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)
509
 
 
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)
514
 
 
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)
520
 
 
521
 
    def initialize(self, a_bzrdir, shared=False):
522
 
        """Create a weave repository.
523
 
 
524
 
        :param shared: If true the repository will be initialized as a shared
525
 
                       repository.
526
 
        """
527
 
        # Create an empty weave
528
 
        sio = StringIO()
529
 
        weavefile.write_weave_v5(weave.Weave(), sio)
530
 
        empty_weave = sio.getvalue()
531
 
 
532
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
533
 
        dirs = ['revision-store', 'weaves']
534
 
        files = [('inventory.weave', StringIO(empty_weave)),
535
 
                 ]
536
 
        utf8_files = [('format', self.get_format_string())]
537
 
 
538
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
539
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
540
 
 
541
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
542
 
        """See RepositoryFormat.open().
543
 
 
544
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
545
 
                                    repository at a slightly different url
546
 
                                    than normal. I.e. during 'upgrade'.
547
 
        """
548
 
        if not _found:
549
 
            format = RepositoryFormat.find_format(a_bzrdir)
550
 
        if _override_transport is not None:
551
 
            repo_transport = _override_transport
552
 
        else:
553
 
            repo_transport = a_bzrdir.get_repository_transport(None)
554
 
        control_files = lockable_files.LockableFiles(repo_transport,
555
 
                                '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
564
 
        return result
565
 
 
566
 
 
567
 
class TextVersionedFiles(VersionedFiles):
568
 
    """Just-a-bunch-of-files based VersionedFile stores."""
569
 
 
570
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
571
 
        self._compressed = compressed
572
 
        self._transport = transport
573
 
        self._mapper = mapper
574
 
        if self._compressed:
575
 
            self._ext = '.gz'
576
 
        else:
577
 
            self._ext = ''
578
 
        self._is_locked = is_locked
579
 
        self._can_write = can_write
580
 
 
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)
587
 
        if '/' in key[-1]:
588
 
            raise ValueError('bad idea to put / in %r' % (key,))
589
 
        text = ''.join(lines)
590
 
        if self._compressed:
591
 
            text = bytes_to_gzip(text)
592
 
        path = self._map(key)
593
 
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
594
 
 
595
 
    def insert_record_stream(self, stream):
596
 
        adapters = {}
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')))
605
 
            else:
606
 
                adapter_key = record.storage_kind, 'fulltext'
607
 
                try:
608
 
                    adapter = adapters[adapter_key]
609
 
                except KeyError:
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)))
615
 
                try:
616
 
                    self.add_lines(record.key, None, lines)
617
 
                except RevisionAlreadyPresent:
618
 
                    pass
619
 
 
620
 
    def _load_text(self, key):
621
 
        if not self._is_locked():
622
 
            raise errors.ObjectNotLocked(self)
623
 
        path = self._map(key)
624
 
        try:
625
 
            text = self._transport.get_bytes(path)
626
 
            compressed = self._compressed
627
 
        except errors.NoSuchFile:
628
 
            if self._compressed:
629
 
                # try without the .gz
630
 
                path = path[:-3]
631
 
                try:
632
 
                    text = self._transport.get_bytes(path)
633
 
                    compressed = False
634
 
                except errors.NoSuchFile:
635
 
                    return None
636
 
            else:
637
 
                return None
638
 
        if compressed:
639
 
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
640
 
        return text
641
 
 
642
 
    def _map(self, key):
643
 
        return self._mapper.map(key) + self._ext
644
 
 
645
 
 
646
 
class RevisionTextStore(TextVersionedFiles):
647
 
    """Legacy thunk for format 4 repositories."""
648
 
 
649
 
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
650
 
        can_write):
651
 
        """Create a RevisionTextStore at transport with serializer."""
652
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
653
 
            is_locked, can_write)
654
 
        self._serializer = serializer
655
 
 
656
 
    def _load_text_parents(self, key):
657
 
        text = self._load_text(key)
658
 
        if text is None:
659
 
            return None, None
660
 
        parents = self._serializer.read_revision_from_string(text).parent_ids
661
 
        return text, tuple((parent,) for parent in parents)
662
 
 
663
 
    def get_parent_map(self, keys):
664
 
        result = {}
665
 
        for key in keys:
666
 
            parents = self._load_text_parents(key)[1]
667
 
            if parents is None:
668
 
                continue
669
 
            result[key] = parents
670
 
        return result
671
 
 
672
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
673
 
        for key in keys:
674
 
            text, parents = self._load_text_parents(key)
675
 
            if text is None:
676
 
                yield AbsentContentFactory(key)
677
 
            else:
678
 
                yield FulltextContentFactory(key, parents, None, text)
679
 
 
680
 
    def keys(self):
681
 
        if not self._is_locked():
682
 
            raise errors.ObjectNotLocked(self)
683
 
        relpaths = set()
684
 
        for quoted_relpath in self._transport.iter_files_recursive():
685
 
            relpath = urllib.unquote(quoted_relpath)
686
 
            path, ext = os.path.splitext(relpath)
687
 
            if ext == '.gz':
688
 
                relpath = path
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])
693
 
 
694
 
 
695
 
class SignatureTextStore(TextVersionedFiles):
696
 
    """Legacy thunk for format 4-7 repositories."""
697
 
 
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
702
 
 
703
 
    def get_parent_map(self, keys):
704
 
        result = {}
705
 
        for key in keys:
706
 
            text = self._load_text(key)
707
 
            if text is None:
708
 
                continue
709
 
            result[key] = None
710
 
        return result
711
 
 
712
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
713
 
        for key in keys:
714
 
            text = self._load_text(key)
715
 
            if text is None:
716
 
                yield AbsentContentFactory(key)
717
 
            else:
718
 
                yield FulltextContentFactory(key, None, None, text)
719
 
 
720
 
    def keys(self):
721
 
        if not self._is_locked():
722
 
            raise errors.ObjectNotLocked(self)
723
 
        relpaths = set()
724
 
        for quoted_relpath in self._transport.iter_files_recursive():
725
 
            relpath = urllib.unquote(quoted_relpath)
726
 
            path, ext = os.path.splitext(relpath)
727
 
            if ext == '.gz':
728
 
                relpath = path
729
 
            if not relpath.endswith('.sig'):
730
 
                continue
731
 
            relpaths.add(relpath[:-4])
732
 
        paths = list(relpaths)
733
 
        return set([self._mapper.unmap(path) for path in paths])
734
 
 
735
 
_legacy_formats = [RepositoryFormat4(),
736
 
                   RepositoryFormat5(),
737
 
                   RepositoryFormat6()]