~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Robert Collins
  • Date: 2005-10-03 01:15:02 UTC
  • mfrom: (1092.2.28)
  • Revision ID: robertc@robertcollins.net-20051003011502-f579a509a136b774
mergeĀ fromĀ baz2bzr

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., 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.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
 
        self._fetch_order = 'topological'
104
 
        self._fetch_reconcile = True
105
 
 
106
 
    @needs_read_lock
107
 
    def _all_possible_ids(self):
108
 
        """Return all the possible revisions that we could find."""
109
 
        if 'evil' in debug.debug_flags:
110
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
111
 
        return [key[-1] for key in self.inventories.keys()]
112
 
 
113
 
    @needs_read_lock
114
 
    def _all_revision_ids(self):
115
 
        """Returns a list of all the revision ids in the repository. 
116
 
 
117
 
        These are in as much topological order as the underlying store can 
118
 
        present: for weaves ghosts may lead to a lack of correctness until
119
 
        the reweave updates the parents list.
120
 
        """
121
 
        return [key[-1] for key in self.revisions.keys()]
122
 
 
123
 
    def _activate_new_inventory(self):
124
 
        """Put a replacement inventory.new into use as inventories."""
125
 
        # Copy the content across
126
 
        t = self.bzrdir._control_files._transport
127
 
        t.copy('inventory.new.weave', 'inventory.weave')
128
 
        # delete the temp inventory
129
 
        t.delete('inventory.new.weave')
130
 
        # Check we can parse the new weave properly as a sanity check
131
 
        self.inventories.keys()
132
 
 
133
 
    def _backup_inventory(self):
134
 
        t = self.bzrdir._control_files._transport
135
 
        t.copy('inventory.weave', 'inventory.backup.weave')
136
 
 
137
 
    def _temp_inventories(self):
138
 
        t = self.bzrdir._control_files._transport
139
 
        return self._format._get_inventories(t, self, 'inventory.new')
140
 
 
141
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
142
 
                           timezone=None, committer=None, revprops=None,
143
 
                           revision_id=None):
144
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
145
 
        result = CommitBuilder(self, parents, config, timestamp, timezone,
146
 
                              committer, revprops, revision_id)
147
 
        self.start_write_group()
148
 
        return result
149
 
 
150
 
    @needs_read_lock
151
 
    def get_revisions(self, revision_ids):
152
 
        revs = self._get_revisions(revision_ids)
153
 
        return revs
154
 
 
155
 
    def _inventory_add_lines(self, revision_id, parents, lines,
156
 
        check_content=True):
157
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
158
 
        present_parents = self.get_graph().get_parent_map(parents)
159
 
        final_parents = []
160
 
        for parent in parents:
161
 
            if parent in present_parents:
162
 
                final_parents.append((parent,))
163
 
        return self.inventories.add_lines((revision_id,), final_parents, lines,
164
 
            check_content=check_content)[0]
165
 
 
166
 
    def is_shared(self):
167
 
        """AllInOne repositories cannot be shared."""
168
 
        return False
169
 
 
170
 
    @needs_write_lock
171
 
    def set_make_working_trees(self, new_value):
172
 
        """Set the policy flag for making working trees when creating branches.
173
 
 
174
 
        This only applies to branches that use this repository.
175
 
 
176
 
        The default is 'True'.
177
 
        :param new_value: True to restore the default, False to disable making
178
 
                          working trees.
179
 
        """
180
 
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
181
 
 
182
 
    def make_working_trees(self):
183
 
        """Returns the policy for making working trees on new branches."""
184
 
        return True
185
 
 
186
 
    def revision_graph_can_have_wrong_parents(self):
187
 
        # XXX: This is an old format that we don't support full checking on, so
188
 
        # just claim that checking for this inconsistency is not required.
189
 
        return False
190
 
 
191
 
 
192
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
193
 
    """A subclass of MetaDirRepository to set weave specific policy."""
194
 
 
195
 
    @property
196
 
    def _serializer(self):
197
 
        return xml5.serializer_v5
198
 
 
199
 
    def __init__(self, _format, a_bzrdir, control_files):
200
 
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
201
 
        self._fetch_order = 'topological'
202
 
        self._fetch_reconcile = True
