~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/fetch.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-11 21:41:24 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080711214124-qi09irlj7pd5cuzg
Shortcut the case when one revision is in the ancestry of the other.

At the cost of a heads() check, when one parent supersedes, we don't have to extract
the text for the other. Changes merge time from 3m37s => 3m21s. Using a
CachingParentsProvider would drop the time down to 3m11s.

Show diffs side-by-side

added added

removed removed

Lines of Context:
81
81
        """Create a repo fetcher.
82
82
 
83
83
        :param find_ghosts: If True search the entire history for ghosts.
84
 
        :param _write_group_acquired_callable: Don't use; this parameter only
85
 
            exists to facilitate a hack done in InterPackRepo.fetch.  We would
86
 
            like to remove this parameter.
87
84
        """
88
85
        # result variables.
89
86
        self.failed_revisions = []
112
109
                try:
113
110
                    self.__fetch()
114
111
                except:
115
 
                    self.to_repository.abort_write_group(suppress_errors=True)
 
112
                    self.to_repository.abort_write_group()
116
113
                    raise
117
114
                else:
118
115
                    self.to_repository.commit_write_group()
182
179
                    to_texts = self.to_repository.texts
183
180
                    from_texts = self.from_repository.texts
184
181
                    to_texts.insert_record_stream(from_texts.get_record_stream(
185
 
                        text_keys, self.to_repository._fetch_order,
186
 
                        not self.to_repository._fetch_uses_deltas))
 
182
                        text_keys, 'topological', False))
187
183
                    # Cause an error if a text occurs after we have done the
188
184
                    # copy.
189
185
                    text_keys = None
202
198
                    self._fetch_revision_texts(revs, pb)
203
199
                else:
204
200
                    raise AssertionError("Unknown knit kind %r" % knit_kind)
205
 
            if self.to_repository._fetch_reconcile:
206
 
                self.to_repository.reconcile()
207
201
        finally:
208
202
            if pb is not None:
209
203
                pb.finished()
232
226
    def _fetch_inventory_weave(self, revs, pb):
233
227
        pb.update("fetch inventory", 0, 2)
234
228
        to_weave = self.to_repository.inventories
235
 
        # just merge, this is optimisable and its means we don't
236
 
        # copy unreferenced data such as not-needed inventories.
237
 
        pb.update("fetch inventory", 1, 3)
238
 
        from_weave = self.from_repository.inventories
239
 
        pb.update("fetch inventory", 2, 3)
240
 
        # we fetch only the referenced inventories because we do not
241
 
        # know for unselected inventories whether all their required
242
 
        # texts are present in the other repository - it could be
243
 
        # corrupt.
244
 
        to_weave.insert_record_stream(from_weave.get_record_stream(
245
 
            [(rev_id,) for rev_id in revs],
246
 
            self.to_repository._fetch_order,
247
 
            not self.to_repository._fetch_uses_deltas))
248
 
 
249
 
    def _fetch_revision_texts(self, revs, pb):
250
 
        # fetch signatures first and then the revision texts
 
229
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
230
        try:
 
231
            # just merge, this is optimisable and its means we don't
 
232
            # copy unreferenced data such as not-needed inventories.
 
233
            pb.update("fetch inventory", 1, 3)
 
234
            from_weave = self.from_repository.inventories
 
235
            pb.update("fetch inventory", 2, 3)
 
236
            # we fetch only the referenced inventories because we do not
 
237
            # know for unselected inventories whether all their required
 
238
            # texts are present in the other repository - it could be
 
239
            # corrupt.
 
240
            to_weave.insert_record_stream(from_weave.get_record_stream(
 
241
                [(rev_id,) for rev_id in revs],
 
242
                'topological', False))
 
243
        finally:
 
244
            child_pb.finished()
 
245
 
 
246
    def _generate_root_texts(self, revs):
 
247
        """This will be called by __fetch between fetching weave texts and
 
248
        fetching the inventory weave.
 
249
 
 
250
        Subclasses should override this if they need to generate root texts
 
251
        after fetching weave texts.
 
252
        """
 
253
        pass
 
254
 
 
255
 
 
256
class GenericRepoFetcher(RepoFetcher):
 
257
    """This is a generic repo to repo fetcher.
 
258
 
 
259
    This makes minimal assumptions about repo layout and contents.
 
260
    It triggers a reconciliation after fetching to ensure integrity.
 
261
    """
 
262
 
 
263
    def _fetch_revision_texts(self, revs, pb):
 
264
        """Fetch revision object texts"""
 
265
        count = 0
 
266
        total = len(revs)
 
267
        for rev in revs:
 
268
            pb.update('copying revisions', count, total)
 
269
            try:
 
270
                sig_text = self.from_repository.get_signature_text(rev)
 
271
                self.to_repository.add_signature_text(rev, sig_text)
 
272
            except errors.NoSuchRevision:
 
273
                # not signed.
 
274
                pass
 
275
            self._copy_revision(rev)
 
276
            count += 1
 
277
        # fixup inventory if needed: 
 
278
        # this is expensive because we have no inverse index to current ghosts.
 
