398
379
def _add(self, version_id, lines, parents, sha1=None, nostore_sha=None):
399
380
"""Add a single text on top of the weave.
401
382
Returns the index number of the newly added version.
404
385
Symbolic name for this version.
405
386
(Typically the revision-id of the revision that added it.)
406
If None, a name will be allocated based on the hash. (sha1:SHAHASH)
409
389
List or set of direct parent version numbers.
412
392
Sequence of lines to be added in the new version.
414
394
:param nostore_sha: See VersionedFile.add_lines.
396
assert isinstance(version_id, basestring)
416
397
self._check_lines_not_unicode(lines)
417
398
self._check_lines_are_lines(lines)
419
400
sha1 = sha_strings(lines)
420
401
if sha1 == nostore_sha:
421
402
raise errors.ExistingContent
422
if version_id is None:
423
version_id = "sha1:" + sha1
424
403
if version_id in self._name_map:
425
404
return self._check_repeated_add(version_id, parents, lines, sha1)
699
692
# we're still spending ~1/4 of the method in isinstance though.
700
693
# so lets hard code the acceptable string classes we expect:
701
694
# 449 0 1202.9420 786.2930 bzrlib.weave:556(_extract)
702
# +71352 0 377.5560 377.5560 +<method 'append' of 'list'
695
# +71352 0 377.5560 377.5560 +<method 'append' of 'list'
704
697
# yay, down to ~1/4 the initial extract time, and our inline time
705
698
# has shrunk again, with isinstance no longer dominating.
706
699
# tweaking the stack inclusion test to use a set gives:
707
700
# 449 0 1122.8030 713.0080 bzrlib.weave:556(_extract)
708
# +71352 0 354.9980 354.9980 +<method 'append' of 'list'
701
# +71352 0 354.9980 354.9980 +<method 'append' of 'list'
710
703
# - a 5% win, or possibly just noise. However with large istacks that
711
704
# 'in' test could dominate, so I'm leaving this change in place -
712
705
# when its fast enough to consider profiling big datasets we can review.
717
710
for l in self._weave:
718
711
if l.__class__ == tuple:
725
719
iset.remove(istack.pop())
727
721
if v in included:
730
726
if v in included:
733
raise AssertionError()
730
assert l.__class__ in (str, unicode)
735
731
if isactive is None:
736
732
isactive = (not dset) and istack and (istack[-1] in included)
848
840
# no lines outside of insertion blocks, that deletions are
849
841
# properly paired, etc.
843
def _join(self, other, pb, msg, version_ids, ignore_missing):
844
"""Worker routine for join()."""
845
if not other.versions():
846
return # nothing to update, easy
849
# versions is never none, InterWeave checks this.
852
# two loops so that we do not change ourselves before verifying it
854
# work through in index order to make sure we get all dependencies
857
# get the selected versions only that are in other.versions.
858
version_ids = set(other.versions()).intersection(set(version_ids))
859
# pull in the referenced graph.
860
version_ids = other.get_ancestry(version_ids)
861
pending_parents = other.get_parent_map(version_ids)
862
pending_graph = pending_parents.items()
863
if len(pending_graph) != len(version_ids):
864
raise RevisionNotPresent(
865
set(version_ids) - set(pending_parents.keys()), self)
866
for name in topo_sort(pending_graph):
867
other_idx = other._name_map[name]
868
# returns True if we have it, False if we need it.
869
if not self._check_version_consistent(other, other_idx, name):
870
names_to_join.append((other_idx, name))
878
for other_idx, name in names_to_join:
879
# TODO: If all the parents of the other version are already
880
# present then we can avoid some work by just taking the delta
881
# and adjusting the offsets.
882
new_parents = self._imported_parents(other, other_idx)
883
sha1 = other._sha1s[other_idx]
888
pb.update(msg, merged, len(names_to_join))
890
lines = other.get_lines(other_idx)
891
self._add(name, lines, new_parents, sha1)
893
mutter("merged = %d, processed = %d, file_id=%s; deltat=%d"%(
894
merged, processed, self._weave_name, time.time()-time0))
851
896
def _imported_parents(self, other, other_idx):
852
897
"""Return list of parents in self corresponding to indexes in other."""
952
996
write_weave_v5(self, sio)
954
bytes = sio.getvalue()
955
path = self._weave_name + WeaveFile.WEAVE_SUFFIX
957
self._transport.put_bytes(path, bytes, self._filemode)
958
except errors.NoSuchFile:
959
self._transport.mkdir(dirname(path))
960
self._transport.put_bytes(path, bytes, self._filemode)
998
self._transport.put_file(self._weave_name + WeaveFile.WEAVE_SUFFIX,
963
1003
def get_suffixes():
964
1004
"""See VersionedFile.get_suffixes()."""
965
1005
return [WeaveFile.WEAVE_SUFFIX]
967
def insert_record_stream(self, stream):
968
super(WeaveFile, self).insert_record_stream(stream)
1007
def join(self, other, pb=None, msg=None, version_ids=None,
1008
ignore_missing=False):
1009
"""Join other into self and save."""
1010
super(WeaveFile, self).join(other, pb, msg, version_ids, ignore_missing)
972
1014
def _reweave(wa, wb, pb=None, msg=None):
973
1015
"""Combine two weaves and return the result.
975
This works even if a revision R has different parents in
1017
This works even if a revision R has different parents in
976
1018
wa and wb. In the resulting weave all the parents are given.
978
This is done by just building up a new weave, maintaining ordering
1020
This is done by just building up a new weave, maintaining ordering
979
1021
of the versions in the two inputs. More efficient approaches
980
might be possible but it should only be necessary to do
981
this operation rarely, when a new previously ghost version is
1022
might be possible but it should only be necessary to do
1023
this operation rarely, when a new previously ghost version is
984
1026
:param pb: An optional progress bar, indicating how far done we are
1029
1070
p = combined.setdefault(name, set())
1030
1071
p.update(map(weave._idx_to_name, weave._parents[idx]))
1031
1072
return combined
1076
"""Show the weave's table-of-contents"""
1077
print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
1078
for i in (6, 50, 10, 10):
1081
for i in range(w.num_versions()):
1084
parent_str = ' '.join(map(str, w._parents[i]))
1085
print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
1089
def weave_stats(weave_file, pb):
1090
from bzrlib.weavefile import read_weave
1092
wf = file(weave_file, 'rb')
1094
# FIXME: doesn't work on pipes
1095
weave_size = wf.tell()
1099
for i in range(vers):
1100
pb.update('checking sizes', i, vers)
1101
for origin, lineno, line in w._extract([i]):
1106
print 'versions %9d' % vers
1107
print 'weave file %9d bytes' % weave_size
1108
print 'total contents %9d bytes' % total
1109
print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
1112
print 'average size %9d bytes' % avg
1113
print 'relative size %9.2fx' % (float(weave_size) / float(avg))
1117
print """bzr weave tool
1119
Experimental tool for weave algorithm.
1122
weave init WEAVEFILE
1123
Create an empty weave file
1124
weave get WEAVEFILE VERSION
1125
Write out specified version.
1126
weave check WEAVEFILE
1127
Check consistency of all versions.
1129
Display table of contents.
1130
weave add WEAVEFILE NAME [BASE...] < NEWTEXT
1131
Add NEWTEXT, with specified parent versions.
1132
weave annotate WEAVEFILE VERSION
1133
Display origin of each line.
1134
weave merge WEAVEFILE VERSION1 VERSION2 > OUT
1135
Auto-merge two versions and display conflicts.
1136
weave diff WEAVEFILE VERSION1 VERSION2
1137
Show differences between two versions.
1141
% weave init foo.weave
1143
% weave add foo.weave ver0 < foo.txt
1146
(create updated version)
1148
% weave get foo.weave 0 | diff -u - foo.txt
1149
% weave add foo.weave ver1 0 < foo.txt
1152
% weave get foo.weave 0 > foo.txt (create forked version)
1154
% weave add foo.weave ver2 0 < foo.txt
1157
% weave merge foo.weave 1 2 > foo.txt (merge them)
1158
% vi foo.txt (resolve conflicts)
1159
% weave add foo.weave merged 1 2 < foo.txt (commit merged version)
1171
# in case we're run directly from the subdirectory
1172
sys.path.append('..')
1174
from bzrlib.weavefile import write_weave, read_weave
1175
from bzrlib.progress import ProgressBar
1190
return read_weave(file(argv[2], 'rb'))
1196
# at the moment, based on everything in the file
1198
parents = map(int, argv[4:])
1199
lines = sys.stdin.readlines()
1200
ver = w.add(name, parents, lines)
1201
write_weave(w, file(argv[2], 'wb'))
1202
print 'added version %r %d' % (name, ver)
1205
if os.path.exists(fn):
1206
raise IOError("file exists")
1208
write_weave(w, file(fn, 'wb'))
1209
elif cmd == 'get': # get one version
1211
sys.stdout.writelines(w.get_iter(int(argv[3])))
1216
v1, v2 = map(int, argv[3:5])
1219
diff_gen = bzrlib.patiencediff.unified_diff(lines1, lines2,
1220
'%s version %d' % (fn, v1),
1221
'%s version %d' % (fn, v2))
1222
sys.stdout.writelines(diff_gen)
1224
elif cmd == 'annotate':
1226
# newline is added to all lines regardless; too hard to get
1227
# reasonable formatting otherwise
1229
for origin, text in w.annotate(int(argv[3])):
1230
text = text.rstrip('\r\n')
1232
print ' | %s' % (text)
1234
print '%5d | %s' % (origin, text)
1240
elif cmd == 'stats':
1241
weave_stats(argv[2], ProgressBar())
1243
elif cmd == 'check':
1248
print '%d versions ok' % w.num_versions()
1250
elif cmd == 'inclusions':
1252
print ' '.join(map(str, w.inclusions([int(argv[3])])))
1254
elif cmd == 'parents':
1256
print ' '.join(map(str, w._parents[int(argv[3])]))
1258
elif cmd == 'plan-merge':
1259
# replaced by 'bzr weave-plan-merge'
1261
for state, line in w.plan_merge(int(argv[3]), int(argv[4])):
1263
print '%14s | %s' % (state, line),
1264
elif cmd == 'merge':
1265
# replaced by 'bzr weave-merge-text'
1267
p = w.plan_merge(int(argv[3]), int(argv[4]))
1268
sys.stdout.writelines(w.weave_merge(p))
1270
raise ValueError('unknown command %r' % cmd)
1273
if __name__ == '__main__':
1275
sys.exit(main(sys.argv))
1278
class InterWeave(InterVersionedFile):
1279
"""Optimised code paths for weave to weave operations."""
1281
_matching_file_from_factory = staticmethod(WeaveFile)
1282
_matching_file_to_factory = staticmethod(WeaveFile)
1285
def is_compatible(source, target):
1286
"""Be compatible with weaves."""
1288
return (isinstance(source, Weave) and
1289
isinstance(target, Weave))
1290
except AttributeError:
1293
def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False):
1294
"""See InterVersionedFile.join."""
1295
version_ids = self._get_source_version_ids(version_ids, ignore_missing)
1296
if self.target.versions() == [] and version_ids is None:
1297
self.target._copy_weave_content(self.source)
1299
self.target._join(self.source, pb, msg, version_ids, ignore_missing)
1302
InterVersionedFile.register_optimiser(InterWeave)