/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-07-29 08:40:05 UTC
  • mfrom: (3535.7.1 bzr)
  • Revision ID: pqm@pqm.ubuntu.com-20080729084005-ohq1fb6mheyeaqr6
Improved 'Using saved location' message for 'bzr send'. (Toni Ruottu)

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