~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
90
 
from cStringIO import StringIO
 
94
from difflib import SequenceMatcher
 
95
 
 
96
 
91
97
 
92
98
 
93
99
class WeaveError(Exception):
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.
211
217
        return not self.__eq__(other)
212
218
 
213
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
        
214
228
    def lookup(self, name):
 
229
        """Convert symbolic version name to index."""
215
230
        try:
216
231
            return self._name_map[name]
217
232
        except KeyError:
218
 
            raise WeaveError("name %s not present in weave %s" %
 
233
            raise WeaveError("name %r not present in weave %r" %
219
234
                             (name, self._weave_name))
220
235
 
221
 
        
222
 
    def add(self, name, parents, text):
 
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):
223
258
        """Add a single text on top of the weave.
224
259
  
225
260
        Returns the index number of the newly added version.
232
267
            List or set of direct parent version numbers.
233
268
            
234
269
        text
235
 
            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
236
276
 
237
277
        assert isinstance(name, basestring)
 
278
        if sha1 is None:
 
279
            sha1 = sha_strings(text)
238
280
        if name in self._name_map:
239
 
            raise WeaveError("name %r already present in weave" % name)
240
 
        
 
281
            return self._check_repeated_add(name, parents, text, sha1)
 
282
 
 
283
        parents = map(self.maybe_lookup, parents)
241
284
        self._check_versions(parents)
242
285
        ## self._check_lines(text)
243
286
        new_version = len(self._parents)
244
287
 
245
 
        s = sha.new()
246
 
        map(s.update, text)
247
 
        sha1 = s.hexdigest()
248
 
        del s
249
288
 
250
289
        # if we abort after here the (in-memory) weave will be corrupt because only
251
290
        # some fields are updated
300
339
        #print 'basis_lines:', basis_lines
301
340
        #print 'new_lines:  ', lines
302
341
 
303
 
        from difflib import SequenceMatcher
304
342
        s = SequenceMatcher(None, basis_lines, text)
305
343
 
306
344
        # offset gives the number of lines that have been inserted
345
383
    def inclusions(self, versions):
346
384
        """Return set of all ancestors of given version(s)."""
347
385
        i = set(versions)
348
 
        v = max(versions)
349
 
        try:
350
 
            while v >= 0:
351
 
                if v in i:
352
 
                    # include all its parents
353
 
                    i.update(self._parents[v])
354
 
                v -= 1
355
 
            return i
356
 
        except IndexError:
357
 
            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]
358
397
 
359
398
 
360
399
    def minimal_parents(self, version):
400
439
                raise IndexError("invalid version number %r" % i)
401
440
 
402
441
    
403
 
    def annotate(self, index):
404
 
        return list(self.annotate_iter(index))
405
 
 
406
 
 
407
 
    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):
408
447
        """Yield list of (index-id, line) pairs for the specified version.
409
448
 
410
449
        The index indicates when the line originated in the weave."""
411
 
        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):
412
452
            yield origin, text
413
453
 
414
454
 
512
552
    
513
553
 
514
554
 
515
 
    def get_iter(self, version):
 
555
    def get_iter(self, name_or_index):
516
556
        """Yield lines for the specified version."""
517
 
        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):
518
559
            yield line
519
560
 
520
561
 
521
 
    def get_text(self, version):
 
562
    def get_text(self, name_or_index):
 
563
        return ''.join(self.get_iter(name_or_index))
522
564
        assert isinstance(version, int)
523
 
        s = StringIO()
524
 
        s.writelines(self.get_iter(version))
525
 
        return s.getvalue()
526
 
 
527
 
 
528
 
    def get(self, index):
529
 
        return list(self.get_iter(index))
 
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
530
572
 
531
573
 
532
574
    def mash_iter(self, included):
533
575
        """Return composed version of multiple included versions."""
 
576
        included = map(self.maybe_lookup, included)
534
577
        for origin, lineno, text in self._extract(included):
535
578
            yield text
536
579
 
741
784
 
742
785
 
743
786
 
744
 
def weave_stats(weave_file):
745
 
    from bzrlib.progress import ProgressBar
 
787
def weave_stats(weave_file, pb):
746
788
    from bzrlib.weavefile import read_weave
747
789
 
748
 
    pb = ProgressBar()
749
 
 
750
790
    wf = file(weave_file, 'rb')
751
791
    w = read_weave(wf)
752
792
    # FIXME: doesn't work on pipes
793
833
        Display composite of all selected versions.
794
834
    weave merge WEAVEFILE VERSION1 VERSION2 > OUT
795
835
        Auto-merge two versions and display conflicts.
 
836
    weave diff WEAVEFILE VERSION1 VERSION2 
 
837
        Show differences between two versions.
796
838
 
797
839
example:
798
840
 
823
865
def main(argv):
824
866
    import sys
825
867
    import os
826
 
    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
827
875
    from bzrlib.progress import ProgressBar
828
876
 
829
877
    try:
866
914
        w = readit()
867
915
        sys.stdout.writelines(w.mash_iter(map(int, argv[3:])))
868
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
            
869
929
    elif cmd == 'annotate':
870
930
        w = readit()
871
931
        # newline is added to all lines regardless; too hard to get
883
943
        weave_toc(readit())
884
944
 
885
945
    elif cmd == 'stats':
886
 
        weave_stats(argv[2])
 
946
        weave_stats(argv[2], ProgressBar())
887
947
        
888
948
    elif cmd == 'check':
889
949
        w = readit()