/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Martin Pool
  • Date: 2005-09-13 08:20:13 UTC
  • Revision ID: mbp@sourcefrog.net-20050913082013-15b0ffec911f2a0c
- fix up imports

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
 
46
46
# with delta folded in and mutation of the list, 36.13s
47
47
 
48
 
# with all this and simplification of add code, 33s 
 
48
# with all this and simplification of add code, 33s
 
49
 
 
50
 
 
51
 
49
52
 
50
53
 
51
54
# TODO: Perhaps have copy method for Weave instances?
59
62
# binaries, perhaps by naively splitting on \n or perhaps using
60
63
# something like a rolling checksum.
61
64
 
62
 
# TODO: Track version names as well as indexes. 
63
 
 
64
65
# TODO: End marker for each version so we can stop reading?
65
66
 
66
67
# TODO: Check that no insertion occurs inside a deletion that was
75
76
# description of which revisions include it.  Nice for checking all
76
77
# shas in parallel.
77
78
 
78
 
 
 
79
# TODO: Using a single _extract routine and then processing the output
 
80
# is probably inefficient.  It's simple enough that we can afford to
 
81
# have slight specializations for different ways its used: annotate,
 
82
# basis for add, get, etc.
 
83
 
 
84
# TODO: Perhaps the API should work only in names to hide the integer
 
85
# indexes from the user?
 
86
 
 
87
 
 
88
 
 
89
import sha
 
90
from cStringIO import StringIO
79
91
 
80
92
 
81
93
class WeaveError(Exception):
101
113
 
102
114
    * a nonnegative index number.
103
115
 
104
 
    * a version-id string.
 
116
    * a version-id string. (not implemented yet)
105
117
 
106
118
    Typically the index number will be valid only inside this weave and
107
119
    the version-id is used to reference it in the larger world.
118
130
    The instruction can be '{' or '}' for an insertion block, and '['
119
131
    and ']' for a deletion block respectively.  The version is the
120
132
    integer version index.  There is no replace operator, only deletes
121
 
    and inserts.
 
133
    and inserts.  For '}', the end of an insertion, there is no
 
134
    version parameter because it always closes the most recently
 
135
    opened insertion.
122
136
 
123
137
    Constraints/notes:
124
138
 
160
174
        each version; the parent's parents are implied.
161
175
 
162
176
    _sha1s
163
 
        List of hex SHA-1 of each version, or None if not recorded.
 
177
        List of hex SHA-1 of each version.
 
178
 
 
179
    _names
 
180
        List of symbolic names for each version.  Each should be unique.
 
181
 
 
182
    _name_map
 
183
        For each name, the version number.
 
184
 
 
185
    _weave_name
 
186
        Descriptive name of this weave; typically the filename if known.
 
187
        Set by read_weave.
164
188
    """
165
189
 
166
 
    __slots__ = ['_weave', '_parents', '_sha1s']
 
190
    __slots__ = ['_weave', '_parents', '_sha1s', '_names', '_name_map',
 
191
                 '_weave_name']
167
192
    
168
 
    def __init__(self):
 
193
    def __init__(self, weave_name=None):
169
194
        self._weave = []
170
195
        self._parents = []
171
196
        self._sha1s = []
 
197
        self._names = []
 
198
        self._name_map = {}
 
199
        self._weave_name = weave_name
172
200
 
173
201
 
174
202
    def __eq__(self, other):
175
203
        if not isinstance(other, Weave):
176
204
            return False
177
205
        return self._parents == other._parents \
178
 
               and self._weave == other._weave
 
206
               and self._weave == other._weave \
 
207
               and self._sha1s == other._sha1s 
 
208
 
179
209
    
180
 
 
181
210
    def __ne__(self, other):
182
211
        return not self.__eq__(other)
183
212
 
 
213
 
 
214
    def lookup(self, name):
 
215
        try:
 
216
            return self._name_map[name]
 
217
        except KeyError:
 
218
            raise WeaveError("name %s not present in weave %s" %
 
219
                             (name, self._weave_name))
 
220
 
 
221
 
 
222
    def idx_to_name(self, version):
 
223
        return self._names[version]
 
224
 
184
225
        
185
 
    def add(self, parents, text):
 
226
    def add(self, name, parents, text):
186
227
        """Add a single text on top of the weave.
187
228
  
188
229
        Returns the index number of the newly added version.
189
230
 
 
231
        name
 
232
            Symbolic name for this version.
 
233
            (Typically the revision-id of the revision that added it.)
 
234
 
190
235
        parents
191
236
            List or set of direct parent version numbers.
192
237
            
193
238
        text
