25
25
# with intset (r926) 2000 versions in 93s !!!
26
26
# better to just use plain sets.
28
# making _extract build and return a list, rather than being a generator
31
# with python -O, r923 does 2000 versions in 36.87s
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.
37
# r931, which avoids using a generator for extract, does 36.98s
39
# with memoized inclusions, takes 41.49s; not very good
41
# with slots, takes 37.35s; without takes 39.16, a bit surprising
43
# with the delta calculation mixed in with the add method, rather than
44
# separated, takes 36.78s
46
# with delta folded in and mutation of the list, 36.13s
48
# with all this and simplification of add code, 33s
51
28
# TODO: Perhaps have copy method for Weave instances?
53
30
# XXX: If we do weaves this way, will a merge still behave the same
194
174
Sequence of lines to be added in the new version."""
196
self._check_versions(parents)
175
## self._check_versions(parents)
197
176
## self._check_lines(text)
198
new_version = len(self._parents)
203
183
sha1 = s.hexdigest()
206
# if we abort after here the weave will be corrupt
207
self._parents.append(frozenset(parents))
186
# TODO: It'd probably be faster to append things on to a new
187
# list rather than modifying the existing one, which is likely
188
# to cause a lot of copying.
191
ancestors = self.inclusions(parents)
192
delta = self._delta(ancestors, text)
194
# offset gives the number of lines that have been inserted
195
# into the weave up to the current point; if the original edit instruction
196
# says to change line A then we actually change (A+offset)
199
for i1, i2, newlines in delta:
202
assert i2 <= len(self._l)
204
# the deletion and insertion are handled separately.
205
# first delete the region.
207
self._l.insert(i1+offset, ('[', idx))
208
self._l.insert(i2+offset+1, (']', idx))
213
# there may have been a deletion spanning up to
214
# i2; we want to insert after this region to make sure
215
# we don't destroy ourselves
217
self._l[i:i] = [('{', idx)] \
220
offset += 2 + len(newlines)
222
self._addversion(parents)
224
# special case; adding with no parents revision; can do this
225
# more quickly by just appending unconditionally
226
self._l.append(('{', idx))
228
self._l.append(('}', idx))
230
self._addversion(None)
208
232
self._sha1s.append(sha1)
212
# special case; adding with no parents revision; can do
213
# this more quickly by just appending unconditionally.
214
# even more specially, if we're adding an empty text we
215
# need do nothing at all.
217
self._weave.append(('{', new_version))
218
self._weave.extend(text)
219
self._weave.append(('}', new_version))
223
if len(parents) == 1:
224
pv = list(parents)[0]
225
if sha1 == self._sha1s[pv]:
226
# special case: same as the single parent
230
ancestors = self.inclusions(parents)
234
# basis a list of (origin, lineno, line)
237
for origin, lineno, line in self._extract(ancestors):
238
basis_lineno.append(lineno)
239
basis_lines.append(line)
241
# another small special case: a merge, producing the same text as auto-merge
242
if text == basis_lines:
245
# add a sentinal, because we can also match against the final line
246
basis_lineno.append(len(self._weave))
248
# XXX: which line of the weave should we really consider
249
# matches the end of the file? the current code says it's the
250
# last line of the weave?
252
#print 'basis_lines:', basis_lines
253
#print 'new_lines: ', lines
255
from difflib import SequenceMatcher
256
s = SequenceMatcher(None, basis_lines, text)
258
# offset gives the number of lines that have been inserted
259
# into the weave up to the current point; if the original edit instruction
260
# says to change line A then we actually change (A+offset)
263
for tag, i1, i2, j1, j2 in s.get_opcodes():
264
# i1,i2 are given in offsets within basis_lines; we need to map them
265
# back to offsets within the entire weave
266
#print 'raw match', tag, i1, i2, j1, j2
270
i1 = basis_lineno[i1]
271
i2 = basis_lineno[i2]
273
assert 0 <= j1 <= j2 <= len(text)
275
#print tag, i1, i2, j1, j2
277
# the deletion and insertion are handled separately.
278
# first delete the region.
280
self._weave.insert(i1+offset, ('[', new_version))
281
self._weave.insert(i2+offset+1, (']', new_version))
285
# there may have been a deletion spanning up to
286
# i2; we want to insert after this region to make sure
287
# we don't destroy ourselves
289
self._weave[i:i] = ([('{', new_version)]
291
+ [('}', new_version)])
292
offset += 2 + (j2 - j1)
297
237
def inclusions(self, versions):
480
421
def dump(self, to_file):
481
422
from pprint import pprint
482
print >>to_file, "Weave._weave = ",
483
pprint(self._weave, to_file)
484
print >>to_file, "Weave._parents = ",
485
pprint(self._parents, to_file)
423
print >>to_file, "Weave._l = ",
424
pprint(self._l, to_file)
425
print >>to_file, "Weave._v = ",
426
pprint(self._v, to_file)
489
430
def numversions(self):
490
l = len(self._parents)
491
432
assert l == len(self._sha1s)
496
return self.numversions()
499
436
def check(self, progress_bar=None):
500
437
# check no circular inclusions
501
438
for version in range(self.numversions()):
502
inclusions = list(self._parents[version])
439
inclusions = list(self._v[version])
504
441
inclusions.sort()
505
442
if inclusions[-1] >= version:
565
502
If line1=line2, this is a pure insert; if newlines=[] this is a
566
503
pure delete. (Similar to difflib.)
505
# basis a list of (origin, lineno, line)
508
for origin, lineno, line in self._extract(included):
509
basis_lineno.append(lineno)
510
basis_lines.append(line)
512
# add a sentinal, because we can also match against the final line
513
basis_lineno.append(len(self._l))
515
# XXX: which line of the weave should we really consider
516
# matches the end of the file? the current code says it's the
517
# last line of the weave?
519
from difflib import SequenceMatcher
520
s = SequenceMatcher(None, basis_lines, lines)
522
# TODO: Perhaps return line numbers from composed weave as well?
524
for tag, i1, i2, j1, j2 in s.get_opcodes():
525
##print tag, i1, i2, j1, j2
530
# i1,i2 are given in offsets within basis_lines; we need to map them
531
# back to offsets within the entire weave
532
real_i1 = basis_lineno[i1]
533
real_i2 = basis_lineno[i2]
537
assert j2 <= len(lines)
539
yield real_i1, real_i2, lines[j1:j2]
645
def weave_info(filename, out):
674
646
"""Show some text information about the weave."""
675
print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
for i in (6, 40, 20):
679
for i in range(w.numversions()):
681
print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
685
def weave_stats(weave_file):
686
from bzrlib.progress import ProgressBar
687
from bzrlib.weavefile import read_weave
691
wf = file(weave_file, 'rb')
647
from weavefile import read_weave
648
wf = file(filename, 'rb')
692
649
w = read_weave(wf)
693
650
# FIXME: doesn't work on pipes
694
651
weave_size = wf.tell()
652
print >>out, "weave file size %d bytes" % weave_size
653
print >>out, "weave contains %d versions" % len(w._v)
698
for i in range(vers):
699
pb.update('checking sizes', i, vers)
700
for line in w.get_iter(i):
705
print 'versions %9d' % vers
706
print 'weave file %9d bytes' % weave_size
707
print 'total contents %9d bytes' % total
708
print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
656
print '%6s %6s %8s %40s %20s' % ('ver', 'lines', 'bytes', 'sha1', 'parents')
657
for i in (6, 6, 8, 40, 20):
660
for i in range(len(w._v)):
663
bytes = sum((len(a) for a in text))
665
print '%6d %6d %8d %40s' % (i, lines, bytes, sha1),
671
print >>out, "versions total %d bytes" % total
672
print >>out, "compression ratio %.3f" % (float(total)/float(weave_size))