~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Aaron Bentley
  • Date: 2005-09-29 21:07:17 UTC
  • mfrom: (1393.1.6)
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1419.
  • Revision ID: abentley@panoramicfeedback.com-20050929210717-cd73981590f17017
Merged the weave changes

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
 
87
91
 
88
92
 
89
93
import sha
 
94
from difflib import SequenceMatcher
 
95
 
90
96
 
91
97
 
92
98
 
113
119
 
114
120
    * a nonnegative index number.
115
121
 
116
 
    * a version-id string. (not implemented yet)
 
122
    * a version-id string.
117
123
 
118
124
    Typically the index number will be valid only inside this weave and
119
125
    the version-id is used to reference it in the larger world.
181
187
 
182
188
    _name_map
183
189
        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.
184
194
    """
185
195
 
186
 
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map']
 
196
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
 
197
                 '_weave_name']
187
198
    
188
 
    def __init__(self):
 
199
    def __init__(self, weave_name=None):
189
200
        self._weave = []
190
201
        self._parents = []
191
202
        self._sha1s = []
192
203
        self._names = []
193
204
        self._name_map = {}
 
205
        self._weave_name = weave_name
194
206
 
195
207
 
196
208
    def __eq__(self, other):
205
217
        return not self.__eq__(other)
206
218
 
207
219
 
 
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
        
208
228
    def lookup(self, name):
 
229
        """Convert symbolic version name to index."""
209
230
        try:
210
231
            return self._name_map[name]
211
232
        except KeyError:
212
 
            raise WeaveError("name %s not present in weave" % name)
213
 
 
214
 
        
215
 
    def add(self, name, parents, text):
 
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):
216
258
        """Add a single text on top of the weave.
217
259
  
218
260
        Returns the index number of the newly added version.
225
267
            List or set of direct parent version numbers.
226
268
            
227
269
        text
228
 
            Sequence of lines to be added in the new version."""
 
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
229
276
 
230
277
        assert isinstance(name, basestring)
 
278
        if sha1 is None:
 
279
            sha1 = sha_strings(text)
231
280
        if name in self._name_map:
232
 
            raise WeaveError("name %r already present in weave" % name)
233
 
        
 
281
            return self._check_repeated_add(name, parents, text, sha1)
 
282
 
 
283
        parents = map(self.maybe_lookup, parents)
234
284
        self._check_versions(parents)
235
285
        ## self._check_lines(text)
236
286
        new_version = len(self._parents)
237
287
 
238
 
        s = sha.new()
239
 
        map(s.update, text)
240
 
        sha1 = s.hexdigest()
241
 
        del s
242
288
 
243
289
        # if we abort after here the (in-memory) weave will be corrupt because only
244
290
        # some fields are updated
293
339
        #print 'basis_lines:', basis_lines
294
340
        #print 'new_lines:  ', lines
295
341
 
296
 
        from difflib import SequenceMatcher
297
342
        s = SequenceMatcher(None, basis_lines, text)
298
343
 
299
344
        # offset gives the number of lines that have been inserted
338
383
    def inclusions(self, versions):
339
384
        """Return set of all ancestors of given version(s)."""
340
385
        i = set(versions)
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)
 
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]
351
397
 
352
398
 
353
399
    def minimal_parents(self, version):
393
439
                raise IndexError("invalid version number %r" % i)
394
440
 
395
441
    
396
 
    def annotate(self, index):
397
 
        return list(self.annotate_iter(index))
398
 
 
399
 
 
400
 
    def annotate_iter(self, version):
 
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):
401
447
        """Yield list of (index-id, line) pairs for the specified version.
402
448
 
403
449
        The index indicates when the line originated in the weave."""
404
 
        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):
405
452
            yield origin, text
406
453
 
407
454
 
451
498
 
452
499
        The set typically but not necessarily corresponds to a version.
453
500
        """
 
501
        for i in versions:
 
502
            if not isinstance(i, int):
 
503
                raise ValueError(i)
 
504
            
454
505
        included = self.inclusions(versions)
455
506
 
456
507
        istack = []
501
552
    
502
553
 
503
554
 
504
 
    def get_iter(self, version):
 
555
    def get_iter(self, name_or_index):
505
556
        """Yield lines for the specified version."""
506
 
        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):
507
559
            yield line
508
560
 
509
561
 
510
 
    def get(self, index):
511
 
        return list(self.get_iter(index))
 
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
512
572
 
513
573
 
514
574
    def mash_iter(self, included):
515
575
        """Return composed version of multiple included versions."""
 
576
        included = map(self.maybe_lookup, included)
516
577
        for origin, lineno, text in self._extract(included):
517
578
            yield text
518
579
 
723
784
 
724
785
 
725
786
 
726
 
def weave_stats(weave_file):
727
 
    from bzrlib.progress import ProgressBar
 
787
def weave_stats(weave_file, pb):
728
788
    from bzrlib.weavefile import read_weave
729
789
 
730
 
    pb = ProgressBar()
731
 
 
732
790
    wf = file(weave_file, 'rb')
733
791
    w = read_weave(wf)
734
792
    # FIXME: doesn't work on pipes
775
833
        Display composite of all selected versions.
776
834
    weave merge WEAVEFILE VERSION1 VERSION2 > OUT
777
835
        Auto-merge two versions and display conflicts.
 
836
    weave diff WEAVEFILE VERSION1 VERSION2 
 
837
        Show differences between two versions.
778
838
 
779
839
example:
780
840
 
805
865
def main(argv):
806
866
    import sys
807
867
    import os
808
 
    from weavefile import write_weave, read_weave
 
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
809
875
    from bzrlib.progress import ProgressBar
810
876
 
811
877
    try:
848
914
        w = readit()
849
915
        sys.stdout.writelines(w.mash_iter(map(int, argv[3:])))
850
916
 
 
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
            
851
929
    elif cmd == 'annotate':
852
930
        w = readit()
853
931
        # newline is added to all lines regardless; too hard to get
865
943
        weave_toc(readit())
866
944
 
867
945
    elif cmd == 'stats':
868
 
        weave_stats(argv[2])
 
946
        weave_stats(argv[2], ProgressBar())
869
947
        
870
948
    elif cmd == 'check':
871
949
        w = readit()