/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

Move config to a separate file, support BranchConfig.username().

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