67
67
# the possible relationships.
71
72
from difflib import SequenceMatcher
73
74
from bzrlib.trace import mutter
74
from bzrlib.errors import WeaveError, WeaveFormatError, WeaveParentMismatch, \
75
WeaveRevisionNotPresent, WeaveRevisionAlreadyPresent
75
from bzrlib.errors import (WeaveError, WeaveFormatError, WeaveParentMismatch,
76
WeaveRevisionNotPresent, WeaveRevisionAlreadyPresent)
77
import bzrlib.errors as errors
76
78
from bzrlib.tsort import topo_sort
548
555
def get_iter(self, name_or_index):
549
556
"""Yield lines for the specified version."""
550
557
incls = [self.maybe_lookup(name_or_index)]
562
# We don't have sha1 sums for multiple entries
551
564
for origin, lineno, line in self._extract(incls):
569
expected_sha1 = self._sha1s[index]
570
measured_sha1 = cur_sha.hexdigest()
571
if measured_sha1 != expected_sha1:
572
raise errors.WeaveInvalidChecksum(
573
'file %s, revision %s, expected: %s, measured %s'
574
% (self._weave_name, self._names[index],
575
expected_sha1, measured_sha1))
555
578
def get_text(self, name_or_index):
600
629
raise WeaveFormatError("invalid included version %d for index %d"
601
630
% (inclusions[-1], version))
603
# try extracting all versions; this is a bit slow and parallel
604
# extraction could be used
632
# try extracting all versions; parallel extraction is used
605
633
nv = self.numversions()
634
sha1s = [sha.new() for i in range(nv)]
635
texts = [[] for i in range(nv)]
638
# For creating the ancestry, IntSet is much faster (3.7s vs 0.17s)
639
# The problem is that set membership is much more expensive
641
for p in self._parents[i]:
642
new_inc.update(inclusions[p])
644
#assert set(new_inc) == self.inclusions([i]), 'failed %s != %s' % (new_inc, self.inclusions([i]))
645
inclusions.append(new_inc)
647
nlines = len(self._weave)
649
update_text = 'checking weave'
651
short_name = os.path.basename(self._weave_name)
652
update_text = 'checking %s' % (short_name,)
653
update_text = update_text[:25]
655
for lineno, insert, deleteset, line in self._walk():
657
progress_bar.update(update_text, lineno, nlines)
659
for j, j_inc in enumerate(inclusions):
660
# The active inclusion must be an ancestor,
661
# and no ancestors must have deleted this line,
662
# because we don't support resurrection.
663
if (insert in j_inc) and not (deleteset & j_inc):
664
sha1s[j].update(line)
606
666
for version in range(nv):
608
progress_bar.update('checking text', version, nv)
610
for l in self.get_iter(version):
667
hd = sha1s[version].hexdigest()
613
668
expected = self._sha1s[version]
614
669
if hd != expected:
615
raise WeaveError("mismatched sha1 for version %d; "
616
"got %s, expected %s"
617
% (version, hd, expected))
670
raise errors.WeaveInvalidChecksum(
671
"mismatched sha1 for version %s: "
672
"got %s, expected %s"
673
% (self._names[version], hd, expected))
619
675
# TODO: check insertions are properly nested, that there are
620
676
# no lines outside of insertion blocks, that deletions are
621
677
# properly paired, etc.
625
def merge(self, merge_versions):
626
"""Automerge and mark conflicts between versions.
628
This returns a sequence, each entry describing alternatives
629
for a chunk of the file. Each of the alternatives is given as
632
If there is a chunk of the file where there's no diagreement,
633
only one alternative is given.
635
# approach: find the included versions common to all the
637
raise NotImplementedError()
641
679
def _delta(self, included, lines):
642
680
"""Return changes from basis to new revision.
767
810
It is illegal for the two weaves to contain different values
768
811
or different parents for any version. See also reweave().
813
:param other: The other weave to pull into this one
814
:param pb: An optional progress bar
815
:param msg: An optional message to display for progress
770
817
if other.numversions() == 0:
771
818
return # nothing to update, easy
772
819
# two loops so that we do not change ourselves before verifying it
774
821
# work through in index order to make sure we get all dependencies
775
for other_idx, name in enumerate(other._names):
776
if self._check_version_consistent(other, other_idx, name):
778
for other_idx, name in enumerate(other._names):
779
# TODO: If all the parents of the other version are already
824
for other_idx, name in enumerate(other._names):
825
self._check_version_consistent(other, other_idx, name)
826
sha1 = other._sha1s[other_idx]
830
if name in self._name_map:
831
idx = self.lookup(name)
832
n1 = set(map(other.idx_to_name, other._parents[other_idx]))
833
n2 = set(map(self.idx_to_name, self._parents[idx]))
834
if sha1 == self._sha1s[idx] and n1 == n2:
837
names_to_join.append((other_idx, name))
844
for other_idx, name in names_to_join:
845
# TODO: If all the parents of the other version are already
780
846
# present then we can avoid some work by just taking the delta
781
847
# and adjusting the offsets.
782
848
new_parents = self._imported_parents(other, other_idx)
849
sha1 = other._sha1s[other_idx]
854
pb.update(msg, merged, len(names_to_join))
783
856
lines = other.get_lines(other_idx)
784
sha1 = other._sha1s[other_idx]
785
857
self.add(name, new_parents, lines, sha1)
859
mutter("merged = %d, processed = %d, file_id=%s; deltat=%d"%(
860
merged, processed, self._weave_name, time.time( )-time0))
788
862
def _imported_parents(self, other, other_idx):
789
863
"""Return list of parents in self corresponding to indexes in other."""
818
892
self_parents = self._parents[this_idx]
819
893
other_parents = other._parents[other_idx]
820
n1 = [self._names[i] for i in self_parents]
821
n2 = [other._names[i] for i in other_parents]
894
n1 = set([self._names[i] for i in self_parents])
895
n2 = set([other._names[i] for i in other_parents])
825
897
raise WeaveParentMismatch("inconsistent parents "
826
898
"for version {%s}: %s vs %s" % (name, n1, n2))
832
def reweave(self, other):
833
"""Reweave self with other."""
834
new_weave = reweave(self, other)
904
def reweave(self, other, pb=None, msg=None):
905
"""Reweave self with other.
907
:param other: The other weave to merge
908
:param pb: An optional progress bar, indicating how far done we are
909
:param msg: An optional message for the progress
911
new_weave = reweave(self, other, pb=pb, msg=msg)
835
912
for attr in self.__slots__:
836
913
setattr(self, attr, getattr(new_weave, attr))
916
def reweave(wa, wb, pb=None, msg=None):
840
917
"""Combine two weaves and return the result.
842
919
This works even if a revision R has different parents in
858
938
mutter("combined parents: %r", combined_parents)
859
939
order = topo_sort(combined_parents.iteritems())
860
940
mutter("order to reweave: %r", order)
945
for idx, name in enumerate(order):
947
pb.update(msg, idx, len(order))
862
948
if name in wa._name_map:
863
949
lines = wa.get_lines(name)
864
950
if name in wb._name_map:
865
assert lines == wb.get_lines(name)
951
lines_b = wb.get_lines(name)
953
mutter('Weaves differ on content. rev_id {%s}', name)
954
mutter('weaves: %s, %s', wa._weave_name, wb._weave_name)
956
lines = list(difflib.unified_diff(lines, lines_b,
957
wa._weave_name, wb._weave_name))
958
mutter('lines:\n%s', ''.join(lines))
959
raise errors.WeaveTextDiffers(name, wa, wb)
867
961
lines = wb.get_lines(name)
868
962
wr.add(name, combined_parents[name], lines)
1224
def lsprofile_main(argv):
1225
from bzrlib.lsprof import profile
1226
ret,stats = profile(main, argv)
1130
1232
if __name__ == '__main__':
1132
1234
if '--profile' in sys.argv:
1133
1235
args = sys.argv[:]
1134
1236
args.remove('--profile')
1135
1237
sys.exit(profile_main(args))
1238
elif '--lsprof' in sys.argv:
1240
args.remove('--lsprof')
1241
sys.exit(lsprofile_main(args))
1137
1243
sys.exit(main(sys.argv))