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
90
from cStringIO import StringIO
94
from difflib import SequenceMatcher
93
99
class WeaveError(Exception):
211
217
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)
214
228
def lookup(self, name):
229
"""Convert symbolic version name to index."""
216
231
return self._name_map[name]
218
raise WeaveError("name %s not present in weave %s" %
233
raise WeaveError("name %r not present in weave %r" %
219
234
(name, self._weave_name))
222
def add(self, name, parents, text):
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):
223
258
"""Add a single text on top of the weave.
225
260
Returns the index number of the newly added version.
232
267
List or set of direct parent version numbers.
235
Sequence of lines to be added in the new version."""
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
237
277
assert isinstance(name, basestring)
279
sha1 = sha_strings(text)
238
280
if name in self._name_map:
239
raise WeaveError("name %r already present in weave" % name)
281
return self._check_repeated_add(name, parents, text, sha1)
283
parents = map(self.maybe_lookup, parents)
241
284
self._check_versions(parents)
242
285
## self._check_lines(text)
243
286
new_version = len(self._parents)
250
289
# if we abort after here the (in-memory) weave will be corrupt because only
251
290
# some fields are updated
300
339
#print 'basis_lines:', basis_lines
301
340
#print 'new_lines: ', lines
303
from difflib import SequenceMatcher
304
342
s = SequenceMatcher(None, basis_lines, text)
306
344
# offset gives the number of lines that have been inserted
345
383
def inclusions(self, versions):
346
384
"""Return set of all ancestors of given version(s)."""
347
385
i = set(versions)
352
# include all its parents
353
i.update(self._parents[v])
357
raise ValueError("version %d not present in weave" % v)
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]
360
399
def minimal_parents(self, version):
400
439
raise IndexError("invalid version number %r" % i)
403
def annotate(self, index):
404
return list(self.annotate_iter(index))
407
def annotate_iter(self, version):
442
def annotate(self, name_or_index):
443
return list(self.annotate_iter(name_or_index))
446
def annotate_iter(self, name_or_index):
408
447
"""Yield list of (index-id, line) pairs for the specified version.
410
449
The index indicates when the line originated in the weave."""
411
for origin, lineno, text in self._extract([version]):
450
incls = [self.maybe_lookup(name_or_index)]
451
for origin, lineno, text in self._extract(incls):
412
452
yield origin, text
515
def get_iter(self, version):
555
def get_iter(self, name_or_index):
516
556
"""Yield lines for the specified version."""
517
for origin, lineno, line in self._extract([version]):
557
incls = [self.maybe_lookup(name_or_index)]
558
for origin, lineno, line in self._extract(incls):
521
def get_text(self, version):
562
def get_text(self, name_or_index):
563
return ''.join(self.get_iter(name_or_index))
522
564
assert isinstance(version, int)
524
s.writelines(self.get_iter(version))
528
def get(self, index):
529
return list(self.get_iter(index))
567
def get_lines(self, name_or_index):
568
return list(self.get_iter(name_or_index))
532
574
def mash_iter(self, included):
533
575
"""Return composed version of multiple included versions."""
576
included = map(self.maybe_lookup, included)
534
577
for origin, lineno, text in self._extract(included):
744
def weave_stats(weave_file):
745
from bzrlib.progress import ProgressBar
787
def weave_stats(weave_file, pb):
746
788
from bzrlib.weavefile import read_weave
750
790
wf = file(weave_file, 'rb')
751
791
w = read_weave(wf)
752
792
# FIXME: doesn't work on pipes
793
833
Display composite of all selected versions.
794
834
weave merge WEAVEFILE VERSION1 VERSION2 > OUT
795
835
Auto-merge two versions and display conflicts.
836
weave diff WEAVEFILE VERSION1 VERSION2
837
Show differences between two versions.
826
from weavefile import write_weave, read_weave
871
# in case we're run directly from the subdirectory
872
sys.path.append('..')
874
from bzrlib.weavefile import write_weave, read_weave
827
875
from bzrlib.progress import ProgressBar
867
915
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)
869
929
elif cmd == 'annotate':
871
931
# newline is added to all lines regardless; too hard to get