279
        # but on local disk its a few seconds and sftp push is already insane.
 
280
        # so we just-do-it.
 
281
        # FIXME: repository should inform if this is needed.
 
282
        self.to_repository.reconcile()
 
283
 
 
284
    def _copy_revision(self, rev_id):
 
285
        rev = self.from_repository.get_revision(rev_id)
 
286
        self.to_repository.add_revision(rev_id, rev)
 
287
 
 
288
 
 
289
class KnitRepoFetcher(RepoFetcher):
 
290
    """This is a knit format repository specific fetcher.
 
291
 
 
292
    This differs from the GenericRepoFetcher by not doing a 
 
293
    reconciliation after copying, and using knit joining to
 
294
    copy revision texts.
 
295
    """
 
296
 
 
297
    def _fetch_revision_texts(self, revs, pb):
251
298
        # may need to be a InterRevisionStore call here.
252
299
        to_sf = self.to_repository.signatures
253
300
        from_sf = self.from_repository.signatures
254
301
        # A missing signature is just skipped.
255
302
        to_sf.insert_record_stream(filter_absent(from_sf.get_record_stream(
256
303
            [(rev_id,) for rev_id in revs],
257
 
            self.to_repository._fetch_order,
258
 
            not self.to_repository._fetch_uses_deltas)))
 
304
            'unordered', False)))
259
305
        self._fetch_just_revision_texts(revs)
260
306
 
261
307
    def _fetch_just_revision_texts(self, version_ids):
262
308
        to_rf = self.to_repository.revisions
263
309
        from_rf = self.from_repository.revisions
264
 
        # If a revision has a delta, this is actually expanded inside the
265
 
        # insert_record_stream code now, which is an alternate fix for
266
 
        # bug #261339
267
310
        to_rf.insert_record_stream(from_rf.get_record_stream(
268
311
            [(rev_id,) for rev_id in version_ids],
269
 
            self.to_repository._fetch_order,
270
 
            not self.to_repository._fetch_uses_deltas))
271
 
 
272
 
    def _generate_root_texts(self, revs):
273
 
        """This will be called by __fetch between fetching weave texts and
274
 
        fetching the inventory weave.
275
 
 
276
 
        Subclasses should override this if they need to generate root texts
277
 
        after fetching weave texts.
278
 
        """
279
 
        pass
 
312
            'topological', False))
280
313
 
281
314
 
282
315
class Inter1and2Helper(object):
383
416
                                      parents)
384
417
 
385
418
    def fetch_revisions(self, revision_ids):
386
 
        # TODO: should this batch them up rather than requesting 10,000
387
 
        #       revisions at once?
388
419
        for revision in self.source.get_revisions(revision_ids):
389
420
            self.target.add_revision(revision.revision_id, revision)
390
421
 
391
422
 
392
 
class Model1toKnit2Fetcher(RepoFetcher):
 
423
class Model1toKnit2Fetcher(GenericRepoFetcher):
393
424
    """Fetch from a Model1 repository into a Knit2 repository
394
425
    """
395
426
    def __init__(self, to_repository, from_repository, last_revision=None,
396
427
                 pb=None, find_ghosts=True):
397
428
        self.helper = Inter1and2Helper(from_repository, to_repository)
398
 
        RepoFetcher.__init__(self, to_repository, from_repository,
 
429
        GenericRepoFetcher.__init__(self, to_repository, from_repository,
399
430
            last_revision, pb, find_ghosts)
400
431
 
401
432
    def _generate_root_texts(self, revs):
404
435
    def _fetch_inventory_weave(self, revs, pb):
405
436
        self.helper.regenerate_inventory(revs)
406
437
 
407
 
    def _fetch_revision_texts(self, revs, pb):
408
 
        """Fetch revision object texts"""
409
 
        count = 0
410
 
        total = len(revs)
411
 
        for rev in revs:
412
 
            pb.update('copying revisions', count, total)
413
 
            try:
414
 
                sig_text = self.from_repository.get_signature_text(rev)
415
 
                self.to_repository.add_signature_text(rev, sig_text)
416
 
            except errors.NoSuchRevision:
417
 
                # not signed.
418
 
                pass
419
 
            self._copy_revision(rev)
420
 
            count += 1
421
 
 
422
438
    def _copy_revision(self, rev):
423
439
        self.helper.fetch_revisions([rev])
424
440
 
425
441
 
426
 
class Knit1to2Fetcher(RepoFetcher):
 
442
class Knit1to2Fetcher(KnitRepoFetcher):
427
443
    """Fetch from a Knit1 repository into a Knit2 repository"""
428
444
 
429
 
    def __init__(self, to_repository, from_repository, last_revision=None,
 
445
    def __init__(self, to_repository, from_repository, last_revision=None, 
430
446
                 pb=None, find_ghosts=True):
431
447
        self.helper = Inter1and2Helper(from_repository, to_repository)
432
 
        RepoFetcher.__init__(self, to_repository, from_repository,
 
448
        KnitRepoFetcher.__init__(self, to_repository, from_repository,
433
449
            last_revision, pb, find_ghosts)
434
450
 
435
451
    def _generate_root_texts(self, revs):