~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

Fix test_upgrade defects related to non local or absent working trees.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 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 gzip
24
 
import os
25
 
from cStringIO import StringIO
26
 
import urllib
27
 
 
28
 
from bzrlib.lazy_import import lazy_import
29
 
lazy_import(globals(), """
30
 
import itertools
31
 
 
32
 
from bzrlib import (
33
 
    xml5,
34
 
    graph as _mod_graph,
35
 
    ui,
36
 
    )
37
 
""")
38
 
from bzrlib import (
39
 
    debug,
40
 
    errors,
 
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
 
 
22
from bzrlib import (
 
23
    bzrdir,
41
24
    lockable_files,
42
25
    lockdir,
43
 
    osutils,
44
 
    symbol_versioning,
45
 
    trace,
46
 
    tuned_gzip,
47
 
    urlutils,
48
 
    versionedfile,
49
26
    weave,
50
27
    weavefile,
 
28
    xml5,
51
29
    )
52
30
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
31
from bzrlib.repository import (
54
 
    InterRepository,
 
32
    MetaDirRepository,
 
33
    MetaDirRepositoryFormat,
 
34
    Repository,
55
35
    RepositoryFormat,
56
36
    )
57
37
from bzrlib.store.text import TextStore
58
 
from bzrlib.versionedfile import (
59
 
    AbsentContentFactory,
60
 
    FulltextContentFactory,
61
 
    VersionedFiles,
62
 
    )
63
 
from bzrlib.vf_repository import (
64
 
    InterSameDataRepository,
65
 
    VersionedFileCommitBuilder,
66
 
    VersionedFileRepository,
67
 
    VersionedFileRepositoryFormat,
68
 
    MetaDirVersionedFileRepository,
69
 
    MetaDirVersionedFileRepositoryFormat,
70
 
    )
71
 
 
72
 
from bzrlib.plugins.weave_fmt import bzrdir as weave_bzrdir
73
 
 
74
 
 
75
 
class AllInOneRepository(VersionedFileRepository):
 
38
from bzrlib.trace import mutter
 
39
 
 
40
 
 
41
class AllInOneRepository(Repository):
76
42
    """Legacy support - the repository behaviour for all-in-one branches."""
77
43
 
78
 
    @property
79
 
    def _serializer(self):
80
 
        return xml5.serializer_v5
81
 
 
82
 
    def _escape(self, file_or_path):
83
 
        if not isinstance(file_or_path, basestring):
84
 
            file_or_path = '/'.join(file_or_path)
85
 
        if file_or_path == '':
86
 
            return u''
87
 
        return urlutils.escape(osutils.safe_unicode(file_or_path))
88
 
 
89
 
    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):
90
47
        # we reuse one control files instance.
91
 
        dir_mode = a_bzrdir._get_dir_mode()
92
 
        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
93
50
 
94
51
        def get_store(name, compressed=True, prefixed=False):
95
52
            # FIXME: This approach of assuming stores are all entirely compressed
96
 
            # or entirely uncompressed is tidy, but breaks upgrade from
97
 
            # 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 
98
55
            # still want the option to look for both.
99
 
            relpath = self._escape(name)
100
 
            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),
101
58
                              prefixed=prefixed, compressed=compressed,
102
59
                              dir_mode=dir_mode,
103
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)
104
65
            return store
105
66
 
106
67
        # not broken out yet because the controlweaves|inventory_store
107
 
        # and texts bits are still different.
 
68
        # and text_store | weave_store bits are still different.
108
69
        if isinstance(_format, RepositoryFormat4):
109
 
            # cannot remove these - there is still no consistent api
 
70
            # cannot remove these - there is still no consistent api 
110
71
            # which allows access to this old info.
111
72
            self.inventory_store = get_store('inventory-store')
112
 
            self._text_store = get_store('text-store')
113
 
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files)
114
 
 
115
 
    @needs_read_lock
116
 
    def _all_possible_ids(self):
117
 
        """Return all the possible revisions that we could find."""
118
 
        if 'evil' in debug.debug_flags:
119
 
            trace.mutter_callsite(
120
 
                3, "_all_possible_ids scales with size of history.")
121
 
        return [key[-1] for key in self.inventories.keys()]
122
 
 
123
 
    @needs_read_lock
124
 
    def _all_revision_ids(self):
125
 
        """Returns a list of all the revision ids in the repository.
126
 
 
127
 
        These are in as much topological order as the underlying store can
128
 
        present: for weaves ghosts may lead to a lack of correctness until
129
 
        the reweave updates the parents list.
130
 
        """
