~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/weaverepo.py

  • Committer: Ian Clatworthy
  • Date: 2007-12-11 02:07:30 UTC
  • mto: (3119.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 3120.
  • Revision ID: ian.clatworthy@internode.on.net-20071211020730-sdj4kj794dw0628e
make help topics more discoverable

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]
 
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
175
178
 
 
179
    @needs_read_lock
176
180
    def is_shared(self):
177
181
        """AllInOne repositories cannot be shared."""
178
182
        return False
187
191
        :param new_value: True to restore the default, False to disable making
188
192
                          working trees.
189
193
        """
190
 
        raise errors.RepositoryUpgradeRequired(self.user_url)
191
 
 
 
194
        raise NotImplementedError(self.set_make_working_trees)
 
195
    
192
196
    def make_working_trees(self):
193
197
        """Returns the policy for making working trees on new branches."""
194
198
        return True
195
199
 
196
 
 
197
 
class WeaveMetaDirRepository(MetaDirVersionedFileRepository):
 
200
    def revision_graph_can_have_wrong_parents(self):
 
201
        # XXX: This is an old format that we don't support full checking on, so
 
202
        # just claim that checking for this inconsistency is not required.
 
203
        return False
 
204
 
 
205
 
 
206
class WeaveMetaDirRepository(MetaDirRepository):
198
207
    """A subclass of MetaDirRepository to set weave specific policy."""
199
208
 
200
 
    def __init__(self, _format, a_bzrdir, control_files):
201
 
        super(WeaveMetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
202
 
        self._serializer = _format._serializer
 
209
    _serializer = xml5.serializer_v5
203
210
 
204
211
    @needs_read_lock
205
212
    def _all_possible_ids(self):
206
213
        """Return all the possible revisions that we could find."""
207
214
        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()]
 
215
            mutter_callsite(3, "_all_possible_ids scales with size of history.")
 
216
        return self.get_inventory_weave().versions()
211
217
 
212
218
    @needs_read_lock
213
219
    def _all_revision_ids(self):
214
 
        """Returns a list of all the revision ids in the repository.
 
220
        """Returns a list of all the revision ids in the repository. 
215
221
 
216
 
        These are in as much topological order as the underlying store can
 
222
        These are in as much topological order as the underlying store can 
217
223
        present: for weaves ghosts may lead to a lack of correctness until
218
224
        the reweave updates the parents list.
219
225
        """
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')
 
226
        if self._revision_store.text_store.listable():
 
227
            return self._revision_store.all_revision_ids(self.get_transaction())
 
228
        result = self._all_possible_ids()
 
229
        # TODO: jam 20070210 Ensure that _all_possible_ids returns non-unicode
 
230
        #       ids. (It should, since _revision_store's API should change to
 
231
        #       return utf8 revision_ids)
 
232
        return self._eliminate_revisions_not_present(result)
 
233
 
 
234
    def _check_revision_parents(self, revision, inventory):
 
235
        """Private to Repository and Fetch.
 
236
        
 
237
        This checks the parentage of revision in an inventory weave for 
 
238
        consistency and is only applicable to inventory-weave-for-ancestry
 
239
        using repository formats & fetchers.
 
240
        """
 
241
        weave_parents = inventory.get_parents(revision.revision_id)
 
242
        weave_names = inventory.versions()
 
243
        for parent_id in revision.parent_ids:
 
244
            if parent_id in weave_names:
 
245
                # this parent must not be a ghost.
 
246
                if not parent_id in weave_parents:
 
247
                    # but it is a ghost
 
248
                    raise errors.CorruptRepository(self)
239
249
 
240
250
    def get_commit_builder(self, branch, parents, config, timestamp=None,
241
251
                           timezone=None, committer=None, revprops=None,
242
 
                           revision_id=None, lossy=False):
 
252
                           revision_id=None):
243
253
        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)
 
254
        result = WeaveCommitBuilder(self, parents, config, timestamp, timezone,
 
255
                              committer, revprops, revision_id)
