~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/knit.py

  • Committer: Robert Collins
  • Date: 2006-03-02 03:12:34 UTC
  • mto: (1594.2.4 integration)
  • mto: This revision was merged to the branch mainline in revision 1596.
  • Revision ID: robertc@robertcollins.net-20060302031234-cf6b75961f27c5df
InterVersionedFile implemented.

Show diffs side-by-side

added added

removed removed

Lines of Context:
76
76
from bzrlib.trace import mutter
77
77
from bzrlib.osutils import contains_whitespace, contains_linebreaks, \
78
78
     sha_strings
79
 
from bzrlib.versionedfile import VersionedFile
 
79
from bzrlib.versionedfile import VersionedFile, InterVersionedFile
80
80
from bzrlib.tsort import topo_sort
81
81
 
82
82
 
260
260
        self._data = _KnitData(transport, relpath + DATA_SUFFIX,
261
261
            mode)
262
262
 
 
263
    def create_empty(self, name, transport, mode=None):
 
264
        return KnitVersionedFile(transport, name, 'w', self.factory, delta=self.delta)
 
265
 
263
266
    def versions(self):
264
267
        """See VersionedFile.versions."""
265
268
        return self._index.get_versions()
503
506
 
504
507
        return self.factory.lower_fulltext(KnitContent(new_lines))
505
508
 
506
 
    def join(self, other, pb=None, msg=None, version_ids=None):
507
 
        """See VersionedFile.join."""
508
 
        assert isinstance(other, KnitVersionedFile)
509
 
 
510
 
        if version_ids is None:
511
 
            version_ids = other.versions()
512
 
        if not version_ids:
513
 
            return 0
514
 
 
515
 
        if pb is None:
516
 
            from bzrlib.progress import DummyProgress
517
 
            pb = DummyProgress()
518
 
 
519
 
        version_ids = list(version_ids)
520
 
        if None in version_ids:
521
 
            version_ids.remove(None)
522
 
 
523
 
        other_ancestry = set(other.get_ancestry(version_ids))
524
 
        this_versions = set(self._index.get_versions())
525
 
        needed_versions = other_ancestry - this_versions
526
 
        cross_check_versions = other_ancestry.intersection(this_versions)
527
 
        mismatched_versions = set()
528
 
        for version in cross_check_versions:
529
 
            # scan to include needed parents.
530
 
            n1 = set(self.get_parents(version))
531
 
            n2 = set(other.get_parents(version))
532
 
            if n1 != n2:
533
 
                # FIXME TEST this check for cycles being introduced works
534
 
                # the logic is we have a cycle if in our graph we are an
535
 
                # ancestor of any of the n2 revisions.
536
 
                for parent in n2:
537
 
                    if parent in n1:
538
 
                        # safe
539
 
                        continue
540
 
                    else:
541
 
                        parent_ancestors = other.get_ancestry(parent)
542
 
                        if version in parent_ancestors:
543
 
                            raise errors.GraphCycleError([parent, version])
544
 
                # ensure this parent will be available later.
545
 
                new_parents = n2.difference(n1)
546
 
                needed_versions.update(new_parents.difference(this_versions))
547
 
                mismatched_versions.add(version)
548
 
 
549
 
        if not needed_versions and not cross_check_versions:
550
 
            return 0
551
 
        full_list = topo_sort(other._index.get_graph())
552
 
 
553
 
        version_list = [i for i in full_list if (not self.has_version(i)
554
 
                        and i in needed_versions)]
555
 
 
556
 
        records = []
557
 
        for version_id in version_list:
558
 
            data_pos, data_size = other._index.get_position(version_id)
559
 
            records.append((version_id, data_pos, data_size))
560
 
 
561
 
        count = 0
562
 
        for version_id, lines, digest \
563
 
                in other._data.read_records_iter(records):
564
 
            options = other._index.get_options(version_id)
565
 
            parents = other._index.get_parents(version_id)
566
 
            
567
 
            for parent in parents:
