~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Jelmer Vernooij
  • Date: 2009-02-23 20:55:58 UTC
  • mfrom: (4034 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4053.
  • Revision ID: jelmer@samba.org-20090223205558-1cx2k4w1zgs8r5qa
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
61
61
# where the basis and destination are unchanged.
62
62
 
63
63
# FIXME: Sometimes we will be given a parents list for a revision
64
 
# that includes some redundant parents (i.e. already a parent of 
65
 
# something in the list.)  We should eliminate them.  This can 
 
64
# that includes some redundant parents (i.e. already a parent of
 
65
# something in the list.)  We should eliminate them.  This can
66
66
# be done fairly efficiently because the sequence numbers constrain
67
67
# the possible relationships.
68
68
 
131
131
 
132
132
class Weave(VersionedFile):
133
133
    """weave - versioned text file storage.
134
 
    
 
134
 
135
135
    A Weave manages versions of line-based text files, keeping track
136
136
    of the originating version for each line.
137
137
 
183
183
 
184
184
    * It doesn't seem very useful to have an active insertion
185
185
      inside an inactive insertion, but it might happen.
186
 
      
 
186
 
187
187
    * Therefore, all instructions are always"considered"; that
188
188
      is passed onto and off the stack.  An outer inactive block
189
189
      doesn't disable an inner block.
259
259
 
260
260
    def copy(self):
261
261
        """Return a deep copy of self.
262
 
        
 
262
 
263
263
        The copy can be modified without affecting the original weave."""
264
264
        other = Weave()
265
265
        other._weave = self._weave[:]
275
275
            return False
276
276
        return self._parents == other._parents \
277
277
               and self._weave == other._weave \
278
 
               and self._sha1s == other._sha1s 
279
 
    
 
278
               and self._sha1s == other._sha1s
 
279
 
280
280
    def __ne__(self, other):
281
281
        return not self.__eq__(other)
282
282
 
349
349
    def insert_record_stream(self, stream):
350
350
        """Insert a record stream into this versioned file.
351
351
 
352
 
        :param stream: A stream of records to insert. 
 
352
        :param stream: A stream of records to insert.
353
353
        :return: None
354
354
        :seealso VersionedFile.get_record_stream:
355
355
        """
372
372
                    adapter_factory = adapter_registry.get(adapter_key)
373
373
                    adapter = adapter_factory(self)
374
374
                    adapters[adapter_key] = adapter
375
 
                lines = split_lines(adapter.get_bytes(
376
 
                    record, record.get_bytes_as(record.storage_kind)))
 
375
                lines = split_lines(adapter.get_bytes(record))
377
376
                try:
378
377
                    self.add_lines(record.key[0], parents, lines)
379
378
                except RevisionAlreadyPresent:
399
398
 
400
399
    def _add(self, version_id, lines, parents, sha1=None, nostore_sha=None):
401
400
        """Add a single text on top of the weave.
402
 
  
 
401
 
403
402
        Returns the index number of the newly added version.
404
403
 
405
404
        version_id
408
407
 
409
408
        parents
410
409
            List or set of direct parent version numbers.
411
 
            
 
410
 
412
411
        lines
413
412
            Sequence of lines to be added in the new version.
414
413
 
436
435
        self._names.append(version_id)
437
436
        self._name_map[version_id] = new_version
438
437
 
439
 
            
 
438
 
440
439
        if not parents:
441
440
            # special case; adding with no parents revision; can do
442
441
            # this more quickly by just appending unconditionally.
453
452
            if sha1 == self._sha1s[pv]:
454
453
                # special case: same as the single parent
455
454
                return new_version
456
 
            
 
455
 
457
456
 
458
457
        ancestors = self._inclusions(parents)
459
458
 
508
507
                # i2; we want to insert after this region to make sure
509
508
                # we don't destroy ourselves
510
509
                i = i2 + offset
511
 
                self._weave[i:i] = ([('{', new_version)] 
512
 
                                    + lines[j1:j2] 
 
510
                self._weave[i:i] = ([('{', new_version)]
 
511
                                    + lines[j1:j2]
513
512
                                    + [('}', None)])
514
513
                offset += 2 + (j2 - j1)
515
514
        return new_version
542
541
            if not isinstance(l, basestring):
543
542
                raise ValueError("text line should be a string or unicode, not %s"
544
543
                                 % type(l))
545
 
        
 
544
 
546
545
 
547
546
 
548
547
    def _check_versions(self, indexes):
556
555
    def _compatible_parents(self, my_parents, other_parents):
557
556
        """During join check that other_parents are joinable with my_parents.
558
557
 
559
 
        Joinable is defined as 'is a subset of' - supersets may require 
 
558
        Joinable is defined as 'is a subset of' - supersets may require
560
559
        regeneration of diffs, but subsets do not.
561
560
        """
562
561
        return len(other_parents.difference(my_parents)) == 0
587
586
 
588
587
    def _walk_internal(self, version_ids=None):
589
588
        """Helper method for weave actions."""
590
 
        
 
589
 
591
590
        istack = []
592
591
        dset = set()
593
592
 
674
673
        for i in versions:
675
674
            if not isinstance(i, int):
676
675
                raise ValueError(i)
677
 
            
 
676
 
678
677
        included = self._inclusions(versions)
679
678
 
680
679
        istack = []
689
688
 
690
689
        WFE = WeaveFormatError
691
690
 
692
 
        # wow. 
 
691
        # wow.
693
692
        #  449       0   4474.6820   2356.5590   bzrlib.weave:556(_extract)
694
693
        #  +285282   0   1676.8040   1676.8040   +<isinstance>
695
694
        # 1.6 seconds in 'isinstance'.
701
700
        # we're still spending ~1/4 of the method in isinstance though.
702
701
        # so lets hard code the acceptable string classes we expect:
703
702
        #  449       0   1202.9420    786.2930   bzrlib.weave:556(_extract)
704
 
        # +71352     0    377.5560    377.5560   +<method 'append' of 'list' 
 
703
        # +71352     0    377.5560    377.5560   +<method 'append' of 'list'
705
704
        #                                          objects>
706
705
        # yay, down to ~1/4 the initial extract time, and our inline time
707
706
        # has shrunk again, with isinstance no longer dominating.
708
707
        # tweaking the stack inclusion test to use a set gives:
709
708
        #  449       0   1122.8030    713.0080   bzrlib.weave:556(_extract)
710
 
        # +71352     0    354.9980    354.9980   +<method 'append' of 'list' 
 
709
        # +71352     0    354.9980    354.9980   +<method 'append' of 'list'
711
710
        #                                          objects>
712
711
        # - a 5% win, or possibly just noise. However with large istacks that
713
712
        # 'in' test could dominate, so I'm leaving this change in place -
714
713
        # when its fast enough to consider profiling big datasets we can review.
715
714
 
716
 
              
717
 
             
 
715
 
 
716
 
718
717
 
719
718
        for l in self._weave:
720
719
            if l.__class__ == tuple:
749
748
 
750
749
    def _maybe_lookup(self, name_or_index):
751
750
        """Convert possible symbolic name to index, or pass through indexes.
752
 
        
 
751
 
753
752
        NOT FOR PUBLIC USE.
754
753
        """
755
754
        if isinstance(name_or_index, (int, long)):
765
764
        measured_sha1 = sha_strings(result)
766
765
        if measured_sha1 != expected_sha1:
767
766
            raise errors.WeaveInvalidChecksum(
768
 
                    'file %s, revision %s, expected: %s, measured %s' 
 
767
                    'file %s, revision %s, expected: %s, measured %s'
769
768
                    % (self._weave_name, version_id,
770
769
                       expected_sha1, measured_sha1))
771
770
        return result
813
812
 
814
813
            if set(new_inc) != set(self.get_ancestry(name)):
815
814
                raise AssertionError(
816
 
                    'failed %s != %s' 
 
815
                    'failed %s != %s'
817
816
                    % (set(new_inc), set(self.get_ancestry(name))))
818
817
            inclusions[name] = new_inc
819
818
 
857
856
            parent_name = other._names[parent_idx]
858
857
            if parent_name not in self._name_map:
859
858
                # should not be possible
860
 
                raise WeaveError("missing parent {%s} of {%s} in %r" 
 
859
                raise WeaveError("missing parent {%s} of {%s} in %r"
861
860
                                 % (parent_name, other._name_map[other_idx], self))
862
861
            new_parents.append(self._name_map[parent_name])
863
862
        return new_parents
870
869
         * the same text
871
870
         * the same direct parents (by name, not index, and disregarding
872
871
           order)
873
 
        
 
872
 
874
873
        If present & correct return True;
875
 
        if not present in self return False; 
 
874
        if not present in self return False;
876
875
        if inconsistent raise error."""
877
876
        this_idx = self._name_map.get(name, -1)
878
877
        if this_idx != -1:
911
910
    """A WeaveFile represents a Weave on disk and writes on change."""
912
911
 
913
912
    WEAVE_SUFFIX = '.weave'
914
 
    
 
913
 
915
914
    def __init__(self, name, transport, filemode=None, create=False, access_mode='w', get_scope=None):
916
915
        """Create a WeaveFile.
917
 
        
 
916
 
918
917
        :param create: If not True, only open an existing knit.
919
918
        """
920
919
        super(WeaveFile, self).__init__(name, access_mode, get_scope=get_scope,
981
980
def _reweave(wa, wb, pb=None, msg=None):
982
981
    """Combine two weaves and return the result.
983
982
 
984
 
    This works even if a revision R has different parents in 
 
983
    This works even if a revision R has different parents in
985
984
    wa and wb.  In the resulting weave all the parents are given.
986
985
 
987
 
    This is done by just building up a new weave, maintaining ordering 
 
986
    This is done by just building up a new weave, maintaining ordering
988
987
    of the versions in the two inputs.  More efficient approaches
989
 
    might be possible but it should only be necessary to do 
990
 
    this operation rarely, when a new previously ghost version is 
 
988
    might be possible but it should only be necessary to do
 
989
    this operation rarely, when a new previously ghost version is
991
990
    inserted.
992
991
 
993
992
    :param pb: An optional progress bar, indicating how far done we are
1029
1028
 
1030
1029
def _reweave_parent_graphs(wa, wb):
1031
1030
    """Return combined parent ancestry for two weaves.
1032
 
    
 
1031
 
1033
1032
    Returned as a list of (version_name, set(parent_names))"""
1034
1033
    combined = {}
1035
1034
    for weave in [wa, wb]:
1100
1099
        Display origin of each line.
1101
1100
    weave merge WEAVEFILE VERSION1 VERSION2 > OUT
1102
1101
        Auto-merge two versions and display conflicts.
1103
 
    weave diff WEAVEFILE VERSION1 VERSION2 
 
1102
    weave diff WEAVEFILE VERSION1 VERSION2
1104
1103
        Show differences between two versions.
1105
1104
 
1106
1105
example:
1123
1122
 
1124
1123
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
1125
1124
    % vi foo.txt                            (resolve conflicts)
1126
 
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
1127
 
    
 
1125
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)
 
1126
 
1128
1127
"""
1129
 
    
 
1128
 
1130
1129
 
1131
1130
 
1132
1131
def main(argv):
1155
1154
 
1156
1155
    def readit():
1157
1156
        return read_weave(file(argv[2], 'rb'))
1158
 
    
 
1157
 
1159
1158
    if cmd == 'help':
1160
1159
        usage()
1161
1160
    elif cmd == 'add':
1176
1175
    elif cmd == 'get': # get one version
1177
1176
        w = readit()
1178
1177
        sys.stdout.writelines(w.get_iter(int(argv[3])))
1179
 
        
 
1178
 
1180
1179
    elif cmd == 'diff':
1181
1180
        w = readit()
1182
1181
        fn = argv[2]
1187
1186
                                '%s version %d' % (fn, v1),
1188
1187
                                '%s version %d' % (fn, v2))
1189
1188
        sys.stdout.writelines(diff_gen)
1190
 
            
 
1189
 
1191
1190
    elif cmd == 'annotate':
1192
1191
        w = readit()
1193
1192
        # newline is added to all lines regardless; too hard to get
1200
1199
            else:
1201
1200
                print '%5d | %s' % (origin, text)
1202
1201
                lasto = origin
1203
 
                
 
1202
 
1204
1203
    elif cmd == 'toc':
1205
1204
        weave_toc(readit())
1206
1205
 
1207
1206
    elif cmd == 'stats':
1208
1207
        weave_stats(argv[2], ProgressBar())
1209
 
        
 
1208
 
1210
1209
    elif cmd == 'check':
1211
1210
        w = readit()
1212
1211
        pb = ProgressBar()
1235
1234
        sys.stdout.writelines(w.weave_merge(p))
1236
1235
    else:
1237
1236
        raise ValueError('unknown command %r' % cmd)
1238
 
    
 
1237
 
1239
1238
 
1240
1239
if __name__ == '__main__':
1241
1240
    import sys