~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-26 22:14:42 UTC
  • mto: This revision was merged to the branch mainline in revision 3747.
  • Revision ID: john@arbash-meinel.com-20080926221442-3r67j99sr9rwe9w0
Make message optional, don't check the memory flag directly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
 
18
 
"""Old weave-based repository formats"""
19
 
 
20
 
from StringIO import StringIO
 
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
21
26
 
22
27
from bzrlib import (
23
28
    bzrdir,
 
29
    debug,
 
30
    errors,
24
31
    lockable_files,
25
32
    lockdir,
 
33
    osutils,
 
34
    revision as _mod_revision,
 
35
    versionedfile,
26
36
    weave,
27
37
    weavefile,
28
38
    xml5,
29
39
    )
30
40
from bzrlib.decorators import needs_read_lock, needs_write_lock
31
41
from bzrlib.repository import (
32
 
    MetaDirRepository,
 
42
    CommitBuilder,
 
43
    MetaDirVersionedFileRepository,
33
44
    MetaDirRepositoryFormat,
34
45
    Repository,
35
46
    RepositoryFormat,
36
47
    )
37
48
from bzrlib.store.text import TextStore
38
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
    )
39
56
 
40
57
 
41
58
class AllInOneRepository(Repository):
43
60
 
44
61
    _serializer = xml5.serializer_v5
45
62
 
46
 
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
 
63
    def __init__(self, _format, a_bzrdir):
47
64
        # we reuse one control files instance.
48
 
        dir_mode = a_bzrdir._control_files._dir_mode
49
 
        file_mode = a_bzrdir._control_files._file_mode
 
65
        dir_mode = a_bzrdir._get_dir_mode()
 
66
        file_mode = a_bzrdir._get_file_mode()
50
67
 
51
68
        def get_store(name, compressed=True, prefixed=False):
52
69
            # FIXME: This approach of assuming stores are all entirely compressed
54
71
            # some existing branches where there's a mixture; we probably 
55
72
            # still want the option to look for both.
56
73
            relpath = a_bzrdir._control_files._escape(name)
57
 
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
 
74
            store = TextStore(a_bzrdir.transport.clone(relpath),
58
75
                              prefixed=prefixed, compressed=compressed,
59
76
                              dir_mode=dir_mode,
60
77
                              file_mode=file_mode)
61
 
            #if self._transport.should_cache():
62
 
            #    cache_path = os.path.join(self.cache_root, name)
63
 
            #    os.mkdir(cache_path)
64
 
            #    store = bzrlib.store.CachedStore(store, cache_path)
65
78
            return store
66
79
 
67
80
        # not broken out yet because the controlweaves|inventory_store
68
 
        # and text_store | weave_store bits are still different.
 
81
        # and texts bits are still different.
69
82
        if isinstance(_format, RepositoryFormat4):
70
83
            # cannot remove these - there is still no consistent api 
71
84
            # which allows access to this old info.
72
85
            self.inventory_store = get_store('inventory-store')
73
 
            text_store = get_store('text-store')
74
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_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')
75
125
 
76
126
    def get_commit_builder(self, branch, parents, config, timestamp=None,
77
127
                           timezone=None, committer=None, revprops=None,
78
128
                           revision_id=None):
79
129
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
80
 
        return Repository.get_commit_builder(self, branch, parents, config,
81
 
            timestamp, timezone, committer, revprops, revision_id)
 
130
        result = CommitBuilder(self, parents, config, timestamp, timezone,
 
131
                              committer, revprops, revision_id)
 
132
        self.start_write_group()
 
133
        return result
82
134
 
83
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
 
84
151
    def is_shared(self):
85
152
        """AllInOne repositories cannot be shared."""
86
153
        return False
95
162
        :param new_value: True to restore the default, False to disable making
96
163
                          working trees.
