/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

  • Committer: John Arbash Meinel
  • Date: 2009-08-17 22:11:06 UTC
  • mto: This revision was merged to the branch mainline in revision 4629.
  • Revision ID: john@arbash-meinel.com-20090817221106-snef4s9g2f7zue60
Finish implementation tests.

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
        return self.open(a_bzrdir, _found=True)
 
306
 
 
307
    def open(self, a_bzrdir, _found=False):
 
308
        """See RepositoryFormat.open()."""
 
309
        if not _found:
 
310
            # we are being called directly and must probe.
 
311
            raise NotImplementedError
 
312
 
 
313
        repo_transport = a_bzrdir.get_repository_transport(None)
 
314
        control_files = a_bzrdir._control_files
 
315
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
 
316
        result.revisions = self._get_revisions(repo_transport, result)
 
317
        result.signatures = self._get_signatures(repo_transport, result)
 
318
        result.inventories = self._get_inventories(repo_transport, result)
 
319
        result.texts = self._get_texts(repo_transport, result)
 
320
        result.chk_bytes = None
 
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 get_format_description(self):
 
342
        """See RepositoryFormat.get_format_description()."""
 
343
        return "Repository format 4"
 
344
 
 
345
    def initialize(self, url, shared=False, _internal=False):
 
346
        """Format 4 branches cannot be created."""
 
347
        raise errors.UninitializableFormat(self)
 
348
 
 
349
    def is_supported(self):
 
350
        """Format 4 is not supported.
 
351
 
 
352
        It is not supported because the model changed from 4 to 5 and the
 
353
        conversion logic is expensive - so doing it on the fly was not
 
354
        feasible.
 
355
        """
 
356
        return False
 
357
 
 
358
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
359
        # No inventories store written so far.
 
360
        return None
 
361
 
 
362
    def _get_revisions(self, repo_transport, repo):
 
363
        from bzrlib.xml4 import serializer_v4
 
364
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
365
            serializer_v4, True, versionedfile.PrefixMapper(),
 
366
            repo.is_locked, repo.is_write_locked)
 
367
 
 
368
    def _get_signatures(self, repo_transport, repo):
 
369
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
370
            False, versionedfile.PrefixMapper(),
 
371
            repo.is_locked, repo.is_write_locked)
 
372
 
 
373
    def _get_texts(self, repo_transport, repo):
 
374
        return None
 
375
 
 
376
 
 
377
class RepositoryFormat5(PreSplitOutRepositoryFormat):
 
378
    """Bzr control format 5.
 
379
 
 
380
    This repository format has:
 
381
     - weaves for file texts and inventory
 
382
     - flat stores
 
383
     - TextStores for revisions and signatures.
 
384
    """
 
385
 
 
386
    _versionedfile_class = weave.WeaveFile
 
387
    _matchingbzrdir = bzrdir.BzrDirFormat5()
 
388
    @property
 
389
    def _serializer(self):
 
390
        return xml5.serializer_v5
 
391
 
 
392
    def get_format_description(self):
 
393
        """See RepositoryFormat.get_format_description()."""
 
394
        return "Weave repository format 5"
 
395
 
 
396
    def network_name(self):
 
397
        """The network name for this format is the control dirs disk label."""
 
398
        return self._matchingbzrdir.get_format_string()
 
399
 
 
400
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
401
        mapper = versionedfile.ConstantMapper(name)
 
402
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
403
            weave.WeaveFile, mapper, repo.is_locked)
 
404
 
 
405
    def _get_revisions(self, repo_transport, repo):
 
406
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
407
            xml5.serializer_v5, False, versionedfile.PrefixMapper(),
 
408
            repo.is_locked, repo.is_write_locked)
 
409
 
 
410
    def _get_signatures(self, repo_transport, repo):
 
411
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
412
            False, versionedfile.PrefixMapper(),
 
413
            repo.is_locked, repo.is_write_locked)
 
414
 
 
415
    def _get_texts(self, repo_transport, repo):
 
416
        mapper = versionedfile.PrefixMapper()
 
417
        base_transport = repo_transport.clone('weaves')
 
418
        return versionedfile.ThunkedVersionedFiles(base_transport,
 
419
            weave.WeaveFile, mapper, repo.is_locked)
 
