~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Robert Collins
  • Date: 2005-09-29 02:01:49 UTC
  • Revision ID: robertc@robertcollins.net-20050929020149-1ff16722c6a01b2c
reenable remotebranch tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
"""Weave - storage of related text file versions"""
23
23
 
 
24
# before intset (r923) 2000 versions in 41.5s
 
25
# with intset (r926) 2000 versions in 93s !!!
 
26
# better to just use plain sets.
 
27
 
 
28
# making _extract build and return a list, rather than being a generator
 
29
# takes 37.94s
 
30
 
 
31
# with python -O, r923 does 2000 versions in 36.87s
 
32
 
 
33
# with optimizations to avoid mutating lists - 35.75!  I guess copying
 
34
# all the elements every time costs more than the small manipulations.
 
35
# a surprisingly small change.
 
36
 
 
37
# r931, which avoids using a generator for extract, does 36.98s
 
38
 
 
39
# with memoized inclusions, takes 41.49s; not very good
 
40
 
 
41
# with slots, takes 37.35s; without takes 39.16, a bit surprising
 
42
 
 
43
# with the delta calculation mixed in with the add method, rather than
 
44
# separated, takes 36.78s
 
45
 
 
46
# with delta folded in and mutation of the list, 36.13s
 
47
 
 
48
# with all this and simplification of add code, 33s
 
49
 
 
50
 
 
51
 
 
52
 
 
53
 
 
54
# TODO: Perhaps have copy method for Weave instances?
24
55
 
25
56
# XXX: If we do weaves this way, will a merge still behave the same
26
57
# way if it's done in a different order?  That's a pretty desirable
43
74
 
44
75
# TODO: Parallel-extract that passes back each line along with a
45
76
# description of which revisions include it.  Nice for checking all
46
 
# shas or calculating stats in parallel.
 
77
# shas in parallel.
47
78
 
48
79
# TODO: Using a single _extract routine and then processing the output
49
80
# is probably inefficient.  It's simple enough that we can afford to
57
88
# variant that is passed a pre-cooked version of the single basis
58
89
# version?
59
90
 
60
 
# TODO: Have a way to go back and insert a revision V1 that is a parent 
61
 
# of an already-stored revision V2.  This means some lines previously 
62
 
# counted as new in V2 will be discovered to have actually come from V1.  
63
 
# It is probably necessary to insert V1, then compute a whole new diff 
64
 
# from the mashed ancestors to V2.  This must be repeated for every 
65
 
# direct child of V1.  The deltas from V2 to its descendents won't change,
66
 
# although their location within the weave may change.  It may be possible
67
 
# to just adjust the location of those instructions rather than 
68
 
# re-weaving the whole thing.  This is expected to be a fairly rare
69
 
# operation, only used when inserting data that was previously a ghost.
70
 
 
71
 
 
72
91
 
73
92
 
74
93
import sha
75
94
from difflib import SequenceMatcher
76
95
 
77
96
 
 
97
from bzrlib.osutils import sha_strings
78
98
 
79
99
 
80
100
class WeaveError(Exception):
186
206
        self._weave_name = weave_name
187
207
 
188
208
 
189
 
    def copy(self):
190
 
        """Return a deep copy of self.
191
 
        
192
 
        The copy can be modified without affecting the original weave."""
193
 
        other = Weave()
194
 
        other._weave = self._weave[:]
195
 
        other._parents = self._parents[:]
196
 
        other._sha1s = self._sha1s[:]
197
 
        other._names = self._names[:]
198
 
        other._name_map = self._name_map.copy()
199
 
        other._weave_name = self._weave_name
200
 
        return other
201
 
 
202
209
    def __eq__(self, other):
203
210
        if not isinstance(other, Weave):
204
211
            return False
228
235
                             (name, self._weave_name))
229
236
 
230
237
 
231
 
    def iter_names(self):
232
 
        """Yield a list of all names in this weave."""
233
 
        return iter(self._names)
234
 
 
235
238
    def idx_to_name(self, version):
236
239
        return self._names[version]
237
240
 
270
273
        sha -- SHA-1 of the file, if known.  This is trusted to be
271
274
            correct if supplied.
272
275
        """
273
 
        from bzrlib.osutils import sha_strings
274
276
 
275
277
        assert isinstance(name, basestring)
276
278
        if sha1 is None:
377
379
 
378
380
        return new_version
379
381
 
380
 
    def add_identical(self, old_rev_id, new_rev_id, parents):
381
 
        """Add an identical text to old_rev_id as new_rev_id."""
382
 
        old_lines = self.get(self.lookup(old_rev_id))
383
 
        self.add(new_rev_id, parents, old_lines)
384
382
 
385
383
    def inclusions(self, versions):
386
384
        """Return set of all ancestors of given version(s)."""
641
639
        If there is a chunk of the file where there's no diagreement,
642
640
        only one alternative is given.
643
641
        """
 
642
 
644
643
        # approach: find the included versions common to all the
645
644
        # merged versions
