84
84
# TODO: Perhaps the API should work only in names to hide the integer
85
85
# indexes from the user?
87
# TODO: Is there any potential performance win by having an add()
88
# variant that is passed a pre-cooked version of the single basis
94
from difflib import SequenceMatcher
189
183
For each name, the version number.
192
Descriptive name of this weave; typically the filename if known.
196
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
186
__slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
199
def __init__(self, weave_name=None):
201
190
self._parents = []
204
193
self._name_map = {}
205
self._weave_name = weave_name
208
196
def __eq__(self, other):
217
205
return not self.__eq__(other)
220
def maybe_lookup(self, name_or_index):
221
"""Convert possible symbolic name to index, or pass through indexes."""
222
if isinstance(name_or_index, (int, long)):
225
return self.lookup(name_or_index)
228
208
def lookup(self, name):
229
"""Convert symbolic version name to index."""
231
210
return self._name_map[name]
233
raise WeaveError("name %r not present in weave %r" %
234
(name, self._weave_name))
237
def idx_to_name(self, version):
238
return self._names[version]
241
def _check_repeated_add(self, name, parents, text, sha1):
242
"""Check that a duplicated add is OK.
244
If it is, return the (old) index; otherwise raise an exception.
246
idx = self.lookup(name)
247
if sorted(self._parents[idx]) != sorted(parents):
248
raise WeaveError("name \"%s\" already present in weave "
249
"with different parents" % name)
250
if sha1 != self._sha1s[idx]:
251
raise WeaveError("name \"%s\" already present in weave "
252
"with different text" % name)
257
def add(self, name, parents, text, sha1=None):
212
raise WeaveError("name %s not present in weave" % name)
215
def add(self, name, parents, text):
258
216
"""Add a single text on top of the weave.
260
218
Returns the index number of the newly added version.
267
225
List or set of direct parent version numbers.
270
Sequence of lines to be added in the new version.
272
sha -- SHA-1 of the file, if known. This is trusted to be
275
from bzrlib.osutils import sha_strings
228
Sequence of lines to be added in the new version."""
277
230
assert isinstance(name, basestring)
279
sha1 = sha_strings(text)
280
231
if name in self._name_map:
281
return self._check_repeated_add(name, parents, text, sha1)
283
parents = map(self.maybe_lookup, parents)
232
raise WeaveError("name %r already present in weave" % name)
284
234
self._check_versions(parents)
285
235
## self._check_lines(text)
286
236
new_version = len(self._parents)
289
243
# if we abort after here the (in-memory) weave will be corrupt because only
290
244
# some fields are updated
339
293
#print 'basis_lines:', basis_lines
340
294
#print 'new_lines: ', lines
296
from difflib import SequenceMatcher
342
297
s = SequenceMatcher(None, basis_lines, text)
344
299
# offset gives the number of lines that have been inserted
383
338
def inclusions(self, versions):
384
339
"""Return set of all ancestors of given version(s)."""
385
340
i = set(versions)
386
for v in xrange(max(versions), 0, -1):
388
# include all its parents
389
i.update(self._parents[v])
391
## except IndexError:
392
## raise ValueError("version %d not present in weave" % v)
395
def parents(self, version):
396
return self._parents[version]
345
# include all its parents
346
i.update(self._parents[v])
350
raise ValueError("version %d not present in weave" % v)
399
353
def minimal_parents(self, version):
439
393
raise IndexError("invalid version number %r" % i)
442
def annotate(self, name_or_index):
443
return list(self.annotate_iter(name_or_index))
446
def annotate_iter(self, name_or_index):
396
def annotate(self, index):
397
return list(self.annotate_iter(index))
400
def annotate_iter(self, version):
447
401
"""Yield list of (index-id, line) pairs for the specified version.
449
403
The index indicates when the line originated in the weave."""
450
incls = [self.maybe_lookup(name_or_index)]
451
for origin, lineno, text in self._extract(incls):
404
for origin, lineno, text in self._extract([version]):
452
405
yield origin, text
555
def get_iter(self, name_or_index):
504
def get_iter(self, version):
556
505
"""Yield lines for the specified version."""
557
incls = [self.maybe_lookup(name_or_index)]
558
for origin, lineno, line in self._extract(incls):
506
for origin, lineno, line in self._extract([version]):
562
def get_text(self, name_or_index):
563
return ''.join(self.get_iter(name_or_index))
564
assert isinstance(version, int)
567
def get_lines(self, name_or_index):
568
return list(self.get_iter(name_or_index))
510
def get(self, index):
511
return list(self.get_iter(index))
574
514
def mash_iter(self, included):
575
515
"""Return composed version of multiple included versions."""
576
included = map(self.maybe_lookup, included)
577
516
for origin, lineno, text in self._extract(included):
787
def weave_stats(weave_file, pb):
726
def weave_stats(weave_file):
727
from bzrlib.progress import ProgressBar
788
728
from bzrlib.weavefile import read_weave
790
732
wf = file(weave_file, 'rb')
791
733
w = read_weave(wf)
792
734
# FIXME: doesn't work on pipes
833
775
Display composite of all selected versions.
834
776
weave merge WEAVEFILE VERSION1 VERSION2 > OUT
835
777
Auto-merge two versions and display conflicts.
836
weave diff WEAVEFILE VERSION1 VERSION2
837
Show differences between two versions.
871
# in case we're run directly from the subdirectory
872
sys.path.append('..')
874
from bzrlib.weavefile import write_weave, read_weave
808
from weavefile import write_weave, read_weave
875
809
from bzrlib.progress import ProgressBar
915
849
sys.stdout.writelines(w.mash_iter(map(int, argv[3:])))
918
from difflib import unified_diff
921
v1, v2 = map(int, argv[3:5])
924
diff_gen = unified_diff(lines1, lines2,
925
'%s version %d' % (fn, v1),
926
'%s version %d' % (fn, v2))
927
sys.stdout.writelines(diff_gen)
929
851
elif cmd == 'annotate':
931
853
# newline is added to all lines regardless; too hard to get