194
239
            Sequence of lines to be added in the new version."""
195
240
 
 
241
        assert isinstance(name, basestring)
 
242
        if name in self._name_map:
 
243
            raise WeaveError("name %r already present in weave" % name)
 
244
        
196
245
        self._check_versions(parents)
197
246
        ## self._check_lines(text)
198
247
        new_version = len(self._parents)
199
248
 
200
 
        import sha
201
249
        s = sha.new()
202
250
        map(s.update, text)
203
251
        sha1 = s.hexdigest()
204
252
        del s
205
253
 
206
 
        # if we abort after here the weave will be corrupt
207
 
        self._parents.append(frozenset(parents))
 
254
        # if we abort after here the (in-memory) weave will be corrupt because only
 
255
        # some fields are updated
 
256
        self._parents.append(parents[:])
208
257
        self._sha1s.append(sha1)
 
258
        self._names.append(name)
 
259
        self._name_map[name] = new_version
209
260
 
210
261
            
211
262
        if not parents:
216
267
            if text:
217
268
                self._weave.append(('{', new_version))
218
269
                self._weave.extend(text)
219
 
                self._weave.append(('}', new_version))
 
270
                self._weave.append(('}', None))
220
271
        
221
272
            return new_version
222
273
 
238
289
            basis_lineno.append(lineno)
239
290
            basis_lines.append(line)
240
291
 
241
 
        # another small special case: a merge, producing the same text as auto-merge
 
292
        # another small special case: a merge, producing the same text
 
293
        # as auto-merge
242
294
        if text == basis_lines:
243
295
            return new_version            
244
296
 
287
339
                # we don't destroy ourselves
288
340
                i = i2 + offset
289
341
                self._weave[i:i] = ([('{', new_version)] 
290
 
                                + text[j1:j2] 
291
 
                                + [('}', new_version)])
 
342
                                    + text[j1:j2] 
 
343
                                    + [('}', None)])
292
344
                offset += 2 + (j2 - j1)
293
345
 
294
346
        return new_version
309
361
            raise ValueError("version %d not present in weave" % v)
310
362
 
311
363
 
 
364
    def parents(self, version):
 
365
        return self._parents[version]
 
366
 
 
367
 
312
368
    def minimal_parents(self, version):
313
369
        """Find the minimal set of parents for the version."""
314
370
        included = self._parents[version]
384
440
                if c == '{':
385
441
                    istack.append(v)
386
442
                elif c == '}':
387
 
                    oldv = istack.pop()
 
443
                    istack.pop()
388
444
                elif c == '[':
389
445
                    assert v not in dset
390
446
                    dset.add(v)
410
466
 
411
467
        The set typically but not necessarily corresponds to a version.
412
468
        """
 
469
        for i in versions:
 
470
            if not isinstance(i, int):
 
471
                raise ValueError(i)
 
472
            
413
473
        included = self.inclusions(versions)
414
474
 
415
475
        istack = []
431
491
                    assert v not in istack
432
492
                    istack.append(v)
433
493
                elif c == '}':
434
 
                    oldv = istack.pop()
435
 
                    assert oldv == v
 
494
                    istack.pop()
436
495
                elif c == '[':
437
496
                    if v in included:
438
497
                        assert v not in dset
467
526
            yield line
468
527
 
469
528
 
 
529
    def get_text(self, version):
 
530
        assert isinstance(version, int)
 
531
        s = StringIO()
 
532
        s.writelines(self.get_iter(version))
 
533
        return s.getvalue()
 
534
 
 
535
 
470
536
    def get(self, index):
471
537
        return list(self.get_iter(index))
472
538
 
508
574
 
509
575
        # try extracting all versions; this is a bit slow and parallel
510
576
        # extraction could be used
511
 
        import sha
512
577
        nv = self.numversions()
513
578
        for version in range(nv):
514
579
            if progress_bar:
670
735
 
671
736
 
672
737
 
673
 
def weave_info(w):
674
 
    """Show some text information about the weave."""
675
 
    print '%6s %40s %20s' % ('ver', 'sha1', 'parents')
676
 
    for i in (6, 40, 20):
 
738
def weave_toc(w):
 
739
    """Show the weave's table-of-contents"""
 
740
    print '%6s %50s %10s %10s' % ('ver', 'name', 'sha1', 'parents')
 
741
    for i in (6, 50, 10, 10):
677
742
        print '-' * i,
678
743
    print
679
744
    for i in range(w.numversions()):
680
745
        sha1 = w._sha1s[i]
681
 
        print '%6d %40s %s' % (i, sha1, ' '.join(map(str, w._parents[i])))
 
746
        name = w._names[i]
 
747
        parent_str = ' '.join(map(str, w._parents[i]))
 
748
        print '%6d %-50.50s %10.10s %s' % (i, name, sha1, parent_str)
682
749
 
683
750
 
684
751
 
706
773
    print 'weave file        %9d bytes' % weave_size
707
774
    print 'total contents    %9d bytes' % total
708
775
    print 'compression ratio %9.2fx' % (float(total) / float(weave_size))
