~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Robert Collins
  • Date: 2007-10-17 09:39:41 UTC
  • mfrom: (2911 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2933.
  • Revision ID: robertc@robertcollins.net-20071017093941-v7d1djrt2617citb
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
46
46
from bzrlib.store.versioned import VersionedFileStore
47
47
from bzrlib.store.text import TextStore
48
48
from bzrlib.testament import Testament
 
49
from bzrlib.util import bencode
49
50
""")
50
51
 
51
52
from bzrlib.decorators import needs_read_lock, needs_write_lock
621
622
        self.weave_store = text_store
622
623
        # for tests
623
624
        self._reconcile_does_inventory_gc = True
 
625
        self._reconcile_fixes_text_parents = False
624
626
        # not right yet - should be more semantically clear ? 
625
627
        # 
626
628
        self.control_store = control_store
760
762
            result['size'] = t
761
763
        return result
762
764
 
 
765
    def get_data_stream(self, revision_ids):
 
766
        raise NotImplementedError(self.get_data_stream)
 
767
 
 
768
    def insert_data_stream(self, stream):
 
769
        """XXX What does this really do? 
 
770
        
 
771
        Is it a substitute for fetch? 
 
772
        Should it manage its own write group ?
 
773
        """
 
774
        for item_key, bytes in stream:
 
775
            if item_key[0] == 'file':
 
776
                (file_id,) = item_key[1:]
 
777
                knit = self.weave_store.get_weave_or_empty(
 
778
                    file_id, self.get_transaction())
 
779
            elif item_key == ('inventory',):
 
780
                knit = self.get_inventory_weave()
 
781
            elif item_key == ('revisions',):
 
782
                knit = self._revision_store.get_revision_file(
 
783
                    self.get_transaction())
 
784
            elif item_key == ('signatures',):
 
785
                knit = self._revision_store.get_signature_file(
 
786
                    self.get_transaction())
 
787
            else:
 
788
                raise RepositoryDataStreamError(
 
789
                    "Unrecognised data stream key '%s'" % (item_key,))
 
790
            decoded_list = bencode.bdecode(bytes)
 
791
            format = decoded_list.pop(0)
 
792
            data_list = []
 
793
            knit_bytes = ''
 
794
            for version, options, parents, some_bytes in decoded_list:
 
795
                data_list.append((version, options, len(some_bytes), parents))
 
796
                knit_bytes += some_bytes
 
797
            knit.insert_data_stream(
 
798
                (format, data_list, StringIO(knit_bytes).read))
 
799
 
763
800
    @needs_read_lock
764
801
    def missing_revision_ids(self, other, revision_id=None):
765
802
        """Return the revision ids that other has that this does not.
1151
1188
        # maybe this generator should explicitly have the contract that it
1152
1189
        # should not be iterated until the previously yielded item has been
1153
1190
        # processed?
 
1191
        self.lock_read()
1154
1192
        inv_w = self.get_inventory_weave()
1155
1193
        inv_w.enable_cache()
1156
1194
 
1181
1219
                pass
1182
1220
            else:
1183
1221
                revisions_with_signatures.add(rev_id)
 
1222
        self.unlock()
1184
1223
        yield ("signatures", None, revisions_with_signatures)
1185
1224
 
1186
1225
        # revisions
1459
1498
                [parents_provider, other_repository._make_parents_provider()])
1460
1499
        return graph.Graph(parents_provider)
1461
1500
 
 
1501
    def get_versioned_file_checker(self, revisions, revision_versions_cache):
 
1502
        return VersionedFileChecker(revisions, revision_versions_cache, self)
 
1503
 
1462
1504
    @needs_write_lock
1463
1505
    def set_make_working_trees(self, new_value):
1464
1506
        """Set the policy flag for making working trees when creating branches.
1493
1535
                                                       self.get_transaction())
1494
1536
 
1495
1537
    @needs_read_lock
1496
 
    def check(self, revision_ids):
 
1538
    def check(self, revision_ids=None):
1497
1539
        """Check consistency of all history of given revision_ids.