131
 
        return [key[-1] for key in self.revisions.keys()]
132
 
 
133
 
    def _activate_new_inventory(self):
134
 
        """Put a replacement inventory.new into use as inventories."""
135
 
        # Copy the content across
136
 
        t = self.bzrdir._control_files._transport
137
 
        t.copy('inventory.new.weave', 'inventory.weave')
138
 
        # delete the temp inventory
139
 
        t.delete('inventory.new.weave')
140
 
        # Check we can parse the new weave properly as a sanity check
141
 
        self.inventories.keys()
142
 
 
143
 
    def _backup_inventory(self):
144
 
        t = self.bzrdir._control_files._transport
145
 
        t.copy('inventory.weave', 'inventory.backup.weave')
146
 
 
147
 
    def _temp_inventories(self):
148
 
        t = self.bzrdir._control_files._transport
149
 
        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)
150
75
 
151
76
    def get_commit_builder(self, branch, parents, config, timestamp=None,
152
77
                           timezone=None, committer=None, revprops=None,
153
 
                           revision_id=None, lossy=False):
 
78
                           revision_id=None):
154
79
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
155
 
        result = VersionedFileCommitBuilder(self, parents, config, timestamp,
156
 
            timezone, committer, revprops, revision_id, lossy=lossy)
157
 
        self.start_write_group()
158
 
        return result
 
80
        return Repository.get_commit_builder(self, branch, parents, config,
 
81
            timestamp, timezone, committer, revprops, revision_id)
159
82
 
160
83
    @needs_read_lock
161
 
    def get_revisions(self, revision_ids):
162
 
        revs = self._get_revisions(revision_ids)
163
 
        return revs
164
 
 
165
 
    def _inventory_add_lines(self, revision_id, parents, lines,
166
 
        check_content=True):
167
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
168
 
        present_parents = self.get_graph().get_parent_map(parents)
169
 
        final_parents = []
170
 
        for parent in parents:
171
 
            if parent in present_parents:
172
 
                final_parents.append((parent,))
173
 
        return self.inventories.add_lines((revision_id,), final_parents, lines,
174
 
            check_content=check_content)[0]
175
 
 
176
84
    def is_shared(self):
177
85
        """AllInOne repositories cannot be shared."""
178
86
        return False
187
95
        :param new_value: True to restore the default, False to disable making
188
96
                          working trees.
189
97
        """
190
 
        raise errors.RepositoryUpgradeRequired(self.user_url)
191
 
 
 
98
        raise NotImplementedError(self.set_make_working_trees)
 
99
    
192
100
    def make_working_trees(self):
193
101
        """Returns the policy for making working trees on new branches."""
194
102
        return True
195
103
 
196
104
 
197
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
 
105
class WeaveMetaDirRepository(MetaDirRepository):
198
106
    """A subclass of MetaDirRepository to set weave specific policy."""
199
107
 
200
 
    def __init__(self, _format, a_bzrdir, control_files):
201
 
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
202
 
        self._serializer = _format._serializer
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
 
            trace.mutter_callsite(
209
 
                3, "_all_possible_ids scales with size of history.")
210
 
        return [key[-1] for key in self.inventories.keys()]
211
 
 
212
 
    @needs_read_lock
213
 
    def _all_revision_ids(self):
214
 
        """Returns a list of all the revision ids in the repository.
215
 
 
216
 
        These are in as much topological order as the underlying store can
217
 
        present: for weaves ghosts may lead to a lack of correctness until
218
 
        the reweave updates the parents list.
