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