~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-16 16:58:03 UTC
  • mfrom: (3224.3.1 news-typo)
  • Revision ID: pqm@pqm.ubuntu.com-20080316165803-tisoc9mpob9z544o
(Matt Nordhoff) Trivial NEWS typo fix

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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Deprecated weave-based repository formats.
18
18
 
20
20
ghosts.
21
21
"""
22
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 (
 
23
from StringIO import StringIO
 
24
 
 
25
from bzrlib import (
 
26
    bzrdir,
39
27
    debug,
40
28
    errors,
41
29
    lockable_files,
42
30
    lockdir,
43
31
    osutils,
44
 
    symbol_versioning,
45
 
    trace,
46
 
    tuned_gzip,
47
 
    urlutils,
48
 
    versionedfile,
 
32
    revision as _mod_revision,
49
33
    weave,
50
34
    weavefile,
 
35
    xml5,
51
36
    )
52
37
from bzrlib.decorators import needs_read_lock, needs_write_lock
53
38
from bzrlib.repository import (
54
 
    InterRepository,
 
39
    CommitBuilder,
 
40
    MetaDirRepository,
 
41
    MetaDirRepositoryFormat,
 
42
    Repository,
55
43
    RepositoryFormat,
56
44
    )
57
45
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):
 
46
from bzrlib.trace import mutter
 
47
 
 
48
 
 
49
class AllInOneRepository(Repository):
76
50
    """Legacy support - the repository behaviour for all-in-one branches."""
77
51
 
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):
 
52
    _serializer = xml5.serializer_v5
 
53
 
 
54
    def __init__(self, _format, a_bzrdir, _revision_store, control_store, text_store):
90
55
        # we reuse one control files instance.
91
 
        dir_mode = a_bzrdir._get_dir_mode()
92
 
        file_mode = a_bzrdir._get_file_mode()
 
56
        dir_mode = a_bzrdir._control_files._dir_mode
 
57
        file_mode = a_bzrdir._control_files._file_mode
93
58
 
94
59
        def get_store(name, compressed=True, prefixed=False):
95
60
            # 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
 
61
            # or entirely uncompressed is tidy, but breaks upgrade from 
 
62
            # some existing branches where there's a mixture; we probably 
98
63
            # still want the option to look for both.
99
 
            relpath = self._escape(name)
100
 
            store = TextStore(a_bzrdir.transport.clone(relpath),
 
64
            relpath = a_bzrdir._control_files._escape(name)
 
65
            store = TextStore(a_bzrdir._control_files._transport.clone(relpath),
101
66
                              prefixed=prefixed, compressed=compressed,
102
67
                              dir_mode=dir_mode,
103
68
                              file_mode=file_mode)
104
69
            return store
105
70
 
106
71
        # not broken out yet because the controlweaves|inventory_store
107
 
        # and texts bits are still different.
 
72
        # and text_store | weave_store bits are still different.
108
73
        if isinstance(_format, RepositoryFormat4):
109
 
            # cannot remove these - there is still no consistent api
 
74
            # cannot remove these - there is still no consistent api 
110
75
            # which allows access to this old info.
111
76
            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)
 
77
            text_store = get_store('text-store')
 
78
        super(AllInOneRepository, self).__init__(_format, a_bzrdir, a_bzrdir._control_files, _revision_store, control_store, text_store)
114
79
 
115
80
    @needs_read_lock
116
81
    def _all_possible_ids(self):
117
82
        """Return all the possible revisions that we could find."""
118
83
        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()]
 
84
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
85
        return self.get_inventory_weave().versions()
122
86
 
123
87
    @needs_read_lock
124
88
    def _all_revision_ids(self):
125
 
        """Returns a list of all the revision ids in the repository.
 
89
        """Returns a list of all the revision ids in the repository. 
126
90
 
127
 
        These are in as much topological order as the underlying store can
 
91
        These are in as much topological order as the underlying store can 
128
92
        present: for weaves ghosts may lead to a lack of correctness until
129
93
        the reweave updates the parents list.
130
94
        """
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')
 
95
        if self._revision_store.text_store.listable():
 
96
            return self._revision_store.all_revision_ids(self.get_transaction())
 
97
        result = self._all_possible_ids()
 
98
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
 
99
        #       ids. (It should, since _revision_store's API should change to
 
100
        #       return utf8 revision_ids)
 