219
 
        """
220
 
        return [key[-1] for key in self.revisions.keys()]
221
 
 
222
 
    def _activate_new_inventory(self):
223
 
        """Put a replacement inventory.new into use as inventories."""
224
 
        # Copy the content across
225
 
        t = self._transport
226
 
        t.copy('inventory.new.weave', 'inventory.weave')
227
 
        # delete the temp inventory
228
 
        t.delete('inventory.new.weave')
229
 
        # Check we can parse the new weave properly as a sanity check
230
 
        self.inventories.keys()
231
 
 
232
 
    def _backup_inventory(self):
233
 
        t = self._transport
234
 
        t.copy('inventory.weave', 'inventory.backup.weave')
235
 
 
236
 
    def _temp_inventories(self):
237
 
        t = self._transport
238
 
        return self._format._get_inventories(t, self, 'inventory.new')
 
108
    _serializer = xml5.serializer_v5
239
109
 
240
110
    def get_commit_builder(self, branch, parents, config, timestamp=None,
241
111
                           timezone=None, committer=None, revprops=None,
242
 
                           revision_id=None, lossy=False):
 
112
                           revision_id=None):
243
113
        self._check_ascii_revisionid(revision_id, self.get_commit_builder)
244
 
        result = VersionedFileCommitBuilder(self, parents, config, timestamp,
245
 
            timezone, committer, revprops, revision_id, lossy=lossy)
246
 
        self.start_write_group()
247
 
        return result
248
 
 
249
 
    @needs_read_lock
250
 
    def get_revision(self, revision_id):
251
 
        """Return the Revision object for a named revision"""
252
 
        r = self.get_revision_reconcile(revision_id)
253
 
        return r
254
 
 
255
 
    def _inventory_add_lines(self, revision_id, parents, lines,
256
 
        check_content=True):
257
 
        """Store lines in inv_vf and return the sha1 of the inventory."""
258
 
        present_parents = self.get_graph().get_parent_map(parents)
259
 
        final_parents = []
260
 
        for parent in parents:
261
 
            if parent in present_parents:
262
 
                final_parents.append((parent,))
263
 
        return self.inventories.add_lines((revision_id,), final_parents, lines,
264
 
            check_content=check_content)[0]
265
 
 
266
 
 
267
 
class PreSplitOutRepositoryFormat(VersionedFileRepositoryFormat):
 
114
        return MetaDirRepository.get_commit_builder(self, branch, parents,
 
115
            config, timestamp, timezone, committer, revprops, revision_id)
 
116
 
 
117
 
 
118
class PreSplitOutRepositoryFormat(RepositoryFormat):
268
119
    """Base class for the pre split out repository formats."""
269
120
 
270
121
    rich_root_data = False
271
 
    supports_tree_reference = False
272
 
    supports_ghosts = False
273
 
    supports_external_lookups = False
274
 
    supports_chks = False
275
 
    supports_nesting_repositories = True
276
 
    _fetch_order = 'topological'
277
 
    _fetch_reconcile = True
278
 
    fast_deltas = False
279
 
    supports_leaving_lock = False
280
 
    # XXX: This is an old format that we don't support full checking on, so
281
 
    # just claim that checking for this inconsistency is not required.
282
 
    revision_graph_can_have_wrong_parents = False
283
122
 
284
123
    def initialize(self, a_bzrdir, shared=False, _internal=False):
285
 
        """Create a weave repository."""
 
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
        """
286
129
        if shared:
287
130
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
288
131
 
289
132
        if not _internal:
290
133
            # always initialized when the bzrdir is.
291
134
            return self.open(a_bzrdir, _found=True)
292
 
 
 
135
        
293
136
        # Create an empty weave
294
137
        sio = StringIO()
295
138
        weavefile.write_weave_v5(weave.Weave(), sio)
296
139
        empty_weave = sio.getvalue()
297
140
 
298
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
299
 
 
 
141
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
142
        dirs = ['revision-store', 'weaves']
 
143
        files = [('inventory.weave', StringIO(empty_weave)),
 
144
                 ]
 
145
        
300
146
        # FIXME: RBC 20060125 don't peek under the covers
301
147
        # NB: no need to escape relative paths that are url safe.
302
148
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
303
 
            'branch-lock', lockable_files.TransportLock)
 
149
                                'branch-lock', lockable_files.TransportLock)
304
150
        control_files.create_lock()
305
151
        control_files.lock_write()
306
 
        transport = a_bzrdir.transport
 
152
        control_files._transport.mkdir_multi(dirs,
 
153
                mode=control_files._dir_mode)
307
154
        try:
308
 
            transport.mkdir_multi(['revision-store', 'weaves'],
309
 
                mode=a_bzrdir._get_dir_mode())
310
 
            transport.put_bytes_non_atomic('inventory.weave', empty_weave,
311
 
                mode=a_bzrdir._get_file_mode())
 
155
            for file, content in files:
 
156
                control_files.put(file, content)
312
157
        finally:
313
158
            control_files.unlock()
314
 
        repository = self.open(a_bzrdir, _found=True)
315
 
        self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
316
 
        return repository
 
159
        return self.open(a_bzrdir, _found=True)
 
160
 
 
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)
317
171
 
318
172
    def open(self, a_bzrdir, _found=False):
319
173
        """See RepositoryFormat.open()."""
323
177
 
324
178
        repo_transport = a_bzrdir.get_repository_transport(None)
