~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Robert Collins
  • Date: 2007-07-04 01:39:50 UTC
  • mto: This revision was merged to the branch mainline in revision 2581.
  • Revision ID: robertc@robertcollins.net-20070704013950-7pp23plwyqjvgkxg
Review feedback.

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
122
    supports_tree_reference = False
272
 
    supports_ghosts = False
273
 
    supports_external_lookups = False
274
 
    supports_chks = False
275
 
    _fetch_order = 'topological'
276
 
    _fetch_reconcile = True
277
 
    fast_deltas = False
278
 
    supports_leaving_lock = False
279
 
    # XXX: This is an old format that we don't support full checking on, so
280
 
    # just claim that checking for this inconsistency is not required.
281
 
    revision_graph_can_have_wrong_parents = False
282
123
 
283
124
    def initialize(self, a_bzrdir, shared=False, _internal=False):
284
 
        """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
        """
285
130
        if shared:
286
131
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
287
132
 
288
133
        if not _internal:
289
134
            # always initialized when the bzrdir is.
290
135
            return self.open(a_bzrdir, _found=True)
291
 
 
 
136
        
292
137
        # Create an empty weave
293
138
        sio = StringIO()
294
139
        weavefile.write_weave_v5(weave.Weave(), sio)
295
140
        empty_weave = sio.getvalue()
296
141
 
297
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
298
 
 
 
142
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
143
        dirs = ['revision-store', 'weaves']
 
144
        files = [('inventory.weave', StringIO(empty_weave)),
 
145
                 ]
 
146
        
299
147
        # FIXME: RBC 20060125 don't peek under the covers
300
148
        # NB: no need to escape relative paths that are url safe.
301
149
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
302
 
            'branch-lock', lockable_files.TransportLock)
 
150
                                'branch-lock', lockable_files.TransportLock)
303
151
        control_files.create_lock()
304
152
        control_files.lock_write()
305
 
        transport = a_bzrdir.transport
 
153
        control_files._transport.mkdir_multi(dirs,
 
154
                mode=control_files._dir_mode)
306
155
        try:
307
 
            transport.mkdir_multi(['revision-store', 'weaves'],
308
 
                mode=a_bzrdir._get_dir_mode())
309
 
            transport.put_bytes_non_atomic('inventory.weave', empty_weave,
310
 
                mode=a_bzrdir._get_file_mode())
 
156
            for file, content in files:
 
157
                control_files.put(file, content)
311
158
        finally:
312
159
            control_files.unlock()
313
 
        repository = self.open(a_bzrdir, _found=True)
314
 
        self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
315
 
        return repository
 
160
        return self.open(a_bzrdir, _found=True)
 
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)
316
172
 
317
173
    def open(self, a_bzrdir, _found=False):
318
174
        """See RepositoryFormat.open()."""
322
178
 
323
179
        repo_transport = a_bzrdir.get_repository_transport(None)
324
180
        control_files = a_bzrdir._control_files
325
 
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
326
 
        result.revisions = self._get_revisions(repo_transport, result)
327
 
        result.signatures = self._get_signatures(repo_transport, result)
328
 
        result.inventories = self._get_inventories(repo_transport, result)
329
 
        result.texts = self._get_texts(repo_transport, result)
330
 
        result.chk_bytes = None
331
 
        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)
332
189
 
333
 
    def is_deprecated(self):
334
 
        return True
 
190
    def check_conversion_target(self, target_format):
 
191
        pass
335
192
 
336
193
 
337
194
class RepositoryFormat4(PreSplitOutRepositoryFormat):
346
203
    has been removed.
347
204
    """
348
205
 
349
 
    supports_funky_characters = False
 
206
    _matchingbzrdir = bzrdir.BzrDirFormat4()
350
207
 
351
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat4()
 
208
    def __init__(self):
 
209
        super(RepositoryFormat4, self).__init__()
352
210
 
353
211
    def get_format_description(self):
354
212
        """See RepositoryFormat.get_format_description()."""
362
220
        """Format 4 is not supported.
363
221
 
364
222
        It is not supported because the model changed from 4 to 5 and the
365
 
        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 
366
224
        feasible.