203
 
 
204
 
    @needs_read_lock
205
 
    def _all_possible_ids(self):
206
 
        """Return all the possible revisions that we could find."""
207
 
        if 'evil' in debug.debug_flags:
208
 
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
209
 
        return [key[-1] for key in self.inventories.keys()]
210
 
 
211
 
    @needs_read_lock
212
 
    def _all_revision_ids(self):
213
 
        """Returns a list of all the revision ids in the repository. 
214
 
 
215
 
        These are in as much topological order as the underlying store can 
216
 
        present: for weaves ghosts may lead to a lack of correctness until
217
 
        the reweave updates the parents list.
218
 
        """
219
 
        return [key[-1] for key in self.revisions.keys()]
220
 
 
221
 
    def _activate_new_inventory(self):
222
 
        """Put a replacement inventory.new into use as inventories."""
223
 
        # Copy the content across
224
 
        t = self._transport
225
 
        t.copy('inventory.new.weave', 'inventory.weave')
226
 
        # delete the temp inventory
227
 
        t.delete('inventory.new.weave')
228
 
        # Check we can parse the new weave properly as a sanity check
229
 
        self.inventories.keys()
230
 
 
231
 
    def _backup_inventory(self):
232
 
        t = self._transport
233
 
        t.copy('inventory.weave', 'inventory.backup.weave')
234
 
 
235
 
    def _temp_inventories(self):
236
 
        t = self._transport
237
 
        return self._format._get_inventories(t, self, 'inventory.new')
238
 
 
239
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
240
 
                           timezone=None, committer=None, revprops=None,
241
 
                           revision_id=None):
242
 
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
243
 
        result = CommitBuilder(self, parents, config, timestamp, timezone,
244
 
                              committer, revprops, revision_id)
245
 
        self.start_write_group()
246
 
        return result
247
 
 
248
 
    @needs_read_lock
249
 
    def get_revision(self, revision_id):
250
 
        """Return the Revision object for a named revision"""
251
 
        r = self.get_revision_reconcile(revision_id)
252
 
        return r
253
 
 
254
 
    def _inventory_add_lines(self, revision_id, parents, lines,
255
 
        check_content=True):
256
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
257
 
        present_parents = self.get_graph().get_parent_map(parents)
258
 
        final_parents = []
259
 
        for parent in parents:
260
 
            if parent in present_parents:
261
 
                final_parents.append((parent,))
262
 
        return self.inventories.add_lines((revision_id,), final_parents, lines,
263
 
            check_content=check_content)[0]
264
 
 
265
 
    def revision_graph_can_have_wrong_parents(self):
266
 
        return False
267
 
 
268
 
 
269
 
class PreSplitOutRepositoryFormat(RepositoryFormat):
270
 
    """Base class for the pre split out repository formats."""
271
 
 
272
 
    rich_root_data = False
273
 
    supports_tree_reference = False
274
 
    supports_ghosts = False
275
 
    supports_external_lookups = False
276
 
 
277
 
    def initialize(self, a_bzrdir, shared=False, _internal=False):
278
 
        """Create a weave repository."""
279
 
        if shared:
280
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
281
 
 
282
 
        if not _internal:
283
 
            # always initialized when the bzrdir is.
284
 
            return self.open(a_bzrdir, _found=True)
285
 
        
286
 
        # Create an empty weave
287
 
        sio = StringIO()
288
 
        weavefile.write_weave_v5(weave.Weave(), sio)
289
 
        empty_weave = sio.getvalue()
290
 
 
291
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
292
 
        
293
 
        # FIXME: RBC 20060125 don't peek under the covers
294
 
        # NB: no need to escape relative paths that are url safe.
295
 
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
296
 
            'branch-lock', lockable_files.TransportLock)
297
 
        control_files.create_lock()
298
 
        control_files.lock_write()
299
 
        transport = a_bzrdir.transport
300
 
        try:
301
 
            transport.mkdir_multi(['revision-store', 'weaves'],
302
 
                mode=a_bzrdir._get_dir_mode())
303
 
            transport.put_bytes_non_atomic('inventory.weave', empty_weave)
304
 
        finally:
305
 
            control_files.unlock()
306
 
        return self.open(a_bzrdir, _found=True)
