~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Jelmer Vernooij
  • Date: 2009-01-28 18:42:55 UTC
  • mto: This revision was merged to the branch mainline in revision 3968.
  • Revision ID: jelmer@samba.org-20090128184255-bdmklkvm83ltk191
Update NEWS

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