325
179
        control_files = a_bzrdir._control_files
326
 
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
327
 
        result.revisions = self._get_revisions(repo_transport, result)
328
 
        result.signatures = self._get_signatures(repo_transport, result)
329
 
        result.inventories = self._get_inventories(repo_transport, result)
330
 
        result.texts = self._get_texts(repo_transport, result)
331
 
        result.chk_bytes = None
332
 
        return result
 
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)
333
188
 
334
 
    def is_deprecated(self):
335
 
        return True
 
189
    def check_conversion_target(self, target_format):
 
190
        pass
336
191
 
337
192
 
338
193
class RepositoryFormat4(PreSplitOutRepositoryFormat):
347
202
    has been removed.
348
203
    """
349
204
 
350
 
    supports_funky_characters = False
 
205
    _matchingbzrdir = bzrdir.BzrDirFormat4()
351
206
 
352
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat4()
 
207
    def __init__(self):
 
208
        super(RepositoryFormat4, self).__init__()
353
209
 
354
210
    def get_format_description(self):
355
211
        """See RepositoryFormat.get_format_description()."""
363
219
        """Format 4 is not supported.
364
220
 
365
221
        It is not supported because the model changed from 4 to 5 and the
366
 
        conversion logic is expensive - so doing it on the fly was not
 
222
        conversion logic is expensive - so doing it on the fly was not 
367
223
        feasible.
368
224
        """
369
225
        return False
370
226
 
371
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
372
 
        # No inventories store written so far.
373
 
        return None
374
 
 
375
 
    def _get_revisions(self, repo_transport, repo):
376
 
        from bzrlib.plugins.weave_fmt.xml4 import serializer_v4
377
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
378
 
            serializer_v4, True, versionedfile.PrefixMapper(),
379
 
            repo.is_locked, repo.is_write_locked)
380
 
 
381
 
    def _get_signatures(self, repo_transport, repo):
382
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
383
 
            False, versionedfile.PrefixMapper(),
384
 
            repo.is_locked, repo.is_write_locked)
385
 
 
386
 
    def _get_texts(self, repo_transport, repo):
387
 
        return None
 
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
        """
 
232
        return None
 
233
    
 
234
    def _get_revision_store(self, repo_transport, control_files):
 
235
        """See RepositoryFormat._get_revision_store()."""
 
236
        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()."""
388
244
 
389
245
 
390
246
class RepositoryFormat5(PreSplitOutRepositoryFormat):
397
253
    """
398
254
 
399
255
    _versionedfile_class = weave.WeaveFile
400
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat5()
401
 
    supports_funky_characters = False
 
256
    _matchingbzrdir = bzrdir.BzrDirFormat5()
402
257
 
403
 
    @property
404
 
    def _serializer(self):
405
 
        return xml5.serializer_v5
 
258
    def __init__(self):
 
259
        super(RepositoryFormat5, self).__init__()
406
260
 
407
261
    def get_format_description(self):
408
262
        """See RepositoryFormat.get_format_description()."""
409
263
        return "Weave repository format 5"
410
264
 
411
 
    def network_name(self):
412
 
        """The network name for this format is the control dirs disk label."""
413
 
        return self._matchingbzrdir.get_format_string()
414
 
 
415
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
416
 
        mapper = versionedfile.ConstantMapper(name)
417
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
418
 
            weave.WeaveFile, mapper, repo.is_locked)
419
 
 
420
 
    def _get_revisions(self, repo_transport, repo):
421
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
422
 
            xml5.serializer_v5, False, versionedfile.PrefixMapper(),
423
 
            repo.is_locked, repo.is_write_locked)
424
 
 
425
 
    def _get_signatures(self, repo_transport, repo):
426
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
427
 
            False, versionedfile.PrefixMapper(),
428
 
            repo.is_locked, repo.is_write_locked)
429
 
 
430
 
    def _get_texts(self, repo_transport, repo):
431
 
        mapper = versionedfile.PrefixMapper()
432
 
        base_transport = repo_transport.clone('weaves')
433
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
434
 
            weave.WeaveFile, mapper, repo.is_locked)
 
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)
435
276
 
436
277
 