307
 
 
308
 
    def open(self, a_bzrdir, _found=False):
309
 
        """See RepositoryFormat.open()."""
310
 
        if not _found:
311
 
            # we are being called directly and must probe.
312
 
            raise NotImplementedError
313
 
 
314
 
        repo_transport = a_bzrdir.get_repository_transport(None)
315
 
        control_files = a_bzrdir._control_files
316
 
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
317
 
        result.revisions = self._get_revisions(repo_transport, result)
318
 
        result.signatures = self._get_signatures(repo_transport, result)
319
 
        result.inventories = self._get_inventories(repo_transport, result)
320
 
        result.texts = self._get_texts(repo_transport, result)
321
 
        return result
322
 
 
323
 
    def check_conversion_target(self, target_format):
324
 
        pass
325
 
 
326
 
 
327
 
class RepositoryFormat4(PreSplitOutRepositoryFormat):
328
 
    """Bzr repository format 4.
329
 
 
330
 
    This repository format has:
331
 
     - flat stores
332
 
     - TextStores for texts, inventories,revisions.
333
 
 
334
 
    This format is deprecated: it indexes texts using a text id which is
335
 
    removed in format 5; initialization and write support for this format
336
 
    has been removed.
337
 
    """
338
 
 
339
 
    _matchingbzrdir = bzrdir.BzrDirFormat4()
340
 
 
341
 
    def __init__(self):
342
 
        super(RepositoryFormat4, self).__init__()
343
 
        self._fetch_order = 'topological'
344
 
        self._fetch_reconcile = True
345
 
 
346
 
    def get_format_description(self):
347
 
        """See RepositoryFormat.get_format_description()."""
348
 
        return "Repository format 4"
349
 
 
350
 
    def initialize(self, url, shared=False, _internal=False):
351
 
        """Format 4 branches cannot be created."""
352
 
        raise errors.UninitializableFormat(self)
353
 
 
354
 
    def is_supported(self):
355
 
        """Format 4 is not supported.
356
 
 
357
 
        It is not supported because the model changed from 4 to 5 and the
358
 
        conversion logic is expensive - so doing it on the fly was not 
359
 
        feasible.
360
 
        """
361
 
        return False
362
 
 
363
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
364
 
        # No inventories store written so far.
365
 
        return None
366
 
 
367
 
    def _get_revisions(self, repo_transport, repo):
368
 
        from bzrlib.xml4 import serializer_v4
369
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
370
 
            serializer_v4, True, versionedfile.PrefixMapper(),
371
 
            repo.is_locked, repo.is_write_locked)
372
 
 
373
 
    def _get_signatures(self, repo_transport, repo):
374
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
375
 
            False, versionedfile.PrefixMapper(),
376
 
            repo.is_locked, repo.is_write_locked)
377
 
 
378
 
    def _get_texts(self, repo_transport, repo):
379
 
        return None
380
 
 
381
 
 
382
 
class RepositoryFormat5(PreSplitOutRepositoryFormat):
383
 
    """Bzr control format 5.
384
 
 
385
 
    This repository format has:
386
 
     - weaves for file texts and inventory
387
 
     - flat stores
388
 
     - TextStores for revisions and signatures.
389
 
    """
390
 
 
391
 
    _versionedfile_class = weave.WeaveFile
392
 
    _matchingbzrdir = bzrdir.BzrDirFormat5()
393
 
 
394
 
    def __init__(self):
395
 
        super(RepositoryFormat5, self).__init__()
396
 
        self._fetch_order = 'topological'
397
 
        self._fetch_reconcile = True
398
 
 
399
 
    def get_format_description(self):
400
 
        """See RepositoryFormat.get_format_description()."""
401
 
        return "Weave repository format 5"
402
 
 
403
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
404
 
        mapper = versionedfile.ConstantMapper(name)
405
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
406
 
            weave.WeaveFile, mapper, repo.is_locked)
407
 
 
408
 
    def _get_revisions(self, repo_transport, repo):
409
 
        from bzrlib.xml5 import serializer_v5
410
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
411
 
            serializer_v5, False, versionedfile.PrefixMapper(),
412
 
            repo.is_locked, repo.is_write_locked)