97
164
        """
98
 
        raise NotImplementedError(self.set_make_working_trees)
99
 
    
 
165
        raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
 
166
 
100
167
    def make_working_trees(self):
101
168
        """Returns the policy for making working trees on new branches."""
102
169
        return True
103
170
 
104
 
 
105
 
class WeaveMetaDirRepository(MetaDirRepository):
 
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):
106
178
    """A subclass of MetaDirRepository to set weave specific policy."""
107
179
 
108
180
    _serializer = xml5.serializer_v5
109
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
        self._fetch_reconcile = True
 
186
 
 
187
    @needs_read_lock
 
188
    def _all_possible_ids(self):
 
189
        """Return all the possible revisions that we could find."""
 
190
        if 'evil' in debug.debug_flags:
 
191
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
192
        return [key[-1] for key in self.inventories.keys()]
 
193
 
 
194
    @needs_read_lock
 
195
    def _all_revision_ids(self):
 
196
        """Returns a list of all the revision ids in the repository. 
 
197
 
 
198
        These are in as much topological order as the underlying store can 
 
199
        present: for weaves ghosts may lead to a lack of correctness until
 
200
        the reweave updates the parents list.
 
201
        """
 
202
        return [key[-1] for key in self.revisions.keys()]
 
203
 
 
204
    def _activate_new_inventory(self):
 
205
        """Put a replacement inventory.new into use as inventories."""
 
206
        # Copy the content across
 
207
        t = self._transport
 
208
        t.copy('inventory.new.weave', 'inventory.weave')
 
209
        # delete the temp inventory
 
210
        t.delete('inventory.new.weave')
 
211
        # Check we can parse the new weave properly as a sanity check
 
212
        self.inventories.keys()
 
213
 
 
214
    def _backup_inventory(self):
 
215
        t = self._transport
 
216
        t.copy('inventory.weave', 'inventory.backup.weave')
 
217
 
 
218
    def _temp_inventories(self):
 
219
        t = self._transport
 
220
        return self._format._get_inventories(t, self, 'inventory.new')
 
221
 
110
222
    def get_commit_builder(self, branch, parents, config, timestamp=None,
111
223
                           timezone=None, committer=None, revprops=None,
112
224
                           revision_id=None):
113
225
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
114
 
        return MetaDirRepository.get_commit_builder(self, branch, parents,
115
 
            config, timestamp, timezone, committer, revprops, revision_id)
 
226
        result = CommitBuilder(self, parents, config, timestamp, timezone,
 
227
                              committer, revprops, revision_id)
 
228
        self.start_write_group()
 
229
        return result
 
230
 
 
231
    @needs_read_lock
 
232
    def get_revision(self, revision_id):
 
233
        """Return the Revision object for a named revision"""
 
234
        r = self.get_revision_reconcile(revision_id)
 
235
        return r
 
236
 
 
237
    def _inventory_add_lines(self, revision_id, parents, lines,
 
238
        check_content=True):
 
239
        """Store lines in inv_vf and return the sha1 of the inventory."""
 
240
        present_parents = self.get_graph().get_parent_map(parents)
 
241
        final_parents = []
 
242
        for parent in parents:
 
243
            if parent in present_parents:
 
244
                final_parents.append((parent,))
 
245
        return self.inventories.add_lines((revision_id,), final_parents, lines,
 
246
            check_content=check_content)[0]
 
247
 
 
248
    def revision_graph_can_have_wrong_parents(self):
 
249
        return False
116
250
 
117
251
 
118
252
class PreSplitOutRepositoryFormat(RepositoryFormat):
120
254
 
121
255
    rich_root_data = False
122
256
    supports_tree_reference = False
 
257
    supports_ghosts = False
 
258
    supports_external_lookups = False
123
259
 
124
260
    def initialize(self, a_bzrdir, shared=False, _internal=False):
125
 
        """Create a weave repository.
126
 
        
127
 
        TODO: when creating split out bzr branch formats, move this to a common