367
225
        """
368
226
        return False
369
227
 
370
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
371
 
        # No inventories store written so far.
372
 
        return None
373
 
 
374
 
    def _get_revisions(self, repo_transport, repo):
375
 
        from bzrlib.plugins.weave_fmt.xml4 import serializer_v4
376
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
377
 
            serializer_v4, True, versionedfile.PrefixMapper(),
378
 
            repo.is_locked, repo.is_write_locked)
379
 
 
380
 
    def _get_signatures(self, repo_transport, repo):
381
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
382
 
            False, versionedfile.PrefixMapper(),
383
 
            repo.is_locked, repo.is_write_locked)
384
 
 
385
 
    def _get_texts(self, repo_transport, repo):
386
 
        return None
 
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
        """
 
233
        return None
 
234
    
 
235
    def _get_revision_store(self, repo_transport, control_files):
 
236
        """See RepositoryFormat._get_revision_store()."""
 
237
        from bzrlib.xml4 import serializer_v4
 
238
        return self._get_text_rev_store(repo_transport,
 
239
                                        control_files,
 
240
                                        'revision-store',
 
241
                                        serializer=serializer_v4)
 
242
 
 
243
    def _get_text_store(self, transport, control_files):
 
244
        """See RepositoryFormat._get_text_store()."""
387
245
 
388
246
 
389
247
class RepositoryFormat5(PreSplitOutRepositoryFormat):
396
254
    """
397
255
 
398
256
    _versionedfile_class = weave.WeaveFile
399
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat5()
400
 
    supports_funky_characters = False
 
257
    _matchingbzrdir = bzrdir.BzrDirFormat5()
401
258
 
402
 
    @property
403
 
    def _serializer(self):
404
 
        return xml5.serializer_v5
 
259
    def __init__(self):
 
260
        super(RepositoryFormat5, self).__init__()
405
261
 
406
262
    def get_format_description(self):
407
263
        """See RepositoryFormat.get_format_description()."""
408
264
        return "Weave repository format 5"
409
265
 
410
 
    def network_name(self):
411
 
        """The network name for this format is the control dirs disk label."""
412
 
        return self._matchingbzrdir.get_format_string()
413
 
 
414
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
415
 
        mapper = versionedfile.ConstantMapper(name)
416
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
417
 
            weave.WeaveFile, mapper, repo.is_locked)
418
 
 
419
 
    def _get_revisions(self, repo_transport, repo):
420
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
421
 
            xml5.serializer_v5, False, versionedfile.PrefixMapper(),
422
 
            repo.is_locked, repo.is_write_locked)
423
 
 
424
 
    def _get_signatures(self, repo_transport, repo):
425
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
426
 
            False, versionedfile.PrefixMapper(),
427
 
            repo.is_locked, repo.is_write_locked)
428
 
 
429
 
    def _get_texts(self, repo_transport, repo):
430
 
        mapper = versionedfile.PrefixMapper()
431
 
        base_transport = repo_transport.clone('weaves')
432
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
433
 
            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)
434
277
 
435
278
 
436
279
class RepositoryFormat6(PreSplitOutRepositoryFormat):
443
286
    """
444
287
 
445
288
    _versionedfile_class = weave.WeaveFile
446
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat6()
447
 
    supports_funky_characters = False
448
 
    @property
449
 
    def _serializer(self):
450
 
        return xml5.serializer_v5
 
289
    _matchingbzrdir = bzrdir.BzrDirFormat6()
 
290
 
 
291
    def __init__(self):
 
292
        super(RepositoryFormat6, self).__init__()
451
293
 
452
294
    def get_format_description(self):
453
295
        """See RepositoryFormat.get_format_description()."""
454
296
        return "Weave repository format 6"
455
297
 
456
 
    def network_name(self):
457
 
        """The network name for this format is the control dirs disk label."""
458
 
        return self._matchingbzrdir.get_format_string()
459
 
 
460
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
461
 
        mapper = versionedfile.ConstantMapper(name)
462
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
463
 
            weave.WeaveFile, mapper, repo.is_locked)
464
 
 
465
 
    def _get_revisions(self, repo_transport, repo):
466
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
467
 
            xml5.serializer_v5, False, versionedfile.HashPrefixMapper(),
468
 
            repo.is_locked, repo.is_write_locked)
469
 
 
470
 
    def _get_signatures(self, repo_transport, repo):
471
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
472
 
            False, versionedfile.HashPrefixMapper(),
473
 
            repo.is_locked, repo.is_write_locked)
474
 
 
475
 
    def _get_texts(self, repo_transport, repo):
476
 
        mapper = versionedfile.HashPrefixMapper()