413
 
 
414
 
    def _get_signatures(self, repo_transport, repo):
415
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
416
 
            False, versionedfile.PrefixMapper(),
417
 
            repo.is_locked, repo.is_write_locked)
418
 
 
419
 
    def _get_texts(self, repo_transport, repo):
420
 
        mapper = versionedfile.PrefixMapper()
421
 
        base_transport = repo_transport.clone('weaves')
422
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
423
 
            weave.WeaveFile, mapper, repo.is_locked)
424
 
 
425
 
 
426
 
class RepositoryFormat6(PreSplitOutRepositoryFormat):
427
 
    """Bzr control format 6.
428
 
 
429
 
    This repository format has:
430
 
     - weaves for file texts and inventory
431
 
     - hash subdirectory based stores.
432
 
     - TextStores for revisions and signatures.
433
 
    """
434
 
 
435
 
    _versionedfile_class = weave.WeaveFile
436
 
    _matchingbzrdir = bzrdir.BzrDirFormat6()
437
 
 
438
 
    def __init__(self):
439
 
        super(RepositoryFormat6, self).__init__()
440
 
        self._fetch_order = 'topological'
441
 
        self._fetch_reconcile = True
442
 
 
443
 
    def get_format_description(self):
444
 
        """See RepositoryFormat.get_format_description()."""
445
 
        return "Weave repository format 6"
446
 
 
447
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
448
 
        mapper = versionedfile.ConstantMapper(name)
449
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
450
 
            weave.WeaveFile, mapper, repo.is_locked)
451
 
 
452
 
    def _get_revisions(self, repo_transport, repo):
453
 
        from bzrlib.xml5 import serializer_v5
454
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
455
 
            serializer_v5, False, versionedfile.HashPrefixMapper(),
456
 
            repo.is_locked, repo.is_write_locked)
457
 
 
458
 
    def _get_signatures(self, repo_transport, repo):
459
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
460
 
            False, versionedfile.HashPrefixMapper(),
461
 
            repo.is_locked, repo.is_write_locked)
462
 
 
463
 
    def _get_texts(self, repo_transport, repo):
464
 
        mapper = versionedfile.HashPrefixMapper()
465
 
        base_transport = repo_transport.clone('weaves')
466
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
467
 
            weave.WeaveFile, mapper, repo.is_locked)
468
 
 
469
 
 
470
 
class RepositoryFormat7(MetaDirRepositoryFormat):
471
 
    """Bzr repository 7.
472
 
 
473
 
    This repository format has:
474
 
     - weaves for file texts and inventory
475
 
     - hash subdirectory based stores.
476
 
     - TextStores for revisions and signatures.
477
 
     - a format marker of its own
478
 
     - an optional 'shared-storage' flag
479
 
     - an optional 'no-working-trees' flag
480
 
    """
481
 
 
482
 
    _versionedfile_class = weave.WeaveFile
483
 
    supports_ghosts = False
484
 
 
485
 
    def get_format_string(self):
486
 
        """See RepositoryFormat.get_format_string()."""
487
 
        return "Bazaar-NG Repository format 7"
488
 
 
489
 
    def get_format_description(self):
490
 
        """See RepositoryFormat.get_format_description()."""
491
 
        return "Weave repository format 7"
492
 
 
493
 
    def check_conversion_target(self, target_format):
494
 
        pass
495
 
 
496
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
497
 
        mapper = versionedfile.ConstantMapper(name)
498
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
499
 
            weave.WeaveFile, mapper, repo.is_locked)
500
 
 
501
 
    def _get_revisions(self, repo_transport, repo):
502
 
        from bzrlib.xml5 import serializer_v5
503
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
504
 
            serializer_v5, True, versionedfile.HashPrefixMapper(),
505
 
            repo.is_locked, repo.is_write_locked)
506
 
 
507
 
    def _get_signatures(self, repo_transport, repo):
508
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
509
 
            True, versionedfile.HashPrefixMapper(),
510
 
            repo.is_locked, repo.is_write_locked)
511
 
 
512
 
    def _get_texts(self, repo_transport, repo):
513
 
        mapper = versionedfile.HashPrefixMapper()
514
 
        base_transport = repo_transport.clone('weaves')