568
 
                assert self.has_version(parent)
569
 
 
570
 
            if self.factory.annotated:
571
 
                # FIXME jrydberg: it should be possible to skip
572
 
                # re-annotating components if we know that we are
573
 
                # going to pull all revisions in the same order.
574
 
                new_version_id = version_id
575
 
                new_version_idx = self._index.num_versions()
576
 
                if 'fulltext' in options:
577
 
                    lines = self._reannotate_fulltext(other, lines,
578
 
                        new_version_id, new_version_idx)
579
 
                elif 'line-delta' in options:
580
 
                    lines = self._reannotate_line_delta(other, lines,
581
 
                        new_version_id, new_version_idx)
582
 
 
583
 
            count = count + 1
584
 
            pb.update(self.filename, count, len(version_list))
585
 
 
586
 
            pos, size = self._data.add_record(version_id, digest, lines)
587
 
            self._index.add_version(version_id, options, pos, size, parents)
588
 
 
589
 
        for version in mismatched_versions:
590
 
            n1 = set(self.get_parents(version))
591
 
            n2 = set(other.get_parents(version))
592
 
            # write a combined record to our history.
593
 
            new_parents = self.get_parents(version) + list(n2.difference(n1))
594
 
            current_values = self._index._cache[version]
595
 
            self._index.add_version(version,
596
 
                                    current_values[1], 
597
 
                                    current_values[2],
598
 
                                    current_values[3],
599
 
                                    new_parents)
600
 
        pb.clear()
601
 
        return count
602
 
 
603
509
    def walk(self, version_ids):
604
510
        """See VersionedFile.walk."""
605
511
        # We take the short path here, and extract all relevant texts
630
536
        self._mode = mode
631
537
 
632
538
    def write_header(self):
633
 
        old_len = self._transport.append(self._filename, self.HEADER)
 
539
        old_len = self._transport.append(self._filename, StringIO(self.HEADER))
634
540
        if old_len != 0:
635
541
            raise KnitCorrupt(self._filename, 'misaligned after writing header')
636
542
 
745
651
                                        size,
746
652
                                        ' '.join([str(self.lookup(vid)) for 
747
653
                                                  vid in parents]))
748
 
        self._transport.append(self._filename, content)
 
654
        self._transport.append(self._filename, StringIO(content))
749
655
 
750
656
    def has_version(self, version_id):
751
657
        """True if the version is in the index."""
811
717
        data_file.close()
812
718
 
813
719
        content = sio.getvalue()
814
 
        start_pos = self._transport.append(self._filename, content)
 
720
        start_pos = self._transport.append(self._filename, StringIO(content))
815
721
        return start_pos, len(content)
816
722
 
817
723
    def _parse_record(self, version_id, data):
889
795
            components[record_id] = (content, digest)
890
796
        return components
891
797
 
 
798
 
 
799
class InterKnit(InterVersionedFile):
 
800
    """Optimised code paths for knit to knit operations."""
 
801
    
 
802
    _matching_file_factory = staticmethod(AnnotatedKnitFactory)
 
803
    
 
804
    @staticmethod
 
805
    def is_compatible(source, target):
 
806
        """Be compatible with knits.  """
 
807
        try:
 
808
            return (isinstance(source, KnitVersionedFile) and
 
809
                    isinstance(target, KnitVersionedFile))
 
810
        except AttributeError:
 
811
            return False
 
812
 
 
813
    def join(self, pb=None, msg=None, version_ids=None):
 
814
        """See InterVersionedFile.join."""
 
815
        assert isinstance(self.source, KnitVersionedFile)
 
816
        assert isinstance(self.target, KnitVersionedFile)
 
817
 
 
818
        if version_ids is None:
 
819
            version_ids = self.source.versions()
 
820
        if not version_ids:
 
821
            return 0
 
822
 
 
823
        if pb is None:
 
824
            from bzrlib.progress import DummyProgress
 
