~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Robert Collins
  • Date: 2005-09-28 09:35:50 UTC
  • mfrom: (1185.1.47)
  • Revision ID: robertc@robertcollins.net-20050928093550-3ca194dfaffc79f1
merge from integration

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
 
 
97
from bzrlib.osutils import sha_strings
91
98
 
92
99
 
93
100
class WeaveError(Exception):
113
120
 
114
121
    * a nonnegative index number.
115
122
 
116
 
    * a version-id string. (not implemented yet)
 
123
    * a version-id string.
117
124
 
118
125
    Typically the index number will be valid only inside this weave and
119
126
    the version-id is used to reference it in the larger world.
211
218
        return not self.__eq__(other)
212
219
 
213
220
 
 
221
    def maybe_lookup(self, name_or_index):
 
222
        """Convert possible symbolic name to index, or pass through indexes."""
 
223
        if isinstance(name_or_index, (int, long)):
 
224
            return name_or_index
 
225
        else:
 
226
            return self.lookup(name_or_index)
 
227
 
 
228
        
214
229
    def lookup(self, name):
 
230
        """Convert symbolic version name to index."""
215
231
        try:
216
232
            return self._name_map[name]
217
233
        except KeyError:
218
 
            raise WeaveError("name %s not present in weave %s" %
 
234
            raise WeaveError("name %r not present in weave %r" %
219
235
                             (name, self._weave_name))
220
236
 
221
 
        
222
 
    def add(self, name, parents, text):
 
237
 
 
238
    def idx_to_name(self, version):
 
239
        return self._names[version]
 
240
 
 
241
 
 
242
    def _check_repeated_add(self, name, parents, text, sha1):
 
243
        """Check that a duplicated add is OK.
 
244
 
 
245
        If it is, return the (old) index; otherwise raise an exception.
 
246
        """
 
247
        idx = self.lookup(name)
 
248
        if sorted(self._parents[idx]) != sorted(parents):
 
249
            raise WeaveError("name \"%s\" already present in weave "
 
250
                             "with different parents" % name)
 
251
        if sha1 != self._sha1s[idx]:
 
252
            raise WeaveError("name \"%s\" already present in weave "
 
253
                             "with different text" % name)            
 
254
        return idx
 
255
        
 
256
 
 
257
        
 
258
    def add(self, name, parents, text, sha1=None):
223
259
        """Add a single text on top of the weave.
224
260
  
225
261
        Returns the index number of the newly added version.
232
268
            List or set of direct parent version numbers.
233
269
            
234
270
        text
235
 
            Sequence of lines to be added in the new version."""
 
271
            Sequence of lines to be added in the new version.
 
272
 
 
273
        sha -- SHA-1 of the file, if known.  This is trusted to be
 
274
            correct if supplied.
 
275
        """
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
 
793
836
        Display composite of all selected versions.
794
837
    weave merge WEAVEFILE VERSION1 VERSION2 > OUT
795
838
        Auto-merge two versions and display conflicts.
 
839
    weave diff WEAVEFILE VERSION1 VERSION2 
 
840
        Show differences between two versions.
796
841
 
797
842
example:
798
843
 
823
868
def main(argv):
824
869
    import sys
825
870
    import os
826
 
    from weavefile import write_weave, read_weave
 
871
    from bzrlib.weavefile import write_weave, read_weave
827
872
    from bzrlib.progress import ProgressBar
828
873
 
829
874
    try:
866
911
        w = readit()
867
912
        sys.stdout.writelines(w.mash_iter(map(int, argv[3:])))
868
913
 
 
914
    elif cmd == 'diff':
 
915
        from difflib import unified_diff
 
916
        w = readit()
 
917
        fn = argv[2]
 
918
        v1, v2 = map(int, argv[3:5])
 
919
        lines1 = w.get(v1)
 
920
        lines2 = w.get(v2)
 
921
        diff_gen = unified_diff(lines1, lines2,
 
922
                                '%s version %d' % (fn, v1),
 
923
                                '%s version %d' % (fn, v2))
 
924
        sys.stdout.writelines(diff_gen)
 
925
            
869
926
    elif cmd == 'annotate':
870
927
        w = readit()
871
928
        # newline is added to all lines regardless; too hard to get