515
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
516
 
            weave.WeaveFile, mapper, repo.is_locked)
517
 
 
518
 
    def initialize(self, a_bzrdir, shared=False):
519
 
        """Create a weave repository.
520
 
 
521
 
        :param shared: If true the repository will be initialized as a shared
522
 
                       repository.
523
 
        """
524
 
        # Create an empty weave
525
 
        sio = StringIO()
526
 
        weavefile.write_weave_v5(weave.Weave(), sio)
527
 
        empty_weave = sio.getvalue()
528
 
 
529
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
530
 
        dirs = ['revision-store', 'weaves']
531
 
        files = [('inventory.weave', StringIO(empty_weave)), 
532
 
                 ]
533
 
        utf8_files = [('format', self.get_format_string())]
534
 
 
535
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
536
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
537
 
 
538
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
539
 
        """See RepositoryFormat.open().
540
 
        
541
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
542
 
                                    repository at a slightly different url
543
 
                                    than normal. I.e. during 'upgrade'.
544
 
        """
545
 
        if not _found:
546
 
            format = RepositoryFormat.find_format(a_bzrdir)
547
 
        if _override_transport is not None:
548
 
            repo_transport = _override_transport
549
 
        else:
550
 
            repo_transport = a_bzrdir.get_repository_transport(None)
551
 
        control_files = lockable_files.LockableFiles(repo_transport,
552
 
                                'lock', lockdir.LockDir)
553
 
        result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
554
 
            control_files=control_files)
555
 
        result.revisions = self._get_revisions(repo_transport, result)
556
 
        result.signatures = self._get_signatures(repo_transport, result)
557
 
        result.inventories = self._get_inventories(repo_transport, result)
558
 
        result.texts = self._get_texts(repo_transport, result)
559
 
        result._transport = repo_transport
560
 
        return result
561
 
 
562
 
 
563
 
class TextVersionedFiles(VersionedFiles):
564
 
    """Just-a-bunch-of-files based VersionedFile stores."""
565
 
 
566
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
567
 
        self._compressed = compressed
568
 
        self._transport = transport
569
 
        self._mapper = mapper
570
 
        if self._compressed:
571
 
            self._ext = '.gz'
572
 
        else:
573
 
            self._ext = ''
574
 
        self._is_locked = is_locked
575
 
        self._can_write = can_write
576
 
 
577
 
    def add_lines(self, key, parents, lines):
578
 
        """Add a revision to the store."""
579
 
        if not self._is_locked():
580
 
            raise errors.ObjectNotLocked(self)
581
 
        if not self._can_write():
582
 
            raise errors.ReadOnlyError(self)
583
 
        if '/' in key[-1]:
584
 
            raise ValueError('bad idea to put / in %r' % (key,))
585
 
        text = ''.join(lines)
586
 
        if self._compressed:
587
 
            text = bytes_to_gzip(text)
588
 
        path = self._map(key)
589
 
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
590
 
 
591
 
    def insert_record_stream(self, stream):
592
 
        adapters = {}
593
 
        for record in stream:
594
 
            # Raise an error when a record is missing.
595
 
            if record.storage_kind == 'absent':
596
 
                raise errors.RevisionNotPresent([record.key[0]], self)
597
 
            # adapt to non-tuple interface
598
 
            if record.storage_kind == 'fulltext':
599
 
                self.add_lines(record.key, None,
600
 
                    osutils.split_lines(record.get_bytes_as('fulltext')))
601
 
            else:
602
 
                adapter_key = record.storage_kind, 'fulltext'
603
 
                try:
604
 
                    adapter = adapters[adapter_key]
605
 
                except KeyError:
606
 
                    adapter_factory = adapter_registry.get(adapter_key)
607
 
                    adapter = adapter_factory(self)
608
 
                    adapters[adapter_key] = adapter
609
 
                lines = osutils.split_lines(adapter.get_bytes(
610
 
                    record, record.get_bytes_as(record.storage_kind)))
611
 
                try:
612
 
                    self.add_lines(record.key, None, lines)
613
 
                except RevisionAlreadyPresent:
614
 
                    pass
615
 
 
616
 
    def _load_text(self, key):
617
 
        if not self._is_locked():
