/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: Ian Clatworthy
  • Date: 2009-03-17 06:18:30 UTC
  • mto: (4157.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 4158.
  • Revision ID: ian.clatworthy@canonical.com-20090317061830-9glppr51ggivnrgl
Show usage on --usage, not -h

Show diffs side-by-side

added added

removed removed

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