437
278
class RepositoryFormat6(PreSplitOutRepositoryFormat):
444
285
    """
445
286
 
446
287
    _versionedfile_class = weave.WeaveFile
447
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat6()
448
 
    supports_funky_characters = False
449
 
    @property
450
 
    def _serializer(self):
451
 
        return xml5.serializer_v5
 
288
    _matchingbzrdir = bzrdir.BzrDirFormat6()
 
289
 
 
290
    def __init__(self):
 
291
        super(RepositoryFormat6, self).__init__()
452
292
 
453
293
    def get_format_description(self):
454
294
        """See RepositoryFormat.get_format_description()."""
455
295
        return "Weave repository format 6"
456
296
 
457
 
    def network_name(self):
458
 
        """The network name for this format is the control dirs disk label."""
459
 
        return self._matchingbzrdir.get_format_string()
460
 
 
461
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
462
 
        mapper = versionedfile.ConstantMapper(name)
463
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
464
 
            weave.WeaveFile, mapper, repo.is_locked)
465
 
 
466
 
    def _get_revisions(self, repo_transport, repo):
467
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
468
 
            xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
469
 
            repo.is_locked, repo.is_write_locked)
470
 
 
471
 
    def _get_signatures(self, repo_transport, repo):
472
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
473
 
            False, versionedfile.HashPrefixMapper(),
474
 
            repo.is_locked, repo.is_write_locked)
475
 
 
476
 
    def _get_texts(self, repo_transport, repo):
477
 
        mapper = versionedfile.HashPrefixMapper()
478
 
        base_transport = repo_transport.clone('weaves')
479
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
480
 
            weave.WeaveFile, mapper, repo.is_locked)
481
 
 
482
 
 
483
 
class RepositoryFormat7(MetaDirVersionedFileRepositoryFormat):
 
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)
 
308
 
 
309
 
 
310
class RepositoryFormat7(MetaDirRepositoryFormat):
484
311
    """Bzr repository 7.
485
312
 
486
313
    This repository format has:
493
320
    """
494
321
 
495
322
    _versionedfile_class = weave.WeaveFile
496
 
    supports_ghosts = False
497
 
    supports_chks = False
498
 
    supports_funky_characters = False
499
 
    revision_graph_can_have_wrong_parents = False
500
323
 
501
 
    _fetch_order = 'topological'
502
 
    _fetch_reconcile = True
503
 
    fast_deltas = False
504
 
    @property
505
 
    def _serializer(self):
506
 
        return xml5.serializer_v5
 
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)
507
330
 
508
331
    def get_format_string(self):
509
332
        """See RepositoryFormat.get_format_string()."""
513
336
        """See RepositoryFormat.get_format_description()."""
514
337
        return "Weave repository format 7"
515
338
 
516
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
517
 
        mapper = versionedfile.ConstantMapper(name)
518
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
519
 
            weave.WeaveFile, mapper, repo.is_locked)
520
 
 
521
 
    def _get_revisions(self, repo_transport, repo):
522
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
523
 
            xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
524
 
            repo.is_locked, repo.is_write_locked)
525
 
 
526
 
    def _get_signatures(self, repo_transport, repo):
527
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
528
 
            True, versionedfile.HashPrefixMapper(),
529
 
            repo.is_locked, repo.is_write_locked)
530
 
 
531
 
    def _get_texts(self, repo_transport, repo):
532
 
        mapper = versionedfile.HashPrefixMapper()
533
 
        base_transport = repo_transport.clone('weaves')
534
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
535
 
            weave.WeaveFile, mapper, repo.is_locked)
 
339
    def check_conversion_target(self, target_format):
 
340
        pass
 
341
 
 
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)
536
356
 
537
357
    def initialize(self, a_bzrdir, shared=False):
538
358
        """Create a weave repository.
545
365
        weavefile.write_weave_v5(weave.Weave(), sio)
546
366
        empty_weave = sio.getvalue()
547
367
 
548
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
 
368
        mutter('creating repository in %s.', a_bzrdir.transport.base)
549
369
        dirs = ['revision-store', 'weaves']
550
 
        files = [('inventory.weave', StringIO(empty_weave)),
 
370
        files = [('inventory.weave', StringIO(empty_weave)), 
551
371
                 ]
552
372
        utf8_files = [('format', self.get_format_string())]
553
 
 
 
373
 
554
374
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
555
375
        return self.open(a_bzrdir=a_bzrdir, _found=True)
556
376
 
557
377
    def open(self, a_bzrdir, _found=False, _override_transport=None):
558
378
        """See RepositoryFormat.open().
559
 
 
 
379
        
560
380
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
561
381
                                    repository at a slightly different url
562
382
                                    than normal. I.e. during 'upgrade'.
563
383
        """
564
384
        if not _found:
565
385
            format = RepositoryFormat.find_format(a_bzrdir)
 
386
            assert format.__class__ ==  self.__class__
566
387
        if _override_transport is not None:
567
388
            repo_transport = _override_transport
568
389
        else:
569
390
            repo_transport = a_bzrdir.get_repository_transport(None)
570
391
        control_files = lockable_files.LockableFiles(repo_transport,
571
392
                                'lock', lockdir.LockDir)
572
 
        result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
573
 
            control_files=control_files)
