/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

Warn if a config variable can't be interpreted as a boolean

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
    graph as _mod_graph,
 
32
    )
 
33
""")
 
34
from bzrlib import (
 
35
    bzrdir,
 
36
    debug,
 
37
    errors,
 
38
    lockable_files,
 
39
    lockdir,
 
40
    osutils,
 
41
    revision as _mod_revision,
 
42
    urlutils,
 
43
    versionedfile,
 
44
    weave,
 
45
    weavefile,
 
46
    )
 
47
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
48
from bzrlib.repository import (
 
49
    CommitBuilder,
 
50
    MetaDirVersionedFileRepository,
 
51
    MetaDirRepositoryFormat,
 
52
    Repository,
 
53
    RepositoryFormat,
 
54
    )
 
55
from bzrlib.store.text import TextStore
 
56
from bzrlib.trace import mutter
 
57
from bzrlib.tuned_gzip import GzipFile, bytes_to_gzip
 
58
from bzrlib.versionedfile import (
 
59
    AbsentContentFactory,
 
60
    FulltextContentFactory,
 
61
    VersionedFiles,
 
62
    )
 
63
 
 
64
 
 
65
class AllInOneRepository(Repository):
 
66
    """Legacy support - the repository behaviour for all-in-one branches."""
 
67
 
 
68
    @property
 
69
    def _serializer(self):
 
70
        return xml5.serializer_v5
 
71
 
 
72
    def _escape(self, file_or_path):
 
73
        if not isinstance(file_or_path, basestring):
 
74
            file_or_path = '/'.join(file_or_path)
 
75
        if file_or_path == '':
 
76
            return u''
 
77
        return urlutils.escape(osutils.safe_unicode(file_or_path))
 
78
 
 
79
    def __init__(self, _format, a_bzrdir):
 
80
        # we reuse one control files instance.
 
81
        dir_mode = a_bzrdir._get_dir_mode()
 
82
        file_mode = a_bzrdir._get_file_mode()
 
83
 
 
84
        def get_store(name, compressed=True, prefixed=False):
 
85
            # FIXME: This approach of assuming stores are all entirely compressed
 
86
            # or entirely uncompressed is tidy, but breaks upgrade from
 
87
            # some existing branches where there's a mixture; we probably
 
88
            # still want the option to look for both.
 
89
            relpath = self._escape(name)
 
90
            store = TextStore(a_bzrdir.transport.clone(relpath),
 
91
                              prefixed=prefixed, compressed=compressed,
 
92
                              dir_mode=dir_mode,
 
93
                              file_mode=file_mode)
 
94
            return store
 
95
 
 
96
        # not broken out yet because the controlweaves|inventory_store
 
97
        # and texts bits are still different.
 
98
        if isinstance(_format, RepositoryFormat4):
 
99
            # cannot remove these - there is still no consistent api
 
100
            # which allows access to this old info.
 
101
            self.inventory_store = get_store('inventory-store')
 
102
            self._text_store = get_store('text-store')
 
103
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
 
104
 
 
105
    @needs_read_lock
 
106
    def _all_possible_ids(self):
 
107
        """Return all the possible revisions that we could find."""
 
108
        if 'evil' in debug.debug_flags:
 
109
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
110
        return [key[-1] for key in self.inventories.keys()]
 
111
 
 
112
    @needs_read_lock
 
113
    def _all_revision_ids(self):
 
114
        """Returns a list of all the revision ids in the repository.
 
115
 
 
116
        These are in as much topological order as the underlying store can
 
117
        present: for weaves ghosts may lead to a lack of correctness until
 
118
        the reweave updates the parents list.
 