646
645
        raise NotImplementedError()
666
665
        If line1=line2, this is a pure insert; if newlines=[] this is a
667
666
        pure delete.  (Similar to difflib.)
668
667
        """
669
 
        raise NotImplementedError()
 
668
 
670
669
 
671
670
            
672
671
    def plan_merge(self, ver_a, ver_b):
766
765
                       state
767
766
 
768
767
                
769
 
    def join(self, other):
770
 
        """Integrate versions from other into this weave.
771
 
 
772
 
        The resulting weave contains all the history of both weaves; 
773
 
        any version you could retrieve from either self or other can be 
774
 
        retrieved from self after this call.
775
 
 
776
 
        It is illegal for the two weaves to contain different values 
777
 
        for any version.
778
 
        """
779
 
        if other.numversions() == 0:
780
 
            return          # nothing to update, easy
781
 
        # work through in index order to make sure we get all dependencies
782
 
        for other_idx, name in enumerate(other._names):
783
 
            # TODO: If all the parents of the other version are already 
784
 
            # present then we can avoid some work by just taking the delta
785
 
            # and adjusting the offsets.
786
 
            if self._check_version_consistent(other, other_idx, name):
787
 
                continue
788
 
            new_parents = self._imported_parents(other, other_idx)
789
 
            lines = other.get_lines(other_idx)
790
 
            sha1 = other._sha1s[other_idx]
791
 
            self.add(name, new_parents, lines, sha1)
792
 
 
793
 
 
794
 
    def _imported_parents(self, other, other_idx):
795
 
        """Return list of parents in self corresponding to indexes in other."""
796
 
        new_parents = []
797
 
        for parent_idx in other._parents[other_idx]:
798
 
            parent_name = other._names[parent_idx]
799
 
            if parent_name not in self._names:
800
 
                # should not be possible
801
 
                raise WeaveError("missing parent {%s} of {%s} in %r" 
802
 
                                 % (parent_name, other._name_map[other_idx], self))
803
 
            new_parents.append(self._name_map[parent_name])
804
 
        return new_parents
805
 
 
806
 
    def _check_version_consistent(self, other, other_idx, name):
807
 
        """Check if a version in consistent in this and other.
808
 
 
809
 
        To be consistent it must have:
810
 
 
811
 
         * the same text
812
 
         * the same direct parents (by name, not index, and disregarding
813
 
           order)
814
 
        
815
 
        If present & correct return True;
816
 
        if not present in self return False; 
817
 
        if inconsistent raise error."""
818
 
        this_idx = self._name_map.get(name, -1)
819
 
        if this_idx != -1:
820
 
            if self._sha1s[this_idx] != other._sha1s[other_idx]:
821
 
                raise WeaveError("inconsistent texts for version {%s} "
822
 
                                 "when joining weaves"
823
 
                                 % (name))
824
 
            self_parents = self._parents[this_idx]
825
 
            other_parents = other._parents[other_idx]
826
 
            n1 = [self._names[i] for i in self_parents]
827
 
            n2 = [other._names[i] for i in other_parents]
828
 
            n1.sort()
829
 
            n2.sort()
830
 
            if n1 != n2:
831
 
                raise WeaveError("inconsistent parents for version {%s}: "
832
 
                                 "%s vs %s"
833
 
                                 % (name, n1, n2))
834
 
            else:
835
 
                return True         # ok!
836
 
        else:
837
 
            return False
 
768
 
 
769
 
 
770
 
838
771
 
839
772
 
840
773
def weave_toc(w):
851
784
 
852
785
 
853
786
 
854
 
def weave_stats(weave_file, pb):
 
787
def weave_stats(weave_file):
 
788
    from bzrlib.progress import ProgressBar
855
789
    from bzrlib.weavefile import read_weave
856
790
 
 
791
    pb = ProgressBar()
 
792
 
857
793
    wf = file(weave_file, 'rb')
858
794
    w = read_weave(wf)
859
795
    # FIXME: doesn't work on pipes
863
799
    vers = len(w)
864
800
    for i in range(vers):
865
801
        pb.update('checking sizes', i, vers)
866
 
        for origin, lineno, line in w._extract([i]):
 
802
        for line in w.get_iter(i):
867
803
            total += len(line)
868
804
 
869
805
    pb.clear()
932
868
def main(argv):
933
869
    import sys
934
870
    import os
935
 
    try:
936
 
        import bzrlib
937
 
    except ImportError:
938
 
        # in case we're run directly from the subdirectory
939
 
        sys.path.append('..')
940
 
        import bzrlib
941
871
    from bzrlib.weavefile import write_weave, read_weave
942
872
    from bzrlib.progress import ProgressBar
943
873
 
1010
940
        weave_toc(readit())
1011
941
 
1012
942
    elif cmd == 'stats':
1013
 
        weave_stats(argv[2], ProgressBar())
 
943
        weave_stats(argv[2])
1014
944
        
1015
945
    elif cmd == 'check':
1016
946
        w = readit()