574
 
        result.revisions = self._get_revisions(repo_transport, result)
575
 
        result.signatures = self._get_signatures(repo_transport, result)
576
 
        result.inventories = self._get_inventories(repo_transport, result)
577
 
        result.texts = self._get_texts(repo_transport, result)
578
 
        result.chk_bytes = None
579
 
        result._transport = repo_transport
580
 
        return result
581
 
 
582
 
    def is_deprecated(self):
583
 
        return True
584
 
 
585
 
 
586
 
class TextVersionedFiles(VersionedFiles):
587
 
    """Just-a-bunch-of-files based VersionedFile stores."""
588
 
 
589
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
590
 
        self._compressed = compressed
591
 
        self._transport = transport
592
 
        self._mapper = mapper
593
 
        if self._compressed:
594
 
            self._ext = '.gz'
595
 
        else:
596
 
            self._ext = ''
597
 
        self._is_locked = is_locked
598
 
        self._can_write = can_write
599
 
 
600
 
    def add_lines(self, key, parents, lines):
601
 
        """Add a revision to the store."""
602
 
        if not self._is_locked():
603
 
            raise errors.ObjectNotLocked(self)
604
 
        if not self._can_write():
605
 
            raise errors.ReadOnlyError(self)
606
 
        if '/' in key[-1]:
607
 
            raise ValueError('bad idea to put / in %r' % (key,))
608
 
        text = ''.join(lines)
609
 
        if self._compressed:
610
 
            text = tuned_gzip.bytes_to_gzip(text)
611
 
        path = self._map(key)
612
 
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
613
 
 
614
 
    def insert_record_stream(self, stream):
615
 
        adapters = {}
616
 
        for record in stream:
617
 
            # Raise an error when a record is missing.
618
 
            if record.storage_kind == 'absent':
619
 
                raise errors.RevisionNotPresent([record.key[0]], self)
620
 
            # adapt to non-tuple interface
621
 
            if record.storage_kind == 'fulltext':
622
 
                self.add_lines(record.key, None,
623
 
                    osutils.split_lines(record.get_bytes_as('fulltext')))
624
 
            else:
625
 
                adapter_key = record.storage_kind, 'fulltext'
626
 
                try:
627
 
                    adapter = adapters[adapter_key]
628
 
                except KeyError:
629
 
                    adapter_factory = adapter_registry.get(adapter_key)
630
 
                    adapter = adapter_factory(self)
631
 
                    adapters[adapter_key] = adapter
632
 
                lines = osutils.split_lines(adapter.get_bytes(
633
 
                    record, record.get_bytes_as(record.storage_kind)))
634
 
                try:
635
 
                    self.add_lines(record.key, None, lines)
636
 
                except errors.RevisionAlreadyPresent:
637
 
                    pass
638
 
 
639
 
    def _load_text(self, key):
640
 
        if not self._is_locked():
641
 
            raise errors.ObjectNotLocked(self)
642
 
        path = self._map(key)
643
 
        try:
644
 
            text = self._transport.get_bytes(path)
645
 
            compressed = self._compressed
646
 
        except errors.NoSuchFile:
647
 
            if self._compressed:
648
 
                # try without the .gz
649
 
                path = path[:-3]
650
 
                try:
651
 
                    text = self._transport.get_bytes(path)
652
 
                    compressed = False
653
 
                except errors.NoSuchFile:
654
 
                    return None
655
 
            else:
656
 
                return None
657
 
        if compressed:
658
 
            text = gzip.GzipFile(mode='rb', fileobj=StringIO(text)).read()
659
 
        return text
660
 
 
661
 
    def _map(self, key):
662
 
        return self._mapper.map(key) + self._ext
663
 
 
664
 
 
665
 
class RevisionTextStore(TextVersionedFiles):
666
 
    """Legacy thunk for format 4 repositories."""
667
 
 
668
 
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
669
 
        can_write):
670
 
        """Create a RevisionTextStore at transport with serializer."""
671
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
672
 
            is_locked, can_write)
673
 
        self._serializer = serializer