128
 
        base for Format5, Format6. or something like that.
129
 
        """
 
261
        """Create a weave repository."""
130
262
        if shared:
131
263
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
132
264
 
140
272
        empty_weave = sio.getvalue()
141
273
 
142
274
        mutter('creating repository in %s.', a_bzrdir.transport.base)
143
 
        dirs = ['revision-store', 'weaves']
144
 
        files = [('inventory.weave', StringIO(empty_weave)),
145
 
                 ]
146
275
        
147
276
        # FIXME: RBC 20060125 don't peek under the covers
148
277
        # NB: no need to escape relative paths that are url safe.
149
278
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
150
 
                                'branch-lock', lockable_files.TransportLock)
 
279
            'branch-lock', lockable_files.TransportLock)
151
280
        control_files.create_lock()
152
281
        control_files.lock_write()
153
 
        control_files._transport.mkdir_multi(dirs,
154
 
                mode=control_files._dir_mode)
 
282
        transport = a_bzrdir.transport
155
283
        try:
156
 
            for file, content in files:
157
 
                control_files.put(file, content)
 
284
            transport.mkdir_multi(['revision-store', 'weaves'],
 
285
                mode=a_bzrdir._get_dir_mode())
 
286
            transport.put_bytes_non_atomic('inventory.weave', empty_weave)
158
287
        finally:
159
288
            control_files.unlock()
160
289
        return self.open(a_bzrdir, _found=True)
161
290
 
162
 
    def _get_control_store(self, repo_transport, control_files):
163
 
        """Return the control store for this repository."""
164
 
        return self._get_versioned_file_store('',
165
 
                                              repo_transport,
166
 
                                              control_files,
167
 
                                              prefixed=False)
168
 
 
169
 
    def _get_text_store(self, transport, control_files):
170
 
        """Get a store for file texts for this format."""
171
 
        raise NotImplementedError(self._get_text_store)
172
 
 
173
291
    def open(self, a_bzrdir, _found=False):
174
292
        """See RepositoryFormat.open()."""
175
293
        if not _found:
178
296
 
179
297
        repo_transport = a_bzrdir.get_repository_transport(None)
180
298
        control_files = a_bzrdir._control_files
181
 
        text_store = self._get_text_store(repo_transport, control_files)
182
 
        control_store = self._get_control_store(repo_transport, control_files)
183
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
184
 
        return AllInOneRepository(_format=self,
185
 
                                  a_bzrdir=a_bzrdir,
186
 
                                  _revision_store=_revision_store,
187
 
                                  control_store=control_store,
188
 
                                  text_store=text_store)
 
299
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
 
300
        result.revisions = self._get_revisions(repo_transport, result)
 
301
        result.signatures = self._get_signatures(repo_transport, result)
 
302
        result.inventories = self._get_inventories(repo_transport, result)
 
303
        result.texts = self._get_texts(repo_transport, result)
 
304
        return result
189
305
 
190
306
    def check_conversion_target(self, target_format):
191
307
        pass
207
323
 
208
324
    def __init__(self):
209
325
        super(RepositoryFormat4, self).__init__()
 
326
        self._fetch_order = 'topological'
 
327
        self._fetch_reconcile = True
210
328
 
211
329
    def get_format_description(self):
212
330
        """See RepositoryFormat.get_format_description()."""
225
343
        """
226
344
        return False
227
345
 
228
 
    def _get_control_store(self, repo_transport, control_files):
229
 
        """Format 4 repositories have no formal control store at this point.
230
 
        
231
 
        This will cause any control-file-needing apis to fail - this is desired.
232
 
        """
 
346
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
347
        # No inventories store written so far.
233
348
        return None
234
 
    
235
 
    def _get_revision_store(self, repo_transport, control_files):
236
 
        """See RepositoryFormat._get_revision_store()."""
 
349
 
 
350
    def _get_revisions(self, repo_transport, repo):
