~lifeless/bzr/index.range_map

« back to all changes in this revision

Viewing changes to bzrlib/fetch.py

  • Committer: Robert Collins
  • Date: 2008-06-19 01:17:19 UTC
  • mfrom: (3218.1.277 +trunk)
  • Revision ID: robertc@robertcollins.net-20080619011719-1c4g4uxzzhdls2wf
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
stored, so that if a revision is present we can totally recreate it.
28
28
However, we can't know what files are included in a revision until we
29
29
read its inventory.  So we query the inventory store of the source for
30
 
the ids we need, and then pull those ids and finally actually join
31
 
the inventories.
 
30
the ids we need, and then pull those ids and then return to the inventories.
32
31
"""
33
32
 
34
33
import bzrlib
41
40
        )
42
41
from bzrlib.trace import mutter
43
42
import bzrlib.ui
44
 
 
45
 
from bzrlib.lazy_import import lazy_import
 
43
from bzrlib.versionedfile import filter_absent
46
44
 
47
45
# TODO: Avoid repeatedly opening weaves so many times.
48
46
 
113
111
                else:
114
112
                    self.to_repository.commit_write_group()
115
113
            finally:
116
 
                if self.nested_pb is not None:
117
 
                    self.nested_pb.finished()
118
 
                self.to_repository.unlock()
 
114
                try:
 
115
                    if self.nested_pb is not None:
 
116
                        self.nested_pb.finished()
 
117
                finally:
 
118
                    self.to_repository.unlock()
119
119
        finally:
120
120
            self.from_repository.unlock()
121
121
 
157
157
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
158
158
        try:
159
159
            revs = search.get_keys()
160
 
            data_to_fetch = self.from_repository.item_keys_introduced_by(revs, pb)
 
160
            graph = self.from_repository.get_graph()
 
161
            revs = list(graph.iter_topo_order(revs))
 
162
            data_to_fetch = self.from_repository.item_keys_introduced_by(revs,
 
163
                                                                         pb)
161
164
            for knit_kind, file_id, revisions in data_to_fetch:
162
165
                if knit_kind != phase:
163
166
                    phase = knit_kind
168
171
                if knit_kind == "file":
169
172
                    self._fetch_weave_text(file_id, revisions)
170
173
                elif knit_kind == "inventory":
171
 
                    # XXX:
172
 
                    # Once we've processed all the files, then we generate the root
173
 
                    # texts (if necessary), then we process the inventory.  It's a
174
 
                    # bit distasteful to have knit_kind == "inventory" mean this,
175
 
                    # perhaps it should happen on the first non-"file" knit, in case
176
 
                    # it's not always inventory?
 
174
                    # Before we process the inventory we generate the root
 
175
                    # texts (if necessary) so that the inventories references
 
176
                    # will be valid.
177
177
                    self._generate_root_texts(revs)
 
178
                    # NB: This currently reopens the inventory weave in source;
 
179
                    # using a full get_data_stream instead would avoid this.
178
180
                    self._fetch_inventory_weave(revs, pb)
179
181
                elif knit_kind == "signatures":
180
182
                    # Nothing to do here; this will be taken care of when
214
216
            self.to_repository.get_transaction())
215
217
        from_weave = self.from_weaves.get_weave(file_id,
216
218
            self.from_repository.get_transaction())
217
 
        # we fetch all the texts, because texts do
218
 
        # not reference anything, and its cheap enough
219
 
        to_weave.join(from_weave, version_ids=required_versions)
220
 
        # we don't need *all* of this data anymore, but we dont know
221
 
        # what we do. This cache clearing will result in a new read 
222
 
        # of the knit data when we do the checkout, but probably we
223
 
        # want to emit the needed data on the fly rather than at the
224
 
        # end anyhow.
225
 
        # the from weave should know not to cache data being joined,
226
 
        # but its ok to ask it to clear.
227
 
        from_weave.clear_cache()
228
 
        to_weave.clear_cache()
 
219
        # Fetch all the texts.
 
220
        to_weave.insert_record_stream(from_weave.get_record_stream(
 
221
            required_versions, 'topological', False))
229
222
 
230
223
    def _fetch_inventory_weave(self, revs, pb):
231
224
        pb.update("fetch inventory", 0, 2)
241
234
            # know for unselected inventories whether all their required
242
235
            # texts are present in the other repository - it could be
243
236
            # corrupt.
244
 
            to_weave.join(from_weave, pb=child_pb, msg='merge inventory',
245
 
                          version_ids=revs)
246
 
            from_weave.clear_cache()
 
237
            to_weave.insert_record_stream(from_weave.get_record_stream(revs,
 
238
                'topological', False))
247
239
        finally:
248
240
            child_pb.finished()
249
241
 
278
270
            except errors.NoSuchRevision:
279
271
                # not signed.
280
272
                pass
281
 
            to_store.add_revision(self.from_repository.get_revision(rev),
282
 
                                  to_txn)
 
273
            self._copy_revision(rev, to_txn)
283
274
            count += 1
284
275
        # fixup inventory if needed: 
285
276
        # this is expensive because we have no inverse index to current ghosts.
287
278
        # so we just-do-it.
288
279
        # FIXME: repository should inform if this is needed.
289
280
        self.to_repository.reconcile()
290
 
    
 
281
 
 
282
    def _copy_revision(self, rev, to_txn):
 
283
        to_store = self.to_repository._revision_store
 
284
        to_store.add_revision(self.from_repository.get_revision(rev), to_txn)
 
285
 
291
286
 
292
287
class KnitRepoFetcher(RepoFetcher):
293
288
    """This is a knit format repository specific fetcher.
305
300
            to_transaction)
306
301
        from_sf = self.from_repository._revision_store.get_signature_file(
307
302
            from_transaction)