101
        return self._eliminate_revisions_not_present(result)
 
102
 
 
103
    def _check_revision_parents(self, revision, inventory):
 
104
        """Private to Repository and Fetch.
 
105
        
 
106
        This checks the parentage of revision in an inventory weave for 
 
107
        consistency and is only applicable to inventory-weave-for-ancestry
 
108
        using repository formats & fetchers.
 
109
        """
 
110
        weave_parents = inventory.get_parents(revision.revision_id)
 
111
        weave_names = inventory.versions()
 
112
        for parent_id in revision.parent_ids:
 
113
            if parent_id in weave_names:
 
114
                # this parent must not be a ghost.
 
115
                if not parent_id in weave_parents:
 
116
                    # but it is a ghost
 
117
                    raise errors.CorruptRepository(self)
150
118
 
151
119
    def get_commit_builder(self, branch, parents, config, timestamp=None,
152
120
                           timezone=None, committer=None, revprops=None,
153
 
                           revision_id=None, lossy=False):
 
121
                           revision_id=None):
154
122
        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)
 
123
        result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
 
124
                              committer, revprops, revision_id)
157
125
        self.start_write_group()
158
126
        return result
159
127
 
160
128
    @needs_read_lock
161
129
    def get_revisions(self, revision_ids):
162
130
        revs = self._get_revisions(revision_ids)
 
131
        # weave corruption can lead to absent revision markers that should be
 
132
        # present.
 
133
        # the following test is reasonably cheap (it needs a single weave read)
 
134
        # and the weave is cached in read transactions. In write transactions
 
135
        # it is not cached but typically we only read a small number of
 
136
        # revisions. For knits when they are introduced we will probably want
 
137
        # to ensure that caching write transactions are in use.
 
138
        inv = self.get_inventory_weave()
 
139
        for rev in revs:
 
140
            self._check_revision_parents(rev, inv)
163
141
        return revs
164
142
 
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
 
 
 
143
    @needs_read_lock
 
144
    def get_revision_graph(self, revision_id=None):
 
145
        """Return a dictionary containing the revision graph.
 
146
        
 
147
        :param revision_id: The revision_id to get a graph from. If None, then
 
148
        the entire revision graph is returned. This is a deprecated mode of
 
149
        operation and will be removed in the future.
 
150
        :return: a dictionary of revision_id->revision_parents_list.
 
151
        """
 
152
        if 'evil' in debug.debug_flags:
 
153
            mutter_callsite(2,
 
154
                "get_revision_graph scales with size of history.")
 
155
        # special case NULL_REVISION
 
156
        if revision_id == _mod_revision.NULL_REVISION:
 
157
            return {}
 
158
        a_weave = self.get_inventory_weave()
 
159
        all_revisions = self._eliminate_revisions_not_present(
 
160
                                a_weave.versions())
 
161
        entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for 
 
162
                             node in all_revisions])
 
163
        if revision_id is None:
 
164
            return entire_graph
 
165
        elif revision_id not in entire_graph:
 
166
            raise errors.NoSuchRevision(self, revision_id)
 
167
        else:
 
168
            # add what can be reached from revision_id
 
169
            result = {}
 
170
            pending = set([revision_id])
 
171
            while len(pending) > 0:
 
172
                node = pending.pop()
 
173
                result[node] = entire_graph[node]
 
174
                for revision_id in result[node]:
 
175
                    if revision_id not in result:
 
176
                        pending.add(revision_id)
 
177
            return result
 
178
 
 
179
    def has_revisions(self, revision_ids):
 
180
        """See Repository.has_revisions()."""
 
181
        result = set()
 
182
        transaction = self.get_transaction()
 
183
        for revision_id in revision_ids:
 
184
            if self._revision_store.has_revision_id(revision_id, transaction):
 
185
                result.add(revision_id)
 
186
        return result
 
187
 
 
188
    @needs_read_lock
176
189
    def is_shared(self):
177
190
        """AllInOne repositories cannot be shared."""
178
191
        return False
187
200
        :param new_value: True to restore the default, False to disable making
188
201
                          working trees.
