~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 14:01:20 UTC
  • mfrom: (3280.2.5 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080316140120-i3yq8yr1l66m11h7
Start 1.4 development

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