237
351
        from bzrlib.xml4 import serializer_v4
238
 
        return self._get_text_rev_store(repo_transport,
239
 
                                        control_files,
240
 
                                        'revision-store',
241
 
                                        serializer=serializer_v4)
242
 
 
243
 
    def _get_text_store(self, transport, control_files):
244
 
        """See RepositoryFormat._get_text_store()."""
 
352
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
353
            serializer_v4, True, versionedfile.PrefixMapper(),
 
354
            repo.is_locked, repo.is_write_locked)
 
355
 
 
356
    def _get_signatures(self, repo_transport, repo):
 
357
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
358
            False, versionedfile.PrefixMapper(),
 
359
            repo.is_locked, repo.is_write_locked)
 
360
 
 
361
    def _get_texts(self, repo_transport, repo):
 
362
        return None
245
363
 
246
364
 
247
365
class RepositoryFormat5(PreSplitOutRepositoryFormat):
258
376
 
259
377
    def __init__(self):
260
378
        super(RepositoryFormat5, self).__init__()
 
379
        self._fetch_order = 'topological'
 
380
        self._fetch_reconcile = True
261
381
 
262
382
    def get_format_description(self):
263
383
        """See RepositoryFormat.get_format_description()."""
264
384
        return "Weave repository format 5"
265
385
 
266
 
    def _get_revision_store(self, repo_transport, control_files):
267
 
        """See RepositoryFormat._get_revision_store()."""
268
 
        """Return the revision store object for this a_bzrdir."""
269
 
        return self._get_text_rev_store(repo_transport,
270
 
                                        control_files,
271
 
                                        'revision-store',
272
 
                                        compressed=False)
273
 
 
274
 
    def _get_text_store(self, transport, control_files):
275
 
        """See RepositoryFormat._get_text_store()."""
276
 
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
 
386
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
387
        mapper = versionedfile.ConstantMapper(name)
 
388
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
389
            weave.WeaveFile, mapper, repo.is_locked)
 
390
 
 
391
    def _get_revisions(self, repo_transport, repo):
 
392
        from bzrlib.xml5 import serializer_v5
 
393
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
394
            serializer_v5, False, versionedfile.PrefixMapper(),
 
395
            repo.is_locked, repo.is_write_locked)
 
396
 
 
397
    def _get_signatures(self, repo_transport, repo):
 
398
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
399
            False, versionedfile.PrefixMapper(),
 
400
            repo.is_locked, repo.is_write_locked)
 
401
 
 
402
    def _get_texts(self, repo_transport, repo):
 
403
        mapper = versionedfile.PrefixMapper()
 
404
        base_transport = repo_transport.clone('weaves')
 
405
        return versionedfile.ThunkedVersionedFiles(base_transport,
 
406
            weave.WeaveFile, mapper, repo.is_locked)
277
407
 
278
408
 
279
409
class RepositoryFormat6(PreSplitOutRepositoryFormat):
290
420
 
291
421
    def __init__(self):
292
422
        super(RepositoryFormat6, self).__init__()
 
423
        self._fetch_order = 'topological'
 
424
        self._fetch_reconcile = True
293
425
 
294
426
    def get_format_description(self):
295
427
        """See RepositoryFormat.get_format_description()."""
296
428
        return "Weave repository format 6"
297
429
 
298
 
    def _get_revision_store(self, repo_transport, control_files):
299
 
        """See RepositoryFormat._get_revision_store()."""
300
 
        return self._get_text_rev_store(repo_transport,
301
 
                                        control_files,
302
 
                                        'revision-store',
303
 
                                        compressed=False,
304
 
                                        prefixed=True)
305
 
 
306
 
    def _get_text_store(self, transport, control_files):
307
 
        """See RepositoryFormat._get_text_store()."""
308
 
        return self._get_versioned_file_store('weaves', transport, control_files)
 
430
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
431
        mapper = versionedfile.ConstantMapper(name)
 