477
 
        base_transport = repo_transport.clone('weaves')
478
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
479
 
            weave.WeaveFile, mapper, repo.is_locked)
480
 
 
481
 
 
482
 
class RepositoryFormat7(MetaDirVersionedFileRepositoryFormat):
 
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)
 
309
 
 
310
 
 
311
class RepositoryFormat7(MetaDirRepositoryFormat):
483
312
    """Bzr repository 7.
484
313
 
485
314
    This repository format has:
492
321
    """
493
322
 
494
323
    _versionedfile_class = weave.WeaveFile
495
 
    supports_ghosts = False
496
 
    supports_chks = False
497
 
    supports_funky_characters = False
498
 
    revision_graph_can_have_wrong_parents = False
499
324
 
500
 
    _fetch_order = 'topological'
501
 
    _fetch_reconcile = True
502
 
    fast_deltas = False
503
 
    @property
504
 
    def _serializer(self):
505
 
        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)
506
331
 
507
332
    def get_format_string(self):
508
333
        """See RepositoryFormat.get_format_string()."""
512
337
        """See RepositoryFormat.get_format_description()."""
513
338
        return "Weave repository format 7"
514
339
 
515
 
    def _get_inventories(self, repo_transport, repo, name='inventory'):
516
 
        mapper = versionedfile.ConstantMapper(name)
517
 
        return versionedfile.ThunkedVersionedFiles(repo_transport,
518
 
            weave.WeaveFile, mapper, repo.is_locked)
519
 
 
520
 
    def _get_revisions(self, repo_transport, repo):
521
 
        return RevisionTextStore(repo_transport.clone('revision-store'),
522
 
            xml5.serializer_v5, True, versionedfile.HashPrefixMapper(),
523
 
            repo.is_locked, repo.is_write_locked)
524
 
 
525
 
    def _get_signatures(self, repo_transport, repo):
526
 
        return SignatureTextStore(repo_transport.clone('revision-store'),
527
 
            True, versionedfile.HashPrefixMapper(),
528
 
            repo.is_locked, repo.is_write_locked)
529
 
 
530
 
    def _get_texts(self, repo_transport, repo):
531
 
        mapper = versionedfile.HashPrefixMapper()
532
 
        base_transport = repo_transport.clone('weaves')
533
 
        return versionedfile.ThunkedVersionedFiles(base_transport,
534
 
            weave.WeaveFile, mapper, repo.is_locked)
 
340
    def check_conversion_target(self, target_format):
 
341
        pass
 
342
 
 
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)
535
357
 
536
358
    def initialize(self, a_bzrdir, shared=False):
537
359
        """Create a weave repository.
544
366
        weavefile.write_weave_v5(weave.Weave(), sio)
545
367
        empty_weave = sio.getvalue()
546
368
 
547
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
 
369
        mutter('creating repository in %s.', a_bzrdir.transport.base)
548
370
        dirs = ['revision-store', 'weaves']
549
 
        files = [('inventory.weave', StringIO(empty_weave)),
 
371
        files = [('inventory.weave', StringIO(empty_weave)), 
550
372
                 ]
551
373
        utf8_files = [('format', self.get_format_string())]
552
 
 
 
374
 
553
375
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
554
376
        return self.open(a_bzrdir=a_bzrdir, _found=True)
555
377
 
556
378
    def open(self, a_bzrdir, _found=False, _override_transport=None):
557
379
        """See RepositoryFormat.open().
558
 
 
 
380
        
559
381
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
560
382
                                    repository at a slightly different url
561
383
                                    than normal. I.e. during 'upgrade'.