420
 
 
421
 
 
422
class RepositoryFormat6(PreSplitOutRepositoryFormat):
 
423
    """Bzr control format 6.
 
424
 
 
425
    This repository format has:
 
426
     - weaves for file texts and inventory
 
427
     - hash subdirectory based stores.
 
428
     - TextStores for revisions and signatures.
 
429
    """
 
430
 
 
431
    _versionedfile_class = weave.WeaveFile
 
432
    _matchingbzrdir = bzrdir.BzrDirFormat6()
 
433
    @property
 
434
    def _serializer(self):
 
435
        return xml5.serializer_v5
 
436
 
 
437
    def get_format_description(self):
 
438
        """See RepositoryFormat.get_format_description()."""
 
439
        return "Weave repository format 6"
 
440
 
 
441
    def network_name(self):
 
442
        """The network name for this format is the control dirs disk label."""
 
443
        return self._matchingbzrdir.get_format_string()
 
444
 
 
445
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
446
        mapper = versionedfile.ConstantMapper(name)
 
447
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
448
            weave.WeaveFile, mapper, repo.is_locked)
 
449
 
 
450
    def _get_revisions(self, repo_transport, repo):
 
451
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
452
            xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
 
453
            repo.is_locked, repo.is_write_locked)
 
454
 
 
455
    def _get_signatures(self, repo_transport, repo):
 
456
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
457
            False, versionedfile.HashPrefixMapper(),
 
458
            repo.is_locked, repo.is_write_locked)
 
459
 
 
460
    def _get_texts(self, repo_transport, repo):
 
461
        mapper = versionedfile.HashPrefixMapper()
 
462
        base_transport = repo_transport.clone('weaves')
 
463
        return versionedfile.ThunkedVersionedFiles(base_transport,
 
464
            weave.WeaveFile, mapper, repo.is_locked)
 
465
 
 
466
 
 
467
class RepositoryFormat7(MetaDirRepositoryFormat):
 
468
    """Bzr repository 7.
 
469
 
 
470
    This repository format has:
 
471
     - weaves for file texts and inventory
 
472
     - hash subdirectory based stores.
 
473
     - TextStores for revisions and signatures.
 
474
     - a format marker of its own
 
475
     - an optional 'shared-storage' flag
 
476
     - an optional 'no-working-trees' flag
 
477
    """
 
478
 
 
479
    _versionedfile_class = weave.WeaveFile
 
480
    supports_ghosts = False
 
481
    supports_chks = False
 
482
 
 
483
    _fetch_order = 'topological'
 
484
    _fetch_reconcile = True
 
485
    fast_deltas = False
 
486
    @property
 
487
    def _serializer(self):
 
488
        return xml5.serializer_v5
 
489
 
 
490
    def get_format_string(self):
 
491
        """See RepositoryFormat.get_format_string()."""
 
492
        return "Bazaar-NG Repository format 7"
 
493
 
 
494
    def get_format_description(self):
 
495
        """See RepositoryFormat.get_format_description()."""
 
496
        return "Weave repository format 7"
 
497
 
 
498
    def check_conversion_target(self, target_format):
 
499
        pass
 
500
 
 
501
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
502
        mapper = versionedfile.ConstantMapper(name)
 
503
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
504
            weave.WeaveFile, mapper, repo.is_locked)
 
505
 
 
506
    def _get_revisions(self, repo_transport, repo):
 
507
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
508
            xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
 
509
            repo.is_locked, repo.is_write_locked)
 
510
 
 
511
    def _get_signatures(self, repo_transport, repo):
 
512
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
513
            True, versionedfile.HashPrefixMapper(),
 
514
            repo.is_locked, repo.is_write_locked)
 
515
 
 
516
    def _get_texts(self, repo_transport, repo):
 
517
        mapper = versionedfile.HashPrefixMapper()
 
518
        base_transport = repo_transport.clone('weaves')
 
519
        return versionedfile.ThunkedVersionedFiles(base_transport,
 
520
            weave.WeaveFile, mapper, repo.is_locked)
 
521
 
 
522
    def initialize(self, a_bzrdir, shared=False):
 
523
        """Create a weave repository.
 
524
 
 
525
        :param shared: If true the repository will be initialized as a shared
 
526
                       repository.
 
527
        """
 