674
 
 
675
 
    def _load_text_parents(self, key):
676
 
        text = self._load_text(key)
677
 
        if text is None:
678
 
            return None, None
679
 
        parents = self._serializer.read_revision_from_string(text).parent_ids
680
 
        return text, tuple((parent,) for parent in parents)
681
 
 
682
 
    def get_parent_map(self, keys):
683
 
        result = {}
684
 
        for key in keys:
685
 
            parents = self._load_text_parents(key)[1]
686
 
            if parents is None:
687
 
                continue
688
 
            result[key] = parents
689
 
        return result
690
 
 
691
 
    def get_known_graph_ancestry(self, keys):
692
 
        """Get a KnownGraph instance with the ancestry of keys."""
693
 
        keys = self.keys()
694
 
        parent_map = self.get_parent_map(keys)
695
 
        kg = _mod_graph.KnownGraph(parent_map)
696
 
        return kg
697
 
 
698
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
699
 
        for key in keys:
700
 
            text, parents = self._load_text_parents(key)
701
 
            if text is None:
702
 
                yield AbsentContentFactory(key)
703
 
            else:
704
 
                yield FulltextContentFactory(key, parents, None, text)
705
 
 
706
 
    def keys(self):
707
 
        if not self._is_locked():
708
 
            raise errors.ObjectNotLocked(self)
709
 
        relpaths = set()
710
 
        for quoted_relpath in self._transport.iter_files_recursive():
711
 
            relpath = urllib.unquote(quoted_relpath)
712
 
            path, ext = os.path.splitext(relpath)
713
 
            if ext == '.gz':
714
 
                relpath = path
715
 
            if not relpath.endswith('.sig'):
716
 
                relpaths.add(relpath)
717
 
        paths = list(relpaths)
718
 
        return set([self._mapper.unmap(path) for path in paths])
719
 
 
720
 
 
721
 
class SignatureTextStore(TextVersionedFiles):
722
 
    """Legacy thunk for format 4-7 repositories."""
723
 
 
724
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
725
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
726
 
            is_locked, can_write)
727
 
        self._ext = '.sig' + self._ext
728
 
 
729
 
    def get_parent_map(self, keys):
730
 
        result = {}
731
 
        for key in keys:
732
 
            text = self._load_text(key)
733
 
            if text is None:
734
 
                continue
735
 
            result[key] = None
736
 
        return result
737
 
 
738
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
739
 
        for key in keys:
740
 
            text = self._load_text(key)
741
 
            if text is None:
742
 
                yield AbsentContentFactory(key)
743
 
            else:
744
 
                yield FulltextContentFactory(key, None, None, text)
745
 
 
746
 
    def keys(self):
747
 
        if not self._is_locked():
748
 
            raise errors.ObjectNotLocked(self)
749
 
        relpaths = set()
750
 
        for quoted_relpath in self._transport.iter_files_recursive():
751
 
            relpath = urllib.unquote(quoted_relpath)
752
 
            path, ext = os.path.splitext(relpath)
753
 
            if ext == '.gz':
754
 
                relpath = path
755
 
            if not relpath.endswith('.sig'):
756
 
                continue
757
 
            relpaths.add(relpath[:-4])
758
 
        paths = list(relpaths)
759
 
        return set([self._mapper.unmap(path) for path in paths])
760
 
 
761
 
 
762
 
class InterWeaveRepo(InterSameDataRepository):
763
 
    """Optimised code paths between Weave based repositories.
764
 
    """
765
 
 
766
 
    @classmethod
767
 
    def _get_repo_format_to_test(self):
768
 
        return RepositoryFormat7()
769
 
 
770
 
    @staticmethod
771
 
    def is_compatible(source, target):
772
 
        """Be compatible with known Weave formats.
773
 
 
774
 
        We don't test for the stores being of specific types because that
775
 
        could lead to confusing results, and there is no need to be
776
 
        overly general.
777
 
        """
778
 
        try:
779
 
            return (isinstance(source._format, (RepositoryFormat5,
780
 
                                                RepositoryFormat6,
781
 
                                                RepositoryFormat7)) and
782
 
                    isinstance(target._format, (RepositoryFormat5,
783
 
                                                RepositoryFormat6,
784
 
                                                RepositoryFormat7)))
785
 
        except AttributeError:
786
 
            return False
787
 
 
788
 
    @needs_write_lock
789
 
    def copy_content(self, revision_id=None):
790
 
        """See InterRepository.copy_content()."""