189
202
        """
190
 
        raise errors.RepositoryUpgradeRequired(self.user_url)
191
 
 
 
203
        raise NotImplementedError(self.set_make_working_trees)
 
204
    
192
205
    def make_working_trees(self):
193
206
        """Returns the policy for making working trees on new branches."""
194
207
        return True
195
208
 
196
 
 
197
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
 
209
    def revision_graph_can_have_wrong_parents(self):
 
210
        # XXX: This is an old format that we don't support full checking on, so
 
211
        # just claim that checking for this inconsistency is not required.
 
212
        return False
 
213
 
 
214
 
 
215
class WeaveMetaDirRepository(MetaDirRepository):
198
216
    """A subclass of MetaDirRepository to set weave specific policy."""
199
217
 
200
 
    def __init__(self, _format, a_bzrdir, control_files):
201
 
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
202
 
        self._serializer = _format._serializer
 
218
    _serializer = xml5.serializer_v5
203
219
 
204
220
    @needs_read_lock
205
221
    def _all_possible_ids(self):
206
222
        """Return all the possible revisions that we could find."""
207
223
        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()]
 
224
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
225
        return self.get_inventory_weave().versions()
211
226
 
212
227
    @needs_read_lock
213
228
    def _all_revision_ids(self):
214
 
        """Returns a list of all the revision ids in the repository.
 
229
        """Returns a list of all the revision ids in the repository. 
215
230
 
216
 
        These are in as much topological order as the underlying store can
 
231
        These are in as much topological order as the underlying store can 
217
232
        present: for weaves ghosts may lead to a lack of correctness until
218
233
        the reweave updates the parents list.
219
234
        """
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')
 
235
        if self._revision_store.text_store.listable():
 
236
            return self._revision_store.all_revision_ids(self.get_transaction())
 
237
        result = self._all_possible_ids()
 
238
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
 
239
        #       ids. (It should, since _revision_store's API should change to
 
240
        #       return utf8 revision_ids)
 
241
        return self._eliminate_revisions_not_present(result)
 
242
 
 
243
    def _check_revision_parents(self, revision, inventory):
 
244
        """Private to Repository and Fetch.
 
245
        
 
246
        This checks the parentage of revision in an inventory weave for 
 
247
        consistency and is only applicable to inventory-weave-for-ancestry
 
248
        using repository formats & fetchers.
 
249
        """
 
250
        weave_parents = inventory.get_parents(revision.revision_id)
 
251
        weave_names = inventory.versions()
 
252
        for parent_id in revision.parent_ids:
 
253
            if parent_id in weave_names:
 
254
                # this parent must not be a ghost.
 
255
                if not parent_id in weave_parents:
 
256
                    # but it is a ghost
 
257
                    raise errors.CorruptRepository(self)
239
258
 
240
259
    def get_commit_builder(self, branch, parents, config, timestamp=None,
241
260
                           timezone=None, committer=None, revprops=None,
242
 
                           revision_id=None, lossy=False):
 
261
                           revision_id=None):
243
262
        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)
 
263
        result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
 
264
                              committer, revprops, revision_id)
246
265
        self.start_write_group()
247
266
        return result
248
267
 
249
268
    @needs_read_lock
250
269
    def get_revision(self, revision_id):
251
270
        """Return the Revision object for a named revision"""
 
271
        # TODO: jam 20070210 get_revision_reconcile should do this for us
252
272
        r = self.get_revision_reconcile(revision_id)
 
273
        # weave corruption can lead to absent revision markers that should be
 
274
        # present.
 
275
        # the following test is reasonably cheap (it needs a single weave read)
 
276
        # and the weave is cached in read transactions. In write transactions
 
277
        # it is not cached but typically we only read a small number of
 
278
        # revisions. For knits when they are introduced we will probably want
 
279
        # to ensure that caching write transactions are in use.
 
280
        inv = self.get_inventory_weave()
 
281
        self._check_revision_parents(r, inv)
253
282
        return r
254
283
 
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):
 
284
    @needs_read_lock
 
285
    def get_revision_graph(self, revision_id=None):
 
286
        """Return a dictionary containing the revision graph.
 
287
        
 
288
        :param revision_id: The revision_id to get a graph from. If None, then
 
289
        the entire revision graph is returned. This is a deprecated mode of
 
290
        operation and will be removed in the future.
 
291
        :return: a dictionary of revision_id->revision_parents_list.
 
292
        """
 
293
        if 'evil' in debug.debug_flags:
 
294
            mutter_callsite(3,
 
295
                "get_revision_graph scales with size of history.")
 
