/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: 2008-07-17 00:41:25 UTC
  • mto: This revision was merged to the branch mainline in revision 3545.
  • Revision ID: ian.clatworthy@canonical.com-20080717004125-xqq4jhlh9ldxgg7a
fix how cog.py is invoked on Windows (Mark Hammond)

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