246
256
        self.start_write_group()
247
257
        return result
248
258
 
249
259
    @needs_read_lock
250
260
    def get_revision(self, revision_id):
251
261
        """Return the Revision object for a named revision"""
 
262
        # TODO: jam 20070210 get_revision_reconcile should do this for us
252
263
        r = self.get_revision_reconcile(revision_id)
 
264
        # weave corruption can lead to absent revision markers that should be
 
265
        # present.
 
266
        # the following test is reasonably cheap (it needs a single weave read)
 
267
        # and the weave is cached in read transactions. In write transactions
 
268
        # it is not cached but typically we only read a small number of
 
269
        # revisions. For knits when they are introduced we will probably want
 
270
        # to ensure that caching write transactions are in use.
 
271
        inv = self.get_inventory_weave()
 
272
        self._check_revision_parents(r, inv)
253
273
        return r
254
274
 
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):
 
275
    @needs_read_lock
 
276
    def get_revision_graph(self, revision_id=None):
 
277
        """Return a dictionary containing the revision graph.
 
278
        
 
279
        :param revision_id: The revision_id to get a graph from. If None, then
 
280
        the entire revision graph is returned. This is a deprecated mode of
 
281
        operation and will be removed in the future.
 
282
        :return: a dictionary of revision_id->revision_parents_list.
 
283
        """
 
284
        if 'evil' in debug.debug_flags:
 
285
            mutter_callsite(3,
 
286
                "get_revision_graph scales with size of history.")
 
287
        # special case NULL_REVISION
 
288
        if revision_id == _mod_revision.NULL_REVISION:
 
289
            return {}
 
290
        a_weave = self.get_inventory_weave()
 
291
        all_revisions = self._eliminate_revisions_not_present(
 
292
                                a_weave.versions())
 
293
        entire_graph = dict([(node, tuple(a_weave.get_parents(node))) for 
 
294
                             node in all_revisions])
 
295
        if revision_id is None:
 
296
            return entire_graph
 
297
        elif revision_id not in entire_graph:
 
298
            raise errors.NoSuchRevision(self, revision_id)
 
299
        else:
 
300
            # add what can be reached from revision_id
 
301
            result = {}
 
302
            pending = set([revision_id])
 
303
            while len(pending) > 0:
 
304
                node = pending.pop()
 
305
                result[node] = entire_graph[node]
 
306
                for revision_id in result[node]:
 
307
                    if revision_id not in result:
 
308
                        pending.add(revision_id)
 
309
            return result
 
310
 
 
311
    def revision_graph_can_have_wrong_parents(self):
 
312
        # XXX: This is an old format that we don't support full checking on, so
 
313
        # just claim that checking for this inconsistency is not required.
 
314
        return False
 
315
 
 
316
 
 
317
class PreSplitOutRepositoryFormat(RepositoryFormat):
268
318
    """Base class for the pre split out repository formats."""
269
319
 
270
320
    rich_root_data = False
271
321
    supports_tree_reference = False
272
322
    supports_ghosts = False
273
 
    supports_external_lookups = False
274
 
    supports_chks = False
275
 
    _fetch_order = 'topological'
276
 
    _fetch_reconcile = True
277
 
    fast_deltas = False
278
 
    supports_leaving_lock = False
279
 
    # XXX: This is an old format that we don't support full checking on, so
280
 
    # just claim that checking for this inconsistency is not required.
281
 
    revision_graph_can_have_wrong_parents = False
282
323
 
283
324
    def initialize(self, a_bzrdir, shared=False, _internal=False):
284
325
        """Create a weave repository."""
288
329
        if not _internal:
289
330
            # always initialized when the bzrdir is.
290
331
            return self.open(a_bzrdir, _found=True)
291
 
 
 
332
        
292
333
        # Create an empty weave
293
334
        sio = StringIO()
294
335
        weavefile.write_weave_v5(weave.Weave(), sio)