528
        # Create an empty weave
 
529
        sio = StringIO()
 
530
        weavefile.write_weave_v5(weave.Weave(), sio)
 
531
        empty_weave = sio.getvalue()
 
532
 
 
533
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
534
        dirs = ['revision-store', 'weaves']
 
535
        files = [('inventory.weave', StringIO(empty_weave)),
 
536
                 ]
 
537
        utf8_files = [('format', self.get_format_string())]
 
538
 
 
539
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
540
        return self.open(a_bzrdir=a_bzrdir, _found=True)
 
541
 
 
542
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
543
        """See RepositoryFormat.open().
 
544
 
 
545
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
546
                                    repository at a slightly different url
 
547
                                    than normal. I.e. during 'upgrade'.
 
548
        """
 
549
        if not _found:
 
550
            format = RepositoryFormat.find_format(a_bzrdir)
 
551
        if _override_transport is not None:
 
552
            repo_transport = _override_transport
 
553
        else:
 
554
            repo_transport = a_bzrdir.get_repository_transport(None)
 
555
        control_files = lockable_files.LockableFiles(repo_transport,
 
556
                                'lock', lockdir.LockDir)
 
557
        result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
 
558
            control_files=control_files)
 
559
        result.revisions = self._get_revisions(repo_transport, result)
 
560
        result.signatures = self._get_signatures(repo_transport, result)
 
561
        result.inventories = self._get_inventories(repo_transport, result)
 
562
        result.texts = self._get_texts(repo_transport, result)
 
563
        result.chk_bytes = None
 
564
        result._transport = repo_transport
 
565
        return result
 
566
 
 
567
 
 
568
class TextVersionedFiles(VersionedFiles):
 
569
    """Just-a-bunch-of-files based VersionedFile stores."""
 
570
 
 
571
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
572
        self._compressed = compressed
 
573
        self._transport = transport
 
574
        self._mapper = mapper
 
575
        if self._compressed:
 
576
            self._ext = '.gz'
 
577
        else:
 
578
            self._ext = ''
 
579
        self._is_locked = is_locked
 
580
        self._can_write = can_write
 
581
 
 
582
    def add_lines(self, key, parents, lines):
 
583
        """Add a revision to the store."""
 
584
        if not self._is_locked():
 
585
            raise errors.ObjectNotLocked(self)
 
586
        if not self._can_write():
 
587
            raise errors.ReadOnlyError(self)
 
588
        if '/' in key[-1]:
 
589
            raise ValueError('bad idea to put / in %r' % (key,))
 
590
        text = ''.join(lines)
 
591
        if self._compressed:
 
592
            text = bytes_to_gzip(text)
 
593
        path = self._map(key)
 
594
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
 
595
 
 
596
    def insert_record_stream(self, stream):
 
597
        adapters = {}
 
598
        for record in stream:
 
599
            # Raise an error when a record is missing.
 
600
            if record.storage_kind == 'absent':
 
601
                raise errors.RevisionNotPresent([record.key[0]], self)
 
602
            # adapt to non-tuple interface
 
603
            if record.storage_kind == 'fulltext':
 
604
                self.add_lines(record.key, None,
 
605
                    osutils.split_lines(record.get_bytes_as('fulltext')))
 
606
            else:
 
607
                adapter_key = record.storage_kind, 'fulltext'
 
608
                try:
 
609
                    adapter = adapters[adapter_key]
 
610
                except KeyError:
 
611
                    adapter_factory = adapter_registry.get(adapter_key)
 
612
                    adapter = adapter_factory(self)
 
613
                    adapters[adapter_key] = adapter
 
614
                lines = osutils.split_lines(adapter.get_bytes(
 
615
                    record, record.get_bytes_as(record.storage_kind)))
 
616
                try:
 
617
                    self.add_lines(record.key, None, lines)
 
618
                except RevisionAlreadyPresent:
 
619
                    pass
 
620
 
 
621
    def _load_text(self, key):
 
622
        if not self._is_locked():
 
623
            raise errors.ObjectNotLocked(self)
 
624
        path = self._map(key)
 
625
        try:
 
626
            text = self._transport.get_bytes(path)
 
627
            compressed = self._compressed
 
628
        except errors.NoSuchFile:
 
629
            if self._compressed:
 
