~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Martin Pool
  • Date: 2005-08-30 06:10:39 UTC
  • Revision ID: mbp@sourcefrog.net-20050830061039-1d0347fb236c39ad
- clean up some code in revision.py

- move all exceptions to bzrlib.errors

Show diffs side-by-side

added added

removed removed

Lines of Context:
84
84
# TODO: Perhaps the API should work only in names to hide the integer
85
85
# indexes from the user?
86
86
 
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
89
 
# version?
90
 
 
91
87
 
92
88
 
93
89
import sha
94
 
from difflib import SequenceMatcher
95
 
 
96
90
 
97
91
 
98
92
 
119
113
 
120
114
    * a nonnegative index number.
121
115
 
122
 
    * a version-id string.
 
116
    * a version-id string. (not implemented yet)
123
117
 
124
118
    Typically the index number will be valid only inside this weave and
125
119
    the version-id is used to reference it in the larger world.
187
181
 
188
182
    _name_map
189
183
        For each name, the version number.
190
 
 
191
 
    _weave_name
192
 
        Descriptive name of this weave; typically the filename if known.
193
 
        Set by read_weave.
194
184
    """
195
185
 
196
 
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
197
 
                 '_weave_name']
 
186
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
198
187
    
199
 
    def __init__(self, weave_name=None):
 
188
    def __init__(self):
200
189
        self._weave = []
201
190
        self._parents = []
202
191
        self._sha1s = []
203
192
        self._names = []
204
193
        self._name_map = {}
205
 
        self._weave_name = weave_name
206
194
 
207
195
 
208
196
    def __eq__(self, other):
217
205
        return not self.__eq__(other)
218
206
 
219
207
 
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)):
223
 
            return name_or_index
224
 
        else:
225
 
            return self.lookup(name_or_index)
226
 
 
227
 
        
228
208
    def lookup(self, name):
229
 
        """Convert symbolic version name to index."""
230
209
        try:
231
210
            return self._name_map[name]
232
211
        except KeyError:
233
 
            raise WeaveError("name %r not present in weave %r" %
234
 
                             (name, self._weave_name))
235
 
 
236
 
 
237
 
    def idx_to_name(self, version):
238
 
        return self._names[version]
239
 
 
240
 
 
241
 
    def _check_repeated_add(self, name, parents, text, sha1):
242
 
        """Check that a duplicated add is OK.
243
 
 
244
 
        If it is, return the (old) index; otherwise raise an exception.
245
 
        """
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)            
253
 
        return idx
254
 
        
255
 
 
256
 
        
257
 
    def add(self, name, parents, text, sha1=None):
 
212
            raise WeaveError("name %s not present in weave" % name)
 
213
 
 
214
        
 
215
    def add(self, name, parents, text):
258
216
        """Add a single text on top of the weave.
259
217
  
260
218
        Returns the index number of the newly added version.
267
225
            List or set of direct parent version numbers.
268
226
            
269
227
        text
270
 
            Sequence of lines to be added in the new version.
271
 
 
272
 
        sha -- SHA-1 of the file, if known.  This is trusted to be
273
 
            correct if supplied.
274
 
        """
275
 
        from bzrlib.osutils import sha_strings
 
228
            Sequence of lines to be added in the new version."""
276
229
 
277
230
        assert isinstance(name, basestring)
278
 
        if sha1 is None:
279
 
            sha1 = sha_strings(text)
280
231
        if name in self._name_map:
281
 
            return self._check_repeated_add(name, parents, text, sha1)
282
 
 
283
 
        parents = map(self.maybe_lookup, parents)
 
232
            raise WeaveError("name %r already present in weave" % name)
 
233
        
284
234
        self._check_versions(parents)
285
235
        ## self._check_lines(text)
286
236
        new_version = len(self._parents)
287
237
 
 
238
        s = sha.new()
 
239
        map(s.update, text)
 
240
        sha1 = s.hexdigest()
 
241
        del s