295
336
        empty_weave = sio.getvalue()
296
337
 
297
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
298
 
 
 
338
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
339
        dirs = ['revision-store', 'weaves']
 
340
        files = [('inventory.weave', StringIO(empty_weave)),
 
341
                 ]
 
342
        
299
343
        # FIXME: RBC 20060125 don't peek under the covers
300
344
        # NB: no need to escape relative paths that are url safe.
301
345
        control_files = lockable_files.LockableFiles(a_bzrdir.transport,
302
 
            'branch-lock', lockable_files.TransportLock)
 
346
                                'branch-lock', lockable_files.TransportLock)
303
347
        control_files.create_lock()
304
348
        control_files.lock_write()
305
 
        transport = a_bzrdir.transport
 
349
        control_files._transport.mkdir_multi(dirs,
 
350
                mode=control_files._dir_mode)
306
351
        try:
307
 
            transport.mkdir_multi(['revision-store', 'weaves'],
308
 
                mode=a_bzrdir._get_dir_mode())
309
 
            transport.put_bytes_non_atomic('inventory.weave', empty_weave,
310
 
                mode=a_bzrdir._get_file_mode())
 
352
            for file, content in files:
 
353
                control_files.put(file, content)
311
354
        finally:
312
355
            control_files.unlock()
313
 
        repository = self.open(a_bzrdir, _found=True)
314
 
        self._run_post_repo_init_hooks(repository, a_bzrdir, shared)
315
 
        return repository
 
356
        return self.open(a_bzrdir, _found=True)
 
357
 
 
358
    def _get_control_store(self, repo_transport, control_files):
 
359
        """Return the control store for this repository."""
 
360
        return self._get_versioned_file_store('',
 
361
                                              repo_transport,
 
362
                                              control_files,
 
363
                                              prefixed=False)
 
364
 
 
365
    def _get_text_store(self, transport, control_files):
 
366
        """Get a store for file texts for this format."""
 
367
        raise NotImplementedError(self._get_text_store)
316
368
 
317
369
    def open(self, a_bzrdir, _found=False):
318
370
        """See RepositoryFormat.open()."""
322
374
 
323
375
        repo_transport = a_bzrdir.get_repository_transport(None)
324
376
        control_files = a_bzrdir._control_files
325
 
        result = AllInOneRepository(_format=self, a_bzrdir=a_bzrdir)
326
 
        result.revisions = self._get_revisions(repo_transport, result)
327
 
        result.signatures = self._get_signatures(repo_transport, result)
328
 
        result.inventories = self._get_inventories(repo_transport, result)
329
 
        result.texts = self._get_texts(repo_transport, result)
330
 
        result.chk_bytes = None
331
 
        return result
 
377
        text_store = self._get_text_store(repo_transport, control_files)
 
378
        control_store = self._get_control_store(repo_transport, control_files)
 
379
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
380
        return AllInOneRepository(_format=self,
 
381
                                  a_bzrdir=a_bzrdir,
 
382
                                  _revision_store=_revision_store,
 
383
                                  control_store=control_store,
 
384
                                  text_store=text_store)
332
385
 
333
 
    def is_deprecated(self):
334
 
        return True
 
386
    def check_conversion_target(self, target_format):
 
387
        pass
335
388
 
336
389
 
337
390
class RepositoryFormat4(PreSplitOutRepositoryFormat):
346
399
    has been removed.
347
400
    """
348
401
 
349
 
    supports_funky_characters = False
 
402
    _matchingbzrdir = bzrdir.BzrDirFormat4()
350
403
 
351
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat4()
 
404
    def __init__(self):
 
405
        super(RepositoryFormat4, self).__init__()
352
406
 
353
407
    def get_format_description(self):
354
408
        """See RepositoryFormat.get_format_description()."""
362
416
        """Format 4 is not supported.
363
417
 
364
418
        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
 
419
        conversion logic is expensive - so doing it on the fly was not 
366
420
        feasible.