825
            pb = DummyProgress()
 
826
 
 
827
        version_ids = list(version_ids)
 
828
        if None in version_ids:
 
829
            version_ids.remove(None)
 
830
 
 
831
        self.source_ancestry = set(self.source.get_ancestry(version_ids))
 
832
        this_versions = set(self.target._index.get_versions())
 
833
        needed_versions = self.source_ancestry - this_versions
 
834
        cross_check_versions = self.source_ancestry.intersection(this_versions)
 
835
        mismatched_versions = set()
 
836
        for version in cross_check_versions:
 
837
            # scan to include needed parents.
 
838
            n1 = set(self.target.get_parents(version))
 
839
            n2 = set(self.source.get_parents(version))
 
840
            if n1 != n2:
 
841
                # FIXME TEST this check for cycles being introduced works
 
842
                # the logic is we have a cycle if in our graph we are an
 
843
                # ancestor of any of the n2 revisions.
 
844
                for parent in n2:
 
845
                    if parent in n1:
 
846
                        # safe
 
847
                        continue
 
848
                    else:
 
849
                        parent_ancestors = self.source.get_ancestry(parent)
 
850
                        if version in parent_ancestors:
 
851
                            raise errors.GraphCycleError([parent, version])
 
852
                # ensure this parent will be available later.
 
853
                new_parents = n2.difference(n1)
 
854
                needed_versions.update(new_parents.difference(this_versions))
 
855
                mismatched_versions.add(version)
 
856
 
 
857
        if not needed_versions and not cross_check_versions:
 
858
            return 0
 
859
        full_list = topo_sort(self.source._index.get_graph())
 
860
 
 
861
        version_list = [i for i in full_list if (not self.target.has_version(i)
 
862
                        and i in needed_versions)]
 
863
 
 
864
        records = []
 
865
        for version_id in version_list:
 
866
            data_pos, data_size = self.source._index.get_position(version_id)
 
867
            records.append((version_id, data_pos, data_size))
 
868
 
 
869
        count = 0
 
870
        for version_id, lines, digest \
 
871
                in self.source._data.read_records_iter(records):
 
872
            options = self.source._index.get_options(version_id)
 
873
            parents = self.source._index.get_parents(version_id)
 
874
            
 
875
            for parent in parents:
 
876
                assert self.target.has_version(parent)
 
877
 
 
878
            if self.target.factory.annotated:
 
879
                # FIXME jrydberg: it should be possible to skip
 
880
                # re-annotating components if we know that we are
 
881
                # going to pull all revisions in the same order.
 
882
                new_version_id = version_id
 
883
                new_version_idx = self.target._index.num_versions()
 
884
                if 'fulltext' in options:
 
885
                    lines = self.target._reannotate_fulltext(self.source, lines,
 
886
                        new_version_id, new_version_idx)
 
887
                elif 'line-delta' in options:
 
888
                    lines = self.target._reannotate_line_delta(self.source, lines,
 
889
                        new_version_id, new_version_idx)
 
890
 
 
891
            count = count + 1
 
892
            pb.update(self.target.filename, count, len(version_list))
 
893
 
 
894
            pos, size = self.target._data.add_record(version_id, digest, lines)
 
895
            self.target._index.add_version(version_id, options, pos, size, parents)
 
896
 
 
897
        for version in mismatched_versions:
 
898
            n1 = set(self.target.get_parents(version))
 
899
            n2 = set(self.source.get_parents(version))
 
900
            # write a combined record to our history.
 
901
            new_parents = self.target.get_parents(version) + list(n2.difference(n1))
 
902
            current_values = self.target._index._cache[version]
 
903
            self.target._index.add_version(version,
 
904
                                    current_values[1], 
 
905
                                    current_values[2],
 
906
                                    current_values[3],
 
907
                                    new_parents)
 
908
        pb.clear()
 
909
        return count
 
910
 
 
911
 
 
912
InterVersionedFile.register_optimiser(InterKnit)