562
384
        """
563
385
        if not _found:
564
386
            format = RepositoryFormat.find_format(a_bzrdir)
 
387
            assert format.__class__ ==  self.__class__
565
388
        if _override_transport is not None:
566
389
            repo_transport = _override_transport
567
390
        else:
568
391
            repo_transport = a_bzrdir.get_repository_transport(None)
569
392
        control_files = lockable_files.LockableFiles(repo_transport,
570
393
                                'lock', lockdir.LockDir)
571
 
        result = WeaveMetaDirRepository(_format=self, a_bzrdir=a_bzrdir,
572
 
            control_files=control_files)
573
 
        result.revisions = self._get_revisions(repo_transport, result)
574
 
        result.signatures = self._get_signatures(repo_transport, result)
575
 
        result.inventories = self._get_inventories(repo_transport, result)
576
 
        result.texts = self._get_texts(repo_transport, result)
577
 
        result.chk_bytes = None
578
 
        result._transport = repo_transport
579
 
        return result
580
 
 
581
 
    def is_deprecated(self):
582
 
        return True
583
 
 
584
 
 
585
 
class TextVersionedFiles(VersionedFiles):
586
 
    """Just-a-bunch-of-files based VersionedFile stores."""
587
 
 
588
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
589
 
        self._compressed = compressed
590
 
        self._transport = transport
591
 
        self._mapper = mapper
592
 
        if self._compressed:
593
 
            self._ext = '.gz'
594
 
        else:
595
 
            self._ext = ''
596
 
        self._is_locked = is_locked
597
 
        self._can_write = can_write
598
 
 
599
 
    def add_lines(self, key, parents, lines):
600
 
        """Add a revision to the store."""
601
 
        if not self._is_locked():
602
 
            raise errors.ObjectNotLocked(self)
603
 
        if not self._can_write():
604
 
            raise errors.ReadOnlyError(self)
605
 
        if '/' in key[-1]:
606
 
            raise ValueError('bad idea to put / in %r' % (key,))
607
 
        text = ''.join(lines)
608
 
        if self._compressed:
609
 
            text = tuned_gzip.bytes_to_gzip(text)
610
 
        path = self._map(key)
611
 
        self._transport.put_bytes_non_atomic(path, text, create_parent_dir=True)
612
 
 
613
 
    def insert_record_stream(self, stream):
614
 
        adapters = {}
615
 
        for record in stream:
616
 
            # Raise an error when a record is missing.
617
 
            if record.storage_kind == 'absent':
618
 
                raise errors.RevisionNotPresent([record.key[0]], self)
619
 
            # adapt to non-tuple interface
620
 
            if record.storage_kind == 'fulltext':
621
 
                self.add_lines(record.key, None,
622
 
                    osutils.split_lines(record.get_bytes_as('fulltext')))
623
 
            else:
624
 
                adapter_key = record.storage_kind, 'fulltext'
625
 
                try:
626
 
                    adapter = adapters[adapter_key]
627
 
                except KeyError:
628
 
                    adapter_factory = adapter_registry.get(adapter_key)
629
 
                    adapter = adapter_factory(self)
630
 
                    adapters[adapter_key] = adapter
631
 
                lines = osutils.split_lines(adapter.get_bytes(
632
 
                    record, record.get_bytes_as(record.storage_kind)))
633
 
                try:
634
 
                    self.add_lines(record.key, None, lines)
635
 
                except errors.RevisionAlreadyPresent:
636
 
                    pass
637
 
 
638
 
    def _load_text(self, key):
639
 
        if not self._is_locked():
640
 
            raise errors.ObjectNotLocked(self)
641
 
        path = self._map(key)
642
 
        try:
643
 
            text = self._transport.get_bytes(path)
644
 
            compressed = self._compressed
645
 
        except errors.NoSuchFile:
646
 
            if self._compressed:
647
 
                # try without the .gz
648
 
                path = path[:-3]
649
 
                try:
650
 
                    text = self._transport.get_bytes(path)
651
 
                    compressed = False
652
 
                except errors.NoSuchFile:
653
 
                    return None
654
 
            else:
655
 
                return None
656
 
        if compressed:
657
 
            text = gzip.GzipFile(mode='rb', fileobj=StringIO(text)).read()
658
 
        return text
659
 
 
660
 
    def _map(self, key):
661
 
        return self._mapper.map(key) + self._ext
662
 
 
663
 
 
664
 
class RevisionTextStore(TextVersionedFiles):
665
 
    """Legacy thunk for format 4 repositories."""
666
 
 
667
 
    def __init__(self, transport, serializer, compressed, mapper, is_locked,
668
 
        can_write):
669
 
        """Create a RevisionTextStore at transport with serializer."""
670
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
671
 
            is_locked, can_write)
672
 
        self._serializer = serializer
673
 
 
674
 
    def _load_text_parents(self, key):
675
 
        text = self._load_text(key)
676
 
        if text is None:
677
 
            return None, None
678
 
        parents = self._serializer.read_revision_from_string(text).parent_ids
679
 
        return text, tuple((parent,) for parent in parents)
680
 
 
681
 
    def get_parent_map(self, keys):
682
 
        result = {}
683
 
        for key in keys:
684
 
            parents = self._load_text_parents(key)[1]
685
 
            if parents is None:
686
 
                continue
687
 
            result[key] = parents
688
 
        return result
689
 
 
690
 
    def get_known_graph_ancestry(self, keys):
691
 
        """Get a KnownGraph instance with the ancestry of keys."""
692
 
        keys = self.keys()
693
 
        parent_map = self.get_parent_map(keys)
694
 
        kg = _mod_graph.KnownGraph(parent_map)
695
 
        return kg
696
 
 
697
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
698
 
        for key in keys:
699
 
            text, parents = self._load_text_parents(key)
700
 
            if text is None:
701
 
                yield AbsentContentFactory(key)
702
 
            else:
703
 
                yield FulltextContentFactory(key, parents, None, text)
704
 
 
705
 
    def keys(self):
706
 
        if not self._is_locked():
707
 
            raise errors.ObjectNotLocked(self)
708
 
        relpaths = set()
709
 
        for quoted_relpath in self._transport.iter_files_recursive():
710
 
            relpath = urllib.unquote(quoted_relpath)
711
 
            path, ext = os.path.splitext(relpath)
712
 
            if ext == '.gz':
713
 
                relpath = path
714
 
            if not relpath.endswith('.sig'):
715
 
                relpaths.add(relpath)
716
 
        paths = list(relpaths)
717
 
        return set([self._mapper.unmap(path) for path in paths])
718
 
 
719
 
 
720
 
class SignatureTextStore(TextVersionedFiles):
721
 
    """Legacy thunk for format 4-7 repositories."""
722
 
 
723
 
    def __init__(self, transport, compressed, mapper, is_locked, can_write):
724
 
        TextVersionedFiles.__init__(self, transport, compressed, mapper,
725
 
            is_locked, can_write)
726
 
        self._ext = '.sig' + self._ext
727
 
 
728
 
    def get_parent_map(self, keys):
729
 
        result = {}
730
 
        for key in keys:
731
 
            text = self._load_text(key)
732
 
            if text is None:
733
 
                continue
734
 
            result[key] = None
735
 
        return result
736
 
 
737
 
    def get_record_stream(self, keys, sort_order, include_delta_closure):
738
 
        for key in keys:
739
 
            text = self._load_text(key)
740
 
            if text is None:
741
 
                yield AbsentContentFactory(key)
742
 
            else:
743
 
                yield FulltextContentFactory(key, None, None, text)
744
 
 
745
 
    def keys(self):
746
 
        if not self._is_locked():
747
 
            raise errors.ObjectNotLocked(self)
748
 
        relpaths = set()
749
 
        for quoted_relpath in self._transport.iter_files_recursive():
750
 
            relpath = urllib.unquote(quoted_relpath)
751
 
            path, ext = os.path.splitext(relpath)
752
 
            if ext == '.gz':
753
 
                relpath = path
754
 
            if not relpath.endswith('.sig'):
755
 
                continue
756
 
            relpaths.add(relpath[:-4])
757
 
        paths = list(relpaths)
758
 
        return set([self._mapper.unmap(path) for path in paths])
759
 
 
760
 
 
761
 
class InterWeaveRepo(InterSameDataRepository):
762
 
    """Optimised code paths between Weave based repositories.