296
        # special case NULL_REVISION
 
297
        if revision_id == _mod_revision.NULL_REVISION:
 
298
            return {}
 
299
        a_weave = self.get_inventory_weave()
 
300
        all_revisions = self._eliminate_revisions_not_present(
 
301
                                a_weave.versions())
 
302
        entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for 
 
303
                             node in all_revisions])
 
304
        if revision_id is None:
 
305
            return entire_graph
 
306
        elif revision_id not in entire_graph:
 
307
            raise errors.NoSuchRevision(self, revision_id)
 
308
        else:
 
309
            # add what can be reached from revision_id
 
310
            result = {}
 
311
            pending = set([revision_id])
 
312
            while len(pending) > 0:
 
313
                node = pending.pop()
 
314
                result[node] = entire_graph[node]
 
315
                for revision_id in result[node]:
 
316
                    if revision_id not in result:
 
317
                        pending.add(revision_id)
 
318
            return result
 
319
 
 
320
    def has_revisions(self, revision_ids):
 
321
        """See Repository.has_revisions()."""
 
322
        result = set()
 
323
        transaction = self.get_transaction()
 
324
        for revision_id in revision_ids:
 
325
            if self._revision_store.has_revision_id(revision_id, transaction):
 
326
                result.add(revision_id)
 
327
        return result
 
328
 
 
329
    def revision_graph_can_have_wrong_parents(self):
 
330
        # XXX: This is an old format that we don't support full checking on, so
 
331
        # just claim that checking for this inconsistency is not required.
 
332
        return False
 
333
 
 
334
 
 
335
class PreSplitOutRepositoryFormat(RepositoryFormat):
268
336
    """Base class for the pre split out repository formats."""
269
337
 
270
338
    rich_root_data = False
271
339
    supports_tree_reference = False
272
340
    supports_ghosts = False
273
341
    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
342
 
284
343
    def initialize(self, a_bzrdir, shared=False, _internal=False):
285
344
        """Create a weave repository."""
289
348
        if not _internal:
290
349
            # always initialized when the bzrdir is.
291
350
            return self.open(a_bzrdir, _found=True)
292
 
 
 
351
        
293
352
        # Create an empty weave
294
353
        sio = StringIO()
295
354
        weavefile.write_weave_v5(weave.Weave(), sio)
296
355
        empty_weave = sio.getvalue()
297
356
 
298
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
299
 
 
 
357
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
358
        dirs = ['revision-store', 'weaves']
 
359
        files = [('inventory.weave', StringIO(empty_weave)),
 
360
                 ]
 
361
        
300
362
        # FIXME: RBC 20060125 don't peek under the covers
301
363
        # NB: no need to escape relative paths that are url safe.
302
364
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
303
 
            'branch-lock', lockable_files.TransportLock)
 
365
                                'branch-lock', lockable_files.TransportLock)
304
366
        control_files.create_lock()
305
367
        control_files.lock_write()
306
 
        transport = a_bzrdir.transport
 
368
        control_files._transport.mkdir_multi(dirs,
 
369
                mode=control_files._dir_mode)
307
370
        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())
 
371
            for file, content in files:
 
372
                control_files.put(file, content)
312
373
        finally:
313
374
            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
 
375
        return self.open(a_bzrdir, _found=True)
 
376
 
 
377
    def _get_control_store(self, repo_transport, control_files):
 
378
        """Return the control store for this repository."""
 
379
        return self._get_versioned_file_store('',
 
380
                                              repo_transport,
 
381
                                              control_files,
 
382
                                              prefixed=False)
 
383
 
 
384
    def _get_text_store(self, transport, control_files):
 
385
        """Get a store for file texts for this format."""
 
386
        raise NotImplementedError(self._get_text_store)
317
387
 
318
388
    def open(self, a_bzrdir, _found=False):
319
389
        """See RepositoryFormat.open()."""
322
392
            raise NotImplementedError
323
393
 
324
394
        repo_transport = a_bzrdir.get_repository_transport(None)
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
 
395
        control_files = a_bzrdir._control_files
 
396
        text_store = self._get_text_store(repo_transport, control_files)
 
397
        control_store = self._get_control_store(repo_transport, control_files)
 
398
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
399
        return AllInOneRepository(_format=self,
 
400
                                  a_bzrdir=a_bzrdir,
 
401
                                  _revision_store=_revision_store,
 
402
                                  control_store=control_store,
 
403
                                  text_store=text_store)