119
        """
 
120
        return [key[-1] for key in self.revisions.keys()]
 
121
 
 
122
    def _activate_new_inventory(self):
 
123
        """Put a replacement inventory.new into use as inventories."""
 
124
        # Copy the content across
 
125
        t = self.bzrdir._control_files._transport
 
126
        t.copy('inventory.new.weave', 'inventory.weave')
 
127
        # delete the temp inventory
 
128
        t.delete('inventory.new.weave')
 
129
        # Check we can parse the new weave properly as a sanity check
 
130
        self.inventories.keys()
 
131
 
 
132
    def _backup_inventory(self):
 
133
        t = self.bzrdir._control_files._transport
 
134
        t.copy('inventory.weave', 'inventory.backup.weave')
 
135
 
 
136
    def _temp_inventories(self):
 
137
        t = self.bzrdir._control_files._transport
 
138
        return self._format._get_inventories(t, self, 'inventory.new')
 
139
 
 
140
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
141
                           timezone=None, committer=None, revprops=None,
 
142
                           revision_id=None):
 
143
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
 
144
        result = CommitBuilder(self, parents, config, timestamp, timezone,
 
145
                              committer, revprops, revision_id)
 
146
        self.start_write_group()
 
147
        return result
 
148
 
 
149
    @needs_read_lock
 
150
    def get_revisions(self, revision_ids):
 
151
        revs = self._get_revisions(revision_ids)
 
152
        return revs
 
153
 
 
154
    def _inventory_add_lines(self, revision_id, parents, lines,
 
155
        check_content=True):
 
156
        """Store lines in inv_vf and return the sha1 of the inventory."""
 
157
        present_parents = self.get_graph().get_parent_map(parents)
 
158
        final_parents = []
 
159
        for parent in parents:
 
160
            if parent in present_parents:
 
161
                final_parents.append((parent,))
 
162
        return self.inventories.add_lines((revision_id,), final_parents, lines,
 
163
            check_content=check_content)[0]
 
164
 
 
165
    def is_shared(self):
 
166
        """AllInOne repositories cannot be shared."""
 
167
        return False
 
168
 
 
169
    @needs_write_lock
 
170
    def set_make_working_trees(self, new_value):
 
171
        """Set the policy flag for making working trees when creating branches.
 
172
 
 
173
        This only applies to branches that use this repository.
 
174
 
 
175
        The default is 'True'.
 
176
        :param new_value: True to restore the default, False to disable making
 
177
                          working trees.
 
178
        """
 
179
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
 
180
 
 
181
    def make_working_trees(self):
 
182
        """Returns the policy for making working trees on new branches."""
 
183
        return True
 
184
 
 
185
    def revision_graph_can_have_wrong_parents(self):
 
186
        # XXX: This is an old format that we don't support full checking on, so
 
187
        # just claim that checking for this inconsistency is not required.
 
188
        return False
 
189
 
 
190
 
 
191
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
 
192
    """A subclass of MetaDirRepository to set weave specific policy."""
 
193
 
 
194
    def __init__(self, _format, a_bzrdir, control_files):
 
195
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
 
196
        self._serializer = _format._serializer
 
197
 
 
198
    @needs_read_lock
 
199
    def _all_possible_ids(self):
 
200
        """Return all the possible revisions that we could find."""
 
201
        if 'evil' in debug.debug_flags:
 
202
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
203
        return [key[-1] for key in self.inventories.keys()]
 
204
 
 
205
    @needs_read_lock
 
206
    def _all_revision_ids(self):
 
207
        """Returns a list of all the revision ids in the repository.
 
208
 
 
209
        These are in as much topological order as the underlying store can
 
210
        present: for weaves ghosts may lead to a lack of correctness until
 
211
        the reweave updates the parents list.
 