763
 
    """
764
 
 
765
 
    @classmethod
766
 
    def _get_repo_format_to_test(self):
767
 
        return RepositoryFormat7()
768
 
 
769
 
    @staticmethod
770
 
    def is_compatible(source, target):
771
 
        """Be compatible with known Weave formats.
772
 
 
773
 
        We don't test for the stores being of specific types because that
774
 
        could lead to confusing results, and there is no need to be
775
 
        overly general.
776
 
        """
777
 
        try:
778
 
            return (isinstance(source._format, (RepositoryFormat5,
779
 
                                                RepositoryFormat6,
780
 
                                                RepositoryFormat7)) and
781
 
                    isinstance(target._format, (RepositoryFormat5,
782
 
                                                RepositoryFormat6,
783
 
                                                RepositoryFormat7)))
784
 
        except AttributeError:
785
 
            return False
786
 
 
787
 
    @needs_write_lock
788
 
    def copy_content(self, revision_id=None):
789
 
        """See InterRepository.copy_content()."""
790
 
        # weave specific optimised path:
791
 
        try:
792
 
            self.target.set_make_working_trees(self.source.make_working_trees())
793
 
        except (errors.RepositoryUpgradeRequired, NotImplemented):
794
 
            pass
795
 
        # FIXME do not peek!
796
 
        if self.source._transport.listable():
797
 
            pb = ui.ui_factory.nested_progress_bar()
798
 
            try:
799
 
                self.target.texts.insert_record_stream(
800
 
                    self.source.texts.get_record_stream(
801
 
                        self.source.texts.keys(), 'topological', False))
802
 
                pb.update('Copying inventory', 0, 1)
803
 
                self.target.inventories.insert_record_stream(
804
 
                    self.source.inventories.get_record_stream(
805
 
                        self.source.inventories.keys(), 'topological', False))
806
 
                self.target.signatures.insert_record_stream(
807
 
                    self.source.signatures.get_record_stream(
808
 
                        self.source.signatures.keys(),
809
 
                        'unordered', True))
810
 
                self.target.revisions.insert_record_stream(
811
 
                    self.source.revisions.get_record_stream(
812
 
                        self.source.revisions.keys(),
813
 
                        'topological', True))
814
 
            finally:
815
 
                pb.finished()
816
 
        else:
817
 
            self.target.fetch(self.source, revision_id=revision_id)
818
 
 
819
 
    @needs_read_lock
820
 
    def search_missing_revision_ids(self,
821
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
822
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
823
 
            limit=None):
824
 
        """See InterRepository.search_missing_revision_ids()."""
825
 
        # we want all revisions to satisfy revision_id in source.
826
 
        # but we don't want to stat every file here and there.
827
 
        # we want then, all revisions other needs to satisfy revision_id
828
 
        # checked, but not those that we have locally.
829
 
        # so the first thing is to get a subset of the revisions to
830
 
        # satisfy revision_id in source, and then eliminate those that
831
 
        # we do already have.
832
 
        # this is slow on high latency connection to self, but as this
833
 
        # disk format scales terribly for push anyway due to rewriting
834
 
        # inventory.weave, this is considered acceptable.
835
 
        # - RBC 20060209
836
 
        if symbol_versioning.deprecated_passed(revision_id):
837
 
            symbol_versioning.warn(
838
 
                'search_missing_revision_ids(revision_id=...) was '
839
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
840
 
                DeprecationWarning, stacklevel=2)
841
 
            if revision_ids is not None:
842
 
                raise AssertionError(
843
 
                    'revision_ids is mutually exclusive with revision_id')
844
 
            if revision_id is not None:
845
 
                revision_ids = [revision_id]
846
 
        del revision_id
847
 
        source_ids_set = self._present_source_revisions_for(
848
 
            revision_ids, if_present_ids)
849
 
        # source_ids is the worst possible case we may need to pull.
850
 
        # now we want to filter source_ids against what we actually
851
 
        # have in target, but don't try to check for existence where we know
852
 
        # we do not have a revision as that would be pointless.
853
 
        target_ids = set(self.target._all_possible_ids())
854
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
855
 
        actually_present_revisions = set(
856
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
857
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
858
 
        if revision_ids is not None:
859
 
            # we used get_ancestry to determine source_ids then we are assured all
860
 
            # revisions referenced are present as they are installed in topological order.
861
 
            # and the tip revision was validated by get_ancestry.
862
 
            result_set = required_revisions
863
 
        else:
864
 
            # if we just grabbed the possibly available ids, then
865
 
            # we only have an estimate of whats available and need to validate
866
 
            # that against the revision records.
867
 
            result_set = set(
868
 
                self.source._eliminate_revisions_not_present(required_revisions))
869
 
        if limit is not None:
870
 
            topo_ordered = self.get_graph().iter_topo_order(result_set)
871
 
            result_set = set(itertools.islice(topo_ordered, limit))
872
 
        return self.source.revision_ids_to_search_result(result_set)
873
 
 
874
 
 
875
 
InterRepository.register_optimiser(InterWeaveRepo)
876
 
 
877
 
 
878
 
def get_extra_interrepo_test_combinations():
879
 
    from bzrlib.repofmt import knitrepo
880
 
    return [(InterRepository, RepositoryFormat5(),
881
 
        knitrepo.RepositoryFormatKnit3())]
 
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
 
 
404
 
 
405
_legacy_formats = [RepositoryFormat4(),
 
406
                   RepositoryFormat5(),
 
407
                   RepositoryFormat6()]