709
 
 
 
776
    if vers:
 
777
        avg = total/vers
 
778
        print 'average size      %9d bytes' % avg
 
779
        print 'relative size     %9.2fx' % (float(weave_size) / float(avg))
710
780
 
711
781
 
712
782
def usage():
721
791
        Write out specified version.
722
792
    weave check WEAVEFILE
723
793
        Check consistency of all versions.
724
 
    weave info WEAVEFILE
 
794
    weave toc WEAVEFILE
725
795
        Display table of contents.
726
 
    weave add WEAVEFILE [BASE...] < NEWTEXT
 
796
    weave add WEAVEFILE NAME [BASE...] < NEWTEXT
727
797
        Add NEWTEXT, with specified parent versions.
728
798
    weave annotate WEAVEFILE VERSION
729
799
        Display origin of each line.
736
806
 
737
807
    % weave init foo.weave
738
808
    % vi foo.txt
739
 
    % weave add foo.weave < foo.txt
 
809
    % weave add foo.weave ver0 < foo.txt
740
810
    added version 0
741
811
 
742
812
    (create updated version)
743
813
    % vi foo.txt
744
814
    % weave get foo.weave 0 | diff -u - foo.txt
745
 
    % weave add foo.weave 0 < foo.txt
 
815
    % weave add foo.weave ver1 0 < foo.txt
746
816
    added version 1
747
817
 
748
818
    % weave get foo.weave 0 > foo.txt       (create forked version)
749
819
    % vi foo.txt
750
 
    % weave add foo.weave 0 < foo.txt
 
820
    % weave add foo.weave ver2 0 < foo.txt
751
821
    added version 2
752
822
 
753
823
    % weave merge foo.weave 1 2 > foo.txt   (merge them)
754
824
    % vi foo.txt                            (resolve conflicts)
755
 
    % weave add foo.weave 1 2 < foo.txt     (commit merged version)     
 
825
    % weave add foo.weave merged 1 2 < foo.txt     (commit merged version)     
756
826
    
757
827
"""
758
828
    
764
834
    from weavefile import write_weave, read_weave
765
835
    from bzrlib.progress import ProgressBar
766
836
 
767
 
    #import psyco
768
 
    #psyco.full()
 
837
    try:
 
838
        import psyco
 
839
        psyco.full()
 
840
    except ImportError:
 
841
        pass
 
842
 
 
843
    if len(argv) < 2:
 
844
        usage()
 
845
        return 0
769
846
 
770
847
    cmd = argv[1]
771
848
 
777
854
    elif cmd == 'add':
778
855
        w = readit()
779
856
        # at the moment, based on everything in the file
780
 
        parents = map(int, argv[3:])
 
857
        name = argv[3]
 
858
        parents = map(int, argv[4:])
781
859
        lines = sys.stdin.readlines()
782
 
        ver = w.add(parents, lines)
 
860
        ver = w.add(name, parents, lines)
783
861
        write_weave(w, file(argv[2], 'wb'))
784
 
        print 'added version %d' % ver
 
862
        print 'added version %r %d' % (name, ver)
785
863
    elif cmd == 'init':
786
864
        fn = argv[2]
787
865
        if os.path.exists(fn):
809
887
                print '%5d | %s' % (origin, text)
810
888
                lasto = origin
811
889
                
812
 
    elif cmd == 'info':
813
 
        weave_info(readit())
 
890
    elif cmd == 'toc':
 
891
        weave_toc(readit())
814
892
 
815
893
    elif cmd == 'stats':
816
894
        weave_stats(argv[2])
865
943
        raise ValueError('unknown command %r' % cmd)
866
944
    
867
945
 
 
946
 
 
947
def profile_main(argv): 
 
948
    import tempfile, hotshot, hotshot.stats
 
949
 
 
950
    prof_f = tempfile.NamedTemporaryFile()
 
951
 
 
952
    prof = hotshot.Profile(prof_f.name)
 
953
 
 
954
    ret = prof.runcall(main, argv)
 
955
    prof.close()
 
956
 
 
957
    stats = hotshot.stats.load(prof_f.name)
 
958
    #stats.strip_dirs()
 
959
    stats.sort_stats('cumulative')
 
960
    ## XXX: Might like to write to stderr or the trace file instead but
 
961
    ## print_stats seems hardcoded to stdout
 
962
    stats.print_stats(20)
 
963
            
 
964
    return ret
 
965
 
 
966
 
868
967
if __name__ == '__main__':
869
968
    import sys
870
 
    sys.exit(main(sys.argv))
 
969
    if '--profile' in sys.argv:
 
970
        args = sys.argv[:]
 
971
        args.remove('--profile')
 
972
        sys.exit(profile_main(args))
 
973
    else:
 
974
        sys.exit(main(sys.argv))
 
975