1498
1540
 
1499
1541
        Different repository implementations should override _check().
1501
1543
        :param revision_ids: A non-empty list of revision_ids whose ancestry
1502
1544
             will be checked.  Typically the last revision_id of a branch.
1503
1545
        """
1504
 
        if not revision_ids:
1505
 
            raise ValueError("revision_ids must be non-empty in %s.check" 
1506
 
                    % (self,))
1507
1546
        return self._check(revision_ids)
1508
1547
 
1509
1548
    def _check(self, revision_ids):
1537
1576
                    revision_id.decode('ascii')
1538
1577
                except UnicodeDecodeError:
1539
1578
                    raise errors.NonAsciiRevisionId(method, self)
1540
 
 
1541
 
 
1542
 
 
 
1579
    
 
1580
    def revision_graph_can_have_wrong_parents(self):
 
1581
        """Is it possible for this repository to have a revision graph with
 
1582
        incorrect parents?
 
1583
 
 
1584
        If True, then this repository must also implement
 
1585
        _find_inconsistent_revision_parents so that check and reconcile can
 
1586
        check for inconsistencies before proceeding with other checks that may
 
1587
        depend on the revision index being consistent.
 
1588
        """
 
1589
        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
 
1590
        
1543
1591
# remove these delegates a while after bzr 0.15
1544
1592
def __make_delegated(name, from_module):
1545
1593
    def _deprecated_repository_forwarder():
2421
2469
        return f.count_copied, f.failed_revisions
2422
2470
 
2423
2471
 
2424
 
class InterRemoteRepository(InterRepository):
2425
 
    """Code for converting between RemoteRepository objects.
2426
 
 
2427
 
    This just gets an non-remote repository from the RemoteRepository, and calls
2428
 
    InterRepository.get again.