630
                # try without the .gz
 
631
                path = path[:-3]
 
632
                try:
 
633
                    text = self._transport.get_bytes(path)
 
634
                    compressed = False
 
635
                except errors.NoSuchFile:
 
636
                    return None
 
637
            else:
 
638
                return None
 
639
        if compressed:
 
640
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
 
641
        return text
 
642
 
 
643
    def _map(self, key):
 
644
        return self._mapper.map(key) + self._ext
 
645
 
 
646
 
 
647
class RevisionTextStore(TextVersionedFiles):
 
648
    """Legacy thunk for format 4 repositories."""
 
649
 
 
650
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
 
651
        can_write):
 
652
        """Create a RevisionTextStore at transport with serializer."""
 
653
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
654
            is_locked, can_write)
 
655
        self._serializer = serializer
 
656
 
 
657
    def _load_text_parents(self, key):
 
658
        text = self._load_text(key)
 
659
        if text is None:
 
660
            return None, None
 
661
        parents = self._serializer.read_revision_from_string(text).parent_ids
 
662
        return text, tuple((parent,) for parent in parents)
 
663
 
 
664
    def get_parent_map(self, keys):
 
665
        result = {}
 
666
        for key in keys:
 
667
            parents = self._load_text_parents(key)[1]
 
668
            if parents is None:
 
669
                continue
 
670
            result[key] = parents
 
671
        return result
 
672
 
 
673
    def get_known_graph_ancestry(self, keys):
 
674
        """Get a KnownGraph instance with the ancestry of keys."""
 
675
        keys = self.keys()
 
676
        parent_map = self.get_parent_map(keys)
 
677
        kg = _mod_graph.KnownGraph(parent_map)
 
678
        return kg
 
679
 
 
680
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
681
        for key in keys:
 
682
            text, parents = self._load_text_parents(key)
 
683
            if text is None:
 
684
                yield AbsentContentFactory(key)
 
685
            else:
 
686
                yield FulltextContentFactory(key, parents, None, text)
 
687
 
 
688
    def keys(self):
 
689
        if not self._is_locked():
 
690
            raise errors.ObjectNotLocked(self)
 
691
        relpaths = set()
 
692
        for quoted_relpath in self._transport.iter_files_recursive():
 
693
            relpath = urllib.unquote(quoted_relpath)
 
694
            path, ext = os.path.splitext(relpath)
 
695
            if ext == '.gz':
 
696
                relpath = path
 
697
            if '.sig' not in relpath:
 
698
                relpaths.add(relpath)
 
699
        paths = list(relpaths)
 
700
        return set([self._mapper.unmap(path) for path in paths])
 
701
 
 
702
 
 
703
class SignatureTextStore(TextVersionedFiles):
 
704
    """Legacy thunk for format 4-7 repositories."""
 
705
 
 
706
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
707
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
708
            is_locked, can_write)
 
709
        self._ext = '.sig' + self._ext
 
710
 
 
711
    def get_parent_map(self, keys):
 
712
        result = {}
 
713
        for key in keys:
 
714
            text = self._load_text(key)
 
715
            if text is None:
 
716
                continue
 
717
            result[key] = None
 
718
        return result
 
719
 
 
720
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
721
        for key in keys:
 
722
            text = self._load_text(key)
 
723
            if text is None:
 
724
                yield AbsentContentFactory(key)
 
725
            else:
 
726
                yield FulltextContentFactory(key, None, None, text)
 
727
 
 
728
    def keys(self):
 
729
        if not self._is_locked():
 
730
            raise errors.ObjectNotLocked(self)
 
731
        relpaths = set()
 
732
        for quoted_relpath in self._transport.iter_files_recursive():
 
733
            relpath = urllib.unquote(quoted_relpath)
 
734
            path, ext = os.path.splitext(relpath)
 
735
            if ext == '.gz':
 
736
                relpath = path
 
737
            if not relpath.endswith('.sig'):
 
738
                continue
 
739
            relpaths.add(relpath[:-4])
 
740
        paths = list(relpaths)
 
741
        return set([self._mapper.unmap(path) for path in paths])
 
742
 
 
743
_legacy_formats = [RepositoryFormat4(),
 
744
                   RepositoryFormat5(),
 
745
                   RepositoryFormat6()]