/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: Canonical.com Patch Queue Manager
  • Date: 2009-02-20 02:25:09 UTC
  • mfrom: (3990.5.4 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090220022509-leun2dkfewbwcgn7
(robertc) Add a network_name method to RepositoryFormat for use with
        communicating with smart servers. (Andrew Bennetts, Robert Collins)

Show diffs side-by-side

added added

removed removed

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