52
50
# have slight specializations for different ways its used: annotate,
53
51
# basis for add, get, etc.
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.
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
# TODO: Reweave can possibly be made faster by remembering diffs
61
# where the basis and destination are unchanged.
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.
65
71
from difflib import SequenceMatcher
73
from bzrlib.trace import mutter
70
76
class WeaveError(Exception):
774
def join(self, other):
775
"""Integrate versions from other into this weave.
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.
781
It is illegal for the two weaves to contain different values
782
or different parents for any version. See also reweave().
784
if other.numversions() == 0:
785
return # nothing to update, easy
786
# two loops so that we do not change ourselves before verifying it
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):
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)
802
def _imported_parents(self, other, other_idx):
803
"""Return list of parents in self corresponding to indexes in other."""
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])
814
def _check_version_consistent(self, other, other_idx, name):
815
"""Check if a version in consistent in this and other.
817
To be consistent it must have:
820
* the same direct parents (by name, not index, and disregarding
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)
828
if self._sha1s[this_idx] != other._sha1s[other_idx]:
829
raise WeaveError("inconsistent texts for version {%s} "
830
"when joining weaves"
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]
839
raise WeaveParentMismatch("inconsistent parents "
840
"for version {%s}: %s vs %s" % (name, n1, n2))
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))
854
"""Combine two weaves and return the result.
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.
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
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)
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)
881
lines = wb.get_lines(name)
882
wr.add(name, combined_parents[name], lines)
886
def _reweave_parent_graphs(wa, wb):
887
"""Return combined parent ancestry for two weaves.
889
Returned as a list of (version_name, set(parent_names))"""
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]))
898
def _make_reweave_order(wa_order, wb_order, combined_parents):
899
"""Return an order to reweave versions respecting parents."""
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):
908
next_a = wa_order[ia]
912
if combined_parents[next_a].issubset(done):
914
result.append(next_a)
918
next_b = wb_order[ib]
922
elif combined_parents[next_b].issubset(done):
924
result.append(next_b)
927
raise WeaveError("don't know how to reweave at {%s} and {%s}"
748
932
def weave_toc(w):