618
 
            raise errors.ObjectNotLocked(self)
619
 
        path = self._map(key)
620
 
        try:
621
 
            text = self._transport.get_bytes(path)
622
 
            compressed = self._compressed
623
 
        except errors.NoSuchFile:
624
 
            if self._compressed:
625
 
                # try without the .gz
626
 
                path = path[:-3]
627
 
                try:
628
 
                    text = self._transport.get_bytes(path)
629
 
                    compressed = False
630
 
                except errors.NoSuchFile:
631
 
                    return None
632
 
            else:
633
 
                return None
634
 
        if compressed:
635
 
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
636
 
        return text
637
 
 
638
 
    def _map(self, key):
639
 
        return self._mapper.map(key) + self._ext
640
 
 
641
 
 
642
 
class RevisionTextStore(TextVersionedFiles):
643
 
    """Legacy thunk for format 4 repositories."""
644
 
 
645
 
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
646
 
        can_write):
647
 
        """Create a RevisionTextStore at transport with serializer."""
648
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
649
 
            is_locked, can_write)
650
 
        self._serializer = serializer
651
 
 
652
 
    def _load_text_parents(self, key):
653
 
        text = self._load_text(key)
654
 
        if text is None:
655
 
            return None, None
656
 
        parents = self._serializer.read_revision_from_string(text).parent_ids
657
 
        return text, tuple((parent,) for parent in parents)
658
 
 
659
 
    def get_parent_map(self, keys):
660
 
        result = {}
661
 
        for key in keys:
662
 
            parents = self._load_text_parents(key)[1]
663
 
            if parents is None:
664
 
                continue
665
 
            result[key] = parents
666
 
        return result
667
 
    
668
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
669
 
        for key in keys:
670
 
            text, parents = self._load_text_parents(key)
671
 
            if text is None:
672
 
                yield AbsentContentFactory(key)
673
 
            else:
674
 
                yield FulltextContentFactory(key, parents, None, text)
675
 
 
676
 
    def keys(self):
677
 
        if not self._is_locked():
678
 
            raise errors.ObjectNotLocked(self)
679
 
        relpaths = set()
680
 
        for quoted_relpath in self._transport.iter_files_recursive():
681
 
            relpath = urllib.unquote(quoted_relpath)
682
 
            path, ext = os.path.splitext(relpath)
683
 
            if ext == '.gz':
684
 
                relpath = path
685
 
            if '.sig' not in relpath:
686
 
                relpaths.add(relpath)
687
 
        paths = list(relpaths)
688
 
        return set([self._mapper.unmap(path) for path in paths])
689
 
 
690
 
 
691
 
class SignatureTextStore(TextVersionedFiles):
692
 
    """Legacy thunk for format 4-7 repositories."""
693
 
 
694
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
695
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
696
 
            is_locked, can_write)
697
 
        self._ext = '.sig' + self._ext
698
 
 
699
 
    def get_parent_map(self, keys):
700
 
        result = {}
701
 
        for key in keys:
702
 
            text = self._load_text(key)
703
 
            if text is None:
704
 
                continue
705
 
            result[key] = None
706
 
        return result
707
 
    
708
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
709
 
        for key in keys:
710
 
            text = self._load_text(key)
711
 
            if text is None:
712
 
                yield AbsentContentFactory(key)
713
 
            else:
714
 
                yield FulltextContentFactory(key, None, None, text)
715
 
 
716
 
    def keys(self):
717
 
        if not self._is_locked():
718
 
            raise errors.ObjectNotLocked(self)
719
 
        relpaths = set()
720
 
        for quoted_relpath in self._transport.iter_files_recursive():
721
 
            relpath = urllib.unquote(quoted_relpath)
722
 
            path, ext = os.path.splitext(relpath)
723
 
            if ext == '.gz':
724
 
                relpath = path
725
 
            if not relpath.endswith('.sig'):
726
 
                continue
727
 
            relpaths.add(relpath[:-4])
728
 
        paths = list(relpaths)
729
 
        return set([self._mapper.unmap(path) for path in paths])
730
 
 
731
 
_legacy_formats = [RepositoryFormat4(),
732
 
                   RepositoryFormat5(),
733
 
                   RepositoryFormat6()]