~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
    """
52
52
 
53
53
    @needs_read_lock
 
54
    def _all_possible_ids(self):
 
55
        """Return all the possible revisions that we could find."""
 
56
        return self.get_inventory_weave().names()
 
57
 
 
58
    @needs_read_lock
54
59
    def all_revision_ids(self):
55
 
        """Returns a list of all the revision ids in the repository.
56
 
 
57
 
        It would be nice to have this topologically sorted, but its not yet.
58
 
        """
59
 
        possible_ids = self.get_inventory_weave().names()
 
60
        """Returns a list of all the revision ids in the repository. 
 
61
 
 
62
        These are in as much topological order as the underlying store can 
 
63
        present: for weaves ghosts may lead to a lack of correctness until
 
64
        the reweave updates the parents list.
 
65
        """
 
66
        result = self._all_possible_ids()
 
67
        return self._eliminate_revisions_not_present(result)
 
68
 
 
69
    @needs_read_lock
 
70
    def _eliminate_revisions_not_present(self, revision_ids):
 
71
        """Check every revision id in revision_ids to see if we have it.
 
72
 
 
73
        Returns a set of the present revisions.
 
74
        """
60
75
        result = []
61
 
        for id in possible_ids:
 
76
        for id in revision_ids:
62
77
            if self.has_revision(id):
63
78
               result.append(id)
64
79
        return result
161
176
    def lock_read(self):
162
177
        self.control_files.lock_read()
163
178
 
 
179
    @needs_read_lock
 
180
    def missing_revision_ids(self, other, revision_id=None):
 
181
        """Return the revision ids that other has that this does not.
 
182
        
 
183
        These are returned in topological order.
 
184
 
 
185
        revision_id: only return revision ids included by revision_id.
 
186
        """
 
187
        if self._compatible_formats(other):
 
188
            # fast path for weave-inventory based stores.
 
189
            # we want all revisions to satisft revision_id in other.
 
190
            # but we dont want to stat every file here and there.
 
191
            # we want then, all revisions other needs to satisfy revision_id 
 
192
            # checked, but not those that we have locally.
 
193
            # so the first thing is to get a subset of the revisions to 
 
194
            # satisfy revision_id in other, and then eliminate those that
 
195
            # we do already have. 
 
196
            # this is slow on high latency connection to self, but as as this
 
197
            # disk format scales terribly for push anyway due to rewriting 
 
198
            # inventory.weave, this is considered acceptable.
 
199
            # - RBC 20060209
 
200
            if revision_id is not None:
 
201
                other_ids = other.get_ancestry(revision_id)
 
202
                assert other_ids.pop(0) == None
 
203
            else:
 
204
                other_ids = other._all_possible_ids()
 
205
            other_ids_set = set(other_ids)
 
206
            # other ids is the worst case to pull now.
 
207
            # now we want to filter other_ids against what we actually
 
208
            # have, but dont try to stat what we know we dont.
 
209
            my_ids = set(self._all_possible_ids())
 
210
            possibly_present_revisions = my_ids.intersection(other_ids_set)
 
211
            actually_present_revisions = set(self._eliminate_revisions_not_present(possibly_present_revisions))
 
212
            required_revisions = other_ids_set.difference(actually_present_revisions)
 
213
            required_topo_revisions = [rev_id for rev_id in other_ids if rev_id in required_revisions]
 
214
            if revision_id is not None:
 
215
                # we used get_ancestry to determine other_ids then we are assured all
 
216
                # revisions referenced are present as they are installed in topological order.
 
217
                return required_topo_revisions
 
218
            else:
 
219
                # we only have an estimate of whats available
 
220
                return other._eliminate_revisions_not_present(required_topo_revisions)
 
221
        # slow code path.
 
222
        my_ids = set(self.all_revision_ids())
 
223
        if revision_id is not None:
 
224
            other_ids = other.get_ancestry(revision_id)
 
225
            assert other_ids.pop(0) == None
 
226
        else:
 
227
            other_ids = other.all_revision_ids()
 
228
        result_set = set(other_ids).difference(my_ids)
 
229
        return [rev_id for rev_id in other_ids if rev_id in result_set]
 
230
 
164
231
    @staticmethod
165
232
    def open(base):
166
233
        """Open the repository rooted at base.
171
238
        control = bzrdir.BzrDir.open(base)
172
239
        return control.open_repository()
173
240
 
174
 
    def push_stores(self, to, revision=NULL_REVISION):
175
 
        """FIXME: document and find a consistent name with other classes."""
176
 
        if (not isinstance(self._format, RepositoryFormat4) or
177
 
            self._format != to._format):
178
 
            from bzrlib.fetch import RepoFetcher
179
 
            mutter("Using fetch logic to push between %s(%s) and %s(%s)",
180
 
                   self, self._format, to, to._format)
181
 
            RepoFetcher(to_repository=to, from_repository=self,
182
 
                         last_revision=revision)
183
 
            return
 
241
    def _compatible_formats(self, other):
 
242
        """Return True if the stores in self and other are 'compatible'
 
243
        
 
244
        'compatible' means that they are both the same underlying type
 
245
        i.e. both weave stores, or both knits and thus support fast-path
 
246
        operations."""
 
247
        return (isinstance(self._format, (RepositoryFormat5,
 
248
                                          RepositoryFormat6,
 
249
                                          RepositoryFormat7)) and
 
250
                isinstance(other._format, (RepositoryFormat5,
 
251
                                           RepositoryFormat6,
 
252
                                           RepositoryFormat7)))
184
253
 
