201
212
return self._parents == other._parents \
202
213
and self._weave == other._weave \
203
214
and self._sha1s == other._sha1s
206
216
def __ne__(self, other):
207
217
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)
219
@deprecated_method(zero_eight)
220
def idx_to_name(self, index):
221
"""Old public interface, the public interface is all names now."""
224
def _idx_to_name(self, version):
225
return self._names[version]
227
@deprecated_method(zero_eight)
220
228
def lookup(self, name):
229
"""Backwards compatability thunk:
231
Return name, as name is valid in the api now, and spew deprecation
236
def _lookup(self, name):
221
237
"""Convert symbolic version name to index."""
223
239
return self._name_map[name]
225
raise WeaveRevisionNotPresent(name, self)
241
raise RevisionNotPresent(name, self._weave_name)
243
@deprecated_method(zero_eight)
244
def iter_names(self):
245
"""Deprecated convenience function, please see VersionedFile.names()."""
246
return iter(self.names())
248
@deprecated_method(zero_eight)
250
"""See Weave.versions for the current api."""
251
return self.versions()
254
"""See VersionedFile.versions."""
228
255
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]
257
def has_version(self, version_id):
258
"""See VersionedFile.has_version."""
259
return self._name_map.has_key(version_id)
261
__contains__ = has_version
263
def get_parents(self, version_id):
264
"""See VersionedFile.get_parent."""
265
return map(self._idx_to_name, self._parents[self._lookup(version_id)])
237
267
def _check_repeated_add(self, name, parents, text, sha1):
238
268
"""Check that a duplicated add is OK.
240
270
If it is, return the (old) index; otherwise raise an exception.
242
idx = self.lookup(name)
272
idx = self._lookup(name)
243
273
if sorted(self._parents[idx]) != sorted(parents) \
244
274
or sha1 != self._sha1s[idx]:
245
raise WeaveRevisionAlreadyPresent(name, self)
275
raise RevisionAlreadyPresent(name, self._weave_name)
278
@deprecated_method(zero_eight)
279
def add_identical(self, old_rev_id, new_rev_id, parents):
280
"""Please use Weave.clone_text now."""
281
return self.clone_text(new_rev_id, old_rev_id, parents)
283
def _add_lines(self, version_id, parents, lines):
284
"""See VersionedFile.add_lines."""
285
return self._add(version_id, lines, map(self._lookup, parents))
287
@deprecated_method(zero_eight)
248
288
def add(self, name, parents, text, sha1=None):
289
"""See VersionedFile.add_lines for the non deprecated api."""
290
return self._add(name, text, map(self._maybe_lookup, parents), sha1)
292
def _add(self, version_id, lines, parents, sha1=None):
249
293
"""Add a single text on top of the weave.
251
295
Returns the index number of the newly added version.
254
298
Symbolic name for this version.
255
299
(Typically the revision-id of the revision that added it.)
258
302
List or set of direct parent version numbers.
261
305
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
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)
274
parents = map(self.maybe_lookup, parents)
308
assert isinstance(version_id, basestring)
310
sha1 = sha_strings(lines)
311
if version_id in self._name_map:
312
return self._check_repeated_add(version_id, parents, lines, sha1)
275
314
self._check_versions(parents)
276
## self._check_lines(text)
315
## self._check_lines(lines)
277
316
new_version = len(self._parents)
280
318
# if we abort after here the (in-memory) weave will be corrupt because only
281
319
# some fields are updated
282
320
# XXX: FIXME implement a succeed-or-fail of the rest of this routine.
283
321
# - Robert Collins 20060226
284
322
self._parents.append(parents[:])
285
323
self._sha1s.append(sha1)
286
self._names.append(name)
287
self._name_map[name] = new_version
324
self._names.append(version_id)
325
self._name_map[version_id] = new_version
388
426
## except IndexError:
389
427
## raise ValueError("version %d not present in weave" % v)
392
def parents(self, version):
393
return self._parents[version]
396
def parent_names(self, version):
397
"""Return version names for parents of a version."""
398
return map(self.idx_to_name, self._parents[self.lookup(version)])
401
def minimal_parents(self, version):
402
"""Find the minimal set of parents for the version."""
403
included = self._parents[version]
429
@deprecated_method(zero_eight)
430
def inclusions(self, version_ids):
431
"""Deprecated - see VersionedFile.get_ancestry for the replacement."""
408
li.sort(reverse=True)
416
gotit.update(self.inclusions(pv))
418
assert mininc[0] >= 0
419
assert mininc[-1] < version
434
if isinstance(version_ids[0], int):
435
return [self._idx_to_name(v) for v in self._inclusions(version_ids)]
437
return self.get_ancestry(version_ids)
439
def get_ancestry(self, version_ids):
440
"""See VersionedFile.get_ancestry."""
441
if isinstance(version_ids, basestring):
442
version_ids = [version_ids]
443
i = self._inclusions([self._lookup(v) for v in version_ids])
444
return [self._idx_to_name(v) for v in i]
424
446
def _check_lines(self, text):
425
447
if not isinstance(text, list):
440
462
except IndexError:
441
463
raise IndexError("invalid version number %r" % i)
465
def _compatible_parents(self, my_parents, other_parents):
466
"""During join check that other_parents are joinable with my_parents.
468
Joinable is defined as 'is a subset of' - supersets may require
469
regeneration of diffs, but subsets do not.
471
return len(other_parents.difference(my_parents)) == 0
473
def annotate(self, version_id):
474
if isinstance(version_id, int):
475
warn('Weave.annotate(int) is deprecated. Please use version names'
476
' in all circumstances as of 0.8',
481
for origin, lineno, text in self._extract([version_id]):
482
result.append((origin, text))
485
return super(Weave, self).annotate(version_id)
444
def annotate(self, name_or_index):
445
return list(self.annotate_iter(name_or_index))
448
def annotate_iter(self, name_or_index):
449
"""Yield list of (index-id, line) pairs for the specified version.
487
def annotate_iter(self, version_id):
488
"""Yield list of (version-id, line) pairs for the specified version.
451
490
The index indicates when the line originated in the weave."""
452
incls = [self.maybe_lookup(name_or_index)]
491
incls = [self._lookup(version_id)]
453
492
for origin, lineno, text in self._extract(incls):
493
yield self._idx_to_name(origin), text
495
@deprecated_method(zero_eight)
460
(lineno, insert, deletes, text)
461
for each literal line.
497
"""_walk has become visit, a supported api."""
498
return self._walk_internal()
500
def iter_lines_added_or_present_in_versions(self, version_ids=None):
501
"""See VersionedFile.iter_lines_added_or_present_in_versions()."""
502
if version_ids is None:
503
version_ids = self.versions()
504
version_ids = set(version_ids)
505
for lineno, inserted, deletes, line in self._walk_internal(version_ids):
506
# if inserted not in version_ids then it was inserted before the
507
# versions we care about, but because weaves cannot represent ghosts
508
# properly, we do not filter down to that
509
# if inserted not in version_ids: continue
515
#@deprecated_method(zero_eight)
516
def walk(self, version_ids=None):
517
"""See VersionedFile.walk."""
518
return self._walk_internal(version_ids)
520
def _walk_internal(self, version_ids=None):
521
"""Helper method for weave actions."""
576
657
% (self._weave_name, self._names[index],
577
658
expected_sha1, measured_sha1))
580
def get_text(self, name_or_index):
581
return ''.join(self.get_iter(name_or_index))
582
assert isinstance(version, int)
585
def get_lines(self, name_or_index):
586
return list(self.get_iter(name_or_index))
660
@deprecated_method(zero_eight)
661
def get(self, version_id):
662
"""Please use either Weave.get_text or Weave.get_lines as desired."""
663
return self.get_lines(version_id)
665
def get_lines(self, version_id):
666
"""See VersionedFile.get_lines()."""
667
return list(self._get_iter(version_id))
592
669
def get_sha1(self, name):
593
670
"""Get the stored sha1 sum for the given revision.
595
672
:param name: The name of the version to lookup
597
return self._sha1s[self.lookup(name)]
599
def mash_iter(self, included):
600
"""Return composed version of multiple included versions."""
601
included = map(self.maybe_lookup, included)
602
for origin, lineno, text in self._extract(included):
606
def dump(self, to_file):
607
from pprint import pprint
608
print >>to_file, "Weave._weave = ",
609
pprint(self._weave, to_file)
610
print >>to_file, "Weave._parents = ",
611
pprint(self._parents, to_file)
674
return self._sha1s[self._lookup(name)]
676
@deprecated_method(zero_eight)
615
677
def numversions(self):
678
"""How many versions are in this weave?
680
Deprecated in favour of num_versions.
682
return self.num_versions()
684
def num_versions(self):
685
"""How many versions are in this weave?"""
616
686
l = len(self._parents)
617
687
assert l == len(self._sha1s)
622
return self.numversions()
690
__len__ = num_versions
624
692
def check(self, progress_bar=None):
693
# TODO evaluate performance hit of using string sets in this routine.
625
694
# check no circular inclusions
626
for version in range(self.numversions()):
695
for version in range(self.num_versions()):
627
696
inclusions = list(self._parents[version])
629
698
inclusions.sort()
654
727
update_text = 'checking %s' % (short_name,)
655
728
update_text = update_text[:25]
657
for lineno, insert, deleteset, line in self._walk():
730
for lineno, insert, deleteset, line in self._walk_internal():
659
732
progress_bar.update(update_text, lineno, nlines)
661
for j, j_inc in enumerate(inclusions):
734
for name, name_inclusions in inclusions.items():
662
735
# The active inclusion must be an ancestor,
663
736
# and no ancestors must have deleted this line,
664
737
# because we don't support resurrection.
665
if (insert in j_inc) and not (deleteset & j_inc):
666
sha1s[j].update(line)
738
if (insert in name_inclusions) and not (deleteset & name_inclusions):
739
sha1s[name].update(line)
668
for version in range(nv):
742
version = self._idx_to_name(i)
669
743
hd = sha1s[version].hexdigest()
670
expected = self._sha1s[version]
744
expected = self._sha1s[i]
671
745
if hd != expected:
672
746
raise errors.WeaveInvalidChecksum(
673
747
"mismatched sha1 for version %s: "
674
748
"got %s, expected %s"
675
% (self._names[version], hd, expected))
749
% (version, hd, expected))
677
751
# TODO: check insertions are properly nested, that there are
678
752
# no lines outside of insertion blocks, that deletions are
679
753
# properly paired, etc.
681
def _delta(self, included, lines):
682
"""Return changes from basis to new revision.
684
The old text for comparison is the union of included revisions.
686
This is used in inserting a new text.
688
Delta is returned as a sequence of
689
(weave1, weave2, newlines).
691
This indicates that weave1:weave2 of the old weave should be
692
replaced by the sequence of lines in newlines. Note that
693
these line numbers are positions in the total weave and don't
694
correspond to the lines in any extracted version, or even the
695
extracted union of included versions.
697
If line1=line2, this is a pure insert; if newlines=[] this is a
698
pure delete. (Similar to difflib.)
700
raise NotImplementedError()
703
def plan_merge(self, ver_a, ver_b):
704
"""Return pseudo-annotation indicating how the two versions merge.
706
This is computed between versions a and b and their common
709
Weave lines present in none of them are skipped entirely.
711
inc_a = self.inclusions([ver_a])
712
inc_b = self.inclusions([ver_b])
713
inc_c = inc_a & inc_b
715
for lineno, insert, deleteset, line in self._walk():
716
if deleteset & inc_c:
717
# killed in parent; can't be in either a or b
718
# not relevant to our work
719
yield 'killed-base', line
720
elif insert in inc_c:
721
# was inserted in base
722
killed_a = bool(deleteset & inc_a)
723
killed_b = bool(deleteset & inc_b)
724
if killed_a and killed_b:
725
yield 'killed-both', line
727
yield 'killed-a', line
729
yield 'killed-b', line
731
yield 'unchanged', line
732
elif insert in inc_a:
733
if deleteset & inc_a:
734
yield 'ghost-a', line
738
elif insert in inc_b:
739
if deleteset & inc_b:
740
yield 'ghost-b', line
744
# not in either revision
745
yield 'irrelevant', line
747
yield 'unchanged', '' # terminator
751
def weave_merge(self, plan, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n'):
755
# TODO: Return a structured form of the conflicts (e.g. 2-tuples for
756
# conflicted regions), rather than just inserting the markers.
758
# TODO: Show some version information (e.g. author, date) on
759
# conflicted regions.
760
for state, line in plan:
761
if state == 'unchanged' or state == 'killed-both':
762
# resync and flush queued conflicts changes if any
763
if not lines_a and not lines_b:
765
elif ch_a and not ch_b:
767
for l in lines_a: yield l
768
elif ch_b and not ch_a:
769
for l in lines_b: yield l
770
elif lines_a == lines_b:
771
for l in lines_a: yield l
774
for l in lines_a: yield l
776
for l in lines_b: yield l
783
if state == 'unchanged':
786
elif state == 'killed-a':
789
elif state == 'killed-b':
792
elif state == 'new-a':
795
elif state == 'new-b':
799
assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
804
def join(self, other, pb=None, msg=None):
806
"""Integrate versions from other into this weave.
808
The resulting weave contains all the history of both weaves;
809
any version you could retrieve from either self or other can be
810
retrieved from self after this call.
812
It is illegal for the two weaves to contain different values
813
or different parents for any version. See also reweave().
815
:param other: The other weave to pull into this one
816
:param pb: An optional progress bar
817
:param msg: An optional message to display for progress
819
if other.numversions() == 0:
755
def _join(self, other, pb, msg, version_ids, ignore_missing):
756
"""Worker routine for join()."""
757
if not other.versions():
820
758
return # nothing to update, easy
761
for version_id in version_ids:
762
if not other.has_version(version_id) and not ignore_missing:
763
raise RevisionNotPresent(version_id, self._weave_name)
765
version_ids = other.versions()
821
767
# two loops so that we do not change ourselves before verifying it
823
769
# work through in index order to make sure we get all dependencies
824
770
names_to_join = []
826
for other_idx, name in enumerate(other._names):
827
self._check_version_consistent(other, other_idx, name)
828
sha1 = other._sha1s[other_idx]
772
# get the selected versions only that are in other.versions.
773
version_ids = set(other.versions()).intersection(set(version_ids))
774
# pull in the referenced graph.
775
version_ids = other.get_ancestry(version_ids)
776
pending_graph = [(version, other.get_parents(version)) for
777
version in version_ids]
778
for name in topo_sort(pending_graph):
779
other_idx = other._name_map[name]
780
# returns True if we have it, False if we need it.
781
if not self._check_version_consistent(other, other_idx, name):
782
names_to_join.append((other_idx, name))
832
if name in self._name_map:
833
idx = self.lookup(name)
834
n1 = set(map(other.idx_to_name, other._parents[other_idx]))
835
n2 = set(map(self.idx_to_name, self._parents[idx]))
836
if sha1 == self._sha1s[idx] and n1 == n2:
839
names_to_join.append((other_idx, name))
841
786
if pb and not msg:
842
787
msg = 'weave join'
846
791
for other_idx, name in names_to_join:
847
792
# TODO: If all the parents of the other version are already
848
793
# present then we can avoid some work by just taking the delta
849
@deprecated_method(zero_eight)
906
850
def reweave(self, other, pb=None, msg=None):
907
"""Reweave self with other.
851
"""reweave has been superceded by plain use of join."""
852
return self.join(other, pb, msg)
854
def _reweave(self, other, pb, msg):
855
"""Reweave self with other - internal helper for join().
909
857
:param other: The other weave to merge
910
858
:param pb: An optional progress bar, indicating how far done we are
911
859
:param msg: An optional message for the progress
913
new_weave = reweave(self, other, pb=pb, msg=msg)
861
new_weave = _reweave(self, other, pb=pb, msg=msg)
862
self._copy_weave_content(new_weave)
864
def _copy_weave_content(self, otherweave):
865
"""adsorb the content from otherweave."""
914
866
for attr in self.__slots__:
915
setattr(self, attr, getattr(new_weave, attr))
867
if attr != '_weave_name':
868
setattr(self, attr, copy(getattr(otherweave, attr)))
871
class WeaveFile(Weave):
872
"""A WeaveFile represents a Weave on disk and writes on change."""
874
WEAVE_SUFFIX = '.weave'
876
def __init__(self, name, transport, filemode=None, create=False, access_mode='w'):
877
"""Create a WeaveFile.
879
:param create: If not True, only open an existing knit.
881
super(WeaveFile, self).__init__(name, access_mode)
882
self._transport = transport
883
self._filemode = filemode
885
_read_weave_v5(self._transport.get(name + WeaveFile.WEAVE_SUFFIX), self)
886
except errors.NoSuchFile:
892
def _add_lines(self, version_id, parents, lines):
893
"""Add a version and save the weave."""
894
super(WeaveFile, self)._add_lines(version_id, parents, lines)
897
def _clone_text(self, new_version_id, old_version_id, parents):
898
"""See VersionedFile.clone_text."""
899
super(WeaveFile, self)._clone_text(new_version_id, old_version_id, parents)
902
def copy_to(self, name, transport):
903
"""See VersionedFile.copy_to()."""
904
# as we are all in memory always, just serialise to the new place.
906
write_weave_v5(self, sio)
908
transport.put(name + WeaveFile.WEAVE_SUFFIX, sio, self._filemode)
910
def create_empty(self, name, transport, filemode=None):
911
return WeaveFile(name, transport, filemode, create=True)
914
"""Save the weave."""
915
self._check_write_ok()
917
write_weave_v5(self, sio)
919
self._transport.put(self._weave_name + WeaveFile.WEAVE_SUFFIX,
925
"""See VersionedFile.get_suffixes()."""
926
return [WeaveFile.WEAVE_SUFFIX]
928
def join(self, other, pb=None, msg=None, version_ids=None,
929
ignore_missing=False):
930
"""Join other into self and save."""
931
super(WeaveFile, self).join(other, pb, msg, version_ids, ignore_missing)
935
@deprecated_function(zero_eight)
918
936
def reweave(wa, wb, pb=None, msg=None):
937
"""reweaving is deprecation, please just use weave.join()."""
938
_reweave(wa, wb, pb, msg)
940
def _reweave(wa, wb, pb=None, msg=None):
919
941
"""Combine two weaves and return the result.
921
943
This works even if a revision R has different parents in