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
545
555
def get_iter(self, name_or_index):
546
556
"""Yield lines for the specified version."""
547
557
incls = [self.maybe_lookup(name_or_index)]
562
# We don't have sha1 sums for multiple entries
548
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))
552
578
def get_text(self, name_or_index):
597
629
raise WeaveFormatError("invalid included version %d for index %d"
598
630
% (inclusions[-1], version))
600
# try extracting all versions; this is a bit slow and parallel
601
# extraction could be used
632
# try extracting all versions; parallel extraction is used
602
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)
603
666
for version in range(nv):
605
progress_bar.update('checking text', version, nv)
607
for l in self.get_iter(version):
667
hd = sha1s[version].hexdigest()
610
668
expected = self._sha1s[version]
611
669
if hd != expected:
612
raise WeaveError("mismatched sha1 for version %d; "
613
"got %s, expected %s"
614
% (version, hd, expected))
670
raise errors.WeaveInvalidChecksum(
671
"mismatched sha1 for version %s: "
672
"got %s, expected %s"
673
% (self._names[version], hd, expected))
616
675
# TODO: check insertions are properly nested, that there are
617
676
# no lines outside of insertion blocks, that deletions are
618
677
# properly paired, etc.
622
def merge(self, merge_versions):
623
"""Automerge and mark conflicts between versions.
625
This returns a sequence, each entry describing alternatives
626
for a chunk of the file. Each of the alternatives is given as
629
If there is a chunk of the file where there's no diagreement,
630
only one alternative is given.
632
# approach: find the included versions common to all the
634
raise NotImplementedError()
638
679
def _delta(self, included, lines):
639
680
"""Return changes from basis to new revision.
771
821
# work through in index order to make sure we get all dependencies
772
822
for other_idx, name in enumerate(other._names):
773
if self._check_version_consistent(other, other_idx, name):
823
self._check_version_consistent(other, other_idx, name)
775
831
for other_idx, name in enumerate(other._names):
776
# TODO: If all the parents of the other version are already
832
# TODO: If all the parents of the other version are already
777
833
# present then we can avoid some work by just taking the delta
778
834
# and adjusting the offsets.
779
835
new_parents = self._imported_parents(other, other_idx)
836
sha1 = other._sha1s[other_idx]
840
if name in self._name_map:
841
idx = self.lookup(name)
842
n1 = set(map(other.idx_to_name, other._parents[other_idx]))
843
n2 = set(map(self.idx_to_name, self._parents[idx]))
844
if sha1 == self._sha1s[idx] and n1 == n2:
848
pb.update(msg, other_idx, len(other._names))
780
851
lines = other.get_lines(other_idx)
781
sha1 = other._sha1s[other_idx]
782
852
self.add(name, new_parents, lines, sha1)
854
mutter("merged = %d, processed = %d, file_id=%s; deltat=%d"%(
855
merged,processed,self._weave_name, time.time( )-time0))
785
860
def _imported_parents(self, other, other_idx):
786
861
"""Return list of parents in self corresponding to indexes in other."""
815
890
self_parents = self._parents[this_idx]
816
891
other_parents = other._parents[other_idx]
817
n1 = [self._names[i] for i in self_parents]
818
n2 = [other._names[i] for i in other_parents]
892
n1 = set([self._names[i] for i in self_parents])
893
n2 = set([other._names[i] for i in other_parents])
822
895
raise WeaveParentMismatch("inconsistent parents "
823
896
"for version {%s}: %s vs %s" % (name, n1, n2))
829
def reweave(self, other):
830
"""Reweave self with other."""
831
new_weave = reweave(self, other)
902
def reweave(self, other, pb=None, msg=None):
903
"""Reweave self with other.
905
:param other: The other weave to merge
906
:param pb: An optional progress bar, indicating how far done we are
907
:param msg: An optional message for the progress
909
new_weave = reweave(self, other, pb=pb, msg=msg)
832
910
for attr in self.__slots__:
833
911
setattr(self, attr, getattr(new_weave, attr))
914
def reweave(wa, wb, pb=None, msg=None):
837
915
"""Combine two weaves and return the result.
839
917
This works even if a revision R has different parents in
855
936
mutter("combined parents: %r", combined_parents)
856
937
order = topo_sort(combined_parents.iteritems())
857
938
mutter("order to reweave: %r", order)
943
for idx, name in enumerate(order):
945
pb.update(msg, idx, len(order))
859
946
if name in wa._name_map:
860
947
lines = wa.get_lines(name)
861
948
if name in wb._name_map:
862
assert lines == wb.get_lines(name)
949
lines_b = wb.get_lines(name)
951
mutter('Weaves differ on content. rev_id {%s}', name)
952
mutter('weaves: %s, %s', wa._weave_name, wb._weave_name)
954
lines = list(difflib.unified_diff(lines, lines_b,
955
wa._weave_name, wb._weave_name))
956
mutter('lines:\n%s', ''.join(lines))
957
raise errors.WeaveTextDiffers(name, wa, wb)
864
959
lines = wb.get_lines(name)
865
960
wr.add(name, combined_parents[name], lines)
1222
def lsprofile_main(argv):
1223
from bzrlib.lsprof import profile
1224
ret,stats = profile(main, argv)
1127
1230
if __name__ == '__main__':
1129
1232
if '--profile' in sys.argv:
1130
1233
args = sys.argv[:]
1131
1234
args.remove('--profile')
1132
1235
sys.exit(profile_main(args))
1236
elif '--lsprof' in sys.argv:
1238
args.remove('--lsprof')
1239
sys.exit(lsprofile_main(args))
1134
1241
sys.exit(main(sys.argv))