262
259
__contains__ = has_version
264
def get_delta(self, version_id):
265
"""See VersionedFile.get_delta."""
266
return self.get_deltas([version_id])[version_id]
268
def get_deltas(self, version_ids):
269
"""See VersionedFile.get_deltas."""
270
version_ids = self.get_ancestry(version_ids)
271
for version_id in version_ids:
272
if not self.has_version(version_id):
273
raise RevisionNotPresent(version_id, self)
274
# try extracting all versions; parallel extraction is used
275
nv = self.num_versions()
281
last_parent_lines = {}
283
parent_inclusions = {}
288
# its simplest to generate a full set of prepared variables.
290
name = self._names[i]
291
sha1s[name] = self.get_sha1(name)
292
parents_list = self.get_parents(name)
294
parent = parents_list[0]
295
parents[name] = parent
296
parent_inclusions[name] = inclusions[parent]
299
parent_inclusions[name] = set()
300
# we want to emit start, finish, replacement_length, replacement_lines tuples.
301
diff_hunks[name] = []
302
current_hunks[name] = [0, 0, 0, []] # #start, finish, repl_length, repl_tuples
303
parent_linenums[name] = 0
305
parent_noeols[name] = False
306
last_parent_lines[name] = None
307
new_inc = set([name])
308
for p in self._parents[i]:
309
new_inc.update(inclusions[self._idx_to_name(p)])
310
# debug only, known good so far.
311
#assert set(new_inc) == set(self.get_ancestry(name)), \
312
# 'failed %s != %s' % (set(new_inc), set(self.get_ancestry(name)))
313
inclusions[name] = new_inc
315
nlines = len(self._weave)
317
for lineno, inserted, deletes, line in self._walk_internal():
318
# a line is active in a version if:
319
# insert is in the versions inclusions
321
# deleteset & the versions inclusions is an empty set.
322
# so - if we have a included by mapping - version is included by
323
# children, we get a list of children to examine for deletes affect
324
# ing them, which is less than the entire set of children.
325
for version_id in version_ids:
326
# The active inclusion must be an ancestor,
327
# and no ancestors must have deleted this line,
328
# because we don't support resurrection.
329
parent_inclusion = parent_inclusions[version_id]
330
inclusion = inclusions[version_id]
331
parent_active = inserted in parent_inclusion and not (deletes & parent_inclusion)
332
version_active = inserted in inclusion and not (deletes & inclusion)
333
if not parent_active and not version_active:
334
# unrelated line of ancestry
336
elif parent_active and version_active:
338
parent_linenum = parent_linenums[version_id]
339
if current_hunks[version_id] != [parent_linenum, parent_linenum, 0, []]:
340
diff_hunks[version_id].append(tuple(current_hunks[version_id]))
342
current_hunks[version_id] = [parent_linenum, parent_linenum, 0, []]
343
parent_linenums[version_id] = parent_linenum
346
noeols[version_id] = True
349
elif parent_active and not version_active:
351
current_hunks[version_id][1] += 1
352
parent_linenums[version_id] += 1
353
last_parent_lines[version_id] = line
354
elif not parent_active and version_active:
356
# noeol only occurs at the end of a file because we
357
# diff linewise. We want to show noeol changes as a
358
# empty diff unless the actual eol-less content changed.
361
if last_parent_lines[version_id][-1] != '\n':
362
parent_noeols[version_id] = True
363
except (TypeError, IndexError):
366
if theline[-1] != '\n':
367
noeols[version_id] = True
371
parent_should_go = False
373
if parent_noeols[version_id] == noeols[version_id]:
374
# no noeol toggle, so trust the weaves statement
375
# that this line is changed.
377
if parent_noeols[version_id]:
378
theline = theline + '\n'
379
elif parent_noeols[version_id]:
380
# parent has no eol, we do:
381
# our line is new, report as such..
383
elif noeols[version_id]:
384
# append a eol so that it looks like
386
theline = theline + '\n'
387
if parents[version_id] is not None:
388
#if last_parent_lines[version_id] is not None:
389
parent_should_go = True
390
if last_parent_lines[version_id] != theline:
393
#parent_should_go = False
395
current_hunks[version_id][2] += 1
396
current_hunks[version_id][3].append((inserted, theline))
398
# last hunk last parent line is not eaten
399
current_hunks[version_id][1] -= 1
400
if current_hunks[version_id][1] < 0:
401
current_hunks[version_id][1] = 0
402
# import pdb;pdb.set_trace()
403
# assert current_hunks[version_id][1] >= 0
407
version = self._idx_to_name(i)
408
if current_hunks[version] != [0, 0, 0, []]:
409
diff_hunks[version].append(tuple(current_hunks[version]))
411
for version_id in version_ids:
412
result[version_id] = (
416
diff_hunks[version_id],
420
261
def get_parents(self, version_id):
421
262
"""See VersionedFile.get_parent."""
422
263
return map(self._idx_to_name, self._parents[self._lookup(version_id)])
652
481
@deprecated_method(zero_eight)
654
"""_walk has become visit, a supported api."""
655
return self._walk_internal()
657
def iter_lines_added_or_present_in_versions(self, version_ids=None):
658
"""See VersionedFile.iter_lines_added_or_present_in_versions()."""
659
if version_ids is None:
660
version_ids = self.versions()
661
version_ids = set(version_ids)
662
for lineno, inserted, deletes, line in self._walk_internal(version_ids):
663
# if inserted not in version_ids then it was inserted before the
664
# versions we care about, but because weaves cannot represent ghosts
665
# properly, we do not filter down to that
666
# if inserted not in version_ids: continue
672
#@deprecated_method(zero_eight)
483
"""_walk has become walk, a supported api."""
673
486
def walk(self, version_ids=None):
674
487
"""See VersionedFile.walk."""
675
return self._walk_internal(version_ids)
677
def _walk_internal(self, version_ids=None):
678
"""Helper method for weave actions."""
738
546
WFE = WeaveFormatError
741
# 449 0 4474.6820 2356.5590 bzrlib.weave:556(_extract)
742
# +285282 0 1676.8040 1676.8040 +<isinstance>
743
# 1.6 seconds in 'isinstance'.
744
# changing the first isinstance:
745
# 449 0 2814.2660 1577.1760 bzrlib.weave:556(_extract)
746
# +140414 0 762.8050 762.8050 +<isinstance>
747
# note that the inline time actually dropped (less function calls)
748
# and total processing time was halved.
749
# we're still spending ~1/4 of the method in isinstance though.
750
# so lets hard code the acceptable string classes we expect:
751
# 449 0 1202.9420 786.2930 bzrlib.weave:556(_extract)
752
# +71352 0 377.5560 377.5560 +<method 'append' of 'list'
754
# yay, down to ~1/4 the initial extract time, and our inline time
755
# has shrunk again, with isinstance no longer dominating.
756
# tweaking the stack inclusion test to use a set gives:
757
# 449 0 1122.8030 713.0080 bzrlib.weave:556(_extract)
758
# +71352 0 354.9980 354.9980 +<method 'append' of 'list'
760
# - a 5% win, or possibly just noise. However with large istacks that
761
# 'in' test could dominate, so I'm leaving this change in place -
762
# when its fast enough to consider profiling big datasets we can review.
767
548
for l in self._weave:
768
if l.__class__ == tuple:
549
if isinstance(l, tuple):
553
assert v not in istack
776
iset.remove(istack.pop())
778
558
if v in included:
779
559
assert v not in dset
822
602
return self._lookup(name_or_index)
604
def _get_iter(self, version_id):
605
"""Yield lines for the specified version."""
606
incls = [self._maybe_lookup(version_id)]
611
# We don't have sha1 sums for multiple entries
613
for origin, lineno, line in self._extract(incls):
618
expected_sha1 = self._sha1s[index]
619
measured_sha1 = cur_sha.hexdigest()
620
if measured_sha1 != expected_sha1:
621
raise errors.WeaveInvalidChecksum(
622
'file %s, revision %s, expected: %s, measured %s'
623
% (self._weave_name, self._names[index],
624
expected_sha1, measured_sha1))
824
626
@deprecated_method(zero_eight)
825
627
def get(self, version_id):
826
628
"""Please use either Weave.get_text or Weave.get_lines as desired."""
926
718
# no lines outside of insertion blocks, that deletions are
927
719
# properly paired, etc.
929
def _join(self, other, pb, msg, version_ids, ignore_missing):
721
def _join(self, other, pb, msg, version_ids):
930
722
"""Worker routine for join()."""
931
723
if not other.versions():
932
724
return # nothing to update, easy
935
727
for version_id in version_ids:
936
if not other.has_version(version_id) and not ignore_missing:
728
if not self.has_version(version_id):
937
729
raise RevisionNotPresent(version_id, self._weave_name)
939
version_ids = other.versions()
730
assert version_ids == None
941
732
# two loops so that we do not change ourselves before verifying it
943
734
# work through in index order to make sure we get all dependencies
944
735
names_to_join = []
946
# get the selected versions only that are in other.versions.
947
version_ids = set(other.versions()).intersection(set(version_ids))
948
# pull in the referenced graph.
949
version_ids = other.get_ancestry(version_ids)
950
pending_graph = [(version, other.get_parents(version)) for
951
version in version_ids]
952
for name in topo_sort(pending_graph):
953
other_idx = other._name_map[name]
954
# returns True if we have it, False if we need it.
955
if not self._check_version_consistent(other, other_idx, name):
956
names_to_join.append((other_idx, name))
737
for other_idx, name in enumerate(other._names):
738
self._check_version_consistent(other, other_idx, name)
739
sha1 = other._sha1s[other_idx]
743
if name in self._name_map:
744
idx = self._lookup(name)
745
n1 = set(map(other._idx_to_name, other._parents[other_idx]))
746
n2 = set(map(self._idx_to_name, self._parents[idx]))
747
if sha1 == self._sha1s[idx] and n1 == n2:
750
names_to_join.append((other_idx, name))
960
752
if pb and not msg:
961
753
msg = 'weave join'
1048
836
WEAVE_SUFFIX = '.weave'
1050
def __init__(self, name, transport, filemode=None, create=False, access_mode='w'):
1051
"""Create a WeaveFile.
1053
:param create: If not True, only open an existing knit.
1055
super(WeaveFile, self).__init__(name, access_mode)
838
def __init__(self, name, transport, mode=None):
839
super(WeaveFile, self).__init__(name)
1056
840
self._transport = transport
1057
self._filemode = filemode
1059
843
_read_weave_v5(self._transport.get(name + WeaveFile.WEAVE_SUFFIX), self)
1060
844
except errors.NoSuchFile:
1063
845
# new file, save it
1066
def _add_lines(self, version_id, parents, lines, parent_texts):
848
def add_lines(self, version_id, parents, lines):
1067
849
"""Add a version and save the weave."""
1068
result = super(WeaveFile, self)._add_lines(version_id, parents, lines,
850
super(WeaveFile, self).add_lines(version_id, parents, lines)
1073
def _clone_text(self, new_version_id, old_version_id, parents):
1074
"""See VersionedFile.clone_text."""
1075
super(WeaveFile, self)._clone_text(new_version_id, old_version_id, parents)
1078
853
def copy_to(self, name, transport):
1079
854
"""See VersionedFile.copy_to()."""
1081
856
sio = StringIO()
1082
857
write_weave_v5(self, sio)
1084
transport.put(name + WeaveFile.WEAVE_SUFFIX, sio, self._filemode)
859
transport.put(name + WeaveFile.WEAVE_SUFFIX, sio, self._mode)
1086
def create_empty(self, name, transport, filemode=None):
1087
return WeaveFile(name, transport, filemode, create=True)
861
def create_empty(self, name, transport, mode=None):
862
return WeaveFile(name, transport, mode)
1089
864
def _save(self):
1090
865
"""Save the weave."""
1091
self._check_write_ok()
1092
866
sio = StringIO()
1093
867
write_weave_v5(self, sio)
1095
869
self._transport.put(self._weave_name + WeaveFile.WEAVE_SUFFIX,
1100
874
def get_suffixes():
1101
875
"""See VersionedFile.get_suffixes()."""
1102
876
return [WeaveFile.WEAVE_SUFFIX]
1104
def join(self, other, pb=None, msg=None, version_ids=None,
1105
ignore_missing=False):
878
def join(self, other, pb=None, msg=None, version_ids=None):
1106
879
"""Join other into self and save."""
1107
super(WeaveFile, self).join(other, pb, msg, version_ids, ignore_missing)
880
super(WeaveFile, self).join(other, pb, msg, version_ids)