212
        """
 
213
        return [key[-1] for key in self.revisions.keys()]
 
214
 
 
215
    def _activate_new_inventory(self):
 
216
        """Put a replacement inventory.new into use as inventories."""
 
217
        # Copy the content across
 
218
        t = self._transport
 
219
        t.copy('inventory.new.weave', 'inventory.weave')
 
220
        # delete the temp inventory
 
221
        t.delete('inventory.new.weave')
 
222
        # Check we can parse the new weave properly as a sanity check
 
223
        self.inventories.keys()
 
224
 
 
225
    def _backup_inventory(self):
 
226
        t = self._transport
 
227
        t.copy('inventory.weave', 'inventory.backup.weave')
 
228
 
 
229
    def _temp_inventories(self):
 
230
        t = self._transport
 
231
        return self._format._get_inventories(t, self, 'inventory.new')
 
232
 
 
233
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
234
                           timezone=None, committer=None, revprops=None,
 
235
                           revision_id=None):
 
236
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
 
237
        result = CommitBuilder(self, parents, config, timestamp, timezone,
 
238
                              committer, revprops, revision_id)
 
239
        self.start_write_group()
 
240
        return result
 
241
 
 
242
    @needs_read_lock
 
243
    def get_revision(self, revision_id):
 
244
        """Return the Revision object for a named revision"""
 
245
        r = self.get_revision_reconcile(revision_id)
 
246
        return r
 
247
 
 
248
    def _inventory_add_lines(self, revision_id, parents, lines,
 
249
        check_content=True):
 
250
        """Store lines in inv_vf and return the sha1 of the inventory."""
 
251
        present_parents = self.get_graph().get_parent_map(parents)
 
252
        final_parents = []
 
253
        for parent in parents:
 
254
            if parent in present_parents:
 
255
                final_parents.append((parent,))
 
256
        return self.inventories.add_lines((revision_id,), final_parents, lines,
 
257
            check_content=check_content)[0]
 
258
 
 
259
    def revision_graph_can_have_wrong_parents(self):
 
260
        return False
 
261
 
 
262
 
 
263
class PreSplitOutRepositoryFormat(RepositoryFormat):
 
264
    """Base class for the pre split out repository formats."""
 
265
 
 
266
    rich_root_data = False
 
267
    supports_tree_reference = False
 
268
    supports_ghosts = False
 
269
    supports_external_lookups = False
 
270
    supports_chks = False
 
271
    _fetch_order = 'topological'
 
272
    _fetch_reconcile = True
 
273
    fast_deltas = False
 
274
 
 
275
    def initialize(self, a_bzrdir, shared=False, _internal=False):
 
276
        """Create a weave repository."""
 
277
        if shared:
 
278
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
279
 
 
280
        if not _internal:
 
281
            # always initialized when the bzrdir is.
 
282
            return self.open(a_bzrdir, _found=True)
 
283
 
 
284
        # Create an empty weave
 
285
        sio = StringIO()
 
286
        weavefile.write_weave_v5(weave.Weave(), sio)
 
287
        empty_weave = sio.getvalue()
 
288
 
 
289
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
290
 
 
291
        # FIXME: RBC 20060125 don't peek under the covers
 
292
        # NB: no need to escape relative paths that are url safe.
 
293
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
 
294
            'branch-lock', lockable_files.TransportLock)
 
295
        control_files.create_lock()
 
296
        control_files.lock_write()
 
297
        transport = a_bzrdir.transport
 
298
        try:
 
299
            transport.mkdir_multi(['revision-store', 'weaves'],
 
300
                mode=a_bzrdir._get_dir_mode())
 
301
            transport.put_bytes_non_atomic('inventory.weave', empty_weave,
 
302
                mode=a_bzrdir._get_file_mode())
 
303
        finally:
 
304
            control_files.unlock()
 
305
        repository = self.open(a_bzrdir, _found=True)
 
306
        self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
 
307
        return repository
 
308
 
 
309
    def open(self, a_bzrdir, _found=False):
 
310
        """See RepositoryFormat.open()."""
 
311
        if not _found:
 
312
            # we are being called directly and must probe.
 
313
            raise NotImplementedError
 
314
 
 
315
        repo_transport = a_bzrdir.get_repository_transport(None)
 
316
        control_files = a_bzrdir._control_files
 
317
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
 
318
        result.revisions = self._get_revisions(repo_transport, result)
 
319
        result.signatures = self._get_signatures(repo_transport, result)
 
320
        result.inventories = self._get_inventories(repo_transport, result)
 
321
        result.texts = self._get_texts(repo_transport, result)
 
322
        result.chk_bytes = None
 
323
        return result
 
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 _get_inventories(self, repo_transport, repo, name='inventory'):
 
498
        mapper = versionedfile.ConstantMapper(name)
 
499
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
500
            weave.WeaveFile, mapper, repo.is_locked)
 
501
 
 
502
    def _get_revisions(self, repo_transport, repo):
 
503
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
504
            xml5.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.chk_bytes = None
 
560
        result._transport = repo_transport
 
561
        return result
 
562
 
 
563
 
 
564
class TextVersionedFiles(VersionedFiles):
 
565
    """Just-a-bunch-of-files based VersionedFile stores."""
 
566
 
 
567
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
568
        self._compressed = compressed
 
569
        self._transport = transport
 
570
        self._mapper = mapper
 
571
        if self._compressed:
 
572
            self._ext = '.gz'
 
573
        else:
 
574
            self._ext = ''
 
575
        self._is_locked = is_locked
 
576
        self._can_write = can_write
 
577
 
 
578
    def add_lines(self, key, parents, lines):
 
579
        """Add a revision to the store."""
 
580
        if not self._is_locked():
 
581
            raise errors.ObjectNotLocked(self)
 
582
        if not self._can_write():
 
583
            raise errors.ReadOnlyError(self)
 
584
        if '/' in key[-1]:
 
585
            raise ValueError('bad idea to put / in %r' % (key,))
 
586
        text = ''.join(lines)
 
587
        if self._compressed:
 
588
            text = bytes_to_gzip(text)
 
589
        path = self._map(key)
 
590
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
 
591
 
 
592
    def insert_record_stream(self, stream):
 
593
        adapters = {}
 
594
        for record in stream:
 
595
            # Raise an error when a record is missing.
 
596
            if record.storage_kind == 'absent':
 
597
                raise errors.RevisionNotPresent([record.key[0]], self)
 
598
            # adapt to non-tuple interface
 
599
            if record.storage_kind == 'fulltext':
 
600
                self.add_lines(record.key, None,
 
601
                    osutils.split_lines(record.get_bytes_as('fulltext')))
 
602
            else:
 
603
                adapter_key = record.storage_kind, 'fulltext'
 
604
                try:
 
605
                    adapter = adapters[adapter_key]
 
606
                except KeyError:
 
607
                    adapter_factory = adapter_registry.get(adapter_key)
 
608
                    adapter = adapter_factory(self)
 
609
                    adapters[adapter_key] = adapter
 
610
                lines = osutils.split_lines(adapter.get_bytes(
 
611
                    record, record.get_bytes_as(record.storage_kind)))
 
612
                try:
 
613
                    self.add_lines(record.key, None, lines)
 
614
                except RevisionAlreadyPresent:
 
615
                    pass
 
616
 
 
617
    def _load_text(self, key):
 
618
        if not self._is_locked():
 
619
            raise errors.ObjectNotLocked(self)
 
620
        path = self._map(key)
 
621
        try:
 
622
            text = self._transport.get_bytes(path)
 
623
            compressed = self._compressed
 
624
        except errors.NoSuchFile:
 
625
            if self._compressed:
 
626
                # try without the .gz
 
627
                path = path[:-3]
 
628
                try:
 
629
                    text = self._transport.get_bytes(path)
 
630
                    compressed = False
 
631
                except errors.NoSuchFile:
 
632
                    return None
 
633
            else:
 
634
                return None
 
635
        if compressed:
 
636
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
 
637
        return text
 
638
 
 
639
    def _map(self, key):
 
640
        return self._mapper.map(key) + self._ext
 
641
 
 
642
 
 
643
class RevisionTextStore(TextVersionedFiles):
 
644
    """Legacy thunk for format 4 repositories."""
 
645
 
 
646
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
 
647
        can_write):
 
648
        """Create a RevisionTextStore at transport with serializer."""
 
649
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
650
            is_locked, can_write)
 
651
        self._serializer = serializer
 
652
 
 
653
    def _load_text_parents(self, key):
 
654
        text = self._load_text(key)
 
655
        if text is None:
 
656
            return None, None
 
657
        parents = self._serializer.read_revision_from_string(text).parent_ids
 
658
        return text, tuple((parent,) for parent in parents)
 
659
 
 
660
    def get_parent_map(self, keys):
 
661
        result = {}
 
662
        for key in keys:
 
663
            parents = self._load_text_parents(key)[1]
 
664
            if parents is None:
 
665
                continue
 
666
            result[key] = parents
 
667
        return result
 
668
 
 
669
    def get_known_graph_ancestry(self, keys):
 
670
        """Get a KnownGraph instance with the ancestry of keys."""
 
671
        keys = self.keys()
 
672
        parent_map = self.get_parent_map(keys)
 
673
        kg = _mod_graph.KnownGraph(parent_map)
 
674
        return kg
 
675
 
 
676
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
677
        for key in keys:
 
678
            text, parents = self._load_text_parents(key)
 
679
            if text is None:
 
680
                yield AbsentContentFactory(key)
 
681
            else:
 
682
                yield FulltextContentFactory(key, parents, None, text)
 
683
 
 
684
    def keys(self):
 
685
        if not self._is_locked():
 
686
            raise errors.ObjectNotLocked(self)
 
687
        relpaths = set()
 
688
        for quoted_relpath in self._transport.iter_files_recursive():
 
689
            relpath = urllib.unquote(quoted_relpath)
 
690
            path, ext = os.path.splitext(relpath)
 
691
            if ext == '.gz':
 
692
                relpath = path
 
693
            if not relpath.endswith('.sig'):
 
694
                relpaths.add(relpath)
 
695
        paths = list(relpaths)
 
696
        return set([self._mapper.unmap(path) for path in paths])
 
697
 
 
698
 
 
699
class SignatureTextStore(TextVersionedFiles):
 
700
    """Legacy thunk for format 4-7 repositories."""
 
701
 
 
702
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
703
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
704
            is_locked, can_write)
 
705
        self._ext = '.sig' + self._ext
 
706
 
 
707
    def get_parent_map(self, keys):
 
708
        result = {}
 
709
        for key in keys:
 
710
            text = self._load_text(key)
 
711
            if text is None:
 
712
                continue
 
713
            result[key] = None
 
714
        return result
 
715
 
 
716
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
717
        for key in keys:
 
718
            text = self._load_text(key)
 
719
            if text is None:
 
720
                yield AbsentContentFactory(key)
 
721
            else:
 
722
                yield FulltextContentFactory(key, None, None, text)
 
723
 
 
724
    def keys(self):
 
725
        if not self._is_locked():
 
726
            raise errors.ObjectNotLocked(self)
 
727
        relpaths = set()
 
728
        for quoted_relpath in self._transport.iter_files_recursive():
 
729
            relpath = urllib.unquote(quoted_relpath)
 
730
            path, ext = os.path.splitext(relpath)
 
731
            if ext == '.gz':
 
732
                relpath = path
 
733
            if not relpath.endswith('.sig'):
 
734
                continue
 
735
            relpaths.add(relpath[:-4])
 
736
        paths = list(relpaths)
 
737
        return set([self._mapper.unmap(path) for path in paths])
 
738
 
 
739
_legacy_formats = [RepositoryFormat4(),
 
740
                   RepositoryFormat5(),
 
741
                   RepositoryFormat6()]