~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Aaron Bentley
  • Date: 2005-10-18 18:48:27 UTC
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1474.
  • Revision ID: abentley@panoramicfeedback.com-20051018184827-2cc69376beb1cdf3
Switched to ConfigObj

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
"""Weave - storage of related text file versions"""
23
23
 
24
24
 
25
 
# TODO: Perhaps have copy method for Weave instances?
26
 
 
27
25
# XXX: If we do weaves this way, will a merge still behave the same
28
26
# way if it's done in a different order?  That's a pretty desirable
29
27
# property.
52
50
# have slight specializations for different ways its used: annotate,
53
51
# basis for add, get, etc.
54
52
 
55
 
# TODO: Perhaps the API should work only in names to hide the integer
56
 
# indexes from the user?
 
53
# TODO: Probably the API should work only in names to hide the integer
 
54
# indexes from the user.
57
55
 
58
56
# TODO: Is there any potential performance win by having an add()
59
57
# variant that is passed a pre-cooked version of the single basis
60
58
# version?
61
59
 
 
60
# TODO: Reweave can possibly be made faster by remembering diffs
 
61
# where the basis and destination are unchanged.
 
62
 
 
63
# FIXME: Sometimes we will be given a parents list for a revision
 
64
# that includes some redundant parents (i.e. already a parent of 
 
65
# something in the list.)  We should eliminate them.  This can 
 
66
# be done fairly efficiently because the sequence numbers constrain
 
67
# the possible relationships.
62
68
 
63
69
 
64
70
import sha
65
71
from difflib import SequenceMatcher
66
72
 
67
 
 
 
73
from bzrlib.trace import mutter
68
74
 
69
75
 
70
76
class WeaveError(Exception):
73
79
 
74
80
class WeaveFormatError(WeaveError):
75
81
    """Weave invariant violated"""
 
82
 
 
83
 
 
84
class WeaveParentMismatch(WeaveError):
 
85
    """Parents are mismatched between two revisions."""
76
86
    
77
87
 
78
88
class Weave(object):
176
186
        self._weave_name = weave_name
177
187
 
178
188
 
 
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
 
179
202
    def __eq__(self, other):
180
203
        if not isinstance(other, Weave):
181
204
            return False
205
228
                             (name, self._weave_name))
206
229
 
207
230
 
 
231
    def iter_names(self):
 
232
        """Yield a list of all names in this weave."""
 
233
        return iter(self._names)
 
234
 
208
235
    def idx_to_name(self, version):
209
236
        return self._names[version]
210
237
 
371
398
        return self._parents[version]
372
399
 
373
400
 
 
401
    def parent_names(self, version):
 
402
        """Return version names for parents of a version."""
 
403
        return map(self.idx_to_name, self._parents[self.lookup(version)])
 
404
 
 
405
 
374
406
    def minimal_parents(self, version):
375
407
        """Find the minimal set of parents for the version."""
376
408
        included = self._parents[version]
614
646
        If there is a chunk of the file where there's no diagreement,
615
647
        only one alternative is given.
616
648
        """
617
 
 
618
649
        # approach: find the included versions common to all the
619
650
        # merged versions
620
651
        raise NotImplementedError()
640
671
        If line1=line2, this is a pure insert; if newlines=[] this is a
641
672
        pure delete.  (Similar to difflib.)
642
673
        """
643
 
 
 
674
        raise NotImplementedError()
644
675
 
645
676
            
646
677
    def plan_merge(self, ver_a, ver_b):
740
771
                       state
741
772
 
742
773
                
743
 
 
744
 
 
745
 
 
 
774
    def join(self, other):
 
775
        """Integrate versions from other into this weave.
 
776
 
 
777
        The resulting weave contains all the history of both weaves; 
 
778
        any version you could retrieve from either self or other can be 
 
779
        retrieved from self after this call.
 
780
 
 
781
        It is illegal for the two weaves to contain different values 
 
782
        or different parents for any version.  See also reweave().
 
783
        """
 
784
        if other.numversions() == 0:
 
785
            return          # nothing to update, easy
 
786
        # two loops so that we do not change ourselves before verifying it
 
787
        # will be ok
 
788
        # work through in index order to make sure we get all dependencies
 
789
        for other_idx, name in enumerate(other._names):
 
790
            if self._check_version_consistent(other, other_idx, name):
 
791
                continue
 
792
        for other_idx, name in enumerate(other._names):
 
793
            # TODO: If all the parents of the other version are already 
 
794
            # present then we can avoid some work by just taking the delta
 
795
            # and adjusting the offsets.
 
796
            new_parents = self._imported_parents(other, other_idx)
 
797
            lines = other.get_lines(other_idx)
 
798
            sha1 = other._sha1s[other_idx]
 
799
            self.add(name, new_parents, lines, sha1)
 
800
 
 
801
 
 
802
    def _imported_parents(self, other, other_idx):
 
803
        """Return list of parents in self corresponding to indexes in other."""
 
804
        new_parents = []
 
805
        for parent_idx in other._parents[other_idx]:
 
806
            parent_name = other._names[parent_idx]
 
807
            if parent_name not in self._names:
 
