~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Martin Pool
  • Date: 2007-07-11 01:55:33 UTC
  • mto: This revision was merged to the branch mainline in revision 2599.
  • Revision ID: mbp@sourcefrog.net-20070711015533-dzcxkjg0ujh8yuhl
Option help improvements (thanks jamesw)

Show diffs side-by-side

added added

removed removed

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