206
213
def __ne__(self, other):
207
214
return not self.__eq__(other)
209
def __contains__(self, name):
210
return self._name_map.has_key(name)
212
def maybe_lookup(self, name_or_index):
213
"""Convert possible symbolic name to index, or pass through indexes."""
214
if isinstance(name_or_index, (int, long)):
217
return self.lookup(name_or_index)
216
@deprecated_method(zero_eight)
217
def idx_to_name(self, index):
218
"""Old public interface, the public interface is all names now."""
221
def _idx_to_name(self, version):
222
return self._names[version]
224
@deprecated_method(zero_eight)
220
225
def lookup(self, name):
226
"""Backwards compatability thunk:
228
Return name, as name is valid in the api now, and spew deprecation
233
def _lookup(self, name):
221
234
"""Convert symbolic version name to index."""
223
236
return self._name_map[name]
225
raise WeaveRevisionNotPresent(name, self)
238
raise RevisionNotPresent(name, self._weave_name)
240
@deprecated_method(zero_eight)
241
def iter_names(self):
242
"""Deprecated convenience function, please see VersionedFile.names()."""
243
return iter(self.names())
245
@deprecated_method(zero_eight)
247
"""See Weave.versions for the current api."""
248
return self.versions()
251
"""See VersionedFile.versions."""
228
252
return self._names[:]
230
def iter_names(self):
231
"""Yield a list of all names in this weave."""
232
return iter(self._names)
234
def idx_to_name(self, version):
235
return self._names[version]
254
def has_version(self, version_id):
255
"""See VersionedFile.has_version."""
256
return self._name_map.has_key(version_id)
258
__contains__ = has_version
260
@deprecated_method(zero_eight)
261
def parent_names(self, version):
262
"""Return version names for parents of a version.
264
See get_parents for the current api.
266
return self.get_parents(version)
268
def get_parents(self, version_id):
269
"""See VersionedFile.get_parent."""
270
return map(self._idx_to_name, self._parents[self._lookup(version_id)])
237
272
def _check_repeated_add(self, name, parents, text, sha1):
238
273
"""Check that a duplicated add is OK.
240
275
If it is, return the (old) index; otherwise raise an exception.
242
idx = self.lookup(name)
277
idx = self._lookup(name)
243
278
if sorted(self._parents[idx]) != sorted(parents) \
244
279
or sha1 != self._sha1s[idx]:
245
raise WeaveRevisionAlreadyPresent(name, self)
280
raise RevisionAlreadyPresent(name, self._weave_name)
283
@deprecated_method(zero_eight)
284
def add_identical(self, old_rev_id, new_rev_id, parents):
285
"""Please use Weave.clone_text now."""
286
return self.clone_text(new_rev_id, old_rev_id, parents, None)
288
def add_lines(self, version_id, parents, lines):
289
"""See VersionedFile.add_lines."""
290
return self._add(version_id, lines, map(self._lookup, parents))
292
@deprecated_method(zero_eight)
248
293
def add(self, name, parents, text, sha1=None):
294
"""See VersionedFile.add_lines for the non deprecated api."""
295
return self._add(name, text, map(self._maybe_lookup, parents), sha1)
297
def _add(self, version_id, lines, parents, sha1=None):
249
298
"""Add a single text on top of the weave.
251
300
Returns the index number of the newly added version.
254
303
Symbolic name for this version.
255
304
(Typically the revision-id of the revision that added it.)
258
307
List or set of direct parent version numbers.
261
310
Sequence of lines to be added in the new version.
263
sha -- SHA-1 of the file, if known. This is trusted to be
266
312
from bzrlib.osutils import sha_strings
268
assert isinstance(name, basestring)
270
sha1 = sha_strings(text)
271
if name in self._name_map:
272
return self._check_repeated_add(name, parents, text, sha1)
314
assert isinstance(version_id, basestring)
316
sha1 = sha_strings(lines)
317
if version_id in self._name_map:
318
return self._check_repeated_add(version_id, parents, lines, sha1)
274
parents = map(self.maybe_lookup, parents)
275
320
self._check_versions(parents)
276
## self._check_lines(text)
321
## self._check_lines(lines)
277
322
new_version = len(self._parents)
280
324
# if we abort after here the (in-memory) weave will be corrupt because only
281
325
# some fields are updated
282
326
self._parents.append(parents[:])
283
327
self._sha1s.append(sha1)
284
self._names.append(name)
285
self._name_map[name] = new_version
328
self._names.append(version_id)
329
self._name_map[version_id] = new_version
364
407
# we don't destroy ourselves
366
409
self._weave[i:i] = ([('{', new_version)]
369
412
offset += 2 + (j2 - j1)
371
413
return new_version
373
def add_identical(self, old_rev_id, new_rev_id, parents):
374
"""Add an identical text to old_rev_id as new_rev_id."""
375
old_lines = self.get(self.lookup(old_rev_id))
376
self.add(new_rev_id, parents, old_lines)
415
def clone_text(self, new_version_id, old_version_id, parents,
417
"""See VersionedFile.clone_text."""
418
old_lines = self.get_text(old_version_id)
419
self.add_lines(new_version_id, parents, old_lines)
378
def inclusions(self, versions):
421
def _inclusions(self, versions):
379
422
"""Return set of all ancestors of given version(s)."""
380
423
i = set(versions)
381
424
for v in xrange(max(versions), 0, -1):
386
429
## except IndexError:
387
430
## raise ValueError("version %d not present in weave" % v)
390
def parents(self, version):
391
return self._parents[version]
394
def parent_names(self, version):
395
"""Return version names for parents of a version."""
396
return map(self.idx_to_name, self._parents[self.lookup(version)])
399
def minimal_parents(self, version):
400
"""Find the minimal set of parents for the version."""
401
included = self._parents[version]
432
@deprecated_method(zero_eight)
433
def inclusions(self, version_ids):
434
"""Deprecated - see VersionedFile.get_ancestry for the replacement."""
406
li.sort(reverse=True)
414
gotit.update(self.inclusions(pv))
416
assert mininc[0] >= 0
417
assert mininc[-1] < version
437
if isinstance(version_ids[0], int):
438
return [self._idx_to_name(v) for v in self._inclusions(version_ids)]
440
return self.get_ancestry(version_ids)
442
def get_ancestry(self, version_ids):
443
"""See VersionedFile.get_ancestry."""
444
if isinstance(version_ids, basestring):
445
version_ids = [version_ids]
446
i = self._inclusions([self._lookup(v) for v in version_ids])
447
return [self._idx_to_name(v) for v in i]
422
449
def _check_lines(self, text):
423
450
if not isinstance(text, list):
438
465
except IndexError:
439
466
raise IndexError("invalid version number %r" % i)
468
def annotate(self, version_id):
469
if isinstance(version_id, int):
470
warn('Weave.annotate(int) is deprecated. Please use version names'
471
' in all circumstances as of 0.8',
476
for origin, lineno, text in self._extract([version_id]):
477
result.append((origin, text))
480
return super(Weave, self).annotate(version_id)
442
def annotate(self, name_or_index):
443
return list(self.annotate_iter(name_or_index))
446
def annotate_iter(self, name_or_index):
447
"""Yield list of (index-id, line) pairs for the specified version.
482
def annotate_iter(self, version_id):
483
"""Yield list of (version-id, line) pairs for the specified version.
449
485
The index indicates when the line originated in the weave."""
450
incls = [self.maybe_lookup(name_or_index)]
486
incls = [self._lookup(version_id)]
451
487
for origin, lineno, text in self._extract(incls):
488
yield self._idx_to_name(origin), text
490
@deprecated_method(zero_eight)
492
"""_walk has become walk, a supported api."""
458
(lineno, insert, deletes, text)
459
for each literal line.
495
def walk(self, version_ids=None):
496
"""See VersionedFile.walk."""
590
@deprecated_method(zero_eight)
555
591
def get_iter(self, name_or_index):
592
"""Deprecated, please do not use. Lookups are not not needed."""
593
return self._get_iter(self._maybe_lookup(name_or_index))
595
@deprecated_method(zero_eight)
596
def maybe_lookup(self, name_or_index):
597
"""Deprecated, please do not use. Lookups are not not needed."""
598
return self._maybe_lookup(name_or_index)
600
def _maybe_lookup(self, name_or_index):
601
"""Convert possible symbolic name to index, or pass through indexes.
605
if isinstance(name_or_index, (int, long)):
608
return self._lookup(name_or_index)
610
def _get_iter(self, version_id):
556
611
"""Yield lines for the specified version."""
557
incls = [self.maybe_lookup(name_or_index)]
612
incls = [self._maybe_lookup(version_id)]
558
613
if len(incls) == 1:
560
615
cur_sha = sha.new()
574
629
% (self._weave_name, self._names[index],
575
630
expected_sha1, measured_sha1))
578
def get_text(self, name_or_index):
579
return ''.join(self.get_iter(name_or_index))
580
assert isinstance(version, int)
583
def get_lines(self, name_or_index):
584
return list(self.get_iter(name_or_index))
632
@deprecated_method(zero_eight)
633
def get(self, version_id):
634
"""Please use either Weave.get_text or Weave.get_lines as desired."""
635
return self.get_lines(version_id)
637
def get_lines(self, version_id):
638
"""See VersionedFile.get_lines()."""
639
return list(self._get_iter(version_id))
590
641
def get_sha1(self, name):
591
642
"""Get the stored sha1 sum for the given revision.
593
644
:param name: The name of the version to lookup
595
return self._sha1s[self.lookup(name)]
597
def mash_iter(self, included):
598
"""Return composed version of multiple included versions."""
599
included = map(self.maybe_lookup, included)
600
for origin, lineno, text in self._extract(included):
604
def dump(self, to_file):
605
from pprint import pprint
606
print >>to_file, "Weave._weave = ",
607
pprint(self._weave, to_file)
608
print >>to_file, "Weave._parents = ",
609
pprint(self._parents, to_file)
646
return self._sha1s[self._lookup(name)]
613
648
def numversions(self):
614
649
l = len(self._parents)
615
650
assert l == len(self._sha1s)
620
return self.numversions()
653
__len__ = numversions
622
655
def check(self, progress_bar=None):
656
# TODO evaluate performance hit of using string sets in this routine.
623
657
# check no circular inclusions
624
658
for version in range(self.numversions()):
625
659
inclusions = list(self._parents[version])
652
690
update_text = 'checking %s' % (short_name,)
653
691
update_text = update_text[:25]
655
for lineno, insert, deleteset, line in self._walk():
693
for lineno, insert, deleteset, line in self.walk():
657
695
progress_bar.update(update_text, lineno, nlines)
659
for j, j_inc in enumerate(inclusions):
697
for name, name_inclusions in inclusions.items():
660
698
# The active inclusion must be an ancestor,
661
699
# and no ancestors must have deleted this line,
662
700
# because we don't support resurrection.
663
if (insert in j_inc) and not (deleteset & j_inc):
664
sha1s[j].update(line)
701
if (insert in name_inclusions) and not (deleteset & name_inclusions):
702
sha1s[name].update(line)
666
for version in range(nv):
705
version = self._idx_to_name(i)
667
706
hd = sha1s[version].hexdigest()
668
expected = self._sha1s[version]
707
expected = self._sha1s[i]
669
708
if hd != expected:
670
709
raise errors.WeaveInvalidChecksum(
671
710
"mismatched sha1 for version %s: "
672
711
"got %s, expected %s"
673
% (self._names[version], hd, expected))
712
% (version, hd, expected))
675
714
# TODO: check insertions are properly nested, that there are
676
715
# no lines outside of insertion blocks, that deletions are
677
716
# properly paired, etc.
679
def _delta(self, included, lines):
680
"""Return changes from basis to new revision.
682
The old text for comparison is the union of included revisions.
684
This is used in inserting a new text.
686
Delta is returned as a sequence of
687
(weave1, weave2, newlines).
689
This indicates that weave1:weave2 of the old weave should be
690
replaced by the sequence of lines in newlines. Note that
691
these line numbers are positions in the total weave and don't
692
correspond to the lines in any extracted version, or even the
693
extracted union of included versions.
695
If line1=line2, this is a pure insert; if newlines=[] this is a
696
pure delete. (Similar to difflib.)
698
raise NotImplementedError()
701
def plan_merge(self, ver_a, ver_b):
702
"""Return pseudo-annotation indicating how the two versions merge.
704
This is computed between versions a and b and their common
707
Weave lines present in none of them are skipped entirely.
709
inc_a = self.inclusions([ver_a])
710
inc_b = self.inclusions([ver_b])
711
inc_c = inc_a & inc_b
713
for lineno, insert, deleteset, line in self._walk():
714
if deleteset & inc_c:
715
# killed in parent; can't be in either a or b
716
# not relevant to our work
717
yield 'killed-base', line
718
elif insert in inc_c:
719
# was inserted in base
720
killed_a = bool(deleteset & inc_a)
721
killed_b = bool(deleteset & inc_b)
722
if killed_a and killed_b:
723
yield 'killed-both', line
725
yield 'killed-a', line
727
yield 'killed-b', line
729
yield 'unchanged', line
730
elif insert in inc_a:
731
if deleteset & inc_a:
732
yield 'ghost-a', line
736
elif insert in inc_b:
737
if deleteset & inc_b:
738
yield 'ghost-b', line
742
# not in either revision
743
yield 'irrelevant', line
745
yield 'unchanged', '' # terminator
749
def weave_merge(self, plan):
753
# TODO: Return a structured form of the conflicts (e.g. 2-tuples for
754
# conflicted regions), rather than just inserting the markers.
756
# TODO: Show some version information (e.g. author, date) on
757
# conflicted regions.
758
for state, line in plan:
759
if state == 'unchanged' or state == 'killed-both':
760
# resync and flush queued conflicts changes if any
761
if not lines_a and not lines_b:
763
elif ch_a and not ch_b:
765
for l in lines_a: yield l
766
elif ch_b and not ch_a:
767
for l in lines_b: yield l
768
elif lines_a == lines_b:
769
for l in lines_a: yield l
772
for l in lines_a: yield l
774
for l in lines_b: yield l
781
if state == 'unchanged':
784
elif state == 'killed-a':
787
elif state == 'killed-b':
790
elif state == 'new-a':
793
elif state == 'new-b':
797
assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
802
def join(self, other, pb=None, msg=None):
719
def join(self, other, pb=None, msg=None, version_ids=None):
804
721
"""Integrate versions from other into this weave.