332
404
 
333
 
    def is_deprecated(self):
334
 
        return True
 
405
    def check_conversion_target(self, target_format):
 
406
        pass
335
407
 
336
408
 
337
409
class RepositoryFormat4(PreSplitOutRepositoryFormat):
346
418
    has been removed.
347
419
    """
348
420
 
349
 
    supports_funky_characters = False
 
421
    _matchingbzrdir = bzrdir.BzrDirFormat4()
350
422
 
351
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat4()
 
423
    def __init__(self):
 
424
        super(RepositoryFormat4, self).__init__()
352
425
 
353
426
    def get_format_description(self):
354
427
        """See RepositoryFormat.get_format_description()."""
362
435
        """Format 4 is not supported.
363
436
 
364
437
        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
 
438
        conversion logic is expensive - so doing it on the fly was not 
366
439
        feasible.
367
440
        """
368
441
        return False
369
442
 
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
 
443
    def _get_control_store(self, repo_transport, control_files):
 
444
        """Format 4 repositories have no formal control store at this point.
 
445
        
 
446
        This will cause any control-file-needing apis to fail - this is desired.
 
447
        """
 
448
        return None
 
449
    
 
450
    def _get_revision_store(self, repo_transport, control_files):
 
451
        """See RepositoryFormat._get_revision_store()."""
 
452
        from bzrlib.xml4 import serializer_v4
 
453
        return self._get_text_rev_store(repo_transport,
 
454
                                        control_files,
 
455
                                        'revision-store',
 
456
                                        serializer=serializer_v4)
 
457
 
 
458
    def _get_text_store(self, transport, control_files):
 
459
        """See RepositoryFormat._get_text_store()."""
387
460
 
388
461
 
389
462
class RepositoryFormat5(PreSplitOutRepositoryFormat):
396
469
    """
397
470
 
398
471
    _versionedfile_class = weave.WeaveFile
399
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat5()
400
 
    supports_funky_characters = False
 
472
    _matchingbzrdir = bzrdir.BzrDirFormat5()
401
473
 
402
 
    @property
403
 
    def _serializer(self):
404
 
        return xml5.serializer_v5
 
474
    def __init__(self):
 
475
        super(RepositoryFormat5, self).__init__()
405
476
 
406
477
    def get_format_description(self):
407
478
        """See RepositoryFormat.get_format_description()."""
408
479
        return "Weave repository format 5"
409
480
 
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)
 
481
    def _get_revision_store(self, repo_transport, control_files):
 
482
        """See RepositoryFormat._get_revision_store()."""
 
483
        """Return the revision store object for this a_bzrdir."""
 
484
        return self._get_text_rev_store(repo_transport,
 
485
                                        control_files,
 
486
                                        'revision-store',
 
487
                                        compressed=False)
 
488
 
 
489
    def _get_text_store(self, transport, control_files):
 
490
        """See RepositoryFormat._get_text_store()."""
 
491
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
434
492
 
435
493
 
436
494
class RepositoryFormat6(PreSplitOutRepositoryFormat):
443
501
    """
444
502
 
445
503
    _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
 
504
    _matchingbzrdir = bzrdir.BzrDirFormat6()
 
505
 
 
506
    def __init__(self):
 
507
        super(RepositoryFormat6, self).__init__()
451
508
 
452
509
    def get_format_description(self):
453
510
        """See RepositoryFormat.get_format_description()."""
454
511
        return "Weave repository format 6"
455
512
 
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):
 
513
    def _get_revision_store(self, repo_transport, control_files):
 
514
        """See RepositoryFormat._get_revision_store()."""
 
515
        return self._get_text_rev_store(repo_transport,
 
516
                                        control_files,
 
517
                                        'revision-store',
 
518
                                        compressed=False,
 
519
                                        prefixed=True)
 
520
 
 
521
    def _get_text_store(self, transport, control_files):
 
522
        """See RepositoryFormat._get_text_store()."""
 
523
        return self._get_versioned_file_store('weaves', transport, control_files)
 
524
 
 
525
class RepositoryFormat7(MetaDirRepositoryFormat):
483
526
    """Bzr repository 7.
484
527
 
485
528
    This repository format has:
493
536
 