367
421
        """
368
422
        return False
369
423
 
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
 
424
    def _get_control_store(self, repo_transport, control_files):
 
425
        """Format 4 repositories have no formal control store at this point.
 
426
        
 
427
        This will cause any control-file-needing apis to fail - this is desired.
 
428
        """
 
429
        return None
 
430
    
 
431
    def _get_revision_store(self, repo_transport, control_files):
 
432
        """See RepositoryFormat._get_revision_store()."""
 
433
        from bzrlib.xml4 import serializer_v4
 
434
        return self._get_text_rev_store(repo_transport,
 
435
                                        control_files,
 
436
                                        'revision-store',
 
437
                                        serializer=serializer_v4)
 
438
 
 
439
    def _get_text_store(self, transport, control_files):
 
440
        """See RepositoryFormat._get_text_store()."""
387
441
 
388
442
 
389
443
class RepositoryFormat5(PreSplitOutRepositoryFormat):
396
450
    """
397
451
 
398
452
    _versionedfile_class = weave.WeaveFile
399
 
    _matchingbzrdir = weave_bzrdir.BzrDirFormat5()
400
 
    supports_funky_characters = False
 
453
    _matchingbzrdir = bzrdir.BzrDirFormat5()
401
454
 
402
 
    @property
403
 
    def _serializer(self):
404
 
        return xml5.serializer_v5
 
455
    def __init__(self):
 
456
        super(RepositoryFormat5, self).__init__()
405
457
 
406
458
    def get_format_description(self):
407
459
        """See RepositoryFormat.get_format_description()."""
408
460
        return "Weave repository format 5"
409
461
 
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)
 
462
    def _get_revision_store(self, repo_transport, control_files):
 
463
        """See RepositoryFormat._get_revision_store()."""
 
464
        """Return the revision store object for this a_bzrdir."""
 
465
        return self._get_text_rev_store(repo_transport,
 
466
                                        control_files,
 
467
                                        'revision-store',
 
468
                                        compressed=False)
 
469
 
 
470
    def _get_text_store(self, transport, control_files):
 
471
        """See RepositoryFormat._get_text_store()."""
 
472
        return self._get_versioned_file_store('weaves', transport, control_files, prefixed=False)
434
473
 
435
474
 
436
475
class RepositoryFormat6(PreSplitOutRepositoryFormat):
443
482
    """
444
483
 
445
484
    _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
 
485
    _matchingbzrdir = bzrdir.BzrDirFormat6()
 
486
 
 
487
    def __init__(self):
 
488
        super(RepositoryFormat6, self).__init__()
451
489
 
452
490
    def get_format_description(self):
453
491
        """See RepositoryFormat.get_format_description()."""
454
492
        return "Weave repository format 6"
455
493
 
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):
 
494
    def _get_revision_store(self, repo_transport, control_files):
 
495
        """See RepositoryFormat._get_revision_store()."""
 
496
        return self._get_text_rev_store(repo_transport,
 
497
                                        control_files,
 
498
                                        'revision-store',
 
499
                                        compressed=False,
 
500
                                        prefixed=True)
 
501
 
 
502
    def _get_text_store(self, transport, control_files):
 
503
        """See RepositoryFormat._get_text_store()."""
 
504
        return self._get_versioned_file_store('weaves', transport, control_files)
 
505
 
 
506
class RepositoryFormat7(MetaDirRepositoryFormat):
483
507
    """Bzr repository 7.
484
508
 
485
509
    This repository format has:
493
517
 
494
518
    _versionedfile_class = weave.WeaveFile
495
519
    supports_ghosts = False
496
 
    supports_chks = False
497
 
    supports_funky_characters = False
498
 
    revision_graph_can_have_wrong_parents = False
499
520
 
500
 
    _fetch_order = 'topological'
501
 
    _fetch_reconcile = True
502
 
    fast_deltas = False
503
 
    @property
504
 
    def _serializer(self):
505
 
        return xml5.serializer_v5
 