308
 
        to_sf.join(from_sf, version_ids=revs, ignore_missing=True)
 
303
        # A missing signature is just skipped.
 
304
        to_sf.insert_record_stream(filter_absent(from_sf.get_record_stream(revs,
 
305
            'unordered', False)))
 
306
        self._fetch_just_revision_texts(revs, from_transaction, to_transaction)
 
307
 
 
308
    def _fetch_just_revision_texts(self, version_ids, from_transaction,
 
309
                                   to_transaction):
309
310
        to_rf = self.to_repository._revision_store.get_revision_file(
310
311
            to_transaction)
311
312
        from_rf = self.from_repository._revision_store.get_revision_file(
312
313
            from_transaction)
313
 
        to_rf.join(from_rf, version_ids=revs)
 
314
        to_rf.insert_record_stream(from_rf.get_record_stream(version_ids,
 
315
            'topological', False))
314
316
 
315
317
 
316
318
class Inter1and2Helper(object):
347
349
                yield tree
348
350
            revs = revs[100:]
349
351
 
 
352
    def _find_root_ids(self, revs, parent_map, graph):
 
353
        revision_root = {}
 
354
        planned_versions = {}
 
355
        for tree in self.iter_rev_trees(revs):
 
356
            revision_id = tree.inventory.root.revision
 
357
            root_id = tree.get_root_id()
 
358
            planned_versions.setdefault(root_id, []).append(revision_id)
 
359
            revision_root[revision_id] = root_id
 
360
        # Find out which parents we don't already know root ids for
 
361
        parents = set()
 
362
        for revision_parents in parent_map.itervalues():
 
363
            parents.update(revision_parents)
 
364
        parents.difference_update(revision_root.keys() + [NULL_REVISION])
 
365
        # Limit to revisions present in the versionedfile
 
366
        parents = graph.get_parent_map(parents).keys()
 
367
        for tree in self.iter_rev_trees(parents):
 
368
            root_id = tree.get_root_id()
 
369
            revision_root[tree.get_revision_id()] = root_id
 
370
        return revision_root, planned_versions
 
371
 
350
372
    def generate_root_texts(self, revs):
351
373
        """Generate VersionedFiles for all root ids.
352
 
        
 
374
 
353
375
        :param revs: the revisions to include
354
376
        """
355
 
        inventory_weave = self.source.get_inventory_weave()
356
 
        parent_texts = {}
357
 
        versionedfile = {}
358
377
        to_store = self.target.weave_store
359
 
        for tree in self.iter_rev_trees(revs):
360
 
            revision_id = tree.inventory.root.revision
361
 
            root_id = tree.get_root_id()
362
 
            parents = inventory_weave.get_parents(revision_id)
363
 
            if root_id not in versionedfile:
364
 
                versionedfile[root_id] = to_store.get_weave_or_empty(root_id, 
365
 
                    self.target.get_transaction())
366
 
            _, _, parent_texts[root_id] = versionedfile[root_id].add_lines(
367
 
                revision_id, parents, [], parent_texts)
 
378
        graph = self.source.get_graph()
 
379
        parent_map = graph.get_parent_map(revs)
 
380
        revision_root, planned_versions = self._find_root_ids(
 
381
            revs, parent_map, graph)
 
382
        for root_id, versions in planned_versions.iteritems():
 
383
            versionedfile = to_store.get_weave_or_empty(root_id,
 
384
                self.target.get_transaction())
 
385
            parent_texts = {}
 
386
            for revision_id in versions:
 
387
                if revision_id in versionedfile:
 
388
                    continue
 
389
                parents = parent_map[revision_id]
 
390
                # We drop revision parents with different file-ids, because
 
391
                # a version cannot have a version with another file-id as its
 
392
                # parent.
 
393
                # When a parent revision is a ghost, we guess that its root id
 
394
                # was unchanged.
 
395
                parents = tuple(p for p in parents if p != NULL_REVISION
 
396
                    and revision_root.get(p, root_id) == root_id)
 
397
                result = versionedfile.add_lines_with_ghosts(
 
398
                    revision_id, parents, [], parent_texts)
 
399
                parent_texts[revision_id] = result[2]
368
400
 
369
401
    def regenerate_inventory(self, revs):
370
402
        """Generate a new inventory versionedfile in target, convertin data.
378
410
            self.target.add_inventory(tree.get_revision_id(), tree.inventory,
379
411
                                      parents)
380
412
 
 
413
    def fetch_revisions(self, revision_ids):
 
414
        for revision in self.source.get_revisions(revision_ids):
 
415
            self.target.add_revision(revision.revision_id, revision)
 
416
 
381
417
 
382
418
class Model1toKnit2Fetcher(GenericRepoFetcher):
383
419
    """Fetch from a Model1 repository into a Knit2 repository
393
429
 
394
430
    def _fetch_inventory_weave(self, revs, pb):
395
431
        self.helper.regenerate_inventory(revs)
396
 
 
 
432
 
 
433
    def _copy_revision(self, rev, to_txn):
 
434
        self.helper.fetch_revisions([rev])
 
435
 
397
436
 
398
437
class Knit1to2Fetcher(KnitRepoFetcher):
399
438
    """Fetch from a Knit1 repository into a Knit2 repository"""
410
449
    def _fetch_inventory_weave(self, revs, pb):
411
450
        self.helper.regenerate_inventory(revs)
412
451
 
 
452
    def _fetch_just_revision_texts(self, version_ids, from_transaction,
 
453
                                   to_transaction):
 
454
        self.helper.fetch_revisions(version_ids)
 
455
 
413
456
 
414
457
class RemoteToOtherFetcher(GenericRepoFetcher):
415
458