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