791
 
        # weave specific optimised path:
792
 
        try:
793
 
            self.target.set_make_working_trees(self.source.make_working_trees())
794
 
        except (errors.RepositoryUpgradeRequired, NotImplemented):
795
 
            pass
796
 
        # FIXME do not peek!
797
 
        if self.source._transport.listable():
798
 
            pb = ui.ui_factory.nested_progress_bar()
799
 
            try:
800
 
                self.target.texts.insert_record_stream(
801
 
                    self.source.texts.get_record_stream(
802
 
                        self.source.texts.keys(), 'topological', False))
803
 
                pb.update('Copying inventory', 0, 1)
804
 
                self.target.inventories.insert_record_stream(
805
 
                    self.source.inventories.get_record_stream(
806
 
                        self.source.inventories.keys(), 'topological', False))
807
 
                self.target.signatures.insert_record_stream(
808
 
                    self.source.signatures.get_record_stream(
809
 
                        self.source.signatures.keys(),
810
 
                        'unordered', True))
811
 
                self.target.revisions.insert_record_stream(
812
 
                    self.source.revisions.get_record_stream(
813
 
                        self.source.revisions.keys(),
814
 
                        'topological', True))
815
 
            finally:
816
 
                pb.finished()
817
 
        else:
818
 
            self.target.fetch(self.source, revision_id=revision_id)
819
 
 
820
 
    @needs_read_lock
821
 
    def search_missing_revision_ids(self,
822
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
823
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
824
 
            limit=None):
825
 
        """See InterRepository.search_missing_revision_ids()."""
826
 
        # we want all revisions to satisfy revision_id in source.
827
 
        # but we don't want to stat every file here and there.
828
 
        # we want then, all revisions other needs to satisfy revision_id
829
 
        # checked, but not those that we have locally.
830
 
        # so the first thing is to get a subset of the revisions to
831
 
        # satisfy revision_id in source, and then eliminate those that
832
 
        # we do already have.
833
 
        # this is slow on high latency connection to self, but as this
834
 
        # disk format scales terribly for push anyway due to rewriting
835
 
        # inventory.weave, this is considered acceptable.
836
 
        # - RBC 20060209
837
 
        if symbol_versioning.deprecated_passed(revision_id):
838
 
            symbol_versioning.warn(
839
 
                'search_missing_revision_ids(revision_id=...) was '
840
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
841
 
                DeprecationWarning, stacklevel=2)
842
 
            if revision_ids is not None:
843
 
                raise AssertionError(
844
 
                    'revision_ids is mutually exclusive with revision_id')
845
 
            if revision_id is not None:
846
 
                revision_ids = [revision_id]
847
 
        del revision_id
848
 
        source_ids_set = self._present_source_revisions_for(
849
 
            revision_ids, if_present_ids)
850
 
        # source_ids is the worst possible case we may need to pull.
851
 
        # now we want to filter source_ids against what we actually
852
 
        # have in target, but don't try to check for existence where we know
853
 
        # we do not have a revision as that would be pointless.
854
 
        target_ids = set(self.target._all_possible_ids())
855
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
856
 
        actually_present_revisions = set(
857
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
858
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
859
 
        if revision_ids is not None:
860
 
            # we used get_ancestry to determine source_ids then we are assured all
861
 
            # revisions referenced are present as they are installed in topological order.
862
 
            # and the tip revision was validated by get_ancestry.
863
 
            result_set = required_revisions
864
 
        else:
865
 
            # if we just grabbed the possibly available ids, then
866
 
            # we only have an estimate of whats available and need to validate
867
 
            # that against the revision records.
868
 
            result_set = set(
869
 
                self.source._eliminate_revisions_not_present(required_revisions))
870
 
        if limit is not None:
871
 
            topo_ordered = self.get_graph().iter_topo_order(result_set)
872
 
            result_set = set(itertools.islice(topo_ordered, limit))
873
 
        return self.source.revision_ids_to_search_result(result_set)
874
 
 
875
 
 
876
 
InterRepository.register_optimiser(InterWeaveRepo)
877
 
 
878
 
 
879
 
def get_extra_interrepo_test_combinations():
880
 
    from bzrlib.repofmt import knitrepo
881
 
    return [(InterRepository, RepositoryFormat5(),
882
 
        knitrepo.RepositoryFormatKnit3())]
 
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
 
 
403
 
 
404
_legacy_formats = [RepositoryFormat4(),
 
405
                   RepositoryFormat5(),
 
406
                   RepositoryFormat6()]