2429
 
    """
2430
 
 
2431
 
    def __init__(self, source, target):
2432
 
        if isinstance(source, remote.RemoteRepository):
2433
 
            source._ensure_real()
2434
 
            real_source = source._real_repository
2435
 
        else:
2436
 
            real_source = source
2437
 
        if isinstance(target, remote.RemoteRepository):
2438
 
            target._ensure_real()
2439
 
            real_target = target._real_repository
2440
 
        else:
2441
 
            real_target = target
2442
 
        self.real_inter = InterRepository.get(real_source, real_target)
2443
 
 
2444
 
    @staticmethod
2445
 
    def is_compatible(source, target):
2446
 
        if isinstance(source, remote.RemoteRepository):
2447
 
            return True
 
2472
class InterRemoteToOther(InterRepository):
 
2473
 
 
2474
    def __init__(self, source, target):
 
2475
        InterRepository.__init__(self, source, target)
 
2476
        self._real_inter = None
 
2477
 
 
2478
    @staticmethod
 
2479
    def is_compatible(source, target):
 
2480
        if not isinstance(source, remote.RemoteRepository):
 
2481
            return False
 
2482
        source._ensure_real()
 
2483
        real_source = source._real_repository
 
2484
        # Is source's model compatible with target's model, and are they the
 
2485
        # same format?  Currently we can only optimise fetching from an
 
2486
        # identical model & format repo.
 
2487
        assert not isinstance(real_source, remote.RemoteRepository), (
 
2488
            "We don't support remote repos backed by remote repos yet.")
 
2489
        return real_source._format == target._format
 
2490
 
 
2491
    @needs_write_lock
 
2492
    def fetch(self, revision_id=None, pb=None):
 
2493
        """See InterRepository.fetch()."""
 
2494
        from bzrlib.fetch import RemoteToOtherFetcher
 
2495
        mutter("Using fetch logic to copy between %s(remote) and %s(%s)",
 
2496
               self.source, self.target, self.target._format)
 
2497
        # TODO: jam 20070210 This should be an assert, not a translate
 
2498
        revision_id = osutils.safe_revision_id(revision_id)
 
2499
        f = RemoteToOtherFetcher(to_repository=self.target,
 
2500
                                 from_repository=self.source,
 
2501
                                 last_revision=revision_id,
 
2502
                                 pb=pb)
 
2503
        return f.count_copied, f.failed_revisions
 
2504
 
 
2505
    @classmethod
 
2506
    def _get_repo_format_to_test(self):
 
2507
        return None
 
2508
 
 
2509
 
 
2510
class InterOtherToRemote(InterRepository):
 
2511
 
 
2512
    def __init__(self, source, target):
 
2513
        InterRepository.__init__(self, source, target)
 
2514
        self._real_inter = None
 
2515
 
 
2516
    @staticmethod
 
2517
    def is_compatible(source, target):
2448
2518
        if isinstance(target, remote.RemoteRepository):
2449
2519
            return True
2450
2520
        return False
2451
2521
 
 
2522
    def _ensure_real_inter(self):
 
2523
        if self._real_inter is None:
 
2524
            self.target._ensure_real()
 
2525
            real_target = self.target._real_repository
 
2526
            self._real_inter = InterRepository.get(self.source, real_target)
 
2527
    
2452
2528
    def copy_content(self, revision_id=None):
2453
 
        self.real_inter.copy_content(revision_id=revision_id)
 
2529
        self._ensure_real_inter()
 
2530
        self._real_inter.copy_content(revision_id=revision_id)
2454
2531
 
2455
2532
    def fetch(self, revision_id=None, pb=None):
2456
 
        self.real_inter.fetch(revision_id=revision_id, pb=pb)
 
2533
        self._ensure_real_inter()
 
2534
        self._real_inter.fetch(revision_id=revision_id, pb=pb)
2457
2535
 
2458
2536
    @classmethod
2459
2537
    def _get_repo_format_to_test(self):
2466
2544
InterRepository.register_optimiser(InterModel1and2)
2467
2545
InterRepository.register_optimiser(InterKnit1and2)
2468
2546
InterRepository.register_optimiser(InterPackRepo)
2469
 
InterRepository.register_optimiser(InterRemoteRepository)
 
2547
InterRepository.register_optimiser(InterRemoteToOther)
 
2548
InterRepository.register_optimiser(InterOtherToRemote)
2470
2549
 
2471
2550
 
2472
2551
class CopyConverter(object):
2549
2628
    if _unescape_re is None:
2550
2629
        _unescape_re = re.compile('\&([^;]*);')
2551
2630
    return _unescape_re.sub(_unescaper, data)
 
2631
 
 
2632
 
 
2633
class _RevisionTextVersionCache(object):
 
2634
    """A cache of the versionedfile versions for revision and file-id."""
 
2635
 
 
2636
    def __init__(self, repository):
 
2637
        self.repository = repository
 
2638
        self.revision_versions = {}
 
2639
        self.revision_parents = {}
 
2640
        self.repo_graph = self.repository.get_graph()
 
2641
        self.rev_heads = {}
 
2642
 
 
2643
    def add_revision_text_versions(self, tree):
 
2644
        """Cache text version data from the supplied revision tree"""
 
2645
        inv_revisions = {}
 
2646
        for path, entry in tree.iter_entries_by_dir():
 
2647
            inv_revisions[entry.file_id] = entry.revision
 
2648
        self.revision_versions[tree.get_revision_id()] = inv_revisions
 
2649
        return inv_revisions
 
2650
 
 
2651
    def get_text_version(self, file_id, revision_id):
 
2652
        """Determine the text version for a given file-id and revision-id"""
 
2653
        try:
 
2654
            inv_revisions = self.revision_versions[revision_id]
 
2655
        except KeyError:
 
2656
            try:
 
2657
                tree = self.repository.revision_tree(revision_id)
 
2658
            except errors.RevisionNotPresent:
 
2659
                self.revision_versions[revision_id] = inv_revisions = {}
 
2660
            else:
 
2661
                inv_revisions = self.add_revision_text_versions(tree)
 
2662
        return inv_revisions.get(file_id)
 
2663
 
 
2664
    def prepopulate_revs(self, revision_ids):
 
2665
        # Filter out versions that we don't have an inventory for, so that the
 
2666
        # revision_trees() call won't fail.
 
2667
        inv_weave = self.repository.get_inventory_weave()
 
2668
        revs = [r for r in revision_ids if inv_weave.has_version(r)]
 
2669
        # XXX: this loop is very similar to
 
2670
        # bzrlib.fetch.Inter1and2Helper.iter_rev_trees.
 
2671
        while revs:
 
2672
            for tree in self.repository.revision_trees(revs[:100]):
 
2673
                if tree.inventory.revision_id is None:
 
2674
                    tree.inventory.revision_id = tree.get_revision_id()
 
2675
                self.add_revision_text_versions(tree)
 
2676
            revs = revs[100:]
 
2677
 
 
2678
    def get_parents(self, revision_id):
 
2679
        try:
 
2680
            return self.revision_parents[revision_id]
 
2681
        except KeyError:
 
2682
            parents = self.repository.get_parents([revision_id])[0]
 
2683
            self.revision_parents[revision_id] = parents
 
2684
            return parents
 
2685
 
 
2686
    def heads(self, revision_ids):
 
2687
        revision_ids = tuple(revision_ids)
 
2688
        try:
 
2689
            return self.rev_heads[revision_ids]
 
2690
        except KeyError:
 
2691
            heads = self.repo_graph.heads(revision_ids)
 
2692
            self.rev_heads[revision_ids] = heads
 
2693
            return heads
 
2694
 
 
2695
 
 
2696
class VersionedFileChecker(object):
 
2697
 
 
2698
    def __init__(self, planned_revisions, revision_versions, repository):
 
2699
        self.planned_revisions = planned_revisions
 
2700
        self.revision_versions = revision_versions
 
2701
        self.repository = repository
 
2702
    
 
2703
    def calculate_file_version_parents(self, revision_id, file_id):
 
2704
        text_revision = self.revision_versions.get_text_version(
 
2705
            file_id, revision_id)
 
2706
        if text_revision is None:
 
2707
            return None
 
2708
        parents_of_text_revision = self.revision_versions.get_parents(
 
2709
            text_revision)
 
2710
        parents_from_inventories = []
 
2711
        for parent in parents_of_text_revision:
 
2712
            if parent == _mod_revision.NULL_REVISION:
 
2713
                continue
 
2714
            introduced_in = self.revision_versions.get_text_version(file_id,
 
2715
                    parent)
 
2716
            if introduced_in is not None:
 
2717
                parents_from_inventories.append(introduced_in)
 
2718
        heads = set(self.revision_versions.heads(parents_from_inventories))
 
2719
        new_parents = []
 
2720
        for parent in parents_from_inventories:
 
2721
            if parent in heads and parent not in new_parents:
 
2722
                new_parents.append(parent)
 
2723
        return tuple(new_parents)
 
2724
 
 
2725
    def check_file_version_parents(self, weave, file_id):
 
2726
        result = {}
 
2727
        for num, revision_id in enumerate(self.planned_revisions):
 
2728
            correct_parents = self.calculate_file_version_parents(
 
2729
                revision_id, file_id)
 
2730
            if correct_parents is None:
 
2731
                continue
 
2732
            text_revision = self.revision_versions.get_text_version(
 
2733
                file_id, revision_id)
 
2734
            knit_parents = tuple(weave.get_parents(text_revision))
 
2735
            if correct_parents != knit_parents:
 
2736
                result[revision_id] = (knit_parents, correct_parents)
 
2737
        return result