521
    def _get_control_store(self, repo_transport, control_files):
 
522
        """Return the control store for this repository."""
 
523
        return self._get_versioned_file_store('',
 
524
                                              repo_transport,
 
525
                                              control_files,
 
526
                                              prefixed=False)
506
527
 
507
528
    def get_format_string(self):
508
529
        """See RepositoryFormat.get_format_string()."""
512
533
        """See RepositoryFormat.get_format_description()."""
513
534
        return "Weave repository format 7"
514
535
 
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)
 
536
    def check_conversion_target(self, target_format):
 
537
        pass
 
538
 
 
539
    def _get_revision_store(self, repo_transport, control_files):
 
540
        """See RepositoryFormat._get_revision_store()."""
 
541
        return self._get_text_rev_store(repo_transport,
 
542
                                        control_files,
 
543
                                        'revision-store',
 
544
                                        compressed=False,
 
545
                                        prefixed=True,
 
546
                                        )
 
547
 
 
548
    def _get_text_store(self, transport, control_files):
 
549
        """See RepositoryFormat._get_text_store()."""
 
550
        return self._get_versioned_file_store('weaves',
 
551
                                              transport,
 
552
                                              control_files)
535
553
 
536
554
    def initialize(self, a_bzrdir, shared=False):
537
555
        """Create a weave repository.
544
562
        weavefile.write_weave_v5(weave.Weave(), sio)
545
563
        empty_weave = sio.getvalue()
546
564
 
547
 
        trace.mutter('creating repository in %s.', a_bzrdir.transport.base)
 
565
        mutter('creating repository in %s.', a_bzrdir.transport.base)
548
566
        dirs = ['revision-store', 'weaves']
549
 
        files = [('inventory.weave', StringIO(empty_weave)),
 
567
        files = [('inventory.weave', StringIO(empty_weave)), 
550
568
                 ]
551
569
        utf8_files = [('format', self.get_format_string())]
552
 
 
 
570
 
553
571
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
554
572
        return self.open(a_bzrdir=a_bzrdir, _found=True)
555
573
 
556
574
    def open(self, a_bzrdir, _found=False, _override_transport=None):
557
575
        """See RepositoryFormat.open().
558
 
 
 
576
        
559
577
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
560
578
                                    repository at a slightly different url
561
579
                                    than normal. I.e. during 'upgrade'.
562
580
        """
563
581
        if not _found:
564
582
            format = RepositoryFormat.find_format(a_bzrdir)
 
583
            assert format.__class__ ==  self.__class__
565
584
        if _override_transport is not None:
566
585
            repo_transport = _override_transport
567
586
        else:
568
587
            repo_transport = a_bzrdir.get_repository_transport(None)
569
588
        control_files = lockable_files.LockableFiles(repo_transport,
570
589
                                '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())]
 
590
        text_store = self._get_text_store(repo_transport, control_files)
 
591
        control_store = self._get_control_store(repo_transport, control_files)
 
592
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
593
        return WeaveMetaDirRepository(_format=self,
 
594
            a_bzrdir=a_bzrdir,
 
595
            control_files=control_files,
 
596
            _revision_store=_revision_store,
 
597
            control_store=control_store,
 
598
            text_store=text_store)
 
599
 
 
600
 
 
601
class WeaveCommitBuilder(CommitBuilder):
 
602
    """A builder for weave based repos that don't support ghosts."""
 
603
 
 
604
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
 
605
        versionedfile = self.repository.weave_store.get_weave_or_empty(
 
606
            file_id, self.repository.get_transaction())
 
607
        result = versionedfile.add_lines(
 
608
            self._new_revision_id, parents, new_lines,
 
609
            nostore_sha=nostore_sha)[0:2]
 
610
        versionedfile.clear_cache()
 
611
        return result
 
612
 
 
613
 
 
614
_legacy_formats = [RepositoryFormat4(),
 
615
                   RepositoryFormat5(),
 
616
                   RepositoryFormat6()]