494
537
    _versionedfile_class = weave.WeaveFile
495
538
    supports_ghosts = False
496
 
    supports_chks = False
497
 
    supports_funky_characters = False
498
 
    revision_graph_can_have_wrong_parents = False
499
539
 
500
 
    _fetch_order = 'topological'
501
 
    _fetch_reconcile = True
502
 
    fast_deltas = False
503
 
    @property
504
 
    def _serializer(self):
505
 
        return xml5.serializer_v5
 
540
    def _get_control_store(self, repo_transport, control_files):
 
541
        """Return the control store for this repository."""
 
542
        return self._get_versioned_file_store('',
 
543
                                              repo_transport,
 
544
                                              control_files,
 
545
                                              prefixed=False)
506
546
 
507
547
    def get_format_string(self):
508
548
        """See RepositoryFormat.get_format_string()."""
512
552
        """See RepositoryFormat.get_format_description()."""
513
553
        return "Weave repository format 7"
514
554
 
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)
 
555
    def check_conversion_target(self, target_format):
 
556
        pass
 
557
 
 
558
    def _get_revision_store(self, repo_transport, control_files):
 
559
        """See RepositoryFormat._get_revision_store()."""
 
560
        return self._get_text_rev_store(repo_transport,
 
561
                                        control_files,
 
562
                                        'revision-store',
 
563
                                        compressed=False,
 
564
                                        prefixed=True,
 
565
                                        )
 
566
 
 
567
    def _get_text_store(self, transport, control_files):
 
568
        """See RepositoryFormat._get_text_store()."""
 
569
        return self._get_versioned_file_store('weaves',
 
570
                                              transport,
 
571
                                              control_files)
535
572
 
536
573
    def initialize(self, a_bzrdir, shared=False):
537
574
        """Create a weave repository.
544
581
        weavefile.write_weave_v5(weave.Weave(), sio)
545
582
        empty_weave = sio.getvalue()
546
583
 
547
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
 
584
        mutter('creating repository in %s.', a_bzrdir.transport.base)
548
585
        dirs = ['revision-store', 'weaves']
549
 
        files = [('inventory.weave', StringIO(empty_weave)),
 
586
        files = [('inventory.weave', StringIO(empty_weave)), 
550
587
                 ]
551
588
        utf8_files = [('format', self.get_format_string())]
552
 
 
 
589
 
553
590
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
554
591
        return self.open(a_bzrdir=a_bzrdir, _found=True)
555
592
 
556
593
    def open(self, a_bzrdir, _found=False, _override_transport=None):
557
594
        """See RepositoryFormat.open().
558
 
 
 
595
        
559
596
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
560
597
                                    repository at a slightly different url
561
598
                                    than normal. I.e. during 'upgrade'.
562
599
        """
563
600
        if not _found:
564
601
            format = RepositoryFormat.find_format(a_bzrdir)
 
602
            assert format.__class__ ==  self.__class__
565
603
        if _override_transport is not None:
566
604
            repo_transport = _override_transport
567
605
        else:
568
606
            repo_transport = a_bzrdir.get_repository_transport(None)
569
607
        control_files = lockable_files.LockableFiles(repo_transport,
570
608
                                '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())]
 
609
        text_store = self._get_text_store(repo_transport, control_files)
 
610
        control_store = self._get_control_store(repo_transport, control_files)
 
611
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
612
        return WeaveMetaDirRepository(_format=self,
 
613
            a_bzrdir=a_bzrdir,
 
614
            control_files=control_files,
 
615
            _revision_store=_revision_store,
 
616
            control_store=control_store,
 
617
            text_store=text_store)
 
618
 
 
619
 
 
620
class WeaveCommitBuilder(CommitBuilder):
 
621
    """A builder for weave based repos that don't support ghosts."""
 
622
 
 
623
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
 
624
        versionedfile = self.repository.weave_store.get_weave_or_empty(
 
625
            file_id, self.repository.get_transaction())
 
626
        result = versionedfile.add_lines(
 
627
            self._new_revision_id, parents, new_lines,
 
628
            nostore_sha=nostore_sha)[0:2]
 
629
        versionedfile.clear_cache()
 
630
        return result
 
631
 
 
632
 
 
633
_legacy_formats = [RepositoryFormat4(),
 
634
                   RepositoryFormat5(),
 
635
                   RepositoryFormat6()]