288
242
 
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
341
295
 
 
296
        from difflib import SequenceMatcher
342
297
        s = SequenceMatcher(None, basis_lines, text)
343
298
 
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):
387
 
            if v in i:
388
 
                # include all its parents
389
 
                i.update(self._parents[v])
390
 
        return i
391
 
        ## except IndexError:
392
 
        ##     raise ValueError("version %d not present in weave" % v)
393
 
 
394
 
 
395
 
    def parents(self, version):
396
 
        return self._parents[version]
 
341
        v = max(versions)
 
342
        try:
 
343
            while v >= 0:
 
344
                if v in i:
 
345
                    # include all its parents
 
346
                    i.update(self._parents[v])
 
347
                v -= 1
 
348
            return i
 
349
        except IndexError:
 
350
            raise ValueError("version %d not present in weave" % v)
397
351
 
398
352
 
399
353
    def minimal_parents(self, version):
439
393
                raise IndexError("invalid version number %r" % i)
440
394
 
441
395
    
442
 
    def annotate(self, name_or_index):
443
 
        return list(self.annotate_iter(name_or_index))
444
 
 
445
 
 
446
 
    def annotate_iter(self, name_or_index):
 
396
    def annotate(self, index):
 
397
        return list(self.annotate_iter(index))
 
398
 
 
399
 
 
400
    def annotate_iter(self, version):
447
401
        """Yield list of (index-id, line) pairs for the specified version.
448
402
 
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
453
406
 
454
407
 
498
451
 
499
452
        The set typically but not necessarily corresponds to a version.
500
453
        """
501
 
        for i in versions:
502
 
            if not isinstance(i, int):
503
 
                raise ValueError(i)
504
 
            
505
454
        included = self.inclusions(versions)
506
455
 
507
456
        istack = []
552
501
    
553
502
 
554
503
 
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]):
559
507
            yield line
560
508
 
561
509
 
562
 
    def get_text(self, name_or_index):
563
 
        return ''.join(self.get_iter(name_or_index))
564
 
        assert isinstance(version, int)
565
 
 
566
 
 
567
 
    def get_lines(self, name_or_index):
568
 
        return list(self.get_iter(name_or_index))
569
 
 
570
 
 
571
 
    get = get_lines
 
510
    def get(self, index):
 
511
        return list(self.get_iter(index))
572
512
 
573
513
 
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):
578
517
            yield text
579
518
 
784
723
 
785
724
 
786
725
 
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
789
729
 
 
730
    pb = ProgressBar()
 
731
 
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.
838
778
 
839
779
example:
840
780
 
865
805
def main(argv):
866
806
    import sys
867
807
    import os
868
 
    try:
869
 
        import bzrlib
870
 
    except ImportError:
871
 
        # in case we're run directly from the subdirectory
872
 
        sys.path.append('..')
873
 
        import bzrlib
874
 
    from bzrlib.weavefile import write_weave, read_weave
 
808
    from weavefile import write_weave, read_weave
875
809
    from bzrlib.progress import ProgressBar
876
810
 
877
811
    try:
914
848
        w = readit()
915
849
        sys.stdout.writelines(w.mash_iter(map(int, argv[3:])))
916
850
 
917
 
    elif cmd == 'diff':
918
 
        from difflib import unified_diff
919
 
        w = readit()
920
 
        fn = argv[2]
921
 
        v1, v2 = map(int, argv[3:5])
922
 
        lines1 = w.get(v1)
923
 
        lines2 = w.get(v2)
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)
928
 
            
929
851
    elif cmd == 'annotate':
930
852
        w = readit()
931
853
        # newline is added to all lines regardless; too hard to get
943
865
        weave_toc(readit())
944
866
 
945
867
    elif cmd == 'stats':
946
 
        weave_stats(argv[2], ProgressBar())
 
868
        weave_stats(argv[2])
947
869
        
948
870
    elif cmd == 'check':
949
871
        w = readit()