432
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
433
            weave.WeaveFile, mapper, repo.is_locked)
 
434
 
 
435
    def _get_revisions(self, repo_transport, repo):
 
436
        from bzrlib.xml5 import serializer_v5
 
437
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
438
            serializer_v5, False, versionedfile.HashPrefixMapper(),
 
439
            repo.is_locked, repo.is_write_locked)
 
440
 
 
441
    def _get_signatures(self, repo_transport, repo):
 
442
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
443
            False, versionedfile.HashPrefixMapper(),
 
444
            repo.is_locked, repo.is_write_locked)
 
445
 
 
446
    def _get_texts(self, repo_transport, repo):
 
447
        mapper = versionedfile.HashPrefixMapper()
 
448
        base_transport = repo_transport.clone('weaves')
 
449
        return versionedfile.ThunkedVersionedFiles(base_transport,
 
450
            weave.WeaveFile, mapper, repo.is_locked)
309
451
 
310
452
 
311
453
class RepositoryFormat7(MetaDirRepositoryFormat):
321
463
    """
322
464
 
323
465
    _versionedfile_class = weave.WeaveFile
324
 
 
325
 
    def _get_control_store(self, repo_transport, control_files):
326
 
        """Return the control store for this repository."""
327
 
        return self._get_versioned_file_store('',
328
 
                                              repo_transport,
329
 
                                              control_files,
330
 
                                              prefixed=False)
 
466
    supports_ghosts = False
331
467
 
332
468
    def get_format_string(self):
333
469
        """See RepositoryFormat.get_format_string()."""
340
476
    def check_conversion_target(self, target_format):
341
477
        pass
342
478
 
343
 
    def _get_revision_store(self, repo_transport, control_files):
344
 
        """See RepositoryFormat._get_revision_store()."""
345
 
        return self._get_text_rev_store(repo_transport,
346
 
                                        control_files,
347
 
                                        'revision-store',
348
 
                                        compressed=False,
349
 
                                        prefixed=True,
350
 
                                        )
351
 
 
352
 
    def _get_text_store(self, transport, control_files):
353
 
        """See RepositoryFormat._get_text_store()."""
354
 
        return self._get_versioned_file_store('weaves',
355
 
                                              transport,
356
 
                                              control_files)
 
479
    def _get_inventories(self, repo_transport, repo, name='inventory'):
 
480
        mapper = versionedfile.ConstantMapper(name)
 
481
        return versionedfile.ThunkedVersionedFiles(repo_transport,
 
482
            weave.WeaveFile, mapper, repo.is_locked)
 
483
 
 
484
    def _get_revisions(self, repo_transport, repo):
 
485
        from bzrlib.xml5 import serializer_v5
 
486
        return RevisionTextStore(repo_transport.clone('revision-store'),
 
487
            serializer_v5, True, versionedfile.HashPrefixMapper(),
 
488
            repo.is_locked, repo.is_write_locked)
 
489
 
 
490
    def _get_signatures(self, repo_transport, repo):
 
491
        return SignatureTextStore(repo_transport.clone('revision-store'),
 
492
            True, versionedfile.HashPrefixMapper(),
 
493
            repo.is_locked, repo.is_write_locked)
 
494
 
 
495
    def _get_texts(self, repo_transport, repo):
 
496
        mapper = versionedfile.HashPrefixMapper()
 
497
        base_transport = repo_transport.clone('weaves')
 
498
        return versionedfile.ThunkedVersionedFiles(base_transport,
 
499
            weave.WeaveFile, mapper, repo.is_locked)
357
500
 
358
501
    def initialize(self, a_bzrdir, shared=False):
359
502
        """Create a weave repository.