808
                # should not be possible
 
809
                raise WeaveError("missing parent {%s} of {%s} in %r" 
 
810
                                 % (parent_name, other._name_map[other_idx], self))
 
811
            new_parents.append(self._name_map[parent_name])
 
812
        return new_parents
 
813
 
 
814
    def _check_version_consistent(self, other, other_idx, name):
 
815
        """Check if a version in consistent in this and other.
 
816
 
 
817
        To be consistent it must have:
 
818
 
 
819
         * the same text
 
820
         * the same direct parents (by name, not index, and disregarding
 
821
           order)
 
822
        
 
823
        If present & correct return True;
 
824
        if not present in self return False; 
 
825
        if inconsistent raise error."""
 
826
        this_idx = self._name_map.get(name, -1)
 
827
        if this_idx != -1:
 
828
            if self._sha1s[this_idx] != other._sha1s[other_idx]:
 
829
                raise WeaveError("inconsistent texts for version {%s} "
 
830
                                 "when joining weaves"
 
831
                                 % (name))
 
832
            self_parents = self._parents[this_idx]
 
833
            other_parents = other._parents[other_idx]
 
834
            n1 = [self._names[i] for i in self_parents]
 
835
            n2 = [other._names[i] for i in other_parents]
 
836
            n1.sort()
 
837
            n2.sort()
 
838
            if n1 != n2:
 
839
                raise WeaveParentMismatch("inconsistent parents "
 
840
                    "for version {%s}: %s vs %s" % (name, n1, n2))
 
841
            else:
 
842
                return True         # ok!
 
843
        else:
 
844
            return False
 
845
 
 
846
    def reweave(self, other):
 
847
        """Reweave self with other."""
 
848
        new_weave = reweave(self, other)
 
849
        for attr in self.__slots__:
 
850
            setattr(self, attr, getattr(new_weave, attr))
 
851
 
 
852
 
 
853
def reweave(wa, wb):
 
854
    """Combine two weaves and return the result.
 
855
 
 
856
    This works even if a revision R has different parents in 
 
857
    wa and wb.  In the resulting weave all the parents are given.
 
858
 
 
859
    This is done by just building up a new weave, maintaining ordering 
 
860
    of the versions in the two inputs.  More efficient approaches
 
861
    might be possible but it should only be necessary to do 
 
862
    this operation rarely, when a new previously ghost version is 
 
863
    inserted.
 
864
    """
 
865
    wr = Weave()
 
866
    ia = ib = 0
 
867
    queue_a = range(wa.numversions())
 
868
    queue_b = range(wb.numversions())
 
869
    # first determine combined parents of all versions
 
870
    # map from version name -> all parent names
 
871
    combined_parents = _reweave_parent_graphs(wa, wb)
 
872
    mutter("combined parents: %r", combined_parents)
 
873
    order = _make_reweave_order(wa._names, wb._names, combined_parents)
 
874
    mutter("order to reweave: %r", order)
 
875
    for name in order:
 
876
        if name in wa._name_map:
 
877
            lines = wa.get_lines(name)
 
878
            if name in wb._name_map:
 
879
                assert lines == wb.get_lines(name)
 
880
        else:
 
881
            lines = wb.get_lines(name)
 
882
        wr.add(name, combined_parents[name], lines)
 
883
    return wr
 
884
 
 
885
 
 
886
def _reweave_parent_graphs(wa, wb):
 
887
    """Return combined parent ancestry for two weaves.
 
888
    
 
889
    Returned as a list of (version_name, set(parent_names))"""
 
890
    combined = {}
 
891
    for weave in [wa, wb]:
 
892
        for idx, name in enumerate(weave._names):
 
893
            p = combined.setdefault(name, set())
 
894
            p.update(map(weave.idx_to_name, weave._parents[idx]))
 
895
    return combined
 
896
 
 
897
 
 
898
def _make_reweave_order(wa_order, wb_order, combined_parents):
 
899
    """Return an order to reweave versions respecting parents."""
 
900
    done = set()
 
901
    result = []
 
902
    ia = ib = 0
 
903
    next_a = next_b = None
 
904
    len_a = len(wa_order)
 
905
    len_b = len(wb_order)
 
906
    while ia < len(wa_order) or ib < len(wb_order):
 
907
        if ia < len_a:
 
908
            next_a = wa_order[ia]
 
909
            if next_a in done:
 
910
                ia += 1
 
911
                continue
 
912
            if combined_parents[next_a].issubset(done):
 
913
                ia += 1
 
914
                result.append(next_a)
 
915
                done.add(next_a)
 
916
                continue
 
917
        if ib < len_b:
 
918
            next_b = wb_order[ib]
 
919
            if next_b in done:
 
920
                ib += 1
 
921
                continue
 
922
            elif combined_parents[next_b].issubset(done):
 
923
                ib += 1
 
924
                result.append(next_b)
 
925
                done.add(next_b)
 
926
                continue
 
927
        raise WeaveError("don't know how to reweave at {%s} and {%s}"
 
928
                         % (next_a, next_b))
 
929
    return result
746
930
 
747
931
 
748
932
def weave_toc(w):