185
 
        # format 4 to format 4 logic only.
186
 
        store_pairs = ((self.text_store,      to.text_store),
187
 
                       (self.inventory_store, to.inventory_store),
188
 
                       (self.revision_store,  to.revision_store))
 
254
    @needs_read_lock
 
255
    def copy_content_into(self, destination, revision_id=None, basis=None):
 
256
        """Make a complete copy of the content in self into destination."""
 
257
        destination.lock_write()
189
258
        try:
190
 
            for from_store, to_store in store_pairs: 
191
 
                copy_all(from_store, to_store)
192
 
        except UnlistableStore:
193
 
            raise UnlistableBranch(from_store)
 
259
            # optimised paths:
 
260
            # compatible stores
 
261
            if self._compatible_formats(destination):
 
262
                if basis is not None:
 
263
                    # copy the basis in, then fetch remaining data.
 
264
                    basis.copy_content_into(destination, revision_id)
 
265
                    destination.fetch(self, revision_id=revision_id)
 
266
                else:
 
267
                    # FIXME do not peek!
 
268
                    if self.control_files._transport.listable():
 
269
                        destination.control_weaves.copy_multi(self.control_weaves,
 
270
                                                              ['inventory'])
 
271
                        copy_all(self.weave_store, destination.weave_store)
 
272
                        copy_all(self.revision_store, destination.revision_store)
 
273
                    else:
 
274
                        destination.fetch(self, revision_id=revision_id)
 
275
            # compatible v4 stores
 
276
            elif isinstance(self._format, RepositoryFormat4):
 
277
                if not isinstance(destination._format, RepositoryFormat4):
 
278
                    raise BzrError('cannot copy v4 branches to anything other than v4 branches.')
 
279
                store_pairs = ((self.text_store,      destination.text_store),
 
280
                               (self.inventory_store, destination.inventory_store),
 
281
                               (self.revision_store,  destination.revision_store))
 
282
                try:
 
283
                    for from_store, to_store in store_pairs: 
 
284
                        copy_all(from_store, to_store)
 
285
                except UnlistableStore:
 
286
                    raise UnlistableBranch(from_store)
 
287
            # fallback - 'fetch'
 
288
            else:
 
289
                destination.fetch(self, revision_id=revision_id)
 
290
        finally:
 
291
            destination.unlock()
 
292
 
 
293
    @needs_write_lock
 
294
    def fetch(self, source, revision_id=None):
 
295
        """Fetch the content required to construct revision_id from source.
 
296
 
 
297
        If revision_id is None all content is copied.
 
298
        """
 
299
        from bzrlib.fetch import RepoFetcher
 
300
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
301
               source, source._format, self, self._format)
 
302
        RepoFetcher(to_repository=self, from_repository=source, last_revision=revision_id)
194
303
 
195
304
    def unlock(self):
196
305
        self.control_files.unlock()
197
306
 
198
307
    @needs_read_lock
199
 
    def clone(self, a_bzrdir):
 
308
    def clone(self, a_bzrdir, revision_id=None, basis=None):
200
309
        """Clone this repository into a_bzrdir using the current format.
201
310
 
202
311
        Currently no check is made that the format of this repository and
203
312
        the bzrdir format are compatible. FIXME RBC 20060201.
204
313
        """
205
 
        result = self._format.initialize(a_bzrdir)
206
 
        self.copy(result)
 
314
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
 
315
            # use target default format.
 
316
            result = a_bzrdir.create_repository()
 
317
        # FIXME RBC 20060209 split out the repository type to avoid this check ?
 
318
        elif isinstance(a_bzrdir._format,
 
319
                      (bzrdir.BzrDirFormat4,
 
320
                       bzrdir.BzrDirFormat5,
 
321
                       bzrdir.BzrDirFormat6)):
 
322
            result = a_bzrdir.open_repository()
 
323
        else:
 
324
            result = self._format.initialize(a_bzrdir)
 
325
        self.copy_content_into(result, revision_id, basis)
207
326
        return result
208
327
 
209
 
    @needs_read_lock
210
 
    def copy(self, destination):
211
 
        destination.lock_write()
212
 
        try:
213
 
            destination.control_weaves.copy_multi(self.control_weaves, 
214
 
                ['inventory'])
215
 
            copy_all(self.weave_store, destination.weave_store)
216
 
            copy_all(self.revision_store, destination.revision_store)
217
 
        finally:
218
 
            destination.unlock()
219
 
 
220
328
    def has_revision(self, revision_id):
221
329
        """True if this branch has a copy of the revision.
222
330
 
521
629
        """
522
630
        raise NotImplementedError(self.get_format_string)
523
631
 
524
 
    def initialize(self, a_bzrdir):
 
632
    def initialize(self, a_bzrdir, _internal=False):
525
633
        """Create a weave repository.
526
634
        
527
635
        TODO: when creating split out bzr branch formats, move this to a common
529
637
        """
530
638
        from bzrlib.weavefile import write_weave_v5
531
639
        from bzrlib.weave import Weave
 
640
 
 
641
        if not _internal:
 
642
            # always initialized when the bzrdir is.
 
643
            return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
532
644
        
533
645
        # Create an empty weave
534
646
        sio = StringIO()
603
715
        super(RepositoryFormat4, self).__init__()
604
716
        self._matchingbzrdir = bzrdir.BzrDirFormat4()
605
717
 
606
 
    def initialize(self, url):
 
718
    def initialize(self, url, _internal=False):
607
719
        """Format 4 branches cannot be created."""
608
720
        raise errors.UninitializableFormat(self)
609
721