384
527
        """
385
528
        if not _found:
386
529
            format = RepositoryFormat.find_format(a_bzrdir)
387
 
            assert format.__class__ ==  self.__class__
388
530
        if _override_transport is not None:
389
531
            repo_transport = _override_transport
390
532
        else:
391
533
            repo_transport = a_bzrdir.get_repository_transport(None)
392
534
        control_files = lockable_files.LockableFiles(repo_transport,
393
535
                                'lock', lockdir.LockDir)
394
 
        text_store = self._get_text_store(repo_transport, control_files)
395
 
        control_store = self._get_control_store(repo_transport, control_files)
396
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
397
 
        return WeaveMetaDirRepository(_format=self,
398
 
            a_bzrdir=a_bzrdir,
399
 
            control_files=control_files,
400
 
            _revision_store=_revision_store,
401
 
            control_store=control_store,
402
 
            text_store=text_store)
403
 
 
 
536
        result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
 
537
            control_files=control_files)
 
538
        result.revisions = self._get_revisions(repo_transport, result)
 
539
        result.signatures = self._get_signatures(repo_transport, result)
 
540
        result.inventories = self._get_inventories(repo_transport, result)
 
541
        result.texts = self._get_texts(repo_transport, result)
 
542
        result._transport = repo_transport
 
543
        return result
 
544
 
 
545
 
 
546
class TextVersionedFiles(VersionedFiles):
 
547
    """Just-a-bunch-of-files based VersionedFile stores."""
 
548
 
 
549
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
550
        self._compressed = compressed
 
551
        self._transport = transport
 
552
        self._mapper = mapper
 
553
        if self._compressed:
 
554
            self._ext = '.gz'
 
555
        else:
 
556
            self._ext = ''
 
557
        self._is_locked = is_locked
 
558
        self._can_write = can_write
 
559
 
 
560
    def add_lines(self, key, parents, lines):
 
561
        """Add a revision to the store."""
 
562
        if not self._is_locked():
 
563
            raise errors.ObjectNotLocked(self)
 
564
        if not self._can_write():
 
565
            raise errors.ReadOnlyError(self)
 
566
        if '/' in key[-1]:
 
567
            raise ValueError('bad idea to put / in %r' % (key,))
 
568
        text = ''.join(lines)
 
569
        if self._compressed:
 
570
            text = bytes_to_gzip(text)
 
571
        path = self._map(key)
 
572
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
 
573
 
 
574
    def insert_record_stream(self, stream):
 
575
        adapters = {}
 
576
        for record in stream:
 
577
            # Raise an error when a record is missing.
 
578
            if record.storage_kind == 'absent':
 
579
                raise errors.RevisionNotPresent([record.key[0]], self)
 
580
            # adapt to non-tuple interface
 
581
            if record.storage_kind == 'fulltext':
 
582
                self.add_lines(record.key, None,
 
583
                    osutils.split_lines(record.get_bytes_as('fulltext')))
 
584
            else:
 
585
                adapter_key = record.storage_kind, 'fulltext'
 
586
                try:
 
587
                    adapter = adapters[adapter_key]
 
588
                except KeyError:
 
589
                    adapter_factory = adapter_registry.get(adapter_key)
 
590
                    adapter = adapter_factory(self)
 
591
                    adapters[adapter_key] = adapter
 
592
                lines = osutils.split_lines(adapter.get_bytes(
 
593
                    record, record.get_bytes_as(record.storage_kind)))
 
594
                try:
 
595
                    self.add_lines(record.key, None, lines)
 
596
                except RevisionAlreadyPresent:
 
597
                    pass
 
598
 
 
599
    def _load_text(self, key):
 
600
        if not self._is_locked():
 
601
            raise errors.ObjectNotLocked(self)
 
602
        path = self._map(key)
 
603
        try:
 
604
            text = self._transport.get_bytes(path)
 
605
            compressed = self._compressed
 
606
        except errors.NoSuchFile:
 
607
            if self._compressed:
 
608
                # try without the .gz
 
609
                path = path[:-3]
 
610
                try:
 
611
                    text = self._transport.get_bytes(path)
 
612
                    compressed = False
 
613
                except errors.NoSuchFile:
 
614
                    return None
 
615
            else:
 
616
                return None
 
617
        if compressed:
 
618
            text = GzipFile(mode='rb', fileobj=StringIO(text)).read()
 
619
        return text
 
620
 
 
621
    def _map(self, key):
 
622
        return self._mapper.map(key) + self._ext
 
623
 
 
624
 
 
625
class RevisionTextStore(TextVersionedFiles):
 
626
    """Legacy thunk for format 4 repositories."""
 
627
 
 
628
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
 
629
        can_write):
 
630
        """Create a RevisionTextStore at transport with serializer."""
 
631
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
632
            is_locked, can_write)
 
633
        self._serializer = serializer
 
634
 
 
635
    def _load_text_parents(self, key):
 
636
        text = self._load_text(key)
 
637
        if text is None:
 
638
            return None, None
 
639
        parents = self._serializer.read_revision_from_string(text).parent_ids
 
640
        return text, tuple((parent,) for parent in parents)
 
641
 
 
642
    def get_parent_map(self, keys):
 
643
        result = {}
 
644
        for key in keys:
 
645
            parents = self._load_text_parents(key)[1]
 
646
            if parents is None:
 
647
                continue
 
648
            result[key] = parents
 
649
        return result
 
650
    
 
651
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
652
        for key in keys:
 
653
            text, parents = self._load_text_parents(key)
 
654
            if text is None:
 
655
                yield AbsentContentFactory(key)
 
656
            else:
 
657
                yield FulltextContentFactory(key, parents, None, text)
 
658
 
 
659
    def keys(self):
 
660
        if not self._is_locked():
 
661
            raise errors.ObjectNotLocked(self)
 
662
        relpaths = set()
 
663
        for quoted_relpath in self._transport.iter_files_recursive():
 
664
            relpath = urllib.unquote(quoted_relpath)
 
665
            path, ext = os.path.splitext(relpath)
 
666
            if ext == '.gz':
 
667
                relpath = path
 
668
            if '.sig' not in relpath:
 
669
                relpaths.add(relpath)
 
670
        paths = list(relpaths)
 
671
        return set([self._mapper.unmap(path) for path in paths])
 
672
 
 
673
 
 
674
class SignatureTextStore(TextVersionedFiles):
 
675
    """Legacy thunk for format 4-7 repositories."""
 
676
 
 
677
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
 
678
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
 
679
            is_locked, can_write)
 
680
        self._ext = '.sig' + self._ext
 
681
 
 
682
    def get_parent_map(self, keys):
 
683
        result = {}
 
684
        for key in keys:
 
685
            text = self._load_text(key)
 
686
            if text is None:
 
687
                continue
 
688
            result[key] = None
 
689
        return result
 
690
    
 
691
    def get_record_stream(self, keys, sort_order, include_delta_closure):
 
692
        for key in keys:
 
693
            text = self._load_text(key)
 
694
            if text is None:
 
695
                yield AbsentContentFactory(key)
 
696
            else:
 
697
                yield FulltextContentFactory(key, None, None, text)
 
698
 
 
699
    def keys(self):
 
700
        if not self._is_locked():
 
701
            raise errors.ObjectNotLocked(self)
 
702
        relpaths = set()
 
703
        for quoted_relpath in self._transport.iter_files_recursive():
 
704
            relpath = urllib.unquote(quoted_relpath)
 
705
            path, ext = os.path.splitext(relpath)
 
706
            if ext == '.gz':
 
707
                relpath = path
 
708
            if not relpath.endswith('.sig'):
 
709
                continue
 
710
            relpaths.add(relpath[:-4])
 
711
        paths = list(relpaths)
 
712
        return set([self._mapper.unmap(path) for path in paths])
404
713
 
405
714
_legacy_formats = [RepositoryFormat4(),
406
715
                   RepositoryFormat5(),